├── frontend └── altinn-support-dashboard.client │ ├── index.html.__projection.g.js │ ├── src │ ├── vite-env.d.ts │ ├── components │ │ ├── Dashboard │ │ │ ├── styles │ │ │ │ ├── ContactInfoCell.module.css │ │ │ │ ├── OrganizationList.module.css │ │ │ │ ├── ContactsSearchBar.module.css │ │ │ │ ├── RoleDetails.module.css │ │ │ │ ├── NotificationContactTable.module.css │ │ │ │ ├── DetailedOrgView.module.css │ │ │ │ ├── ERRolesTable.module.css │ │ │ │ ├── OrganizationCard.module.css │ │ │ │ └── ContactsTable.module.css │ │ │ ├── models │ │ │ │ └── mainContentTypes.ts │ │ │ ├── utils │ │ │ │ ├── dateUtils.ts │ │ │ │ ├── personUtils.ts │ │ │ │ └── contactUtils.ts │ │ │ ├── components │ │ │ │ ├── ErrorAlert.tsx │ │ │ │ ├── contacts │ │ │ │ │ ├── ContactsSearchBar.tsx │ │ │ │ │ ├── NotificationContactCell.tsx │ │ │ │ │ ├── ContactInfoCell.tsx │ │ │ │ │ └── NotificationContactTable.tsx │ │ │ │ ├── organizations │ │ │ │ │ └── OrganizationList.tsx │ │ │ │ └── RoleDetails.tsx │ │ │ └── hooks │ │ │ │ └── useAuthorizedFetch.ts │ │ ├── SettingsContent │ │ │ ├── styles │ │ │ │ ├── SettingsVersionComponent.module.css │ │ │ │ ├── SettingsDarkModeComponent.module.css │ │ │ │ ├── SettingsActionsButtons.module.css │ │ │ │ ├── SettingsLanguageComponent.module.css │ │ │ │ └── SettingsPatComponent.module.css │ │ │ ├── models │ │ │ │ └── settingsTypes.ts │ │ │ ├── utils │ │ │ │ ├── apiUtils.ts │ │ │ │ └── versionUtils.ts │ │ │ ├── SettingsActionButtons.tsx │ │ │ ├── SettingsDarkModeComponent.tsx │ │ │ ├── SettingsLanguageComponent.tsx │ │ │ └── SettingsVersionComponent.tsx │ │ ├── InformationDialog │ │ │ ├── styles │ │ │ │ ├── InformationDialogBox.module.css │ │ │ │ └── InformationDialogContent.module.css │ │ │ ├── InformationDialogBox.tsx │ │ │ └── InformationDialogContent.tsx │ │ ├── ManualRoleSearch │ │ │ ├── styles │ │ │ │ ├── ResultTable.module.css │ │ │ │ ├── SearchButton.module.css │ │ │ │ ├── InputComponent.module.css │ │ │ │ ├── EmptySearchButton.module.css │ │ │ │ ├── ManualRoleSearchTextField.module.css │ │ │ │ └── RoleTable.module.css │ │ │ ├── utils │ │ │ │ └── storageUtils.ts │ │ │ ├── ManualRoleSearchTextfield.tsx │ │ │ ├── ManualRoleSearchButton.tsx │ │ │ ├── ManualRoleSearchResult.tsx │ │ │ ├── ManualRoleEmptySearchButton.tsx │ │ │ ├── ManualRoleSearchTable.tsx │ │ │ └── ManualRoleSearchInput.tsx │ │ ├── VersionDialog │ │ │ ├── styles │ │ │ │ ├── VersionDialogTitle.module.css │ │ │ │ ├── VersionDialogButton.module.css │ │ │ │ └── VersionDialogContent.module.css │ │ │ ├── utils │ │ │ │ └── formatDateutils.ts │ │ │ ├── VersionDialogButton.tsx │ │ │ ├── VersionDialog.tsx │ │ │ ├── VersionDialogTitle.tsx │ │ │ ├── VersionDialogBox.tsx │ │ │ └── VersionDialogContent.tsx │ │ ├── Sidebar │ │ │ ├── styles │ │ │ │ ├── SideBarEnvToggle.module.css │ │ │ │ ├── SideBarDateTime.module.css │ │ │ │ └── NavItem.module.css │ │ │ ├── SidebarDateTime.tsx │ │ │ ├── NavItem.tsx │ │ │ ├── hooks │ │ │ │ └── useSidebarDrag.ts │ │ │ └── SidebarEnvToggle.tsx │ │ ├── Popup.tsx │ │ ├── RoleTypeCell.tsx │ │ ├── Popup.module.css │ │ ├── TopSearchBar │ │ │ ├── TopSearchBarComponent.tsx │ │ │ ├── styles │ │ │ │ └── TopSearchBarTextfield.module.css │ │ │ └── TopSearchBarTextField.tsx │ │ └── OrganizationCreation │ │ │ ├── form-fields │ │ │ ├── FullNameField.tsx │ │ │ ├── WebsiteUrlField.tsx │ │ │ └── DescriptionField.tsx │ │ │ └── models │ │ │ └── organizationTypes.ts │ ├── global.d.ts │ ├── assets │ │ ├── logo.png │ │ ├── logologin.png │ │ ├── log-in-blue.png │ │ ├── log-in-white.png │ │ └── fun │ │ │ ├── partymeow.gif │ │ │ ├── partyparrot.gif │ │ │ ├── sleeping cat.gif │ │ │ └── sleeping dog.gif │ ├── setupTests.ts │ ├── models │ │ ├── ansattportenModels.ts │ │ └── models.ts │ ├── stores │ │ ├── Appstate.ts │ │ ├── Dashboardstate.ts │ │ ├── DashboardStore.ts │ │ └── Appstore.ts │ ├── pages │ │ ├── styles │ │ │ ├── SettingsPage.module.css │ │ │ ├── DashboardPage.module.css │ │ │ ├── ManualRoleSearchPage.module.css │ │ │ └── SignInPage.module.css │ │ ├── NewOrganizationPage.tsx │ │ ├── SettingsPage.tsx │ │ ├── SignOutPage.tsx │ │ ├── DashboardPage.tsx │ │ ├── SignInPage.tsx │ │ └── ManualRoleSearchPage.tsx │ ├── hooks │ │ ├── ansattportenHooks.ts │ │ └── useVersionCheck.ts │ ├── app │ │ ├── PrivateRoutes.tsx │ │ ├── App.css │ │ └── App.tsx │ ├── main.tsx │ └── utils │ │ └── ansattportenApi.ts │ ├── .vite │ ├── deps_temp_167ae938 │ │ └── package.json │ └── deps_temp_796f6a3f │ │ └── package.json │ ├── design-tokens │ ├── $designsystemet.jsonc │ ├── primitives │ │ └── modes │ │ │ ├── size │ │ │ ├── large.json │ │ │ ├── small.json │ │ │ ├── medium.json │ │ │ └── global.json │ │ │ └── typography │ │ │ ├── primary │ │ │ └── test2.json │ │ │ ├── secondary │ │ │ └── test2.json │ │ │ └── size │ │ │ ├── large.json │ │ │ ├── medium.json │ │ │ └── small.json │ ├── $metadata.json │ └── semantic │ │ └── modes │ │ ├── main-color │ │ └── accent.json │ │ └── support-color │ │ ├── brand1.json │ │ ├── brand2.json │ │ └── brand3.json │ ├── public │ ├── asd_128.png │ ├── asd_128_white.png │ └── version.json │ ├── nuget.config │ ├── .gitignore │ ├── tsconfig.node.json │ ├── index.html │ ├── .eslintrc.cjs │ ├── tsconfig.json │ ├── design-tokens-build │ ├── types.d.ts │ └── colors.d.ts │ ├── altinn-support-dashboard.esproj │ ├── vitest.config.ts │ ├── tsconfig.app.json │ ├── vite.config.ts │ └── package.json ├── package.json ├── renovate.json ├── backend ├── src │ └── altinn-support-dashboard.backend │ │ ├── appsettings.Development.json │ │ ├── wwwroot │ │ └── dist │ │ │ ├── assets │ │ │ ├── logo-HzEGxyCu.png │ │ │ ├── index-D-jCIp55.css │ │ │ └── index-DMldKdW7.css │ │ │ ├── index.html │ │ │ └── vite.svg │ │ ├── altinn-support-dashboard.Server.http │ │ ├── Clients │ │ └── Interfaces │ │ │ ├── IPartyApiClient.cs │ │ │ ├── IAltinn3ApiClient.cs │ │ │ ├── IDataBrregClient.cs │ │ │ └── IAltinnApiClient.cs │ │ ├── Security │ │ ├── AltinnResources.cs │ │ ├── AltinnResourceRequirment.cs │ │ ├── AltinnResourceHandler.cs │ │ ├── AnsattportenConstants.cs │ │ ├── AnsattportenLoginSettings.cs │ │ └── BasicAuthenticationHandler.cs │ │ ├── Models │ │ ├── Configuration.cs │ │ ├── OfficialContact.cs │ │ ├── altinn3 │ │ │ ├── PersonalContactDto.cs │ │ │ └── NotificationAddressDto.cs │ │ ├── ansattporten │ │ │ └── AuthDetails.cs │ │ ├── BrregConfiguration.cs │ │ ├── PartyModel.cs │ │ ├── EnviromentConfigurations.cs │ │ ├── OrganizationModel.cs │ │ ├── GiteaConfiguration.cs │ │ ├── RoleModel.cs │ │ ├── PersonalContact.cs │ │ ├── UnderenhetModel.cs │ │ ├── BrregEnhet.cs │ │ └── ErRollerModel.cs │ │ ├── Services │ │ ├── Interfaces │ │ │ ├── IAnsattportenService.cs │ │ │ ├── IDataBrregService.cs │ │ │ ├── IPartyApiService.cs │ │ │ ├── IAltinnApiService.cs │ │ │ └── IGiteaService.cs │ │ └── AnsattportenService.cs │ │ ├── Properties │ │ └── launchSettings.json │ │ ├── Validation │ │ └── InputValidation.cs │ │ └── Controllers │ │ └── EnhetsregisterController.cs ├── test │ └── altinn-support-dashboard.backend.Tests │ │ ├── coverlet.runsettings │ │ └── altinn-support-dashboard.backend.Tests.csproj └── README.md ├── LICENSE ├── .github └── workflows │ ├── backend-ci.yml │ ├── frontend-ci.yml │ ├── altinn-support-dashboard-prod.yml │ └── altinn-support-dashboard-test.yml ├── altinn-support-dashboard.sln └── tree.md /frontend/altinn-support-dashboard.client/index.html.__projection.g.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/.vite/deps_temp_167ae938/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module" 3 | } 4 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/.vite/deps_temp_796f6a3f/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module" 3 | } 4 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/design-tokens/$designsystemet.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@digdir/designsystemet", 3 | "version": "1.6.0" 4 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/Dashboard/styles/ContactInfoCell.module.css: -------------------------------------------------------------------------------- 1 | .bold { 2 | font-weight: bolder; 3 | } 4 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/SettingsContent/styles/SettingsVersionComponent.module.css: -------------------------------------------------------------------------------- 1 | .link { 2 | margin-left: 10px; 3 | } 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "@types/testing-library__jest-dom": "^5.14.9", 4 | "@vitest/coverage-v8": "^4.0.14" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "local>Altinn/renovate-config" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/InformationDialog/styles/InformationDialogBox.module.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | .dialogBox { 5 | padding: 2rem; 6 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/Dashboard/styles/OrganizationList.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | overflow-y: auto; 3 | max-height: 80vh; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/global.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.module.css' { 2 | const content: Record; 3 | export default content; 4 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/public/asd_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altinn/altinn-support-dashboard/main/frontend/altinn-support-dashboard.client/public/asd_128.png -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altinn/altinn-support-dashboard/main/frontend/altinn-support-dashboard.client/src/assets/logo.png -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | import * as matchers from '@testing-library/jest-dom/matchers'; 2 | import { expect } from 'vitest'; 3 | 4 | expect.extend(matchers); -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/public/asd_128_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altinn/altinn-support-dashboard/main/frontend/altinn-support-dashboard.client/public/asd_128_white.png -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/assets/logologin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altinn/altinn-support-dashboard/main/frontend/altinn-support-dashboard.client/src/assets/logologin.png -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/SettingsContent/styles/SettingsDarkModeComponent.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | padding: 1rem; 3 | box-shadow: var(--ds-shadow-sm); 4 | } 5 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/assets/log-in-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altinn/altinn-support-dashboard/main/frontend/altinn-support-dashboard.client/src/assets/log-in-blue.png -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/assets/log-in-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altinn/altinn-support-dashboard/main/frontend/altinn-support-dashboard.client/src/assets/log-in-white.png -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/assets/fun/partymeow.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altinn/altinn-support-dashboard/main/frontend/altinn-support-dashboard.client/src/assets/fun/partymeow.gif -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/Dashboard/styles/ContactsSearchBar.module.css: -------------------------------------------------------------------------------- 1 | .input :focus { 2 | outline: none; 3 | } 4 | 5 | .input { 6 | margin-bottom: 0.5rem; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/ManualRoleSearch/styles/ResultTable.module.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .result-table { 4 | margin-top: 3rem; 5 | width:100%; 6 | margin-left: 1rem; 7 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/SettingsContent/styles/SettingsActionsButtons.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | box-shadow: var(--ds-shadow-sm); 4 | } 5 | 6 | 7 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/assets/fun/partyparrot.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altinn/altinn-support-dashboard/main/frontend/altinn-support-dashboard.client/src/assets/fun/partyparrot.gif -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/assets/fun/sleeping cat.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altinn/altinn-support-dashboard/main/frontend/altinn-support-dashboard.client/src/assets/fun/sleeping cat.gif -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/assets/fun/sleeping dog.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altinn/altinn-support-dashboard/main/frontend/altinn-support-dashboard.client/src/assets/fun/sleeping dog.gif -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/wwwroot/dist/assets/logo-HzEGxyCu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altinn/altinn-support-dashboard/main/backend/src/altinn-support-dashboard.backend/wwwroot/dist/assets/logo-HzEGxyCu.png -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/VersionDialog/styles/VersionDialogTitle.module.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .heading { 4 | font-size: larger; 5 | } 6 | 7 | .paragraph { 8 | font-size: medium; 9 | color: gray; 10 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/SettingsContent/styles/SettingsLanguageComponent.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | padding: 1rem; 3 | box-shadow: var(--ds-shadow-sm); 4 | } 5 | 6 | .title { 7 | margin-bottom: 0.35rem; 8 | } 9 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/models/ansattportenModels.ts: -------------------------------------------------------------------------------- 1 | export interface authDetails { 2 | isLoggedIn: boolean; 3 | name: string; 4 | orgName: string; 5 | ansattportenActive: boolean; 6 | userPolicies: string[]; 7 | } 8 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/Dashboard/models/mainContentTypes.ts: -------------------------------------------------------------------------------- 1 | export type SortDirection = "ascending" | "descending" | "none" | undefined; 2 | 3 | export type ERRolesSortField = "type" | "person" | "sistEndret" | null; 4 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/VersionDialog/styles/VersionDialogButton.module.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .button { 4 | max-width: 8rem; 5 | max-height: 2rem; 6 | font-size: small; 7 | margin: 0.5rem; 8 | float: right; 9 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/stores/Appstate.ts: -------------------------------------------------------------------------------- 1 | export interface AppState { 2 | environment: string; 3 | isDarkMode: boolean; 4 | 5 | setEnvironment: (env: string) => void; 6 | setIsDarkMode: (darkmode: boolean) => void; 7 | } 8 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/altinn-support-dashboard.Server.http: -------------------------------------------------------------------------------- 1 | @altinn_support_dashboard.Server_HostAddress = http://localhost:5237 2 | 3 | GET {{altinn_support_dashboard.Server_HostAddress}}/weatherforecast/ 4 | Accept: application/json 5 | 6 | ### 7 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/pages/styles/SettingsPage.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | padding: 16px; 3 | height: 100%; 4 | max-height: calc(100vh - 80px); 5 | overflow: scroll; 6 | } 7 | 8 | .container > * { 9 | margin-bottom: 1rem; 10 | } 11 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Clients/Interfaces/IPartyApiClient.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | public interface IPartyApiClient 4 | { 5 | Task GetParty(string orgNumber, bool isOrg); 6 | Task GetPartyRoles(string partyUuid); 7 | Task GetPartyByUuid(string uuid); 8 | } -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Security/AltinnResources.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | namespace Security; 4 | 5 | public class AltinnResources 6 | { 7 | public required string TT02Resource { get; set; } 8 | public required string ProductionResource { get; set; } 9 | } 10 | 11 | 12 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Models/Configuration.cs: -------------------------------------------------------------------------------- 1 | namespace altinn_support_dashboard.Server.Models; 2 | 3 | public class Configuration 4 | { 5 | public required EnvironmentConfiguration Production { get; set; } 6 | public required EnvironmentConfiguration TT02 { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Clients/Interfaces/IAltinn3ApiClient.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | public interface IAltinn3ApiClient 4 | { 5 | Task GetPersonalContactsAltinn3(string orgNumber, string environmentName); 6 | Task GetNotificationAddresses(string orgNumber, string environmentName); 7 | } -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Models/OfficialContact.cs: -------------------------------------------------------------------------------- 1 | public class OfficialContact 2 | { 3 | public string? MobileNumber { get; set; } 4 | public DateTime? MobileNumberChanged { get; set; } 5 | public string? EMailAddress { get; set; } 6 | public DateTime? EMailAddressChanged { get; set; } 7 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/ManualRoleSearch/styles/SearchButton.module.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .search-button { 4 | align-self: flex-end; 5 | margin-left: 0; 6 | } 7 | 8 | @media (max-width:1439px) { 9 | .search-button { 10 | font-size: var(--ds-font-size-3); 11 | } 12 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/stores/Dashboardstate.ts: -------------------------------------------------------------------------------- 1 | import { SelectedOrg } from "../models/models"; 2 | 3 | export interface DashboardState { 4 | query: string; 5 | selectedOrg: SelectedOrg | null; 6 | setQuery: (q: string) => void; 7 | setSelectedOrg: (org: SelectedOrg | null) => void; 8 | } 9 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/ManualRoleSearch/styles/InputComponent.module.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .input-fields { 4 | display: flex; 5 | gap: 1rem; 6 | } 7 | 8 | @media (max-width:1439px) { 9 | .input-fields { 10 | gap: 0.5rem; 11 | font-size: var(--ds-font-size-3); 12 | } 13 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/ManualRoleSearch/styles/EmptySearchButton.module.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .empty-search-button { 4 | margin-bottom: 0.2rem; 5 | align-self: flex-end; 6 | } 7 | 8 | @media (max-width:1439px) { 9 | .empty-search-button { 10 | font-size: var(--ds-font-size-3); 11 | } 12 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Clients/Interfaces/IDataBrregClient.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | public interface IDataBrregClient 4 | { 5 | Task GetRolesAsync(string orgNumber, string environmentName); 6 | Task GetUnderenheter(string orgNumber, string environmentName); 7 | Task GetEnhetsdetaljer(string orgNumber, string environmentName); 8 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/ManualRoleSearch/styles/ManualRoleSearchTextField.module.css: -------------------------------------------------------------------------------- 1 | 2 | .textfield-div{ 3 | flex:1; 4 | min-width: 0; 5 | } 6 | 7 | .textfield-div input { 8 | width: 100%; 9 | min-width: 70%; 10 | padding: 1rem; 11 | } 12 | 13 | .textfield-div input:focus{ 14 | outline: none; 15 | box-shadow:none; 16 | } 17 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/VersionDialog/styles/VersionDialogContent.module.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .heading { 4 | font-size: larger; 5 | } 6 | 7 | .paragraph { 8 | font-size: large; 9 | } 10 | 11 | .list { 12 | margin-top: 1rem; 13 | margin-bottom: 0.5rem; 14 | padding-left: 1.5rem; 15 | } 16 | 17 | .listItem { 18 | font-size: medium; 19 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/design-tokens/primitives/modes/size/large.json: -------------------------------------------------------------------------------- 1 | { 2 | "size": { 3 | "_mode-font-size": { 4 | "$type": "dimension", 5 | "$value": "21" 6 | }, 7 | "_base": { 8 | "$type": "dimension", 9 | "$value": "18" 10 | }, 11 | "_step": { 12 | "$type": "dimension", 13 | "$value": "4" 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/design-tokens/primitives/modes/size/small.json: -------------------------------------------------------------------------------- 1 | { 2 | "size": { 3 | "_mode-font-size": { 4 | "$type": "dimension", 5 | "$value": "16" 6 | }, 7 | "_base": { 8 | "$type": "dimension", 9 | "$value": "18" 10 | }, 11 | "_step": { 12 | "$type": "dimension", 13 | "$value": "4" 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/stores/DashboardStore.ts: -------------------------------------------------------------------------------- 1 | import { create } from "zustand"; 2 | import { DashboardState } from "./Dashboardstate"; 3 | 4 | export const useDashboardStore = create((set) => ({ 5 | query: "", 6 | selectedOrg: null, 7 | setQuery: (q) => set({ query: q }), 8 | setSelectedOrg: (org) => set({ selectedOrg: org }), 9 | })); 10 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Security/AltinnResourceRequirment.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | 3 | public class AltinnResourceRequirement : IAuthorizationRequirement 4 | { 5 | public string resource { get; } 6 | 7 | public AltinnResourceRequirement(string resource) 8 | { 9 | this.resource = resource; 10 | } 11 | 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/design-tokens/primitives/modes/size/medium.json: -------------------------------------------------------------------------------- 1 | { 2 | "size": { 3 | "_mode-font-size": { 4 | "$type": "dimension", 5 | "$value": "18" 6 | }, 7 | "_base": { 8 | "$type": "dimension", 9 | "$value": "18" 10 | }, 11 | "_step": { 12 | "$type": "dimension", 13 | "$value": "4" 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/Sidebar/styles/SideBarEnvToggle.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | text-align: center; 3 | margin-bottom: 0.35rem; 4 | } 5 | 6 | .select { 7 | border-radius: 100px; 8 | } 9 | 10 | @media (max-width: 1439px) { 11 | .container { 12 | font-size: var(--ds-font-size-3); 13 | } 14 | .select{ 15 | max-height: 2.5rem; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/Dashboard/styles/RoleDetails.module.css: -------------------------------------------------------------------------------- 1 | .Container { 2 | box-shadow: 3 | 0px 1px 3px rgba(0, 0, 0, 0.12), 4 | 0px 1px 2px rgba(0, 0, 0, 0.24); 5 | } 6 | 7 | .Button { 8 | margin: 0.5rem 0 1rem 0; 9 | height: 2.5rem; 10 | font-size: 0.95rem; 11 | padding: 0.2rem 0.2rem; 12 | min-width: unset; 13 | min-height: unset; 14 | } 15 | 16 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Services/Interfaces/IAnsattportenService.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | using System.Security.Claims; 4 | 5 | namespace altinn_support_dashboard.Server.Services.Interfaces; 6 | 7 | public interface IAnsattportenService 8 | { 9 | Task> GetUserPolicies(ClaimsPrincipal user); 10 | Task GetRepresentationOrgName(ClaimsPrincipal user); 11 | 12 | } 13 | 14 | 15 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/ManualRoleSearch/utils/storageUtils.ts: -------------------------------------------------------------------------------- 1 | // ManualRoleSearch/utils/storageUtils.ts 2 | export const getLocalStorageValue = (key: string, defaultValue = ''): string => { 3 | return localStorage.getItem(key) || defaultValue; 4 | }; 5 | 6 | export const setLocalStorageValue = (key: string, value: string): void => { 7 | localStorage.setItem(key, value); 8 | }; 9 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/ManualRoleSearch/styles/RoleTable.module.css: -------------------------------------------------------------------------------- 1 | .table-div { 2 | max-height: 75vh; 3 | overflow-y: auto; 4 | border-width: 1px; 5 | box-shadow: 6 | 0px 1px 3px rgba(0, 0, 0, 0.12), 7 | 0px 1px 2px rgba(0, 0, 0, 0.24); 8 | } 9 | 10 | 11 | @media (max-width:1439px) { 12 | .table-div { 13 | max-height: 60vh; 14 | font-size: var(--ds-font-size-3); 15 | } 16 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/Popup.tsx: -------------------------------------------------------------------------------- 1 | import { toast } from "react-toastify"; 2 | import popupStyle from "./Popup.module.css"; 3 | 4 | export function showPopup(message: string, type?: string) { 5 | if (type === "error") { 6 | return toast.error(message, { 7 | position: "bottom-right", 8 | autoClose: 5000, 9 | className: popupStyle.errorMessage, 10 | }); 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/VersionDialog/utils/formatDateutils.ts: -------------------------------------------------------------------------------- 1 | const formatDate = (dateString: string) => { 2 | try { 3 | const date = new Date(dateString); 4 | return date.toLocaleDateString("nb-NO", { 5 | year: "numeric", 6 | month: "long", 7 | day: "numeric", 8 | }); 9 | } catch { 10 | return dateString; 11 | } 12 | }; 13 | 14 | export default formatDate; 15 | 16 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", 5 | "skipLibCheck": true, 6 | "module": "ESNext", 7 | "moduleResolution": "bundler", 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "noEmit": true, 11 | 12 | }, 13 | "include": [ "vite.config.ts" ] 14 | } 15 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Altinn Support Dashboard 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /backend/test/altinn-support-dashboard.backend.Tests/coverlet.runsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | cobertura 7 | false 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Models/altinn3/PersonalContactDto.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Models.altinn3Dtos; 3 | 4 | public class PersonalContactDto 5 | { 6 | public string? OrgNr { get; set; } 7 | public required string NationalIdentityNumber { get; set; } 8 | public required string Name { get; set; } 9 | public required string Email { get; set; } 10 | public required string Phone { get; set; } 11 | public DateTime? LastChanged { get; set; } 12 | } 13 | 14 | 15 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 5 | ignorePatterns: ["dist", ".eslintrc.cjs"], 6 | parser: "@typescript-eslint/parser", 7 | plugins: ["react-refresh"], 8 | rules: { 9 | "react-refresh/only-export-components": [ 10 | "warn", 11 | { allowConstantExport: true }, 12 | ], 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/Dashboard/utils/dateUtils.ts: -------------------------------------------------------------------------------- 1 | export const formatDate = (dateString: string): string => { 2 | if (!dateString || dateString.startsWith('0001-01-01')) { 3 | return '-'; 4 | } 5 | const date = new Date(dateString); 6 | return date.toLocaleString('no-NO', { 7 | day: '2-digit', 8 | month: '2-digit', 9 | year: 'numeric', 10 | hour: '2-digit', 11 | minute: '2-digit', 12 | }); 13 | }; 14 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // Reduserer strenge typekontroller for å unngå lint-feil 4 | "strict": true, 5 | "types": [] 6 | }, 7 | "files": [], 8 | "references": [ 9 | { 10 | "path": "./tsconfig.app.json" 11 | }, 12 | { 13 | "path": "./tsconfig.node.json" 14 | } 15 | ], 16 | "exclude": [ 17 | "**/*.test.ts", 18 | "**/*.test.tsx", 19 | "**/node_modules", 20 | "./dist" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/hooks/ansattportenHooks.ts: -------------------------------------------------------------------------------- 1 | import { useQuery, UseQueryResult } from "@tanstack/react-query"; 2 | import { authDetails } from "../models/ansattportenModels"; 3 | import { fetchAuthDetails } from "../utils/ansattportenApi"; 4 | 5 | export const useAuthDetails = () => { 6 | const authDetailsQuery: UseQueryResult = useQuery({ 7 | queryKey: ["authDetails"], 8 | queryFn: () => fetchAuthDetails(), 9 | }); 10 | 11 | return authDetailsQuery; 12 | }; 13 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/Sidebar/styles/SideBarDateTime.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | text-align: center; 3 | margin-bottom: 20px; 4 | color: #fff; 5 | font-style: italic; 6 | } 7 | 8 | .time { 9 | font-size: var(--ds-font-size-6); 10 | } 11 | 12 | .date { 13 | font-size: var(--ds-font-size-4); 14 | } 15 | 16 | @media (max-width: 1439px) { 17 | .time { 18 | font-size: var(--ds-font-size-4); 19 | } 20 | .date { 21 | font-size: var(--ds-font-size-3); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/pages/NewOrganizationPage.tsx: -------------------------------------------------------------------------------- 1 | import OrganizationCreationComponent from "../components/OrganizationCreation/OrganizationCreationComponent"; 2 | import { useAppStore } from "../stores/Appstore"; 3 | 4 | const NewOrganizationPage: React.FC = () => { 5 | const environment = useAppStore.getState().environment; 6 | return ( 7 |
8 | 9 |
10 | ); 11 | }; 12 | 13 | export default NewOrganizationPage; 14 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Models/ansattporten/AuthDetails.cs: -------------------------------------------------------------------------------- 1 | // Models/EnvironmentConfiguration.cs 2 | 3 | 4 | namespace altinn_support_dashboard.Server.Models.ansattporten; 5 | 6 | 7 | public class AuthDetails 8 | { 9 | public bool IsLoggedIn { get; set; } 10 | 11 | //Temporary 12 | public bool? AnsattportenActive { get; set; } 13 | 14 | public string? Name { get; set; } 15 | 16 | public List? UserPolicies { get; set; } 17 | 18 | public string? OrgName { get; set; } 19 | } 20 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/wwwroot/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Altinn Support Dashboard 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Models/altinn3/NotificationAddressDto.cs: -------------------------------------------------------------------------------- 1 | namespace Models.altinn3Dtos; 2 | 3 | public class NotificationAddressDto 4 | { 5 | public required int NotificationAddressId { get; set; } 6 | public required string CountryCode { get; set; } 7 | public required string Email { get; set; } 8 | public required string Phone { get; set; } 9 | public required string SourceOrgNumber { get; set; } 10 | public required string RequestedOrgNumber { get; set; } 11 | public DateTime? LastChanged { get; set; } 12 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/design-tokens-build/types.d.ts: -------------------------------------------------------------------------------- 1 | /* build: v1.6.0 */ 2 | import type {} from '@digdir/designsystemet/types'; 3 | 4 | // Augment types based on theme 5 | declare module '@digdir/designsystemet/types' { 6 | export interface ColorDefinitions { 7 | accent: never; 8 | brand1: never; 9 | brand2: never; 10 | brand3: never; 11 | neutral: never; 12 | } 13 | export interface SeverityColorDefinitions { 14 | info: never; 15 | success: never; 16 | warning: never; 17 | danger: never; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Services/Interfaces/IDataBrregService.cs: -------------------------------------------------------------------------------- 1 | using altinn_support_dashboard.Server.Models; 2 | using System.Threading.Tasks; 3 | 4 | namespace altinn_support_dashboard.Server.Services.Interfaces 5 | { 6 | public interface IDataBrregService 7 | { 8 | Task GetRolesAsync(string orgNumber, string environmentName); 9 | Task GetUnderenheter(string orgNumber, string environmentName); 10 | Task GetEnhetsdetaljer(string orgNumber, string environmentName); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/design-tokens/primitives/modes/typography/primary/test2.json: -------------------------------------------------------------------------------- 1 | { 2 | "test2": { 3 | "font-family": { 4 | "$type": "fontFamilies", 5 | "$value": "Inter" 6 | }, 7 | "font-weight": { 8 | "medium": { 9 | "$type": "fontWeights", 10 | "$value": "Medium" 11 | }, 12 | "semibold": { 13 | "$type": "fontWeights", 14 | "$value": "Semi bold" 15 | }, 16 | "regular": { 17 | "$type": "fontWeights", 18 | "$value": "Regular" 19 | } 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/design-tokens/primitives/modes/typography/secondary/test2.json: -------------------------------------------------------------------------------- 1 | { 2 | "test2": { 3 | "font-family": { 4 | "$type": "fontFamilies", 5 | "$value": "Inter" 6 | }, 7 | "font-weight": { 8 | "medium": { 9 | "$type": "fontWeights", 10 | "$value": "Medium" 11 | }, 12 | "semibold": { 13 | "$type": "fontWeights", 14 | "$value": "Semi bold" 15 | }, 16 | "regular": { 17 | "$type": "fontWeights", 18 | "$value": "Regular" 19 | } 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/app/PrivateRoutes.tsx: -------------------------------------------------------------------------------- 1 | import { Navigate, Outlet } from "react-router-dom"; 2 | import { useAuthDetails } from "../hooks/ansattportenHooks"; 3 | import { Spinner } from "@digdir/designsystemet-react"; 4 | 5 | const PrivateRoutes = () => { 6 | const authDetails = useAuthDetails(); 7 | 8 | if (authDetails.isLoading) { 9 | return ; 10 | } 11 | 12 | return authDetails?.data?.isLoggedIn ? : ; 13 | }; 14 | 15 | export default PrivateRoutes; 16 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/RoleTypeCell.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Table } from "@digdir/designsystemet-react"; 3 | 4 | 5 | interface RoleTypeCellProps { 6 | roleType: string; 7 | } 8 | 9 | const RoleTypeCell: React.FC = ({ roleType }) => { 10 | if (roleType === "External") return Rolle fra BRREG; 11 | else if (roleType === "Local") return Egendefinert rolle 12 | else return {roleType}; 13 | } 14 | 15 | export default RoleTypeCell; -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Services/Interfaces/IPartyApiService.cs: -------------------------------------------------------------------------------- 1 | using altinn_support_dashboard.Server.Models; 2 | 3 | namespace altinn_support_dashboard.Server.Services.Interfaces 4 | { 5 | public interface IPartyApiService 6 | { 7 | Task GetRolesFromOrgAsync(string orgNumber); 8 | Task GetRolesFromPartyAsync(string uuid); 9 | Task GetPartyFromOrgAsync(string orgNumber); 10 | Task GetPartyFromSsnAsync(string ssn); 11 | Task GetPartyFromUuidAsync(string uuid); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/SettingsContent/models/settingsTypes.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export interface SettingsContentProps { 4 | environment: string; 5 | isDarkMode: boolean; 6 | setIsDarkMode: React.Dispatch>; 7 | } 8 | 9 | export interface PatTokenValidationResponse { 10 | isValid: boolean; 11 | message: string; 12 | } 13 | 14 | export interface PatTokenState { 15 | token: string; 16 | isValid: boolean; 17 | username?: string; 18 | isValidating: boolean; 19 | errorMessage?: string; 20 | } 21 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/VersionDialog/VersionDialogButton.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Button, 3 | } from "@digdir/designsystemet-react"; 4 | import styles from "./styles/VersionDialogButton.module.css"; 5 | 6 | 7 | type VersionDialogButtonProps = { 8 | onClose: () => void; 9 | } 10 | 11 | const VersionDialogButton: React.FC = ({ onClose }) => { 12 | return ( 13 | 16 | ); 17 | }; 18 | export default VersionDialogButton; -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Models/BrregConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace altinn_support_dashboard.Server.Models 2 | { 3 | /// 4 | /// Konfigurasjon for Brønnøysundregistrene API 5 | /// 6 | public class BrregApiConfiguration 7 | { 8 | /// 9 | /// Base URL for Brønnøysundregistrene API 10 | /// 11 | public required string BaseUrl { get; set; } 12 | 13 | /// 14 | /// Timeout i sekunder for API-kall 15 | /// 16 | public int Timeout { get; set; } = 30; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Models/PartyModel.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace altinn_support_dashboard.Server.Models; 3 | 4 | public class PartyModel 5 | { 6 | public required string PartyUuid { get; set; } 7 | public string? OrgNumber { get; set; } 8 | public string? Ssn { get; set; } 9 | public string? Name { get; set; } 10 | public PartyPerson? Person { get; set; } 11 | } 12 | 13 | 14 | public class PartyPerson 15 | { 16 | 17 | public string? FirstName { get; set; } 18 | public string? MiddleName { get; set; } 19 | public string? LastName { get; set; } 20 | 21 | } 22 | 23 | 24 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/pages/styles/DashboardPage.module.css: -------------------------------------------------------------------------------- 1 | .dashboard-page-container { 2 | height: 100%; 3 | overflow-y: auto; 4 | padding-right: 2rem; 5 | } 6 | 7 | .dashboard-container { 8 | display: flex; 9 | gap: 1rem; 10 | margin-top: 1.5rem; 11 | } 12 | 13 | .org-list-container { 14 | flex: 2; 15 | min-width: 25%; 16 | max-width: 40%; 17 | } 18 | .detailed-org-container { 19 | flex: 8; 20 | min-width: 30%; 21 | } 22 | 23 | .infoButton { 24 | margin-bottom: -1.5rem; 25 | height: 30px; 26 | min-height: 0; 27 | width: 20px; 28 | min-width: 0; 29 | margin-left: auto; 30 | } 31 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Models/EnviromentConfigurations.cs: -------------------------------------------------------------------------------- 1 | // Models/EnvironmentConfiguration.cs 2 | 3 | using Altinn.ApiClients.Maskinporten.Config; 4 | 5 | public class EnvironmentConfiguration 6 | { 7 | public required string Name { get; set; } 8 | public required string ThemeName { get; set; } 9 | public required string ApiKey { get; set; } 10 | public required string BaseAddressAltinn2 { get; set; } 11 | public required string BaseAddressAltinn3 { get; set; } 12 | 13 | public required MaskinportenSettings MaskinportenSettings { get; set; } 14 | public int Timeout { get; set; } 15 | } 16 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/design-tokens-build/colors.d.ts: -------------------------------------------------------------------------------- 1 | /* This file is deprecated and will be removed in a future release. Use types.d.ts instead */ 2 | /* build: v1.6.0 */ 3 | import type {} from '@digdir/designsystemet/types'; 4 | 5 | // Augment types based on theme 6 | declare module '@digdir/designsystemet/types' { 7 | export interface ColorDefinitions { 8 | accent: never; 9 | brand1: never; 10 | brand2: never; 11 | brand3: never; 12 | neutral: never; 13 | } 14 | export interface SeverityColorDefinitions { 15 | info: never; 16 | success: never; 17 | warning: never; 18 | danger: never; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Clients/Interfaces/IAltinnApiClient.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | public interface IAltinnApiClient 5 | { 6 | 7 | Task GetOrganizationInfo(string orgNumber, string environmentName); 8 | Task GetOrganizationsByPhoneNumber(string phoneNumber, string environmentName); 9 | Task GetOrganizationsByEmail(string email, string environmentName); 10 | Task GetPersonalContacts(string contactInfo, string environmentName); 11 | Task GetPersonRoles(string subject, string reportee, string environmentName); 12 | Task GetOfficialContacts(string orgNumber, string environmentName); 13 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/stores/Appstore.ts: -------------------------------------------------------------------------------- 1 | import { create } from "zustand"; 2 | import { persist } from "zustand/middleware"; 3 | import { AppState } from "./Appstate"; 4 | 5 | //sets all global variables 6 | export const useAppStore = create()( 7 | //handles persistence 8 | persist( 9 | (set) => ({ 10 | environment: "PROD", 11 | isDarkMode: false, 12 | 13 | setEnvironment: (env: string) => set({ environment: env }), 14 | setIsDarkMode: (darkMode: boolean) => set({ isDarkMode: darkMode }), 15 | }), 16 | { 17 | //local storage key for global data 18 | name: "app-storage", 19 | }, 20 | ), 21 | ); 22 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/SettingsContent/utils/apiUtils.ts: -------------------------------------------------------------------------------- 1 | export const authorizedFetch = async (url: string, options: RequestInit = {}) => { 2 | const token = localStorage.getItem('authToken') || sessionStorage.getItem('authToken'); 3 | const headers = { 4 | ...options.headers, 5 | Authorization: `Basic ${token}`, 6 | 'Content-Type': 'application/json', 7 | }; 8 | const response = await fetch(url, { ...options, headers }); 9 | if (!response.ok) { 10 | const errorText = await response.text(); 11 | throw new Error(`${response.statusText}: ${errorText}`); 12 | } 13 | return response; 14 | }; 15 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/Dashboard/components/ErrorAlert.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Alert, 3 | Heading 4 | } from "@digdir/designsystemet-react" 5 | 6 | interface ErrorAlertProps { 7 | error: { 8 | message: string; 9 | response?: string; 10 | }; 11 | } 12 | 13 | export const ErrorAlert: React.FC = ({ error }) => { 14 | if (!error?.message) return null; 15 | 16 | return ( 17 | 18 | 19 | {error.message} 20 | 21 | {error.response && ( 22 | 23 | {error.response} 24 | 25 | )} 26 | 27 | ); 28 | }; 29 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/Popup.module.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .errorMessage { 4 | font-size: 18px; 5 | } 6 | 7 | :global([data-color-scheme="dark"] .Toastify__toast--error) 8 | { 9 | background-color: #772222; 10 | color: var(--ds-color-text-default); 11 | } 12 | 13 | :global([data-color-scheme="light"] .Toastify__toast--error) 14 | { 15 | background-color: #f7f7f8; 16 | color: var(--ds-color-neutral-text-subtle); 17 | } 18 | 19 | :global([data-color-scheme="light"] .Toastify__progress-bar--error) 20 | { 21 | background: #E74D3C; 22 | } 23 | 24 | :global([data-color-scheme="dark"] .Toastify__progress-bar--error) 25 | { 26 | background: #E74D3C; 27 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/Sidebar/SidebarDateTime.tsx: -------------------------------------------------------------------------------- 1 | import { useCurrentDateTime } from "../../hooks/hooks"; 2 | import { Heading } from "@digdir/designsystemet-react"; 3 | import classes from "./styles/SideBarDateTime.module.css"; 4 | 5 | const SideBarDateTime: React.FC = () => { 6 | const { formattedDate, formattedTime } = useCurrentDateTime(); 7 | return ( 8 |
9 | 10 | {formattedTime} 11 | 12 | 13 | {formattedDate} 14 | 15 |
16 | ); 17 | }; 18 | 19 | export default SideBarDateTime; 20 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/Dashboard/hooks/useAuthorizedFetch.ts: -------------------------------------------------------------------------------- 1 | const authorizedFetch = async (url: string, options: RequestInit = {}) => { 2 | const token = localStorage.getItem('authToken') || sessionStorage.getItem('authToken'); 3 | const headers = { 4 | ...options.headers, 5 | Authorization: `Basic ${token}`, 6 | 'Content-Type': 'application/json', 7 | }; 8 | const response = await fetch(url, { ...options, headers }); 9 | if (!response.ok) { 10 | const errorText = await response.text(); 11 | throw new Error(`${response.statusText}: ${errorText}`); 12 | } 13 | return response; 14 | }; 15 | 16 | export default authorizedFetch; 17 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/altinn-support-dashboard.esproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | npm run dev 4 | src\ 5 | Jest 6 | 7 | false 8 | 9 | $(MSBuildProjectDirectory)\dist 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/SettingsContent/styles/SettingsPatComponent.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | padding: 1.5rem; 3 | box-shadow: var(--ds-shadow-sm); 4 | } 5 | 6 | .container > * { 7 | margin-bottom: 1rem; 8 | } 9 | 10 | .description { 11 | font-style: italic; 12 | } 13 | 14 | .sectionTitle { 15 | font-size: 1.1rem; 16 | } 17 | 18 | .informationIcon { 19 | height: 32px; 20 | width: 32px; 21 | } 22 | 23 | .section > * { 24 | margin-bottom: 1rem; 25 | } 26 | .patInputWrapper { 27 | display: flex; 28 | gap: 0.25rem; 29 | } 30 | 31 | .patInputField { 32 | flex: 1; 33 | } 34 | 35 | .patgeneratewrapper { 36 | display: flex; 37 | } 38 | 39 | .patActionButtonsWrapper { 40 | display: flex; 41 | gap: 0.25rem; 42 | } 43 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/pages/styles/ManualRoleSearchPage.module.css: -------------------------------------------------------------------------------- 1 | 2 | .input-row { 3 | display: flex; 4 | flex: 1; 5 | width: 80%; 6 | gap: 1rem; 7 | align-items: center; 8 | margin-bottom: 2rem; 9 | } 10 | 11 | @media (max-width:1439px) { 12 | .input-row { 13 | margin-bottom: 1rem; 14 | } 15 | } 16 | 17 | .result-area { 18 | width: 100%; 19 | display: flex; 20 | flex-direction: column; 21 | overflow-y: auto; 22 | } 23 | 24 | @media (min-width:1024px){ 25 | .result-area { 26 | max-height: 70vh; 27 | } 28 | } 29 | 30 | .infoButton { 31 | margin-top: -2rem; 32 | margin-bottom: 1rem; 33 | height: 30px; 34 | min-height: 0; 35 | width: 20px; 36 | min-width: 0; 37 | margin-left: auto; 38 | } 39 | 40 | 41 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/VersionDialog/VersionDialog.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import type { VersionInfo } from '../../hooks/useVersionCheck'; 3 | import VersionDialogBox from './VersionDialogBox'; 4 | 5 | interface VersionDialogProps { 6 | versionInfo: VersionInfo | null; 7 | open: boolean; 8 | onClose: () => void; 9 | } 10 | 11 | /** 12 | * Komponent for å vise dialogboks med versjonsinformasjon 13 | * Brukes når en ny versjon av applikasjonen er tilgjengelig 14 | */ 15 | export const VersionDialog: React.FC = ({ 16 | versionInfo, 17 | open, 18 | onClose 19 | }) => { 20 | if (!versionInfo || !open) return null; 21 | 22 | return ( 23 | 27 | ); 28 | }; 29 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/InformationDialog/InformationDialogBox.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Dialog, Heading } from "@digdir/designsystemet-react"; 3 | import InformationDialogContent from "./InformationDialogContent"; 4 | import styles from "./styles/InformationDialogBox.module.css"; 5 | 6 | interface InformationDialogBoxProps { 7 | dialogRef: React.RefObject; 8 | } 9 | 10 | const InformationDialogBox: React.FC = ({ 11 | dialogRef, 12 | }) => { 13 | return ( 14 | 15 | 16 | Dette verktøyet skal kun brukes til å bekrefte informasjon 17 | 18 | 19 | 20 | ); 21 | }; 22 | export default InformationDialogBox; 23 | 24 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/vitest.config.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { defineConfig } from "vitest/config"; 3 | import { fileURLToPath, URL } from "node:url"; 4 | 5 | export default defineConfig({ 6 | test: { 7 | globals: true, 8 | environment: "jsdom", 9 | setupFiles: ["./src/setupTests.ts"], 10 | 11 | coverage: { 12 | provider: "v8", 13 | reporter: ["cobertura", "text-summary"], 14 | reportsDirectory: "./.coverage", 15 | include: [ 16 | "src/components/*", 17 | "src/utils/*", 18 | "src/services/*", 19 | "src/pages/*", 20 | "src/hooks/*", 21 | ], 22 | exclude: ["**/styles/**", "**/models/**"], 23 | }, 24 | }, 25 | 26 | resolve: { 27 | alias: { 28 | "@": fileURLToPath(new URL("./src", import.meta.url)), 29 | }, 30 | }, 31 | }); 32 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/Dashboard/components/contacts/ContactsSearchBar.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Textfield } from "@digdir/designsystemet-react"; 3 | import styles from "../../styles/ContactsSearchBar.module.css"; 4 | 5 | interface SearchContactsBarProps { 6 | searchQuery: string; 7 | setSearchQuery: (value: string) => void; 8 | handleClearSearch: () => void; 9 | } 10 | 11 | const SearchContactsBar: React.FC = ({ 12 | searchQuery, 13 | setSearchQuery, 14 | }) => { 15 | return ( 16 | setSearchQuery(e.target.value)} 20 | placeholder="Navn / Fødselsnummer / Telefon / E-post" 21 | className={styles["input"]} 22 | /> 23 | ); 24 | }; 25 | 26 | export default SearchContactsBar; 27 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/TopSearchBar/TopSearchBarComponent.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { TopSearchBarTextField } from "./TopSearchBarTextField"; 3 | import { Heading } from "@digdir/designsystemet-react"; 4 | import { SelectedOrg } from "../../models/models"; 5 | 6 | type SearchComponentProps = { 7 | query: string; 8 | setQuery: (query: string) => void; 9 | setSelectedOrg: (selectedOrg: SelectedOrg | null) => void; 10 | }; 11 | 12 | const SearchComponent: React.FC = ({ 13 | query, 14 | setQuery, 15 | setSelectedOrg, 16 | }) => ( 17 |
18 | Søk etter Organisasjoner 19 | 24 |
25 | ); 26 | 27 | export default SearchComponent; 28 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/Sidebar/styles/NavItem.module.css: -------------------------------------------------------------------------------- 1 | /* Base nav button */ 2 | .navButton { 3 | display: block; 4 | position: relative; 5 | padding: 10px; 6 | border-radius: 25px; 7 | cursor: pointer; 8 | text-align: center; 9 | transition: all 0.3s ease; 10 | text-decoration: none; 11 | color: #ffffff; 12 | } 13 | 14 | .navButtonCollapsed { 15 | width: 70px; 16 | min-width: 70px; 17 | } 18 | 19 | .navButton:not(.navButtonSelected):hover { 20 | background-color: rgba(255, 255, 255, 0.1); 21 | } 22 | 23 | /* Selected state */ 24 | .navButtonSelected { 25 | background-color: var(--ds-color-neutral-background-tinted); 26 | color: var(--ds-color-accent-text-default); 27 | border-top-right-radius: 0; 28 | border-bottom-right-radius: 0; 29 | margin-right: -32px; 30 | padding-right: 32px; 31 | width: calc(100% + 20px); 32 | } 33 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/TopSearchBar/styles/TopSearchBarTextfield.module.css: -------------------------------------------------------------------------------- 1 | 2 | .Container { 3 | position: relative; 4 | width: 100%; 5 | margin-top: 0.5rem; 6 | } 7 | 8 | .Container input { 9 | width: 95%; 10 | padding-right: 6rem; 11 | box-shadow: none; 12 | } 13 | .Container input:focus{ 14 | outline: none; 15 | box-shadow:none; 16 | } 17 | 18 | .searchButton { 19 | position:absolute; 20 | right:0.8rem; 21 | top: 50%; 22 | transform: translateY(-50%); 23 | height: 2.5rem; 24 | max-width: 2.5rem; 25 | background: none; 26 | } 27 | 28 | .emptySearchButton{ 29 | position: absolute; 30 | right: 2.5rem; 31 | top: 50%; 32 | transform: translateY(-50%); 33 | height: 3rem; 34 | min-width: 2rem; 35 | background-color: transparent; 36 | color: grey; 37 | font-size: larger; 38 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/InformationDialog/styles/InformationDialogContent.module.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .paragraph { 4 | font-size: var(--ds-font-size-4); 5 | margin-bottom: 1rem; 6 | } 7 | .itemContainer { 8 | display: flex; 9 | flex-direction: row; 10 | align-items: center; 11 | font-size: var(--ds-font-size-4); 12 | } 13 | .listParagraph { 14 | display: 1 1 0; 15 | font-style: italic; 16 | min-width: 0; 17 | max-width: 85%; 18 | } 19 | 20 | .listIconCorrect, 21 | .listIconWrong { 22 | display: flex; 23 | margin-left: auto; 24 | width: 40px; 25 | justify-content: center; 26 | align-items: center; 27 | flex-shrink: 0; 28 | } 29 | 30 | .listIconCorrect { 31 | color: green; 32 | font-size: var(--ds-font-size-8); 33 | } 34 | 35 | .listIconWrong { 36 | color: red; 37 | font-size: var(--ds-font-size-7); 38 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/ManualRoleSearch/ManualRoleSearchTextfield.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Textfield } from '@digdir/designsystemet-react'; 3 | import style from "./styles/ManualRoleSearchTextField.module.css"; 4 | 5 | type ManualRoleSearchTextFieldProps = { 6 | label: string; 7 | value: string; 8 | onChange: (value: string) => void; 9 | } 10 | 11 | const ManualRoleSearchTextField: React.FC = ({ 12 | label, 13 | value, 14 | onChange, 15 | }) => { 16 | return ( 17 |
18 | onChange(e.target.value)} 22 | /> 23 |
24 | ); 25 | }; 26 | 27 | export default ManualRoleSearchTextField; -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/Dashboard/styles/NotificationContactTable.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | flex-basis: 50%; 3 | } 4 | 5 | .table { 6 | max-width: 100%; 7 | } 8 | 9 | .cellText { 10 | font-size: var(--ds-font-size-2); 11 | } 12 | 13 | .tableCell { 14 | font-size: var(--ds-font-size-2); 15 | } 16 | 17 | @media (min-width: 1024px) { 18 | .cellText { 19 | font-size: var(--ds-font-size-3); 20 | } 21 | 22 | .tableCell { 23 | font-size: var(--ds-font-size-3); 24 | } 25 | } 26 | @media (min-width: 1440px) { 27 | .cellText { 28 | font-size: var(--ds-font-size-4); 29 | } 30 | 31 | .tableCell { 32 | font-size: var(--ds-font-size-4); 33 | } 34 | } 35 | 36 | @media (min-width: 1800px) { 37 | .cellText { 38 | font-size: var(--ds-font-size-5); 39 | } 40 | 41 | .tableCell { 42 | font-size: var(--ds-font-size-5); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/pages/styles/SignInPage.module.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .container { 4 | margin-top: 5rem; 5 | display: flex; 6 | flex-direction: column; 7 | align-items: center; 8 | } 9 | 10 | .logo{ 11 | max-width: fit-content; 12 | } 13 | 14 | .container button { 15 | font-size: var(--ds-font-size-5); 16 | padding: 20px 50px; 17 | } 18 | 19 | .ansattPortenButton { 20 | margin-top: 3rem; 21 | } 22 | 23 | .aidevButton { 24 | margin-top: 1.5rem; 25 | } 26 | 27 | .gifContainer { 28 | position: fixed; 29 | bottom: 0.5rem; 30 | } 31 | 32 | .gifContainer img { 33 | width: auto; 34 | height: auto; 35 | max-height: clamp(60px, 30vh, 300px); 36 | max-width: 40vw; 37 | object-fit: contain; 38 | display: block; 39 | } 40 | 41 | @media (max-height: 700px) { 42 | .gifContainer img { 43 | display: none; 44 | } 45 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/Dashboard/components/contacts/NotificationContactCell.tsx: -------------------------------------------------------------------------------- 1 | import { useDashboardStore } from "../../../../stores/DashboardStore"; 2 | import { Table } from "@digdir/designsystemet-react"; 3 | import style from "../../styles/NotificationContactTable.module.css"; 4 | 5 | interface NotificationContactCellProps { 6 | contact: string | null; 7 | } 8 | 9 | const NotificationContactCell: React.FC = ({ 10 | contact, 11 | }) => { 12 | const userInput = useDashboardStore((s) => s.query.replace(/\s/g, "").toLowerCase()); 13 | 14 | //outlines if searchquery is part of the cell 15 | const boldened = contact === userInput ? { fontWeight: "bold" } : undefined; 16 | 17 | return ( 18 | 19 | {contact || "-"} 20 | 21 | ); 22 | }; 23 | 24 | export default NotificationContactCell; 25 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/VersionDialog/VersionDialogTitle.tsx: -------------------------------------------------------------------------------- 1 | import formatDate from "./utils/formatDateutils"; 2 | import { VersionInfo } from "../../hooks/useVersionCheck"; 3 | import { Heading, Dialog, Paragraph } from "@digdir/designsystemet-react"; 4 | import styles from "./styles/VersionDialogTitle.module.css"; 5 | 6 | type VersionDialogTitleProps = { 7 | versionInfo: VersionInfo | null; 8 | }; 9 | 10 | const VersionDialogTitle: React.FC = ({ 11 | versionInfo, 12 | }) => { 13 | return ( 14 | 15 | 16 | Ny versjon: {versionInfo?.version} 🎉🥳 17 | 18 | 19 | Versjon {versionInfo?.version} ble lansert{" "} 20 | {formatDate(versionInfo?.releaseDate ?? "")} 21 | 22 | 23 | ); 24 | }; 25 | 26 | export default VersionDialogTitle; 27 | 28 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/Sidebar/NavItem.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactElement } from "react"; 2 | import { Tooltip } from "@digdir/designsystemet-react"; 3 | import { NavLink } from "react-router-dom"; 4 | import classes from "./styles/NavItem.module.css"; 5 | 6 | interface NavItemProps { 7 | to: string; 8 | title: string; 9 | icon: ReactElement; 10 | isCollapsed: boolean; 11 | } 12 | 13 | const NavItem: React.FC = ({ to, title, icon, isCollapsed }) => { 14 | return ( 15 | 18 | `${classes.navButton} ${isActive ? classes.navButtonSelected : ""} ${isCollapsed ? classes.navButtonCollapsed : ""}` 19 | } 20 | > 21 | {isCollapsed ? ( 22 | 23 | {icon} 24 | 25 | ) : ( 26 | title 27 | )} 28 | 29 | ); 30 | }; 31 | 32 | export default NavItem; 33 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./app/App"; 4 | import "@digdir/designsystemet-theme"; 5 | import "@digdir/designsystemet-css"; 6 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; 7 | import { useAppStore } from "./stores/Appstore"; 8 | import "../design-tokens-build/test2.css"; 9 | 10 | const queryClient = new QueryClient(); 11 | 12 | export const Root = () => { 13 | const isDarkmode = useAppStore((state) => state.isDarkMode); 14 | 15 | return ( 16 |
20 | 21 |
22 | ); 23 | }; 24 | 25 | ReactDOM.createRoot(document.getElementById("root")!).render( 26 | 27 | 28 | 29 | 30 | , 31 | ); 32 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Models/OrganizationModel.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | public class Organization 4 | { 5 | public string? Name { get; set; } 6 | public string? OrganizationNumber { get; set; } 7 | public string? Type { get; set; } 8 | public DateTime? LastChanged { get; set; } 9 | public DateTime? LastConfirmed { get; set; } 10 | 11 | [JsonPropertyName("_links")] 12 | public List? Links { get; set; } 13 | } 14 | 15 | public class OrganizationLink 16 | { 17 | public string? Rel { get; set; } 18 | public string? Href { get; set; } 19 | public string? Title { get; set; } 20 | public string? FileNameWithExtension { get; set; } 21 | public string? MimeType { get; set; } 22 | public bool IsTemplated { get; set; } 23 | public bool Encrypted { get; set; } 24 | public bool SigningLocked { get; set; } 25 | public bool SignedByDefault { get; set; } 26 | public int FileSize { get; set; } 27 | } 28 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/design-tokens/$metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "tokenSetOrder": [ 3 | "primitives/globals", 4 | "primitives/modes/size/small", 5 | "primitives/modes/size/medium", 6 | "primitives/modes/size/large", 7 | "primitives/modes/size/global", 8 | "primitives/modes/typography/size/small", 9 | "primitives/modes/typography/size/medium", 10 | "primitives/modes/typography/size/large", 11 | "primitives/modes/typography/primary/test2", 12 | "primitives/modes/typography/secondary/test2", 13 | "primitives/modes/color-scheme/dark/global", 14 | "primitives/modes/color-scheme/dark/test2", 15 | "primitives/modes/color-scheme/light/global", 16 | "primitives/modes/color-scheme/light/test2", 17 | "themes/test2", 18 | "semantic/color", 19 | "semantic/modes/main-color/accent", 20 | "semantic/modes/support-color/brand1", 21 | "semantic/modes/support-color/brand2", 22 | "semantic/modes/support-color/brand3", 23 | "semantic/style" 24 | ] 25 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/SettingsContent/SettingsActionButtons.tsx: -------------------------------------------------------------------------------- 1 | import { Card, Button } from "@digdir/designsystemet-react"; 2 | import classes from "./styles/SettingsActionsButtons.module.css"; 3 | import { initiateSignOut } from "../../utils/ansattportenApi"; 4 | 5 | const SettingsActionButtons: React.FC = () => { 6 | const handleReload = () => { 7 | window.location.reload(); 8 | }; 9 | 10 | const handleLogout = () => { 11 | initiateSignOut("/signin"); 12 | }; 13 | return ( 14 | 15 | 23 | 26 | 27 | ); 28 | }; 29 | 30 | export default SettingsActionButtons; 31 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/Dashboard/styles/DetailedOrgView.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | flex-grow: 1; 4 | gap: 20px; 5 | overflow: auto; 6 | max-height: 80vh; 7 | } 8 | 9 | .orgDetails { 10 | flex: 2; 11 | padding: 20px; 12 | border-radius: 10px; 13 | overflow-y: auto; 14 | max-height: calc(100vh - 100px); 15 | overflow-x: auto; 16 | transition: all 0.5s ease; 17 | } 18 | .orgDetails.full-width { 19 | flex: 1; 20 | width: 100%; 21 | } 22 | 23 | .orgNumer { 24 | font-size: 1rem; 25 | } 26 | 27 | .orgName { 28 | font-size: 2rem; 29 | } 30 | 31 | .officialContactContainer { 32 | margin: 0.5rem 0 1.5rem 0; 33 | display: flex; 34 | flex-direction: column; 35 | box-shadow: var(--ds-shadow-sm); 36 | } 37 | 38 | .officialContactBottom { 39 | display: flex; 40 | gap: 2rem; 41 | flex: 1; 42 | overflow-x: auto; 43 | flex-direction: column; 44 | } 45 | 46 | @media (min-width: 2100px) { 47 | .officialContactBottom { 48 | flex-direction: row; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/ManualRoleSearch/ManualRoleSearchButton.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Button } from '@digdir/designsystemet-react'; 3 | import style from "./styles/SearchButton.module.css"; 4 | 5 | type SearchButtonProps = { 6 | rollehaver: string; 7 | rollegiver: string; 8 | isLoading: boolean; 9 | refetch: () => void; 10 | sethasSearched: (value: boolean) => void; 11 | }; 12 | 13 | const SearchButton: React.FC = ({ 14 | rollehaver, 15 | rollegiver, 16 | isLoading, 17 | refetch, 18 | sethasSearched, 19 | }) => { 20 | 21 | 22 | const handleSearch = async () => { 23 | sethasSearched(true); 24 | refetch(); 25 | }; 26 | return ( 27 |
28 | 34 |
35 | ); 36 | }; 37 | 38 | export default SearchButton; 39 | 40 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/SettingsContent/SettingsDarkModeComponent.tsx: -------------------------------------------------------------------------------- 1 | import classes from "./styles/SettingsDarkModeComponent.module.css"; 2 | 3 | import { Card, Switch, Heading } from "@digdir/designsystemet-react"; 4 | 5 | import { useAppStore } from "../../stores/Appstore"; 6 | 7 | const SettingsDarkModeComponent: React.FC = () => { 8 | const isDarkMode = useAppStore((state) => state.isDarkMode); 9 | const setIsDarkMode = useAppStore((state) => state.setIsDarkMode); 10 | 11 | const toggleDarkMode = (event: React.ChangeEvent) => { 12 | const newDarkModeState = event.target.checked; 13 | setIsDarkMode(newDarkModeState); 14 | }; 15 | 16 | return ( 17 | 18 | Darkmode 19 | 25 | 26 | ); 27 | }; 28 | 29 | export default SettingsDarkModeComponent; 30 | -------------------------------------------------------------------------------- /backend/README.md: -------------------------------------------------------------------------------- 1 | # Data vi trenger fra nye APIer for å få samme funksjon 2 | 3 | * Organisasjon 4 | * Organisasjonsnummer 5 | * Type 6 | * AS, DA, ANS for eksempel 7 | * Underenheten sitt organisasjonsnummer 8 | * Trenger også her organisasjonsnummer for hovedenheten 9 | * Varslingsadresse for virksomhete 10 | * Telefon nummer og epost 11 | * Når disse ble endret sist 12 | * Person 13 | * Navn 14 | * Fødselsnummer 15 | * Mobilnummer 16 | * Når denne ble endret sist 17 | * Epost 18 | * Når denne ble endret sist 19 | * Roller og enkelttjenester 20 | * Rollenavn 21 | * Daglig leder for eksempel 22 | * Beskrivelse 23 | * Beskrivelse av selve rollen 24 | * Rolledefinisjonskode 25 | * DAGL for daglig leder for eksempel 26 | * Rolletype 27 | * Altinn, Single rights, External, Local 28 | * Om mulig selve navnet på enkelttjenesten 29 | * ER-roller til personen 30 | * Når de ble endret 31 | * Status 32 | 33 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Security/AltinnResourceHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Claims; 2 | using Microsoft.AspNetCore.Authorization; 3 | 4 | public class AltinnResourceHandler : AuthorizationHandler 5 | { 6 | protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, AltinnResourceRequirement requirement) 7 | { 8 | 9 | var claims = context.User.Claims.Where(c => c.Type == "authorization_details"); 10 | 11 | foreach (Claim claim in claims) 12 | { 13 | var json = System.Text.Json.JsonDocument.Parse(claim.Value); 14 | if (json.RootElement.TryGetProperty("resource", out var resource)) 15 | { 16 | string resourceString = resource.ToString(); 17 | if (resourceString == requirement.resource) 18 | { 19 | context.Succeed(requirement); 20 | return Task.CompletedTask; 21 | } 22 | } 23 | } 24 | 25 | return Task.CompletedTask; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/SettingsContent/SettingsLanguageComponent.tsx: -------------------------------------------------------------------------------- 1 | import classes from "./styles/SettingsLanguageComponent.module.css"; 2 | 3 | import { 4 | Card, 5 | Heading, 6 | Select, 7 | SelectOption, 8 | } from "@digdir/designsystemet-react"; 9 | import { useState } from "react"; 10 | 11 | const SettingsLanguageComponent: React.FC = () => { 12 | const [language, setLanguage] = useState("nb"); 13 | const handleLanguageChange = ( 14 | event: React.ChangeEvent, 15 | ) => { 16 | setLanguage(event.target.value as string); 17 | }; 18 | 19 | return ( 20 | 21 | 22 | Språkvalg 23 | 24 | 31 | 32 | ); 33 | }; 34 | 35 | export default SettingsLanguageComponent; 36 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/pages/SettingsPage.tsx: -------------------------------------------------------------------------------- 1 | import classes from "./styles/SettingsPage.module.css"; 2 | import { Heading } from "@digdir/designsystemet-react"; 3 | import SettingsPATComponent from "../components/SettingsContent/SettingsPATComponent"; 4 | import SettingsDarkModeComponent from "../components/SettingsContent/SettingsDarkModeComponent"; 5 | import SettingsLanguageComponent from "../components/SettingsContent/SettingsLanguageComponent"; 6 | import SettingsActionButtons from "../components/SettingsContent/SettingsActionButtons"; 7 | import SettingsVersionComponent from "../components/SettingsContent/SettingsVersionComponent"; 8 | 9 | const SettingsPage: React.FC = () => { 10 | return ( 11 |
12 | 13 | Innstillinger 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | ); 23 | }; 24 | 25 | export default SettingsPage; 26 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 5 | "target": "ES2020", 6 | "useDefineForClassFields": true, 7 | "lib": [ 8 | "ES2020", 9 | "DOM", 10 | "DOM.Iterable" 11 | ], 12 | "module": "ESNext", 13 | /* Bundler mode */ 14 | "moduleResolution": "bundler", 15 | "allowImportingTsExtensions": true, 16 | "resolveJsonModule": true, 17 | "isolatedModules": true, 18 | "moduleDetection": "force", 19 | "noEmit": true, 20 | "jsx": "react-jsx", 21 | /* Linting - redusert for å unngå lint-feil */ 22 | "strict": true, 23 | "noUnusedLocals": false, 24 | "noUnusedParameters": false, 25 | "noFallthroughCasesInSwitch": false, 26 | "noImplicitAny": true, 27 | "skipLibCheck": true 28 | }, 29 | "include": [ 30 | "src/**/*.ts", 31 | "src/**/*.tsx" 32 | ], 33 | "exclude": [ 34 | "**/*.test.ts", 35 | "**/*.test.tsx", 36 | "**/node_modules", 37 | "./dist" 38 | ], 39 | "types": [] 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Altinn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Security/AnsattportenConstants.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Security; 3 | 4 | public class AnsattportenConstants 5 | { 6 | public const string AnsattportenAuthenticationScheme = "AnsattportenAuthScheme"; 7 | public const string AnsattportenAuthorizationPolicy = "AnsattportenAuthenticated"; 8 | public const string AnsattportenCookiesAuthenticationScheme = "AnsattportenCookies"; 9 | public const string AnsattportenClaimType = "ansattporten:altinn:resource"; 10 | 11 | 12 | //these constants give authentication for different parts of the dashboard 13 | public const string AnsattportenTT02AuthorizationPolicy = "TT02Authenticated"; 14 | public const string AnsattportenProductionAuthorizationPolicy = "ProductionAuthenticated"; 15 | 16 | public static List GetPolicies() 17 | { 18 | List policies = new List(); 19 | 20 | policies.Add(AnsattportenAuthorizationPolicy); 21 | policies.Add(AnsattportenTT02AuthorizationPolicy); 22 | policies.Add(AnsattportenProductionAuthorizationPolicy); 23 | 24 | return policies; 25 | } 26 | 27 | } 28 | 29 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Services/Interfaces/IAltinnApiService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using altinn_support_dashboard.Server.Models; 3 | using Models.altinn3Dtos; 4 | 5 | namespace altinn_support_dashboard.Server.Services.Interfaces 6 | { 7 | public interface IAltinnApiService 8 | { 9 | Task GetOrganizationInfo(string orgNumber, string environment); 10 | Task> GetOrganizationsByPhoneNumber(string phoneNumber, string environment); 11 | Task> GetOrganizationsByEmail(string email, string environment); 12 | Task> GetPersonalContacts(string orgNumber, string environment); 13 | 14 | Task> GetOfficialContacts(string orgNumber, string environment); 15 | 16 | Task> GetPersonRoles(string subject, string reportee, string environment); 17 | 18 | Task> GetPersonalContactsAltinn3(string orgNumber, string environment); 19 | 20 | Task> GetNotificationAddressesAltinn3(string orgNumber, string environment); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/VersionDialog/VersionDialogBox.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | Dialog 4 | } from '@digdir/designsystemet-react'; 5 | import type { VersionInfo } from '../../hooks/useVersionCheck'; 6 | import VersionDialogTitle from './VersionDialogTitle'; 7 | import VersionDialogContent from './VersionDialogContent'; 8 | import VersionDialogButton from './VersionDialogButton'; 9 | 10 | 11 | interface VersionDialogBoxProps { 12 | versionInfo: VersionInfo | null; 13 | open: boolean; 14 | onClose: () => void; 15 | } 16 | 17 | 18 | const VersionDialogBox: React.FC =({ 19 | versionInfo, 20 | open, 21 | onClose 22 | }) => { 23 | if (!versionInfo || !open) return null; 24 | return ( 25 | 30 | 31 | 32 | 33 | 34 | ); 35 | } 36 | 37 | export default VersionDialogBox; -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Models/GiteaConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace altinn_support_dashboard.Server.Models 2 | { 3 | /// 4 | /// Konfigurasjon for Gitea API 5 | /// 6 | public class GiteaConfiguration 7 | { 8 | /// 9 | /// Development miljø konfigurasjon (dev.altinn.studio) 10 | /// 11 | public required GiteaEnvironmentConfiguration Development { get; set; } 12 | 13 | /// 14 | /// Production miljø konfigurasjon (altinn.studio) 15 | /// 16 | public required GiteaEnvironmentConfiguration Production { get; set; } 17 | } 18 | 19 | /// 20 | /// Konfigurasjon for et spesifikt Gitea miljø 21 | /// 22 | public class GiteaEnvironmentConfiguration 23 | { 24 | /// 25 | /// Base URL for Gitea API 26 | /// 27 | public required string BaseUrl { get; set; } 28 | 29 | /// 30 | /// Timeout i sekunder for API-kall 31 | /// 32 | public int Timeout { get; set; } = 100; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/ManualRoleSearch/ManualRoleSearchResult.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import RoleTable from "./ManualRoleSearchTable"; 3 | import { Alert, Heading } from "@digdir/designsystemet-react"; 4 | import styles from "./styles/ResultTable.module.css"; 5 | import { Role } from "../../models/models"; 6 | 7 | type ManualRoleSearchResultProps = { 8 | error: Error | null; 9 | isLoading: boolean; 10 | hasSearched: boolean; 11 | roles: Role[]; 12 | }; 13 | 14 | const ManualRoleSearchResult: React.FC = ({ 15 | error, 16 | isLoading, 17 | hasSearched, 18 | roles, 19 | }) => { 20 | return ( 21 |
22 | {error && {error.message} } 23 | {isLoading && Laster roller...} 24 | {!isLoading && hasSearched && roles.length === 0 && !error && ( 25 | Ingen roller funnet. 26 | )} 27 | {roles.length > 0 && !error && } 28 |
29 | ); 30 | }; 31 | 32 | export default ManualRoleSearchResult; 33 | 34 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/ManualRoleSearch/ManualRoleEmptySearchButton.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | setLocalStorageValue 4 | } from "./utils/storageUtils"; 5 | import { Button } from '@digdir/designsystemet-react'; 6 | import styles from "./styles/EmptySearchButton.module.css"; 7 | 8 | 9 | type EmptySearchProps = { 10 | sethasSearched: (value: boolean) => void; 11 | setRollehaver: (value: string) => void; 12 | setRollegiver: (value: string) => void; 13 | }; 14 | 15 | const EmptySearch: React.FC = ({ 16 | sethasSearched, 17 | setRollehaver, 18 | setRollegiver 19 | }) => { 20 | 21 | 22 | const clearSearch = () => { 23 | setRollehaver(""); 24 | setRollegiver(""); 25 | setLocalStorageValue("rollehaver", ""); 26 | setLocalStorageValue("rollegiver", ""); 27 | sethasSearched(false); 28 | } 29 | 30 | return ( 31 |
32 | 35 |
36 | ); 37 | }; 38 | 39 | export default EmptySearch; 40 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Models/RoleModel.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace altinn_support_dashboard.Server.Models 4 | { 5 | public class Role 6 | { 7 | public int? RoleId { get; set; } 8 | public string? RoleType { get; set; } 9 | public int? RoleDefinitionId { get; set; } 10 | public string? RoleName { get; set; } 11 | public string? RoleDescription { get; set; } 12 | public string? RoleDefinitionCode { get; set; } 13 | 14 | [JsonPropertyName("_links")] 15 | public List? Links { get; set; } 16 | 17 | 18 | 19 | } 20 | public class RoleLink 21 | { 22 | public string? Rel { get; set; } 23 | public string? Href { get; set; } 24 | public string? Title { get; set; } 25 | public string? FileNameWithExtension { get; set; } 26 | public string? MimeType { get; set; } 27 | public bool IsTemplated { get; set; } 28 | public bool Encrypted { get; set; } 29 | public bool SigningLocked { get; set; } 30 | public bool SignedByDefault { get; set; } 31 | public int FileSize { get; set; } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/public/version.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "4.0.0", 3 | "releaseDate": "2025-11-06", 4 | "changes": [ 5 | { 6 | "title": "Designsystemet og refaktorering", 7 | "description": "Denne releasen inkluderer implementasjon av designsystemet sitt komponentsystem, og en del mindre endringer. Både frontend og backend har blitt gjort moderate endringer på for å gjøre videreutvikling av ASD enklere og mere effektivt i fremtiden.", 8 | "details": [ 9 | "Frontend er refaktorert til å bruke designsystemet i stedet for Google MUI. Layout og funksjonalitet er stort sett uendret.", 10 | "Når du holder musepekeren over e-post eller telefonnummer til en kontaktperson, vises nå informasjon om når disse opplysningene sist ble endret.", 11 | "I søk med testorganisasjoner i tt02 som ikke kommer fra Tenor kommer nå ER informasjon som hvem som er daglig leder og deres SSN (bare i tt02).", 12 | "E-post og telefonnummer som blir brukt i søk skiller seg ut i organisasjonsinformasjonen.", 13 | "Flere endringer på baksiden gjør det enklere å feilsøke problemer og videreutvikle Altinn support dashboard fremover." 14 | ] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Models/PersonalContact.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text.Json.Serialization; 4 | 5 | //altinn 2 model, will be replaced with PersonalContactDto(altinn3) 6 | public class PersonalContact 7 | { 8 | public string? PersonalContactId { get; set; } 9 | public string? Name { get; set; } 10 | public string? SocialSecurityNumber { get; set; } 11 | public string? MobileNumber { get; set; } 12 | public DateTime? MobileNumberChanged { get; set; } 13 | public string? EMailAddress { get; set; } 14 | public DateTime? EMailAddressChanged { get; set; } 15 | 16 | [JsonPropertyName("_links")] 17 | public List? Links { get; set; } 18 | } 19 | 20 | public class PersonalContactLink 21 | { 22 | public string? Rel { get; set; } 23 | public string? Href { get; set; } 24 | public string? Title { get; set; } 25 | public string? FileNameWithExtension { get; set; } 26 | public string? MimeType { get; set; } 27 | public bool IsTemplated { get; set; } 28 | public bool Encrypted { get; set; } 29 | public bool SigningLocked { get; set; } 30 | public bool SignedByDefault { get; set; } 31 | public int FileSize { get; set; } 32 | } 33 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/app/App.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"); 2 | 3 | /* Global Styles */ 4 | * { 5 | margin: 0; 6 | box-sizing: border-box; 7 | } 8 | 9 | body, 10 | html { 11 | font-family: "Inter", sans-serif; 12 | height: 100%; 13 | overflow: hidden; 14 | } 15 | 16 | /* Animate background, text, and border colors smoothly when switching darkmode */ 17 | html, 18 | body, 19 | [data-color-scheme] * { 20 | transition: 21 | background-color 0.5s ease, 22 | color 0.5s ease, 23 | border-color 0.5s ease, 24 | fill 0.5s ease, 25 | stroke 0.5s ease; 26 | } 27 | 28 | html.theme-transition, 29 | html.theme-transition * { 30 | transition: 31 | background-color 0.25s ease-out, 32 | color 0.25s ease-out, 33 | border-color 0.25s ease-out, 34 | fill 0.25s ease-out, 35 | stroke 0.25s ease-out; 36 | } 37 | /* Wrapper */ 38 | .app-wrapper { 39 | display: flex; 40 | height: 100vh; 41 | overflow: hidden; 42 | 43 | background-color: var(--ds-color-neutral-background-tinted); 44 | } 45 | 46 | /* Main Content Styling */ 47 | .main-content { 48 | flex-grow: 1; 49 | padding: 40px; 50 | background-color: transparent; 51 | display: flex; 52 | flex-direction: column; 53 | overflow: hidden; 54 | } 55 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/Dashboard/components/contacts/ContactInfoCell.tsx: -------------------------------------------------------------------------------- 1 | import { useDashboardStore } from "../../../../stores/DashboardStore"; 2 | 3 | import { Tooltip, Label } from "@digdir/designsystemet-react"; 4 | 5 | import { formatDate } from "../../utils/dateUtils"; 6 | import { useEffect, useState } from "react"; 7 | import classes from "../../styles/ContactInfoCell.module.css"; 8 | 9 | interface ContactInfoCellProps { 10 | contact: string | null; 11 | contactLastChanged: string | null; 12 | } 13 | 14 | const ContactInfoCell: React.FC = ({ 15 | contact, 16 | contactLastChanged, 17 | }) => { 18 | const userInput = useDashboardStore((s) => s.query.replace(/\s/g, "")); 19 | 20 | const [isBold, setIsBold] = useState(false); 21 | 22 | //outlines if searchquery is part of the cell 23 | 24 | useEffect(() => { 25 | if (contact === userInput) { 26 | setIsBold(true); 27 | } 28 | }, [userInput]); 29 | return ( 30 |
31 | {contactLastChanged && ( 32 | 33 | 34 | 35 | )} 36 |
37 | ); 38 | }; 39 | 40 | export default ContactInfoCell; 41 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/Dashboard/styles/ERRolesTable.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | max-height: 40vh; 3 | overflow: auto; 4 | box-shadow: var(--ds-shadow-sm); 5 | } 6 | .heading { 7 | margin-bottom: 0.5rem; 8 | } 9 | 10 | .paragraph { 11 | text-align: center; 12 | font-size: var(--ds-font-size-2); 13 | } 14 | .header-font { 15 | font-size: var(--ds-font-size-2); 16 | } 17 | .cell-font { 18 | font-size: var(--ds-font-size-2); 19 | } 20 | 21 | @media (min-width: 1024px) { 22 | .container { 23 | overflow: auto; 24 | min-width: 40vw; 25 | } 26 | 27 | .paragraph { 28 | font-size: var(--ds-font-size-3); 29 | } 30 | 31 | .header-font { 32 | font-size: var(--ds-font-size-3); 33 | } 34 | .cell-font { 35 | font-size: var(--ds-font-size-3); 36 | } 37 | } 38 | 39 | @media (min-width: 1440px) { 40 | .paragraph { 41 | font-size: var(--ds-font-size-4); 42 | } 43 | 44 | .header-font { 45 | font-size: var(--ds-font-size-4); 46 | } 47 | .cell-font { 48 | font-size: var(--ds-font-size-4); 49 | } 50 | } 51 | @media (min-width: 1800px) { 52 | .paragraph { 53 | font-size: var(--ds-font-size-5); 54 | } 55 | 56 | .header-font { 57 | font-size: var(--ds-font-size-5); 58 | } 59 | .cell-font { 60 | font-size: var(--ds-font-size-5); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/Dashboard/styles/OrganizationCard.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | margin-bottom: 1rem; 3 | } 4 | 5 | .expandButton { 6 | margin-top: 1rem; 7 | position: absolute; 8 | right: 10px; 9 | bottom: 10px; 10 | padding-top: -0.5; 11 | min-height: 1rem; 12 | max-height: 3rem; 13 | } 14 | 15 | .card { 16 | position: relative; 17 | border-radius: 8px; 18 | box-shadow: var(--ds-shadow-sm); 19 | transition: box-shadow 0.3s ease; 20 | } 21 | [data-color-scheme="dark"] .card { 22 | box-shadow: 0 2px 8px rgba(255, 255, 255, 0.08); 23 | } 24 | 25 | .card:hover { 26 | box-shadow: var(--ds-shadow-lg); 27 | transform: translateY(-2px) 0.3s ease; 28 | } 29 | [data-color-scheme="dark"] .card:hover { 30 | transform: translateY(-4px); 31 | box-shadow: 0 6px 16px rgba(255, 255, 255, 0.1); 32 | } 33 | 34 | .subunit { 35 | margin-left: 20px; 36 | margin-top: 10px; 37 | } 38 | 39 | @media (max-width: 1439px) { 40 | .expandButton { 41 | max-height: 2rem; 42 | max-width: 3rem; 43 | } 44 | .card { 45 | min-height: 5rem; 46 | max-height: 10rem; 47 | } 48 | .cardHeader { 49 | font-size: var(--ds-font-size-3); 50 | } 51 | .cardParagraph { 52 | font-size: var(--ds-font-size-2); 53 | } 54 | .subunit .card { 55 | min-height: 5rem; 56 | max-height: 6rem; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/VersionDialog/VersionDialogContent.tsx: -------------------------------------------------------------------------------- 1 | import type { VersionInfo } from "../../hooks/useVersionCheck"; 2 | import { Dialog, Heading, Paragraph, List } from "@digdir/designsystemet-react"; 3 | import styles from "./styles/VersionDialogContent.module.css"; 4 | 5 | type VersionDialogContentProps = { 6 | versionInfo: VersionInfo | null; 7 | }; 8 | 9 | const VersionDialogContent: React.FC = ({ 10 | versionInfo, 11 | }) => { 12 | return ( 13 | 14 | {versionInfo && versionInfo.changes.length > 0 && ( 15 |
16 | 17 | {versionInfo.changes[0].title} 18 | 19 | 20 | {versionInfo.changes[0].description} 21 | 22 | {versionInfo.changes[0].details.length > 0 && ( 23 | 24 | {versionInfo.changes[0].details.map((detail, index) => ( 25 | 26 | {detail} 27 | 28 | ))} 29 | 30 | )} 31 |
32 | )} 33 |
34 | ); 35 | }; 36 | 37 | export default VersionDialogContent; 38 | -------------------------------------------------------------------------------- /backend/test/altinn-support-dashboard.backend.Tests/altinn-support-dashboard.backend.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | altinn_support_dashboard.backend.Tests 6 | enable 7 | enable 8 | 9 | false 10 | true 11 | 12 | 13 | 14 | 15 | all 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | 18 | 19 | 20 | 21 | 22 | all 23 | runtime; build; native; contentfiles; analyzers; buildtransitive 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/Dashboard/styles/ContactsTable.module.css: -------------------------------------------------------------------------------- 1 | .tableContainer { 2 | margin-bottom: 2rem; 3 | box-shadow: var(--ds-shadow-sm); 4 | overflow-x: auto; 5 | } 6 | 7 | .button { 8 | font-size: var(--ds-font-size-2); 9 | } 10 | 11 | .table { 12 | width: 100%; 13 | table-layout: auto; 14 | } 15 | 16 | .tableTitle { 17 | font-size: var(--ds-font-size-6); 18 | } 19 | 20 | .tableCell { 21 | overflow-wrap: break-word; 22 | font-size: var(--ds-font-size-2); 23 | } 24 | 25 | .tableHeaderCell { 26 | cursor: pointer; 27 | 28 | font-size: var(--ds-font-size-2); 29 | 30 | word-break: break-all; 31 | } 32 | 33 | @media (min-width: 1024px) { 34 | .tableCell { 35 | font-size: var(--ds-font-size-3); 36 | } 37 | 38 | .tableHeaderCell { 39 | font-size: var(--ds-font-size-3); 40 | } 41 | .button { 42 | font-size: var(--ds-font-size-3); 43 | } 44 | } 45 | 46 | @media (min-width: 1440px) { 47 | .tableCell { 48 | font-size: var(--ds-font-size-4); 49 | } 50 | 51 | .tableHeaderCell { 52 | font-size: var(--ds-font-size-4); 53 | } 54 | .button { 55 | font-size: var(--ds-font-size-4); 56 | } 57 | } 58 | @media (min-width: 1800px) { 59 | .tableCell { 60 | font-size: var(--ds-font-size-5); 61 | } 62 | .tableHeaderCell { 63 | font-size: var(--ds-font-size-5); 64 | } 65 | .button { 66 | font-size: var(--ds-font-size-5); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/ManualRoleSearch/ManualRoleSearchTable.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import RoleTypeCell from "../RoleTypeCell"; 3 | import { Table } from "@digdir/designsystemet-react"; 4 | import styles from "./styles/RoleTable.module.css"; 5 | import { Role } from "../../models/models"; 6 | 7 | interface RoleTableProps { 8 | roles: Role[]; 9 | } 10 | 11 | const RoleTable: React.FC = ({ roles }) => { 12 | return ( 13 |
14 | 15 | 16 | 17 | Rolletype 18 | Rollenavn 19 | Beskrivelse 20 | Rolledefinisjonskode 21 | 22 | 23 | 24 | {roles.map((role, index) => ( 25 | 26 | 27 | {role.roleName} 28 | {role.roleDescription} 29 | {role.roleDefinitionCode} 30 | 31 | ))} 32 | 33 |
34 |
35 | ); 36 | }; 37 | 38 | export default RoleTable; 39 | 40 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/Dashboard/utils/personUtils.ts: -------------------------------------------------------------------------------- 1 | import { ERRoles } from "../../../models/models"; 2 | 3 | interface PersonName { 4 | fornavn?: string; 5 | mellomnavn?: string | null; 6 | etternavn?: string; 7 | } 8 | 9 | interface PersonInfo { 10 | navn?: PersonName; 11 | fodselsdato?: string; 12 | erDoed?: boolean; 13 | } 14 | 15 | export const formatFullName = (person?: PersonInfo): string => { 16 | if (!person?.navn) return ""; 17 | const { fornavn = "", mellomnavn = "", etternavn = "" } = person.navn; 18 | return `${fornavn} ${mellomnavn ? mellomnavn + " " : ""}${etternavn}`.trim(); 19 | }; 20 | 21 | export const formatPersonStatus = (person?: PersonInfo): string => { 22 | if (!person) return ""; 23 | return person.erDoed ? " (Død)" : ""; 24 | }; 25 | 26 | export const formatRolePersonInfo = (role: ERRoles): string => { 27 | if (!role.roller?.[0]?.person) return ""; 28 | const person = role.roller[0].person; 29 | const name = formatFullName(person); 30 | const status = formatPersonStatus(person); 31 | return `${name}${status}`; 32 | }; 33 | 34 | export const formatRoleTypeInfo = (role: ERRoles): string => { 35 | if (!role.type?.beskrivelse) return ""; 36 | return role.type.beskrivelse; 37 | }; 38 | 39 | export const formatRoleDescription = (role: ERRoles): string => { 40 | const type = formatRoleTypeInfo(role); 41 | const person = formatRolePersonInfo(role); 42 | return `${type}: ${person}`.trim(); 43 | }; 44 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/wwwroot/dist/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/InformationDialog/InformationDialogContent.tsx: -------------------------------------------------------------------------------- 1 | import { Dialog, Paragraph, List } from "@digdir/designsystemet-react"; 2 | import { CheckmarkIcon, XMarkIcon } from "@navikt/aksel-icons"; 3 | import styles from "./styles/InformationDialogContent.module.css"; 4 | 5 | const InformationDialogContent: React.FC = () => { 6 | return ( 7 | 8 | 9 | Dette betyr at det er kunden som oppgir informasjon og du bekrefter om 10 | det stemmer eller ikke. Du skal ikke gi ut noen ny informasjon eller 11 | lese opp. 12 | 13 | 14 | 15 | 16 | Kunden sier adressen og du bekrefter eller avkrefter at den er 17 | riktig 18 | 19 | 23 | 24 | 25 | 26 | Kunden spør og du leser opp adressen 27 | 28 | 29 | 30 | 31 | 32 | ); 33 | }; 34 | 35 | export default InformationDialogContent; 36 | 37 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:57886", 8 | "sslPort": 44375 9 | } 10 | }, 11 | "profiles": { 12 | "https": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "launchUrl": "swagger", 17 | "applicationUrl": "https://localhost:5237", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development", 20 | "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy" 21 | } 22 | }, 23 | "http": { 24 | "commandName": "Project", 25 | "dotnetRunMessages": true, 26 | "launchBrowser": true, 27 | "launchUrl": "swagger", 28 | "applicationUrl": "http://localhost:5237", 29 | "environmentVariables": { 30 | "ASPNETCORE_ENVIRONMENT": "Development", 31 | "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy" 32 | } 33 | }, 34 | "IIS Express": { 35 | "commandName": "IISExpress", 36 | "launchBrowser": true, 37 | "launchUrl": "swagger", 38 | "environmentVariables": { 39 | "ASPNETCORE_ENVIRONMENT": "Development", 40 | "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy" 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/OrganizationCreation/form-fields/FullNameField.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Textfield, Paragraph, Tooltip } from '@digdir/designsystemet-react'; 3 | 4 | interface FullNameFieldProps { 5 | value: string; 6 | onChange: (value: string) => void; 7 | error?: string; 8 | } 9 | 10 | export const FullNameField: React.FC = ({ 11 | value, 12 | onChange, 13 | error 14 | }) => { 15 | return ( 16 |
17 |
18 | 19 | Fullt navn * 20 | 21 | 22 | ℹ️ 23 | 24 |
25 | 26 | onChange(e.target.value)} 30 | style={{ width: '100%' }} 31 | error={error} 32 | description={!error ? "Organisasjonens fulle navn" : undefined} 33 | aria-labelledby="fullname-label" 34 | /> 35 | Fullt navn 36 |
37 | ); 38 | }; 39 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/Sidebar/hooks/useSidebarDrag.ts: -------------------------------------------------------------------------------- 1 | import { 2 | useState, 3 | useEffect, 4 | MouseEvent as ReactMouseEvent, 5 | useCallback, 6 | } from "react"; 7 | 8 | export const useSidebarDrag = () => { 9 | const [isCollapsed, setIsCollapsed] = useState(false); 10 | const [isDragging, setIsDragging] = useState(false); 11 | const [dragStartX, setDragStartX] = useState(0); 12 | 13 | const toggleCollapse = () => { 14 | setIsCollapsed((prev) => !prev); 15 | }; 16 | 17 | const handleDragStart = (e: ReactMouseEvent) => { 18 | e.preventDefault(); 19 | setIsDragging(true); 20 | setDragStartX(e.clientX); 21 | }; 22 | 23 | const handleDragMove = useCallback( 24 | (e: MouseEvent) => { 25 | if (!isDragging) return; 26 | const dragDistance = e.clientX - dragStartX; 27 | if (Math.abs(dragDistance) > 50) { 28 | // Threshold for triggering expand/collapse 29 | setIsCollapsed(dragDistance < 0); 30 | setIsDragging(false); 31 | } 32 | }, 33 | [isDragging, dragStartX], 34 | ); 35 | 36 | const handleDragEnd = () => { 37 | setIsDragging(false); 38 | }; 39 | 40 | useEffect(() => { 41 | if (isDragging) { 42 | window.addEventListener("mousemove", handleDragMove); 43 | window.addEventListener("mouseup", handleDragEnd); 44 | } 45 | return () => { 46 | window.removeEventListener("mousemove", handleDragMove); 47 | window.removeEventListener("mouseup", handleDragEnd); 48 | }; 49 | }, [isDragging, dragStartX, handleDragMove]); 50 | 51 | return { isCollapsed, toggleCollapse, handleDragStart }; 52 | }; 53 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Security/AnsattportenLoginSettings.cs: -------------------------------------------------------------------------------- 1 | 2 | using System.Text.Json.Serialization; 3 | 4 | namespace Security; 5 | 6 | public class AnsattportenLoginSettings 7 | { 8 | public string? AcrValues { get; set; } 9 | 10 | public AuthorizationDetail[]? AuthorizationDetails { get; set; } 11 | 12 | 13 | /// 14 | /// Sets the cookie expiry time in minutes. 15 | /// 16 | public int CookieExpiryTimeInMinutes { get; set; } = 59; 17 | 18 | /// 19 | /// Url of the identity provider. 20 | /// 21 | public required string Authority { get; set; } 22 | 23 | /// 24 | /// Client ID for the OpenID Connect provider. 25 | /// 26 | public required string ClientId { get; set; } 27 | 28 | /// 29 | /// Client secret for the OpenID Connect provider. 30 | /// 31 | public required string ClientSecret { get; set; } 32 | 33 | /// 34 | /// Scopes for the OpenID Connect provider. 35 | /// 36 | public required string[] Scopes { get; set; } 37 | 38 | /// 39 | /// Flag to indicate if HTTPS metadata is required. In non-local environments this should be true. 40 | /// 41 | public bool RequireHttpsMetadata { get; set; } = true; 42 | 43 | } 44 | 45 | public class AuthorizationDetail 46 | { 47 | 48 | public required string Type { get; set; } 49 | 50 | public required string Resource { get; set; } 51 | 52 | public required bool? representation_is_required { get; set; } 53 | 54 | public required string organizationform { get; set; } 55 | } 56 | 57 | -------------------------------------------------------------------------------- /.github/workflows/backend-ci.yml: -------------------------------------------------------------------------------- 1 | name: Backend CI checks 2 | permissions: 3 | contents: read 4 | pull-requests: write 5 | 6 | on: 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | 13 | backend: 14 | name: Backend C# Build & Test 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | 20 | - name: Setup .NET 21 | uses: actions/setup-dotnet@v3 22 | with: 23 | dotnet-version: '8.0.x' # or your version 24 | 25 | - name: Restore dependencies 26 | run: dotnet restore ./backend/src/altinn-support-dashboard.backend 27 | 28 | - name: Build backend (warnings as errors) 29 | run: dotnet build ./backend/src/altinn-support-dashboard.backend --no-restore /warnaserror 30 | 31 | # Test pluss coverage 32 | - name: Run tests with coverage 33 | run: dotnet test --settings coverlet.runsettings 34 | working-directory: backend/test/altinn-support-dashboard.backend.Tests 35 | 36 | - name: Generate Coverage Summary 37 | uses: irongut/CodeCoverageSummary@f13848964841ca2f7a63343b287493e17dc645de 38 | with: 39 | format: markdown 40 | filename: "backend/test/altinn-support-dashboard.backend.Tests/TestResults/**/coverage.cobertura.xml" 41 | output: "both" 42 | fail_below_min: false 43 | badge: true 44 | packagename: "frontend" 45 | 46 | - name: Coverage PR Comment 47 | uses: marocchino/sticky-pull-request-comment@5060d4700a91de252c87eeddd2da026382d9298a 48 | with: 49 | header: "Backend Code Coverage" 50 | recreate: true 51 | path: "code-coverage-results.md" 52 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/utils/ansattportenApi.ts: -------------------------------------------------------------------------------- 1 | import { authDetails } from "../models/ansattportenModels"; 2 | import { authorizedFetch, getBaseUrl } from "./utils"; 3 | 4 | export const fetchAuthDetails = async (): Promise => { 5 | try { 6 | const res = await authorizedFetch(`${getBaseUrl()}/auth/auth-status`); 7 | 8 | if (!res.ok) { 9 | return { 10 | isLoggedIn: false, 11 | name: "", 12 | ansattportenActive: true, 13 | userPolicies: [], 14 | orgName: "", 15 | }; 16 | } 17 | const data = (await res.json()) as authDetails; 18 | 19 | return data; 20 | } catch (err) { 21 | return { 22 | isLoggedIn: false, 23 | name: "", 24 | ansattportenActive: true, 25 | userPolicies: [], 26 | orgName: "", 27 | }; 28 | } 29 | }; 30 | 31 | export const initiateSignIn = async (redirectTo: string) => { 32 | const authDetails = await fetchAuthDetails(); 33 | if (authDetails.ansattportenActive) { 34 | window.location.href = `${getBaseUrl()}/auth/login?redirectTo=${redirectTo}`; 35 | } 36 | }; 37 | 38 | //temporary 39 | export const initiateAiDevSignIn = async (redirectTo: string) => { 40 | window.location.href = `/.auth/login/aad?post_login_redirect_uri=${redirectTo}`; 41 | }; 42 | 43 | export const initiateSignOut = async (redirectTo: string) => { 44 | const authDetails = await fetchAuthDetails(); 45 | 46 | if (authDetails.ansattportenActive) { 47 | window.location.href = `${getBaseUrl()}/auth/logout?redirectTo=${redirectTo}`; 48 | } else { 49 | window.location.href = 50 | "/.auth/logout?post_logout_redirect_uri=/.auth/login/aad?post_login_redirect_uri=/dashboard"; 51 | } 52 | }; 53 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/ManualRoleSearch/ManualRoleSearchInput.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { setLocalStorageValue, } from "./utils/storageUtils"; 3 | import ManualRoleSearchTextField from "./ManualRoleSearchTextfield"; 4 | import { Tooltip } from "@digdir/designsystemet-react"; 5 | import style from "./styles/InputComponent.module.css"; 6 | 7 | 8 | type InputComponentProps = { 9 | rollehaver?: string; 10 | rollegiver?: string; 11 | setRollehaver?: (value: string) => void; 12 | setRollegiver?: (value: string) => void; 13 | }; 14 | 15 | const InputComponent: React.FC = ({ 16 | rollehaver, 17 | rollegiver, 18 | setRollehaver, 19 | setRollegiver, 20 | }) => { 21 | 22 | return ( 23 |
24 | 25 | 26 | { 30 | setRollegiver?.(value); 31 | setLocalStorageValue("rollegiver", value); 32 | }} 33 | /> 34 | 35 | 36 | 37 | 38 | { 42 | setRollehaver?.(value); 43 | setLocalStorageValue("rollehaver", value); 44 | }} 45 | /> 46 | 47 | 48 |
49 | ); 50 | }; 51 | 52 | export default InputComponent; -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/TopSearchBar/TopSearchBarTextField.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { Textfield, Button, Search } from "@digdir/designsystemet-react"; 3 | import styles from "./styles/TopSearchBarTextfield.module.css"; 4 | import { SelectedOrg } from "../../models/models"; 5 | 6 | type Props = { 7 | query: string; 8 | setQuery: (query: string) => void; 9 | setSelectedOrg: (selectedOrg: SelectedOrg | null) => void; 10 | }; 11 | 12 | export const TopSearchBarTextField: React.FC = ({ 13 | query, 14 | setQuery, 15 | setSelectedOrg, 16 | }) => { 17 | const [textFieldValue, setTextFieldValue] = useState(() => { 18 | if (query != null && query !== "") return query; 19 | return ""; 20 | }); 21 | const handleSearch = () => { 22 | if (textFieldValue != query) { 23 | setSelectedOrg(null); 24 | } 25 | setQuery(textFieldValue); 26 | }; 27 | 28 | return ( 29 |
30 | setTextFieldValue(e.target.value)} 35 | onKeyDown={(e) => { 36 | if (e.key === "Enter") { 37 | handleSearch(); 38 | } 39 | }} 40 | /> 41 | 50 | 57 |
58 | ); 59 | }; 60 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Validation/InputValidation.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | 3 | 4 | namespace altinn_support_dashboard.Server.Validation 5 | { 6 | public static class ValidationService 7 | { 8 | public static bool IsValidPhoneNumber(string phoneNumber) 9 | { 10 | return !string.IsNullOrWhiteSpace(phoneNumber) && ((phoneNumber[0] == '+' && phoneNumber.Skip(1).All(char.IsDigit)) || (phoneNumber.All(char.IsDigit))); 11 | } 12 | 13 | public static bool IsValidOrgNumber(string orgNumber) 14 | { 15 | return !string.IsNullOrWhiteSpace(orgNumber) && orgNumber.Length == 9 && orgNumber.All(char.IsDigit); 16 | } 17 | public static bool IsValidEmail(string email) 18 | { 19 | return Regex.IsMatch(email, @"^[^@\s]+@[^@\s]+\.[^@\s]+$"); 20 | } 21 | 22 | public static bool IsValidSubjectOrReportee(string value) 23 | { 24 | if (string.IsNullOrWhiteSpace(value)) 25 | { 26 | return false; 27 | } 28 | 29 | if (value.Length == 9 && long.TryParse(value, out _)) 30 | { 31 | return true; 32 | } 33 | 34 | if (value.Length == 11 && long.TryParse(value, out _)) 35 | { 36 | return true; 37 | } 38 | 39 | return false; 40 | } 41 | 42 | 43 | public static string SanitizeRedirectUrl(string? url) 44 | { 45 | //only relative redirect url's allowed 46 | 47 | if (!String.IsNullOrEmpty(url) && url.StartsWith('/') && !url.StartsWith("//") && !url.StartsWith(':')) 48 | { 49 | return url; 50 | } 51 | return "/"; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/SettingsContent/SettingsVersionComponent.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import classes from "./styles/SettingsVersionComponent.module.css"; 3 | 4 | import { FaSlack, FaBookOpen } from "react-icons/fa"; 5 | import { 6 | getVersionInfo, 7 | fetchVersionData, 8 | VersionData, 9 | } from "./utils/versionUtils"; 10 | import { useAppStore } from "../../stores/Appstore"; 11 | import { Paragraph, Link } from "@digdir/designsystemet-react"; 12 | 13 | const SettingsVersionComponent: React.FC = () => { 14 | const { versionNumber, versionName, releaseDate } = getVersionInfo(); 15 | const [versionInfo, setVersionInfo] = useState(null); 16 | 17 | const environment = useAppStore((state) => state.environment); 18 | 19 | useEffect(() => { 20 | const loadVersionInfo = async () => { 21 | const data = await fetchVersionData(); 22 | setVersionInfo(data); 23 | }; 24 | loadVersionInfo(); 25 | }, []); 26 | 27 | return ( 28 |
29 | 30 | Applikasjonsinformasjon: {versionName} - Versjon{" "} 31 | {versionInfo?.version || versionNumber} 32 | 33 | 34 | Utgivelsesdato:{" "} 35 | {versionInfo?.releaseDate || releaseDate || "Ikke tilgjengelig"} 36 | 37 | Valgt miljø: {environment} 38 |
39 | 40 | 41 | Dokumentasjon 42 | 43 | 47 | 48 | Kontakt oss på Slack 49 | 50 |
51 |
52 | ); 53 | }; 54 | 55 | export default SettingsVersionComponent; 56 | -------------------------------------------------------------------------------- /.github/workflows/frontend-ci.yml: -------------------------------------------------------------------------------- 1 | name: Frontend CI checks 2 | permissions: 3 | contents: read 4 | pull-requests: write 5 | 6 | on: 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | frontend: 13 | name: Frontend Full TypeScript Check 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | 19 | - name: Setup Node.js 20 | uses: actions/setup-node@v4 21 | with: 22 | node-version: '20' 23 | 24 | - name: Install dependencies 25 | run: npm ci 26 | working-directory: ./frontend/altinn-support-dashboard.client 27 | 28 | - name: TypeScript type check 29 | run: npm run typecheck 30 | working-directory: ./frontend/altinn-support-dashboard.client 31 | # package.json: "typecheck": "tsc --noEmit" 32 | 33 | - name: ESLint check 34 | run: npm run lint 35 | working-directory: ./frontend/altinn-support-dashboard.client 36 | # package.json: "lint": "eslint . --ext .ts,.tsx" 37 | 38 | 39 | #Tests and coverage report 40 | - name: Run tests with coverage 41 | working-directory: ./frontend/altinn-support-dashboard.client 42 | run: npm test -- --coverage 43 | 44 | - name: Generate Coverage Summary 45 | uses: irongut/CodeCoverageSummary@f13848964841ca2f7a63343b287493e17dc645de 46 | with: 47 | format: markdown 48 | filename: "frontend/altinn-support-dashboard.client/.coverage/cobertura-coverage.xml" 49 | output: "both" 50 | fail_below_min: false 51 | badge: true 52 | 53 | - name: Post PR Comment 54 | uses: marocchino/sticky-pull-request-comment@5060d4700a91de252c87eeddd2da026382d9298a 55 | with: 56 | header: "Frontend Code Coverage" 57 | recreate: true 58 | path: "code-coverage-results.md" 59 | 60 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/pages/SignOutPage.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Typography, Button, Container } from '@mui/material'; 3 | import LogoutIcon from '@mui/icons-material/Logout'; 4 | import LoginIcon from '@mui/icons-material/Login'; 5 | 6 | const SignOutPage: React.FC = () => { 7 | const handleLoginAgain = () => { 8 | window.location.href = '/'; 9 | }; 10 | 11 | return ( 12 | 26 | 27 | 28 | Du har logget ut 29 | 30 | 31 | Du er nå trygt utlogget. Vi sees snart igjen! 32 | 33 | 50 | 51 | ); 52 | }; 53 | 54 | export default SignOutPage; 55 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/OrganizationCreation/form-fields/WebsiteUrlField.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Textfield, Paragraph, Tooltip } from '@digdir/designsystemet-react'; 3 | 4 | interface WebsiteUrlFieldProps { 5 | value: string; 6 | onChange: (value: string) => void; 7 | error?: string; 8 | } 9 | 10 | export const WebsiteUrlField: React.FC = ({ 11 | value, 12 | onChange, 13 | error 14 | }) => { 15 | return ( 16 |
17 |
18 | 19 | Nettside 20 | 21 | 22 | ℹ️ 23 | 24 |
25 | 26 | { 29 | // Store the value without prefix in the form state 30 | // The validation will handle adding the prefix when needed 31 | const inputValue = e.target.value; 32 | onChange(inputValue); 33 | console.log('WebsiteUrl changed:', inputValue); 34 | }} 35 | prefix="https://" 36 | style={{ width: '100%' }} 37 | error={error} 38 | description={!error ? "F.eks. eksempel.no" : undefined} 39 | placeholder="www.domene.no" 40 | aria-labelledby="website-url-label" 41 | /> 42 | Nettside 43 |
44 | ); 45 | }; 46 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Services/AnsattportenService.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | using System.Security.Claims; 4 | using System.Text.Json; 5 | using altinn_support_dashboard.Server.Services.Interfaces; 6 | using Microsoft.AspNetCore.Authorization; 7 | using Microsoft.Extensions.Options; 8 | using Security; 9 | 10 | public class AnsattportenService : IAnsattportenService 11 | { 12 | private IAuthorizationService _authorizationService; 13 | 14 | public AnsattportenService(IAuthorizationService authorizationService) 15 | { 16 | _authorizationService = authorizationService; 17 | } 18 | public async Task> GetUserPolicies(ClaimsPrincipal user) 19 | { 20 | List policies = AnsattportenConstants.GetPolicies(); 21 | 22 | List userPolicies = new List(); 23 | 24 | foreach (string policy in policies) 25 | { 26 | AuthorizationResult result = await _authorizationService.AuthorizeAsync(user, policy); 27 | 28 | if (result.Succeeded) 29 | { 30 | userPolicies.Add(policy); 31 | 32 | } 33 | } 34 | return userPolicies; 35 | } 36 | 37 | public async Task GetRepresentationOrgName(ClaimsPrincipal user) 38 | { 39 | var authDetailsClaim = user.Claims?.FirstOrDefault(c => c.Type == "authorization_details")?.Value; 40 | 41 | 42 | if (!string.IsNullOrEmpty(authDetailsClaim)) 43 | { 44 | using var doc = JsonDocument.Parse(authDetailsClaim); 45 | 46 | // Navigate to authorized_parties[0].name 47 | var root = doc.RootElement; 48 | 49 | if (root.TryGetProperty("authorized_parties", out var parties) && parties.GetArrayLength() > 0) 50 | { 51 | var orgName = parties[0].GetProperty("name").GetString(); 52 | 53 | return orgName ?? ""; 54 | } 55 | } 56 | 57 | return ""; 58 | } 59 | } 60 | 61 | 62 | -------------------------------------------------------------------------------- /.github/workflows/altinn-support-dashboard-prod.yml: -------------------------------------------------------------------------------- 1 | # Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy 2 | # More GitHub Actions for Azure: https://github.com/Azure/actions 3 | 4 | name: Build and deploy to production 5 | 6 | on: 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - name: Set up .NET Core 17 | uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5 18 | with: 19 | dotnet-version: '8.x' 20 | 21 | - name: Build with dotnet 22 | run: dotnet build --configuration Release 23 | 24 | - name: dotnet publish 25 | run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/myapp 26 | 27 | - name: Upload artifact for deployment job 28 | uses: actions/upload-artifact@v4 29 | with: 30 | name: .net-app 31 | path: ${{env.DOTNET_ROOT}}/myapp 32 | 33 | deploy-to-prod: 34 | runs-on: ubuntu-latest 35 | needs: build 36 | environment: 37 | name: 'Production' 38 | url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} 39 | permissions: 40 | id-token: write #This is required for requesting the JWT 41 | 42 | steps: 43 | - name: Download artifact from build job 44 | uses: actions/download-artifact@v4 45 | with: 46 | name: .net-app 47 | 48 | - name: Login to Azure 49 | uses: azure/login@v2 50 | with: 51 | client-id: ${{ secrets.AZUREAPPSERVICE_CLIENTID_PROD }} 52 | tenant-id: ${{ secrets.AZUREAPPSERVICE_TENTANID_PROD }} 53 | subscription-id: ${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID_PROD }} 54 | 55 | - name: Deploy to Azure Web App 56 | id: deploy-to-webapp 57 | uses: azure/webapps-deploy@v3 58 | with: 59 | app-name: 'altinn-support-dashboard-prod' 60 | slot-name: 'Production' 61 | package: . 62 | -------------------------------------------------------------------------------- /backend/src/altinn-support-dashboard.backend/Services/Interfaces/IGiteaService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using altinn_support_dashboard.Server.Models.Gitea; 3 | 4 | namespace altinn_support_dashboard.Server.Services.Interfaces 5 | { 6 | /// 7 | /// Grensesnitt for Gitea-tjenester 8 | /// 9 | public interface IGiteaService 10 | { 11 | /// 12 | /// Validerer en PAT-token for et gitt miljø 13 | /// 14 | /// Miljønavnet 15 | /// PAT-token som skal valideres 16 | /// Resultat av valideringen 17 | Task ValidateTokenAsync(string environmentName, string token); 18 | 19 | /// 20 | /// Setter aktiv PAT-token for et miljø 21 | /// 22 | /// Miljønavnet 23 | /// PAT-token som skal brukes 24 | void SetToken(string environmentName, string token); 25 | 26 | /// 27 | /// Sjekker om en organisasjon eksisterer 28 | /// 29 | /// Miljønavnet 30 | /// Organisasjonens kortnavn 31 | /// True hvis organisasjonen eksisterer 32 | Task OrganizationExistsAsync(string environmentName, string orgName); 33 | 34 | /// 35 | /// Oppretter en ny organisasjon med standard team og repository 36 | /// 37 | /// Miljønavnet 38 | /// Informasjon om organisasjonen som skal opprettes 39 | /// Resultat av opprettelsen med opprettede teams og status 40 | Task CreateOrganizationAsync(string environmentName, OrganizationCreationRequest request); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/pages/DashboardPage.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import SearchComponent from "../components/TopSearchBar/TopSearchBarComponent"; 3 | import DetailedOrgView from "../components/Dashboard/components/DetailedOrgView"; 4 | import { OrganizationList } from "../components/Dashboard/components/organizations/OrganizationList"; 5 | import { useDashboardStore } from "../stores/DashboardStore"; 6 | import { Button } from "@digdir/designsystemet-react"; 7 | import InformationDialogBox from "../components/InformationDialog/InformationDialogBox"; 8 | import { InformationIcon } from "@navikt/aksel-icons"; 9 | import styles from "./styles/DashboardPage.module.css"; 10 | export const DashboardPage: React.FC = () => { 11 | const query = useDashboardStore((s) => s.query); 12 | const setQuery = useDashboardStore((s) => s.setQuery); 13 | const selectedOrg = useDashboardStore((s) => s.selectedOrg); 14 | const setSelectedOrg = useDashboardStore((s) => s.setSelectedOrg); 15 | const dialogRef = React.useRef(null); 16 | 17 | return ( 18 |
19 | 26 | 27 | 32 |
33 |
34 | 39 |
40 | 41 |
42 | 43 |
44 |
45 |
46 | ); 47 | }; 48 | 49 | export default DashboardPage; 50 | -------------------------------------------------------------------------------- /.github/workflows/altinn-support-dashboard-test.yml: -------------------------------------------------------------------------------- 1 | # Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy 2 | # More GitHub Actions for Azure: https://github.com/Azure/actions 3 | name: Build and deploy to test 4 | 5 | on: 6 | push: 7 | branches: 8 | - main 9 | workflow_dispatch: 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | 18 | - name: Set up .NET Core 19 | uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5 20 | with: 21 | dotnet-version: '8.x' 22 | 23 | - name: Build with dotnet 24 | run: dotnet build --configuration Release 25 | 26 | - name: dotnet publish 27 | run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/myapp 28 | 29 | - name: Upload artifact for deployment job 30 | uses: actions/upload-artifact@v4 31 | with: 32 | name: .net-app 33 | path: ${{env.DOTNET_ROOT}}/myapp 34 | 35 | deploy-to-test: 36 | runs-on: ubuntu-latest 37 | needs: build 38 | environment: 39 | name: 'Test' 40 | url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} 41 | permissions: 42 | id-token: write #This is required for requesting the JWT 43 | 44 | steps: 45 | - name: Download artifact from build job 46 | uses: actions/download-artifact@v4 47 | with: 48 | name: .net-app 49 | 50 | - name: Login to Azure 51 | uses: azure/login@v2 52 | with: 53 | client-id: ${{ secrets.AZUREAPPSERVICE_CLIENTID_PROD }} 54 | tenant-id: ${{ secrets.AZUREAPPSERVICE_TENTANID_PROD }} 55 | subscription-id: ${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID_PROD }} 56 | 57 | - name: Deploy to Azure Web App 58 | id: deploy-to-webapp 59 | uses: azure/webapps-deploy@v3 60 | with: 61 | app-name: 'altinn-support-dashboard-test-app' 62 | slot-name: 'Production' 63 | package: . 64 | 65 | 66 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/design-tokens/primitives/modes/typography/size/large.json: -------------------------------------------------------------------------------- 1 | { 2 | "line-height": { 3 | "sm": { 4 | "$type": "lineHeights", 5 | "$value": "130%" 6 | }, 7 | "md": { 8 | "$type": "lineHeights", 9 | "$value": "150%" 10 | }, 11 | "lg": { 12 | "$type": "lineHeights", 13 | "$value": "170%" 14 | } 15 | }, 16 | "font-size": { 17 | "1": { 18 | "$type": "fontSizes", 19 | "$value": "13" 20 | }, 21 | "2": { 22 | "$type": "fontSizes", 23 | "$value": "16" 24 | }, 25 | "3": { 26 | "$type": "fontSizes", 27 | "$value": "18" 28 | }, 29 | "4": { 30 | "$type": "fontSizes", 31 | "$value": "21" 32 | }, 33 | "5": { 34 | "$type": "fontSizes", 35 | "$value": "24" 36 | }, 37 | "6": { 38 | "$type": "fontSizes", 39 | "$value": "30" 40 | }, 41 | "7": { 42 | "$type": "fontSizes", 43 | "$value": "36" 44 | }, 45 | "8": { 46 | "$type": "fontSizes", 47 | "$value": "48" 48 | }, 49 | "9": { 50 | "$type": "fontSizes", 51 | "$value": "60" 52 | }, 53 | "10": { 54 | "$type": "fontSizes", 55 | "$value": "72" 56 | } 57 | }, 58 | "letter-spacing": { 59 | "1": { 60 | "$type": "letterSpacing", 61 | "$value": "-1%" 62 | }, 63 | "2": { 64 | "$type": "letterSpacing", 65 | "$value": "-0.5%" 66 | }, 67 | "3": { 68 | "$type": "letterSpacing", 69 | "$value": "-0.25%" 70 | }, 71 | "4": { 72 | "$type": "letterSpacing", 73 | "$value": "-0.15%" 74 | }, 75 | "5": { 76 | "$type": "letterSpacing", 77 | "$value": "0%" 78 | }, 79 | "6": { 80 | "$type": "letterSpacing", 81 | "$value": "0.15%" 82 | }, 83 | "7": { 84 | "$type": "letterSpacing", 85 | "$value": "0.25%" 86 | }, 87 | "8": { 88 | "$type": "letterSpacing", 89 | "$value": "0.5%" 90 | }, 91 | "9": { 92 | "$type": "letterSpacing", 93 | "$value": "1.5%" 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/design-tokens/primitives/modes/typography/size/medium.json: -------------------------------------------------------------------------------- 1 | { 2 | "line-height": { 3 | "sm": { 4 | "$type": "lineHeights", 5 | "$value": "130%" 6 | }, 7 | "md": { 8 | "$type": "lineHeights", 9 | "$value": "150%" 10 | }, 11 | "lg": { 12 | "$type": "lineHeights", 13 | "$value": "170%" 14 | } 15 | }, 16 | "font-size": { 17 | "1": { 18 | "$type": "fontSizes", 19 | "$value": "12" 20 | }, 21 | "2": { 22 | "$type": "fontSizes", 23 | "$value": "14" 24 | }, 25 | "3": { 26 | "$type": "fontSizes", 27 | "$value": "16" 28 | }, 29 | "4": { 30 | "$type": "fontSizes", 31 | "$value": "18" 32 | }, 33 | "5": { 34 | "$type": "fontSizes", 35 | "$value": "21" 36 | }, 37 | "6": { 38 | "$type": "fontSizes", 39 | "$value": "24" 40 | }, 41 | "7": { 42 | "$type": "fontSizes", 43 | "$value": "30" 44 | }, 45 | "8": { 46 | "$type": "fontSizes", 47 | "$value": "36" 48 | }, 49 | "9": { 50 | "$type": "fontSizes", 51 | "$value": "48" 52 | }, 53 | "10": { 54 | "$type": "fontSizes", 55 | "$value": "60" 56 | } 57 | }, 58 | "letter-spacing": { 59 | "1": { 60 | "$type": "letterSpacing", 61 | "$value": "-1%" 62 | }, 63 | "2": { 64 | "$type": "letterSpacing", 65 | "$value": "-0.5%" 66 | }, 67 | "3": { 68 | "$type": "letterSpacing", 69 | "$value": "-0.25%" 70 | }, 71 | "4": { 72 | "$type": "letterSpacing", 73 | "$value": "-0.15%" 74 | }, 75 | "5": { 76 | "$type": "letterSpacing", 77 | "$value": "0%" 78 | }, 79 | "6": { 80 | "$type": "letterSpacing", 81 | "$value": "0.15%" 82 | }, 83 | "7": { 84 | "$type": "letterSpacing", 85 | "$value": "0.25%" 86 | }, 87 | "8": { 88 | "$type": "letterSpacing", 89 | "$value": "0.5%" 90 | }, 91 | "9": { 92 | "$type": "letterSpacing", 93 | "$value": "1.5%" 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/design-tokens/primitives/modes/typography/size/small.json: -------------------------------------------------------------------------------- 1 | { 2 | "line-height": { 3 | "sm": { 4 | "$type": "lineHeights", 5 | "$value": "130%" 6 | }, 7 | "md": { 8 | "$type": "lineHeights", 9 | "$value": "150%" 10 | }, 11 | "lg": { 12 | "$type": "lineHeights", 13 | "$value": "170%" 14 | } 15 | }, 16 | "font-size": { 17 | "1": { 18 | "$type": "fontSizes", 19 | "$value": "11" 20 | }, 21 | "2": { 22 | "$type": "fontSizes", 23 | "$value": "13" 24 | }, 25 | "3": { 26 | "$type": "fontSizes", 27 | "$value": "14" 28 | }, 29 | "4": { 30 | "$type": "fontSizes", 31 | "$value": "16" 32 | }, 33 | "5": { 34 | "$type": "fontSizes", 35 | "$value": "18" 36 | }, 37 | "6": { 38 | "$type": "fontSizes", 39 | "$value": "21" 40 | }, 41 | "7": { 42 | "$type": "fontSizes", 43 | "$value": "24" 44 | }, 45 | "8": { 46 | "$type": "fontSizes", 47 | "$value": "30" 48 | }, 49 | "9": { 50 | "$type": "fontSizes", 51 | "$value": "36" 52 | }, 53 | "10": { 54 | "$type": "fontSizes", 55 | "$value": "48" 56 | } 57 | }, 58 | "letter-spacing": { 59 | "1": { 60 | "$type": "letterSpacing", 61 | "$value": "-1%" 62 | }, 63 | "2": { 64 | "$type": "letterSpacing", 65 | "$value": "-0.5%" 66 | }, 67 | "3": { 68 | "$type": "letterSpacing", 69 | "$value": "-0.25%" 70 | }, 71 | "4": { 72 | "$type": "letterSpacing", 73 | "$value": "-0.15%" 74 | }, 75 | "5": { 76 | "$type": "letterSpacing", 77 | "$value": "0%" 78 | }, 79 | "6": { 80 | "$type": "letterSpacing", 81 | "$value": "0.15%" 82 | }, 83 | "7": { 84 | "$type": "letterSpacing", 85 | "$value": "0.25%" 86 | }, 87 | "8": { 88 | "$type": "letterSpacing", 89 | "$value": "0.5%" 90 | }, 91 | "9": { 92 | "$type": "letterSpacing", 93 | "$value": "1.5%" 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/Sidebar/SidebarEnvToggle.tsx: -------------------------------------------------------------------------------- 1 | import classes from "./styles/SideBarEnvToggle.module.css"; 2 | import { useAppStore } from "../../stores/Appstore"; 3 | import { Select, SelectOption } from "@digdir/designsystemet-react"; 4 | import { useEffect } from "react"; 5 | import { useAuthDetails } from "../../hooks/ansattportenHooks"; 6 | 7 | const SidebarEnvToggle: React.FC = () => { 8 | const environment = useAppStore((state) => state.environment); 9 | const setEnvironment = useAppStore((state) => state.setEnvironment); 10 | const authDetails = useAuthDetails(); 11 | const userPolicies = authDetails?.data?.userPolicies; 12 | 13 | const handleEnvironmentChange = (env: string) => { 14 | setEnvironment(env); 15 | }; 16 | 17 | useEffect(() => { 18 | if (userPolicies == null) { 19 | return; 20 | } 21 | 22 | if ( 23 | environment == "TT02" && 24 | !userPolicies.includes("TT02Authenticated") && 25 | userPolicies.includes("ProductionAuthenticated") 26 | ) { 27 | setEnvironment("PROD"); 28 | } else if ( 29 | environment == "PROD" && 30 | !userPolicies.includes("ProductionAuthenticated") && 31 | userPolicies.includes("TT02Authenticated") 32 | ) { 33 | setEnvironment("TT02"); 34 | } 35 | }, [ 36 | authDetails?.data?.isLoggedIn, 37 | environment, 38 | setEnvironment, 39 | userPolicies, 40 | ]); 41 | 42 | return ( 43 |
44 | 58 |
59 | ); 60 | }; 61 | 62 | export default SidebarEnvToggle; 63 | -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/design-tokens/semantic/modes/main-color/accent.json: -------------------------------------------------------------------------------- 1 | { 2 | "color": { 3 | "main": { 4 | "background-default": { 5 | "$type": "color", 6 | "$value": "{color.accent.background-default}" 7 | }, 8 | "background-tinted": { 9 | "$type": "color", 10 | "$value": "{color.accent.background-tinted}" 11 | }, 12 | "surface-default": { 13 | "$type": "color", 14 | "$value": "{color.accent.surface-default}" 15 | }, 16 | "surface-tinted": { 17 | "$type": "color", 18 | "$value": "{color.accent.surface-tinted}" 19 | }, 20 | "surface-hover": { 21 | "$type": "color", 22 | "$value": "{color.accent.surface-hover}" 23 | }, 24 | "surface-active": { 25 | "$type": "color", 26 | "$value": "{color.accent.surface-active}" 27 | }, 28 | "border-subtle": { 29 | "$type": "color", 30 | "$value": "{color.accent.border-subtle}" 31 | }, 32 | "border-default": { 33 | "$type": "color", 34 | "$value": "{color.accent.border-default}" 35 | }, 36 | "border-strong": { 37 | "$type": "color", 38 | "$value": "{color.accent.border-strong}" 39 | }, 40 | "text-subtle": { 41 | "$type": "color", 42 | "$value": "{color.accent.text-subtle}" 43 | }, 44 | "text-default": { 45 | "$type": "color", 46 | "$value": "{color.accent.text-default}" 47 | }, 48 | "base-default": { 49 | "$type": "color", 50 | "$value": "{color.accent.base-default}" 51 | }, 52 | "base-hover": { 53 | "$type": "color", 54 | "$value": "{color.accent.base-hover}" 55 | }, 56 | "base-active": { 57 | "$type": "color", 58 | "$value": "{color.accent.base-active}" 59 | }, 60 | "base-contrast-subtle": { 61 | "$type": "color", 62 | "$value": "{color.accent.base-contrast-subtle}" 63 | }, 64 | "base-contrast-default": { 65 | "$type": "color", 66 | "$value": "{color.accent.base-contrast-default}" 67 | } 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/design-tokens/semantic/modes/support-color/brand1.json: -------------------------------------------------------------------------------- 1 | { 2 | "color": { 3 | "support": { 4 | "background-default": { 5 | "$type": "color", 6 | "$value": "{color.brand1.background-default}" 7 | }, 8 | "background-tinted": { 9 | "$type": "color", 10 | "$value": "{color.brand1.background-tinted}" 11 | }, 12 | "surface-default": { 13 | "$type": "color", 14 | "$value": "{color.brand1.surface-default}" 15 | }, 16 | "surface-tinted": { 17 | "$type": "color", 18 | "$value": "{color.brand1.surface-tinted}" 19 | }, 20 | "surface-hover": { 21 | "$type": "color", 22 | "$value": "{color.brand1.surface-hover}" 23 | }, 24 | "surface-active": { 25 | "$type": "color", 26 | "$value": "{color.brand1.surface-active}" 27 | }, 28 | "border-subtle": { 29 | "$type": "color", 30 | "$value": "{color.brand1.border-subtle}" 31 | }, 32 | "border-default": { 33 | "$type": "color", 34 | "$value": "{color.brand1.border-default}" 35 | }, 36 | "border-strong": { 37 | "$type": "color", 38 | "$value": "{color.brand1.border-strong}" 39 | }, 40 | "text-subtle": { 41 | "$type": "color", 42 | "$value": "{color.brand1.text-subtle}" 43 | }, 44 | "text-default": { 45 | "$type": "color", 46 | "$value": "{color.brand1.text-default}" 47 | }, 48 | "base-default": { 49 | "$type": "color", 50 | "$value": "{color.brand1.base-default}" 51 | }, 52 | "base-hover": { 53 | "$type": "color", 54 | "$value": "{color.brand1.base-hover}" 55 | }, 56 | "base-active": { 57 | "$type": "color", 58 | "$value": "{color.brand1.base-active}" 59 | }, 60 | "base-contrast-subtle": { 61 | "$type": "color", 62 | "$value": "{color.brand1.base-contrast-subtle}" 63 | }, 64 | "base-contrast-default": { 65 | "$type": "color", 66 | "$value": "{color.brand1.base-contrast-default}" 67 | } 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/design-tokens/semantic/modes/support-color/brand2.json: -------------------------------------------------------------------------------- 1 | { 2 | "color": { 3 | "support": { 4 | "background-default": { 5 | "$type": "color", 6 | "$value": "{color.brand2.background-default}" 7 | }, 8 | "background-tinted": { 9 | "$type": "color", 10 | "$value": "{color.brand2.background-tinted}" 11 | }, 12 | "surface-default": { 13 | "$type": "color", 14 | "$value": "{color.brand2.surface-default}" 15 | }, 16 | "surface-tinted": { 17 | "$type": "color", 18 | "$value": "{color.brand2.surface-tinted}" 19 | }, 20 | "surface-hover": { 21 | "$type": "color", 22 | "$value": "{color.brand2.surface-hover}" 23 | }, 24 | "surface-active": { 25 | "$type": "color", 26 | "$value": "{color.brand2.surface-active}" 27 | }, 28 | "border-subtle": { 29 | "$type": "color", 30 | "$value": "{color.brand2.border-subtle}" 31 | }, 32 | "border-default": { 33 | "$type": "color", 34 | "$value": "{color.brand2.border-default}" 35 | }, 36 | "border-strong": { 37 | "$type": "color", 38 | "$value": "{color.brand2.border-strong}" 39 | }, 40 | "text-subtle": { 41 | "$type": "color", 42 | "$value": "{color.brand2.text-subtle}" 43 | }, 44 | "text-default": { 45 | "$type": "color", 46 | "$value": "{color.brand2.text-default}" 47 | }, 48 | "base-default": { 49 | "$type": "color", 50 | "$value": "{color.brand2.base-default}" 51 | }, 52 | "base-hover": { 53 | "$type": "color", 54 | "$value": "{color.brand2.base-hover}" 55 | }, 56 | "base-active": { 57 | "$type": "color", 58 | "$value": "{color.brand2.base-active}" 59 | }, 60 | "base-contrast-subtle": { 61 | "$type": "color", 62 | "$value": "{color.brand2.base-contrast-subtle}" 63 | }, 64 | "base-contrast-default": { 65 | "$type": "color", 66 | "$value": "{color.brand2.base-contrast-default}" 67 | } 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/design-tokens/semantic/modes/support-color/brand3.json: -------------------------------------------------------------------------------- 1 | { 2 | "color": { 3 | "support": { 4 | "background-default": { 5 | "$type": "color", 6 | "$value": "{color.brand3.background-default}" 7 | }, 8 | "background-tinted": { 9 | "$type": "color", 10 | "$value": "{color.brand3.background-tinted}" 11 | }, 12 | "surface-default": { 13 | "$type": "color", 14 | "$value": "{color.brand3.surface-default}" 15 | }, 16 | "surface-tinted": { 17 | "$type": "color", 18 | "$value": "{color.brand3.surface-tinted}" 19 | }, 20 | "surface-hover": { 21 | "$type": "color", 22 | "$value": "{color.brand3.surface-hover}" 23 | }, 24 | "surface-active": { 25 | "$type": "color", 26 | "$value": "{color.brand3.surface-active}" 27 | }, 28 | "border-subtle": { 29 | "$type": "color", 30 | "$value": "{color.brand3.border-subtle}" 31 | }, 32 | "border-default": { 33 | "$type": "color", 34 | "$value": "{color.brand3.border-default}" 35 | }, 36 | "border-strong": { 37 | "$type": "color", 38 | "$value": "{color.brand3.border-strong}" 39 | }, 40 | "text-subtle": { 41 | "$type": "color", 42 | "$value": "{color.brand3.text-subtle}" 43 | }, 44 | "text-default": { 45 | "$type": "color", 46 | "$value": "{color.brand3.text-default}" 47 | }, 48 | "base-default": { 49 | "$type": "color", 50 | "$value": "{color.brand3.base-default}" 51 | }, 52 | "base-hover": { 53 | "$type": "color", 54 | "$value": "{color.brand3.base-hover}" 55 | }, 56 | "base-active": { 57 | "$type": "color", 58 | "$value": "{color.brand3.base-active}" 59 | }, 60 | "base-contrast-subtle": { 61 | "$type": "color", 62 | "$value": "{color.brand3.base-contrast-subtle}" 63 | }, 64 | "base-contrast-default": { 65 | "$type": "color", 66 | "$value": "{color.brand3.base-contrast-default}" 67 | } 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /frontend/altinn-support-dashboard.client/src/components/OrganizationCreation/form-fields/DescriptionField.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Paragraph, Tooltip, Textarea} from '@digdir/designsystemet-react'; 3 | 4 | interface DescriptionFieldProps { 5 | value: string; 6 | onChange: (value: string) => void; 7 | error?: string; 8 | } 9 | 10 | export const DescriptionField: React.FC = ({ 11 | value, 12 | onChange, 13 | error 14 | }) => { 15 | return ( 16 |
17 |
18 | 19 | Beskrivelse 20 | 21 | 22 | ℹ️ 23 | 24 |
25 | 26 | {error && ( 27 |
28 | {error} 29 |
30 | )} 31 | 32 |
33 |