diff --git a/apps/web/components/Organization/Slice/PowerBiSlice/PowerBiSlice.css.ts b/apps/web/components/Organization/Slice/PowerBiSlice/PowerBiSlice.css.ts new file mode 100644 index 000000000000..3e90be0d77b6 --- /dev/null +++ b/apps/web/components/Organization/Slice/PowerBiSlice/PowerBiSlice.css.ts @@ -0,0 +1,5 @@ +import { style } from '@vanilla-extract/css' + +export const container = style({ + height: '500px', +}) diff --git a/apps/web/components/Organization/Slice/PowerBiSlice/PowerBiSlice.tsx b/apps/web/components/Organization/Slice/PowerBiSlice/PowerBiSlice.tsx new file mode 100644 index 000000000000..102499772c13 --- /dev/null +++ b/apps/web/components/Organization/Slice/PowerBiSlice/PowerBiSlice.tsx @@ -0,0 +1,27 @@ +import { PowerBIEmbed } from 'powerbi-client-react' +import { Embed } from 'powerbi-client' +import { PowerBiSlice as PowerBiSliceSchema } from '@island.is/web/graphql/schema' +import * as styles from './PowerBiSlice.css' + +interface PowerBiSliceProps { + slice: PowerBiSliceSchema +} + +export const PowerBiSlice = ({ slice }: PowerBiSliceProps) => { + const getEmbeddedComponent = (embed: Embed) => { + if (slice?.powerBiEmbedProps?.height) { + embed.element.style.height = slice.powerBiEmbedProps.height + } + } + + const embedProps = slice?.powerBiEmbedProps ?? {} + return ( + + ) +} + +export default PowerBiSlice diff --git a/apps/web/components/Organization/Slice/PowerBiSlice/index.ts b/apps/web/components/Organization/Slice/PowerBiSlice/index.ts new file mode 100644 index 000000000000..289b36d29e2e --- /dev/null +++ b/apps/web/components/Organization/Slice/PowerBiSlice/index.ts @@ -0,0 +1,4 @@ +import dynamic from 'next/dynamic' +export const PowerBiSlice = dynamic(() => import('./PowerBiSlice'), { + ssr: false, +}) diff --git a/apps/web/components/Organization/index.ts b/apps/web/components/Organization/index.ts index bea3ad6c1062..d6eef4606b7c 100644 --- a/apps/web/components/Organization/index.ts +++ b/apps/web/components/Organization/index.ts @@ -23,3 +23,4 @@ export * from './Slice/EventSlice/EventSlice' export * from './SearchBox/SearchBox' export * from './Slice/MailingListSignupSlice/MailingListSignupSlice' export * from './Slice/LifeEventPageListSlice/LifeEventPageListSlice' +export * from './Slice/PowerBiSlice' diff --git a/apps/web/screens/Organization/SubPage.tsx b/apps/web/screens/Organization/SubPage.tsx index 975adf0a4f1d..6e3db582d376 100644 --- a/apps/web/screens/Organization/SubPage.tsx +++ b/apps/web/screens/Organization/SubPage.tsx @@ -14,6 +14,7 @@ import { import { withMainLayout } from '@island.is/web/layouts/main' import { ContentLanguage, + PowerBiSlice as PowerBiSliceSchema, Query, QueryGetNamespaceArgs, QueryGetOrganizationPageArgs, @@ -35,6 +36,7 @@ import { SliceDropdown, Form, OneColumnTextSlice, + PowerBiSlice, } from '@island.is/web/components' import { CustomNextError } from '@island.is/web/units/errors' import useContentfulId from '@island.is/web/hooks/useContentfulId' @@ -178,6 +180,9 @@ const SubPage: Screen = ({ OneColumnText: (slice) => ( ), + PowerBiSlice: (slice: PowerBiSliceSchema) => ( + + ), }, })} diff --git a/apps/web/screens/Project/Project.tsx b/apps/web/screens/Project/Project.tsx index d84b26752084..0e39109f594d 100644 --- a/apps/web/screens/Project/Project.tsx +++ b/apps/web/screens/Project/Project.tsx @@ -19,6 +19,7 @@ import { Stepper, stepperUtils, Form, + PowerBiSlice, } from '@island.is/web/components' import { Box, @@ -74,8 +75,9 @@ const ProjectPage: Screen = ({ >(undefined) let content: SliceType[] = [] - if (!!subpage && renderSlicesAsTabs) + if (!!subpage && renderSlicesAsTabs) { content = selectedSliceTab?.content as SliceType[] + } if (!subpage) content = projectPage?.content as SliceType[] useEffect(() => { @@ -136,6 +138,7 @@ const ProjectPage: Screen = ({ richText(subpage.content as SliceType[], { renderComponent: { Form: (slice) =>
, + PowerBiSlice: (slice) => , }, })} @@ -168,7 +171,13 @@ const ProjectPage: Screen = ({ {selectedSliceTab.title} )} - {content && richText(content)} + {content && + richText(content, { + renderComponent: { + Form: (slice) => , + PowerBiSlice: (slice) => , + }, + })} {!subpage && projectPage.stepper && ( { } } +export interface IPowerBiSliceFields { + /** Title */ + title?: string | undefined + + /** Config */ + config: Record +} + +/** A Slice that embeds a Power BI report */ + +export interface IPowerBiSlice extends Entry { + sys: { + id: string + type: string + createdAt: string + updatedAt: string + locale: string + contentType: { + sys: { + id: 'powerBiSlice' + linkType: 'ContentType' + type: 'Link' + } + } + } +} + export interface IProcessEntryFields { /** Type */ type: @@ -3819,6 +3847,7 @@ export type CONTENT_TYPE = | 'organizationTag' | 'overviewLinks' | 'pageHeader' + | 'powerBiSlice' | 'processEntry' | 'projectPage' | 'projectSubpage' diff --git a/libs/cms/src/lib/models/powerBiSlice.model.ts b/libs/cms/src/lib/models/powerBiSlice.model.ts new file mode 100644 index 000000000000..06e51dcee285 --- /dev/null +++ b/libs/cms/src/lib/models/powerBiSlice.model.ts @@ -0,0 +1,28 @@ +import { Field, ID, ObjectType } from '@nestjs/graphql' +import graphqlTypeJson from 'graphql-type-json' +import { SystemMetadata } from '@island.is/shared/types' +import { IPowerBiSlice } from '../generated/contentfulTypes' + +@ObjectType() +export class PowerBiSlice { + @Field(() => ID) + id!: string + + @Field() + title?: string + + @Field(() => graphqlTypeJson, { nullable: true }) + powerBiEmbedProps?: Record +} + +export const mapPowerBiSlice = ({ + fields, + sys, +}: IPowerBiSlice): SystemMetadata => { + return { + typename: 'PowerBiSlice', + id: sys.id, + title: fields.title ?? '', + powerBiEmbedProps: fields.config ?? null, + } +} diff --git a/libs/cms/src/lib/unions/slice.union.ts b/libs/cms/src/lib/unions/slice.union.ts index 26f23fc916b1..aeaa17d2aff5 100644 --- a/libs/cms/src/lib/unions/slice.union.ts +++ b/libs/cms/src/lib/unions/slice.union.ts @@ -34,6 +34,7 @@ import { IGraphCard, ILifeEventPageListSlice, ISidebarCard, + IPowerBiSlice, } from '../generated/contentfulTypes' import { Image, mapImage } from '../models/image.model' import { Asset, mapAsset } from '../models/asset.model' @@ -96,6 +97,7 @@ import { mapLifeEventPageListSlice, } from '../models/lifeEventPageListSlice.model' import { mapSidebarCard, SidebarCard } from '../models/sidebarCard.model' +import { PowerBiSlice, mapPowerBiSlice } from '../models/powerBiSlice.model' type SliceTypes = | ITimeline @@ -129,6 +131,7 @@ type SliceTypes = | IGraphCard | ILifeEventPageListSlice | ISidebarCard + | IPowerBiSlice export const SliceUnion = createUnionType({ name: 'Slice', @@ -167,6 +170,7 @@ export const SliceUnion = createUnionType({ GraphCard, LifeEventPageListSlice, SidebarCard, + PowerBiSlice, ], resolveType: (document) => document.typename, // typename is appended to request on indexing }) @@ -236,6 +240,8 @@ export const mapSliceUnion = (slice: SliceTypes): typeof SliceUnion => { return mapLifeEventPageListSlice(slice as ILifeEventPageListSlice) case 'sidebarCard': return mapSidebarCard(slice as ISidebarCard) + case 'powerBiSlice': + return mapPowerBiSlice(slice as IPowerBiSlice) default: throw new ApolloError(`Can not convert to slice: ${contentType}`) } diff --git a/package.json b/package.json index 397207aa5edf..f9b4a7e0401c 100644 --- a/package.json +++ b/package.json @@ -217,6 +217,8 @@ "pdfkit": "0.11.0", "pg": "8.1.0", "pg-hstore": "2.3.3", + "powerbi-client": "2.21.1", + "powerbi-client-react": "1.3.5", "prom-client": "12.0.0", "pubsub-js": "1.8.0", "react": "17.0.2", diff --git a/yarn.lock b/yarn.lock index e8766d184d3d..4ad8fa39c6e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5177,15 +5177,6 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" -"@nestjs/apollo@^10.0.22": - version "10.1.3" - resolved "https://registry.yarnpkg.com/@nestjs/apollo/-/apollo-10.1.3.tgz#c5f02a032b29b2e55472f09f5a474628ee507695" - integrity sha512-N0qm8R9KOJfOyIKu5U345uEdeO8n/8NOqKr1sJGUaiYn11PAIyg6159wO4Q4lhgJTqCV7QhRQ49sHzVHbjrtiA== - dependencies: - iterall "1.3.0" - lodash.omit "4.5.0" - tslib "2.4.0" - "@nestjs/axios@0.0.7": version "0.0.7" resolved "https://registry.yarnpkg.com/@nestjs/axios/-/axios-0.0.7.tgz#7f134636db13c2c1e8299365c7eceb73cd782b67" @@ -14254,7 +14245,7 @@ dataloader@2.0.0, dataloader@^2.0.0: resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.0.0.tgz#41eaf123db115987e21ca93c005cd7753c55fe6f" integrity sha512-YzhyDAwA4TaQIhM5go+vCLmU0UikghC/t9DTQYZR2M/UvZ1MdOhPezSDZcjj9uqQJOMqjLcpWtyW2iNINdlatQ== -dataloader@2.1.0, dataloader@^2.1.0: +dataloader@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.1.0.tgz#c69c538235e85e7ac6c6c444bae8ecabf5de9df7" integrity sha512-qTcEYLen3r7ojZNgVUaRggOI+KM7jrKxXeSHhogh/TWxYMeONEMqY+hmkobiYQozsGIyg9OYVzO4ZIfoB4I0pQ== @@ -15403,7 +15394,7 @@ es6-map@^0.1.5: es6-symbol "~3.1.1" event-emitter "~0.3.5" -es6-promise@^3.2.1: +es6-promise@^3.1.2, es6-promise@^3.2.1: version "3.3.1" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613" integrity sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM= @@ -18360,6 +18351,13 @@ http-parser-js@>=0.5.1: resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.3.tgz#01d2709c79d41698bb01d4decc5e9da4e4a033d9" integrity sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg== +http-post-message@^0.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/http-post-message/-/http-post-message-0.2.3.tgz#32c560ac615f310a7e459ffc71277b579b242e1e" + integrity sha512-76heerrzYhvWptJfWxUarHw2O3fkMqF48bbq/S6XFWHUc34o8tkySBwtReXuAKJAECZWVu8U0TYLckFcwtSdrg== + dependencies: + es6-promise "^3.2.1" + http-proxy-agent@^4.0.0, http-proxy-agent@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" @@ -19671,7 +19669,7 @@ iterall@1.2.2: resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.2.2.tgz#92d70deb8028e0c39ff3164fdbf4d8b088130cd7" integrity sha512-yynBb1g+RFUPY64fTrFv7nsjRrENBQJaX2UL+2Szc9REFrSNm1rpSXHGzhmAy7a9uv3vlvgBlXnf9RqmPH1/DA== -iterall@1.3.0, iterall@^1.1.3, iterall@^1.2.1, iterall@^1.3.0: +iterall@^1.1.3, iterall@^1.2.1, iterall@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.3.0.tgz#afcb08492e2915cbd8a0884eb93a8c94d0d72fea" integrity sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg== @@ -21701,7 +21699,7 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash.omit@4.5.0, lodash.omit@^4.5.0: +lodash.omit@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.omit/-/lodash.omit-4.5.0.tgz#6eb19ae5a1ee1dd9df0b969e66ce0b7fa30b5e60" integrity sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA= @@ -22894,15 +22892,6 @@ nested-error-stacks@^2.0.0, nested-error-stacks@^2.1.0: resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz#0fbdcf3e13fe4994781280524f8b96b0cdff9c61" integrity sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug== -nestjs-dataloader@9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/nestjs-dataloader/-/nestjs-dataloader-9.0.0.tgz#2fb409c1cdd879cb9c46d0d33e43d00e7d14151e" - integrity sha512-BiBEY+dhmhXxMjLOYjBOXs7kR1GF5z6cCBLLtwtRWGBAl7mz9EliyHk1gwlEgag+PK5w/pToqMUCV3D+SQkx9Q== - dependencies: - "@nestjs/apollo" "^10.0.22" - dataloader "^2.1.0" - rxjs "^7.5.6" - netmask@2.0.1, netmask@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.1.tgz#5a5cbdcbb7b6de650870e15e83d3e9553a414cf4" @@ -25458,6 +25447,37 @@ posthtml@^0.13.4: posthtml-parser "^0.5.0" posthtml-render "^1.2.3" +powerbi-client-react@1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/powerbi-client-react/-/powerbi-client-react-1.3.5.tgz#652f5077505a73c1524a7a9c5e75e2131b47e566" + integrity sha512-YqLpkus9UD6YxRe3W+Kcnao6lRv/Do2XZilx4UWRWlXS+roQ3QI6Dm8KsxUpvMYo636x4mzAjj3FOymQwoLi4w== + dependencies: + lodash.isequal "^4.5.0" + powerbi-client "^2.21.1" + +powerbi-client@2.21.1, powerbi-client@^2.21.1: + version "2.21.1" + resolved "https://registry.yarnpkg.com/powerbi-client/-/powerbi-client-2.21.1.tgz#b9924c73ebbbf9070892d85da7a4257870e627a7" + integrity sha512-v5rbqVnAiES5iU8OerUKdMVLfV934QykZkpge3Iyx9EcKob/Ga1mWa973e/32EejEqtGqoYNcvkrkK7utfGV+g== + dependencies: + http-post-message "^0.2" + powerbi-models "^1.11.0" + powerbi-router "^0.1" + window-post-message-proxy "^0.2" + +powerbi-models@^1.11.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/powerbi-models/-/powerbi-models-1.11.0.tgz#c2f1611068344b717bc211fbe19247c916d964ac" + integrity sha512-zRy6PBCmOhtHkZ6aWC/s7W/oV8aCmu7QlnG1GEy8XThj2baxFYnj2tCYaKAsOX7CvlCymw1NIXN9RhiRQI7EXQ== + +powerbi-router@^0.1: + version "0.1.5" + resolved "https://registry.yarnpkg.com/powerbi-router/-/powerbi-router-0.1.5.tgz#dd2d2d0474f8cb76690a85f6a51d8ca9fea93a32" + integrity sha512-DFJCKxwh/DqMZXtHSo6xZl87mbRviZGn4P7Oi2rT0L4HMI4AjnWIrwg0JCSM7ymBzYnNe5UmrsCaf2Upur5RQA== + dependencies: + es6-promise "^3.2.1" + route-recognizer "^0.1.11" + preact-render-to-string@^5.1.14: version "5.1.19" resolved "https://registry.yarnpkg.com/preact-render-to-string/-/preact-render-to-string-5.1.19.tgz#ffae7c3bd1680be5ecf5991d41fe3023b3051e0e" @@ -27477,6 +27497,11 @@ rosetta@1.0.0: dlv "^1.1.3" templite "^1.1.0" +route-recognizer@^0.1.11: + version "0.1.11" + resolved "https://registry.yarnpkg.com/route-recognizer/-/route-recognizer-0.1.11.tgz#810d8e5702abb4056d6dcb8e865c5685e7c14eb7" + integrity sha512-7JNu5mXQVa39zxmUKyk/bfpeF2WyEC5JKVTJO5HATcoUQpcQsI3eLzhwGU69xeOagQxfOQ+yr2sSv0G8xy+vQA== + rsvp@^4.8.4: version "4.8.5" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" @@ -27542,13 +27567,6 @@ rxjs@^7.0.0: dependencies: tslib "^2.1.0" -rxjs@^7.5.6: - version "7.5.7" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.7.tgz#2ec0d57fdc89ece220d2e702730ae8f1e49def39" - integrity sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA== - dependencies: - tslib "^2.1.0" - safe-buffer@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" @@ -31477,6 +31495,13 @@ wildcard@^2.0.0: resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== +window-post-message-proxy@^0.2: + version "0.2.6" + resolved "https://registry.yarnpkg.com/window-post-message-proxy/-/window-post-message-proxy-0.2.6.tgz#8d9800d441629c8bff0a8cb2bf9e0119702940ca" + integrity sha512-IwNWtUWVFarBa2F9vhmfv2yr0PfPm57QuOBCw3qSLaunhD3THcHUKQ4HrJQiCuYUT7LEjhUtaoA+hrV+wQzNmQ== + dependencies: + es6-promise "^3.1.2" + winston-cloudwatch@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/winston-cloudwatch/-/winston-cloudwatch-3.1.1.tgz#f5b862dc29c9f8501e72541791fcd1d4b4822376"