diff --git a/.github/labeler.yml b/.github/labeler.yml index 92e09577403..cdfdecb086b 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -44,6 +44,9 @@ area/text-editor: area/ui-editor: - 'frontend/packages/ux-editor/**/*' +area/studio-root: + - 'frontend/studio-root/**/*' + ## Other labels kind/dependencies: - 'backend/packagegroups/NuGet.props' diff --git a/.github/workflows/designer-dotnet-test.yaml b/.github/workflows/designer-dotnet-test.yaml index 73ee96deeb4..23fef00d90e 100644 --- a/.github/workflows/designer-dotnet-test.yaml +++ b/.github/workflows/designer-dotnet-test.yaml @@ -17,7 +17,7 @@ jobs: analyze: strategy: matrix: - os: [ubuntu-latest,windows-latest,macos-latest] #windows-latest and macos-latest excluded temporarily + os: [ubuntu-latest,windows-latest,macos-latest] name: Run dotnet build and test runs-on: ${{ matrix.os}} env: @@ -36,4 +36,11 @@ jobs: dotnet build backend/Designer.sln -v m - name: Test run: | - dotnet test backend/Designer.sln --filter FullyQualifiedName~AppDevelopmentController --no-build; dotnet test backend/Designer.sln --filter FullyQualifiedName~PreviewController --no-build; dotnet test backend/Designer.sln --filter "(Category!=GiteaIntegrationTest)&(FullyQualifiedName!~AppDevelopmentController)&(FullyQualifiedName!~PreviewController)" -v m --no-build + dotnet test backend/Designer.sln --filter FullyQualifiedName~AppDevelopmentController --no-build; + dotnet test backend/Designer.sln --filter FullyQualifiedName~PolicyControllerTests --no-build; + dotnet test backend/Designer.sln --filter FullyQualifiedName~PreviewController --no-build; + dotnet test backend/Designer.sln --filter FullyQualifiedName~DataModelsController --no-build; + dotnet test backend/Designer.sln --filter FullyQualifiedName~ResourceAdminController --no-build; + dotnet test backend/Designer.sln --filter FullyQualifiedName~TextController --no-build; + dotnet test backend/Designer.sln --filter "(Category!=GiteaIntegrationTest)&(FullyQualifiedName~RepositoryController)" --no-build; + dotnet test backend/Designer.sln --filter "(Category!=GiteaIntegrationTest)&(FullyQualifiedName!~AppDevelopmentController)&(FullyQualifiedName!~PreviewController)&(FullyQualifiedName!~PolicyControllerTests)&(FullyQualifiedName!~DataModelsController)&(FullyQualifiedName!~ResourceAdminController)&(FullyQualifiedName!~TextController)&(FullyQualifiedName!~RepositoryController)" -v m --no-build diff --git a/Dockerfile b/Dockerfile index ae303637023..2f9cab4ca8a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,7 +23,7 @@ FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine AS final EXPOSE 80 WORKDIR /app ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false \ - DOTNET_RUNNING_IN_CONTAINER=true + DOTNET_RUNNING_IN_CONTAINER=true RUN apk add --no-cache icu-libs krb5-libs libgcc libintl libssl1.1 libstdc++ zlib COPY --from=generate-studio-backend /app_output . @@ -32,6 +32,7 @@ COPY --from=generate-studio-frontend /build/frontend/dist/app-preview ./wwwroot/ COPY --from=generate-studio-frontend /build/frontend/dist/dashboard ./wwwroot/designer/frontend/dashboard COPY --from=generate-studio-frontend /build/frontend/dist/resourceadm ./wwwroot/designer/frontend/resourceadm COPY --from=generate-studio-frontend /build/frontend/dist/language ./wwwroot/designer/frontend/lang +COPY --from=generate-studio-frontend /build/frontend/dist/studio-root ./wwwroot/designer/frontend/studio-root ## Copying app template COPY --from=generate-studio-backend /app_template ./Templates/AspNet diff --git a/README.md b/README.md index 1e872487c89..19ee109664d 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,7 @@ is configured with the following variables. DEVELOP_BACKEND=0 DEVELOP_DASHBOARD=0 DEVELOP_APP_DEVELOPMENT=0 +DEVELOP_STUDIO_ROOT=0 ``` ## Developing Backend diff --git a/backend/src/Designer/Controllers/HomeController.cs b/backend/src/Designer/Controllers/HomeController.cs index 61734fc4fef..122ea5325ca 100644 --- a/backend/src/Designer/Controllers/HomeController.cs +++ b/backend/src/Designer/Controllers/HomeController.cs @@ -80,6 +80,13 @@ public async Task StartPage() return LocalRedirect("/dashboard"); } + [Route("/{*AllValues:regex(^(?!designer).*$)}")] + public IActionResult Index() + { + ViewBag.InstrumentationKey = _applicationInsightsSettings.InstrumentationKey; + return View(); + } + /// /// The default action presenting a list of available apps when the user is logged in /// @@ -87,10 +94,10 @@ public async Task StartPage() [Route("/[controller]/[action]")] [Authorize] [Route("/dashboard/{*AllValues}", Name = "DefaultLoggedIn")] - public ActionResult Index() + public ActionResult Dashboard() { ViewBag.InstrumentationKey = _applicationInsightsSettings.InstrumentationKey; - return View(); + return View("Dashboard"); } /// diff --git a/backend/src/Designer/Views/Home/Dashboard.cshtml b/backend/src/Designer/Views/Home/Dashboard.cshtml new file mode 100644 index 00000000000..1050d473b9a --- /dev/null +++ b/backend/src/Designer/Views/Home/Dashboard.cshtml @@ -0,0 +1,17 @@ +@{ViewBag.Title = "Altinn Studio";} + + + + @ViewBag.Title + + + + +
+ + + + diff --git a/backend/src/Designer/Views/Home/Index.cshtml b/backend/src/Designer/Views/Home/Index.cshtml index 1050d473b9a..abbb39d0a23 100644 --- a/backend/src/Designer/Views/Home/Index.cshtml +++ b/backend/src/Designer/Views/Home/Index.cshtml @@ -1,17 +1,25 @@ -@{ViewBag.Title = "Altinn Studio";} +@{ + ViewBag.Title = "Altinn Studio"; +} + - @ViewBag.Title - - + @ViewBag.Title + + + + + -
- - +
+ + + diff --git a/backend/src/Designer/wwwroot/designer/img/overview-background.svg b/backend/src/Designer/wwwroot/designer/img/page-background.svg similarity index 100% rename from backend/src/Designer/wwwroot/designer/img/overview-background.svg rename to backend/src/Designer/wwwroot/designer/img/page-background.svg diff --git a/development/README.md b/development/README.md index 893d834c0a7..47c68409a37 100644 --- a/development/README.md +++ b/development/README.md @@ -21,6 +21,7 @@ DEVELOP_APP_DEVELOPMENT=0 DEVELOP_BACKEND=0 DEVELOP_DASHBOARD=0 DEVELOP_PREVIEW=0 +DEVELOP_STUDIO_ROOT=0 GITEA_ADMIN_PASS=a_password GITEA_ADMIN_USER=angiteaadminuser GITEA_ORG_USER=ttd diff --git a/development/load-balancer/nginx.conf.conf b/development/load-balancer/nginx.conf.conf index 93111317964..4a9dd826f17 100644 --- a/development/load-balancer/nginx.conf.conf +++ b/development/load-balancer/nginx.conf.conf @@ -36,6 +36,7 @@ http { set $dev_app_preview $DEVELOP_PREVIEW; set $dev_backend $DEVELOP_BACKEND; set $dev_dashboard $DEVELOP_DASHBOARD; + set $dev_studio_root $DEVELOP_STUDIO_ROOT; server_name studio.localhost; @@ -106,6 +107,20 @@ http { } } + location ^~ /designer/frontend/studio-root/ { + proxy_redirect off; + proxy_set_header Host $host; + if ($dev_studio_root) { + add_header X-Dashboard-Source webpackDash; + rewrite /designer/frontend/studio-root/(.*) /$1 break; + proxy_pass http://host.docker.internal:2002; + } + if ($dev_studio_root != 1) { + add_header X-Dashboard-Source dockerDash; + proxy_pass http://studio-designer:6000; + } + } + location ^~ /previewHub { proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; diff --git a/development/utils/ensure-dot-env.js b/development/utils/ensure-dot-env.js index 7a26e390780..9de07b5013b 100644 --- a/development/utils/ensure-dot-env.js +++ b/development/utils/ensure-dot-env.js @@ -4,7 +4,7 @@ const os = require('os'); const getCommit = require('./git-commit-hash'); const randomPass = () => [Math.random().toString(36).substring(2, 5), Math.random().toString(36).substring(2, 5)].join( - 'DIG@' + 'DIG@', ); const defaultEnvVars = { @@ -14,6 +14,7 @@ const defaultEnvVars = { DEVELOP_BACKEND: 0, DEVELOP_DASHBOARD: 0, DEVELOP_PREVIEW: 0, + DEVELOP_STUDIO_ROOT: 0, GITEA_ADMIN_PASS: randomPass(), GITEA_ADMIN_USER: 'localgiteaadmin', GITEA_CYPRESS_USER: 'cypress_testuser', diff --git a/docker-compose.yml b/docker-compose.yml index 63db112694b..2e45879efe9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -version: "3.8" +version: '3.8' volumes: designer-git-data: @@ -15,7 +15,7 @@ services: build: context: ./development/azure-devops-mock ports: - - "6161:6161" + - '6161:6161' environment: - DESIGNER_HOST=studio-loadbalancer @@ -24,13 +24,14 @@ services: container_name: studio-loadbalancer restart: always ports: - - "80:80" + - '80:80' environment: - DEVELOP_APP_DEVELOPMENT=${DEVELOP_APP_DEVELOPMENT:-0} - DEVELOP_RESOURCE_ADMIN=${DEVELOP_RESOURCE_ADMIN:-0} - DEVELOP_BACKEND=${DEVELOP_BACKEND:-0} - DEVELOP_DASHBOARD=${DEVELOP_DASHBOARD:-0} - DEVELOP_PREVIEW=${DEVELOP_PREVIEW:-0} + - DEVELOP_STUDIO_ROOT=${DEVELOP_STUDIO_ROOT:-0} - NGINX_ENVSUBST_OUTPUT_DIR=/etc/nginx/ - NGINX_ENVSUBST_TEMPLATE_SUFFIX=.conf - NGINX_HOST=localhost @@ -39,7 +40,7 @@ services: depends_on: - studio_repositories extra_hosts: - - "host.docker.internal:host-gateway" + - 'host.docker.internal:host-gateway' volumes: - ./frontend/language/src:/www-root/designer/frontend/lang:ro - ./development/load-balancer:/etc/nginx/templates/:ro @@ -50,8 +51,8 @@ services: image: designer:${COMMIT:-latest} restart: always volumes: - - "designer-git-data:/AltinnCore/Repos" - - "keys:/mnt/keys" + - 'designer-git-data:/AltinnCore/Repos' + - 'keys:/mnt/keys' environment: - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_URLS=http://+:6000 @@ -77,22 +78,22 @@ services: - TestdataRepositorySettings:RepositoryLocation=/Testdata - Integrations:AzureDevOpsSettings:BaseUri=http://studio-azure-mock:6161/_apis/ ports: - - "6000:6000" + - '6000:6000' depends_on: - studio_repositories build: context: . extra_hosts: - - "host.docker.internal:host-gateway" + - 'host.docker.internal:host-gateway' studio_repositories: container_name: studio-repositories image: repositories:latest restart: always volumes: - - "gitea-git-data:/var/lib/gitea/git" - - "gitea-avatars-data:/var/lib/gitea/avatars" - - "gitea-attachments-data:/var/lib/gitea/attachments" + - 'gitea-git-data:/var/lib/gitea/git' + - 'gitea-avatars-data:/var/lib/gitea/avatars' + - 'gitea-attachments-data:/var/lib/gitea/attachments' environment: - GITEA____RUN_MODE=prod - GITEA__database__DB_TYPE=postgres @@ -116,12 +117,12 @@ services: - USER_GID=1000 - USER_UID=1000 ports: - - "3000:3000" - - "222:22" + - '3000:3000' + - '222:22' build: context: ./gitea/ extra_hosts: - - "host.docker.internal:host-gateway" + - 'host.docker.internal:host-gateway' studio_db: container_name: studio-db @@ -132,7 +133,7 @@ services: POSTGRES_USER: designer_admin POSTGRES_DB: designerdb ports: - - "5432:5432" + - '5432:5432' volumes: - ./development/db/init.sql:/docker-entrypoint-initdb.d/init.sql - ./development/db/data.sql:/data.sql diff --git a/frontend/app-development/features/administration/components/Administration.module.css b/frontend/app-development/features/administration/components/Administration.module.css index ee983f812c1..7b9fdd3fc04 100644 --- a/frontend/app-development/features/administration/components/Administration.module.css +++ b/frontend/app-development/features/administration/components/Administration.module.css @@ -4,25 +4,6 @@ flex-direction: column; } -.pageContainer { - background-color: #e6eff8; - flex: 1; - position: relative; -} - -.pageContainer::before { - background-image: url('/designer/img/overview-background.svg'); - background-size: 768px auto; - background-repeat: no-repeat; - - content: ' '; - display: block; - position: absolute; - margin: auto; - width: 100%; - height: 100%; -} - .container { margin: auto; padding: var(--fds-spacing-12); @@ -39,50 +20,34 @@ color: var(--primary-color-700); } -.content { +.panel { background-color: white; border-radius: 4px; box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.05); - display: flex; - flex-direction: column; padding: var(--fds-spacing-10); position: relative; - gap: var(--fds-spacing-10); } -.main, -.aside { +.content { display: flex; flex-direction: column; gap: var(--fds-spacing-10); } -.main { - flex: 5; -} - +.main, .aside { - flex: 3; -} - -@media (min-width: 1200px) { - .main { - flex: 1; - } - - .aside { - flex: unset; - width: 350px; - } + display: flex; + flex-direction: column; + gap: var(--fds-spacing-10); } -.mainBlock { +.mainSection { background-color: #f4f5f6; border-radius: 4px; padding: var(--fds-spacing-4); } -.asideBlock { +.asideSection { background-color: #fbfbfc; border: solid 2px #efefef; border-radius: 4px; @@ -90,20 +55,10 @@ padding: var(--fds-spacing-4); } -.divider { - background-color: #efefef; - border: none; - display: none; - height: 2px; - opacity: 0.5; - width: 100%; -} - -@media (min-width: 768px) { - .pageContainer::before { - background-position: center 0; - background-size: 100% auto; - } +.footer { + border-top: 2px solid #efefef; + margin-top: var(--fds-spacing-10); + padding-top: var(--fds-spacing-8); } @media (min-width: 960px) { @@ -111,7 +66,29 @@ flex-direction: row; } + .main { + flex: 1; + } + + .aside { + width: 275px; + } + .divider { display: block; } + + .footer { + width: calc(100% - 275px - var(--fds-spacing-10)); + } +} + +@media (min-width: 1200px) { + .aside { + width: 350px; + } + + .footer { + width: calc(100% - 350px - var(--fds-spacing-10)); + } } diff --git a/frontend/app-development/features/administration/components/Administration.tsx b/frontend/app-development/features/administration/components/Administration.tsx index c6547a87b1c..a1d2be2c366 100644 --- a/frontend/app-development/features/administration/components/Administration.tsx +++ b/frontend/app-development/features/administration/components/Administration.tsx @@ -2,7 +2,7 @@ import React from 'react'; import classes from './Administration.module.css'; import { useAppConfigQuery, useOrgListQuery } from 'app-development/hooks/queries'; import { useStudioUrlParams } from 'app-shared/hooks/useStudioUrlParams'; -import { Heading } from '@digdir/design-system-react'; +import { Heading, Link } from '@digdir/design-system-react'; import { toast } from 'react-toastify'; import { useTranslation } from 'react-i18next'; import { Documentation } from './Documentation'; @@ -12,6 +12,7 @@ import { Navigation } from './Navigation'; import { AltinnSpinner } from 'app-shared/components'; import { Center } from 'app-shared/components/Center'; import { News } from './News'; +import { PageContainer } from 'app-shared/components/PageContainer/PageContainer'; export const Administration = () => { const { org, app } = useStudioUrlParams(); @@ -44,35 +45,40 @@ export const Administration = () => { } return ( -
+
-
+
{appConfigData?.serviceName || app} -
-
-
-
- -
- {hasEnvironments && ( -
- -
- )} -
- -
-
- + +
+
+
+
+ +
+ {hasEnvironments && ( +
+ +
+ )} +
+ +
+
+ +
+
+ {t('general.contact')} +
-
+
); }; diff --git a/frontend/app-development/features/administration/components/News.module.css b/frontend/app-development/features/administration/components/News.module.css index 1c0ea62c7c4..949ce0471a3 100644 --- a/frontend/app-development/features/administration/components/News.module.css +++ b/frontend/app-development/features/administration/components/News.module.css @@ -1,16 +1,19 @@ .news { - height: 380px; + max-height: 380px; overflow-y: scroll; padding-top: 24px; border-top: #efefef solid 1px; } .newsContent { - margin-bottom: var(--fds-spacing-4); background-color: var(--fds-semantic-surface-warning-subtle); padding: var(--fds-spacing-3) var(--fds-spacing-6); } +.newsContent:not(:last-child) { + margin-bottom: var(--fds-spacing-4); +} + .newsContainer { padding-top: 24px; } diff --git a/frontend/app-development/layout/AppShell.tsx b/frontend/app-development/layout/AppShell.tsx index c714c83d2bc..880575ba03b 100644 --- a/frontend/app-development/layout/AppShell.tsx +++ b/frontend/app-development/layout/AppShell.tsx @@ -3,7 +3,7 @@ import { Outlet, matchPath, useLocation } from 'react-router-dom'; import { PageHeader } from './PageHeader'; import { useRepoStatusQuery, useUserQuery } from 'app-shared/hooks/queries'; import { ServerCodes } from 'app-shared/enums/ServerCodes'; -import { NotFoundPage } from 'app-development/features/notFound'; +import { NotFoundPage } from 'app-shared/components/notFound'; import { PageSpinner } from 'app-shared/components'; import { Center } from 'app-shared/components/Center'; import { MergeConflictWarning } from '../features/simpleMerge/MergeConflictWarning'; diff --git a/frontend/app-development/router/PageRoutes.tsx b/frontend/app-development/router/PageRoutes.tsx index e359a68044a..7369bb2686e 100644 --- a/frontend/app-development/router/PageRoutes.tsx +++ b/frontend/app-development/router/PageRoutes.tsx @@ -4,7 +4,7 @@ import { Navigate, Route, Routes } from 'react-router-dom'; import { AppShell } from 'app-development/layout/AppShell'; import { RoutePaths } from 'app-development/enums/RoutePaths'; import { routerRoutes } from 'app-development/router/routes'; -import { NotFoundPage } from 'app-development/features/notFound'; +import { NotFoundPage } from 'app-shared/components/notFound'; const BASE_PATH = '/:org/:app'; diff --git a/frontend/app-development/router/routes.tsx b/frontend/app-development/router/routes.tsx index 4cabf7a3e4f..87ff5d5afdd 100644 --- a/frontend/app-development/router/routes.tsx +++ b/frontend/app-development/router/routes.tsx @@ -7,7 +7,6 @@ import { DeployPage } from '../features/appPublish/pages/deployPage'; import { ProcessEditor } from 'app-development/features/processEditor'; import { shouldDisplayFeature } from 'app-shared/utils/featureToggleUtils'; import { RoutePaths } from 'app-development/enums/RoutePaths'; -import { TopBarMenu } from 'app-shared/enums/TopBarMenu'; interface IRouteProps { headerTextKey?: string; @@ -24,54 +23,33 @@ interface IRouteProps { interface RouterRoute { path: RoutePaths; - exact: boolean; - activeSubHeaderSelection: TopBarMenu; subapp: any; - activeLeftMenuSelection?: string; props?: IRouteProps; } export const routerRoutes: RouterRoute[] = [ { path: RoutePaths.UIEditor, - exact: true, - activeSubHeaderSelection: TopBarMenu.Create, - activeLeftMenuSelection: 'UI-Editor', subapp: SubApp, }, { path: RoutePaths.Overview, - exact: true, - activeSubHeaderSelection: TopBarMenu.About, - activeLeftMenuSelection: 'Om appen', subapp: shouldDisplayFeature('newAdministration') ? Administration : LegacyAdministration, }, { path: RoutePaths.Datamodel, - exact: true, - activeSubHeaderSelection: TopBarMenu.Datamodel, - activeLeftMenuSelection: '', subapp: DataModellingContainer, }, { path: RoutePaths.Deploy, - exact: true, - activeSubHeaderSelection: TopBarMenu.Deploy, - activeLeftMenuSelection: '', subapp: DeployPage, }, { - activeSubHeaderSelection: TopBarMenu.Text, - activeLeftMenuSelection: 'Tekster', path: RoutePaths.Text, - exact: true, subapp: TextEditor, }, { - activeSubHeaderSelection: TopBarMenu.ProcessEditor, - activeLeftMenuSelection: '', path: RoutePaths.ProcessEditor, - exact: true, subapp: ProcessEditor, }, ]; diff --git a/frontend/dashboard/app/App.module.css b/frontend/dashboard/app/App.module.css index 8100903fc4a..da90c922d25 100644 --- a/frontend/dashboard/app/App.module.css +++ b/frontend/dashboard/app/App.module.css @@ -3,12 +3,9 @@ display: grid; grid-template-rows: auto 1fr; } -.root > header { - background-color: rgb(2, 47, 81); -} -.appDashboardSpinner{ +.appDashboardSpinner { height: 100vh; justify-content: center; align-items: center; -} \ No newline at end of file +} diff --git a/frontend/language/src/en.json b/frontend/language/src/en.json index 95ce4b35913..dbf6e24cca1 100644 --- a/frontend/language/src/en.json +++ b/frontend/language/src/en.json @@ -242,7 +242,7 @@ "general.disabled": "Disabled", "general.edit": "Edit", "general.enabled": "Enabled", - "general.error_message": "An error has occurred!", + "general.error_message": "An error has occurred! If the problem persists, please contact us.", "general.error_message_with_colon": "Error message:", "general.fetch_error_message": "Failed to load required data for this page. Please reload the page to try again", "general.fetch_error_title": "An error occurred while loading", diff --git a/frontend/language/src/nb.json b/frontend/language/src/nb.json index bd55b86fa1c..2b52f091ea2 100644 --- a/frontend/language/src/nb.json +++ b/frontend/language/src/nb.json @@ -131,6 +131,14 @@ "app_release.release_title": "Applikasjonen bygges basert på", "app_release.release_title_link": "siste commit til master", "app_release.release_version": "Versjon:", + "contact.email.content": "Du kan sende skriftlig henvendelse til Altinn servicedesk på e-post ved spørsmål om å opprette organisasjoner eller miljøer, ved tekniske problemer, om dokumentasjonen eller annet.", + "contact.email.heading": "Send e-post", + "contact.email.link": "<0 href=\"mailto:servicedesk@altinn.no\">servicedesk@altinn.no", + "contact.fetch_app_error_message": "Kunne ikke laste inn tittelen for denne appen. Prøv igjen senere.", + "contact.slack.content": "Dersom du har spørsmål om hvordan du bygger en applikasjon, kan du snakke direkte med utviklingsteamet i Altinn Studio på Slack. De kan hjelpe deg med:", + "contact.slack.content_list": "<0>å bygge applikasjonene slik du ønsker<0>svare på spørsmål og veilede<0>ta imot innspill på ny funksjonalitet", + "contact.slack.heading": "Skriv melding i vår Slack-kanal", + "contact.slack.link": "<0 href=\"https://altinn.slack.com\">altinn.slack.com", "dashboard.all_apps": "Alle applikasjoner", "dashboard.all_datamodels": "Alle datamodeller", "dashboard.all_resources": "Alle ressurser", @@ -279,12 +287,14 @@ "general.choose_method": "Velg metode", "general.close": "Lukk", "general.close_schema": "Lukk skjema", + "general.contact": "Kontakt og hjelp", "general.contains": "Inneholder", "general.continue": "Fortsett", "general.control_submit": "Kontroller og send inn", "general.create": "Opprett", "general.create_new": "Lag ny", "general.customer_service_phone_number": "+47 75 00 60 00", + "general.dashboard": "Dashbord", "general.dataModel": "datamodell", "general.date": "Dato", "general.date_time_format": "{{date}} kl. {{time}}", @@ -293,7 +303,7 @@ "general.disabled": "Deaktivert", "general.edit": "Endre", "general.enabled": "Aktivert", - "general.error_message": "Det har oppstått en feil!", + "general.error_message": "Det har oppstått en feil! Om problemet vedvarer, ta kontakt med oss.", "general.error_message_with_colon": "Feilmelding:", "general.false": "Usann", "general.fetch_error_message": "Kunne ikke laste inn nødvendig data for denne siden. Last siden på nytt for å prøve igjen.", diff --git a/frontend/packages/process-editor/img/ConfirmationTask.svg b/frontend/packages/process-editor/img/ConfirmationTask.svg new file mode 100644 index 00000000000..7f6e45f5466 --- /dev/null +++ b/frontend/packages/process-editor/img/ConfirmationTask.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/packages/process-editor/img/DataTask.svg b/frontend/packages/process-editor/img/DataTask.svg new file mode 100644 index 00000000000..de0e0897a6a --- /dev/null +++ b/frontend/packages/process-editor/img/DataTask.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/packages/process-editor/img/FeedbackTask.svg b/frontend/packages/process-editor/img/FeedbackTask.svg new file mode 100644 index 00000000000..af1a896b68f --- /dev/null +++ b/frontend/packages/process-editor/img/FeedbackTask.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/packages/process-editor/img/PaymentTask.svg b/frontend/packages/process-editor/img/PaymentTask.svg new file mode 100644 index 00000000000..2b7d3cc0831 --- /dev/null +++ b/frontend/packages/process-editor/img/PaymentTask.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/packages/process-editor/img/SignTask.svg b/frontend/packages/process-editor/img/SignTask.svg new file mode 100644 index 00000000000..ba6fe324e73 --- /dev/null +++ b/frontend/packages/process-editor/img/SignTask.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/packages/process-editor/src/bpmnProviders/SupportedPaletteProvider.js b/frontend/packages/process-editor/src/bpmnProviders/SupportedPaletteProvider.js index 3d99b882af7..ea04760e6ca 100644 --- a/frontend/packages/process-editor/src/bpmnProviders/SupportedPaletteProvider.js +++ b/frontend/packages/process-editor/src/bpmnProviders/SupportedPaletteProvider.js @@ -1,9 +1,4 @@ -const supportedEntries = [ - 'create.exclusive-gateway', - 'create.start-event', - 'create.end-event', - 'create.task', -]; +const supportedEntries = ['create.exclusive-gateway', 'create.start-event', 'create.end-event']; class SupportedPaletteProvider { constructor(bpmnFactory, create, elementFactory, palette, translate, modeling) { @@ -87,7 +82,7 @@ class SupportedPaletteProvider { const customEntries = { 'create.altinn-data-task': { group: 'activity', - className: 'bpmn-icon-task', + className: 'bpmn-icon-task-generic bpmn-icon-data-task', title: translate('Create Altinn Data Task'), action: { dragstart: createCustomTask('data'), @@ -96,7 +91,7 @@ class SupportedPaletteProvider { 'create.altinn-confirmation-task': { group: 'activity', title: translate('Create Altinn Confirm Task'), - className: 'bpmn-icon-task', + className: 'bpmn-icon-task-generic bpmn-icon-confirmation-task', action: { dragstart: createCustomTask('confirmation'), }, @@ -104,14 +99,14 @@ class SupportedPaletteProvider { 'create.altinn-feedback-task': { group: 'activity', title: translate('Create Altinn Feedback Task'), - className: 'bpmn-icon-task', + className: 'bpmn-icon-task-generic bpmn-icon-feedback-task', action: { dragstart: createCustomTask('feedback'), }, }, 'create.altinn-signing-task': { group: 'activity', - className: 'bpmn-icon-task', + className: 'bpmn-icon-task-generic bpmn-icon-signing-task', title: translate('Create Altinn signing Task'), action: { dragstart: createCustomSigningTask(), diff --git a/frontend/packages/process-editor/src/components/Canvas/Canvas.module.css b/frontend/packages/process-editor/src/components/Canvas/Canvas.module.css index cbc6b49fdc0..4ad93267683 100644 --- a/frontend/packages/process-editor/src/components/Canvas/Canvas.module.css +++ b/frontend/packages/process-editor/src/components/Canvas/Canvas.module.css @@ -3,6 +3,11 @@ box-sizing: border-box; } +.canvasContainer { + height: 100vh; + --svg-data-task-icon: url('data:image/svg+xml,'); +} + .canvasMenuContainer { composes: container; justify-content: space-between; @@ -15,7 +20,35 @@ font-weight: 500; } -.alertContainer { +.alertContainer { composes: container; max-width: 780px; -} \ No newline at end of file +} + +div[class*='bpmn-icon-task-generic'] { + background-color: black; +} + +div[class*='bpmn-icon-task-generic']:hover { + background-color: #0086e6; +} + +div[class*='bpmn-icon-data-task'] { + -webkit-mask: url('../../../img/DataTask.svg') 0 0 /46px 46px; + mask: url('../../../img/DataTask.svg') 0 0 /46px 46px; +} + +div[class*='bpmn-icon-confirmation-task'] { + -webkit-mask: url('../../../img/ConfirmationTask.svg') 0 0/46px 46px; + mask: url('../../../img/ConfirmationTask.svg') 0 0/46px 46px; +} + +div[class*='bpmn-icon-feedback-task'] { + -webkit-mask: url('../../../img/FeedbackTask.svg') 0 0/46px 46px; + mask: url('../../../img/FeedbackTask.svg') 0 0/46px 46px; +} + +div[class*='bpmn-icon-signing-task'] { + -webkit-mask: url('../../../img/SignTask.svg') 0 0/46px 46px; + mask: url('../../../img/SignTask.svg') 0 0/46px 46px; +} diff --git a/frontend/packages/process-editor/src/components/Canvas/Canvas.tsx b/frontend/packages/process-editor/src/components/Canvas/Canvas.tsx index e663aa644ed..2f7158670f4 100644 --- a/frontend/packages/process-editor/src/components/Canvas/Canvas.tsx +++ b/frontend/packages/process-editor/src/components/Canvas/Canvas.tsx @@ -87,7 +87,7 @@ export const Viewer = (): JSX.Element => { const Editor = (): JSX.Element => { const { canvasRef } = useBpmnEditor(); - return
; + return
; }; type CanvasActionsProps = { diff --git a/frontend/packages/shared/src/components/ErrorBoundaryFallback.tsx b/frontend/packages/shared/src/components/ErrorBoundaryFallback.tsx index d8bc4c024de..1ce597c4e53 100644 --- a/frontend/packages/shared/src/components/ErrorBoundaryFallback.tsx +++ b/frontend/packages/shared/src/components/ErrorBoundaryFallback.tsx @@ -1,14 +1,14 @@ import React from 'react'; import { useErrorBoundary } from 'react-error-boundary'; -import { useTranslation } from 'react-i18next'; +import { Trans, useTranslation } from 'react-i18next'; import { _useIsProdHack } from 'app-shared/utils/_useIsProdHack'; -import { Alert, Button, ErrorMessage, Paragraph } from '@digdir/design-system-react'; +import { Alert, Button, ErrorMessage, Link, Paragraph } from '@digdir/design-system-react'; import { Center } from './Center'; import classes from './ErrorBoundaryFallback.module.css'; export type ErrorBoundaryFallbackProps = { error: Error; -} +}; export const ErrorBoundaryFallback = ({ error }: ErrorBoundaryFallbackProps) => { const { t } = useTranslation(); @@ -17,10 +17,17 @@ export const ErrorBoundaryFallback = ({ error }: ErrorBoundaryFallbackProps) => return (
- {t('general.error_message')} + + }} + /> + {!_useIsProdHack && {error.message}}
- +
diff --git a/frontend/packages/shared/src/components/PageContainer/PageContainer.module.css b/frontend/packages/shared/src/components/PageContainer/PageContainer.module.css new file mode 100644 index 00000000000..6b414bcd0e0 --- /dev/null +++ b/frontend/packages/shared/src/components/PageContainer/PageContainer.module.css @@ -0,0 +1,25 @@ +.pageContainer { + background-color: #e6eff8; + flex: 1; + position: relative; +} + +.pageContainer::before { + background-image: url('/designer/img/page-background.svg'); + background-size: 768px auto; + background-repeat: no-repeat; + + content: ' '; + display: block; + position: absolute; + margin: auto; + width: 100%; + height: 100%; +} + +@media (min-width: 768px) { + .pageContainer::before { + background-position: center 0; + background-size: 100% auto; + } +} diff --git a/frontend/packages/shared/src/components/PageContainer/PageContainer.tsx b/frontend/packages/shared/src/components/PageContainer/PageContainer.tsx new file mode 100644 index 00000000000..8af96a50270 --- /dev/null +++ b/frontend/packages/shared/src/components/PageContainer/PageContainer.tsx @@ -0,0 +1,10 @@ +import React, { ReactNode } from 'react'; +import classes from './PageContainer.module.css'; + +type PageContainerProps = { + children: ReactNode; +}; + +export const PageContainer = ({ children }: PageContainerProps) => { + return
{children}
; +}; diff --git a/frontend/app-development/features/notFound/NotFoundPage.module.css b/frontend/packages/shared/src/components/notFound/NotFoundPage.module.css similarity index 100% rename from frontend/app-development/features/notFound/NotFoundPage.module.css rename to frontend/packages/shared/src/components/notFound/NotFoundPage.module.css diff --git a/frontend/app-development/features/notFound/NotFoundPage.test.tsx b/frontend/packages/shared/src/components/notFound/NotFoundPage.test.tsx similarity index 92% rename from frontend/app-development/features/notFound/NotFoundPage.test.tsx rename to frontend/packages/shared/src/components/notFound/NotFoundPage.test.tsx index c7d6bdfba13..100ee026005 100644 --- a/frontend/app-development/features/notFound/NotFoundPage.test.tsx +++ b/frontend/packages/shared/src/components/notFound/NotFoundPage.test.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import { NotFoundPage } from './NotFoundPage'; -import { textMock } from '../../../testing/mocks/i18nMock'; +import { textMock } from '../../../../../testing/mocks/i18nMock'; describe('NotFoundPage', () => { it('renders correctly', () => { diff --git a/frontend/app-development/features/notFound/NotFoundPage.tsx b/frontend/packages/shared/src/components/notFound/NotFoundPage.tsx similarity index 100% rename from frontend/app-development/features/notFound/NotFoundPage.tsx rename to frontend/packages/shared/src/components/notFound/NotFoundPage.tsx diff --git a/frontend/app-development/features/notFound/images/Background404.png b/frontend/packages/shared/src/components/notFound/images/Background404.png similarity index 100% rename from frontend/app-development/features/notFound/images/Background404.png rename to frontend/packages/shared/src/components/notFound/images/Background404.png diff --git a/frontend/app-development/features/notFound/images/PCImage404.png b/frontend/packages/shared/src/components/notFound/images/PCImage404.png similarity index 100% rename from frontend/app-development/features/notFound/images/PCImage404.png rename to frontend/packages/shared/src/components/notFound/images/PCImage404.png diff --git a/frontend/app-development/features/notFound/index.ts b/frontend/packages/shared/src/components/notFound/index.ts similarity index 100% rename from frontend/app-development/features/notFound/index.ts rename to frontend/packages/shared/src/components/notFound/index.ts diff --git a/frontend/packages/shared/src/constants.js b/frontend/packages/shared/src/constants.js index 45b632d0b55..9622caf8a23 100644 --- a/frontend/packages/shared/src/constants.js +++ b/frontend/packages/shared/src/constants.js @@ -2,6 +2,7 @@ export const APP_DEVELOPMENT_BASENAME = '/editor'; export const DASHBOARD_BASENAME = '/dashboard'; export const RESOURCEADM_BASENAME = '/resourceadm'; export const PREVIEW_BASENAME = '/preview'; +export const STUDIO_ROOT_BASENAME = '/'; export const DEFAULT_LANGUAGE = 'nb'; export const BASE_CONTAINER_ID = '__base__'; export const DEPLOYMENTS_REFETCH_INTERVAL = 15000; diff --git a/frontend/packages/shared/src/contexts/ServicesContext.tsx b/frontend/packages/shared/src/contexts/ServicesContext.tsx index 4e8897ff78d..72d0adb180b 100644 --- a/frontend/packages/shared/src/contexts/ServicesContext.tsx +++ b/frontend/packages/shared/src/contexts/ServicesContext.tsx @@ -15,13 +15,14 @@ import { ToastContainer, Slide, toast } from 'react-toastify'; import { ErrorBoundary } from 'react-error-boundary'; import { AxiosError } from 'axios'; import type { i18n } from 'i18next'; -import { useTranslation } from 'react-i18next'; +import { Trans, useTranslation } from 'react-i18next'; import { ErrorBoundaryFallback } from '../components/ErrorBoundaryFallback'; import { ApiError } from 'app-shared/types/api/ApiError'; import 'react-toastify/dist/ReactToastify.css'; import 'app-shared/styles/toast.css'; import { userLogoutAfterPath } from 'app-shared/api/paths'; +import { Link } from '@digdir/design-system-react'; export type ServicesContextProps = typeof queries & typeof mutations; export type ServicesContextProviderProps = ServicesContextProps & { @@ -62,7 +63,21 @@ const handleError = ( } } - toast.error(() => t('general.error_message'), { toastId: 'default' }); + toast.error( + () => ( + + {' '} + + ), + }} + /> + ), + { toastId: 'default' }, + ); }; export const ServicesContextProvider = ({ diff --git a/frontend/packages/shared/src/icons/Slack.svg b/frontend/packages/shared/src/icons/Slack.svg new file mode 100644 index 00000000000..6d92ff70022 --- /dev/null +++ b/frontend/packages/shared/src/icons/Slack.svg @@ -0,0 +1,26 @@ + + + + + + + + + + diff --git a/frontend/packages/shared/src/navigation/main-header/Header.module.css b/frontend/packages/shared/src/navigation/main-header/Header.module.css index 247784083e1..f65e28c6e93 100644 --- a/frontend/packages/shared/src/navigation/main-header/Header.module.css +++ b/frontend/packages/shared/src/navigation/main-header/Header.module.css @@ -1,6 +1,7 @@ -.appBar { +header.appBar { background-color: #022f51; box-shadow: none; + z-index: 1; } .toolbar { diff --git a/frontend/packages/shared/src/navigation/main-header/Header.tsx b/frontend/packages/shared/src/navigation/main-header/Header.tsx index 36f42800750..f267de8e5b0 100644 --- a/frontend/packages/shared/src/navigation/main-header/Header.tsx +++ b/frontend/packages/shared/src/navigation/main-header/Header.tsx @@ -12,7 +12,7 @@ export enum SelectedContextType { } export interface IHeaderContext { - selectableOrgs: IGiteaOrganisation[]; + selectableOrgs?: IGiteaOrganisation[]; user: IUser; } @@ -31,7 +31,11 @@ export const getOrgUsernameByUsername = (username: string, orgs: IGiteaOrganisat return org?.username; }; -export function Header() { +export type HeaderProps = { + showMenu?: boolean; +}; + +export function Header({ showMenu = true }: HeaderProps) { const { selectableOrgs } = React.useContext(HeaderContext); const selectedContext = useSelectedContext(); @@ -55,9 +59,11 @@ export function Header() { )} - - - + {showMenu && ( + + + + )} diff --git a/frontend/packages/shared/src/navigation/main-header/HeaderMenu.tsx b/frontend/packages/shared/src/navigation/main-header/HeaderMenu.tsx index f5bb54d4edb..5660aa7559a 100644 --- a/frontend/packages/shared/src/navigation/main-header/HeaderMenu.tsx +++ b/frontend/packages/shared/src/navigation/main-header/HeaderMenu.tsx @@ -13,7 +13,6 @@ import * as testids from '../../../../../testing/testids'; export type HeaderMenuProps = { org: string; repo?: string; - user?: any; }; export function HeaderMenu({ org, repo }: HeaderMenuProps) { @@ -49,7 +48,7 @@ export function HeaderMenu({ org, repo }: HeaderMenuProps) { }; const getRepoPath = () => { - const owner = org || user.login; + const owner = org || user?.login; if (owner && repo) { return repositoryPath(owner, repo); } @@ -64,7 +63,7 @@ export function HeaderMenu({ org, repo }: HeaderMenuProps) { - {user.full_name || user.login}{' '} + {user?.full_name || user?.login}{' '} {selectedContext !== SelectedContextType.All && selectedContext !== SelectedContextType.Self && ( <> @@ -77,7 +76,7 @@ export function HeaderMenu({ org, repo }: HeaderMenuProps) { @@ -109,7 +108,7 @@ export function HeaderMenu({ org, repo }: HeaderMenuProps) { selected={selectedContext === SelectedContextType.Self} onClick={() => handleSetSelectedContext('')} > - {user.full_name || user.login} + {user?.full_name || user?.login} diff --git a/frontend/resourceadm/app/App.module.css b/frontend/resourceadm/app/App.module.css index caded1c185a..de9317a4955 100644 --- a/frontend/resourceadm/app/App.module.css +++ b/frontend/resourceadm/app/App.module.css @@ -3,7 +3,3 @@ flex-direction: column; min-height: 100vh; } - -.root > header { - background-color: rgb(2, 47, 81); -} diff --git a/frontend/studio-root/README.md b/frontend/studio-root/README.md new file mode 100644 index 00000000000..a80302a3d02 --- /dev/null +++ b/frontend/studio-root/README.md @@ -0,0 +1,28 @@ +This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). + +## Available Scripts + +In the project directory, you can run: + +### `yarn start` + +Runs the app in the development mode.
+Open [http://localhost:2002](http://localhost:2002) to view it in the browser. + +The page will reload if you make edits.
+You will also see any lint errors in the console. + +### `yarn test` + +Launches the test runner in the interactive watch mode.
+See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. + +### `yarn run build` + +Builds the app for production to the `build` folder.
+It correctly bundles React in production mode and optimizes the build for the best performance. + +The build is minified and the filenames include the hashes.
+Your app is ready to be deployed! + +See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. diff --git a/frontend/studio-root/app/App.css b/frontend/studio-root/app/App.css new file mode 100644 index 00000000000..e4a882146a0 --- /dev/null +++ b/frontend/studio-root/app/App.css @@ -0,0 +1,31 @@ +:root { + --studio-font-family: 'Inter', sans-serif; +} + +html, +body { + margin: 0; + padding: 0; + font-family: Roboto, 'San Fransisco', 'Helvetica Neue', Helvetica, Arial, sans-serif; + height: 100%; + background: #fff; + font-size: 1rem; +} + +h1, +h2, +h3 { + letter-spacing: 2px; +} + +a { + text-decoration: none; +} + +::-webkit-scrollbar { + width: 5px; +} +::-webkit-scrollbar-thumb { + background: darkgray; + border-radius: 5px; +} diff --git a/frontend/studio-root/app/App.module.css b/frontend/studio-root/app/App.module.css new file mode 100644 index 00000000000..f28a87cf4bb --- /dev/null +++ b/frontend/studio-root/app/App.module.css @@ -0,0 +1,5 @@ +.root { + height: 100vh; + display: grid; + grid-template-rows: auto 1fr; +} diff --git a/frontend/studio-root/app/App.tsx b/frontend/studio-root/app/App.tsx new file mode 100644 index 00000000000..2b2c5d0e02a --- /dev/null +++ b/frontend/studio-root/app/App.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import classes from './App.module.css'; +import { Route, Routes } from 'react-router-dom'; +import { NotFoundPage } from 'app-shared/components/notFound'; + +import './App.css'; +import { PageLayout } from '../pages/PageLayout'; +import { Contact } from '../pages/Contact/Contact'; + +export const App = (): JSX.Element => { + return ( +
+ + }> + } /> + } /> + + +
+ ); +}; diff --git a/frontend/studio-root/index.tsx b/frontend/studio-root/index.tsx new file mode 100644 index 00000000000..e0558fdc231 --- /dev/null +++ b/frontend/studio-root/index.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { createRoot } from 'react-dom/client'; +import { BrowserRouter } from 'react-router-dom'; +import { STUDIO_ROOT_BASENAME } from 'app-shared/constants'; +import { App } from './app/App'; +import i18next from 'i18next'; +import { initReactI18next } from 'react-i18next'; +import nb from '../language/src/nb.json'; +import en from '../language/src/en.json'; +import { DEFAULT_LANGUAGE } from 'app-shared/constants'; +import { QueryClientConfig } from '@tanstack/react-query'; +import { ServicesContextProvider } from 'app-shared/contexts/ServicesContext'; +import * as queries from 'app-shared/api/queries'; +import * as mutations from 'app-shared/api/mutations'; +import 'app-shared/design-tokens'; + +i18next.use(initReactI18next).init({ + lng: DEFAULT_LANGUAGE, + resources: { + nb: { translation: nb }, + en: { translation: en }, + }, + fallbackLng: 'nb', +}); + +const container = document.getElementById('root'); +const root = createRoot(container); + +const queryClientConfig: QueryClientConfig = { + defaultOptions: { + queries: { + retry: false, + refetchOnWindowFocus: false, + }, + }, +}; + +root.render( + + + + + , +); diff --git a/frontend/studio-root/jest.config.js b/frontend/studio-root/jest.config.js new file mode 100644 index 00000000000..ab77700c819 --- /dev/null +++ b/frontend/studio-root/jest.config.js @@ -0,0 +1 @@ +module.exports = require('../jest.config'); diff --git a/frontend/studio-root/package.json b/frontend/studio-root/package.json new file mode 100644 index 00000000000..7c68c8864c1 --- /dev/null +++ b/frontend/studio-root/package.json @@ -0,0 +1,14 @@ +{ + "name": "studio-root", + "version": "0.1.0", + "author": "Altinn", + "license": "3-Clause BSD", + "private": true, + "scripts": { + "build": "cross-env NODE_ENV=production webpack --config ../webpack.config.prod.js", + "start": "yarn typecheck:watch & cross-env NODE_ENV=development webpack-dev-server --config ../webpack.config.dev.js --mode development", + "test": "jest --maxWorkers=50%", + "typecheck": "npx tsc --noEmit", + "typecheck:watch": "tsc --noEmit -w" + } +} diff --git a/frontend/studio-root/pages/Contact/Contact.module.css b/frontend/studio-root/pages/Contact/Contact.module.css new file mode 100644 index 00000000000..7c351387756 --- /dev/null +++ b/frontend/studio-root/pages/Contact/Contact.module.css @@ -0,0 +1,71 @@ +:root { + --primary-color-700: #022f51; +} + +.container { + background-color: var(--colors-white); + border-radius: 4px; + box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.05); + display: flex; + flex-direction: column; + font-family: var(--studio-font-family); + position: relative; + + margin: var(--fds-spacing-10) auto; + max-width: 1230px; +} + +.content { + display: flex; + flex-direction: column; + gap: var(--fds-spacing-6); + margin: var(--fds-spacing-12) var(--fds-spacing-8); +} + +.section { + background-color: var(--colors-grey-100); + border-radius: 4px; + display: flex; + gap: var(--fds-spacing-6); + padding: var(--fds-spacing-8); + max-width: 735px; +} + +.iconContainer { + background-color: var(--primary-color-700); + border-radius: 100%; + color: var(--colors-white); + display: flex; + align-items: center; + justify-content: center; + height: 60px; + width: 60px; +} + +.icon { + font-size: 2.5rem; +} + +.textContainer { + flex: 1; +} + +.link a { + font-size: 1rem; +} + +@media (min-width: 768px) { + .content { + margin: var(--fds-spacing-18); + } + + .section { + padding: var(--fds-spacing-12); + } +} + +@media (min-width: 960px) { + .section { + padding-right: var(--fds-spacing-22); + } +} diff --git a/frontend/studio-root/pages/Contact/Contact.test.tsx b/frontend/studio-root/pages/Contact/Contact.test.tsx new file mode 100644 index 00000000000..cddf7bbaafa --- /dev/null +++ b/frontend/studio-root/pages/Contact/Contact.test.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { screen, render } from '@testing-library/react'; +import { Contact } from './Contact'; +import { textMock } from '../../../testing/mocks/i18nMock'; + +describe('Contact', () => { + it('renders component', async () => { + render(); + + expect(screen.getByRole('heading', { name: textMock('general.contact') })).toBeInTheDocument(); + + expect( + screen.getByRole('heading', { name: textMock('contact.email.heading') }), + ).toBeInTheDocument(); + expect(screen.getByText(textMock('contact.email.content'))).toBeInTheDocument(); + expect(screen.getByText(textMock('contact.email.link'))).toBeInTheDocument(); + + expect( + screen.getByRole('heading', { name: textMock('contact.slack.heading') }), + ).toBeInTheDocument(); + expect(screen.getByText(textMock('contact.slack.content'))).toBeInTheDocument(); + expect(screen.getByText(textMock('contact.slack.content_list'))).toBeInTheDocument(); + expect(screen.getByText(textMock('contact.slack.link'))).toBeInTheDocument(); + }); +}); diff --git a/frontend/studio-root/pages/Contact/Contact.tsx b/frontend/studio-root/pages/Contact/Contact.tsx new file mode 100644 index 00000000000..2e785dd3a97 --- /dev/null +++ b/frontend/studio-root/pages/Contact/Contact.tsx @@ -0,0 +1,62 @@ +import React from 'react'; +import classes from './Contact.module.css'; +import { Heading, Link, Paragraph } from '@digdir/design-system-react'; +import { Trans, useTranslation } from 'react-i18next'; +import { EnvelopeClosedIcon } from '@navikt/aksel-icons'; +import { PageContainer } from 'app-shared/components/PageContainer/PageContainer'; +import Slack from 'app-shared/icons/Slack.svg'; + +export const Contact = () => { + const { t } = useTranslation(); + + return ( + +
+
+
+ + {t('general.contact')} + +
+
+
+ +
+
+ + {t('contact.email.heading')} + + {t('contact.email.content')} + + + + + +
+
+
+
+ +
+
+ + {t('contact.slack.heading')} + + {t('contact.slack.content')} + + +
  • + + + + + + + +
  • +
    +
    +
    +
    + ); +}; diff --git a/frontend/studio-root/pages/PageLayout/PageLayout.tsx b/frontend/studio-root/pages/PageLayout/PageLayout.tsx new file mode 100644 index 00000000000..17ef22e05f6 --- /dev/null +++ b/frontend/studio-root/pages/PageLayout/PageLayout.tsx @@ -0,0 +1,26 @@ +import { HeaderContext } from 'app-shared/navigation/main-header/Header'; +import { Outlet } from 'react-router-dom'; +import { useUserQuery } from 'app-shared/hooks/queries'; +import React, { useMemo } from 'react'; +import type { IHeaderContext } from 'app-shared/navigation/main-header/Header'; +import AppHeader from 'app-shared/navigation/main-header/Header'; + +export const PageLayout = () => { + const { data: user } = useUserQuery(); + + const headerContextValue: IHeaderContext = useMemo( + () => ({ + user, + }), + [user], + ); + + return ( + <> + + + + + + ); +}; diff --git a/frontend/studio-root/pages/PageLayout/index.ts b/frontend/studio-root/pages/PageLayout/index.ts new file mode 100644 index 00000000000..5d502a22ed6 --- /dev/null +++ b/frontend/studio-root/pages/PageLayout/index.ts @@ -0,0 +1 @@ +export { PageLayout } from './PageLayout'; diff --git a/frontend/studio-root/public/index.html b/frontend/studio-root/public/index.html new file mode 100644 index 00000000000..860e912b99f --- /dev/null +++ b/frontend/studio-root/public/index.html @@ -0,0 +1,28 @@ + + + + + + + + + Altinn Studio + + + + + + + + + +
    + + + + diff --git a/frontend/studio-root/tsconfig.json b/frontend/studio-root/tsconfig.json new file mode 100644 index 00000000000..3c43903cfdd --- /dev/null +++ b/frontend/studio-root/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../tsconfig.json" +} diff --git a/frontend/testing/mockend/ports.json b/frontend/testing/mockend/ports.json index cbf77952ffb..01f801d9869 100644 --- a/frontend/testing/mockend/ports.json +++ b/frontend/testing/mockend/ports.json @@ -2,5 +2,6 @@ "app-development": 2004, "resourceadm": 2023, "app-preview": 2005, - "dashboard": 2003 + "dashboard": 2003, + "studio-root": 2002 } diff --git a/package.json b/package.json index 24ad4e28139..7043353f031 100644 --- a/package.json +++ b/package.json @@ -96,6 +96,7 @@ "start-app-preview": "yarn workspace app-preview start", "start-dashboard": "yarn workspace dashboard start", "start-resourceadm": "yarn workspace resourceadm start", + "start-studio-root": "yarn workspace studio-root start", "stats": "node frontend/stats.js", "syncpack": "npx syncpack fix-mismatches && npx syncpack format && npx syncpack set-semver-ranges", "test": "jest --maxWorkers=50% --config=frontend/jest.config.js", @@ -112,6 +113,7 @@ "frontend/dashboard", "frontend/resourceadm", "frontend/language", + "frontend/studio-root", "frontend/testing/mockend", "frontend/testing/cypress", "frontend/packages/schema-editor", diff --git a/yarn.lock b/yarn.lock index a22c285596e..c46d248f91b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16143,6 +16143,12 @@ __metadata: languageName: node linkType: hard +"studio-root@workspace:frontend/studio-root": + version: 0.0.0-use.local + resolution: "studio-root@workspace:frontend/studio-root" + languageName: unknown + linkType: soft + "style-loader@npm:3.3.3": version: 3.3.3 resolution: "style-loader@npm:3.3.3"