From 6ca2d705b6080552e0e576870cb1860003e50bde Mon Sep 17 00:00:00 2001 From: unakb Date: Mon, 2 Dec 2024 13:57:56 +0000 Subject: [PATCH 01/85] fix(j-s): Prison admin indictment notifications (#17097) * fix(j-s): Prison admin indictment notifications Notifications weren't getting sent due to a bug in the defendant model that returned an undefined value for defendant.caseId * Update defendant.service.ts --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../src/app/modules/defendant/defendant.service.ts | 8 ++++++-- .../src/app/modules/defendant/models/defendant.model.ts | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/defendant.service.ts b/apps/judicial-system/backend/src/app/modules/defendant/defendant.service.ts index 59ca01645a40..fca293e95ffe 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/defendant.service.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/defendant.service.ts @@ -76,6 +76,7 @@ export class DefendantService { private getMessagesForIndictmentToPrisonAdminChanges( defendant: Defendant, + caseId: string, ): Message { const messageType = defendant.isSentToPrisonAdmin === true @@ -84,7 +85,7 @@ export class DefendantService { const message = { type: MessageType.DEFENDANT_NOTIFICATION, - caseId: defendant.caseId, + caseId, elementId: defendant.id, body: { type: messageType, @@ -196,7 +197,10 @@ export class DefendantService { updatedDefendant.isSentToPrisonAdmin !== oldDefendant.isSentToPrisonAdmin ) { messages.push( - this.getMessagesForIndictmentToPrisonAdminChanges(updatedDefendant), + this.getMessagesForIndictmentToPrisonAdminChanges( + updatedDefendant, + theCase.id, + ), ) } diff --git a/apps/judicial-system/backend/src/app/modules/defendant/models/defendant.model.ts b/apps/judicial-system/backend/src/app/modules/defendant/models/defendant.model.ts index bc31334d6bdd..f795a4980c13 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/models/defendant.model.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/models/defendant.model.ts @@ -81,7 +81,7 @@ export class Defendant extends Model { @ApiProperty({ type: String }) caseId!: string - @BelongsTo(() => Case, 'case_id') + @BelongsTo(() => Case, 'caseId') @ApiPropertyOptional({ type: () => Case }) case?: Case From d8e5cd2b5dd03d8dba282739fa372b4a512b471b Mon Sep 17 00:00:00 2001 From: brynjarorng Date: Mon, 2 Dec 2024 14:56:12 +0000 Subject: [PATCH 02/85] fix: add missing ns grant to api for bff (#17105) * fix: add missing ns grant to api for bff * chore: charts update dirty files * chore: nx format:write update dirty files --------- Co-authored-by: andes-it --- apps/api/infra/api.ts | 1 + charts/islandis/values.dev.yaml | 1 + charts/islandis/values.prod.yaml | 1 + charts/islandis/values.staging.yaml | 1 + charts/services/api/values.dev.yaml | 1 + charts/services/api/values.prod.yaml | 1 + charts/services/api/values.staging.yaml | 1 + 7 files changed, 7 insertions(+) diff --git a/apps/api/infra/api.ts b/apps/api/infra/api.ts index 3e2aa3417b11..471e4f82759b 100644 --- a/apps/api/infra/api.ts +++ b/apps/api/infra/api.ts @@ -478,5 +478,6 @@ export const serviceSetup = (services: { 'consultation-portal', 'portals-admin', 'service-portal', + 'portals-my-pages', ) } diff --git a/charts/islandis/values.dev.yaml b/charts/islandis/values.dev.yaml index 0df301b2879b..47abcaf93395 100644 --- a/charts/islandis/values.dev.yaml +++ b/charts/islandis/values.dev.yaml @@ -423,6 +423,7 @@ api: - 'consultation-portal' - 'portals-admin' - 'service-portal' + - 'portals-my-pages' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/islandis/values.prod.yaml b/charts/islandis/values.prod.yaml index 24fe5deaa1c1..cfe7870b0fc5 100644 --- a/charts/islandis/values.prod.yaml +++ b/charts/islandis/values.prod.yaml @@ -411,6 +411,7 @@ api: - 'consultation-portal' - 'portals-admin' - 'service-portal' + - 'portals-my-pages' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/islandis/values.staging.yaml b/charts/islandis/values.staging.yaml index 35e2f6d9e734..bb265c51b251 100644 --- a/charts/islandis/values.staging.yaml +++ b/charts/islandis/values.staging.yaml @@ -423,6 +423,7 @@ api: - 'consultation-portal' - 'portals-admin' - 'service-portal' + - 'portals-my-pages' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/services/api/values.dev.yaml b/charts/services/api/values.dev.yaml index 12179aba0a22..e1ea92a280b8 100644 --- a/charts/services/api/values.dev.yaml +++ b/charts/services/api/values.dev.yaml @@ -172,6 +172,7 @@ grantNamespaces: - 'consultation-portal' - 'portals-admin' - 'service-portal' + - 'portals-my-pages' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/services/api/values.prod.yaml b/charts/services/api/values.prod.yaml index 20f2273e0aac..ccea0dbcdf76 100644 --- a/charts/services/api/values.prod.yaml +++ b/charts/services/api/values.prod.yaml @@ -172,6 +172,7 @@ grantNamespaces: - 'consultation-portal' - 'portals-admin' - 'service-portal' + - 'portals-my-pages' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/services/api/values.staging.yaml b/charts/services/api/values.staging.yaml index 1de52abab703..d200690c8557 100644 --- a/charts/services/api/values.staging.yaml +++ b/charts/services/api/values.staging.yaml @@ -172,6 +172,7 @@ grantNamespaces: - 'consultation-portal' - 'portals-admin' - 'service-portal' + - 'portals-my-pages' grantNamespacesEnabled: true healthCheck: liveness: From 1eef914fdda5ac567cc00865a2bacfe791aa1768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Mon, 2 Dec 2024 15:30:02 +0000 Subject: [PATCH 03/85] chore(j-s): Robot Court Name (#17103) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../backend/src/app/modules/court/court.service.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/court/court.service.ts b/apps/judicial-system/backend/src/app/modules/court/court.service.ts index 5a521901c9a6..7113dd6769b0 100644 --- a/apps/judicial-system/backend/src/app/modules/court/court.service.ts +++ b/apps/judicial-system/backend/src/app/modules/court/court.service.ts @@ -524,7 +524,6 @@ export class CourtService { const subject = `${courtName} - ${courtCaseNumber} - lyktir` const content = JSON.stringify({ isCorrection, - courtName, courtCaseNumber, decision, rulingDate, @@ -581,7 +580,6 @@ export class CourtService { subtypes, defendants, prosecutor, - courtName, }) return this.sendToRobot( @@ -657,7 +655,7 @@ export class CourtService { ): Promise { try { const subject = `${courtName} - ${courtCaseNumber} - Ășthlutun` - const content = JSON.stringify({ ...assignedRole, courtName }) + const content = JSON.stringify(assignedRole) return this.sendToRobot( subject, @@ -692,7 +690,6 @@ export class CourtService { const content = JSON.stringify({ subject: noticeSubject, text: noticeText, - courtName, }) return this.sendToRobot( From 0d59ea66be26d2459afbc6d9956be36816eea689 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAnar=20Vestmann?= <43557895+RunarVestmann@users.noreply.github.com> Date: Mon, 2 Dec 2024 15:54:01 +0000 Subject: [PATCH 04/85] feat(web): Level 2 navigation for parent subpages should be visible despite theme (#17100) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- apps/web/pages/s/[...slugs]/index.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/web/pages/s/[...slugs]/index.tsx b/apps/web/pages/s/[...slugs]/index.tsx index 4f6c72f790ee..2605aea327f0 100644 --- a/apps/web/pages/s/[...slugs]/index.tsx +++ b/apps/web/pages/s/[...slugs]/index.tsx @@ -320,13 +320,17 @@ Component.getProps = async (context) => { } } - if (isStandaloneTheme) { + try { return { page: { type: PageType.STANDALONE_PARENT_SUBPAGE, props: await StandaloneParentSubpage.getProps(modifiedContext), }, } + } catch (error) { + if (!(error instanceof CustomNextError)) { + throw error + } } return { From a621cbe3ba4af24229c48770b38656372bba5442 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Tue, 3 Dec 2024 00:15:12 +0000 Subject: [PATCH 05/85] feat(j-s): Deliver Confirmed Indictment Ruling to Court (#17104) * Delivers confirmed indictment ruling to court * Only uploads ruling to court if ruling decision is ruling or dismillal --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../src/app/modules/case/case.service.ts | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/case/case.service.ts b/apps/judicial-system/backend/src/app/modules/case/case.service.ts index d59a3a2e5684..6454603536c8 100644 --- a/apps/judicial-system/backend/src/app/modules/case/case.service.ts +++ b/apps/judicial-system/backend/src/app/modules/case/case.service.ts @@ -921,14 +921,31 @@ export class CaseService { theCase: Case, user: TUser, ): Promise { - const messages: Message[] = [ - { - type: MessageType.NOTIFICATION, - user, - caseId: theCase.id, - body: { type: CaseNotificationType.RULING }, - }, - ] + const messages: Message[] = + theCase.caseFiles + ?.filter( + (caseFile) => + caseFile.state === CaseFileState.STORED_IN_RVG && + caseFile.key && + caseFile.category === CaseFileCategory.RULING && + theCase.indictmentRulingDecision && + [ + CaseIndictmentRulingDecision.RULING, + CaseIndictmentRulingDecision.DISMISSAL, + ].includes(theCase.indictmentRulingDecision), + ) + .map((caseFile) => ({ + type: MessageType.DELIVERY_TO_COURT_CASE_FILE, + user, + caseId: theCase.id, + elementId: caseFile.id, + })) ?? [] + messages.push({ + type: MessageType.NOTIFICATION, + user, + caseId: theCase.id, + body: { type: CaseNotificationType.RULING }, + }) if (theCase.origin === CaseOrigin.LOKE) { messages.push({ From ebd9b8fe98d3d859b9f1d6a80f8c0a00927f7e34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gunnlaugur=20Gu=C3=B0mundsson?= <34029342+GunnlaugurG@users.noreply.github.com> Date: Tue, 3 Dec 2024 10:37:53 +0000 Subject: [PATCH 06/85] feat(auth-admin): Allow non super admin to change Legal Representative Delegation type (#17107) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../screens/Permission/components/PermissionDelegations.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libs/portals/admin/ids-admin/src/screens/Permission/components/PermissionDelegations.tsx b/libs/portals/admin/ids-admin/src/screens/Permission/components/PermissionDelegations.tsx index d60a688358dc..822121e00696 100644 --- a/libs/portals/admin/ids-admin/src/screens/Permission/components/PermissionDelegations.tsx +++ b/libs/portals/admin/ids-admin/src/screens/Permission/components/PermissionDelegations.tsx @@ -118,10 +118,8 @@ export const PermissionDelegations = () => { {providers.map((provider) => !provider || (!isSuperAdmin && - (provider.id === - AuthDelegationProvider.PersonalRepresentativeRegistry || - provider.id === - AuthDelegationProvider.DistrictCommissionersRegistry)) ? null : ( + provider.id === + AuthDelegationProvider.PersonalRepresentativeRegistry) ? null : (
From fc0d3a86497b3d875560f815c048fd779e85639b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAnar=20Vestmann?= <43557895+RunarVestmann@users.noreply.github.com> Date: Tue, 3 Dec 2024 11:08:51 +0000 Subject: [PATCH 07/85] feat(web): Organization page with "standalone" theme - Level 2 sitemap (#17085) * Fix level 1 sitemap links * Update sitemap level 1 entry node links * Make sure that subpages belong to same main page * Fetch level 2 sitemap and display on a page * Edit breadcrumbs * Add comment * Add comment * Update error handler constant --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- apps/web/pages/s/[...slugs]/index.tsx | 31 +++ .../Organization/Standalone/Level2Sitemap.tsx | 177 ++++++++++++++++++ apps/web/screens/queries/Organization.tsx | 18 ++ libs/cms/src/lib/cms.contentful.service.ts | 168 ++++++++++++++++- libs/cms/src/lib/cms.resolver.ts | 20 +- ...OrganizationPageStandaloneSitemap.input.ts | 7 + .../src/lib/generated/contentfulTypes.d.ts | 3 + ...organizationPageStandaloneSitemap.model.ts | 30 +++ .../models/organizationParentSubpage.model.ts | 3 + 9 files changed, 447 insertions(+), 10 deletions(-) create mode 100644 apps/web/screens/Organization/Standalone/Level2Sitemap.tsx diff --git a/apps/web/pages/s/[...slugs]/index.tsx b/apps/web/pages/s/[...slugs]/index.tsx index 2605aea327f0..9b72d7cda505 100644 --- a/apps/web/pages/s/[...slugs]/index.tsx +++ b/apps/web/pages/s/[...slugs]/index.tsx @@ -34,6 +34,9 @@ import StandaloneHome, { import StandaloneLevel1Sitemap, { type StandaloneLevel1SitemapProps, } from '@island.is/web/screens/Organization/Standalone/Level1Sitemap' +import StandaloneLevel2Sitemap, { + type StandaloneLevel2SitemapProps, +} from '@island.is/web/screens/Organization/Standalone/Level2Sitemap' import StandaloneParentSubpage, { StandaloneParentSubpageProps, } from '@island.is/web/screens/Organization/Standalone/ParentSubpage' @@ -50,6 +53,7 @@ enum PageType { STANDALONE_FRONTPAGE = 'standalone-frontpage', STANDALONE_PARENT_SUBPAGE = 'standalone-parent-subpage', STANDALONE_LEVEL1_SITEMAP = 'standalone-level1-sitemap', + STANDALONE_LEVEL2_SITEMAP = 'standalone-level2-sitemap', SUBPAGE = 'subpage', ALL_NEWS = 'news', PUBLISHED_MATERIAL = 'published-material', @@ -69,6 +73,9 @@ const pageMap: Record> = { [PageType.STANDALONE_LEVEL1_SITEMAP]: (props) => ( ), + [PageType.STANDALONE_LEVEL2_SITEMAP]: (props) => ( + + ), [PageType.SUBPAGE]: (props) => , [PageType.ALL_NEWS]: (props) => , [PageType.PUBLISHED_MATERIAL]: (props) => , @@ -101,6 +108,10 @@ interface Props { type: PageType.STANDALONE_LEVEL1_SITEMAP props: StandaloneLevel1SitemapProps } + | { + type: PageType.STANDALONE_LEVEL2_SITEMAP + props: StandaloneLevel2SitemapProps + } | { type: PageType.SUBPAGE props: { @@ -320,6 +331,26 @@ Component.getProps = async (context) => { } } + if ( + isStandaloneTheme && + organizationPage.topLevelNavigation?.links.some( + (link) => slugs[1] === link.href.split('/').pop(), + ) + ) { + try { + return { + page: { + type: PageType.STANDALONE_LEVEL2_SITEMAP, + props: await StandaloneLevel2Sitemap.getProps(modifiedContext), + }, + } + } catch (error) { + if (!(error instanceof CustomNextError)) { + throw error + } + } + } + try { return { page: { diff --git a/apps/web/screens/Organization/Standalone/Level2Sitemap.tsx b/apps/web/screens/Organization/Standalone/Level2Sitemap.tsx new file mode 100644 index 000000000000..40f72761af7e --- /dev/null +++ b/apps/web/screens/Organization/Standalone/Level2Sitemap.tsx @@ -0,0 +1,177 @@ +import { useMemo } from 'react' + +import { + BreadCrumbItem, + Breadcrumbs, + GridColumn, + GridContainer, + GridRow, + LinkV2, + Stack, +} from '@island.is/island-ui/core' +import { Text } from '@island.is/island-ui/core' +import { + ContentLanguage, + OrganizationPage, + OrganizationPageStandaloneSitemapLevel2, + OrganizationPageTopLevelNavigationLink, + Query, + QueryGetOrganizationPageArgs, + QueryGetOrganizationPageStandaloneSitemapLevel2Args, +} from '@island.is/web/graphql/schema' +import useContentfulId from '@island.is/web/hooks/useContentfulId' +import { StandaloneLayout } from '@island.is/web/layouts/organization/standalone' +import type { Screen, ScreenContext } from '@island.is/web/types' +import { CustomNextError } from '@island.is/web/units/errors' + +import { + GET_ORGANIZATION_PAGE_QUERY, + GET_ORGANIZATION_PAGE_STANDALONE_SITEMAP_LEVEL2_QUERY, +} from '../../queries' + +const LanguageToggleSetup = (props: { ids: string[] }) => { + useContentfulId(...props.ids) + return null +} + +type StandaloneLevel2SitemapScreenContext = ScreenContext & { + organizationPage?: Query['getOrganizationPage'] +} + +export interface StandaloneLevel2SitemapProps { + organizationPage: OrganizationPage + category?: OrganizationPageTopLevelNavigationLink + sitemap: OrganizationPageStandaloneSitemapLevel2 +} + +const StandaloneLevel2Sitemap: Screen< + StandaloneLevel2SitemapProps, + StandaloneLevel2SitemapScreenContext +> = ({ organizationPage, category, sitemap }) => { + const breadcrumbItems: BreadCrumbItem[] = useMemo(() => { + const items: BreadCrumbItem[] = [] + + if (category) { + items.push({ + title: category.label, + href: category.href, + }) + } + return items + }, [category]) + + return ( + + + + + + + + + + {sitemap.label} + + + {sitemap.childCategories.map(({ label, childLinks }) => ( + + + {label} + + + {childLinks.map((link) => ( + + {link.label} + + ))} + + + ))} + + + + + + + + ) +} + +StandaloneLevel2Sitemap.getProps = async ({ + apolloClient, + locale, + query, + organizationPage, +}) => { + const [organizationPageSlug, categorySlug, subcategorySlug] = (query.slugs ?? + []) as string[] + + const [ + { + data: { getOrganizationPage }, + }, + { + data: { getOrganizationPageStandaloneSitemapLevel2 }, + }, + ] = await Promise.all([ + !organizationPage + ? apolloClient.query({ + query: GET_ORGANIZATION_PAGE_QUERY, + variables: { + input: { + slug: organizationPageSlug, + lang: locale as ContentLanguage, + }, + }, + }) + : { + data: { getOrganizationPage: organizationPage }, + }, + apolloClient.query< + Query, + QueryGetOrganizationPageStandaloneSitemapLevel2Args + >({ + query: GET_ORGANIZATION_PAGE_STANDALONE_SITEMAP_LEVEL2_QUERY, + variables: { + input: { + organizationPageSlug, + categorySlug, + subcategorySlug, + lang: locale as ContentLanguage, + }, + }, + }), + ]) + + if (!getOrganizationPage) { + throw new CustomNextError(404, 'Organization page was not found') + } + + if (!getOrganizationPageStandaloneSitemapLevel2) { + throw new CustomNextError( + 404, + 'Organization page standalone level 2 sitemap was not found', + ) + } + + const category = organizationPage?.topLevelNavigation?.links.find( + (link) => categorySlug === link.href.split('/').pop(), + ) + + return { + organizationPage: getOrganizationPage, + category, + sitemap: getOrganizationPageStandaloneSitemapLevel2, + } +} + +export default StandaloneLevel2Sitemap diff --git a/apps/web/screens/queries/Organization.tsx b/apps/web/screens/queries/Organization.tsx index 5b18fbad140a..8a1c92b5a600 100644 --- a/apps/web/screens/queries/Organization.tsx +++ b/apps/web/screens/queries/Organization.tsx @@ -445,3 +445,21 @@ export const GET_ORGANIZATION_PAGE_STANDALONE_SITEMAP_LEVEL1_QUERY = gql` } } ` + +export const GET_ORGANIZATION_PAGE_STANDALONE_SITEMAP_LEVEL2_QUERY = gql` + query GetOrganizationPageStandaloneSitemapLevel2Query( + $input: GetOrganizationPageStandaloneSitemapLevel2Input! + ) { + getOrganizationPageStandaloneSitemapLevel2(input: $input) { + label + childCategories { + label + href + childLinks { + label + href + } + } + } + } +` diff --git a/libs/cms/src/lib/cms.contentful.service.ts b/libs/cms/src/lib/cms.contentful.service.ts index fe3c3065a7a3..f1e40230d6d8 100644 --- a/libs/cms/src/lib/cms.contentful.service.ts +++ b/libs/cms/src/lib/cms.contentful.service.ts @@ -88,8 +88,14 @@ import { mapServiceWebPage } from './models/serviceWebPage.model' import { mapEvent } from './models/event.model' import { GetOrganizationParentSubpageInput } from './dto/getOrganizationParentSubpage.input' import { mapOrganizationParentSubpage } from './models/organizationParentSubpage.model' -import { GetOrganizationPageStandaloneSitemapLevel1Input } from './dto/getOrganizationPageStandaloneSitemap.input' -import { OrganizationPageStandaloneSitemap } from './models/organizationPageStandaloneSitemap.model' +import { + GetOrganizationPageStandaloneSitemapLevel1Input, + GetOrganizationPageStandaloneSitemapLevel2Input, +} from './dto/getOrganizationPageStandaloneSitemap.input' +import { + OrganizationPageStandaloneSitemap, + OrganizationPageStandaloneSitemapLevel2, +} from './models/organizationPageStandaloneSitemap.model' import { SitemapTree, SitemapTreeNodeType } from '@island.is/shared/types' import { getOrganizationPageUrlPrefix } from '@island.is/shared/utils' @@ -1219,7 +1225,7 @@ export class CmsContentfulService { label: node.label, href: `/${getOrganizationPageUrlPrefix(input.lang)}/${ input.organizationPageSlug - }/${node.slug}`, + }/${input.categorySlug}/${node.slug}`, description: node.description, } } @@ -1254,8 +1260,6 @@ export class CmsContentfulService { 1, ) - const entryIdsToRemove: string[] = [] - for (const parentSubpage of parentSubpageResponse.items) { const nodeList = entryNodes.get(parentSubpage.sys.id) if ( @@ -1263,7 +1267,6 @@ export class CmsContentfulService { !parentSubpage.fields.slug || !parentSubpage.fields.title ) { - entryIdsToRemove.push(parentSubpage.sys.id) continue } for (const node of nodeList) { @@ -1274,11 +1277,160 @@ export class CmsContentfulService { } } - // Remove entries from result that could not be resolved to their label or href + // Prune empty values result.childLinks = result.childLinks.filter( - (link) => !('entryId' in link && entryIdsToRemove.includes(link.entryId)), + (link) => link.label && link.href, + ) + + return result + } + + async getOrganizationPageStandaloneSitemapLevel2( + input: GetOrganizationPageStandaloneSitemapLevel2Input, + ): Promise { + const params = { + content_type: 'organizationPage', + 'fields.slug': input.organizationPageSlug, + select: 'fields.sitemap', + limit: 1, + } + + const response = await this.contentfulRepository + .getLocalizedEntries(input.lang, params) + .catch(errorHandler('getOrganizationPageStandaloneSitemapLevel2')) + + const tree = response.items?.[0]?.fields.sitemap?.fields.tree as SitemapTree + + if (!tree) { + return null + } + + const category = tree.childNodes.find( + (node) => + node.type === SitemapTreeNodeType.CATEGORY && + node.slug === input.categorySlug, ) + if (!category) { + return null + } + + const subcategory = category.childNodes.find( + (node) => + node.type === SitemapTreeNodeType.CATEGORY && + node.slug === input.subcategorySlug, + ) + + if (!subcategory || subcategory.type !== SitemapTreeNodeType.CATEGORY) { + return null + } + + const entryNodes = new Map< + string, + { label: string; href: string; entryId: string }[] + >() + + const result: OrganizationPageStandaloneSitemapLevel2 = { + label: subcategory.label, + childCategories: subcategory.childNodes.map((node) => { + if (node.type === SitemapTreeNodeType.CATEGORY) { + return { + label: node.label, + childLinks: node.childNodes.map((childNode) => { + if (childNode.type === SitemapTreeNodeType.CATEGORY) { + // Category at depth 3 should be empty so it gets pruned at a later stage + return { + label: '', + href: '', + childLinks: [], + } + } + if (childNode.type === SitemapTreeNodeType.URL) { + return { + label: childNode.label, + href: childNode.url, + childLinks: [], + } + } + + const entryNode = { + label: '', + href: '', + entryId: childNode.entryId, + childLinks: [], + } + + const nodeList = entryNodes.get(childNode.entryId) ?? [] + nodeList.push(entryNode) + entryNodes.set(childNode.entryId, nodeList) + + return entryNode + }), + } + } + + if (node.type === SitemapTreeNodeType.URL) { + return { + label: node.label, + href: node.url, + childLinks: [], + } + } + + const entryNode = { + label: '', + href: '', + entryId: node.entryId, + childLinks: [], + } + + const nodeList = entryNodes.get(node.entryId) ?? [] + nodeList.push(entryNode) + entryNodes.set(node.entryId, nodeList) + + return entryNode + }), + } + + const parentSubpageResponse = + await this.contentfulRepository.getLocalizedEntries( + input.lang, + { + content_type: 'organizationParentSubpage', + 'sys.id[in]': Array.from(entryNodes.keys()).join(','), + limit: 1000, + }, + 1, + ) + + for (const parentSubpage of parentSubpageResponse.items) { + const nodeList = entryNodes.get(parentSubpage.sys.id) + if ( + !nodeList || + !parentSubpage.fields.slug || + !parentSubpage.fields.title + ) { + continue + } + + for (const node of nodeList) { + node.label = parentSubpage.fields.title + node.href = `/${getOrganizationPageUrlPrefix(input.lang)}/${ + input.organizationPageSlug + }/${parentSubpage.fields.slug}` + } + } + + // Prune empty values + result.childCategories = result.childCategories.filter((childCategory) => { + childCategory.childLinks = childCategory.childLinks.filter( + (childLink) => { + return childLink.href && childLink.label + }, + ) + return childCategory.label && childCategory.childLinks.length > 0 + }) + return result } } diff --git a/libs/cms/src/lib/cms.resolver.ts b/libs/cms/src/lib/cms.resolver.ts index 55720481a4e4..cc26a42e6209 100644 --- a/libs/cms/src/lib/cms.resolver.ts +++ b/libs/cms/src/lib/cms.resolver.ts @@ -128,8 +128,14 @@ import { GetSingleGrantInput } from './dto/getSingleGrant.input' import { GrantList } from './models/grantList.model' import { OrganizationParentSubpage } from './models/organizationParentSubpage.model' import { GetOrganizationParentSubpageInput } from './dto/getOrganizationParentSubpage.input' -import { OrganizationPageStandaloneSitemap } from './models/organizationPageStandaloneSitemap.model' -import { GetOrganizationPageStandaloneSitemapLevel1Input } from './dto/getOrganizationPageStandaloneSitemap.input' +import { + OrganizationPageStandaloneSitemap, + OrganizationPageStandaloneSitemapLevel2, +} from './models/organizationPageStandaloneSitemap.model' +import { + GetOrganizationPageStandaloneSitemapLevel1Input, + GetOrganizationPageStandaloneSitemapLevel2Input, +} from './dto/getOrganizationPageStandaloneSitemap.input' const defaultCache: CacheControlOptions = { maxAge: CACHE_CONTROL_MAX_AGE } @@ -729,6 +735,16 @@ export class CmsResolver { input, ) } + + @CacheControl(defaultCache) + @Query(() => OrganizationPageStandaloneSitemapLevel2, { nullable: true }) + getOrganizationPageStandaloneSitemapLevel2( + @Args('input') input: GetOrganizationPageStandaloneSitemapLevel2Input, + ): Promise { + return this.cmsContentfulService.getOrganizationPageStandaloneSitemapLevel2( + input, + ) + } } @Resolver(() => LatestNewsSlice) diff --git a/libs/cms/src/lib/dto/getOrganizationPageStandaloneSitemap.input.ts b/libs/cms/src/lib/dto/getOrganizationPageStandaloneSitemap.input.ts index 761511383a48..e68109d730d2 100644 --- a/libs/cms/src/lib/dto/getOrganizationPageStandaloneSitemap.input.ts +++ b/libs/cms/src/lib/dto/getOrganizationPageStandaloneSitemap.input.ts @@ -16,3 +16,10 @@ export class GetOrganizationPageStandaloneSitemapLevel1Input { @IsString() lang: ElasticsearchIndexLocale = 'is' } + +@InputType() +export class GetOrganizationPageStandaloneSitemapLevel2Input extends GetOrganizationPageStandaloneSitemapLevel1Input { + @Field() + @IsString() + subcategorySlug!: string +} diff --git a/libs/cms/src/lib/generated/contentfulTypes.d.ts b/libs/cms/src/lib/generated/contentfulTypes.d.ts index dee46c12b306..aa7d3875ab85 100644 --- a/libs/cms/src/lib/generated/contentfulTypes.d.ts +++ b/libs/cms/src/lib/generated/contentfulTypes.d.ts @@ -3241,6 +3241,9 @@ export interface IOrganizationPage extends Entry { } export interface IOrganizationParentSubpageFields { + /** Organization Page */ + organizationPage: IOrganizationPage + /** Internal Title */ internalTitle: string diff --git a/libs/cms/src/lib/models/organizationPageStandaloneSitemap.model.ts b/libs/cms/src/lib/models/organizationPageStandaloneSitemap.model.ts index 197fee9b9f9f..45b76141678e 100644 --- a/libs/cms/src/lib/models/organizationPageStandaloneSitemap.model.ts +++ b/libs/cms/src/lib/models/organizationPageStandaloneSitemap.model.ts @@ -18,3 +18,33 @@ export class OrganizationPageStandaloneSitemap { @CacheField(() => [OrganizationPageStandaloneSitemapLink]) childLinks!: OrganizationPageStandaloneSitemapLink[] } + +@ObjectType() +export class OrganizationPageStandaloneSitemapLevel2Link { + @Field() + label!: string + + @Field() + href!: string +} + +@ObjectType() +export class OrganizationPageStandaloneSitemapLevel2Category { + @Field() + label!: string + + @Field(() => String, { nullable: true }) + href?: string | null + + @CacheField(() => [OrganizationPageStandaloneSitemapLevel2Link]) + childLinks!: OrganizationPageStandaloneSitemapLevel2Link[] +} + +@ObjectType() +export class OrganizationPageStandaloneSitemapLevel2 { + @Field() + label!: string + + @CacheField(() => [OrganizationPageStandaloneSitemapLevel2Category]) + childCategories!: OrganizationPageStandaloneSitemapLevel2Category[] +} diff --git a/libs/cms/src/lib/models/organizationParentSubpage.model.ts b/libs/cms/src/lib/models/organizationParentSubpage.model.ts index 9c9a4d927059..990aadedc9e7 100644 --- a/libs/cms/src/lib/models/organizationParentSubpage.model.ts +++ b/libs/cms/src/lib/models/organizationParentSubpage.model.ts @@ -35,6 +35,9 @@ export const mapOrganizationParentSubpage = ({ fields.pages ?.filter( (page) => + // Child pages should belong to the same organization page + page.fields.organizationPage?.sys?.id === + fields.organizationPage?.sys?.id && Boolean(page.fields.organizationPage?.fields?.slug) && Boolean(page.fields.slug), ) From bebcf4908f94510cf5cc958c6f780b4f3dc4dfe4 Mon Sep 17 00:00:00 2001 From: mannipje <135017126+mannipje@users.noreply.github.com> Date: Tue, 3 Dec 2024 11:42:28 +0000 Subject: [PATCH 08/85] feat(web): Add search input to organization standalone theme mobile frontpage (#17095) * Add search input to organization standalone theme frontpage * chore: nx format:write update dirty files --------- Co-authored-by: andes-it Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../standalone/components/Header.tsx | 104 +++++++++++------- .../organization/standalone/standalone.tsx | 1 + 2 files changed, 66 insertions(+), 39 deletions(-) diff --git a/apps/web/layouts/organization/standalone/components/Header.tsx b/apps/web/layouts/organization/standalone/components/Header.tsx index 919361d42d5f..0ec899b9ea58 100644 --- a/apps/web/layouts/organization/standalone/components/Header.tsx +++ b/apps/web/layouts/organization/standalone/components/Header.tsx @@ -2,8 +2,17 @@ import { useEffect, useState } from 'react' import { useWindowSize } from 'react-use' import cn from 'classnames' -import { ResponsiveSpace, Text, TextProps } from '@island.is/island-ui/core' +import { + Box, + Hidden, + ResponsiveSpace, + Stack, + Text, + TextProps, +} from '@island.is/island-ui/core' import { theme } from '@island.is/island-ui/theme' +import { SearchInput } from '@island.is/web/components' +import { useI18n } from '@island.is/web/i18n' import * as styles from './Header.css' @@ -21,6 +30,7 @@ export interface HeaderProps { imageObjectPosition?: 'left' | 'center' | 'right' className?: string isFrontpage?: boolean + organizationSlug?: string } export const Header: React.FC> = ({ @@ -36,8 +46,10 @@ export const Header: React.FC> = ({ imageObjectPosition = 'center', className, isFrontpage = false, + organizationSlug, }) => { const { width } = useWindowSize() + const { activeLocale } = useI18n() const imageProvided = !!image const [isMobile, setIsMobile] = useState(false) @@ -46,50 +58,64 @@ export const Header: React.FC> = ({ setIsMobile(width < theme.breakpoints.lg) }, [width]) return ( -
+
- {underTitle && isFrontpage && ( -
- - {underTitle} - -
+
+ {underTitle && isFrontpage && ( +
+ + {underTitle} + +
+ )} +
+ {imageProvided && isFrontpage && ( + )}
- {imageProvided && isFrontpage && ( - - )}
-
+ {isFrontpage && ( + + + + + + )} + ) } diff --git a/apps/web/layouts/organization/standalone/standalone.tsx b/apps/web/layouts/organization/standalone/standalone.tsx index e170c447de52..0546ad67422a 100644 --- a/apps/web/layouts/organization/standalone/standalone.tsx +++ b/apps/web/layouts/organization/standalone/standalone.tsx @@ -67,6 +67,7 @@ export const StandaloneLayout = ({ mobileBackground: organizationPage?.themeProperties.mobileBackgroundColor, isFrontpage: isFrontpage, underTitle: bannerTitle, + organizationSlug: organizationPage?.organization?.slug ?? '', } const { activeLocale } = useI18n() From 5f20addb1bbf63c56472a116767d5717519d0e0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnea=20R=C3=BAn=20Vignisd=C3=B3ttir?= Date: Tue, 3 Dec 2024 12:00:20 +0000 Subject: [PATCH 09/85] fix(user-notifications): Run worker after bootstrap (#17072) * run worker after bootstrap * chore: nx format:write update dirty files * fix tests * chore: nx format:write update dirty files --------- Co-authored-by: andes-it Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../notificationsWorker.service.spec.ts | 4 +++- .../notificationsWorker/notificationsWorker.service.ts | 10 ++-------- apps/services/user-notification/src/main.ts | 8 ++++++++ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/notificationsWorker.service.spec.ts b/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/notificationsWorker.service.spec.ts index ce392468eb87..df1a95d6b828 100644 --- a/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/notificationsWorker.service.spec.ts +++ b/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/notificationsWorker.service.spec.ts @@ -116,10 +116,12 @@ describe('NotificationsWorkerService', () => { queue = app.get(getQueueServiceToken('notifications')) notificationModel = app.get(getModelToken(Notification)) notificationsService = app.get(NotificationsService) - notificationsWorkerService = app.get(NotificationsWorkerService) userProfileApi = app.get(V2UsersApi) nationalRegistryService = app.get(NationalRegistryV3ClientService) companyRegistryService = app.get(CompanyRegistryClientService) + + notificationsWorkerService = await app.resolve(NotificationsWorkerService) + notificationsWorkerService.run() }) beforeEach(async () => { diff --git a/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/notificationsWorker.service.ts b/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/notificationsWorker.service.ts index 847bf71116af..fd353149f63d 100644 --- a/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/notificationsWorker.service.ts +++ b/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/notificationsWorker.service.ts @@ -55,7 +55,7 @@ type HandleNotification = { } @Injectable() -export class NotificationsWorkerService implements OnApplicationBootstrap { +export class NotificationsWorkerService { constructor( private readonly notificationDispatch: NotificationDispatchService, private readonly messageProcessor: MessageProcessorService, @@ -83,12 +83,6 @@ export class NotificationsWorkerService implements OnApplicationBootstrap { private readonly notificationModel: typeof Notification, ) {} - onApplicationBootstrap() { - if (this.config.isWorker) { - void this.run() - } - } - async handleDocumentNotification({ profile, messageId, @@ -311,7 +305,7 @@ export class NotificationsWorkerService implements OnApplicationBootstrap { } } - async run() { + public async run() { await this.worker.run( async (message, job): Promise => { const messageId = job.id diff --git a/apps/services/user-notification/src/main.ts b/apps/services/user-notification/src/main.ts index 7f2628e37533..a1efe5e63d7d 100644 --- a/apps/services/user-notification/src/main.ts +++ b/apps/services/user-notification/src/main.ts @@ -1,6 +1,7 @@ import { bootstrap, processJob } from '@island.is/infra-nest-server' import { AppModule } from './app/app.module' import { openApi } from './openApi' +import { NotificationsWorkerService } from './app/modules/notifications/notificationsWorker/notificationsWorker.service' const job = processJob() @@ -15,5 +16,12 @@ if (job === 'cleanup') { healthCheck: { database: true, }, + }).then(async ({ app }) => { + if (job === 'worker') { + const notificationsWorkerService = await app.resolve( + NotificationsWorkerService, + ) + await notificationsWorkerService.run() + } }) } From 692e9d86996fe892255619d819ff961286661e47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Eorkell=20M=C3=A1ni=20=C3=9Eorkelsson?= Date: Tue, 3 Dec 2024 12:13:18 +0000 Subject: [PATCH 10/85] fix(license-service): check app version (#16855) * fix: check app version * fix: add app ver constant * chore: add comment * fix: import type * chore: update version * chore: add MOCKS * chore: nullcheck app * fix: reverse flag * fix: wrong resoler method * fix: add table * fix: wrong resolver method * fix: add logger * Update firearmLicenseMapper.ts * fix: conditional return * Update firearmLicenseMapper.ts * fix: erase mocks --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../src/lib/licenceService.type.ts | 2 ++ .../src/lib/licenseService.service.ts | 8 ++++- .../src/lib/mappers/firearmLicenseMapper.ts | 32 +++++++++---------- .../resolvers/licenseCollection.resolver.ts | 31 ++++++++++++------ .../src/lib/resolvers/userLicense.resolver.ts | 5 +++ .../src/lib/utils/appCompatibilityMode.ts | 15 +++++++++ 6 files changed, 67 insertions(+), 26 deletions(-) create mode 100644 libs/api/domains/license-service/src/lib/utils/appCompatibilityMode.ts diff --git a/libs/api/domains/license-service/src/lib/licenceService.type.ts b/libs/api/domains/license-service/src/lib/licenceService.type.ts index 3423724deb9a..8cb7e33f39f8 100644 --- a/libs/api/domains/license-service/src/lib/licenceService.type.ts +++ b/libs/api/domains/license-service/src/lib/licenceService.type.ts @@ -2,6 +2,7 @@ import { Locale } from '@island.is/shared/types' import { GenericLicenseError } from './dto/GenericLicenseError.dto' import { Payload } from './dto/Payload.dto' import { GenericUserLicense as GenericUserLicenseModel } from './dto/GenericUserLicense.dto' +import { UserAgent } from '@island.is/nest/core' export interface GenericLicenseMappedPayloadResponse { licenseName: string @@ -145,5 +146,6 @@ export interface GenericLicenseMapper { parsePayload: ( payload: Array, locale: Locale, + userAgent?: UserAgent, ) => Promise> } diff --git a/libs/api/domains/license-service/src/lib/licenseService.service.ts b/libs/api/domains/license-service/src/lib/licenseService.service.ts index c6cd333d783e..404777a2cf97 100644 --- a/libs/api/domains/license-service/src/lib/licenseService.service.ts +++ b/libs/api/domains/license-service/src/lib/licenseService.service.ts @@ -47,6 +47,7 @@ import { BarcodeService, TOKEN_EXPIRED_ERROR, } from '@island.is/services/license' +import { UserAgent } from '@island.is/nest/core' const LOG_CATEGORY = 'license-service' @@ -96,6 +97,7 @@ export class LicenseService { user: User, locale: Locale, { includedTypes, excludedTypes, onlyList }: GetGenericLicenseOptions = {}, + userAgent?: UserAgent, ): Promise { const fetchPromises = AVAILABLE_LICENSES.map(async (license) => { if (excludedTypes && excludedTypes.indexOf(license.type) >= 0) { @@ -107,7 +109,7 @@ export class LicenseService { } if (!onlyList) { - return this.getLicensesOfType(user, locale, license.type) + return this.getLicensesOfType(user, locale, license.type, userAgent) } return null @@ -140,6 +142,7 @@ export class LicenseService { user: User, locale: Locale, licenseType: GenericLicenseType, + agent?: UserAgent, ): Promise { const licenseTypeDefinition = AVAILABLE_LICENSES.find( (i) => i.type === licenseType, @@ -187,6 +190,7 @@ export class LicenseService { const licensesPayload = await mapper.parsePayload( licensesFetchResponse.data, locale, + agent, ) const mappedLicenses: Array = await Promise.all( @@ -246,11 +250,13 @@ export class LicenseService { locale: Locale, licenseType: GenericLicenseType, licenseId?: string, + agent?: UserAgent, ): Promise { const licensesOfType = await this.getLicensesOfType( user, locale, licenseType, + agent, ) if (!licensesOfType) { diff --git a/libs/api/domains/license-service/src/lib/mappers/firearmLicenseMapper.ts b/libs/api/domains/license-service/src/lib/mappers/firearmLicenseMapper.ts index 089afc2143b3..dd22f02159f1 100644 --- a/libs/api/domains/license-service/src/lib/mappers/firearmLicenseMapper.ts +++ b/libs/api/domains/license-service/src/lib/mappers/firearmLicenseMapper.ts @@ -15,12 +15,17 @@ import { GenericUserLicenseMetaLinksType, } from '../licenceService.type' import { FirearmLicenseDto } from '@island.is/clients/license-client' -import { Injectable } from '@nestjs/common' +import { Inject, Injectable } from '@nestjs/common' import { isDefined } from '@island.is/shared/utils' import { FormatMessage, IntlService } from '@island.is/cms-translations' import { m } from '../messages' import { expiryTag, formatDate } from '../utils' import { GenericLicenseDataField } from '../dto/GenericLicenseDataField.dto' +import { UserAgent } from '@island.is/nest/core' +import { enableAppCompatibilityMode } from '../utils/appCompatibilityMode' +import { LOGGER_PROVIDER, type Logger } from '@island.is/logging' + +const APP_VERSION_CUTOFF = '1.4.7' @Injectable() export class FirearmLicensePayloadMapper implements GenericLicenseMapper { @@ -28,9 +33,16 @@ export class FirearmLicensePayloadMapper implements GenericLicenseMapper { async parsePayload( payload: Array, locale: Locale = 'is', + userAgent?: UserAgent, ): Promise> { if (!payload) return Promise.resolve([]) + //App version before 1.4.8 doesn't know how to handle table + const enableAppCompatibility = enableAppCompatibilityMode( + userAgent?.app?.version, + APP_VERSION_CUTOFF, + ) + const typedPayload = payload as Array const { formatMessage } = await this.intlService.useIntl( @@ -99,21 +111,9 @@ export class FirearmLicensePayloadMapper implements GenericLicenseMapper { : null, properties ? { - type: GenericLicenseDataFieldType.Group, - hideFromServicePortal: true, - label: formatMessage(m.firearmProperties), - fields: (properties.properties ?? []).map((property) => ({ - type: GenericLicenseDataFieldType.Category, - fields: this.parseProperties( - property, - formatMessage, - )?.filter(isDefined), - })), - } - : null, - properties - ? { - type: GenericLicenseDataFieldType.Table, + type: enableAppCompatibility + ? GenericLicenseDataFieldType.Group + : GenericLicenseDataFieldType.Table, label: formatMessage(m.firearmProperties), fields: (properties.properties ?? []).map((property) => ({ type: GenericLicenseDataFieldType.Category, diff --git a/libs/api/domains/license-service/src/lib/resolvers/licenseCollection.resolver.ts b/libs/api/domains/license-service/src/lib/resolvers/licenseCollection.resolver.ts index 2cc6e34ef319..102d14857c0f 100644 --- a/libs/api/domains/license-service/src/lib/resolvers/licenseCollection.resolver.ts +++ b/libs/api/domains/license-service/src/lib/resolvers/licenseCollection.resolver.ts @@ -9,19 +9,24 @@ import { ApiScope } from '@island.is/auth/scopes' import { Audit } from '@island.is/nest/audit' import type { Locale } from '@island.is/shared/types' -import { UseGuards } from '@nestjs/common' +import { Inject, UseGuards } from '@nestjs/common' import { Args, Query, Resolver } from '@nestjs/graphql' import { GetGenericLicensesInput } from '../dto/GetGenericLicenses.input' import { LicenseService } from '../licenseService.service' import { LicenseCollection } from '../dto/GenericLicenseCollection.dto' import { GenericUserLicense } from '../dto/GenericUserLicense.dto' +import { LOGGER_PROVIDER, type Logger } from '@island.is/logging' +import { ParsedUserAgent, type UserAgent } from '@island.is/nest/core' @UseGuards(IdsUserGuard, ScopesGuard) @Scopes(ApiScope.internal, ApiScope.licenses) @Resolver(() => LicenseCollection) @Audit({ namespace: '@island.is/api/license-service' }) export class LicenseCollectionResolver { - constructor(private readonly licenseServiceService: LicenseService) {} + constructor( + private readonly licenseServiceService: LicenseService, + @Inject(LOGGER_PROVIDER) private readonly logger: Logger, + ) {} @Query(() => [GenericUserLicense], { deprecationReason: 'Use genericLicenseCollection instead', @@ -29,6 +34,7 @@ export class LicenseCollectionResolver { @Audit() async genericLicenses( @CurrentUser() user: User, + @ParsedUserAgent() agent: UserAgent, @Args('locale', { type: () => String, nullable: true }) locale: Locale = 'is', @Args('input', { nullable: true }) input?: GetGenericLicensesInput, @@ -43,6 +49,7 @@ export class LicenseCollectionResolver { force: input?.force, onlyList: input?.onlyList, }, + agent, ) return collection.licenses @@ -54,18 +61,24 @@ export class LicenseCollectionResolver { @Audit() async genericLicenseCollection( @CurrentUser() user: User, + @ParsedUserAgent() agent: UserAgent, @Args('locale', { type: () => String, nullable: true }) locale: Locale = 'is', @Args('input') input: GetGenericLicensesInput, ) { const licenseCollection = - await this.licenseServiceService.getLicenseCollection(user, locale, { - ...input, - includedTypes: input?.includedTypes ?? ['DriversLicense'], - excludedTypes: input?.excludedTypes, - force: input?.force, - onlyList: input?.onlyList, - }) + await this.licenseServiceService.getLicenseCollection( + user, + locale, + { + ...input, + includedTypes: input?.includedTypes ?? ['DriversLicense'], + excludedTypes: input?.excludedTypes, + force: input?.force, + onlyList: input?.onlyList, + }, + agent, + ) return licenseCollection } } diff --git a/libs/api/domains/license-service/src/lib/resolvers/userLicense.resolver.ts b/libs/api/domains/license-service/src/lib/resolvers/userLicense.resolver.ts index ba5f75e6c076..cc42079d6065 100644 --- a/libs/api/domains/license-service/src/lib/resolvers/userLicense.resolver.ts +++ b/libs/api/domains/license-service/src/lib/resolvers/userLicense.resolver.ts @@ -15,6 +15,8 @@ import { GenericUserLicense } from '../dto/GenericUserLicense.dto' import { GetGenericLicenseInput } from '../dto/GetGenericLicense.input' import { LicenseService } from '../licenseService.service' import { GenericLicenseError } from '../dto/GenericLicenseError.dto' +import { logger } from '@island.is/logging' +import { ParsedUserAgent, type UserAgent } from '@island.is/nest/core' @UseGuards(IdsUserGuard, ScopesGuard) @Scopes(ApiScope.internal, ApiScope.licenses) @@ -29,6 +31,8 @@ export class UserLicenseResolver { @Audit() async genericLicense( @CurrentUser() user: User, + @ParsedUserAgent() + agent: UserAgent, @Args('locale', { type: () => String, nullable: true }) locale: Locale = 'is', @Args('input') input: GetGenericLicenseInput, @@ -38,6 +42,7 @@ export class UserLicenseResolver { locale, input.licenseType, input.licenseId, + agent, ) if (license instanceof GenericLicenseError) { diff --git a/libs/api/domains/license-service/src/lib/utils/appCompatibilityMode.ts b/libs/api/domains/license-service/src/lib/utils/appCompatibilityMode.ts new file mode 100644 index 000000000000..ff1cec1c61e6 --- /dev/null +++ b/libs/api/domains/license-service/src/lib/utils/appCompatibilityMode.ts @@ -0,0 +1,15 @@ +export const enableAppCompatibilityMode = ( + version?: string, + versionToCompare?: string, +): boolean => { + if (!version || !versionToCompare) { + return false + } + + const versionComparison = version.localeCompare(versionToCompare, undefined, { + numeric: true, + sensitivity: 'base', + }) + + return versionComparison <= 0 +} From e7c1747f3d43cd78de214cf84811149f9db885c7 Mon Sep 17 00:00:00 2001 From: norda-gunni <161026627+norda-gunni@users.noreply.github.com> Date: Tue, 3 Dec 2024 12:34:03 +0000 Subject: [PATCH 11/85] feat(application-system): Add notifications support to pruning (#17029) * Initial notification test * Make version of notificationconfig that works for application lifecycle * Add notification capability for application pruning * Add testing * Add notificationTemplateId and create notifications prior to pruning * Fix circular dependency * Fix tests * Make body vars optional for PruningNotification * Minor type fix * more testing * readme correction * PR related improvements * Pr comments --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../lifecycle/application-lifecycle.module.ts | 12 +- .../application-lifecycle.service.ts | 100 ++++- ...application-lifecycle-notification.spec.ts | 348 ++++++++++++++++++ .../test/application-lifecycle-unit.spec.ts | 1 + .../lib/application/application.service.ts | 14 +- libs/application/core/src/lib/constants.ts | 15 +- .../template-api-modules/src/lib/index.ts | 3 + libs/application/templates/README.md | 27 ++ .../types/src/lib/ApplicationLifecycle.ts | 19 + .../application/types/src/lib/StateMachine.ts | 10 +- libs/clients/user-notification/src/index.ts | 1 + .../src/lib/eagerApiConfiguration.ts | 26 ++ .../lib/userNotificationEagerClient.module.ts | 9 + 13 files changed, 567 insertions(+), 18 deletions(-) create mode 100644 apps/application-system/api/src/app/modules/application/lifecycle/test/application-lifecycle-notification.spec.ts create mode 100644 libs/clients/user-notification/src/lib/eagerApiConfiguration.ts create mode 100644 libs/clients/user-notification/src/lib/userNotificationEagerClient.module.ts diff --git a/apps/application-system/api/src/app/modules/application/lifecycle/application-lifecycle.module.ts b/apps/application-system/api/src/app/modules/application/lifecycle/application-lifecycle.module.ts index e65e7a39aaaf..b6ead098509f 100644 --- a/apps/application-system/api/src/app/modules/application/lifecycle/application-lifecycle.module.ts +++ b/apps/application-system/api/src/app/modules/application/lifecycle/application-lifecycle.module.ts @@ -16,6 +16,10 @@ import { environment } from '../../../../environments' import { SequelizeConfigService } from '../../../sequelizeConfig.service' import { ApplicationChargeModule } from '../charge/application-charge.module' import { ApplicationLifeCycleService } from './application-lifecycle.service' +import { + UserNotificationClientConfig, + UserNotificationEagerClientModule, +} from '@island.is/clients/user-notification' @Module({ imports: [ @@ -26,10 +30,16 @@ import { ApplicationLifeCycleService } from './application-lifecycle.service' LoggingModule, ApplicationChargeModule, ApplicationFilesModule, + UserNotificationEagerClientModule, AuditModule.forRoot(environment.audit), ConfigModule.forRoot({ isGlobal: true, - load: [signingModuleConfig, ApplicationFilesConfig, FileStorageConfig], + load: [ + UserNotificationClientConfig, + signingModuleConfig, + ApplicationFilesConfig, + FileStorageConfig, + ], }), ], providers: [ApplicationLifeCycleService], diff --git a/apps/application-system/api/src/app/modules/application/lifecycle/application-lifecycle.service.ts b/apps/application-system/api/src/app/modules/application/lifecycle/application-lifecycle.service.ts index b0276442b07d..332c163b2555 100644 --- a/apps/application-system/api/src/app/modules/application/lifecycle/application-lifecycle.service.ts +++ b/apps/application-system/api/src/app/modules/application/lifecycle/application-lifecycle.service.ts @@ -7,19 +7,26 @@ import { LOGGER_PROVIDER } from '@island.is/logging' import type { Logger } from '@island.is/logging' import { ApplicationChargeService } from '../charge/application-charge.service' import { FileService } from '@island.is/application/api/files' +import { + CreateHnippNotificationDto, + NotificationsApi, +} from '@island.is/clients/user-notification' +import { getApplicationTemplateByTypeId } from '@island.is/application/template-loader' +import { + ApplicationWithAttachments, + PruningApplication, +} from '@island.is/application/types' export interface ApplicationPruning { pruned: boolean - application: Pick< - Application, - 'id' | 'attachments' | 'answers' | 'externalData' | 'typeId' | 'state' - > + application: PruningApplication failedAttachments: object } @Injectable() export class ApplicationLifeCycleService { private processingApplications: ApplicationPruning[] = [] + private pruneNotifications = new Map() constructor( @Inject(LOGGER_PROVIDER) @@ -27,6 +34,7 @@ export class ApplicationLifeCycleService { private applicationService: ApplicationService, private fileService: FileService, private applicationChargeService: ApplicationChargeService, + private readonly notificationApi: NotificationsApi, ) { this.logger = logger.child({ context: 'ApplicationLifeCycleService' }) } @@ -38,7 +46,7 @@ export class ApplicationLifeCycleService { await this.pruneAttachments() await this.pruneApplicationCharge() await this.pruneApplicationData() - this.reportResults() + await this.reportResults() this.logger.info(`Application pruning done.`) } @@ -48,10 +56,7 @@ export class ApplicationLifeCycleService { private async fetchApplicationsToBePruned() { const applications = - (await this.applicationService.findAllDueToBePruned()) as Pick< - Application, - 'id' | 'attachments' | 'answers' | 'externalData' | 'typeId' | 'state' - >[] + (await this.applicationService.findAllDueToBePruned()) as PruningApplication[] this.logger.info(`Found ${applications.length} applications to be pruned.`) @@ -62,6 +67,13 @@ export class ApplicationLifeCycleService { failedAttachments: {}, } }) + + for (const { application } of this.processingApplications) { + const notification = await this.preparePrunedNotification(application) + if (notification) { + this.pruneNotifications.set(application.id, notification) + } + } } private async pruneAttachments() { @@ -106,7 +118,7 @@ export class ApplicationLifeCycleService { }, ) - prune.application = updatedApplication + prune.application = updatedApplication as PruningApplication } catch (error) { prune.pruned = false this.logger.error( @@ -117,7 +129,7 @@ export class ApplicationLifeCycleService { } } - private reportResults() { + private async reportResults() { const failed = this.processingApplications.filter( (application) => !application.pruned, ) @@ -126,6 +138,72 @@ export class ApplicationLifeCycleService { (application) => application.pruned, ) + for (const { application } of success) { + const notification = this.pruneNotifications.get(application.id) + if (notification) { + await this.sendPrunedNotification(notification, application.id) + } + } + this.logger.info(`Successful: ${success.length}, Failed: ${failed.length}`) } + + private async preparePrunedNotification( + application: PruningApplication, + ): Promise { + const template = await getApplicationTemplateByTypeId(application.typeId) + const stateConfig = template.stateMachineConfig.states[application.state] + const lifeCycle = stateConfig.meta?.lifecycle + if (lifeCycle && lifeCycle.shouldBePruned && lifeCycle.pruneMessage) { + try { + const pruneMessage = + typeof lifeCycle.pruneMessage === 'function' + ? lifeCycle.pruneMessage(application as ApplicationWithAttachments) + : lifeCycle.pruneMessage + const notification = { + recipient: application.applicant, + templateId: pruneMessage.notificationTemplateId, + args: [ + { + key: 'externalBody', + value: pruneMessage.externalBody || '', + }, + { + key: 'internalBody', + value: pruneMessage.internalBody || '', + }, + ], + } + return notification + } catch (error) { + this.logger.error( + `Failed to prepare pruning notification for application ${application.id}`, + error, + ) + return null + } + } + return null + } + + private async sendPrunedNotification( + notification: CreateHnippNotificationDto, + applicationId: string, + ) { + try { + const response = + await this.notificationApi.notificationsControllerCreateHnippNotification( + { + createHnippNotificationDto: notification, + }, + ) + this.logger.info( + `Prune notification sent with response: ${JSON.stringify(response)}`, + ) + } catch (error) { + this.logger.error( + `Failed to send pruning notification with error: ${error} for application ${applicationId}`, + ) + } + } } diff --git a/apps/application-system/api/src/app/modules/application/lifecycle/test/application-lifecycle-notification.spec.ts b/apps/application-system/api/src/app/modules/application/lifecycle/test/application-lifecycle-notification.spec.ts new file mode 100644 index 000000000000..149ceaf0a3db --- /dev/null +++ b/apps/application-system/api/src/app/modules/application/lifecycle/test/application-lifecycle-notification.spec.ts @@ -0,0 +1,348 @@ +import { LOGGER_PROVIDER } from '@island.is/logging' +import { FileService } from '@island.is/application/api/files' +import { + CreateNotificationResponse, + NotificationsApi, +} from '@island.is/clients/user-notification' +import { Test } from '@nestjs/testing' +import { getApplicationTemplateByTypeId } from '@island.is/application/template-loader' +import { TestApp } from '@island.is/testing/nest' +import { + ApplicationTypes, + PruningApplication, +} from '@island.is/application/types' + +import { ApplicationLifeCycleService } from '../application-lifecycle.service' +import { ApplicationService } from '@island.is/application/api/core' +import { ApplicationChargeService } from '../../charge/application-charge.service' + +jest.mock('@island.is/application/template-loader') + +describe('ApplicationLifeCycleService', () => { + let service: ApplicationLifeCycleService + + const mockLogger = { + info: jest.fn(), + error: jest.fn(), + child: () => mockLogger, + } + + beforeEach(async () => { + const module = await Test.createTestingModule({ + providers: [ + ApplicationLifeCycleService, + { + provide: LOGGER_PROVIDER, + useValue: mockLogger, + }, + { + provide: ApplicationService, + useValue: {}, + }, + { + provide: FileService, + useValue: {}, + }, + { + provide: ApplicationChargeService, + useValue: {}, + }, + { + provide: NotificationsApi, + useValue: {}, + }, + ], + }).compile() + + service = module.get( + ApplicationLifeCycleService, + ) + }) + + describe('preparePrunedNotification', () => { + it('should return notification when all required fields are present', async () => { + const mockApplication = { + id: '123', + typeId: ApplicationTypes.EXAMPLE, + state: 'draft', + applicant: 'user123', + answers: {}, + externalData: {}, + attachments: [], + } + + const mockTemplate = { + stateMachineConfig: { + states: { + draft: { + meta: { + lifecycle: { + shouldBePruned: true, + pruneMessage: { + externalBody: 'external message', + internalBody: 'internal message', + notificationTemplateId: 'template123', + }, + }, + }, + }, + }, + }, + } + + ;(getApplicationTemplateByTypeId as jest.Mock).mockResolvedValue( + mockTemplate, + ) + + const result = await service['preparePrunedNotification'](mockApplication) + + expect(result).toEqual({ + recipient: 'user123', + templateId: 'template123', + args: [ + { + key: 'externalBody', + value: 'external message', + }, + { + key: 'internalBody', + value: 'internal message', + }, + ], + }) + }) + + it('should handle function-based pruneMessage', async () => { + const mockApplication = { + id: '123', + typeId: ApplicationTypes.EXAMPLE, + state: 'draft', + applicant: 'user123', + answers: {}, + externalData: {}, + attachments: [], + } + + const mockTemplate = { + stateMachineConfig: { + states: { + draft: { + meta: { + lifecycle: { + shouldBePruned: true, + pruneMessage: (app: PruningApplication) => ({ + externalBody: `external message for ${app.id}`, + internalBody: `internal message for ${app.id}`, + notificationTemplateId: 'template123', + }), + }, + }, + }, + }, + }, + } + + ;(getApplicationTemplateByTypeId as jest.Mock).mockResolvedValue( + mockTemplate, + ) + + const result = await service['preparePrunedNotification'](mockApplication) + + expect(result).toEqual({ + recipient: 'user123', + templateId: 'template123', + args: [ + { + key: 'externalBody', + value: 'external message for 123', + }, + { + key: 'internalBody', + value: 'internal message for 123', + }, + ], + }) + }) + + it('should return null when required fields are missing', async () => { + const mockApplication = { + id: '123', + typeId: ApplicationTypes.EXAMPLE, + state: 'draft', + applicant: 'user123', + answers: {}, + externalData: {}, + attachments: [], + } + + const mockTemplate = { + stateMachineConfig: { + states: { + draft: { + meta: { + lifecycle: { + shouldBePruned: false, // Missing required field + }, + }, + }, + }, + }, + } + + ;(getApplicationTemplateByTypeId as jest.Mock).mockResolvedValue( + mockTemplate, + ) + + const result = await service['preparePrunedNotification'](mockApplication) + + expect(result).toBeNull() + }) + }) +}) + +describe('ApplicationLifeCycleService', () => { + let service: ApplicationLifeCycleService + let notificationApi: jest.Mocked + let mockLogger: jest.Mocked + + beforeEach(async () => { + mockLogger = { + info: jest.fn(), + error: jest.fn(), + child: jest.fn().mockReturnThis(), + } + + notificationApi = { + notificationsControllerCreateHnippNotification: jest.fn(), + } as any + + const module = await Test.createTestingModule({ + providers: [ + ApplicationLifeCycleService, + { + provide: LOGGER_PROVIDER, + useValue: mockLogger, + }, + { + provide: ApplicationService, + useValue: {}, + }, + { + provide: FileService, + useValue: {}, + }, + { + provide: ApplicationChargeService, + useValue: {}, + }, + { + provide: NotificationsApi, + useValue: notificationApi, + }, + ], + }).compile() + + service = module.get( + ApplicationLifeCycleService, + ) + }) + + describe('reportResults', () => { + it('should successfully send notification when API call succeeds', async () => { + // Setup + const mockNotification = { + recipient: 'test-user', + templateId: 'test-template', + args: [], + } + const mockApplicationId = 'test-id' + + const mockResponse = { id: '123' } + + notificationApi.notificationsControllerCreateHnippNotification.mockResolvedValue( + mockResponse, + ) + + // Execute + await service['sendPrunedNotification']( + mockNotification, + mockApplicationId, + ) + + // Assert + expect( + notificationApi.notificationsControllerCreateHnippNotification, + ).toHaveBeenCalledWith({ + createHnippNotificationDto: mockNotification, + }) + expect(mockLogger.info).toHaveBeenCalledWith( + `Prune notification sent with response: ${JSON.stringify( + mockResponse, + )}`, + ) + }) + + it('should log error when notification API call fails', async () => { + // Setup + const mockNotification = { + recipient: 'test-user', + templateId: 'test-template', + args: [], + } + const mockApplicationId = 'test-id' + const mockError = new Error('API Error') + + notificationApi.notificationsControllerCreateHnippNotification.mockRejectedValue( + mockError, + ) + + // Execute + await service['sendPrunedNotification']( + mockNotification, + mockApplicationId, + ) + + // Assert + expect(mockLogger.error).toHaveBeenCalledWith( + `Failed to send pruning notification with error: ${mockError} for application ${mockApplicationId}`, + ) + }) + + it('should handle malformed pruneMessage function', async () => { + const mockApplication = { + id: '123', + typeId: ApplicationTypes.EXAMPLE, + state: 'draft', + applicant: 'user123', + answers: {}, + externalData: {}, + attachments: [], + } + + const mockTemplate = { + stateMachineConfig: { + states: { + draft: { + meta: { + lifecycle: { + shouldBePruned: true, + pruneMessage: () => { + throw new Error('Unexpected error') + }, + }, + }, + }, + }, + }, + } + + ;(getApplicationTemplateByTypeId as jest.Mock).mockResolvedValue( + mockTemplate, + ) + + const result = await service['preparePrunedNotification'](mockApplication) + expect(result).toBeNull() + expect(mockLogger.error).toHaveBeenCalled() + }) + }) +}) diff --git a/apps/application-system/api/src/app/modules/application/lifecycle/test/application-lifecycle-unit.spec.ts b/apps/application-system/api/src/app/modules/application/lifecycle/test/application-lifecycle-unit.spec.ts index 865bb5b922d3..2cb39db66abb 100644 --- a/apps/application-system/api/src/app/modules/application/lifecycle/test/application-lifecycle-unit.spec.ts +++ b/apps/application-system/api/src/app/modules/application/lifecycle/test/application-lifecycle-unit.spec.ts @@ -15,6 +15,7 @@ let s3Service: S3Service export const createApplications = () => { return [ createApplication({ + state: 'draft', answers: { question: 'yes', isAnotherQuestion: 'yes', diff --git a/libs/application/api/core/src/lib/application/application.service.ts b/libs/application/api/core/src/lib/application/application.service.ts index 194a1bf50dfe..c7f8d8f404f7 100644 --- a/libs/application/api/core/src/lib/application/application.service.ts +++ b/libs/application/api/core/src/lib/application/application.service.ts @@ -226,11 +226,17 @@ export class ApplicationService { }) } - async findAllDueToBePruned(): Promise< - Pick[] - > { + async findAllDueToBePruned(): Promise { return this.applicationModel.findAll({ - attributes: ['id', 'attachments', 'typeId', 'state'], + attributes: [ + 'id', + 'attachments', + 'typeId', + 'state', + 'applicant', + 'answers', + 'externalData', + ], where: { [Op.and]: { pruneAt: { diff --git a/libs/application/core/src/lib/constants.ts b/libs/application/core/src/lib/constants.ts index 5770c475a8dd..37b98d47f859 100644 --- a/libs/application/core/src/lib/constants.ts +++ b/libs/application/core/src/lib/constants.ts @@ -1,4 +1,8 @@ -import { StateLifeCycle } from '@island.is/application/types' +import { + PruningApplication, + PruningNotification, + StateLifeCycle, +} from '@island.is/application/types' export const EphemeralStateLifeCycle: StateLifeCycle = { shouldBeListed: false, @@ -16,6 +20,15 @@ export const pruneAfterDays = (Days: number): StateLifeCycle => { export const DefaultStateLifeCycle: StateLifeCycle = pruneAfterDays(30) +export const defaultLifecycleWithPruneMessage = ( + message: + | PruningNotification + | ((application: PruningApplication) => PruningNotification), +) => ({ + ...DefaultStateLifeCycle, + pruneMessage: message, +}) + export const NO_ANSWER = null export const YES = 'yes' diff --git a/libs/application/template-api-modules/src/lib/index.ts b/libs/application/template-api-modules/src/lib/index.ts index c8c2758da96b..8a86fd0708aa 100644 --- a/libs/application/template-api-modules/src/lib/index.ts +++ b/libs/application/template-api-modules/src/lib/index.ts @@ -7,3 +7,6 @@ export { TemplateApiModuleActionProps, } from './types' export { AttachmentS3Service } from './modules/shared/services' +export { NotificationsService } from './notification/notifications.service' +export { ApplicationsNotificationsModule } from './notification/notifications.module' +export { NotificationType } from './notification/notificationsTemplates' diff --git a/libs/application/templates/README.md b/libs/application/templates/README.md index 55b51ce87c0f..63b063c9d8ac 100644 --- a/libs/application/templates/README.md +++ b/libs/application/templates/README.md @@ -1,5 +1,32 @@ # Templates +## Pruning notifications + +If desired it is possible to have a custom message sent to an application's applicant when it is pruned. +This is done by adding a `pruneMessage` object of type `PruningNotification` or a function returning a `PruningNotification` to the application's lifecycle configuration. +When executed, the function will be passed an argument that is a `PruningApplication` object which contains the application's data. The `PruningNotification` object has `externalBody`, `internalBody` and `notificationTemplateId`. The former two can be used to fill in the values for the `externalBody` and `internalBody` template variables in the notification that will be sent to the user. The `notificationTemplateId` is the id of the notification template that will be used to send the notification. It is up to the user whether to use the body variables or not. + +For example an application might want to send a notification when an application that was in the draft stage was pruned. Here is an example of how that might be accomplished in the application template: + +```typescript +stateMachineConfig: { + initial: States.DRAFT, + states: { + [States.DRAFT]: { + meta: { + name: application.general.name.defaultMessage, + lifecycle: defaultLifecycleWithPruneMessage((application: PruningApplication) => ({ + externalBody: `Application has been in draft for more than 30 days and has been pruned.`, + internalBody: `Application for ${application.externalData.nationalRegistry.data.fullName} has been in draft for more than 30 days. Please note that if desired the application may be re-submitted.`, + notificationTemplateId: 'HNIPP.AS.*', + })), + status: 'draft', + }, + }, + }, + }, +``` + ## Mocking XROAD endpoints with Mockoon for templates ### Prerequisites diff --git a/libs/application/types/src/lib/ApplicationLifecycle.ts b/libs/application/types/src/lib/ApplicationLifecycle.ts index f4dbe2a270b4..913f921789b3 100644 --- a/libs/application/types/src/lib/ApplicationLifecycle.ts +++ b/libs/application/types/src/lib/ApplicationLifecycle.ts @@ -1,4 +1,23 @@ +import { ApplicationWithAttachments } from './Application' export interface ApplicationLifecycle { isListed: boolean pruneAt: Date | null + pruneMessage?: string +} + +export type PruningApplication = Pick< + ApplicationWithAttachments, + | 'id' + | 'attachments' + | 'answers' + | 'externalData' + | 'typeId' + | 'state' + | 'applicant' +> + +export type PruningNotification = { + externalBody?: string + internalBody?: string + notificationTemplateId: string } diff --git a/libs/application/types/src/lib/StateMachine.ts b/libs/application/types/src/lib/StateMachine.ts index 8dd8c370cc7d..7fd80e643f12 100644 --- a/libs/application/types/src/lib/StateMachine.ts +++ b/libs/application/types/src/lib/StateMachine.ts @@ -8,10 +8,15 @@ import { import { AnyEventObject, MachineOptions, StateMachine } from 'xstate/lib/types' import { FormLoader, FormText, StaticText } from './Form' -import { Application, ActionCardTag } from './Application' +import { + Application, + ActionCardTag, + ApplicationWithAttachments, +} from './Application' import { Condition } from './Condition' import { TestSupport } from '@island.is/island-ui/utils' import { TemplateApi } from './template-api/TemplateApi' +import { PruningApplication, PruningNotification } from './ApplicationLifecycle' export type ApplicationRole = 'applicant' | 'assignee' | string @@ -69,6 +74,9 @@ export type StateLifeCycle = // If set to a number prune date will equal current timestamp + whenToPrune (ms) whenToPrune: number | ((application: Application) => Date) shouldDeleteChargeIfPaymentFulfilled?: boolean | null + pruneMessage?: + | PruningNotification + | ((application: PruningApplication) => PruningNotification) } export type PendingActionDisplayType = 'warning' | 'success' | 'info' | 'error' diff --git a/libs/clients/user-notification/src/index.ts b/libs/clients/user-notification/src/index.ts index 7118fca0037d..2f956d9da824 100644 --- a/libs/clients/user-notification/src/index.ts +++ b/libs/clients/user-notification/src/index.ts @@ -1,4 +1,5 @@ export { UserNotificationClientConfig } from './lib/userNotificationClient.config' export { UserNotificationClientModule } from './lib/userNotificationClient.module' +export { UserNotificationEagerClientModule } from './lib/userNotificationEagerClient.module' export * from '../gen/fetch' diff --git a/libs/clients/user-notification/src/lib/eagerApiConfiguration.ts b/libs/clients/user-notification/src/lib/eagerApiConfiguration.ts new file mode 100644 index 000000000000..64d5f7bd0186 --- /dev/null +++ b/libs/clients/user-notification/src/lib/eagerApiConfiguration.ts @@ -0,0 +1,26 @@ +import { createEnhancedFetch } from '@island.is/clients/middlewares' +import { ConfigType } from '@island.is/nest/config' + +import { + Configuration, + UserNotificationApi, + NotificationsApi, +} from '../../gen/fetch' +import { UserNotificationClientConfig } from './userNotificationClient.config' + +export const eagerExportedApis = [UserNotificationApi, NotificationsApi].map( + (Api) => ({ + provide: Api, + useFactory: (config: ConfigType) => + new Api( + new Configuration({ + fetchApi: createEnhancedFetch({ + name: 'clients-user-notification', + organizationSlug: 'stafraent-island', + }), + basePath: `${config.basePath}`, + }), + ), + inject: [UserNotificationClientConfig.KEY], + }), +) diff --git a/libs/clients/user-notification/src/lib/userNotificationEagerClient.module.ts b/libs/clients/user-notification/src/lib/userNotificationEagerClient.module.ts new file mode 100644 index 000000000000..2ebfdef2334f --- /dev/null +++ b/libs/clients/user-notification/src/lib/userNotificationEagerClient.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common' + +import { eagerExportedApis } from './eagerApiConfiguration' + +@Module({ + providers: eagerExportedApis, + exports: eagerExportedApis, +}) +export class UserNotificationEagerClientModule {} From 38cb9501ff56806e39ae76fc7c199ec2f03a56df Mon Sep 17 00:00:00 2001 From: norda-gunni <161026627+norda-gunni@users.noreply.github.com> Date: Tue, 3 Dec 2024 12:47:34 +0000 Subject: [PATCH 12/85] fix(application-system): Fix s3 uri errors in heath insureance application (#17108) * Add extra log lines and start fetching files via URI * Clean up logs --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../templates/health-insurance/health-insurance.utils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/application/template-api-modules/src/lib/modules/templates/health-insurance/health-insurance.utils.ts b/libs/application/template-api-modules/src/lib/modules/templates/health-insurance/health-insurance.utils.ts index e0165c68cd87..fcc28104f09d 100644 --- a/libs/application/template-api-modules/src/lib/modules/templates/health-insurance/health-insurance.utils.ts +++ b/libs/application/template-api-modules/src/lib/modules/templates/health-insurance/health-insurance.utils.ts @@ -93,7 +93,8 @@ export const insuranceToXML = async ( } for (let i = 0; i < arrAttachments.length; i++) { const filename = arrAttachments[i] - const fileContent = await s3Service.getFileContent(filename, 'base64') + const fileUri = attachmentNames[i] // attachmentNames actually contains the URIs + const fileContent = await s3Service.getFileContent(fileUri, 'base64') if (!fileContent) throw new Error(`Unable to fetch file content for: ${filename}`) From 9c6a570fe6426fa616862f2d51e76c9e67058cf3 Mon Sep 17 00:00:00 2001 From: HjorturJ <34068269+HjorturJ@users.noreply.github.com> Date: Tue, 3 Dec 2024 13:00:50 +0000 Subject: [PATCH 13/85] fix(application-system-form): Checkbox ordering and look consolidation (#17065) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../src/lib/CheckboxFormField/CheckboxFormField.tsx | 1 + .../src/lib/RadioFormField/RadioFormField.tsx | 13 +------------ .../ui-shell/src/components/FormMultiField.tsx | 2 +- .../island-ui/core/src/lib/Checkbox/Checkbox.css.ts | 2 +- 4 files changed, 4 insertions(+), 14 deletions(-) diff --git a/libs/application/ui-fields/src/lib/CheckboxFormField/CheckboxFormField.tsx b/libs/application/ui-fields/src/lib/CheckboxFormField/CheckboxFormField.tsx index 3c9ee2fc2b73..57dfb95436ab 100644 --- a/libs/application/ui-fields/src/lib/CheckboxFormField/CheckboxFormField.tsx +++ b/libs/application/ui-fields/src/lib/CheckboxFormField/CheckboxFormField.tsx @@ -79,6 +79,7 @@ export const CheckboxFormField = ({ disabled={disabled} large={large} name={`${id}`} + split={width === 'half' ? '1/2' : '1/1'} onSelect={onSelect} backgroundColor={backgroundColor} defaultValue={ diff --git a/libs/application/ui-fields/src/lib/RadioFormField/RadioFormField.tsx b/libs/application/ui-fields/src/lib/RadioFormField/RadioFormField.tsx index c769b5699326..9f760fdc6927 100644 --- a/libs/application/ui-fields/src/lib/RadioFormField/RadioFormField.tsx +++ b/libs/application/ui-fields/src/lib/RadioFormField/RadioFormField.tsx @@ -48,17 +48,6 @@ export const RadioFormField: FC> = ({ [options, application, locale], ) - console.debug( - `Radio title ${JSON.stringify( - title, - )}, and formatted: ${formatTextWithLocale( - title, - application, - locale as Locale, - formatMessage, - )}`, - ) - return ( {showFieldName && ( @@ -83,7 +72,7 @@ export const RadioFormField: FC> = ({ /> )} - + Date: Tue, 3 Dec 2024 13:33:13 +0000 Subject: [PATCH 14/85] revert(financial-aid): wide revert of financial aid main (#17091) * Revert "fix: revert back to previous version of filelist (#16825)" This reverts commit b90bbfa6e5342e9d3b2dca7e0e2e11e60805d7c2. * Revert "fix(financial-adi): use account number and ledger when sending bank info (#16599)" This reverts commit 45ec2fd51e01e92ab9dcc17cd9308f2da1386808. * Revert "fix(financial-aid): dynamic logo (#16518)" This reverts commit 4678927a15bfdaa03dfc4e5bd93582b2894a717a. * Revert "chore(financial-aid): refactor custom components out (#15549)" This reverts commit 18b87eb67f3aed85e54ca64c20d1f2c87804daa5. * chore: undo changes in veita * chore: nx format:write update dirty files * fix: remove duplicate imports after merge conflict --------- Co-authored-by: andes-it Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../application/core/src/lib/fieldBuilders.ts | 45 - libs/application/core/src/lib/messages.ts | 15 - .../financial-aid/financial-aid.service.ts | 202 +- .../src/assets/ConfirmationImage.tsx | 3736 ----------------- .../src/components/Logo/Logo.tsx | 32 - .../Status/Estimation/Estimation.tsx | 69 - .../Status/Estimation/VeitaEstimation.tsx | 70 - .../src/components/Status/Estimation/utils.ts | 42 - .../src/components/Summary/Files.css.ts | 9 - .../components/Summary/SummaryBlock.css.ts | 11 - .../src/fields/AboutForm/AboutForm.tsx | 37 + .../AboutSpouseForm/AboutSpouseForm.tsx | 38 + .../src/fields/BankInfoForm/BankInfoForm.tsx | 82 + .../Breakdown/Breakdown.tsx | 0 .../ChildrenFilesForm.tsx | 14 +- .../src/fields/ChildrenForm/ChildInput.tsx | 82 + .../src/fields/ChildrenForm/ChildrenForm.tsx | 129 +- .../Confirmation/ApplicantConfirmation.tsx | 39 + .../src/fields/Confirmation/Confirmation.tsx | 116 + .../Confirmation/SpouseConfirmation.tsx | 28 + .../ConfirmationSectionImage.tsx | 18 + .../src/fields/ContactInfo/ContactInfo.tsx | 68 + .../src/fields/CopyUrl/CopyUrl.tsx | 4 +- .../DescriptionText/DescriptionText.css.ts | 0 .../DescriptionText/DescriptionText.tsx | 0 .../DirectTaxPaymentModal.css.ts | 0 .../DirectTaxPaymentModal.tsx | 4 +- .../fields/EmploymentForm/EmploymentForm.tsx | 97 + .../FileUploadContainer.tsx} | 2 +- .../src/fields/{files => Files}/Files.tsx | 2 +- .../HomeCircumstancesForm.tsx | 116 + .../InRelationshipForm/InRelationshipForm.tsx | 67 + .../IncomeFilesForm.tsx | 22 +- .../src/fields/IncomeForm/IncomeForm.tsx | 61 + .../financial-aid/src/fields/Logo/Logo.css.ts | 14 + .../financial-aid/src/fields/Logo/Logo.tsx | 46 + .../FileList}/FileList.css.ts | 2 +- .../FileList}/FileList.tsx | 7 +- .../MissingFiles.tsx | 37 +- .../MissingFilesConfirmation.tsx | 40 +- .../PersonalTaxCreditForm.tsx | 56 + .../PrivacyPolicyAccordion.tsx | 41 + .../fields/ServiceCenter/ServiceCenter.tsx | 55 + .../financial-aid/src/fields/Shared.css.ts | 58 + .../Status/AidAmount/AidAmount.tsx | 20 +- .../src/fields/Status/ApplicantStatus.tsx | 59 +- .../Status/ApprovedAlert/ApprovedAlert.tsx | 2 + .../fields/Status/Estimation/Estimation.tsx | 121 + .../Status/Header/Header.tsx | 2 + .../MissingFilesCard/MissingFilesCard.tsx | 1 + .../Status/MoreActions/MoreActions.tsx | 3 +- .../RejectionMessage/RejectionMessage.tsx | 3 +- .../Status/SpouseAlert/SpouseAlert.tsx | 3 +- .../Status/SpouseApproved/SpouseApproved.tsx | 2 + .../src/fields/Status/SpouseStatus.tsx | 44 +- .../src/fields/Status/Status.css.ts | 6 + .../Status/Timeline/Timeline.css.ts | 0 .../Status/Timeline/Timeline.tsx | 4 +- .../financial-aid/src/fields/Status/index.ts | 10 + .../financial-aid/src/fields/Status/util.ts | 33 - .../src/fields/StudentForm/StudentForm.tsx | 73 + .../Summary/ChildrenInfo.tsx | 8 +- .../Summary/ContactInfo.tsx | 1 + .../Summary/DirectTaxPaymentCell.tsx | 0 .../{components => fields}/Summary/Files.tsx | 2 +- .../Summary/FormInfo.tsx | 0 .../src/fields/Summary/SpouseSummaryForm.tsx | 101 +- .../Summary/SummaryBlock.tsx | 2 +- .../Summary/SummaryComment.tsx | 0 .../src/fields/Summary/SummaryForm.tsx | 123 +- .../Summary/UserInfo.tsx | 1 + .../financial-aid/src/fields/Summary/index.ts | 6 + .../financial-aid/src/fields/Summary/utils.ts | 179 - .../TaxBreakdown/TaxBreakdown.css.ts | 0 .../TaxBreakdown/TaxBreakdown.tsx | 1 + .../TaxBreakdown/TaxBreakdownHeadline.tsx | 0 .../TaxBreakdown/TaxBreakdownItem.tsx | 0 .../TaxReturnFilesForm.tsx | 42 +- .../taxFormContent.tsx} | 2 +- .../UnknownRelationshipForm.tsx | 158 + .../FileUploadController.css.ts | 11 - .../financial-aid/src/fields/index.ts | 44 +- .../src/forms/ApplicantSubmitted.ts | 64 + .../ApplicantSubmittedForm/ApplicantStatus.ts | 15 - .../ApplicantSubmittedForm/MissingFiles.ts | 42 - .../MissingFilesConfirmation.ts | 35 - .../src/forms/ApplicantSubmittedForm/index.ts | 18 - .../financial-aid/src/forms/Application.ts | 251 ++ .../confirmationMultiField.ts | 112 - .../confirmationSection/index.ts | 10 - .../contactInfoMultiField.ts | 22 - .../contactInfoSection/index.ts | 10 - .../financesSection/bankInfoSubSection.ts | 33 - .../financesSection/incomeFileSubSection.ts | 34 - .../financesSection/incomeSubSection.ts | 35 - .../ApplicationForm/financesSection/index.ts | 20 - .../personalTaxCreditSubSection.ts | 37 - .../taxReturnFilesSubSection.ts | 30 - .../src/forms/ApplicationForm/index.ts | 27 - .../childrenFilesSubSection.ts | 34 - .../childrenSubSection.ts | 57 - .../employmentSubSection.ts | 39 - .../homeCircumstancesSubSection.ts | 40 - .../inARelationshipSubSection.ts | 49 - .../personalInterestSection/index.ts | 24 - .../studentSubSection.ts | 38 - .../unknownRelationshipSubSection.ts | 74 - .../ApplicationForm/summarySection/index.ts | 10 - .../summarySection/summaryMultiField.ts | 31 - .../src/forms/MuncipalityNotRegistered.ts | 17 + .../MunicipalityNotRegistered.ts | 61 - .../financial-aid/src/forms/Prerequisites.ts | 115 + .../PrerequisitesForm/externalDataSection.ts | 66 - .../src/forms/PrerequisitesForm/index.ts | 19 - .../PrerequisitesForm/informationSection.ts | 72 - .../src/forms/PrerequisitesSpouse.ts | 88 + .../forms/PrerequisitesSpouseForm/index.ts | 19 - .../informationSection.ts | 84 - .../prerequisitesSection.ts | 39 - .../financial-aid/src/forms/Spouse.ts | 109 + .../src/forms/SpouseForm/index.ts | 29 - .../SpouseForm/spouseConfirmationSection.ts | 88 - .../SpouseForm/spouseContactInfoSection.ts | 32 - .../SpouseForm/spouseIncomeFilesSection.ts | 24 - .../forms/SpouseForm/spouseIncomeSection.ts | 35 - .../forms/SpouseForm/spouseSumarySection.ts | 38 - .../SpouseForm/spouseTaxReturnFilesSection.ts | 34 - .../src/forms/SpouseSubmitted.ts | 64 + .../forms/SpouseSubmittedForm/MissingFiles.ts | 45 - .../MissingFilesConfirmation.ts | 38 - .../forms/SpouseSubmittedForm/SpouseStatus.ts | 15 - .../src/forms/SpouseSubmittedForm/index.ts | 19 - .../src/lib/FinancialAidTemplate.ts | 55 +- .../financial-aid/src/lib/constants.ts | 17 +- .../financial-aid/src/lib/dataSchema.ts | 275 +- .../financial-aid/src/lib/formatters.ts | 135 +- .../src/lib/hooks/useApplication.ts | 6 +- .../src/lib/messages/aboutForm.ts | 2 +- .../src/lib/messages/aboutSpouseForm.ts | 2 +- .../financial-aid/src/lib/messages/error.ts | 5 - .../src/lib/messages/incomeForm.ts | 16 +- .../src/lib/messages/missingFiles.ts | 20 +- .../lib/messages/privacyPolicyAccordion.ts | 14 +- .../src/lib/messages/unknownRelationship.ts | 2 +- .../templates/financial-aid/src/lib/types.ts | 47 +- .../templates/financial-aid/src/lib/utils.ts | 159 +- .../lib/utils/getApplicantsServiceCenter.ts | 16 - .../src/lib/utils/getEmploymentOptions.ts | 25 - .../lib/utils/getHomeCircumstancesOptions.ts | 34 - .../src/lib/utils/getIncomeOptions.ts | 16 - .../lib/utils/getPersonalTaxCreditOptions.ts | 17 - .../src/lib/utils/getStudentOptions.ts | 17 - .../utils/getUnknownRelationshipOptions.tsx | 17 - .../src/{assets => }/svg/akrahreppur.svg | 0 .../src/{assets => }/svg/akranes.svg | 0 .../src/{assets => }/svg/akureyri.svg | 0 .../src/{assets => }/svg/arborg.svg | 0 .../src/{assets => }/svg/arneshreppur.svg | 0 .../src/{assets => }/svg/asahreppur.svg | 0 .../src/{assets => }/svg/blaskogabyggd.svg | 0 .../src/{assets => }/svg/blonduosbaer.svg | 0 .../src/{assets => }/svg/bolungarvik.svg | 0 .../src/{assets => }/svg/borgarbyggd.svg | 0 .../src/{assets => }/svg/dalabyggd.svg | 0 .../src/{assets => }/svg/dalvikurbyggd.svg | 0 .../svg/eyja-og-miklaholtshreppur.svg | 0 .../src/{assets => }/svg/eyjafjardarsveit.svg | 0 .../src/{assets => }/svg/fjallabyggd.svg | 0 .../src/{assets => }/svg/fjardabyggd.svg | 0 .../{assets => }/svg/fljotsdalshreppur.svg | 0 .../src/{assets => }/svg/floahreppur.svg | 0 .../src/{assets => }/svg/gardabaer.svg | 0 .../svg/grimsnes-og-grafningshreppur.svg | 0 .../src/{assets => }/svg/grindavikurbaer.svg | 0 .../{assets => }/svg/grundafjardarbaer.svg | 0 .../{assets => }/svg/grytubakkahreppur.svg | 0 .../src/{assets => }/svg/hafnarfjordur.svg | 0 .../src/{assets => }/svg/helgafellssveit.svg | 0 .../src/{assets => }/svg/horgarsveit.svg | 0 .../src/{assets => }/svg/hornafjordur.svg | 0 .../{assets => }/svg/hrunamannahreppur.svg | 0 .../src/{assets => }/svg/hunathing-vestra.svg | 0 .../src/{assets => }/svg/hunavatnshreppur.svg | 0 .../src/{assets => }/svg/hvalfjardarsveit.svg | 0 .../src/{assets => }/svg/hveragerdisbaer.svg | 0 .../src/{assets => }/svg/isafjardarbaer.svg | 0 .../{assets => }/svg/kaldrananeshreppur.svg | 0 .../src/{assets => }/svg/kjosarhreppur.svg | 0 .../src/{assets => }/svg/kopavogur.svg | 0 .../src/{assets => }/svg/langanesbyggd.svg | 0 .../src/{assets => }/svg/mosfellsbaer.svg | 0 .../src/{assets => }/svg/mulathing.svg | 0 .../src/{assets => }/svg/myrdalshreppur.svg | 0 .../src/{assets => }/svg/nordurthing.svg | 0 .../src/{assets => }/svg/olfus.svg | 0 .../src/{assets => }/svg/rangarthing-ytra.svg | 0 .../{assets => }/svg/rangarthing_eystra.svg | 0 .../src/{assets => }/svg/reykholahreppur.svg | 0 .../src/{assets => }/svg/reykjanesbaer.svg | 0 .../src/{assets => }/svg/sambandid.svg | 0 .../src/{assets => }/svg/seltjarnarnes.svg | 0 .../src/{assets => }/svg/skaftarhreppur.svg | 0 .../src/{assets => }/svg/skagabyggd.svg | 0 .../src/{assets => }/svg/skagafjordur.svg | 0 .../src/{assets => }/svg/skagastrond.svg | 0 .../svg/skeida-og-gnupverjahreppur.svg | 0 .../src/{assets => }/svg/skorradalur.svg | 0 .../{assets => }/svg/skutustadahreppur.svg | 0 .../src/{assets => }/svg/snaefellsbaer.svg | 0 .../src/{assets => }/svg/strandabyggd.svg | 0 .../src/{assets => }/svg/stykkisholmsbaer.svg | 0 .../src/{assets => }/svg/sudavikurhreppur.svg | 0 .../src/{assets => }/svg/sudurnesjabaer.svg | 0 .../src/{assets => }/svg/svalbardshreppur.svg | 0 .../svg/svalbardsstrandarhreppur.svg | 0 .../{assets => }/svg/talknafjardarhreppur.svg | 0 .../src/{assets => }/svg/thingeyjarsveit.svg | 0 .../src/{assets => }/svg/tjorneshreppur.svg | 0 .../{assets => }/svg/vestmannaeyjabaer.svg | 0 .../src/{assets => }/svg/vesturbyggd.svg | 0 .../src/{assets => }/svg/vogar.svg | 0 .../{assets => }/svg/vopnafjardarhreppur.svg | 0 libs/application/types/src/lib/Fields.ts | 30 - libs/application/types/src/lib/Form.ts | 2 +- .../AccordionFormField/AccordionFormField.tsx | 65 - .../BankAccountFormField.tsx | 90 - libs/application/ui-fields/src/lib/index.ts | 2 - .../ui-shell/src/lib/FormShell.tsx | 5 +- libs/application/ui-shell/src/utils.ts | 37 - .../shared/src/lib/formatters.ts | 8 +- .../src/Markdown/markdownOptions.tsx | 1 + 231 files changed, 3023 insertions(+), 7135 deletions(-) delete mode 100644 libs/application/templates/financial-aid/src/assets/ConfirmationImage.tsx delete mode 100644 libs/application/templates/financial-aid/src/components/Logo/Logo.tsx delete mode 100644 libs/application/templates/financial-aid/src/components/Status/Estimation/Estimation.tsx delete mode 100644 libs/application/templates/financial-aid/src/components/Status/Estimation/VeitaEstimation.tsx delete mode 100644 libs/application/templates/financial-aid/src/components/Status/Estimation/utils.ts delete mode 100644 libs/application/templates/financial-aid/src/components/Summary/Files.css.ts delete mode 100644 libs/application/templates/financial-aid/src/components/Summary/SummaryBlock.css.ts create mode 100644 libs/application/templates/financial-aid/src/fields/AboutForm/AboutForm.tsx create mode 100644 libs/application/templates/financial-aid/src/fields/AboutSpouseForm/AboutSpouseForm.tsx create mode 100644 libs/application/templates/financial-aid/src/fields/BankInfoForm/BankInfoForm.tsx rename libs/application/templates/financial-aid/src/{components => fields}/Breakdown/Breakdown.tsx (100%) rename libs/application/templates/financial-aid/src/fields/{childrenFilesForm => ChildrenFilesForm}/ChildrenFilesForm.tsx (58%) create mode 100644 libs/application/templates/financial-aid/src/fields/ChildrenForm/ChildInput.tsx create mode 100644 libs/application/templates/financial-aid/src/fields/Confirmation/ApplicantConfirmation.tsx create mode 100644 libs/application/templates/financial-aid/src/fields/Confirmation/Confirmation.tsx create mode 100644 libs/application/templates/financial-aid/src/fields/Confirmation/SpouseConfirmation.tsx create mode 100644 libs/application/templates/financial-aid/src/fields/ConfirmationSectionImage/ConfirmationSectionImage.tsx create mode 100644 libs/application/templates/financial-aid/src/fields/ContactInfo/ContactInfo.tsx rename libs/application/templates/financial-aid/src/{components => fields}/DescriptionText/DescriptionText.css.ts (100%) rename libs/application/templates/financial-aid/src/{components => fields}/DescriptionText/DescriptionText.tsx (100%) rename libs/application/templates/financial-aid/src/{components => fields}/DirectTaxPaymentsModal/DirectTaxPaymentModal.css.ts (100%) rename libs/application/templates/financial-aid/src/{components => fields}/DirectTaxPaymentsModal/DirectTaxPaymentModal.tsx (96%) create mode 100644 libs/application/templates/financial-aid/src/fields/EmploymentForm/EmploymentForm.tsx rename libs/application/templates/financial-aid/src/fields/{fileUploadController/FileUploadControler.tsx => FileUploadContainer/FileUploadContainer.tsx} (94%) rename libs/application/templates/financial-aid/src/fields/{files => Files}/Files.tsx (95%) create mode 100644 libs/application/templates/financial-aid/src/fields/HomeCircumstancesForm/HomeCircumstancesForm.tsx create mode 100644 libs/application/templates/financial-aid/src/fields/InRelationshipForm/InRelationshipForm.tsx rename libs/application/templates/financial-aid/src/fields/{incomeFilesForm => IncomeFilesForm}/IncomeFilesForm.tsx (55%) create mode 100644 libs/application/templates/financial-aid/src/fields/IncomeForm/IncomeForm.tsx create mode 100644 libs/application/templates/financial-aid/src/fields/Logo/Logo.css.ts create mode 100644 libs/application/templates/financial-aid/src/fields/Logo/Logo.tsx rename libs/application/templates/financial-aid/src/fields/{Summary => MissingFiles/FileList}/FileList.css.ts (97%) rename libs/application/templates/financial-aid/src/fields/{Summary => MissingFiles/FileList}/FileList.tsx (93%) rename libs/application/templates/financial-aid/src/fields/{missingFiles => MissingFiles}/MissingFiles.tsx (85%) rename libs/application/templates/financial-aid/src/fields/{Summary => MissingFiles/MissingFilesConfirmation}/MissingFilesConfirmation.tsx (50%) create mode 100644 libs/application/templates/financial-aid/src/fields/PersonalTaxCreditForm/PersonalTaxCreditForm.tsx create mode 100644 libs/application/templates/financial-aid/src/fields/PrivacyPolicyAccordion/PrivacyPolicyAccordion.tsx create mode 100644 libs/application/templates/financial-aid/src/fields/ServiceCenter/ServiceCenter.tsx create mode 100644 libs/application/templates/financial-aid/src/fields/Shared.css.ts rename libs/application/templates/financial-aid/src/{components => fields}/Status/AidAmount/AidAmount.tsx (72%) rename libs/application/templates/financial-aid/src/{components => fields}/Status/ApprovedAlert/ApprovedAlert.tsx (99%) create mode 100644 libs/application/templates/financial-aid/src/fields/Status/Estimation/Estimation.tsx rename libs/application/templates/financial-aid/src/{components => fields}/Status/Header/Header.tsx (99%) rename libs/application/templates/financial-aid/src/{components => fields}/Status/MissingFilesCard/MissingFilesCard.tsx (99%) rename libs/application/templates/financial-aid/src/{components => fields}/Status/MoreActions/MoreActions.tsx (93%) rename libs/application/templates/financial-aid/src/{components => fields}/Status/RejectionMessage/RejectionMessage.tsx (94%) rename libs/application/templates/financial-aid/src/{components => fields}/Status/SpouseAlert/SpouseAlert.tsx (95%) rename libs/application/templates/financial-aid/src/{components => fields}/Status/SpouseApproved/SpouseApproved.tsx (99%) create mode 100644 libs/application/templates/financial-aid/src/fields/Status/Status.css.ts rename libs/application/templates/financial-aid/src/{components => fields}/Status/Timeline/Timeline.css.ts (100%) rename libs/application/templates/financial-aid/src/{components => fields}/Status/Timeline/Timeline.tsx (96%) create mode 100644 libs/application/templates/financial-aid/src/fields/Status/index.ts delete mode 100644 libs/application/templates/financial-aid/src/fields/Status/util.ts create mode 100644 libs/application/templates/financial-aid/src/fields/StudentForm/StudentForm.tsx rename libs/application/templates/financial-aid/src/{components => fields}/Summary/ChildrenInfo.tsx (96%) rename libs/application/templates/financial-aid/src/{components => fields}/Summary/ContactInfo.tsx (99%) rename libs/application/templates/financial-aid/src/{components => fields}/Summary/DirectTaxPaymentCell.tsx (100%) rename libs/application/templates/financial-aid/src/{components => fields}/Summary/Files.tsx (98%) rename libs/application/templates/financial-aid/src/{components => fields}/Summary/FormInfo.tsx (100%) rename libs/application/templates/financial-aid/src/{components => fields}/Summary/SummaryBlock.tsx (95%) rename libs/application/templates/financial-aid/src/{components => fields}/Summary/SummaryComment.tsx (100%) rename libs/application/templates/financial-aid/src/{components => fields}/Summary/UserInfo.tsx (99%) create mode 100644 libs/application/templates/financial-aid/src/fields/Summary/index.ts delete mode 100644 libs/application/templates/financial-aid/src/fields/Summary/utils.ts rename libs/application/templates/financial-aid/src/{components => fields}/TaxBreakdown/TaxBreakdown.css.ts (100%) rename libs/application/templates/financial-aid/src/{components => fields}/TaxBreakdown/TaxBreakdown.tsx (99%) rename libs/application/templates/financial-aid/src/{components => fields}/TaxBreakdown/TaxBreakdownHeadline.tsx (100%) rename libs/application/templates/financial-aid/src/{components => fields}/TaxBreakdown/TaxBreakdownItem.tsx (100%) rename libs/application/templates/financial-aid/src/fields/{taxReturnFilesForm => TaxReturnFilesForm}/TaxReturnFilesForm.tsx (59%) rename libs/application/templates/financial-aid/src/fields/{taxReturnFilesForm/TaxFormContent.tsx => TaxReturnFilesForm/taxFormContent.tsx} (96%) create mode 100644 libs/application/templates/financial-aid/src/fields/UnknownRelationshipForm/UnknownRelationshipForm.tsx delete mode 100644 libs/application/templates/financial-aid/src/fields/fileUploadController/FileUploadController.css.ts create mode 100644 libs/application/templates/financial-aid/src/forms/ApplicantSubmitted.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/ApplicantSubmittedForm/ApplicantStatus.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/ApplicantSubmittedForm/MissingFiles.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/ApplicantSubmittedForm/MissingFilesConfirmation.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/ApplicantSubmittedForm/index.ts create mode 100644 libs/application/templates/financial-aid/src/forms/Application.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/ApplicationForm/confirmationSection/confirmationMultiField.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/ApplicationForm/confirmationSection/index.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/ApplicationForm/contactInfoSection/contactInfoMultiField.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/ApplicationForm/contactInfoSection/index.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/bankInfoSubSection.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/incomeFileSubSection.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/incomeSubSection.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/index.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/personalTaxCreditSubSection.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/taxReturnFilesSubSection.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/ApplicationForm/index.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/childrenFilesSubSection.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/childrenSubSection.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/employmentSubSection.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/homeCircumstancesSubSection.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/inARelationshipSubSection.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/index.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/studentSubSection.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/unknownRelationshipSubSection.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/ApplicationForm/summarySection/index.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/ApplicationForm/summarySection/summaryMultiField.ts create mode 100644 libs/application/templates/financial-aid/src/forms/MuncipalityNotRegistered.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/MunicipalityNotRegisteredForm/MunicipalityNotRegistered.ts create mode 100644 libs/application/templates/financial-aid/src/forms/Prerequisites.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/PrerequisitesForm/externalDataSection.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/PrerequisitesForm/index.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/PrerequisitesForm/informationSection.ts create mode 100644 libs/application/templates/financial-aid/src/forms/PrerequisitesSpouse.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/PrerequisitesSpouseForm/index.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/PrerequisitesSpouseForm/informationSection.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/PrerequisitesSpouseForm/prerequisitesSection.ts create mode 100644 libs/application/templates/financial-aid/src/forms/Spouse.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/SpouseForm/index.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/SpouseForm/spouseConfirmationSection.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/SpouseForm/spouseContactInfoSection.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/SpouseForm/spouseIncomeFilesSection.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/SpouseForm/spouseIncomeSection.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/SpouseForm/spouseSumarySection.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/SpouseForm/spouseTaxReturnFilesSection.ts create mode 100644 libs/application/templates/financial-aid/src/forms/SpouseSubmitted.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/SpouseSubmittedForm/MissingFiles.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/SpouseSubmittedForm/MissingFilesConfirmation.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/SpouseSubmittedForm/SpouseStatus.ts delete mode 100644 libs/application/templates/financial-aid/src/forms/SpouseSubmittedForm/index.ts delete mode 100644 libs/application/templates/financial-aid/src/lib/utils/getApplicantsServiceCenter.ts delete mode 100644 libs/application/templates/financial-aid/src/lib/utils/getEmploymentOptions.ts delete mode 100644 libs/application/templates/financial-aid/src/lib/utils/getHomeCircumstancesOptions.ts delete mode 100644 libs/application/templates/financial-aid/src/lib/utils/getIncomeOptions.ts delete mode 100644 libs/application/templates/financial-aid/src/lib/utils/getPersonalTaxCreditOptions.ts delete mode 100644 libs/application/templates/financial-aid/src/lib/utils/getStudentOptions.ts delete mode 100644 libs/application/templates/financial-aid/src/lib/utils/getUnknownRelationshipOptions.tsx rename libs/application/templates/financial-aid/src/{assets => }/svg/akrahreppur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/akranes.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/akureyri.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/arborg.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/arneshreppur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/asahreppur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/blaskogabyggd.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/blonduosbaer.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/bolungarvik.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/borgarbyggd.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/dalabyggd.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/dalvikurbyggd.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/eyja-og-miklaholtshreppur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/eyjafjardarsveit.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/fjallabyggd.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/fjardabyggd.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/fljotsdalshreppur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/floahreppur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/gardabaer.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/grimsnes-og-grafningshreppur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/grindavikurbaer.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/grundafjardarbaer.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/grytubakkahreppur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/hafnarfjordur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/helgafellssveit.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/horgarsveit.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/hornafjordur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/hrunamannahreppur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/hunathing-vestra.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/hunavatnshreppur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/hvalfjardarsveit.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/hveragerdisbaer.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/isafjardarbaer.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/kaldrananeshreppur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/kjosarhreppur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/kopavogur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/langanesbyggd.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/mosfellsbaer.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/mulathing.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/myrdalshreppur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/nordurthing.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/olfus.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/rangarthing-ytra.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/rangarthing_eystra.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/reykholahreppur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/reykjanesbaer.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/sambandid.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/seltjarnarnes.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/skaftarhreppur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/skagabyggd.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/skagafjordur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/skagastrond.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/skeida-og-gnupverjahreppur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/skorradalur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/skutustadahreppur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/snaefellsbaer.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/strandabyggd.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/stykkisholmsbaer.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/sudavikurhreppur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/sudurnesjabaer.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/svalbardshreppur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/svalbardsstrandarhreppur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/talknafjardarhreppur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/thingeyjarsveit.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/tjorneshreppur.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/vestmannaeyjabaer.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/vesturbyggd.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/vogar.svg (100%) rename libs/application/templates/financial-aid/src/{assets => }/svg/vopnafjardarhreppur.svg (100%) delete mode 100644 libs/application/ui-fields/src/lib/AccordionFormField/AccordionFormField.tsx delete mode 100644 libs/application/ui-fields/src/lib/BankAccountFormField/BankAccountFormField.tsx diff --git a/libs/application/core/src/lib/fieldBuilders.ts b/libs/application/core/src/lib/fieldBuilders.ts index 2c77343d5aca..1ef54e3fa911 100644 --- a/libs/application/core/src/lib/fieldBuilders.ts +++ b/libs/application/core/src/lib/fieldBuilders.ts @@ -41,8 +41,6 @@ import { StaticTableField, HiddenInputWithWatchedValueField, HiddenInputField, - AccordionField, - BankAccountField, SliderField, MaybeWithApplication, MaybeWithApplicationAndFieldAndLocale, @@ -459,49 +457,6 @@ export const buildKeyValueField = (data: { } } -export const buildAccordionField = ( - data: Omit, -): AccordionField => { - const { - accordionItems, - title, - titleVariant, - id, - marginTop, - marginBottom, - condition, - } = data - return { - children: undefined, - id, - title, - titleVariant, - marginTop, - marginBottom, - accordionItems, - condition, - type: FieldTypes.ACCORDION, - component: FieldComponents.ACCORDION, - } -} - -export const buildBankAccountField = ( - data: Omit, -): BankAccountField => { - const { title, id, marginBottom, marginTop, titleVariant } = data - - return { - children: undefined, - id, - title, - marginBottom, - marginTop, - titleVariant, - type: FieldTypes.BANK_ACCOUNT, - component: FieldComponents.BANK_ACCOUNT, - } -} - export const buildSubmitField = (data: { id: string title: FormText diff --git a/libs/application/core/src/lib/messages.ts b/libs/application/core/src/lib/messages.ts index 65cb2f5cdd67..aaaafd03dee2 100644 --- a/libs/application/core/src/lib/messages.ts +++ b/libs/application/core/src/lib/messages.ts @@ -318,21 +318,6 @@ export const coreDefaultFieldMessages = defineMessages({ defaultMessage: 'VeljiĂ° skjöl til aĂ° hlaĂ°a upp', description: 'Default file upload button label', }, - defaultBankAccountBankNumber: { - id: 'application.system:core.default.bankAccount.bankNumber', - defaultMessage: 'BankanĂșmer', - description: 'Bank account bank number', - }, - defaultBankAccountLedger: { - id: 'application.system:core.default.bankAccount.ledger', - defaultMessage: 'HöfuĂ°bĂłk', - description: 'Bank account ledger', - }, - defaultBankAccountAccountNumber: { - id: 'application.system:core.default.bankAccount.accountNumber', - defaultMessage: 'ReikningsnĂșmer', - description: 'Bank account account number', - }, defaultDownloadButtonTitle: { id: 'application.system:core.default.pdfLinkButtonField.downloadButtonTitle', defaultMessage: 'HlaĂ°a niĂ°ur skjali', diff --git a/libs/application/template-api-modules/src/lib/modules/templates/financial-aid/financial-aid.service.ts b/libs/application/template-api-modules/src/lib/modules/templates/financial-aid/financial-aid.service.ts index a6bb3c78fbf7..d3d79b8777f2 100644 --- a/libs/application/template-api-modules/src/lib/modules/templates/financial-aid/financial-aid.service.ts +++ b/libs/application/template-api-modules/src/lib/modules/templates/financial-aid/financial-aid.service.ts @@ -1,11 +1,9 @@ import { Injectable } from '@nestjs/common' import { - FinancialAidAnswers, ApproveOptions, - ChildrenSchoolInfo, CurrentApplication, - FinancialAidExternalData, + FAApplication, findFamilyStatus, TaxData, } from '@island.is/application/templates/financial-aid' @@ -14,7 +12,6 @@ import { AuthMiddleware } from '@island.is/auth-nest-tools' import { ApplicationState, FileType, - PersonalTaxReturn, UserType, } from '@island.is/financial-aid/shared/lib' import { @@ -27,17 +24,15 @@ import { import { TemplateApiModuleActionProps } from '../../../types' import { BaseTemplateApiService } from '../../base-template-api.service' import { - Application, ApplicationAnswerFile, ApplicationTypes, } from '@island.is/application/types' import { FetchError } from '@island.is/clients/middlewares' import { messages } from '@island.is/application/templates/financial-aid' import { TemplateApiError } from '@island.is/nest/problem' -import { getValueViaPath } from '@island.is/application/core' type Props = Omit & { - application: Application + application: FAApplication } @Injectable() @@ -80,26 +75,15 @@ export class FinancialAidService extends BaseTemplateApiService { auth, }: Props): Promise { const { id, answers, externalData } = application - const answersSchema = answers as FinancialAidAnswers - const externalDataSchema = - externalData as unknown as FinancialAidExternalData - const currentApplicationId = getValueViaPath( - externalData, - 'currentApplication.data.currentApplicationId', - ) as string | undefined - const childrenSchoolInfo = getValueViaPath( - answers, - 'childrenSchoolInfo', - ) as Array - - if (currentApplicationId) { + if (externalData.currentApplication.data?.currentApplicationId) { return { - currentApplicationId, + currentApplicationId: + externalData.currentApplication.data.currentApplicationId, } } const children = answers.childrenSchoolInfo - ? childrenSchoolInfo.map((child) => { + ? answers.childrenSchoolInfo.map((child) => { return { name: child.fullName, nationalId: child.nationalId, @@ -124,117 +108,102 @@ export class FinancialAidService extends BaseTemplateApiService { }) } - const personalTaxReturn = getValueViaPath( - externalData, - 'taxData.data.municipalitiesPersonalTaxReturn.personalTaxReturn', - ) as PersonalTaxReturn | undefined - - const spouseTaxReturn = getValueViaPath( - externalData, - 'taxDataSpouse.data.municipalitiesPersonalTaxReturn.personalTaxReturn', - ) as PersonalTaxReturn | undefined - const spouseTaxFiles = () => { - if (spouseTaxReturn == null) { + if ( + externalData?.taxDataSpouse?.data?.municipalitiesPersonalTaxReturn + ?.personalTaxReturn == null + ) { return [] } - return [spouseTaxReturn] + return [ + externalData?.taxDataSpouse?.data?.municipalitiesPersonalTaxReturn + ?.personalTaxReturn, + ] } const applicantTaxFiles = () => { - if (personalTaxReturn == null) { + if ( + externalData?.taxData?.data?.municipalitiesPersonalTaxReturn + ?.personalTaxReturn == null + ) { return [] } - return [personalTaxReturn] + return [ + externalData?.taxData?.data?.municipalitiesPersonalTaxReturn + ?.personalTaxReturn, + ] } const directTaxPayments = () => { - const combinedTaxPayments = [ - ...(externalDataSchema?.taxData?.data?.municipalitiesDirectTaxPayments - ?.directTaxPayments || []), - ...(externalDataSchema?.taxDataSpouse?.data - ?.municipalitiesDirectTaxPayments?.directTaxPayments || []), - ] - - return combinedTaxPayments.map((d) => { - d.userType = application.assignees.includes(auth.nationalId) - ? UserType.SPOUSE - : UserType.APPLICANT - return d - }) + if (externalData?.taxDataSpouse?.data) { + externalData?.taxData?.data?.municipalitiesDirectTaxPayments?.directTaxPayments.concat( + externalData?.taxDataSpouse?.data.municipalitiesDirectTaxPayments + ?.directTaxPayments, + ) + } + return externalData?.taxData?.data?.municipalitiesDirectTaxPayments?.directTaxPayments.map( + (d) => { + d.userType = application.assignees.includes(auth.nationalId) + ? UserType.SPOUSE + : UserType.APPLICANT + return d + }, + ) } - const files = formatFiles( - answersSchema?.taxReturnFiles ?? [], - FileType.TAXRETURN, - ) - .concat(formatFiles(answersSchema.incomeFiles ?? [], FileType.INCOME)) - .concat( - formatFiles( - answersSchema.spouseIncomeFiles ?? [], - FileType.SPOUSEFILES, - ), - ) - .concat( - formatFiles( - answersSchema.spouseTaxReturnFiles ?? [], - FileType.SPOUSEFILES, - ), - ) + const files = formatFiles(answers.taxReturnFiles, FileType.TAXRETURN) + .concat(formatFiles(answers.incomeFiles, FileType.INCOME)) + .concat(formatFiles(answers.spouseIncomeFiles, FileType.SPOUSEFILES)) + .concat(formatFiles(answers.spouseTaxReturnFiles, FileType.SPOUSEFILES)) .concat(formatFiles(spouseTaxFiles(), FileType.SPOUSEFILES)) .concat(formatFiles(applicantTaxFiles(), FileType.TAXRETURN)) - .concat( - formatFiles(answersSchema.childrenFiles ?? [], FileType.CHILDRENFILES), - ) + .concat(formatFiles(answers.childrenFiles, FileType.CHILDRENFILES)) const newApplication = { - name: externalDataSchema.nationalRegistry.data.fullName, - nationalId: externalDataSchema.nationalRegistry.data.nationalId, - phoneNumber: answersSchema.contactInfo.phone, - email: answersSchema.contactInfo.email, - homeCircumstances: answersSchema.homeCircumstances.type, - homeCircumstancesCustom: answersSchema.homeCircumstances.custom, - student: Boolean(answersSchema.student.isStudent === ApproveOptions.Yes), - studentCustom: answersSchema.student.custom, - hasIncome: Boolean(answersSchema.income.type === ApproveOptions.Yes), + name: externalData.nationalRegistry.data.fullName, + nationalId: externalData.nationalRegistry.data.nationalId, + phoneNumber: answers.contactInfo.phone, + email: answers.contactInfo.email, + homeCircumstances: answers.homeCircumstances.type, + homeCircumstancesCustom: answers.homeCircumstances.custom, + student: Boolean(answers.student.isStudent === ApproveOptions.Yes), + studentCustom: answers.student.custom, + hasIncome: Boolean(answers.income === ApproveOptions.Yes), usePersonalTaxCredit: Boolean( - answersSchema.personalTaxCredit.type === ApproveOptions.Yes, + answers.personalTaxCredit === ApproveOptions.Yes, ), - bankNumber: answersSchema.bankInfo.bankNumber, - ledger: answersSchema.bankInfo.ledger, - accountNumber: answersSchema.bankInfo.accountNumber, - employment: answersSchema.employment.type, - employmentCustom: answersSchema.employment.custom, - formComment: answersSchema.formComment, + bankNumber: answers.bankInfo.bankNumber, + ledger: answers.bankInfo.ledger, + accountNumber: answers.bankInfo.accountNumber, + employment: answers.employment.type, + employmentCustom: answers.employment.custom, + formComment: answers.formComment, state: ApplicationState.NEW, files: files, - children, - childrenComment: answersSchema.childrenComment, + children: children, + childrenComment: answers.childrenComment, spouseNationalId: - externalDataSchema.nationalRegistrySpouse.data?.nationalId || - answersSchema.relationshipStatus?.spouseNationalId, + externalData.nationalRegistrySpouse.data?.nationalId || + answers.relationshipStatus?.spouseNationalId, spouseEmail: - answersSchema.spouseContactInfo?.email || - answersSchema.spouse?.email || - answersSchema.relationshipStatus?.spouseEmail, - spousePhoneNumber: answersSchema.spouseContactInfo?.phone, + answers.spouseContactInfo?.email || + answers.spouse?.email || + answers.relationshipStatus?.spouseEmail, + spousePhoneNumber: answers.spouseContactInfo?.phone, spouseName: - externalDataSchema.nationalRegistrySpouse.data?.name || - answersSchema.spouseName, - spouseFormComment: answersSchema.spouseFormComment, - familyStatus: findFamilyStatus(answersSchema, externalData), - streetName: - externalDataSchema.nationalRegistry.data.address?.streetAddress, - postalCode: externalDataSchema.nationalRegistry.data.address?.postalCode, - city: externalDataSchema.nationalRegistry.data.address?.locality, + externalData.nationalRegistrySpouse.data?.name || answers.spouseName, + spouseFormComment: answers.spouseFormComment, + familyStatus: findFamilyStatus(answers, externalData), + streetName: externalData.nationalRegistry.data.address?.streetAddress, + postalCode: externalData.nationalRegistry.data.address?.postalCode, + city: externalData.nationalRegistry.data.address?.locality, municipalityCode: - externalDataSchema.nationalRegistry.data.address?.municipalityCode, + externalData.nationalRegistry.data.address?.municipalityCode, directTaxPayments: directTaxPayments(), hasFetchedDirectTaxPayment: - externalDataSchema?.taxData?.data?.municipalitiesDirectTaxPayments - ?.success, + externalData?.taxData?.data?.municipalitiesDirectTaxPayments?.success, spouseHasFetchedDirectTaxPayment: - externalDataSchema?.taxDataSpouse?.data?.municipalitiesDirectTaxPayments + externalData?.taxDataSpouse?.data?.municipalitiesDirectTaxPayments ?.success, applicationSystemId: id, } @@ -274,10 +243,8 @@ export class FinancialAidService extends BaseTemplateApiService { auth, application, }: Props): Promise { - const municiplaityCode = getValueViaPath( - application.externalData, - 'nationalRegistry.data.address.municipalityCode', - ) as string + const municiplaityCode = + application.externalData.nationalRegistry.data.address?.municipalityCode if (municiplaityCode == null) { return null } @@ -320,26 +287,23 @@ export class FinancialAidService extends BaseTemplateApiService { application, }: Props): Promise<{ success: boolean }> { const { answers, externalData } = application - const answersSchema = answers as unknown as FinancialAidAnswers - const externalDataSchema = - externalData as unknown as FinancialAidExternalData try { return await this.applicationApiWithAuth( auth, ).applicationControllerSendSpouseEmail({ spouseEmailDto: { - name: externalDataSchema.nationalRegistry.data.fullName, - email: answersSchema.contactInfo.email, + name: externalData.nationalRegistry.data.fullName, + email: answers.contactInfo.email, spouseName: - externalDataSchema.nationalRegistrySpouse.data?.name || - answersSchema.spouseName || + externalData.nationalRegistrySpouse.data?.name || + answers.spouseName || '', spouseEmail: - answersSchema.spouse?.email || - answersSchema.relationshipStatus.spouseEmail || + answers.spouse?.email || + answers.relationshipStatus.spouseEmail || '', municipalityCode: - externalDataSchema.municipality.data?.municipalityId || '', + externalData.municipality.data?.municipalityId || '', created: application.created, applicationSystemId: application.id, }, diff --git a/libs/application/templates/financial-aid/src/assets/ConfirmationImage.tsx b/libs/application/templates/financial-aid/src/assets/ConfirmationImage.tsx deleted file mode 100644 index 35c114439c4d..000000000000 --- a/libs/application/templates/financial-aid/src/assets/ConfirmationImage.tsx +++ /dev/null @@ -1,3736 +0,0 @@ -import React from 'react' - -export const ConfirmationImage = () => ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -) diff --git a/libs/application/templates/financial-aid/src/components/Logo/Logo.tsx b/libs/application/templates/financial-aid/src/components/Logo/Logo.tsx deleted file mode 100644 index fddd5ef14eb8..000000000000 --- a/libs/application/templates/financial-aid/src/components/Logo/Logo.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { Application } from '@island.is/application/types' -import { - Municipality, - logoKeyFromMunicipalityCode, -} from '@island.is/financial-aid/shared/lib' -import React, { useEffect, useState } from 'react' - -type Props = { - application: Application -} - -export const Logo = ({ application }: Props) => { - const [logo, setLogo] = useState() - const municipality = application.externalData.municipality - ?.data as Municipality - - useEffect(() => { - const getLogo = async () => { - const municipalityId = - municipality && municipality?.municipalityId - ? municipality.municipalityId - : '' - const svgLogo = await import( - `../../assets/svg/${logoKeyFromMunicipalityCode[municipalityId]}` - ) - setLogo(svgLogo.default) - } - getLogo() - }, []) - - return Municipality logo -} diff --git a/libs/application/templates/financial-aid/src/components/Status/Estimation/Estimation.tsx b/libs/application/templates/financial-aid/src/components/Status/Estimation/Estimation.tsx deleted file mode 100644 index 176a8a610ba4..000000000000 --- a/libs/application/templates/financial-aid/src/components/Status/Estimation/Estimation.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import React, { useMemo } from 'react' -import { useIntl } from 'react-intl' -import { Text, Box } from '@island.is/island-ui/core' -import { - aidCalculator, - estimatedBreakDown, - showSpouseData, -} from '@island.is/financial-aid/shared/lib' -import { ApproveOptions } from '../../../lib/types' -import { aidAmount as aidAmountMessages } from '../../../lib/messages' -import { findFamilyStatus } from '../../..' -import Breakdown from '../../../components/Breakdown/Breakdown' -import DescriptionText from '../../../components/DescriptionText/DescriptionText' -import { Application, DataProviderResult } from '@island.is/application/types' -import { getEstimationConstants } from './utils' - -type EstimationProps = { - application: Application - municipality: DataProviderResult -} - -export const Estimation = ({ application, municipality }: EstimationProps) => { - const { formatMessage } = useIntl() - const { answers, externalData } = application - - const getAidType = () => { - return !showSpouseData[findFamilyStatus(answers, externalData)] - } - - const { - individualAid, - cohabitationAid, - homeCircumstances, - personalTaxCredit, - } = getEstimationConstants(municipality, answers) - - const aidAmount = useMemo(() => { - if (homeCircumstances && municipality.data) { - return aidCalculator( - homeCircumstances, - getAidType() ? individualAid : cohabitationAid, - ) - } - }, [municipality.data]) - - return ( - <> - - <> - - {formatMessage(aidAmountMessages.title)} - - - - - - - - - ) -} diff --git a/libs/application/templates/financial-aid/src/components/Status/Estimation/VeitaEstimation.tsx b/libs/application/templates/financial-aid/src/components/Status/Estimation/VeitaEstimation.tsx deleted file mode 100644 index f44986ec70e8..000000000000 --- a/libs/application/templates/financial-aid/src/components/Status/Estimation/VeitaEstimation.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import React, { useMemo } from 'react' -import { useIntl } from 'react-intl' -import { Text, Box } from '@island.is/island-ui/core' -import { aidAmount as aidAmountMessages } from '../../../lib/messages' -import Breakdown from '../../../components/Breakdown/Breakdown' -import DescriptionText from '../../../components/DescriptionText/DescriptionText' -import { DataProviderResult } from '@island.is/application/types' -import { getEstimationConstants } from './utils' -import { - estimatedBreakDown, - showSpouseData, - Application as FinancialAidAnswers, - aidCalculator, -} from '@island.is/financial-aid/shared/lib' - -type VeitaEstiamtionProps = { - application: FinancialAidAnswers - municipality: DataProviderResult -} - -export const VeitaEstimation = ({ - application, - municipality, -}: VeitaEstiamtionProps) => { - const { formatMessage } = useIntl() - - const getAidType = () => { - if (application.familyStatus != undefined) { - return !showSpouseData[application.familyStatus] - } else { - return application.spouseNationalId == null - } - } - - const { individualAid, cohabitationAid } = - getEstimationConstants(municipality) - - const aidAmount = useMemo(() => { - if (municipality.data && application.homeCircumstances) { - return aidCalculator( - application.homeCircumstances, - getAidType() ? individualAid : cohabitationAid, - ) - } - }, [municipality.data]) - - return ( - <> - - <> - - {formatMessage(aidAmountMessages.title)} - - - - - - - - - ) -} diff --git a/libs/application/templates/financial-aid/src/components/Status/Estimation/utils.ts b/libs/application/templates/financial-aid/src/components/Status/Estimation/utils.ts deleted file mode 100644 index f9a99f642cf6..000000000000 --- a/libs/application/templates/financial-aid/src/components/Status/Estimation/utils.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { DataProviderResult, FormValue } from '@island.is/application/types' -import { Aid, HomeCircumstances } from '@island.is/financial-aid/shared/lib' -import { ApproveOptions } from '../../../lib/types' - -export const getEstimationConstants = ( - municipality: DataProviderResult, - answers?: FormValue, -) => { - const municipalityData = municipality.data as unknown as Record< - string, - unknown - > - const individualAid = getValueViaPath( - municipalityData, - 'data.individualAid', - ) - const cohabitationAid = getValueViaPath( - municipalityData, - 'cohabitationAid', - ) - - if (answers) { - const homeCircumstances = getValueViaPath( - answers, - 'homeCircumstances.type', - ) - const personalTaxCredit = getValueViaPath( - answers, - 'personalTaxCredit.type', - ) - - return { - individualAid, - cohabitationAid, - homeCircumstances, - personalTaxCredit, - } - } - - return { individualAid, cohabitationAid } -} diff --git a/libs/application/templates/financial-aid/src/components/Summary/Files.css.ts b/libs/application/templates/financial-aid/src/components/Summary/Files.css.ts deleted file mode 100644 index aec52ce1eee5..000000000000 --- a/libs/application/templates/financial-aid/src/components/Summary/Files.css.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { style } from '@vanilla-extract/css' - -export const filesButtons = style({ - selectors: { - '&:hover': { - cursor: 'pointer', - }, - }, -}) diff --git a/libs/application/templates/financial-aid/src/components/Summary/SummaryBlock.css.ts b/libs/application/templates/financial-aid/src/components/Summary/SummaryBlock.css.ts deleted file mode 100644 index e119e774e5ae..000000000000 --- a/libs/application/templates/financial-aid/src/components/Summary/SummaryBlock.css.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { style } from '@vanilla-extract/css' -import { theme } from '@island.is/island-ui/theme' - -export const summaryBlockChild = style({ - minWidth: '50%', - '@media': { - [`screen and (min-width: ${theme.breakpoints.lg}px)`]: { - minWidth: '83%', - }, - }, -}) diff --git a/libs/application/templates/financial-aid/src/fields/AboutForm/AboutForm.tsx b/libs/application/templates/financial-aid/src/fields/AboutForm/AboutForm.tsx new file mode 100644 index 000000000000..976f27f7ee55 --- /dev/null +++ b/libs/application/templates/financial-aid/src/fields/AboutForm/AboutForm.tsx @@ -0,0 +1,37 @@ +import React from 'react' +import { useIntl } from 'react-intl' + +import { Text, Box } from '@island.is/island-ui/core' +import { useLocale } from '@island.is/localization' +import { currentMonth } from '@island.is/financial-aid/shared/lib' + +import { DescriptionText, PrivacyPolicyAccordion } from '..' +import { FAFieldBaseProps } from '../../lib/types' +import withLogo from '../Logo/Logo' +import { aboutForm } from '../../lib/messages' + +const AboutForm = ({ application }: FAFieldBaseProps) => { + const { formatMessage } = useIntl() + const { lang } = useLocale() + + return ( + <> + + {formatMessage(aboutForm.general.description, { + currentMonth: currentMonth(lang), + })} + + + + + + + + ) +} + +export default withLogo(AboutForm) diff --git a/libs/application/templates/financial-aid/src/fields/AboutSpouseForm/AboutSpouseForm.tsx b/libs/application/templates/financial-aid/src/fields/AboutSpouseForm/AboutSpouseForm.tsx new file mode 100644 index 000000000000..9cc82c71dd69 --- /dev/null +++ b/libs/application/templates/financial-aid/src/fields/AboutSpouseForm/AboutSpouseForm.tsx @@ -0,0 +1,38 @@ +import React from 'react' +import { + currentMonth, + getNextPeriod, +} from '@island.is/financial-aid/shared/lib' +import { Box } from '@island.is/island-ui/core' +import { useLocale } from '@island.is/localization' + +import { aboutSpouseForm } from '../../lib/messages' +import { DescriptionText, PrivacyPolicyAccordion } from '..' +import { FAFieldBaseProps } from '../../lib/types' +import withLogo from '../Logo/Logo' + +const AboutSpouseForm = ({ application }: FAFieldBaseProps) => { + const { lang } = useLocale() + const { nationalRegistry, municipality } = application.externalData + + return ( + <> + + + + + + ) +} + +export default withLogo(AboutSpouseForm) diff --git a/libs/application/templates/financial-aid/src/fields/BankInfoForm/BankInfoForm.tsx b/libs/application/templates/financial-aid/src/fields/BankInfoForm/BankInfoForm.tsx new file mode 100644 index 000000000000..463cece0ae97 --- /dev/null +++ b/libs/application/templates/financial-aid/src/fields/BankInfoForm/BankInfoForm.tsx @@ -0,0 +1,82 @@ +import React from 'react' +import { Box, Text, GridRow, GridColumn } from '@island.is/island-ui/core' +import { FAFieldBaseProps } from '../../lib/types' +import { useIntl } from 'react-intl' +import { bankInfoForm } from '../../lib/messages' +import { InputController } from '@island.is/shared/form-fields' +import withLogo from '../Logo/Logo' + +const BankInfoForm = ({ field, application }: FAFieldBaseProps) => { + const { id } = field + const { formatMessage } = useIntl() + const { answers } = application + + const bankNumberId = `${id}.bankNumber` + const ledgerId = `${id}.ledger` + const accountNumberId = `${id}.accountNumber` + + return ( + <> + + {formatMessage(bankInfoForm.general.info)} + + + + + + + + + + + + + + + + + + + + {formatMessage(bankInfoForm.general.descriptionTitle)} + + + + {formatMessage(bankInfoForm.general.description)} + + + ) +} + +export default withLogo(BankInfoForm) diff --git a/libs/application/templates/financial-aid/src/components/Breakdown/Breakdown.tsx b/libs/application/templates/financial-aid/src/fields/Breakdown/Breakdown.tsx similarity index 100% rename from libs/application/templates/financial-aid/src/components/Breakdown/Breakdown.tsx rename to libs/application/templates/financial-aid/src/fields/Breakdown/Breakdown.tsx diff --git a/libs/application/templates/financial-aid/src/fields/childrenFilesForm/ChildrenFilesForm.tsx b/libs/application/templates/financial-aid/src/fields/ChildrenFilesForm/ChildrenFilesForm.tsx similarity index 58% rename from libs/application/templates/financial-aid/src/fields/childrenFilesForm/ChildrenFilesForm.tsx rename to libs/application/templates/financial-aid/src/fields/ChildrenFilesForm/ChildrenFilesForm.tsx index 4e8c901d9749..9a640093a7bf 100644 --- a/libs/application/templates/financial-aid/src/fields/childrenFilesForm/ChildrenFilesForm.tsx +++ b/libs/application/templates/financial-aid/src/fields/ChildrenFilesForm/ChildrenFilesForm.tsx @@ -2,11 +2,11 @@ import React from 'react' import { useIntl } from 'react-intl' import { Text, UploadFile } from '@island.is/island-ui/core' import { childrenFilesForm } from '../../lib/messages' -import { UploadFileType } from '../..' -import Files from '../files/Files' -import { FieldBaseProps } from '@island.is/application/types' +import { FAFieldBaseProps, OverrideAnswerSchema, UploadFileType } from '../..' +import { Files } from '..' +import withLogo from '../Logo/Logo' -export const ChildrenFilesForm = ({ field, application }: FieldBaseProps) => { +const ChildrenFilesForm = ({ field, application }: FAFieldBaseProps) => { const { formatMessage } = useIntl() const { id, answers } = application @@ -17,9 +17,13 @@ export const ChildrenFilesForm = ({ field, application }: FieldBaseProps) => {
) } + +export default withLogo(ChildrenFilesForm) diff --git a/libs/application/templates/financial-aid/src/fields/ChildrenForm/ChildInput.tsx b/libs/application/templates/financial-aid/src/fields/ChildrenForm/ChildInput.tsx new file mode 100644 index 000000000000..587be9cdd6fb --- /dev/null +++ b/libs/application/templates/financial-aid/src/fields/ChildrenForm/ChildInput.tsx @@ -0,0 +1,82 @@ +import React, { FC } from 'react' + +import { Text, Box } from '@island.is/island-ui/core' + +import { childrenForm } from '../../lib/messages' +import format from 'date-fns/format' + +import { InputController } from '@island.is/shared/form-fields' + +import kennitala from 'kennitala' +import { useLocale } from '@island.is/localization' +import { RecordObject } from '@island.is/application/types' +import { getErrorViaPath } from '@island.is/application/core' +import { useFormContext } from 'react-hook-form' + +interface Props { + fieldIndex: string + errors: RecordObject | undefined + childFullName: string + childNationalId: string +} + +export const ChildInput: FC> = ({ + fieldIndex, + errors, + childFullName, + childNationalId, +}) => { + const { setValue, clearErrors } = useFormContext() + + const schoolField = `${fieldIndex}.school` + const nameField = `${fieldIndex}.fullName` + const nationalIdField = `${fieldIndex}.nationalId` + + setValue(nameField, childFullName) + setValue(nationalIdField, childNationalId) + + const nationalId = childNationalId + const kennitalaInfo = kennitala.info(nationalId) + const birthday = kennitalaInfo?.birthday + const age = kennitalaInfo?.age + const dateOfBirth = new Date(birthday) + + const { formatMessage } = useLocale() + + if (age >= 18) { + return null + } + return ( + + + {childFullName} + + + {formatMessage(childrenForm.page.birthday, { + birthday: format(dateOfBirth, 'dd.MM.yyyy'), + })} + + + + { + clearErrors(schoolField) + }} + /> + + + ) +} diff --git a/libs/application/templates/financial-aid/src/fields/ChildrenForm/ChildrenForm.tsx b/libs/application/templates/financial-aid/src/fields/ChildrenForm/ChildrenForm.tsx index 3eca247056e1..37fc08ac83a0 100644 --- a/libs/application/templates/financial-aid/src/fields/ChildrenForm/ChildrenForm.tsx +++ b/libs/application/templates/financial-aid/src/fields/ChildrenForm/ChildrenForm.tsx @@ -1,33 +1,38 @@ import React from 'react' -import { Text, Box } from '@island.is/island-ui/core' +import { useIntl } from 'react-intl' + +import { Text, Box, Input } from '@island.is/island-ui/core' + +import { DescriptionText } from '..' +import { + FAFieldBaseProps, + SummaryComment as SummaryCommentType, +} from '../../lib/types' +import withLogo from '../Logo/Logo' import { childrenForm } from '../../lib/messages' + +import { ChildInput } from './ChildInput' import { sortChildrenUnderAgeByAge } from '../../lib/utils' -import { useFormContext } from 'react-hook-form' -import { useLocale } from '@island.is/localization' -import format from 'date-fns/format' -import { InputController } from '@island.is/shared/form-fields' -import { getErrorViaPath } from '@island.is/application/core' -import kennitala from 'kennitala' -import { - ApplicantChildCustodyInformation, - FieldBaseProps, -} from '@island.is/application/types' +import { Controller, useFormContext } from 'react-hook-form' -export const ChildrenForm = ({ - application, - field, - errors, -}: FieldBaseProps) => { - const { setValue, clearErrors } = useFormContext() - const { formatMessage } = useLocale() +const ChildrenForm = ({ application, field, errors }: FAFieldBaseProps) => { + const { formatMessage } = useIntl() + const { setValue } = useFormContext() - const { externalData } = application - const childrenExternalData = externalData.childrenCustodyInformation - .data as ApplicantChildCustodyInformation[] + const { externalData, answers } = application + const childrenExternalData = externalData.childrenCustodyInformation.data const childrenInfo = sortChildrenUnderAgeByAge(childrenExternalData) + const summaryCommentType = SummaryCommentType.CHILDRENCOMMENT return ( <> + + {formatMessage(childrenForm.general.description)} + + + + + {childrenInfo?.map((child, index) => { const fieldIndex = `${field.id}[${index}]` @@ -37,46 +42,56 @@ export const ChildrenForm = ({ child.livesWithBothParents, ) - const schoolField = `${fieldIndex}.school` - const nameField = `${fieldIndex}.fullName` - const nationalIdField = `${fieldIndex}.nationalId` - - setValue(nameField, child.fullName) - setValue(nationalIdField, child.nationalId) - - const kennitalaInfo = kennitala.info(child.nationalId) - const birthday = new Date(kennitalaInfo?.birthday) - return ( - - - {child.fullName} - - - {formatMessage(childrenForm.page.birthday, { - birthday: format(birthday, 'dd.MM.yyyy'), - })} - + + ) + })} + + + + + {formatMessage(childrenForm.page.commentTitle)} + + + {formatMessage(childrenForm.page.commentText)} + + - - { - clearErrors(schoolField) + { + return ( + { + onChange(e.target.value) + setValue(summaryCommentType, e.target.value) }} /> - - - ) - })} + ) + }} + /> + ) } + +export default withLogo(ChildrenForm) diff --git a/libs/application/templates/financial-aid/src/fields/Confirmation/ApplicantConfirmation.tsx b/libs/application/templates/financial-aid/src/fields/Confirmation/ApplicantConfirmation.tsx new file mode 100644 index 000000000000..79fd6945a41c --- /dev/null +++ b/libs/application/templates/financial-aid/src/fields/Confirmation/ApplicantConfirmation.tsx @@ -0,0 +1,39 @@ +import React from 'react' + +import { ApproveOptions, FAFieldBaseProps } from '../../lib/types' +import { confirmation } from '../../lib/messages' +import { hasFiles, hasSpouse } from '../../lib/utils' +import Confirmation from './Confirmation' + +const ApplicantConfirmation = ({ application }: FAFieldBaseProps) => { + const { answers, externalData } = application + + const applicantHasSpouse = hasSpouse(answers, externalData) + const missingIncomeFiles = + answers.income === ApproveOptions.Yes && !hasFiles('incomeFiles', answers) + + const firstStepText = () => { + switch (true) { + case applicantHasSpouse && missingIncomeFiles: + return confirmation.nextSteps.contentBothMissingFiles + case applicantHasSpouse: + return confirmation.nextSteps.contentSpouseMissingFiles + case missingIncomeFiles: + return confirmation.nextSteps.contentMissingFiles + } + } + + return ( + + ) +} + +export default ApplicantConfirmation diff --git a/libs/application/templates/financial-aid/src/fields/Confirmation/Confirmation.tsx b/libs/application/templates/financial-aid/src/fields/Confirmation/Confirmation.tsx new file mode 100644 index 000000000000..956ef692f842 --- /dev/null +++ b/libs/application/templates/financial-aid/src/fields/Confirmation/Confirmation.tsx @@ -0,0 +1,116 @@ +import React from 'react' +import { MessageDescriptor, useIntl } from 'react-intl' + +import { getNextPeriod } from '@island.is/financial-aid/shared/lib' +import { AlertMessage, Box, Text } from '@island.is/island-ui/core' +import { useLocale } from '@island.is/localization' + +import { confirmation, copyUrl } from '../../lib/messages' +import { DescriptionText, ConfirmationSectionImage, CopyUrl } from '..' + +interface Props { + firstStepText?: MessageDescriptor + missingIncomeFiles: boolean + hasSpouse?: boolean + spouseEmailSuccess?: boolean + municipalityHomepage?: string +} + +const Confirmation = ({ + firstStepText, + missingIncomeFiles, + hasSpouse, + spouseEmailSuccess, + municipalityHomepage, +}: Props) => { + const { formatMessage } = useIntl() + const { lang } = useLocale() + + return ( + <> + + {missingIncomeFiles ? ( + + ) : ( + + )} + {hasSpouse && ( + + + + )} + + + + {formatMessage(confirmation.nextSteps.title)} + + + {firstStepText && } + + + + + + {hasSpouse && ( + <> + + {formatMessage(confirmation.sharedLink.title)} + + + + + + )} + + + {formatMessage(confirmation.links.title)} + + + + + + + + + ) +} + +export default Confirmation diff --git a/libs/application/templates/financial-aid/src/fields/Confirmation/SpouseConfirmation.tsx b/libs/application/templates/financial-aid/src/fields/Confirmation/SpouseConfirmation.tsx new file mode 100644 index 000000000000..2fec48e4e8bf --- /dev/null +++ b/libs/application/templates/financial-aid/src/fields/Confirmation/SpouseConfirmation.tsx @@ -0,0 +1,28 @@ +import React from 'react' + +import { ApproveOptions, FAFieldBaseProps } from '../../lib/types' +import { confirmation } from '../../lib/messages' +import { hasFiles } from '../../lib/utils' +import Confirmation from './Confirmation' + +const SpouseConfirmation = ({ application }: FAFieldBaseProps) => { + const { answers, externalData } = application + + const missingIncomeFiles = + answers.spouseIncome === ApproveOptions.Yes && + !hasFiles('spouseIncomeFiles', answers) + + return ( + + ) +} + +export default SpouseConfirmation diff --git a/libs/application/templates/financial-aid/src/fields/ConfirmationSectionImage/ConfirmationSectionImage.tsx b/libs/application/templates/financial-aid/src/fields/ConfirmationSectionImage/ConfirmationSectionImage.tsx new file mode 100644 index 000000000000..822bf4294a83 --- /dev/null +++ b/libs/application/templates/financial-aid/src/fields/ConfirmationSectionImage/ConfirmationSectionImage.tsx @@ -0,0 +1,18 @@ +import React from 'react' +import { Box } from '@island.is/island-ui/core' + +import { confirmationIllustration } from '../Shared.css' + +const ConfirmationSectionImage = () => { + return ( + + + + ) +} +export default ConfirmationSectionImage diff --git a/libs/application/templates/financial-aid/src/fields/ContactInfo/ContactInfo.tsx b/libs/application/templates/financial-aid/src/fields/ContactInfo/ContactInfo.tsx new file mode 100644 index 000000000000..c3ed1d0da863 --- /dev/null +++ b/libs/application/templates/financial-aid/src/fields/ContactInfo/ContactInfo.tsx @@ -0,0 +1,68 @@ +import React from 'react' +import { Text, Box } from '@island.is/island-ui/core' +import { FAFieldBaseProps } from '../../lib/types' +import { useIntl } from 'react-intl' +import { contactInfo } from '../../lib/messages' +import { InputController } from '@island.is/shared/form-fields' +import { useFormContext } from 'react-hook-form' +import { getValueViaPath } from '@island.is/application/core' +import { answersSchema } from '../../lib/dataSchema' +import { Routes } from '../../lib/constants' +import withLogo from '../Logo/Logo' + +const ContactInfo = ({ field, errors, application }: FAFieldBaseProps) => { + const { formatMessage } = useIntl() + const { answers } = application + const { id } = field + const { clearErrors } = useFormContext() + + const emailPath = `${id}.email` + const phonePath = `${id}.phone` + + return ( + <> + + {formatMessage(contactInfo.general.description)} + + + { + clearErrors(emailPath) + }} + /> + + + { + clearErrors(phonePath) + }} + /> + + + ) +} + +export default withLogo(ContactInfo) diff --git a/libs/application/templates/financial-aid/src/fields/CopyUrl/CopyUrl.tsx b/libs/application/templates/financial-aid/src/fields/CopyUrl/CopyUrl.tsx index 4f3aef864e4a..38b57388ec2a 100644 --- a/libs/application/templates/financial-aid/src/fields/CopyUrl/CopyUrl.tsx +++ b/libs/application/templates/financial-aid/src/fields/CopyUrl/CopyUrl.tsx @@ -8,7 +8,7 @@ interface Props { successMessage: string } -export const CopyUrl = ({ inputLabel, buttonLabel, successMessage }: Props) => { +const CopyUrl = ({ inputLabel, buttonLabel, successMessage }: Props) => { const [currentUrl, setCurrentUrl] = useState(undefined) const copyToClipboard = (val: string) => { @@ -70,3 +70,5 @@ export const CopyUrl = ({ inputLabel, buttonLabel, successMessage }: Props) => { ) } + +export default CopyUrl diff --git a/libs/application/templates/financial-aid/src/components/DescriptionText/DescriptionText.css.ts b/libs/application/templates/financial-aid/src/fields/DescriptionText/DescriptionText.css.ts similarity index 100% rename from libs/application/templates/financial-aid/src/components/DescriptionText/DescriptionText.css.ts rename to libs/application/templates/financial-aid/src/fields/DescriptionText/DescriptionText.css.ts diff --git a/libs/application/templates/financial-aid/src/components/DescriptionText/DescriptionText.tsx b/libs/application/templates/financial-aid/src/fields/DescriptionText/DescriptionText.tsx similarity index 100% rename from libs/application/templates/financial-aid/src/components/DescriptionText/DescriptionText.tsx rename to libs/application/templates/financial-aid/src/fields/DescriptionText/DescriptionText.tsx diff --git a/libs/application/templates/financial-aid/src/components/DirectTaxPaymentsModal/DirectTaxPaymentModal.css.ts b/libs/application/templates/financial-aid/src/fields/DirectTaxPaymentsModal/DirectTaxPaymentModal.css.ts similarity index 100% rename from libs/application/templates/financial-aid/src/components/DirectTaxPaymentsModal/DirectTaxPaymentModal.css.ts rename to libs/application/templates/financial-aid/src/fields/DirectTaxPaymentsModal/DirectTaxPaymentModal.css.ts diff --git a/libs/application/templates/financial-aid/src/components/DirectTaxPaymentsModal/DirectTaxPaymentModal.tsx b/libs/application/templates/financial-aid/src/fields/DirectTaxPaymentsModal/DirectTaxPaymentModal.tsx similarity index 96% rename from libs/application/templates/financial-aid/src/components/DirectTaxPaymentsModal/DirectTaxPaymentModal.tsx rename to libs/application/templates/financial-aid/src/fields/DirectTaxPaymentsModal/DirectTaxPaymentModal.tsx index 76d9d3b26dc2..b2fb8481b914 100644 --- a/libs/application/templates/financial-aid/src/components/DirectTaxPaymentsModal/DirectTaxPaymentModal.tsx +++ b/libs/application/templates/financial-aid/src/fields/DirectTaxPaymentsModal/DirectTaxPaymentModal.tsx @@ -1,10 +1,12 @@ import React from 'react' import { useIntl } from 'react-intl' import { ModalBase, Text, Box, Button } from '@island.is/island-ui/core' + import { DirectTaxPayment } from '@island.is/financial-aid/shared/lib' +import { TaxBreakdown } from '..' import { directTaxPaymentModal } from '../../lib/messages' + import * as styles from './DirectTaxPaymentModal.css' -import TaxBreakdown from '../TaxBreakdown/TaxBreakdown' interface Props { isVisible: boolean diff --git a/libs/application/templates/financial-aid/src/fields/EmploymentForm/EmploymentForm.tsx b/libs/application/templates/financial-aid/src/fields/EmploymentForm/EmploymentForm.tsx new file mode 100644 index 000000000000..1cdf2e305191 --- /dev/null +++ b/libs/application/templates/financial-aid/src/fields/EmploymentForm/EmploymentForm.tsx @@ -0,0 +1,97 @@ +import React from 'react' +import { Box, Input } from '@island.is/island-ui/core' +import { employmentForm, input } from '../../lib/messages' +import { useIntl } from 'react-intl' + +import { RadioController } from '@island.is/shared/form-fields' +import { Controller, useFormContext } from 'react-hook-form' +import { Employment } from '@island.is/financial-aid/shared/lib' +import * as styles from '../Shared.css' +import cn from 'classnames' +import { FAFieldBaseProps, InputTypes } from '../../lib/types' +import withLogo from '../Logo/Logo' + +const EmploymentForm = ({ application, errors }: FAFieldBaseProps) => { + const typeInput = { + id: 'employment.type', + error: errors?.employment?.type, + } as InputTypes + + const customInput = { + id: 'employment.custom', + error: errors?.employment?.custom, + } as InputTypes + + const { formatMessage } = useIntl() + + const { answers } = application + + const { setValue, getValues, clearErrors } = useFormContext() + + return ( + <> + + + + + + { + return ( + { + clearErrors(customInput.id) + onChange(e.target.value) + setValue(customInput.id, e.target.value) + }} + /> + ) + }} + /> + + + ) +} + +export default withLogo(EmploymentForm) diff --git a/libs/application/templates/financial-aid/src/fields/fileUploadController/FileUploadControler.tsx b/libs/application/templates/financial-aid/src/fields/FileUploadContainer/FileUploadContainer.tsx similarity index 94% rename from libs/application/templates/financial-aid/src/fields/fileUploadController/FileUploadControler.tsx rename to libs/application/templates/financial-aid/src/fields/FileUploadContainer/FileUploadContainer.tsx index 297bbefe6d7a..8206fddde401 100644 --- a/libs/application/templates/financial-aid/src/fields/fileUploadController/FileUploadControler.tsx +++ b/libs/application/templates/financial-aid/src/fields/FileUploadContainer/FileUploadContainer.tsx @@ -3,7 +3,7 @@ import { useIntl } from 'react-intl' import { Text, Box, GridRow, GridColumn } from '@island.is/island-ui/core' import { filesText } from '../../lib/messages' import cn from 'classnames' -import * as styles from './FileUploadController.css' +import * as styles from './../Shared.css' interface Props { children: ReactNode hasError?: boolean diff --git a/libs/application/templates/financial-aid/src/fields/files/Files.tsx b/libs/application/templates/financial-aid/src/fields/Files/Files.tsx similarity index 95% rename from libs/application/templates/financial-aid/src/fields/files/Files.tsx rename to libs/application/templates/financial-aid/src/fields/Files/Files.tsx index e1077c1412d5..4fb979e8c9d6 100644 --- a/libs/application/templates/financial-aid/src/fields/files/Files.tsx +++ b/libs/application/templates/financial-aid/src/fields/Files/Files.tsx @@ -3,11 +3,11 @@ import { InputFileUpload, UploadFile } from '@island.is/island-ui/core' import { useIntl } from 'react-intl' import { filesText } from '../../lib/messages' +import { FileUploadContainer } from '..' import { UploadFileType } from '../../lib/types' import { useFormContext } from 'react-hook-form' import { useFileUpload } from '../../lib/hooks/useFileUpload' import { FILE_SIZE_LIMIT, UPLOAD_ACCEPT } from '../../lib/constants' -import FileUploadContainer from '../fileUploadController/FileUploadControler' interface Props { uploadFiles: UploadFile[] diff --git a/libs/application/templates/financial-aid/src/fields/HomeCircumstancesForm/HomeCircumstancesForm.tsx b/libs/application/templates/financial-aid/src/fields/HomeCircumstancesForm/HomeCircumstancesForm.tsx new file mode 100644 index 000000000000..c3bb45953858 --- /dev/null +++ b/libs/application/templates/financial-aid/src/fields/HomeCircumstancesForm/HomeCircumstancesForm.tsx @@ -0,0 +1,116 @@ +import React from 'react' +import { Box, Input } from '@island.is/island-ui/core' +import { homeCircumstancesForm, input } from '../../lib/messages' +import { useIntl } from 'react-intl' + +import { RadioController } from '@island.is/shared/form-fields' +import { Controller, useFormContext } from 'react-hook-form' +import { HomeCircumstances } from '@island.is/financial-aid/shared/lib' +import * as styles from '../Shared.css' +import cn from 'classnames' +import { FAFieldBaseProps, InputTypes } from '../../lib/types' +import withLogo from '../Logo/Logo' + +const HomeCircumstancesForm = ({ application, errors }: FAFieldBaseProps) => { + const typeInput = { + id: 'homeCircumstances.type', + error: errors?.homeCircumstances?.type, + } as InputTypes + + const customInput = { + id: 'homeCircumstances.custom', + error: errors?.homeCircumstances?.custom, + } as InputTypes + + const { formatMessage } = useIntl() + + const { answers } = application + + const { setValue, getValues, clearErrors } = useFormContext() + + return ( + <> + + + + + + { + return ( + { + clearErrors(customInput.id) + onChange(e.target.value) + setValue(customInput.id, e.target.value) + }} + /> + ) + }} + /> + + + ) +} + +export default withLogo(HomeCircumstancesForm) diff --git a/libs/application/templates/financial-aid/src/fields/InRelationshipForm/InRelationshipForm.tsx b/libs/application/templates/financial-aid/src/fields/InRelationshipForm/InRelationshipForm.tsx new file mode 100644 index 000000000000..acab22dbf322 --- /dev/null +++ b/libs/application/templates/financial-aid/src/fields/InRelationshipForm/InRelationshipForm.tsx @@ -0,0 +1,67 @@ +import React from 'react' +import { Box, Text } from '@island.is/island-ui/core' +import { FAFieldBaseProps } from '../../lib/types' +import { + CheckboxController, + InputController, +} from '@island.is/shared/form-fields' +import { useIntl } from 'react-intl' +import { inRelationship } from '../../lib/messages' +import DescriptionText from '../DescriptionText/DescriptionText' +import { useFormContext } from 'react-hook-form' +import withLogo from '../Logo/Logo' + +const spouseEmail = 'spouse.email' +const spouseApproveTerms = 'spouse.approveTerms' + +const InRelationshipForm = ({ errors, application }: FAFieldBaseProps) => { + const { formatMessage } = useIntl() + const { answers } = application + const { clearErrors } = useFormContext() + const spouseEmailError = errors?.spouse?.email + const spouseApproveTermsError = errors?.spouse?.approveTerms + + return ( +
+ + {formatMessage(inRelationship.general.intro)} + + + + + + + { + clearErrors(spouseEmail) + }} + /> + + + +
+ ) +} + +export default withLogo(InRelationshipForm) diff --git a/libs/application/templates/financial-aid/src/fields/incomeFilesForm/IncomeFilesForm.tsx b/libs/application/templates/financial-aid/src/fields/IncomeFilesForm/IncomeFilesForm.tsx similarity index 55% rename from libs/application/templates/financial-aid/src/fields/incomeFilesForm/IncomeFilesForm.tsx rename to libs/application/templates/financial-aid/src/fields/IncomeFilesForm/IncomeFilesForm.tsx index c69fabaa0f5b..baf3a42ed199 100644 --- a/libs/application/templates/financial-aid/src/fields/incomeFilesForm/IncomeFilesForm.tsx +++ b/libs/application/templates/financial-aid/src/fields/IncomeFilesForm/IncomeFilesForm.tsx @@ -2,32 +2,32 @@ import React from 'react' import { useIntl } from 'react-intl' import { Text, UploadFile } from '@island.is/island-ui/core' import { incomeFilesForm } from '../../lib/messages' -import { UploadFileType } from '../..' -import { FieldBaseProps } from '@island.is/application/types' -import { getValueViaPath } from '@island.is/application/core' -import Files from '../files/Files' +import { FAFieldBaseProps, OverrideAnswerSchema, UploadFileType } from '../..' +import { Files } from '..' +import withLogo from '../Logo/Logo' -export const IncomeFilesForm = ({ field, application }: FieldBaseProps) => { +const IncomeFilesForm = ({ field, application }: FAFieldBaseProps) => { const { formatMessage } = useIntl() const { id, answers, externalData } = application - const success = getValueViaPath( - externalData, - 'taxData.data.municipalitiesDirectTaxPayments.success', - ) + return ( <> {formatMessage( - success + externalData?.taxData?.data?.municipalitiesDirectTaxPayments?.success ? incomeFilesForm.general.descriptionTaxSuccess : incomeFilesForm.general.description, )} ) } + +export default withLogo(IncomeFilesForm) diff --git a/libs/application/templates/financial-aid/src/fields/IncomeForm/IncomeForm.tsx b/libs/application/templates/financial-aid/src/fields/IncomeForm/IncomeForm.tsx new file mode 100644 index 000000000000..d26d57b8c8fb --- /dev/null +++ b/libs/application/templates/financial-aid/src/fields/IncomeForm/IncomeForm.tsx @@ -0,0 +1,61 @@ +import React from 'react' +import { Box, Text, GridRow, GridColumn } from '@island.is/island-ui/core' +import { + FAFieldBaseProps, + ApproveOptions, + OverrideAnswerSchema, + ErrorSchema, +} from '../../lib/types' +import { useIntl } from 'react-intl' +import { incomeForm } from '../../lib/messages' +import { RadioController } from '@island.is/shared/form-fields' +import DescriptionText from '../DescriptionText/DescriptionText' +import withLogo from '../Logo/Logo' + +const IncomeForm = ({ field, errors, application }: FAFieldBaseProps) => { + const { formatMessage } = useIntl() + const { answers } = application + + return ( + <> + + + + + {formatMessage(incomeForm.bulletList.headline)} + + + + + + + + + + + + + ) +} + +export default withLogo(IncomeForm) diff --git a/libs/application/templates/financial-aid/src/fields/Logo/Logo.css.ts b/libs/application/templates/financial-aid/src/fields/Logo/Logo.css.ts new file mode 100644 index 000000000000..51dfd173160b --- /dev/null +++ b/libs/application/templates/financial-aid/src/fields/Logo/Logo.css.ts @@ -0,0 +1,14 @@ +import { style } from '@vanilla-extract/css' +import { theme } from '@island.is/island-ui/theme' + +export const logo = style({ + position: 'relative', + left: '16px', + width: '180px', + '@media': { + [`screen and (min-width: ${theme.breakpoints.md}px)`]: { + width: '250px', + left: '46px', + }, + }, +}) diff --git a/libs/application/templates/financial-aid/src/fields/Logo/Logo.tsx b/libs/application/templates/financial-aid/src/fields/Logo/Logo.tsx new file mode 100644 index 000000000000..40b3eff8ca12 --- /dev/null +++ b/libs/application/templates/financial-aid/src/fields/Logo/Logo.tsx @@ -0,0 +1,46 @@ +import React, { useEffect, useState } from 'react' + +import { logoKeyFromMunicipalityCode } from '@island.is/financial-aid/shared/lib' +import { Box } from '@island.is/island-ui/core' + +import { FAFieldBaseProps } from '../../lib/types' +import * as styles from './Logo.css' + +const withLogo = + (Component: React.ComponentType>) => + (props: FAFieldBaseProps) => { + const [logo, setLogo] = useState() + const municipality = props.application.externalData.municipality.data + + useEffect(() => { + const getLogo = async () => { + const svgLogo = await import( + `../../svg/${ + logoKeyFromMunicipalityCode[ + municipality ? municipality.municipalityId : '' + ] + }` + ) + setLogo(svgLogo.default) + } + getLogo() + }, []) + + return ( + <> + + + + + + + + ) + } + +export default withLogo diff --git a/libs/application/templates/financial-aid/src/fields/Summary/FileList.css.ts b/libs/application/templates/financial-aid/src/fields/MissingFiles/FileList/FileList.css.ts similarity index 97% rename from libs/application/templates/financial-aid/src/fields/Summary/FileList.css.ts rename to libs/application/templates/financial-aid/src/fields/MissingFiles/FileList/FileList.css.ts index 56c30e8791d5..2af298541a87 100644 --- a/libs/application/templates/financial-aid/src/fields/Summary/FileList.css.ts +++ b/libs/application/templates/financial-aid/src/fields/MissingFiles/FileList/FileList.css.ts @@ -40,5 +40,5 @@ export const type = style({ export const name = style({ gridColumn: 'span 2', - display: 'flex', + overflow: 'hidden', }) diff --git a/libs/application/templates/financial-aid/src/fields/Summary/FileList.tsx b/libs/application/templates/financial-aid/src/fields/MissingFiles/FileList/FileList.tsx similarity index 93% rename from libs/application/templates/financial-aid/src/fields/Summary/FileList.tsx rename to libs/application/templates/financial-aid/src/fields/MissingFiles/FileList/FileList.tsx index 5a637f8a87d6..1d1542ad16cc 100644 --- a/libs/application/templates/financial-aid/src/fields/Summary/FileList.tsx +++ b/libs/application/templates/financial-aid/src/fields/MissingFiles/FileList/FileList.tsx @@ -1,14 +1,16 @@ import React from 'react' import { useIntl } from 'react-intl' import cn from 'classnames' + import { Text, Box, UploadFile } from '@island.is/island-ui/core' import { getFileSizeInKilo, getFileType, } from '@island.is/financial-aid/shared/lib' -import { useFileUpload } from '../../lib/hooks/useFileUpload' -import { missingFiles } from '../../lib/messages' + import * as styles from './FileList.css' +import { missingFiles } from '../../../lib/messages' +import { useFileUpload } from '../../../lib/hooks/useFileUpload' interface Props { applicationSystemId: string @@ -57,4 +59,5 @@ const FileList = ({ files, applicationSystemId }: Props) => { ) } + export default FileList diff --git a/libs/application/templates/financial-aid/src/fields/missingFiles/MissingFiles.tsx b/libs/application/templates/financial-aid/src/fields/MissingFiles/MissingFiles.tsx similarity index 85% rename from libs/application/templates/financial-aid/src/fields/missingFiles/MissingFiles.tsx rename to libs/application/templates/financial-aid/src/fields/MissingFiles/MissingFiles.tsx index 4b0edab8d80d..ec79d36afd5f 100644 --- a/libs/application/templates/financial-aid/src/fields/missingFiles/MissingFiles.tsx +++ b/libs/application/templates/financial-aid/src/fields/MissingFiles/MissingFiles.tsx @@ -1,5 +1,6 @@ import React, { useEffect, useMemo, useState } from 'react' import { useIntl } from 'react-intl' + import { Text, Box, @@ -14,34 +15,29 @@ import { getCommentFromLatestEvent, } from '@island.is/financial-aid/shared/lib' import { getValueViaPath } from '@island.is/application/core' -import { FieldBaseProps, RecordObject } from '@island.is/application/types' +import { RecordObject } from '@island.is/application/types' + import { filesText, missingFiles } from '../../lib/messages' -import { UploadFileType } from '../../lib/types' +import { Files } from '..' +import { FAFieldBaseProps, UploadFileType } from '../../lib/types' import useApplication from '../../lib/hooks/useApplication' import { Controller, useFormContext } from 'react-hook-form' import { useFileUpload } from '../../lib/hooks/useFileUpload' -import Files from '../files/Files' -import DescriptionText from '../../components/DescriptionText/DescriptionText' +import DescriptionText from '../DescriptionText/DescriptionText' -export const MissingFiles = ({ +const MissingFiles = ({ application, setBeforeSubmitCallback, field, -}: FieldBaseProps) => { - const currentApplicationId = getValueViaPath( - application.externalData, - 'currentApplication.data.currentApplicationId', - ) - const email = getValueViaPath( - application.externalData, - 'municipality.data.email', +}: FAFieldBaseProps) => { + const { currentApplication, updateApplication, loading } = useApplication( + application.externalData.currentApplication.data?.currentApplicationId, ) - const { currentApplication, updateApplication, loading } = - useApplication(currentApplicationId) const isSpouse = getValueViaPath(field as RecordObject, 'props.isSpouse') const { formatMessage } = useIntl() const { setValue, getValues } = useFormContext() + const fileType: UploadFileType = 'otherFiles' const commentType = 'fileUploadComment' const files = getValues(fileType) @@ -75,12 +71,15 @@ export const MissingFiles = ({ } try { - if (!currentApplicationId) { + if ( + !application.externalData.currentApplication.data + ?.currentApplicationId + ) { throw new Error() } const uploadedFiles = await uploadFiles( - currentApplicationId, + application.externalData.currentApplication.data.currentApplicationId, FileType.OTHER, files, ) @@ -171,7 +170,7 @@ export const MissingFiles = ({ @@ -179,3 +178,5 @@ export const MissingFiles = ({ ) } + +export default MissingFiles diff --git a/libs/application/templates/financial-aid/src/fields/Summary/MissingFilesConfirmation.tsx b/libs/application/templates/financial-aid/src/fields/MissingFiles/MissingFilesConfirmation/MissingFilesConfirmation.tsx similarity index 50% rename from libs/application/templates/financial-aid/src/fields/Summary/MissingFilesConfirmation.tsx rename to libs/application/templates/financial-aid/src/fields/MissingFiles/MissingFilesConfirmation/MissingFilesConfirmation.tsx index ac86730f9817..254994aa77bf 100644 --- a/libs/application/templates/financial-aid/src/fields/Summary/MissingFilesConfirmation.tsx +++ b/libs/application/templates/financial-aid/src/fields/MissingFiles/MissingFilesConfirmation/MissingFilesConfirmation.tsx @@ -1,32 +1,26 @@ import React from 'react' import { useIntl } from 'react-intl' import { useFormContext } from 'react-hook-form' -import { Text, Box } from '@island.is/island-ui/core' -import { UploadFileType } from '../../lib/types' -import { missingFiles } from '../../lib/messages' -import FileList from './FileList' -import { RecordObject } from '@island.is/shared/types' -import { getValueViaPath } from '@island.is/application/core' -import { FieldBaseProps } from '@island.is/application/types' - -export const MissingFilesConfirmation = ({ - application, - field, -}: FieldBaseProps) => { + +import { Text, Box, Button } from '@island.is/island-ui/core' + +import { FAFieldBaseProps, UploadFileType } from '../../../lib/types' +import { missingFiles } from '../../../lib/messages' +import FileList from '../FileList/FileList' + +const MissingFilesConfirmation = ({ application }: FAFieldBaseProps) => { const { formatMessage } = useIntl() const { getValues } = useFormContext() - const isSpouse = getValueViaPath( - field as RecordObject, - 'props.isSpouse', - ) - const fileType: UploadFileType = isSpouse - ? 'missingFilesSpouse' - : 'missingFiles' + const fileType: UploadFileType = 'otherFiles' const commentType = 'fileUploadComment' return ( <> + + {formatMessage(missingFiles.confirmation.subtitle)} + + {getValues(commentType)} )} + + + + ) } + +export default MissingFilesConfirmation diff --git a/libs/application/templates/financial-aid/src/fields/PersonalTaxCreditForm/PersonalTaxCreditForm.tsx b/libs/application/templates/financial-aid/src/fields/PersonalTaxCreditForm/PersonalTaxCreditForm.tsx new file mode 100644 index 000000000000..9db94fdb817f --- /dev/null +++ b/libs/application/templates/financial-aid/src/fields/PersonalTaxCreditForm/PersonalTaxCreditForm.tsx @@ -0,0 +1,56 @@ +import React from 'react' +import { Box, Text } from '@island.is/island-ui/core' +import { FAFieldBaseProps, ApproveOptions } from '../../lib/types' +import { useIntl } from 'react-intl' +import { personalTaxCreditForm } from '../../lib/messages' +import { RadioController } from '@island.is/shared/form-fields' +import withLogo from '../Logo/Logo' + +const PersonalTaxCreditForm = ({ + field, + errors, + application, +}: FAFieldBaseProps) => { + const { formatMessage } = useIntl() + const { answers } = application + + return ( + <> + + {formatMessage(personalTaxCreditForm.general.recommendedChoice)} + + + + + + {formatMessage(personalTaxCreditForm.general.descriptionTitle)} + + + {formatMessage(personalTaxCreditForm.general.description)} + + + ) +} + +export default withLogo(PersonalTaxCreditForm) diff --git a/libs/application/templates/financial-aid/src/fields/PrivacyPolicyAccordion/PrivacyPolicyAccordion.tsx b/libs/application/templates/financial-aid/src/fields/PrivacyPolicyAccordion/PrivacyPolicyAccordion.tsx new file mode 100644 index 000000000000..99248972ced8 --- /dev/null +++ b/libs/application/templates/financial-aid/src/fields/PrivacyPolicyAccordion/PrivacyPolicyAccordion.tsx @@ -0,0 +1,41 @@ +import React from 'react' +import { useIntl } from 'react-intl' +import { Accordion, AccordionItem, Text } from '@island.is/island-ui/core' +import { privacyPolicyAccordion } from '../../lib/messages' +import { DescriptionText } from '..' + +interface Props { + municipalityPageUrl?: string +} + +const PrivacyPolicyAccordion = ({ municipalityPageUrl }: Props) => { + const { formatMessage } = useIntl() + + return ( + <> + + {formatMessage(privacyPolicyAccordion.general.sectionTitle)} + + + + + {municipalityPageUrl ? ( + + ) : ( + + )} + + + + ) +} + +export default PrivacyPolicyAccordion diff --git a/libs/application/templates/financial-aid/src/fields/ServiceCenter/ServiceCenter.tsx b/libs/application/templates/financial-aid/src/fields/ServiceCenter/ServiceCenter.tsx new file mode 100644 index 000000000000..cee7dc56fe8c --- /dev/null +++ b/libs/application/templates/financial-aid/src/fields/ServiceCenter/ServiceCenter.tsx @@ -0,0 +1,55 @@ +import React from 'react' +import { useIntl } from 'react-intl' +import { Button, Box } from '@island.is/island-ui/core' + +import { serviceCenters } from '@island.is/financial-aid/shared/data' +import { FAFieldBaseProps } from '../../lib/types' +import { serviceCenter } from '../../lib/messages' +import { DescriptionText } from '..' +import withLogo from '../Logo/Logo' + +const ServiceCenter = ({ application }: FAFieldBaseProps) => { + const { formatMessage } = useIntl() + + const { externalData } = application + + const applicantsCenter = serviceCenters.find( + (serviceCenter) => + serviceCenter.number === + Number(externalData.nationalRegistry.data.address?.municipalityCode), + ) + + return ( + <> + + + + + + + + + {applicantsCenter?.link && ( + + )} + + ) +} + +export default withLogo(ServiceCenter) diff --git a/libs/application/templates/financial-aid/src/fields/Shared.css.ts b/libs/application/templates/financial-aid/src/fields/Shared.css.ts new file mode 100644 index 000000000000..fc9fd02698df --- /dev/null +++ b/libs/application/templates/financial-aid/src/fields/Shared.css.ts @@ -0,0 +1,58 @@ +import { style } from '@vanilla-extract/css' +import { theme } from '@island.is/island-ui/theme' + +export const inputContainer = style({ + maxHeight: '0', + overflow: 'hidden', + transition: 'max-height 300ms ease', +}) + +export const inputAppear = style({ + maxHeight: '300px', +}) + +export const formAppear = style({ + maxHeight: '400px', +}) + +export const filesButtons = style({ + selectors: { + '&:hover': { + cursor: 'pointer', + }, + }, +}) + +export const summaryBlockChild = style({ + minWidth: '50%', + '@media': { + [`screen and (min-width: ${theme.breakpoints.lg}px)`]: { + minWidth: '83%', + }, + }, +}) + +export const confirmationIllustration = style({ + marginTop: theme.spacing[5], + display: 'none', + '@media': { + [`screen and (min-width: ${theme.breakpoints.sm}px)`]: { + display: 'block', + }, + [`screen and (min-width: ${theme.breakpoints.md}px)`]: { + display: 'none', + }, + [`screen and (min-width: ${theme.breakpoints.lg}px)`]: { + display: 'block', + }, + }, +}) + +export const errorMessage = style({ + overflow: 'hidden', + maxHeight: '0', + transition: 'max-height 250ms ease', +}) +export const showErrorMessage = style({ + maxHeight: theme.spacing[5], +}) diff --git a/libs/application/templates/financial-aid/src/components/Status/AidAmount/AidAmount.tsx b/libs/application/templates/financial-aid/src/fields/Status/AidAmount/AidAmount.tsx similarity index 72% rename from libs/application/templates/financial-aid/src/components/Status/AidAmount/AidAmount.tsx rename to libs/application/templates/financial-aid/src/fields/Status/AidAmount/AidAmount.tsx index 59c01e01509f..0d66654d2f0a 100644 --- a/libs/application/templates/financial-aid/src/components/Status/AidAmount/AidAmount.tsx +++ b/libs/application/templates/financial-aid/src/fields/Status/AidAmount/AidAmount.tsx @@ -1,23 +1,23 @@ import React from 'react' import { useIntl } from 'react-intl' + import { Box, Text } from '@island.is/island-ui/core' -import { aidAmount } from '../../../lib/messages' -import { waitingForSpouse } from '../../..' -import Breakdown from '../../../components/Breakdown/Breakdown' -import { Estimation } from '../Estimation/Estimation' -import { Application, DataProviderResult } from '@island.is/application/types' -import { VeitaEstimation } from '../Estimation/VeitaEstimation' import { acceptedAmountBreakDown, Amount, + Application, ApplicationState, - Application as FinancialAidAnswers, } from '@island.is/financial-aid/shared/lib' +import { aidAmount } from '../../../lib/messages' +import { Breakdown } from '../../index' +import { Estimation, VeitaEstimation } from '../index' +import { ExternalData, FAApplication, waitingForSpouse } from '../../..' + interface Props { - application: Application - veitaApplication?: FinancialAidAnswers - municipality: DataProviderResult + application: FAApplication + veitaApplication?: Application + municipality: ExternalData['municipality'] state?: ApplicationState amount?: Amount } diff --git a/libs/application/templates/financial-aid/src/fields/Status/ApplicantStatus.tsx b/libs/application/templates/financial-aid/src/fields/Status/ApplicantStatus.tsx index 269bfbef4971..bfadba2f1fbc 100644 --- a/libs/application/templates/financial-aid/src/fields/Status/ApplicantStatus.tsx +++ b/libs/application/templates/financial-aid/src/fields/Status/ApplicantStatus.tsx @@ -1,28 +1,27 @@ import React from 'react' + import { ApplicationState } from '@island.is/financial-aid/shared/lib' import { Box, LoadingDots } from '@island.is/island-ui/core' + +import { FAFieldBaseProps } from '../../lib/types' import { hasSpouse, waitingForSpouse } from '../../lib/utils' +import { + AidAmount, + ApprovedAlert, + Header, + MissingFilesCard, + MoreActions, + RejectionMessage, + SpouseAlert, + Timeline, +} from './index' import useApplication from '../../lib/hooks/useApplication' -import Header from '../../components/Status/Header/Header' -import SpouseAlert from '../../components/Status/SpouseAlert/SpouseAlert' -import ApprovedAlert from '../../components/Status/ApprovedAlert/ApprovedAlert' -import RejectionMessage from '../../components/Status/RejectionMessage/RejectionMessage' -import MissingFilesCard from '../../components/Status/MissingFilesCard/MissingFilesCard' -import AidAmount from '../../components/Status/AidAmount/AidAmount' -import Timeline from '../../components/Status/Timeline/Timeline' -import MoreActions from '../../components/Status/MoreActions/MoreActions' -import { FieldBaseProps } from '@island.is/application/types' -import { getApplicantStatusConstants } from './util' +import * as styles from './Status.css' -export const ApplicantStatus = ({ - application, - goToScreen, -}: FieldBaseProps) => { - const { answers, externalData } = application - const { currentApplicationId, showCopyUrl, homepage, email, rulesHomepage } = - getApplicantStatusConstants(answers, externalData) - - const { currentApplication, loading } = useApplication(currentApplicationId) +const ApplicantStatus = ({ application, goToScreen }: FAFieldBaseProps) => { + const { currentApplication, loading } = useApplication( + application.externalData.currentApplication.data?.currentApplicationId, + ) const { municipality } = application.externalData const isWaitingForSpouse = waitingForSpouse(application.state) @@ -36,18 +35,22 @@ export const ApplicantStatus = ({ } return ( - +
- {isWaitingForSpouse && } + {isWaitingForSpouse && ( + + )} {state === ApplicationState.APPROVED && ( )} {state === ApplicationState.REJECTED && ( )} {state === ApplicationState.DATANEEDED && ( @@ -68,14 +71,16 @@ export const ApplicantStatus = ({ modified={currentApplication?.modified ?? application.modified} showSpouseStep={ isWaitingForSpouse - ? hasSpouse(answers, externalData) + ? hasSpouse(application.answers, application.externalData) : currentApplication?.spouseNationalId != null } /> ) } + +export default ApplicantStatus diff --git a/libs/application/templates/financial-aid/src/components/Status/ApprovedAlert/ApprovedAlert.tsx b/libs/application/templates/financial-aid/src/fields/Status/ApprovedAlert/ApprovedAlert.tsx similarity index 99% rename from libs/application/templates/financial-aid/src/components/Status/ApprovedAlert/ApprovedAlert.tsx rename to libs/application/templates/financial-aid/src/fields/Status/ApprovedAlert/ApprovedAlert.tsx index 5d84be082daa..09e5ba3dbd76 100644 --- a/libs/application/templates/financial-aid/src/components/Status/ApprovedAlert/ApprovedAlert.tsx +++ b/libs/application/templates/financial-aid/src/fields/Status/ApprovedAlert/ApprovedAlert.tsx @@ -1,10 +1,12 @@ import React, { useMemo } from 'react' import { useIntl } from 'react-intl' + import { ApplicationEvent, ApplicationEventType, } from '@island.is/financial-aid/shared/lib' import { AlertMessage, Box, Text } from '@island.is/island-ui/core' + import { approvedAlert } from '../../../lib/messages' interface Props { diff --git a/libs/application/templates/financial-aid/src/fields/Status/Estimation/Estimation.tsx b/libs/application/templates/financial-aid/src/fields/Status/Estimation/Estimation.tsx new file mode 100644 index 000000000000..01130cfac0d5 --- /dev/null +++ b/libs/application/templates/financial-aid/src/fields/Status/Estimation/Estimation.tsx @@ -0,0 +1,121 @@ +import React, { useMemo } from 'react' +import { useIntl } from 'react-intl' + +import { Text, Box } from '@island.is/island-ui/core' +import { + aidCalculator, + estimatedBreakDown, + showSpouseData, + Application, +} from '@island.is/financial-aid/shared/lib' + +import { Breakdown, DescriptionText } from '../..' +import { ApproveOptions, ExternalData, FAApplication } from '../../../lib/types' +import { aidAmount as aidAmountMessages } from '../../../lib/messages' +import { findFamilyStatus } from '../../..' + +interface EstimationProps { + application: FAApplication + municipality: ExternalData['municipality'] +} + +interface VeitaEstiamtionProps { + application: Application + municipality: ExternalData['municipality'] +} + +export const Estimation = ({ application, municipality }: EstimationProps) => { + const { formatMessage } = useIntl() + + const getAidType = () => { + return !showSpouseData[ + findFamilyStatus(application.answers, application.externalData) + ] + } + + const aidAmount = useMemo(() => { + if (application.answers.homeCircumstances.type && municipality.data) { + return aidCalculator( + application.answers.homeCircumstances.type, + getAidType() + ? municipality.data.individualAid + : municipality.data.cohabitationAid, + ) + } + }, [municipality.data]) + + return ( + <> + + <> + + {formatMessage(aidAmountMessages.title)} + + + + + + + + + ) +} + +export const VeitaEstimation = ({ + application, + municipality, +}: VeitaEstiamtionProps) => { + const { formatMessage } = useIntl() + + const getAidType = () => { + switch (true) { + case application.familyStatus != undefined: + return !showSpouseData[application.familyStatus] + default: + return application.spouseNationalId == null + } + } + + const aidAmount = useMemo(() => { + if (municipality.data && application.homeCircumstances) { + return aidCalculator( + application.homeCircumstances, + getAidType() + ? municipality.data.individualAid + : municipality.data.cohabitationAid, + ) + } + }, [municipality.data]) + + return ( + <> + + <> + + {formatMessage(aidAmountMessages.title)} + + + + + + + + + ) +} diff --git a/libs/application/templates/financial-aid/src/components/Status/Header/Header.tsx b/libs/application/templates/financial-aid/src/fields/Status/Header/Header.tsx similarity index 99% rename from libs/application/templates/financial-aid/src/components/Status/Header/Header.tsx rename to libs/application/templates/financial-aid/src/fields/Status/Header/Header.tsx index 5608da416dc6..861cf274481b 100644 --- a/libs/application/templates/financial-aid/src/components/Status/Header/Header.tsx +++ b/libs/application/templates/financial-aid/src/fields/Status/Header/Header.tsx @@ -1,11 +1,13 @@ import React from 'react' import { useIntl } from 'react-intl' + import { Text } from '@island.is/island-ui/core' import { ApplicationState, getNextPeriod, } from '@island.is/financial-aid/shared/lib' import { useLocale } from '@island.is/localization' + import { getStateMessageAndColor } from '../../../lib/formatters' interface Props { diff --git a/libs/application/templates/financial-aid/src/components/Status/MissingFilesCard/MissingFilesCard.tsx b/libs/application/templates/financial-aid/src/fields/Status/MissingFilesCard/MissingFilesCard.tsx similarity index 99% rename from libs/application/templates/financial-aid/src/components/Status/MissingFilesCard/MissingFilesCard.tsx rename to libs/application/templates/financial-aid/src/fields/Status/MissingFilesCard/MissingFilesCard.tsx index 360608036974..ca643166f01f 100644 --- a/libs/application/templates/financial-aid/src/components/Status/MissingFilesCard/MissingFilesCard.tsx +++ b/libs/application/templates/financial-aid/src/fields/Status/MissingFilesCard/MissingFilesCard.tsx @@ -1,5 +1,6 @@ import React from 'react' import { useIntl } from 'react-intl' + import { Box, ActionCard } from '@island.is/island-ui/core' import { missingFilesCard } from '../../../lib/messages' import { Routes } from '../../../lib/constants' diff --git a/libs/application/templates/financial-aid/src/components/Status/MoreActions/MoreActions.tsx b/libs/application/templates/financial-aid/src/fields/Status/MoreActions/MoreActions.tsx similarity index 93% rename from libs/application/templates/financial-aid/src/components/Status/MoreActions/MoreActions.tsx rename to libs/application/templates/financial-aid/src/fields/Status/MoreActions/MoreActions.tsx index bf0df7cec76d..b19b4be59967 100644 --- a/libs/application/templates/financial-aid/src/components/Status/MoreActions/MoreActions.tsx +++ b/libs/application/templates/financial-aid/src/fields/Status/MoreActions/MoreActions.tsx @@ -1,8 +1,9 @@ import React from 'react' import { useIntl } from 'react-intl' + import { Box, Text } from '@island.is/island-ui/core' +import { DescriptionText } from '../..' import { moreActions } from '../../../lib/messages' -import DescriptionText from '../../../components/DescriptionText/DescriptionText' interface Props { municipalityRulesPage?: string diff --git a/libs/application/templates/financial-aid/src/components/Status/RejectionMessage/RejectionMessage.tsx b/libs/application/templates/financial-aid/src/fields/Status/RejectionMessage/RejectionMessage.tsx similarity index 94% rename from libs/application/templates/financial-aid/src/components/Status/RejectionMessage/RejectionMessage.tsx rename to libs/application/templates/financial-aid/src/fields/Status/RejectionMessage/RejectionMessage.tsx index ef3f301a51df..40d75059c1e3 100644 --- a/libs/application/templates/financial-aid/src/components/Status/RejectionMessage/RejectionMessage.tsx +++ b/libs/application/templates/financial-aid/src/fields/Status/RejectionMessage/RejectionMessage.tsx @@ -1,8 +1,9 @@ import React from 'react' import { useIntl } from 'react-intl' + import { Box, Link, Text } from '@island.is/island-ui/core' import { rejectionMessage } from '../../../lib/messages' -import DescriptionText from '../../../components/DescriptionText/DescriptionText' +import DescriptionText from '../../DescriptionText/DescriptionText' interface Props { rejectionComment?: string diff --git a/libs/application/templates/financial-aid/src/components/Status/SpouseAlert/SpouseAlert.tsx b/libs/application/templates/financial-aid/src/fields/Status/SpouseAlert/SpouseAlert.tsx similarity index 95% rename from libs/application/templates/financial-aid/src/components/Status/SpouseAlert/SpouseAlert.tsx rename to libs/application/templates/financial-aid/src/fields/Status/SpouseAlert/SpouseAlert.tsx index 09dd8c20fb60..d55a3089f9e4 100644 --- a/libs/application/templates/financial-aid/src/components/Status/SpouseAlert/SpouseAlert.tsx +++ b/libs/application/templates/financial-aid/src/fields/Status/SpouseAlert/SpouseAlert.tsx @@ -1,8 +1,9 @@ import React from 'react' import { useIntl } from 'react-intl' + import { AlertMessage, Box } from '@island.is/island-ui/core' import { copyUrl, spouseAlert } from '../../../lib/messages' -import { CopyUrl } from '../../../fields' +import { CopyUrl } from '../..' interface Props { showCopyUrl: boolean diff --git a/libs/application/templates/financial-aid/src/components/Status/SpouseApproved/SpouseApproved.tsx b/libs/application/templates/financial-aid/src/fields/Status/SpouseApproved/SpouseApproved.tsx similarity index 99% rename from libs/application/templates/financial-aid/src/components/Status/SpouseApproved/SpouseApproved.tsx rename to libs/application/templates/financial-aid/src/fields/Status/SpouseApproved/SpouseApproved.tsx index 578ee7aa44e9..22224959ccb9 100644 --- a/libs/application/templates/financial-aid/src/components/Status/SpouseApproved/SpouseApproved.tsx +++ b/libs/application/templates/financial-aid/src/fields/Status/SpouseApproved/SpouseApproved.tsx @@ -1,8 +1,10 @@ import React from 'react' import { useIntl } from 'react-intl' + import { Text } from '@island.is/island-ui/core' import { getNextPeriod } from '@island.is/financial-aid/shared/lib' import { useLocale } from '@island.is/localization' + import { spouseApproved } from '../../../lib/messages' const SpouseApproved = () => { diff --git a/libs/application/templates/financial-aid/src/fields/Status/SpouseStatus.tsx b/libs/application/templates/financial-aid/src/fields/Status/SpouseStatus.tsx index 2ee67b3bd0c7..ce4824047fb1 100644 --- a/libs/application/templates/financial-aid/src/fields/Status/SpouseStatus.tsx +++ b/libs/application/templates/financial-aid/src/fields/Status/SpouseStatus.tsx @@ -1,28 +1,24 @@ import React from 'react' + import { ApplicationState } from '@island.is/financial-aid/shared/lib' import { Box, LoadingDots } from '@island.is/island-ui/core' + +import { FAFieldBaseProps } from '../../lib/types' +import { + Header, + MissingFilesCard, + MoreActions, + SpouseApproved, + Timeline, +} from './index' import useApplication from '../../lib/hooks/useApplication' -import Header from '../../components/Status/Header/Header' -import SpouseApproved from '../../components/Status/SpouseApproved/SpouseApproved' -import MissingFilesCard from '../../components/Status/MissingFilesCard/MissingFilesCard' -import Timeline from '../../components/Status/Timeline/Timeline' -import MoreActions from '../../components/Status/MoreActions/MoreActions' -import { FieldBaseProps } from '@island.is/application/types' -import { getValueViaPath } from '@island.is/application/core' - -export const SpouseStatus = ({ application, goToScreen }: FieldBaseProps) => { - const { externalData } = application - const currentApplicationId = getValueViaPath( - externalData, - 'currentApplication.data.currentApplicationId', - ) - const rulesHomepage = getValueViaPath( - externalData, - 'municipality.data.rulesHomepage', - ) - const email = getValueViaPath(externalData, 'municipality.data.email') +import * as styles from './Status.css' - const { currentApplication, loading } = useApplication(currentApplicationId) +const SpouseStatus = ({ application, goToScreen }: FAFieldBaseProps) => { + const { currentApplication, loading } = useApplication( + application.externalData.currentApplication.data?.currentApplicationId, + ) + const { municipality } = application.externalData const state = currentApplication?.state if (loading) { @@ -30,7 +26,7 @@ export const SpouseStatus = ({ application, goToScreen }: FieldBaseProps) => { } return ( - +
{state === ApplicationState.APPROVED && } @@ -46,9 +42,11 @@ export const SpouseStatus = ({ application, goToScreen }: FieldBaseProps) => { /> ) } + +export default SpouseStatus diff --git a/libs/application/templates/financial-aid/src/fields/Status/Status.css.ts b/libs/application/templates/financial-aid/src/fields/Status/Status.css.ts new file mode 100644 index 000000000000..a839766a3e8b --- /dev/null +++ b/libs/application/templates/financial-aid/src/fields/Status/Status.css.ts @@ -0,0 +1,6 @@ +import { style } from '@vanilla-extract/css' +import { theme } from '@island.is/island-ui/theme' + +export const container = style({ + marginTop: -theme.spacing[4], +}) diff --git a/libs/application/templates/financial-aid/src/components/Status/Timeline/Timeline.css.ts b/libs/application/templates/financial-aid/src/fields/Status/Timeline/Timeline.css.ts similarity index 100% rename from libs/application/templates/financial-aid/src/components/Status/Timeline/Timeline.css.ts rename to libs/application/templates/financial-aid/src/fields/Status/Timeline/Timeline.css.ts diff --git a/libs/application/templates/financial-aid/src/components/Status/Timeline/Timeline.tsx b/libs/application/templates/financial-aid/src/fields/Status/Timeline/Timeline.tsx similarity index 96% rename from libs/application/templates/financial-aid/src/components/Status/Timeline/Timeline.tsx rename to libs/application/templates/financial-aid/src/fields/Status/Timeline/Timeline.tsx index b22b1a4f2011..8613e42576f5 100644 --- a/libs/application/templates/financial-aid/src/components/Status/Timeline/Timeline.tsx +++ b/libs/application/templates/financial-aid/src/fields/Status/Timeline/Timeline.tsx @@ -2,8 +2,10 @@ import React, { useEffect, useState } from 'react' import cn from 'classnames' import { useIntl } from 'react-intl' import findLastIndex from 'lodash/findLastIndex' + import { Text, Box } from '@island.is/island-ui/core' import { ApplicationState } from '@island.is/financial-aid/shared/lib' + import * as styles from './Timeline.css' import { timeline } from '../../../lib/messages' import { timelineSections } from '../../../lib/formatters' @@ -40,7 +42,7 @@ const Timeline = ({ state, modified, created, showSpouseStep }: Props) => { return ( <> - + {formatMessage(timeline.title)} diff --git a/libs/application/templates/financial-aid/src/fields/Status/index.ts b/libs/application/templates/financial-aid/src/fields/Status/index.ts new file mode 100644 index 000000000000..a59a64402502 --- /dev/null +++ b/libs/application/templates/financial-aid/src/fields/Status/index.ts @@ -0,0 +1,10 @@ +export { default as AidAmount } from './AidAmount/AidAmount' +export { Estimation, VeitaEstimation } from './Estimation/Estimation' +export { default as Header } from './Header/Header' +export { default as MissingFilesCard } from './MissingFilesCard/MissingFilesCard' +export { default as MoreActions } from './MoreActions/MoreActions' +export { default as RejectionMessage } from './RejectionMessage/RejectionMessage' +export { default as SpouseAlert } from './SpouseAlert/SpouseAlert' +export { default as SpouseApproved } from './SpouseApproved/SpouseApproved' +export { default as Timeline } from './Timeline/Timeline' +export { default as ApprovedAlert } from './ApprovedAlert/ApprovedAlert' diff --git a/libs/application/templates/financial-aid/src/fields/Status/util.ts b/libs/application/templates/financial-aid/src/fields/Status/util.ts deleted file mode 100644 index 4bc9c7db5367..000000000000 --- a/libs/application/templates/financial-aid/src/fields/Status/util.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { ExternalData, FormValue } from '@island.is/application/types' - -export const getApplicantStatusConstants = ( - answers: FormValue, - externalData: ExternalData, -) => { - const currentApplicationId = getValueViaPath( - answers, - 'externalData.currentApplication.data.currentApplicationId', - ) - const showCopyUrl = getValueViaPath( - externalData, - 'sendSpouseEmail.data.success', - ) - const homepage = getValueViaPath( - externalData, - 'municipality.data.homepage', - ) - const email = getValueViaPath(externalData, 'municipality.data.email') - const rulesHomepage = getValueViaPath( - externalData, - 'municipality.data.rulesHomepage', - ) - - return { - currentApplicationId, - showCopyUrl, - homepage, - email, - rulesHomepage, - } -} diff --git a/libs/application/templates/financial-aid/src/fields/StudentForm/StudentForm.tsx b/libs/application/templates/financial-aid/src/fields/StudentForm/StudentForm.tsx new file mode 100644 index 000000000000..6921708c46b6 --- /dev/null +++ b/libs/application/templates/financial-aid/src/fields/StudentForm/StudentForm.tsx @@ -0,0 +1,73 @@ +import React from 'react' +import { Box, Text } from '@island.is/island-ui/core' +import { FAFieldBaseProps, ApproveOptions } from '../../lib/types' +import { useIntl } from 'react-intl' +import { studentForm, approveOptions } from '../../lib/messages' +import * as styles from '../Shared.css' +import cn from 'classnames' +import { InputController, RadioController } from '@island.is/shared/form-fields' +import { useFormContext } from 'react-hook-form' +import withLogo from '../Logo/Logo' + +const StudentForm = ({ errors, application }: FAFieldBaseProps) => { + const { formatMessage } = useIntl() + const { answers } = application + + const typeInput = { + id: 'student.isStudent', + error: errors?.student?.isStudent, + } + const customInput = { + id: 'student.custom', + error: errors?.student?.custom, + } + const { clearErrors, getValues } = useFormContext() + + return ( + <> + + + + + { + clearErrors(customInput.id) + }} + /> + + {formatMessage(studentForm.input.example)} + + + + ) +} + +export default withLogo(StudentForm) diff --git a/libs/application/templates/financial-aid/src/components/Summary/ChildrenInfo.tsx b/libs/application/templates/financial-aid/src/fields/Summary/ChildrenInfo.tsx similarity index 96% rename from libs/application/templates/financial-aid/src/components/Summary/ChildrenInfo.tsx rename to libs/application/templates/financial-aid/src/fields/Summary/ChildrenInfo.tsx index 3077fe7f9113..3af0704ebcff 100644 --- a/libs/application/templates/financial-aid/src/components/Summary/ChildrenInfo.tsx +++ b/libs/application/templates/financial-aid/src/fields/Summary/ChildrenInfo.tsx @@ -1,14 +1,18 @@ import React from 'react' import { useIntl } from 'react-intl' + import { Box, GridColumn, GridRow, Text } from '@island.is/island-ui/core' import { summaryForm } from '../../lib/messages' import { formatNationalId } from '@island.is/financial-aid/shared/lib' import SummaryBlock from './SummaryBlock' import { Routes } from '../../lib/constants' -import { ChildrenSchoolInfo } from '../../lib/types' interface Props { - childrenSchoolInfo: Array + childrenSchoolInfo: { + nationalId: string + school: string + fullName: string + }[] goToScreen: ((id: string) => void) | undefined childrenComment?: string } diff --git a/libs/application/templates/financial-aid/src/components/Summary/ContactInfo.tsx b/libs/application/templates/financial-aid/src/fields/Summary/ContactInfo.tsx similarity index 99% rename from libs/application/templates/financial-aid/src/components/Summary/ContactInfo.tsx rename to libs/application/templates/financial-aid/src/fields/Summary/ContactInfo.tsx index 224174bfa1ad..32cb65f44338 100644 --- a/libs/application/templates/financial-aid/src/components/Summary/ContactInfo.tsx +++ b/libs/application/templates/financial-aid/src/fields/Summary/ContactInfo.tsx @@ -1,5 +1,6 @@ import React from 'react' import { useIntl } from 'react-intl' + import { GridColumn, GridRow, Text } from '@island.is/island-ui/core' import { Routes } from '../../lib/constants' import { contactInfo } from '../../lib/messages' diff --git a/libs/application/templates/financial-aid/src/components/Summary/DirectTaxPaymentCell.tsx b/libs/application/templates/financial-aid/src/fields/Summary/DirectTaxPaymentCell.tsx similarity index 100% rename from libs/application/templates/financial-aid/src/components/Summary/DirectTaxPaymentCell.tsx rename to libs/application/templates/financial-aid/src/fields/Summary/DirectTaxPaymentCell.tsx diff --git a/libs/application/templates/financial-aid/src/components/Summary/Files.tsx b/libs/application/templates/financial-aid/src/fields/Summary/Files.tsx similarity index 98% rename from libs/application/templates/financial-aid/src/components/Summary/Files.tsx rename to libs/application/templates/financial-aid/src/fields/Summary/Files.tsx index b63e280fe3e1..9a1eb2b3f457 100644 --- a/libs/application/templates/financial-aid/src/components/Summary/Files.tsx +++ b/libs/application/templates/financial-aid/src/fields/Summary/Files.tsx @@ -5,7 +5,7 @@ import { useMutation } from '@apollo/client' import { UploadFile, Box, Icon, Text } from '@island.is/island-ui/core' import { encodeFilenames } from '../../lib/utils' import { CreateSignedUrlMutation } from '../../lib/hooks/useFileUpload' -import * as styles from './Files.css' +import * as styles from '../Shared.css' import SummaryBlock from './SummaryBlock' import { Routes } from '../../lib/constants' import { summaryForm } from '../../lib/messages' diff --git a/libs/application/templates/financial-aid/src/components/Summary/FormInfo.tsx b/libs/application/templates/financial-aid/src/fields/Summary/FormInfo.tsx similarity index 100% rename from libs/application/templates/financial-aid/src/components/Summary/FormInfo.tsx rename to libs/application/templates/financial-aid/src/fields/Summary/FormInfo.tsx diff --git a/libs/application/templates/financial-aid/src/fields/Summary/SpouseSummaryForm.tsx b/libs/application/templates/financial-aid/src/fields/Summary/SpouseSummaryForm.tsx index 6ea1fefede88..3ece2fc20497 100644 --- a/libs/application/templates/financial-aid/src/fields/Summary/SpouseSummaryForm.tsx +++ b/libs/application/templates/financial-aid/src/fields/Summary/SpouseSummaryForm.tsx @@ -1,52 +1,42 @@ -import { FieldBaseProps } from '@island.is/application/types' -import { Box } from '@island.is/island-ui/core' -import { useUserInfo } from '@island.is/react-spa/bff' import React, { useEffect, useState } from 'react' -import { useFormContext } from 'react-hook-form' -import DescriptionText from '../../components/DescriptionText/DescriptionText' -import DirectTaxPaymentModal from '../../components/DirectTaxPaymentsModal/DirectTaxPaymentModal' -import ContactInfo from '../../components/Summary/ContactInfo' -import DirectTaxPaymentCell from '../../components/Summary/DirectTaxPaymentCell' -import Files from '../../components/Summary/Files' -import FormInfo from '../../components/Summary/FormInfo' -import SummaryComment from '../../components/Summary/SummaryComment' -import UserInfo from '../../components/Summary/UserInfo' +import { Box } from '@island.is/island-ui/core' +import * as m from '../../lib/messages' +import { + ApproveOptions, + FAFieldBaseProps, + SummaryComment as SummaryCommentType, +} from '../../lib/types' import { Routes } from '../../lib/constants' +import { DescriptionText, DirectTaxPaymentsModal } from '../index' import { formatAddress, spouseFormItems } from '../../lib/formatters' -import * as m from '../../lib/messages' -import { SummaryComment as SummaryCommentType } from '../../lib/types' -import { getSpouseSummaryConstants } from './utils' +import { + FormInfo, + SummaryComment, + UserInfo, + ContactInfo, + Files, + DirectTaxPaymentCell, +} from './index' +import withLogo from '../Logo/Logo' +import { useFormContext } from 'react-hook-form' +import { useUserInfo } from '@island.is/react-spa/bff' -export const SpouseSummaryForm = ({ - application, - goToScreen, -}: FieldBaseProps) => { +const SpouseSummaryForm = ({ application, goToScreen }: FAFieldBaseProps) => { const { id, answers, externalData } = application const summaryCommentType = SummaryCommentType.SPOUSEFORMCOMMENT const [isModalOpen, setIsModalOpen] = useState(false) const { setValue } = useFormContext() + + const nationalId = + externalData.nationalRegistrySpouse.data?.nationalId || + answers?.relationshipStatus?.spouseNationalId const userInfo = useUserInfo() useEffect(() => { setValue('spouseName', userInfo?.profile.name) }, []) - const { - nationalId, - data, - taxData, - spouseEmail, - spousePhone, - route, - personalTaxReturn, - directTaxPayments, - fetchDate, - spouseTaxReturnFiles, - spouseFormComment, - spouseIncomeFiles, - } = getSpouseSummaryConstants(answers, externalData) - return ( <> @@ -56,7 +46,7 @@ export const SpouseSummaryForm = ({ @@ -65,40 +55,53 @@ export const SpouseSummaryForm = ({ )} - {directTaxPayments && ( - { setIsModalOpen(isOpen) @@ -108,3 +111,5 @@ export const SpouseSummaryForm = ({ ) } + +export default withLogo(SpouseSummaryForm) diff --git a/libs/application/templates/financial-aid/src/components/Summary/SummaryBlock.tsx b/libs/application/templates/financial-aid/src/fields/Summary/SummaryBlock.tsx similarity index 95% rename from libs/application/templates/financial-aid/src/components/Summary/SummaryBlock.tsx rename to libs/application/templates/financial-aid/src/fields/Summary/SummaryBlock.tsx index dab6c13ce932..4607d415db7e 100644 --- a/libs/application/templates/financial-aid/src/components/Summary/SummaryBlock.tsx +++ b/libs/application/templates/financial-aid/src/fields/Summary/SummaryBlock.tsx @@ -3,7 +3,7 @@ import { useIntl } from 'react-intl' import { Box, Button } from '@island.is/island-ui/core' import { summaryForm } from '../../lib/messages' -import * as styles from './SummaryBlock.css' +import * as styles from '../Shared.css' interface Props { editAction?: () => void diff --git a/libs/application/templates/financial-aid/src/components/Summary/SummaryComment.tsx b/libs/application/templates/financial-aid/src/fields/Summary/SummaryComment.tsx similarity index 100% rename from libs/application/templates/financial-aid/src/components/Summary/SummaryComment.tsx rename to libs/application/templates/financial-aid/src/fields/Summary/SummaryComment.tsx diff --git a/libs/application/templates/financial-aid/src/fields/Summary/SummaryForm.tsx b/libs/application/templates/financial-aid/src/fields/Summary/SummaryForm.tsx index 42338bd7608f..79fc7ad25c72 100644 --- a/libs/application/templates/financial-aid/src/fields/Summary/SummaryForm.tsx +++ b/libs/application/templates/financial-aid/src/fields/Summary/SummaryForm.tsx @@ -1,5 +1,6 @@ import React, { useMemo, useState } from 'react' import { useIntl } from 'react-intl' + import { Text, Box, AlertMessage, UploadFile } from '@island.is/island-ui/core' import { getNextPeriod, @@ -9,68 +10,53 @@ import { ChildrenAid, } from '@island.is/financial-aid/shared/lib' import { useLocale } from '@island.is/localization' + import * as m from '../../lib/messages' import { ApproveOptions, + FAFieldBaseProps, SummaryComment as SummaryCommentType, } from '../../lib/types' import { Routes } from '../../lib/constants' +import { DescriptionText, Breakdown } from '../index' import { formatAddress, formItems } from '../../lib/formatters' +import { + FormInfo, + SummaryComment, + UserInfo, + ContactInfo, + Files, + DirectTaxPaymentCell, +} from './index' + +import { DirectTaxPaymentsModal } from '..' import { findFamilyStatus } from '../../lib/utils' -import DescriptionText from '../../components/DescriptionText/DescriptionText' -import Breakdown from '../../components/Breakdown/Breakdown' -import DirectTaxPaymentModal from '../../components/DirectTaxPaymentsModal/DirectTaxPaymentModal' -import SummaryComment from '../../components/Summary/SummaryComment' -import ChildrenInfo from '../../components/Summary/ChildrenInfo' -import ContactInfo from '../../components/Summary/ContactInfo' -import Files from '../../components/Summary/Files' -import FormInfo from '../../components/Summary/FormInfo' -import DirectTaxPaymentCell from '../../components/Summary/DirectTaxPaymentCell' -import UserInfo from '../../components/Summary/UserInfo' -import { FieldBaseProps } from '@island.is/application/types' -import { AnswersSchema } from '../../lib/dataSchema' -import { getSummaryConstants } from './utils' - -export const SummaryForm = ({ application, goToScreen }: FieldBaseProps) => { +import withLogo from '../Logo/Logo' +import ChildrenInfo from './ChildrenInfo' + +const SummaryForm = ({ application, goToScreen }: FAFieldBaseProps) => { const { formatMessage } = useIntl() const { lang } = useLocale() const { id, answers, externalData } = application - const answersSchema = answers as AnswersSchema const summaryCommentType = SummaryCommentType.FORMCOMMENT const [isModalOpen, setIsModalOpen] = useState(false) - const { - homeCircumstances, - individualAid, - cohabitationAid, - childrenSchoolInfo, - municipalitiesDirectTaxPaymentsSuccess, - municipalitiesDirectTaxPayments, - fetchDate, - personalTaxReturn, - personalTaxCreditType, - childrenCustodyData, - childrenAid, - nationalRegistryData, - } = getSummaryConstants(answers, externalData) - const aidAmount = useMemo(() => { - if (externalData.municipality.data && homeCircumstances) { + if (externalData.municipality.data && answers.homeCircumstances) { return aidCalculator( - homeCircumstances, + answers.homeCircumstances.type, findFamilyStatus(answers, externalData) === FamilyStatus.NOT_COHABITATION - ? individualAid - : cohabitationAid, + ? externalData.municipality.data.individualAid + : externalData.municipality.data.cohabitationAid, ) } }, [externalData.municipality.data]) const showAlertMessageAboutChildrenAid = - childrenCustodyData && - childrenCustodyData?.length > 0 && - childrenAid !== ChildrenAid.NOTDEFINED + externalData.childrenCustodyInformation.data.length > 0 && + externalData.municipality.data?.childrenAid !== ChildrenAid.NOTDEFINED const findFilesRouteFrom = ( childrenFiles: UploadFile[], @@ -111,7 +97,7 @@ export const SummaryForm = ({ application, goToScreen }: FieldBaseProps) => { @@ -119,7 +105,8 @@ export const SummaryForm = ({ application, goToScreen }: FieldBaseProps) => { {showAlertMessageAboutChildrenAid && ( - {childrenAid === ChildrenAid.APPLICANT ? ( + {externalData.municipality.data?.childrenAid === + ChildrenAid.APPLICANT ? ( { - {childrenSchoolInfo && childrenSchoolInfo.length > 0 && ( + {answers?.childrenSchoolInfo && answers.childrenSchoolInfo.length > 0 && ( )} @@ -168,40 +155,46 @@ export const SummaryForm = ({ application, goToScreen }: FieldBaseProps) => { - { setIsModalOpen(isOpen) @@ -210,3 +203,5 @@ export const SummaryForm = ({ application, goToScreen }: FieldBaseProps) => { ) } + +export default withLogo(SummaryForm) diff --git a/libs/application/templates/financial-aid/src/components/Summary/UserInfo.tsx b/libs/application/templates/financial-aid/src/fields/Summary/UserInfo.tsx similarity index 99% rename from libs/application/templates/financial-aid/src/components/Summary/UserInfo.tsx rename to libs/application/templates/financial-aid/src/fields/Summary/UserInfo.tsx index cbf87df7ac64..8a8ccbe28816 100644 --- a/libs/application/templates/financial-aid/src/components/Summary/UserInfo.tsx +++ b/libs/application/templates/financial-aid/src/fields/Summary/UserInfo.tsx @@ -1,5 +1,6 @@ import React from 'react' import { useIntl } from 'react-intl' + import { Box, GridColumn, GridRow, Text } from '@island.is/island-ui/core' import { summaryForm } from '../../lib/messages' import { formatNationalId } from '@island.is/financial-aid/shared/lib' diff --git a/libs/application/templates/financial-aid/src/fields/Summary/index.ts b/libs/application/templates/financial-aid/src/fields/Summary/index.ts new file mode 100644 index 000000000000..201c26ccd8e9 --- /dev/null +++ b/libs/application/templates/financial-aid/src/fields/Summary/index.ts @@ -0,0 +1,6 @@ +export { default as ContactInfo } from './ContactInfo' +export { default as Files } from './Files' +export { default as SummaryComment } from './SummaryComment' +export { default as UserInfo } from './UserInfo' +export { default as FormInfo } from './FormInfo' +export { default as DirectTaxPaymentCell } from './DirectTaxPaymentCell' diff --git a/libs/application/templates/financial-aid/src/fields/Summary/utils.ts b/libs/application/templates/financial-aid/src/fields/Summary/utils.ts deleted file mode 100644 index c26ff83f2549..000000000000 --- a/libs/application/templates/financial-aid/src/fields/Summary/utils.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { - ApplicantChildCustodyInformation, - ExternalData, - FormValue, - NationalRegistryIndividual, -} from '@island.is/application/types' -import { ApproveOptions, ChildrenSchoolInfo, TaxData } from '../../lib/types' -import { Routes } from '../../lib/constants' -import { - Aid, - ChildrenAid, - DirectTaxPayment, - HomeCircumstances, - PersonalTaxReturn, -} from '@island.is/financial-aid/shared/lib' -import { UploadFile } from '@island.is/island-ui/core' - -export const getSpouseSummaryConstants = ( - answers: FormValue, - externalData: ExternalData, -) => { - const nationalId = - getValueViaPath( - externalData, - 'nationalRegistrySpouse.data.nationalId', - ) || getValueViaPath(answers, 'relationshipStatus.spouseNationalId') - - const data = getValueViaPath(externalData, 'nationalRegistrySpouse.data') as - | NationalRegistryIndividual - | undefined - - const taxData = getValueViaPath(externalData, 'taxDataSpouse.data') - - const spouseEmail = getValueViaPath( - answers, - 'spouseContactInfo.email', - ) - - const spousePhone = getValueViaPath( - answers, - 'spouseContactInfo.phone', - ) - - const spouseIncomeType = getValueViaPath( - answers, - 'spouseIncome.type', - ) - - const route = - spouseIncomeType === ApproveOptions.Yes - ? Routes.SPOUSEINCOMEFILES - : Routes.SPOUSETAXRETURNFILES - - const personalTaxReturn = getValueViaPath( - externalData, - 'taxDataSpouse.data.municipalitiesPersonalTaxReturn.personalTaxReturn', - ) - - const directTaxPayments = getValueViaPath>( - externalData, - 'taxDataSpouse.data.municipalitiesDirectTaxPayments.directTaxPayments', - ) - - const fetchDate = getValueViaPath( - externalData, - 'nationalRegistry?.date', - ) - - const spouseTaxReturnFiles = getValueViaPath>( - answers, - 'spouseTaxReturnFiles', - ) - - const spouseFormComment = getValueViaPath( - answers, - 'spouseFormComment', - ) - - const spouseIncomeFiles = getValueViaPath>( - answers, - 'spouseIncomeFiles', - ) - - return { - nationalId, - data, - taxData, - spouseEmail, - spousePhone, - route, - personalTaxReturn, - directTaxPayments, - fetchDate, - spouseTaxReturnFiles, - spouseFormComment, - spouseIncomeFiles, - } -} - -export const getSummaryConstants = ( - answers: FormValue, - externalData: ExternalData, -) => { - const homeCircumstances = getValueViaPath( - answers, - 'homeCircumstances.type', - ) - - const individualAid = getValueViaPath( - externalData, - 'municipality.data.individualAid', - ) - - const cohabitationAid = getValueViaPath( - externalData, - 'municipality.data.cohabitationAid', - ) - const childrenSchoolInfo = getValueViaPath>( - answers, - 'childrenSchoolInfo', - ) - - const municipalitiesDirectTaxPaymentsSuccess = getValueViaPath( - externalData, - 'taxData.data.municipalitiesDirectTaxPayments.success', - ) - - const municipalitiesDirectTaxPayments = getValueViaPath< - Array - >( - externalData, - 'taxData.data.municipalitiesDirectTaxPayments.directTaxPayments', - ) - - const fetchDate = getValueViaPath( - externalData, - 'nationalRegistry.date', - ) - - const personalTaxReturn = getValueViaPath( - externalData, - 'taxData.data.municipalitiesPersonalTaxReturn.personalTaxReturn', - ) - - const personalTaxCreditType = getValueViaPath( - answers, - 'personalTaxCredit.type', - ) - - const childrenCustodyData = getValueViaPath< - Array - >(externalData, 'childrenCustodyInformation.data') - - const childrenAid = getValueViaPath( - externalData, - 'municipality.data.childrenAid', - ) - - const nationalRegistryData = getValueViaPath( - externalData, - 'nationalRegistry.data', - ) - - return { - homeCircumstances, - individualAid, - cohabitationAid, - childrenSchoolInfo, - municipalitiesDirectTaxPaymentsSuccess, - municipalitiesDirectTaxPayments, - fetchDate, - personalTaxReturn, - personalTaxCreditType, - childrenCustodyData, - childrenAid, - nationalRegistryData, - } -} diff --git a/libs/application/templates/financial-aid/src/components/TaxBreakdown/TaxBreakdown.css.ts b/libs/application/templates/financial-aid/src/fields/TaxBreakdown/TaxBreakdown.css.ts similarity index 100% rename from libs/application/templates/financial-aid/src/components/TaxBreakdown/TaxBreakdown.css.ts rename to libs/application/templates/financial-aid/src/fields/TaxBreakdown/TaxBreakdown.css.ts diff --git a/libs/application/templates/financial-aid/src/components/TaxBreakdown/TaxBreakdown.tsx b/libs/application/templates/financial-aid/src/fields/TaxBreakdown/TaxBreakdown.tsx similarity index 99% rename from libs/application/templates/financial-aid/src/components/TaxBreakdown/TaxBreakdown.tsx rename to libs/application/templates/financial-aid/src/fields/TaxBreakdown/TaxBreakdown.tsx index 0dc17d19d3b0..6b1047914bb1 100644 --- a/libs/application/templates/financial-aid/src/components/TaxBreakdown/TaxBreakdown.tsx +++ b/libs/application/templates/financial-aid/src/fields/TaxBreakdown/TaxBreakdown.tsx @@ -9,6 +9,7 @@ import { import TaxBreakdownHeadline from './TaxBreakdownHeadline' import groupBy from 'lodash/groupBy' import { directTaxPaymentModal } from '../../lib/messages' + import * as styles from './TaxBreakdown.css' import { useIntl } from 'react-intl' import { useLocale } from '@island.is/localization' diff --git a/libs/application/templates/financial-aid/src/components/TaxBreakdown/TaxBreakdownHeadline.tsx b/libs/application/templates/financial-aid/src/fields/TaxBreakdown/TaxBreakdownHeadline.tsx similarity index 100% rename from libs/application/templates/financial-aid/src/components/TaxBreakdown/TaxBreakdownHeadline.tsx rename to libs/application/templates/financial-aid/src/fields/TaxBreakdown/TaxBreakdownHeadline.tsx diff --git a/libs/application/templates/financial-aid/src/components/TaxBreakdown/TaxBreakdownItem.tsx b/libs/application/templates/financial-aid/src/fields/TaxBreakdown/TaxBreakdownItem.tsx similarity index 100% rename from libs/application/templates/financial-aid/src/components/TaxBreakdown/TaxBreakdownItem.tsx rename to libs/application/templates/financial-aid/src/fields/TaxBreakdown/TaxBreakdownItem.tsx diff --git a/libs/application/templates/financial-aid/src/fields/taxReturnFilesForm/TaxReturnFilesForm.tsx b/libs/application/templates/financial-aid/src/fields/TaxReturnFilesForm/TaxReturnFilesForm.tsx similarity index 59% rename from libs/application/templates/financial-aid/src/fields/taxReturnFilesForm/TaxReturnFilesForm.tsx rename to libs/application/templates/financial-aid/src/fields/TaxReturnFilesForm/TaxReturnFilesForm.tsx index 71480d5c5aa0..a746a9accf6c 100644 --- a/libs/application/templates/financial-aid/src/fields/taxReturnFilesForm/TaxReturnFilesForm.tsx +++ b/libs/application/templates/financial-aid/src/fields/TaxReturnFilesForm/TaxReturnFilesForm.tsx @@ -2,36 +2,22 @@ import React from 'react' import { useIntl } from 'react-intl' import { UploadFile, Box, AlertMessage } from '@island.is/island-ui/core' import { taxReturnForm } from '../../lib/messages' -import { TaxData, UploadFileType } from '../..' -import { FieldBaseProps } from '@island.is/application/types' -import Files from '../files/Files' -import { getValueViaPath } from '@island.is/application/core' -import { getTaxFormContent } from './TaxFormContent' -export const TaxReturnFilesForm = ({ field, application }: FieldBaseProps) => { - const { formatMessage } = useIntl() - const { id, answers, externalData, assignees } = application - const nationalId = getValueViaPath( - externalData, - 'nationalRegistry.data.nationalId', - ) - const taxData = getValueViaPath(externalData, 'taxData.data') - const spouseTaxData = getValueViaPath( - externalData, - 'taxDataSpouse.data', - ) +import { FAFieldBaseProps, OverrideAnswerSchema, UploadFileType } from '../..' - const taxDataToUse = - assignees.includes(nationalId ?? '') && spouseTaxData - ? spouseTaxData - : taxData +import { Files } from '..' +import { getTaxFormContent } from './taxFormContent' +import withLogo from '../Logo/Logo' - if (!taxDataToUse) { - return null - } +const TaxReturnFilesForm = ({ field, application }: FAFieldBaseProps) => { + const { formatMessage } = useIntl() + const { id, answers, externalData, assignees } = application const { municipalitiesDirectTaxPayments, municipalitiesPersonalTaxReturn } = - taxDataToUse + assignees.includes(externalData.nationalRegistry.data.nationalId) && + externalData?.taxDataSpouse?.data + ? externalData.taxDataSpouse.data + : externalData.taxData.data const taxReturnFetchFailed = municipalitiesPersonalTaxReturn?.personalTaxReturn === null @@ -62,7 +48,9 @@ export const TaxReturnFilesForm = ({ field, application }: FieldBaseProps) => { @@ -70,3 +58,5 @@ export const TaxReturnFilesForm = ({ field, application }: FieldBaseProps) => { ) } + +export default withLogo(TaxReturnFilesForm) diff --git a/libs/application/templates/financial-aid/src/fields/taxReturnFilesForm/TaxFormContent.tsx b/libs/application/templates/financial-aid/src/fields/TaxReturnFilesForm/taxFormContent.tsx similarity index 96% rename from libs/application/templates/financial-aid/src/fields/taxReturnFilesForm/TaxFormContent.tsx rename to libs/application/templates/financial-aid/src/fields/TaxReturnFilesForm/taxFormContent.tsx index b24399cae3e2..895d732d6c4c 100644 --- a/libs/application/templates/financial-aid/src/fields/taxReturnFilesForm/TaxFormContent.tsx +++ b/libs/application/templates/financial-aid/src/fields/TaxReturnFilesForm/taxFormContent.tsx @@ -2,7 +2,7 @@ import React from 'react' import { useIntl } from 'react-intl' import { Text, Box } from '@island.is/island-ui/core' import { taxReturnForm } from '../../lib/messages' -import DescriptionText from '../../components/DescriptionText/DescriptionText' +import { DescriptionText } from '..' const DirectTaxPaymentsInfo = () => { const { formatMessage } = useIntl() diff --git a/libs/application/templates/financial-aid/src/fields/UnknownRelationshipForm/UnknownRelationshipForm.tsx b/libs/application/templates/financial-aid/src/fields/UnknownRelationshipForm/UnknownRelationshipForm.tsx new file mode 100644 index 000000000000..46f301a80637 --- /dev/null +++ b/libs/application/templates/financial-aid/src/fields/UnknownRelationshipForm/UnknownRelationshipForm.tsx @@ -0,0 +1,158 @@ +import React from 'react' +import { Box, Text } from '@island.is/island-ui/core' +import { ApproveOptions, ErrorSchema, FAFieldBaseProps } from '../../lib/types' +import { useIntl } from 'react-intl' +import { unknownRelationship, error } from '../../lib/messages' +import DescriptionText from '../DescriptionText/DescriptionText' +import { useFormContext } from 'react-hook-form' +import { + RadioController, + InputController, + CheckboxController, +} from '@island.is/shared/form-fields' + +import * as styles from '../Shared.css' +import cn from 'classnames' +import { isValidEmail, isValidNationalId } from '../../lib/utils' +import withLogo from '../Logo/Logo' + +type validationType = 'email' | 'nationalId' | 'approveItems' +const errorIdForSpouse = 'relationshipStatus' + +const ValidationCheck = (errors: ErrorSchema, type: validationType) => { + const { formatMessage } = useIntl() + const { getValues } = useFormContext() + switch (type) { + case 'email': + return errors.relationshipStatus !== undefined && + !isValidEmail(getValues(`${errorIdForSpouse}.spouseEmail`)) + ? formatMessage(error.validation.email) + : undefined + case 'nationalId': + return errors.relationshipStatus !== undefined && + !isValidNationalId(getValues(`${errorIdForSpouse}.spouseNationalId`)) + ? formatMessage(error.validation.nationalId) + : undefined + case 'approveItems': + return errors.relationshipStatus !== undefined && + getValues(`${errorIdForSpouse}.spouseApproveTerms`)?.length !== 1 + ? formatMessage(error.validation.approveSpouse) + : undefined + } +} + +const UnknownRelationshipForm = ({ errors, application }: FAFieldBaseProps) => { + const { formatMessage } = useIntl() + const { answers } = application + const { clearErrors, getValues } = useFormContext() + + const typeInput = { + id: 'relationshipStatus.unregisteredCohabitation', + error: errors?.relationshipStatus?.unregisteredCohabitation, + } + + const spouseEmail = { + id: 'relationshipStatus.spouseEmail', + error: ValidationCheck(errors, 'email'), + } + const spouseNationalId = { + id: 'relationshipStatus.spouseNationalId', + error: ValidationCheck(errors, 'nationalId'), + } + + const spouseApproveTerms = { + id: 'relationshipStatus.spouseApproveTerms', + error: ValidationCheck(errors, 'approveItems'), + } + + return ( + <> + + {formatMessage(unknownRelationship.general.intro)} + + + + + + {formatMessage(unknownRelationship.form.title)} + + + + + + + + { + clearErrors(errorIdForSpouse) + }} + /> + + + { + clearErrors(errorIdForSpouse) + }} + /> + + + + + ) +} + +export default withLogo(UnknownRelationshipForm) diff --git a/libs/application/templates/financial-aid/src/fields/fileUploadController/FileUploadController.css.ts b/libs/application/templates/financial-aid/src/fields/fileUploadController/FileUploadController.css.ts deleted file mode 100644 index f3d55639bad4..000000000000 --- a/libs/application/templates/financial-aid/src/fields/fileUploadController/FileUploadController.css.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { style } from '@vanilla-extract/css' -import { theme } from '@island.is/island-ui/theme' - -export const errorMessage = style({ - overflow: 'hidden', - maxHeight: '0', - transition: 'max-height 250ms ease', -}) -export const showErrorMessage = style({ - maxHeight: theme.spacing[5], -}) diff --git a/libs/application/templates/financial-aid/src/fields/index.ts b/libs/application/templates/financial-aid/src/fields/index.ts index de41cb87c6bf..49efb9a3335f 100644 --- a/libs/application/templates/financial-aid/src/fields/index.ts +++ b/libs/application/templates/financial-aid/src/fields/index.ts @@ -1,11 +1,33 @@ -export { ChildrenForm } from './ChildrenForm/ChildrenForm' -export { CopyUrl } from './CopyUrl/CopyUrl' -export { SummaryForm } from './Summary/SummaryForm' -export { SpouseSummaryForm } from './Summary/SpouseSummaryForm' -export { ApplicantStatus } from './Status/ApplicantStatus' -export { SpouseStatus } from './Status/SpouseStatus' -export { MissingFilesConfirmation } from './Summary/MissingFilesConfirmation' -export { ChildrenFilesForm } from './childrenFilesForm/ChildrenFilesForm' -export { IncomeFilesForm } from './incomeFilesForm/IncomeFilesForm' -export { TaxReturnFilesForm } from './taxReturnFilesForm/TaxReturnFilesForm' -export { MissingFiles } from './missingFiles/MissingFiles' +export { default as InRelationshipForm } from './InRelationshipForm/InRelationshipForm' +export { default as AboutForm } from './AboutForm/AboutForm' +export { default as ChildrenForm } from './ChildrenForm/ChildrenForm' +export { default as ChildrenFilesForm } from './ChildrenFilesForm/ChildrenFilesForm' +export { default as DescriptionText } from './DescriptionText/DescriptionText' +export { default as StudentForm } from './StudentForm/StudentForm' +export { default as HomeCircumstancesForm } from './HomeCircumstancesForm/HomeCircumstancesForm' +export { default as IncomeForm } from './IncomeForm/IncomeForm' +export { default as EmploymentForm } from './EmploymentForm/EmploymentForm' +export { default as BankInfoForm } from './BankInfoForm/BankInfoForm' +export { default as PersonalTaxCreditForm } from './PersonalTaxCreditForm/PersonalTaxCreditForm' +export { default as ApplicantConfirmation } from './Confirmation/ApplicantConfirmation' +export { default as SpouseConfirmation } from './Confirmation/SpouseConfirmation' +export { default as ConfirmationSectionImage } from './ConfirmationSectionImage/ConfirmationSectionImage' +export { default as CopyUrl } from './CopyUrl/CopyUrl' +export { default as ContactInfo } from './ContactInfo/ContactInfo' +export { default as UnknownRelationshipForm } from './UnknownRelationshipForm/UnknownRelationshipForm' +export { default as SummaryForm } from './Summary/SummaryForm' +export { default as SpouseSummaryForm } from './Summary/SpouseSummaryForm' +export { default as IncomeFilesForm } from './IncomeFilesForm/IncomeFilesForm' +export { default as Files } from './Files/Files' +export { default as FileUploadContainer } from './FileUploadContainer/FileUploadContainer' +export { default as TaxReturnFilesForm } from './TaxReturnFilesForm/TaxReturnFilesForm' +export { default as Breakdown } from './Breakdown/Breakdown' +export { default as AboutSpouseForm } from './AboutSpouseForm/AboutSpouseForm' +export { default as PrivacyPolicyAccordion } from './PrivacyPolicyAccordion/PrivacyPolicyAccordion' +export { default as ServiceCenter } from './ServiceCenter/ServiceCenter' +export { default as ApplicantStatus } from './Status/ApplicantStatus' +export { default as SpouseStatus } from './Status/SpouseStatus' +export { default as MissingFiles } from './MissingFiles/MissingFiles' +export { default as MissingFilesConfirmation } from './MissingFiles/MissingFilesConfirmation/MissingFilesConfirmation' +export { default as DirectTaxPaymentsModal } from './DirectTaxPaymentsModal/DirectTaxPaymentModal' +export { default as TaxBreakdown } from './TaxBreakdown/TaxBreakdown' diff --git a/libs/application/templates/financial-aid/src/forms/ApplicantSubmitted.ts b/libs/application/templates/financial-aid/src/forms/ApplicantSubmitted.ts new file mode 100644 index 000000000000..3fc819cb5f4b --- /dev/null +++ b/libs/application/templates/financial-aid/src/forms/ApplicantSubmitted.ts @@ -0,0 +1,64 @@ +import { + buildCustomField, + buildForm, + buildMultiField, + buildSubmitField, +} from '@island.is/application/core' +import { DefaultEvents, Form } from '@island.is/application/types' +import { Routes } from '../lib/constants' + +import * as m from '../lib/messages' + +export const ApplicantSubmitted: Form = buildForm({ + id: 'FinancialAidApplication', + title: m.status.sectionTitle, + children: [ + buildMultiField({ + id: Routes.APPLICANTSTATUS, + title: m.status.pageTitle, + children: [ + buildCustomField({ + id: Routes.APPLICANTSTATUS, + title: m.status.pageTitle, + component: 'ApplicantStatus', + }), + // Empty submit field to hide all buttons in the footer + buildSubmitField({ + id: '', + title: '', + actions: [], + }), + ], + }), + buildMultiField({ + id: Routes.MISSINGFILES, + title: m.missingFiles.general.pageTitle, + children: [ + buildCustomField( + { + id: Routes.MISSINGFILES, + title: m.missingFiles.general.pageTitle, + component: 'MissingFiles', + }, + { isSpouse: false }, + ), + buildSubmitField({ + id: 'missingFilesSubmit', + title: '', + actions: [ + { + event: DefaultEvents.EDIT, + name: m.missingFiles.general.submit, + type: 'primary', + }, + ], + }), + ], + }), + buildCustomField({ + id: Routes.MISSINGFILESCONFIRMATION, + title: m.missingFiles.confirmation.title, + component: 'MissingFilesConfirmation', + }), + ], +}) diff --git a/libs/application/templates/financial-aid/src/forms/ApplicantSubmittedForm/ApplicantStatus.ts b/libs/application/templates/financial-aid/src/forms/ApplicantSubmittedForm/ApplicantStatus.ts deleted file mode 100644 index 69da1fdff3ed..000000000000 --- a/libs/application/templates/financial-aid/src/forms/ApplicantSubmittedForm/ApplicantStatus.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { buildCustomField, buildSection } from '@island.is/application/core' -import { Routes } from '../../lib/constants' -import * as m from '../../lib/messages' - -export const ApplicantStatus = buildSection({ - id: Routes.APPLICANTSTATUS, - title: 'StaĂ°a umsĂłknar', - children: [ - buildCustomField({ - id: Routes.APPLICANTSTATUS, - title: m.status.pageTitle, - component: 'ApplicantStatus', - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/ApplicantSubmittedForm/MissingFiles.ts b/libs/application/templates/financial-aid/src/forms/ApplicantSubmittedForm/MissingFiles.ts deleted file mode 100644 index cfe796a5429b..000000000000 --- a/libs/application/templates/financial-aid/src/forms/ApplicantSubmittedForm/MissingFiles.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { - buildCustomField, - buildMultiField, - buildSection, - buildSubmitField, -} from '@island.is/application/core' -import { Routes } from '../../lib/constants' -import { DefaultEvents } from '@island.is/application/types' -import * as m from '../../lib/messages' - -export const MissingFiles = buildSection({ - id: Routes.MISSINGFILES, - title: m.missingFiles.general.pageTitle, - children: [ - buildMultiField({ - id: Routes.MISSINGFILES, - title: m.missingFiles.general.pageTitle, - description: m.missingFiles.general.description, - children: [ - buildCustomField( - { - id: Routes.MISSINGFILES, - title: m.missingFiles.general.pageTitle, - component: 'MissingFiles', - }, - { isSpouse: false }, - ), - buildSubmitField({ - id: 'missingFilesSubmit', - title: '', - actions: [ - { - event: DefaultEvents.EDIT, - name: m.missingFiles.general.submit, - type: 'primary', - }, - ], - }), - ], - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/ApplicantSubmittedForm/MissingFilesConfirmation.ts b/libs/application/templates/financial-aid/src/forms/ApplicantSubmittedForm/MissingFilesConfirmation.ts deleted file mode 100644 index 1e3c4de51fbe..000000000000 --- a/libs/application/templates/financial-aid/src/forms/ApplicantSubmittedForm/MissingFilesConfirmation.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { - buildCustomField, - buildMessageWithLinkButtonField, - buildMultiField, - buildSection, -} from '@island.is/application/core' -import { Routes } from '../../lib/constants' -import * as m from '../../lib/messages' - -export const MissingFilesConfirmation = buildSection({ - id: Routes.MISSINGFILESCONFIRMATION, - title: m.missingFiles.confirmation.sectionTitle, - children: [ - buildMultiField({ - id: Routes.MISSINGFILESCONFIRMATION, - title: m.missingFiles.confirmation.sectionTitle, - description: m.missingFiles.confirmation.subtitle, - children: [ - buildCustomField({ - id: Routes.MISSINGFILESCONFIRMATION, - title: '', - component: 'MissingFilesConfirmation', - }), - buildMessageWithLinkButtonField({ - id: 'goToServicePortal', - title: '', - url: '/minarsidur/umsoknir', - buttonTitle: m.missingFiles.confirmation.openServicePortalButtonTitle, - message: m.missingFiles.confirmation.openServicePortalMessageText, - marginTop: 6, - }), - ], - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/ApplicantSubmittedForm/index.ts b/libs/application/templates/financial-aid/src/forms/ApplicantSubmittedForm/index.ts deleted file mode 100644 index b1dacf8a8ac3..000000000000 --- a/libs/application/templates/financial-aid/src/forms/ApplicantSubmittedForm/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { buildForm } from '@island.is/application/core' -import { Application, Form } from '@island.is/application/types' -import * as m from '../../lib/messages' -import { createElement } from 'react' -import { Logo } from '../../components/Logo/Logo' -import { ApplicantStatus } from './ApplicantStatus' -import { MissingFiles } from './MissingFiles' -import { MissingFilesConfirmation } from './MissingFilesConfirmation' - -export const ApplicantSubmitted: Form = buildForm({ - id: 'FinancialAidApplication', - title: m.status.sectionTitle, - logo: (application: Application) => { - const logo = createElement(Logo, { application }) - return () => logo - }, - children: [ApplicantStatus, MissingFiles, MissingFilesConfirmation], -}) diff --git a/libs/application/templates/financial-aid/src/forms/Application.ts b/libs/application/templates/financial-aid/src/forms/Application.ts new file mode 100644 index 000000000000..e99e6c4abf1a --- /dev/null +++ b/libs/application/templates/financial-aid/src/forms/Application.ts @@ -0,0 +1,251 @@ +import { + buildCustomField, + buildForm, + buildMultiField, + buildSection, + buildSubmitField, + buildSubSection, + getValueViaPath, +} from '@island.is/application/core' +import { + ApplicantChildCustodyInformation, + DefaultEvents, + Form, + FormModes, +} from '@island.is/application/types' +import { ApproveOptions, ExternalData } from '../lib/types' + +import * as m from '../lib/messages' +import { Routes } from '../lib/constants' + +export const Application: Form = buildForm({ + id: 'FinancialAidApplication', + title: m.application.name, + mode: FormModes.DRAFT, + children: [ + buildSection({ + id: 'personalInterest', + title: m.section.personalInterest, + children: [ + buildSubSection({ + condition: (_, externalData) => + (externalData as unknown as ExternalData).nationalRegistrySpouse + .data != null, + title: m.inRelationship.general.sectionTitle, + id: Routes.INRELATIONSHIP, + children: [ + buildCustomField({ + id: Routes.INRELATIONSHIP, + title: m.inRelationship.general.pageTitle, + component: 'InRelationshipForm', + }), + ], + }), + buildSubSection({ + condition: (_, externalData) => + (externalData as unknown as ExternalData).nationalRegistrySpouse + .data == null, + title: m.unknownRelationship.general.sectionTitle, + id: Routes.UNKNOWNRELATIONSHIP, + children: [ + buildCustomField({ + id: Routes.UNKNOWNRELATIONSHIP, + title: m.unknownRelationship.general.pageTitle, + component: 'UnknownRelationshipForm', + }), + ], + }), + buildSubSection({ + condition: (_, externalData) => { + const childWithInfo = getValueViaPath( + externalData, + 'childrenCustodyInformation.data', + [], + ) as ApplicantChildCustodyInformation[] + + return Boolean(childWithInfo?.length) + }, + id: Routes.CHILDRENSCHOOLINFO, + title: m.childrenForm.general.sectionTitle, + children: [ + buildCustomField({ + id: Routes.CHILDRENSCHOOLINFO, + title: m.childrenForm.general.pageTitle, + component: 'ChildrenForm', + }), + ], + }), + buildSubSection({ + condition: (_, externalData) => { + const childWithInfo = getValueViaPath( + externalData, + 'childrenCustodyInformation.data', + [], + ) as ApplicantChildCustodyInformation[] + + return Boolean(childWithInfo?.length) + }, + id: Routes.CHILDRENFILES, + title: m.childrenFilesForm.general.sectionTitle, + children: [ + buildCustomField({ + id: Routes.CHILDRENFILES, + title: m.childrenFilesForm.general.pageTitle, + component: 'ChildrenFilesForm', + }), + ], + }), + buildSubSection({ + id: Routes.HOMECIRCUMSTANCES, + title: m.homeCircumstancesForm.general.sectionTitle, + children: [ + buildCustomField({ + id: Routes.HOMECIRCUMSTANCES, + title: m.homeCircumstancesForm.general.pageTitle, + component: 'HomeCircumstancesForm', + }), + ], + }), + buildSubSection({ + id: Routes.STUDENT, + title: m.studentForm.general.sectionTitle, + children: [ + buildCustomField({ + id: Routes.STUDENT, + title: m.studentForm.general.pageTitle, + component: 'StudentForm', + }), + ], + }), + buildSubSection({ + id: Routes.EMPLOYMENT, + title: m.employmentForm.general.sectionTitle, + children: [ + buildCustomField({ + id: Routes.EMPLOYMENT, + title: m.employmentForm.general.pageTitle, + component: 'EmploymentForm', + }), + ], + }), + ], + }), + buildSection({ + id: 'finances', + title: m.section.finances, + children: [ + buildSubSection({ + id: Routes.INCOME, + title: m.incomeForm.general.sectionTitle, + children: [ + buildCustomField({ + id: Routes.INCOME, + title: m.incomeForm.general.pageTitle, + component: 'IncomeForm', + }), + ], + }), + buildSubSection({ + condition: (answers) => answers.income === ApproveOptions.Yes, + id: Routes.INCOMEFILES, + title: m.incomeFilesForm.general.sectionTitle, + children: [ + buildCustomField({ + id: Routes.INCOMEFILES, + title: m.incomeFilesForm.general.pageTitle, + component: 'IncomeFilesForm', + }), + ], + }), + buildSubSection({ + condition: (_, externalData) => + (externalData as unknown as ExternalData).taxData?.data + .municipalitiesDirectTaxPayments.success === false || + (externalData as unknown as ExternalData).taxData?.data + ?.municipalitiesPersonalTaxReturn?.personalTaxReturn == null, + id: Routes.TAXRETURNFILES, + title: m.taxReturnForm.general.sectionTitle, + children: [ + buildCustomField({ + id: Routes.TAXRETURNFILES, + title: m.taxReturnForm.general.pageTitle, + component: 'TaxReturnFilesForm', + }), + ], + }), + buildSubSection({ + id: Routes.PERSONALTAXCREDIT, + title: m.personalTaxCreditForm.general.sectionTitle, + children: [ + buildCustomField({ + id: Routes.PERSONALTAXCREDIT, + title: m.personalTaxCreditForm.general.pageTitle, + component: 'PersonalTaxCreditForm', + }), + ], + }), + buildSubSection({ + id: Routes.BANKINFO, + title: m.bankInfoForm.general.sectionTitle, + children: [ + buildCustomField({ + id: Routes.BANKINFO, + title: m.bankInfoForm.general.pageTitle, + component: 'BankInfoForm', + }), + ], + }), + ], + }), + buildSection({ + id: Routes.CONTACTINFO, + title: m.contactInfo.general.sectionTitle, + children: [ + buildCustomField({ + id: Routes.CONTACTINFO, + title: m.contactInfo.general.pageTitle, + component: 'ContactInfo', + }), + ], + }), + buildSection({ + id: Routes.SUMMARY, + title: m.summaryForm.general.sectionTitle, + children: [ + buildMultiField({ + id: Routes.SUMMARY, + title: m.summaryForm.general.pageTitle, + children: [ + buildCustomField({ + id: Routes.SUMMARY, + title: m.summaryForm.general.pageTitle, + component: 'SummaryForm', + }), + buildSubmitField({ + id: 'submitApplication', + title: '', + actions: [ + { + event: DefaultEvents.SUBMIT, + name: m.summaryForm.general.submit, + type: 'primary', + }, + ], + }), + ], + }), + ], + }), + buildSection({ + id: Routes.CONFIRMATION, + title: m.confirmation.general.sectionTitle, + children: [ + buildCustomField({ + id: Routes.CONFIRMATION, + title: m.confirmation.general.pageTitle, + component: 'ApplicantConfirmation', + }), + ], + }), + ], +}) diff --git a/libs/application/templates/financial-aid/src/forms/ApplicationForm/confirmationSection/confirmationMultiField.ts b/libs/application/templates/financial-aid/src/forms/ApplicationForm/confirmationSection/confirmationMultiField.ts deleted file mode 100644 index 452acf7b3bfa..000000000000 --- a/libs/application/templates/financial-aid/src/forms/ApplicationForm/confirmationSection/confirmationMultiField.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { - buildAlertMessageField, - buildCustomField, - buildDescriptionField, - buildImageField, - buildMultiField, - getValueViaPath, -} from '@island.is/application/core' -import { Routes } from '../../../lib/constants' -import * as m from '../../../lib/messages' -import { - getNextStepsDescription, - hasIncomeFiles, - hasSpouse, -} from '../../../lib/utils' -import { Application, FormValue } from '@island.is/application/types' -import { ConfirmationImage } from '../../../assets/ConfirmationImage' -import { getNextPeriod } from '@island.is/financial-aid/shared/lib' - -export const confirmationMultiField = buildMultiField({ - id: Routes.CONFIRMATION, - title: m.confirmation.general.pageTitle, - children: [ - buildAlertMessageField({ - id: 'confirmationAlert-1', - title: m.confirmation.alertMessagesInRelationship.success, - alertType: 'success', - condition: (answers) => { - return !hasIncomeFiles(answers) - }, - }), - buildAlertMessageField({ - id: 'confirmationAlert-2', - title: m.confirmation.alertMessages.dataNeeded, - message: m.confirmation.alertMessages.dataNeededText, - alertType: 'warning', - condition: (answers) => { - return !hasIncomeFiles(answers) - }, - }), - buildAlertMessageField({ - id: 'confirmationAlert-3', - title: m.confirmation.alertMessagesInRelationship.dataNeeded, - message: (application) => { - const spouse = getValueViaPath( - application.externalData, - 'sendSpouseEmail.data.success', - ) - return spouse - ? m.confirmation.alertMessagesInRelationship.dataNeededText - : m.confirmation.alertMessagesInRelationship.dataNeededAlternativeText - }, - alertType: 'warning', - condition: (answers, externalData) => { - return hasSpouse(answers, externalData) - }, - }), - buildDescriptionField({ - id: 'confirmationDescription', - title: m.confirmation.nextSteps.title, - titleVariant: 'h3', - marginBottom: 2, - }), - buildDescriptionField({ - id: 'confirmationDescription', - title: '', - description: (application: Application) => - getNextStepsDescription(application.answers, application.externalData), - }), - buildDescriptionField({ - id: 'confirmationDescriptionBullets', - title: '', - description: () => ({ - ...m.confirmation.nextSteps.content, - values: { nextMonth: getNextPeriod().month }, - }), - marginBottom: 5, - }), - buildCustomField({ - id: 'confirmation', - title: '', - component: 'CopyUrl', - }), - buildDescriptionField({ - id: 'confirmationLinks', - title: m.confirmation.links.title, - titleVariant: 'h3', - description: (application) => { - const url = getValueViaPath( - application.externalData, - 'municipality.data.homepage', - ) - return { - ...m.confirmation.links.content, - values: { - statusPage: - typeof window !== 'undefined' ? window.location.href : '', - homePage: url ?? '', - }, - } - }, - marginTop: 5, - marginBottom: 5, - }), - buildImageField({ - id: 'confirmationImage', - title: '', - image: ConfirmationImage, - marginBottom: 5, - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/ApplicationForm/confirmationSection/index.ts b/libs/application/templates/financial-aid/src/forms/ApplicationForm/confirmationSection/index.ts deleted file mode 100644 index 451ce25c211d..000000000000 --- a/libs/application/templates/financial-aid/src/forms/ApplicationForm/confirmationSection/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { buildSection } from '@island.is/application/core' -import { Routes } from '../../../lib/constants' -import * as m from '../../../lib/messages' -import { confirmationMultiField } from './confirmationMultiField' - -export const confirmationSection = buildSection({ - id: Routes.CONFIRMATION, - title: m.confirmation.general.pageTitle, - children: [confirmationMultiField], -}) diff --git a/libs/application/templates/financial-aid/src/forms/ApplicationForm/contactInfoSection/contactInfoMultiField.ts b/libs/application/templates/financial-aid/src/forms/ApplicationForm/contactInfoSection/contactInfoMultiField.ts deleted file mode 100644 index c4ec09b70ed8..000000000000 --- a/libs/application/templates/financial-aid/src/forms/ApplicationForm/contactInfoSection/contactInfoMultiField.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { buildMultiField, buildTextField } from '@island.is/application/core' -import { Routes } from '../../../lib/constants' -import * as m from '../../../lib/messages' - -export const contactInfoMultiField = buildMultiField({ - id: Routes.CONTACTINFO, - title: m.contactInfo.general.pageTitle, - description: m.contactInfo.general.description, - children: [ - buildTextField({ - id: `${Routes.CONTACTINFO}.email`, - title: m.contactInfo.emailInput.label, - placeholder: m.contactInfo.emailInput.placeholder, - }), - buildTextField({ - id: `${Routes.CONTACTINFO}.phone`, - title: m.contactInfo.phoneInput.label, - placeholder: '000-0000', - format: '###-####', - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/ApplicationForm/contactInfoSection/index.ts b/libs/application/templates/financial-aid/src/forms/ApplicationForm/contactInfoSection/index.ts deleted file mode 100644 index 54dee03e5fc9..000000000000 --- a/libs/application/templates/financial-aid/src/forms/ApplicationForm/contactInfoSection/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { buildSection } from '@island.is/application/core' -import { Routes } from '../../../lib/constants' -import * as m from '../../../lib/messages' -import { contactInfoMultiField } from './contactInfoMultiField' - -export const contactInfoSection = buildSection({ - id: Routes.CONTACTINFO, - title: m.contactInfo.general.sectionTitle, - children: [contactInfoMultiField], -}) diff --git a/libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/bankInfoSubSection.ts b/libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/bankInfoSubSection.ts deleted file mode 100644 index dccf4eedf682..000000000000 --- a/libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/bankInfoSubSection.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { - buildSubSection, - buildMultiField, - buildDescriptionField, - buildBankAccountField, -} from '@island.is/application/core' -import { Routes } from '../../../lib/constants' -import * as m from '../../../lib/messages' - -export const bankInfoSubSection = buildSubSection({ - id: Routes.BANKINFO, - title: m.bankInfoForm.general.sectionTitle, - children: [ - buildMultiField({ - id: Routes.BANKINFO, - title: m.bankInfoForm.general.pageTitle, - description: m.bankInfoForm.general.info, - children: [ - buildBankAccountField({ - id: Routes.BANKINFO, - title: '', - }), - buildDescriptionField({ - id: `${Routes.BANKINFO}.description`, - title: m.bankInfoForm.general.descriptionTitle, - titleVariant: 'h3', - marginTop: 3, - description: m.bankInfoForm.general.description, - }), - ], - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/incomeFileSubSection.ts b/libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/incomeFileSubSection.ts deleted file mode 100644 index 4648c58268e2..000000000000 --- a/libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/incomeFileSubSection.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { - buildCustomField, - buildFileUploadField, - buildMultiField, - buildSubSection, - getValueViaPath, -} from '@island.is/application/core' -import { ApproveOptions } from '../../../lib/types' -import { FILE_SIZE_LIMIT, Routes, UPLOAD_ACCEPT } from '../../../lib/constants' -import * as m from '../../../lib/messages' - -export const incomeFilesSubSection = buildSubSection({ - condition: (answers) => { - const income = getValueViaPath(answers, 'income.type') - - return income === ApproveOptions.Yes - }, - id: Routes.INCOMEFILES, - title: m.incomeFilesForm.general.sectionTitle, - children: [ - buildMultiField({ - id: Routes.INCOMEFILES, - title: m.incomeFilesForm.general.pageTitle, - description: m.incomeFilesForm.general.descriptionTaxSuccess, - children: [ - buildCustomField({ - id: Routes.INCOMEFILES, - title: m.incomeFilesForm.general.pageTitle, - component: 'IncomeFilesForm', - }), - ], - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/incomeSubSection.ts b/libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/incomeSubSection.ts deleted file mode 100644 index c246424e0bb8..000000000000 --- a/libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/incomeSubSection.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { - buildDescriptionField, - buildMultiField, - buildRadioField, - buildSubSection, -} from '@island.is/application/core' -import * as m from '../../../lib/messages' -import { Routes } from '../../../lib/constants' -import { getIncomeOptions } from '../../../lib/utils/getIncomeOptions' - -export const incomeSubSection = buildSubSection({ - id: Routes.INCOME, - title: m.incomeForm.general.sectionTitle, - children: [ - buildMultiField({ - id: Routes.INCOME, - title: m.incomeForm.general.pageTitle, - children: [ - buildRadioField({ - id: `${Routes.INCOME}.type`, - title: '', - width: 'half', - options: getIncomeOptions(), - }), - buildDescriptionField({ - id: `${Routes.INCOME}.descriptionLeft`, - title: m.incomeForm.bulletList.headline, - titleVariant: 'h3', - marginTop: 3, - description: m.incomeForm.examplesOfIncome.incomeExampleList, - }), - ], - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/index.ts b/libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/index.ts deleted file mode 100644 index 805514527265..000000000000 --- a/libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { buildSection } from '@island.is/application/core' -import * as m from '../../../lib/messages' -import { incomeSubSection } from './incomeSubSection' -import { incomeFilesSubSection } from './incomeFileSubSection' -import { taxReturnFilesSubSection } from './taxReturnFilesSubSection' -import { personalTaxCreditSubSection } from './personalTaxCreditSubSection' -import { bankInfoSubSection } from './bankInfoSubSection' -import { Routes } from '../../../lib/constants' - -export const financesSection = buildSection({ - id: Routes.FINANCES, - title: m.section.finances, - children: [ - incomeSubSection, - incomeFilesSubSection, - taxReturnFilesSubSection, - personalTaxCreditSubSection, - bankInfoSubSection, - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/personalTaxCreditSubSection.ts b/libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/personalTaxCreditSubSection.ts deleted file mode 100644 index 5f52dd95bee7..000000000000 --- a/libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/personalTaxCreditSubSection.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { - buildSubSection, - buildMultiField, - buildRadioField, - buildDescriptionField, -} from '@island.is/application/core' - -import { Routes } from '../../../lib/constants' -import * as m from '../../../lib/messages' -import { getPersonalTaxCreditOptions } from '../../../lib/utils/getPersonalTaxCreditOptions' - -export const personalTaxCreditSubSection = buildSubSection({ - id: Routes.PERSONALTAXCREDIT, - title: m.personalTaxCreditForm.general.sectionTitle, - children: [ - buildMultiField({ - id: Routes.PERSONALTAXCREDIT, - title: m.personalTaxCreditForm.general.pageTitle, - description: m.personalTaxCreditForm.general.recommendedChoice, - children: [ - buildRadioField({ - id: `${Routes.PERSONALTAXCREDIT}.type`, - title: '', - width: 'half', - options: getPersonalTaxCreditOptions(), - }), - buildDescriptionField({ - id: `${Routes.PERSONALTAXCREDIT}.description`, - title: m.personalTaxCreditForm.general.descriptionTitle, - titleVariant: 'h3', - marginTop: 3, - description: m.personalTaxCreditForm.general.description, - }), - ], - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/taxReturnFilesSubSection.ts b/libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/taxReturnFilesSubSection.ts deleted file mode 100644 index b5e179eacb52..000000000000 --- a/libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/taxReturnFilesSubSection.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { - buildCustomField, - buildSubSection, - getValueViaPath, -} from '@island.is/application/core' -import { Routes } from '../../../lib/constants' -import * as m from '../../../lib/messages' - -export const taxReturnFilesSubSection = buildSubSection({ - id: Routes.TAXRETURNFILES, - title: m.taxReturnForm.general.sectionTitle, - condition: (_, externalData) => { - const personalTaxSuccess = getValueViaPath( - externalData, - 'taxData.data.municipalitiesDirectTaxPayments.success', - ) - const personalTaxReturn = getValueViaPath( - externalData, - 'taxData.data.municipalitiesPersonalTaxReturn.personalTaxReturn', - ) - return personalTaxSuccess === false || personalTaxReturn == null - }, - children: [ - buildCustomField({ - id: Routes.TAXRETURNFILES, - title: m.taxReturnForm.general.pageTitle, - component: 'TaxReturnFilesForm', - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/ApplicationForm/index.ts b/libs/application/templates/financial-aid/src/forms/ApplicationForm/index.ts deleted file mode 100644 index d1fe7595bd0e..000000000000 --- a/libs/application/templates/financial-aid/src/forms/ApplicationForm/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { buildForm } from '@island.is/application/core' -import * as m from '../../lib/messages' -import { Application, Form, FormModes } from '@island.is/application/types' -import { financesSection } from './financesSection' -import { contactInfoSection } from './contactInfoSection' -import { summarySection } from './summarySection' -import { confirmationSection } from './confirmationSection' -import { Logo } from '../../components/Logo/Logo' -import { createElement } from 'react' -import { personalInterestSection } from './personalInterestSection' - -export const ApplicationForm: Form = buildForm({ - id: 'FinancialAidApplication', - title: m.application.name, - mode: FormModes.DRAFT, - logo: (application: Application) => { - const logo = createElement(Logo, { application }) - return () => logo - }, - children: [ - personalInterestSection, - financesSection, - contactInfoSection, - summarySection, - confirmationSection, - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/childrenFilesSubSection.ts b/libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/childrenFilesSubSection.ts deleted file mode 100644 index 709c9f211acd..000000000000 --- a/libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/childrenFilesSubSection.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { - buildCustomField, - buildMultiField, - buildSubSection, - getValueViaPath, -} from '@island.is/application/core' -import { Routes } from '../../../lib/constants' -import * as m from '../../../lib/messages' -import { ApplicantChildCustodyInformation } from '@island.is/application/types' - -export const childrenFilesSubSection = buildSubSection({ - condition: (_, externalData) => { - const childWithInfo = getValueViaPath< - Array - >(externalData, 'childrenCustodyInformation.data', []) - - return Boolean(childWithInfo?.length) - }, - id: Routes.CHILDRENFILES, - title: m.childrenFilesForm.general.sectionTitle, - children: [ - buildMultiField({ - title: m.childrenFilesForm.general.pageTitle, - description: m.childrenFilesForm.general.description, - children: [ - buildCustomField({ - id: Routes.CHILDRENFILES, - title: m.childrenFilesForm.general.pageTitle, - component: 'ChildrenFilesForm', - }), - ], - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/childrenSubSection.ts b/libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/childrenSubSection.ts deleted file mode 100644 index 1e4b1a31cffc..000000000000 --- a/libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/childrenSubSection.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { - buildCustomField, - buildDescriptionField, - buildMultiField, - buildSubSection, - buildTextField, - getValueViaPath, -} from '@island.is/application/core' -import { Routes } from '../../../lib/constants' -import * as m from '../../../lib/messages' -import { ApplicantChildCustodyInformation } from '@island.is/application/types' -import { childrenForm } from '../../../lib/messages' -import { SummaryComment } from '../../../lib/types' - -export const childrenSubSection = buildSubSection({ - condition: (_, externalData) => { - const childWithInfo = getValueViaPath< - Array - >(externalData, 'childrenCustodyInformation.data', []) - - return Boolean(childWithInfo?.length) - }, - id: Routes.CHILDRENSCHOOLINFO, - title: m.childrenForm.general.sectionTitle, - children: [ - buildMultiField({ - title: m.childrenForm.general.sectionTitle, - description: childrenForm.general.description, - children: [ - buildDescriptionField({ - id: 'childrenSubsectionDescription', - title: '', - description: childrenForm.page.content, - marginBottom: 4, - }), - buildCustomField({ - id: Routes.CHILDRENSCHOOLINFO, - title: m.childrenForm.general.pageTitle, - component: 'ChildrenForm', - }), - buildDescriptionField({ - id: 'childrenCommentDescription', - title: childrenForm.page.commentTitle, - titleVariant: 'h3', - description: childrenForm.page.commentText, - }), - buildTextField({ - id: SummaryComment.CHILDRENCOMMENT, - title: '', - variant: 'textarea', - placeholder: childrenForm.inputs.commentLabel, - rows: 8, - }), - ], - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/employmentSubSection.ts b/libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/employmentSubSection.ts deleted file mode 100644 index 74d8f2223f04..000000000000 --- a/libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/employmentSubSection.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { - buildMultiField, - buildRadioField, - buildSubSection, - buildTextField, -} from '@island.is/application/core' -import { Routes } from '../../../lib/constants' -import * as m from '../../../lib/messages' -import { getEmploymentOptions } from '../../../lib/utils/getEmploymentOptions' -import { FormValue } from '@island.is/application/types' -import { Employment } from '@island.is/financial-aid/shared/lib' - -export const employmentSubSection = buildSubSection({ - id: Routes.EMPLOYMENT, - title: m.employmentForm.general.sectionTitle, - children: [ - buildMultiField({ - id: Routes.EMPLOYMENT, - title: m.employmentForm.general.pageTitle, - children: [ - buildRadioField({ - id: `${Routes.EMPLOYMENT}.type`, - title: '', - options: getEmploymentOptions(), - }), - buildTextField({ - condition: (answers) => - (answers[Routes.EMPLOYMENT] as FormValue)?.type === - Employment.OTHER, - id: `${Routes.EMPLOYMENT}.custom`, - title: m.input.label, - variant: 'textarea', - rows: 6, - required: true, - }), - ], - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/homeCircumstancesSubSection.ts b/libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/homeCircumstancesSubSection.ts deleted file mode 100644 index ea63e1136aa7..000000000000 --- a/libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/homeCircumstancesSubSection.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { - buildMultiField, - buildRadioField, - buildSubSection, - buildTextField, -} from '@island.is/application/core' -import { Routes } from '../../../lib/constants' -import * as m from '../../../lib/messages' - -import { getHomeCircumstancesOptions } from '../../../lib/utils/getHomeCircumstancesOptions' -import { HomeCircumstances } from '@island.is/financial-aid/shared/lib' -import { FormValue } from '@island.is/application/types' - -export const homeCircumstancesSubSection = buildSubSection({ - id: Routes.HOMECIRCUMSTANCES, - title: m.homeCircumstancesForm.general.sectionTitle, - children: [ - buildMultiField({ - id: Routes.HOMECIRCUMSTANCES, - title: m.homeCircumstancesForm.general.pageTitle, - children: [ - buildRadioField({ - id: `${Routes.HOMECIRCUMSTANCES}.type`, - title: '', - options: getHomeCircumstancesOptions(), - required: true, - }), - buildTextField({ - condition: (answers: FormValue) => - (answers[Routes.HOMECIRCUMSTANCES] as FormValue)?.type === - HomeCircumstances.OTHER, - id: `${Routes.HOMECIRCUMSTANCES}.custom`, - title: m.input.label, - variant: 'textarea', - rows: 6, - }), - ], - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/inARelationshipSubSection.ts b/libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/inARelationshipSubSection.ts deleted file mode 100644 index 07fb93cf609e..000000000000 --- a/libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/inARelationshipSubSection.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { - buildCheckboxField, - buildDescriptionField, - buildMultiField, - buildSubSection, - buildTextField, -} from '@island.is/application/core' -import { Routes } from '../../../lib/constants' -import * as m from '../../../lib/messages' - -export const inARelationshipSubSection = buildSubSection({ - condition: (_, externalData) => - externalData.nationalRegistrySpouse.data !== null, - id: Routes.INRELATIONSHIP, - title: m.inRelationship.general.sectionTitle, - children: [ - buildMultiField({ - title: m.inRelationship.general.sectionTitle, - description: m.inRelationship.general.intro, - children: [ - buildDescriptionField({ - id: 'spouse.description', - title: '', - description: m.inRelationship.general.description, - }), - buildTextField({ - id: 'spouse.email', - title: m.inRelationship.inputs.spouseEmail, - variant: 'email', - required: true, - width: 'full', - placeholder: m.inRelationship.inputs.spouseEmailPlaceholder, - }), - buildCheckboxField({ - id: 'spouse.approveTerms', - title: '', - required: true, - defaultValue: [], - options: [ - { - value: 'yes', - label: m.inRelationship.inputs.checkboxLabel, - }, - ], - }), - ], - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/index.ts b/libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/index.ts deleted file mode 100644 index 5f17d38b3701..000000000000 --- a/libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { buildSection } from '@island.is/application/core' -import * as m from '../../../lib/messages' -import { inARelationshipSubSection } from './inARelationshipSubSection' -import { Routes } from '../../../lib/constants' -import { unknownRelationshipSubSection } from './unknownRelationshipSubSection' -import { childrenSubSection } from './childrenSubSection' -import { childrenFilesSubSection } from './childrenFilesSubSection' -import { homeCircumstancesSubSection } from './homeCircumstancesSubSection' -import { studentSubSection } from './studentSubSection' -import { employmentSubSection } from './employmentSubSection' - -export const personalInterestSection = buildSection({ - id: Routes.PERSONALINTEREST, - title: m.section.personalInterest, - children: [ - inARelationshipSubSection, - unknownRelationshipSubSection, - childrenSubSection, - childrenFilesSubSection, - homeCircumstancesSubSection, - studentSubSection, - employmentSubSection, - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/studentSubSection.ts b/libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/studentSubSection.ts deleted file mode 100644 index 950c0b44fe25..000000000000 --- a/libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/studentSubSection.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { - buildMultiField, - buildRadioField, - buildSubSection, - buildTextField, -} from '@island.is/application/core' -import { Routes } from '../../../lib/constants' -import * as m from '../../../lib/messages' -import { getStudentOptions } from '../../../lib/utils/getStudentOptions' -import { FormValue } from '@island.is/application/types' -import { ApproveOptions } from '../../../lib/types' - -export const studentSubSection = buildSubSection({ - id: Routes.STUDENT, - title: m.studentForm.general.sectionTitle, - children: [ - buildMultiField({ - id: Routes.STUDENT, - title: m.studentForm.general.pageTitle, - children: [ - buildRadioField({ - id: `${Routes.STUDENT}.isStudent`, - title: '', - options: getStudentOptions(), - }), - buildTextField({ - condition: (answers) => - (answers[Routes.STUDENT] as FormValue)?.isStudent === - ApproveOptions.Yes, - id: `${Routes.STUDENT}.custom`, - title: m.studentForm.input.label, - placeholder: m.studentForm.input.example, - required: true, - }), - ], - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/unknownRelationshipSubSection.ts b/libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/unknownRelationshipSubSection.ts deleted file mode 100644 index 0a68eb415e8f..000000000000 --- a/libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/unknownRelationshipSubSection.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { - buildCheckboxField, - buildDescriptionField, - buildMultiField, - buildRadioField, - buildSubSection, - buildTextField, - getValueViaPath, -} from '@island.is/application/core' -import { Routes } from '../../../lib/constants' -import * as m from '../../../lib/messages' -import { getUnknownRelationshipOptions } from '../../../lib/utils/getUnknownRelationshipOptions' -import { FormValue } from '@island.is/application/types' -import { ApproveOptions } from '../../../lib/types' - -export const unknownRelationshipSubSection = buildSubSection({ - condition: (_, externalData) => { - const spouseData = getValueViaPath( - externalData, - 'nationalRegistrySpouse.data', - ) - return spouseData == null - }, - title: m.unknownRelationship.general.sectionTitle, - id: Routes.UNKNOWNRELATIONSHIP, - children: [ - buildMultiField({ - id: Routes.UNKNOWNRELATIONSHIP, - title: m.unknownRelationship.general.pageTitle, - description: m.unknownRelationship.general.intro, - children: [ - buildDescriptionField({ - id: `${Routes.UNKNOWNRELATIONSHIP}.description`, - title: '', - description: m.unknownRelationship.general.description, - marginBottom: 3, - }), - buildRadioField({ - id: 'relationshipStatus.unregisteredCohabitation', - title: m.unknownRelationship.form.title, - options: getUnknownRelationshipOptions(), - }), - buildTextField({ - condition: (answers) => - (answers.relationshipStatus as FormValue) - ?.unregisteredCohabitation === ApproveOptions.Yes, - id: 'relationshipStatus.spouseNationalId', - title: m.unknownRelationship.inputs.spouseNationalId, - placeholder: m.unknownRelationship.inputs.spouseNationalIdPlaceholder, - format: '######-####', - }), - buildTextField({ - condition: (answers) => - (answers.relationshipStatus as FormValue) - ?.unregisteredCohabitation === ApproveOptions.Yes, - id: 'relationshipStatus.spouseEmail', - variant: 'email', - title: m.unknownRelationship.inputs.spouseEmail, - placeholder: m.unknownRelationship.inputs.spouseEmailPlaceholder, - }), - buildCheckboxField({ - condition: (answers) => - (answers.relationshipStatus as FormValue) - ?.unregisteredCohabitation === ApproveOptions.Yes, - id: 'relationshipStatus.spouseApproveTerms', - title: '', - options: [ - { value: 'yes', label: m.unknownRelationship.inputs.checkboxLabel }, - ], - }), - ], - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/ApplicationForm/summarySection/index.ts b/libs/application/templates/financial-aid/src/forms/ApplicationForm/summarySection/index.ts deleted file mode 100644 index f7b3ea045af6..000000000000 --- a/libs/application/templates/financial-aid/src/forms/ApplicationForm/summarySection/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { buildSection } from '@island.is/application/core' -import { Routes } from '../../../lib/constants' -import * as m from '../../../lib/messages' -import { summaryMultiField } from './summaryMultiField' - -export const summarySection = buildSection({ - id: Routes.SUMMARY, - title: m.summaryForm.general.sectionTitle, - children: [summaryMultiField], -}) diff --git a/libs/application/templates/financial-aid/src/forms/ApplicationForm/summarySection/summaryMultiField.ts b/libs/application/templates/financial-aid/src/forms/ApplicationForm/summarySection/summaryMultiField.ts deleted file mode 100644 index 8bc51bde59a5..000000000000 --- a/libs/application/templates/financial-aid/src/forms/ApplicationForm/summarySection/summaryMultiField.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { - buildCustomField, - buildMultiField, - buildSubmitField, -} from '@island.is/application/core' -import { Routes } from '../../../lib/constants' -import * as m from '../../../lib/messages' -import { DefaultEvents } from '@island.is/application/types' - -export const summaryMultiField = buildMultiField({ - id: Routes.SUMMARY, - title: m.summaryForm.general.pageTitle, - children: [ - buildCustomField({ - id: Routes.SUMMARY, - title: m.summaryForm.general.pageTitle, - component: 'SummaryForm', - }), - buildSubmitField({ - id: 'submitApplication', - title: '', - actions: [ - { - event: DefaultEvents.SUBMIT, - name: m.summaryForm.general.submit, - type: 'primary', - }, - ], - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/MuncipalityNotRegistered.ts b/libs/application/templates/financial-aid/src/forms/MuncipalityNotRegistered.ts new file mode 100644 index 000000000000..7c6b016c59f6 --- /dev/null +++ b/libs/application/templates/financial-aid/src/forms/MuncipalityNotRegistered.ts @@ -0,0 +1,17 @@ +import { buildCustomField, buildForm } from '@island.is/application/core' +import { Form } from '@island.is/application/types' +import { Routes } from '../lib/constants' + +import * as m from '../lib/messages' + +export const MuncipalityNotRegistered: Form = buildForm({ + id: 'FinancialAidApplication', + title: '', + children: [ + buildCustomField({ + id: Routes.SERVICECENTER, + title: m.serviceCenter.general.pageTitle, + component: 'ServiceCenter', + }), + ], +}) diff --git a/libs/application/templates/financial-aid/src/forms/MunicipalityNotRegisteredForm/MunicipalityNotRegistered.ts b/libs/application/templates/financial-aid/src/forms/MunicipalityNotRegisteredForm/MunicipalityNotRegistered.ts deleted file mode 100644 index b85092d18df8..000000000000 --- a/libs/application/templates/financial-aid/src/forms/MunicipalityNotRegisteredForm/MunicipalityNotRegistered.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { - buildDescriptionField, - buildForm, - buildLinkField, - buildMultiField, -} from '@island.is/application/core' -import { Application, Form } from '@island.is/application/types' -import { Routes } from '../../lib/constants' -import * as m from '../../lib/messages' -import { createElement } from 'react' -import { Logo } from '../../components/Logo/Logo' -import { getApplicantsServiceCenter } from '../../lib/utils/getApplicantsServiceCenter' - -export const MunicipalityNotRegistered: Form = buildForm({ - id: 'FinancialAidApplication', - title: '', - logo: (application: Application) => { - const logo = createElement(Logo, { application }) - return () => logo - }, - children: [ - buildMultiField({ - id: Routes.SERVICECENTER, - title: m.serviceCenter.general.pageTitle, - description: (application) => { - const applicantsServiceCenter = getApplicantsServiceCenter(application) - return { - ...m.serviceCenter.general.description, - values: { applicantsServiceCenter: applicantsServiceCenter?.name }, - } - }, - children: [ - buildDescriptionField({ - id: `${Routes.SERVICECENTER}-description`, - title: '', - description: m.serviceCenter.general.notRegistered, - }), - buildLinkField({ - id: `${Routes.SERVICECENTER}-link`, - title: (application) => { - const applicantsServiceCenter = - getApplicantsServiceCenter(application) - return { - ...m.serviceCenter.general.linkToServiceCenter, - values: { - applicantsServiceCenter: applicantsServiceCenter?.name, - }, - } - }, - iconProps: { icon: 'open' }, - link: (application) => { - const applicantsServiceCenter = - getApplicantsServiceCenter(application) - - return applicantsServiceCenter?.link - }, - }), - ], - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/Prerequisites.ts b/libs/application/templates/financial-aid/src/forms/Prerequisites.ts new file mode 100644 index 000000000000..b90625f5965f --- /dev/null +++ b/libs/application/templates/financial-aid/src/forms/Prerequisites.ts @@ -0,0 +1,115 @@ +import { + buildCustomField, + buildDataProviderItem, + buildExternalDataProvider, + buildForm, + buildMultiField, + buildSection, + buildSubmitField, +} from '@island.is/application/core' +import { DefaultEvents, Form, FormModes } from '@island.is/application/types' + +import * as m from '../lib/messages' +import { Routes } from '../lib/constants' +import { + CurrentApplicationApi, + NationalRegistryUserApi, + NationalRegistrySpouseApi, + ChildrenCustodyInformationApi, + MunicipalityApi, + TaxDataApi, +} from '../dataProviders' + +export const Prerequisites: Form = buildForm({ + id: 'FinancialAidApplication', + title: m.application.name, + mode: FormModes.DRAFT, + children: [ + buildSection({ + id: 'externalData', + title: m.section.dataGathering, + children: [ + buildExternalDataProvider({ + title: m.externalData.general.pageTitle, + id: 'approveExternalData', + subTitle: m.externalData.general.subTitle, + description: m.externalData.general.description, + checkboxLabel: m.externalData.general.checkboxLabel, + dataProviders: [ + buildDataProviderItem({ + provider: NationalRegistryUserApi, + title: m.externalData.applicant.title, + subTitle: m.externalData.applicant.subTitle, + }), + buildDataProviderItem({ + provider: NationalRegistrySpouseApi, + title: '', + subTitle: '', + }), + buildDataProviderItem({ + provider: ChildrenCustodyInformationApi, + title: '', + subTitle: '', + }), + buildDataProviderItem({ + provider: MunicipalityApi, + title: '', + subTitle: '', + }), + buildDataProviderItem({ + provider: CurrentApplicationApi, + title: '', + subTitle: '', + }), + buildDataProviderItem({ + provider: TaxDataApi, + title: m.externalData.taxData.title, + subTitle: m.externalData.taxData.dataInfo, + }), + buildDataProviderItem({ + id: 'moreTaxInfo', + type: undefined, + title: '', + subTitle: m.externalData.taxData.process, + }), + ], + }), + ], + }), + buildSection({ + id: Routes.ACCECPTCONTRACT, + title: m.aboutForm.general.sectionTitle, + children: [ + buildMultiField({ + id: Routes.ACCECPTCONTRACT, + title: m.aboutForm.general.pageTitle, + children: [ + buildCustomField({ + id: Routes.ACCECPTCONTRACT, + title: m.aboutForm.general.pageTitle, + component: 'AboutForm', + }), + buildSubmitField({ + id: 'toDraft', + title: '', + refetchApplicationAfterSubmit: true, + actions: [ + { + event: DefaultEvents.SUBMIT, + name: m.aboutForm.goToApplication.button, + type: 'primary', + }, + ], + }), + ], + }), + // This is here to be able to show submit button on former screen :( :( :( + buildMultiField({ + id: '', + title: '', + children: [], + }), + ], + }), + ], +}) diff --git a/libs/application/templates/financial-aid/src/forms/PrerequisitesForm/externalDataSection.ts b/libs/application/templates/financial-aid/src/forms/PrerequisitesForm/externalDataSection.ts deleted file mode 100644 index 7a842ad82001..000000000000 --- a/libs/application/templates/financial-aid/src/forms/PrerequisitesForm/externalDataSection.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { - buildDataProviderItem, - buildExternalDataProvider, - buildSection, -} from '@island.is/application/core' -import * as m from '../../lib/messages' -import { - ChildrenCustodyInformationApi, - CurrentApplicationApi, - MunicipalityApi, - NationalRegistrySpouseApi, - NationalRegistryUserApi, - TaxDataApi, -} from '../../dataProviders' - -export const externalDataSection = buildSection({ - id: 'externalData', - title: m.section.dataGathering, - children: [ - buildExternalDataProvider({ - title: m.externalData.general.pageTitle, - id: 'approveExternalData', - subTitle: m.externalData.general.subTitle, - description: m.externalData.general.description, - checkboxLabel: m.externalData.general.checkboxLabel, - dataProviders: [ - buildDataProviderItem({ - provider: NationalRegistryUserApi, - title: m.externalData.applicant.title, - subTitle: m.externalData.applicant.subTitle, - }), - buildDataProviderItem({ - provider: NationalRegistrySpouseApi, - title: '', - subTitle: '', - }), - buildDataProviderItem({ - provider: ChildrenCustodyInformationApi, - title: '', - subTitle: '', - }), - buildDataProviderItem({ - provider: MunicipalityApi, - title: '', - subTitle: '', - }), - buildDataProviderItem({ - provider: CurrentApplicationApi, - title: '', - subTitle: '', - }), - buildDataProviderItem({ - provider: TaxDataApi, - title: m.externalData.taxData.title, - subTitle: m.externalData.taxData.dataInfo, - }), - buildDataProviderItem({ - id: 'moreTaxInfo', - type: undefined, - title: '', - subTitle: m.externalData.taxData.process, - }), - ], - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/PrerequisitesForm/index.ts b/libs/application/templates/financial-aid/src/forms/PrerequisitesForm/index.ts deleted file mode 100644 index f9d16abaaec9..000000000000 --- a/libs/application/templates/financial-aid/src/forms/PrerequisitesForm/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { buildForm } from '@island.is/application/core' -import { Application, Form, FormModes } from '@island.is/application/types' -import * as m from '../../lib/messages' -import { externalDataSection } from './externalDataSection' -import { informationSection } from './informationSection' -import { createElement } from 'react' -import { Logo } from '../../components/Logo/Logo' - -export const PrerequisitesForm: Form = buildForm({ - id: 'FinancialAidApplication', - title: m.application.name, - renderLastScreenButton: true, - mode: FormModes.DRAFT, - logo: (application: Application) => { - const logo = createElement(Logo, { application }) - return () => logo - }, - children: [externalDataSection, informationSection], -}) diff --git a/libs/application/templates/financial-aid/src/forms/PrerequisitesForm/informationSection.ts b/libs/application/templates/financial-aid/src/forms/PrerequisitesForm/informationSection.ts deleted file mode 100644 index a435819050ad..000000000000 --- a/libs/application/templates/financial-aid/src/forms/PrerequisitesForm/informationSection.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { - buildAccordionField, - buildDescriptionField, - buildMultiField, - buildSection, - buildSubmitField, - getValueViaPath, -} from '@island.is/application/core' -import * as m from '../../lib/messages' -import { Routes } from '../../lib/constants' -import { DefaultEvents } from '@island.is/application/types' -import { currentMonth } from '@island.is/financial-aid/shared/lib' - -export const informationSection = buildSection({ - id: Routes.ACCECPTCONTRACT, - title: m.aboutForm.general.sectionTitle, - children: [ - buildMultiField({ - id: Routes.ACCECPTCONTRACT, - title: m.aboutForm.general.pageTitle, - children: [ - buildDescriptionField({ - id: `${Routes.ACCECPTCONTRACT}-description`, - title: () => { - return { - ...m.aboutForm.general.description, - values: { currentMonth: currentMonth() }, - } - }, - titleVariant: 'h5', - description: m.aboutForm.bulletList.content, - marginBottom: 5, - }), - buildAccordionField({ - id: `${Routes.ACCECPTCONTRACT}-accordion`, - title: m.privacyPolicyAccordion.general.sectionTitle, - accordionItems: (application) => { - const url = getValueViaPath( - application.externalData, - 'municipality.data.homepage', - ) - return [ - { - itemTitle: m.privacyPolicyAccordion.accordion.title, - itemContent: { - ...m.privacyPolicyAccordion.accordion.about, - values: { - webInfo: url - ? `vefsĂ­Ă°unni [${url}](${url})` - : 'vefsĂ­Ă°u sveitarfĂ©lagsins', - }, - }, - }, - ] - }, - }), - buildSubmitField({ - id: 'toDraft', - title: '', - refetchApplicationAfterSubmit: true, - actions: [ - { - event: DefaultEvents.SUBMIT, - name: m.aboutForm.goToApplication.button, - type: 'primary', - }, - ], - }), - ], - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/PrerequisitesSpouse.ts b/libs/application/templates/financial-aid/src/forms/PrerequisitesSpouse.ts new file mode 100644 index 000000000000..aca037dad8f1 --- /dev/null +++ b/libs/application/templates/financial-aid/src/forms/PrerequisitesSpouse.ts @@ -0,0 +1,88 @@ +import { + buildCustomField, + buildDataProviderItem, + buildExternalDataProvider, + buildForm, + buildMultiField, + buildSection, + buildSubmitField, +} from '@island.is/application/core' +import { DefaultEvents, Form, FormModes } from '@island.is/application/types' + +import * as m from '../lib/messages' +import { Routes } from '../lib/constants' +import { CurrentApplicationApi, TaxDataSpouseApi } from '../dataProviders' + +export const PrerequisitesSpouse: Form = buildForm({ + id: 'FinancialAidApplication', + title: m.application.name, + mode: FormModes.IN_PROGRESS, + children: [ + buildSection({ + id: 'externalDataSpouse', + title: m.section.dataGathering, + children: [ + buildExternalDataProvider({ + title: m.externalData.general.pageTitle, + id: 'approveExternalDataSpouse', + subTitle: m.externalData.general.subTitle, + description: m.externalData.general.description, + checkboxLabel: m.externalData.general.checkboxLabel, + dataProviders: [ + buildDataProviderItem({ + provider: TaxDataSpouseApi, + title: m.externalData.taxData.title, + subTitle: m.externalData.taxData.dataInfo, + }), + buildDataProviderItem({ + provider: CurrentApplicationApi, + title: '', + subTitle: '', + }), + buildDataProviderItem({ + id: 'moreTaxInfo', + type: undefined, + title: '', + subTitle: m.externalData.taxData.process, + }), + ], + }), + ], + }), + buildSection({ + id: Routes.SPOUSEACCECPTCONTRACT, + title: m.aboutSpouseForm.general.sectionTitle, + children: [ + buildMultiField({ + id: Routes.SPOUSEACCECPTCONTRACT, + title: m.aboutForm.general.pageTitle, + children: [ + buildCustomField({ + id: Routes.SPOUSEACCECPTCONTRACT, + title: m.aboutSpouseForm.general.pageTitle, + component: 'AboutSpouseForm', + }), + buildSubmitField({ + id: 'toDraft', + title: '', + refetchApplicationAfterSubmit: true, + actions: [ + { + event: DefaultEvents.SUBMIT, + name: m.aboutForm.goToApplication.button, + type: 'primary', + }, + ], + }), + ], + }), + // This is here to be able to show submit button on former screen :( :( :( + buildMultiField({ + id: '', + title: '', + children: [], + }), + ], + }), + ], +}) diff --git a/libs/application/templates/financial-aid/src/forms/PrerequisitesSpouseForm/index.ts b/libs/application/templates/financial-aid/src/forms/PrerequisitesSpouseForm/index.ts deleted file mode 100644 index d05efe453597..000000000000 --- a/libs/application/templates/financial-aid/src/forms/PrerequisitesSpouseForm/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { buildForm } from '@island.is/application/core' -import { Application, Form, FormModes } from '@island.is/application/types' -import * as m from '../../lib/messages' -import { prerequisitesSection } from './prerequisitesSection' -import { informationSection } from './informationSection' -import { createElement } from 'react' -import { Logo } from '../../components/Logo/Logo' - -export const PrerequisitesSpouseForm: Form = buildForm({ - id: 'FinancialAidApplication', - title: m.application.name, - renderLastScreenButton: true, - mode: FormModes.IN_PROGRESS, - logo: (application: Application) => { - const logo = createElement(Logo, { application }) - return () => logo - }, - children: [prerequisitesSection, informationSection], -}) diff --git a/libs/application/templates/financial-aid/src/forms/PrerequisitesSpouseForm/informationSection.ts b/libs/application/templates/financial-aid/src/forms/PrerequisitesSpouseForm/informationSection.ts deleted file mode 100644 index f97d6f60b274..000000000000 --- a/libs/application/templates/financial-aid/src/forms/PrerequisitesSpouseForm/informationSection.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { - buildAccordionField, - buildDescriptionField, - buildMultiField, - buildSection, - buildSubmitField, - getValueViaPath, -} from '@island.is/application/core' -import { Routes } from '../../lib/constants' -import * as m from '../../lib/messages' -import { DefaultEvents } from '@island.is/application/types' -import { - currentMonth, - getNextPeriod, -} from '@island.is/financial-aid/shared/lib' - -export const informationSection = buildSection({ - id: Routes.SPOUSEACCECPTCONTRACT, - title: m.aboutSpouseForm.general.sectionTitle, - children: [ - buildMultiField({ - id: Routes.SPOUSEACCECPTCONTRACT, - title: m.aboutForm.general.pageTitle, - children: [ - buildDescriptionField({ - id: `${Routes.SPOUSEACCECPTCONTRACT}-description`, - title: '', - description: (application) => { - const { externalData } = application - const spouseName = getValueViaPath( - externalData, - 'nationalRegistry.data.fullName', - ) - return { - ...m.aboutSpouseForm.general.description, - values: { - spouseName, - currentMonth: currentMonth(), - nextMonth: getNextPeriod().month, - }, - } - }, - marginBottom: 5, - }), - buildAccordionField({ - id: `${Routes.SPOUSEACCECPTCONTRACT}-accordion`, - title: m.privacyPolicyAccordion.general.sectionTitle, - accordionItems: (application) => { - const url = getValueViaPath( - application.externalData, - 'municipality.data.homepage', - ) - - return [ - { - itemTitle: m.privacyPolicyAccordion.accordion.title, - itemContent: { - ...m.privacyPolicyAccordion.accordion.about, - values: { - webInfo: url - ? `vefsĂ­Ă°unni [${url}](${url})` - : 'vefsĂ­Ă°u sveitarfĂ©lagsins', - }, - }, - }, - ] - }, - }), - buildSubmitField({ - id: 'toDraft', - title: '', - refetchApplicationAfterSubmit: true, - actions: [ - { - event: DefaultEvents.SUBMIT, - name: m.aboutForm.goToApplication.button, - type: 'primary', - }, - ], - }), - ], - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/PrerequisitesSpouseForm/prerequisitesSection.ts b/libs/application/templates/financial-aid/src/forms/PrerequisitesSpouseForm/prerequisitesSection.ts deleted file mode 100644 index c14859c3cacf..000000000000 --- a/libs/application/templates/financial-aid/src/forms/PrerequisitesSpouseForm/prerequisitesSection.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { - buildDataProviderItem, - buildExternalDataProvider, - buildSection, -} from '@island.is/application/core' -import * as m from '../../lib/messages' -import { CurrentApplicationApi, TaxDataSpouseApi } from '../../dataProviders' - -export const prerequisitesSection = buildSection({ - id: 'externalDataSpouse', - title: m.section.dataGathering, - children: [ - buildExternalDataProvider({ - title: m.externalData.general.pageTitle, - id: 'approveExternalDataSpouse', - subTitle: m.externalData.general.subTitle, - description: m.externalData.general.description, - checkboxLabel: m.externalData.general.checkboxLabel, - dataProviders: [ - buildDataProviderItem({ - provider: TaxDataSpouseApi, - title: m.externalData.taxData.title, - subTitle: m.externalData.taxData.dataInfo, - }), - buildDataProviderItem({ - provider: CurrentApplicationApi, - title: '', - subTitle: '', - }), - buildDataProviderItem({ - id: 'moreTaxInfo', - type: undefined, - title: '', - subTitle: m.externalData.taxData.process, - }), - ], - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/Spouse.ts b/libs/application/templates/financial-aid/src/forms/Spouse.ts new file mode 100644 index 000000000000..afa35aeed8bc --- /dev/null +++ b/libs/application/templates/financial-aid/src/forms/Spouse.ts @@ -0,0 +1,109 @@ +import { + buildCustomField, + buildForm, + buildMultiField, + buildSection, + buildSubmitField, +} from '@island.is/application/core' +import { DefaultEvents, Form, FormModes } from '@island.is/application/types' + +import * as m from '../lib/messages' +import { ApproveOptions, ExternalData } from '../lib/types' +import { Routes } from '../lib/constants' + +export const Spouse: Form = buildForm({ + id: 'FinancialAidApplication', + title: m.application.name, + mode: FormModes.IN_PROGRESS, + children: [ + buildSection({ + id: Routes.SPOUSEINCOME, + title: m.incomeForm.general.sectionTitle, + children: [ + buildCustomField({ + id: Routes.SPOUSEINCOME, + title: m.incomeForm.general.pageTitle, + component: 'IncomeForm', + }), + ], + }), + buildSection({ + condition: (answers) => answers.spouseIncome === ApproveOptions.Yes, + id: Routes.SPOUSEINCOMEFILES, + title: m.incomeFilesForm.general.sectionTitle, + children: [ + buildCustomField({ + id: Routes.SPOUSEINCOMEFILES, + title: m.incomeFilesForm.general.pageTitle, + component: 'IncomeFilesForm', + }), + ], + }), + buildSection({ + condition: (_, externalData) => + (externalData as unknown as ExternalData)?.taxDataSpouse?.data + ?.municipalitiesDirectTaxPayments?.success === false || + (externalData as unknown as ExternalData)?.taxDataSpouse?.data + ?.municipalitiesPersonalTaxReturn?.personalTaxReturn == null, + id: Routes.SPOUSETAXRETURNFILES, + title: m.taxReturnForm.general.sectionTitle, + children: [ + buildCustomField({ + id: Routes.SPOUSETAXRETURNFILES, + title: m.taxReturnForm.general.pageTitle, + component: 'TaxReturnFilesForm', + }), + ], + }), + buildSection({ + id: Routes.SPOUSECONTACTINFO, + title: m.contactInfo.general.sectionTitle, + children: [ + buildCustomField({ + id: Routes.SPOUSECONTACTINFO, + title: m.contactInfo.general.pageTitle, + component: 'ContactInfo', + }), + ], + }), + buildSection({ + id: Routes.SPOUSESUMMARY, + title: m.summaryForm.general.sectionTitle, + children: [ + buildMultiField({ + id: Routes.SPOUSESUMMARY, + title: m.summaryForm.general.pageTitle, + children: [ + buildCustomField({ + id: Routes.SPOUSESUMMARY, + title: m.summaryForm.general.pageTitle, + component: 'SpouseSummaryForm', + }), + buildSubmitField({ + id: 'submitApplication', + title: '', + actions: [ + { + event: DefaultEvents.SUBMIT, + name: m.summaryForm.general.submit, + type: 'primary', + }, + ], + }), + ], + }), + ], + }), + buildSection({ + id: Routes.SPOUSECONFIRMATION, + title: m.confirmation.general.sectionTitle, + children: [ + buildCustomField({ + id: Routes.SPOUSECONFIRMATION, + title: m.confirmation.general.pageTitle, + component: 'SpouseConfirmation', + }), + ], + }), + ], +}) diff --git a/libs/application/templates/financial-aid/src/forms/SpouseForm/index.ts b/libs/application/templates/financial-aid/src/forms/SpouseForm/index.ts deleted file mode 100644 index 0c813f8f5ed6..000000000000 --- a/libs/application/templates/financial-aid/src/forms/SpouseForm/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { buildForm } from '@island.is/application/core' -import { Application, Form, FormModes } from '@island.is/application/types' -import * as m from '../../lib/messages' -import { spouseIncomeSection } from './spouseIncomeSection' -import { spouseIncomeFilesSection } from './spouseIncomeFilesSection' -import { spouseTaxReturnFilesSection } from './spouseTaxReturnFilesSection' -import { spouseContactInfoSection } from './spouseContactInfoSection' -import { spouseSummarySection } from './spouseSumarySection' -import { spouseConfirmationSection } from './spouseConfirmationSection' -import { createElement } from 'react' -import { Logo } from '../../components/Logo/Logo' - -export const spouseForm: Form = buildForm({ - id: 'FinancialAidApplication', - title: m.application.name, - mode: FormModes.IN_PROGRESS, - logo: (application: Application) => { - const logo = createElement(Logo, { application }) - return () => logo - }, - children: [ - spouseIncomeSection, - spouseIncomeFilesSection, - spouseTaxReturnFilesSection, - spouseContactInfoSection, - spouseSummarySection, - spouseConfirmationSection, - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/SpouseForm/spouseConfirmationSection.ts b/libs/application/templates/financial-aid/src/forms/SpouseForm/spouseConfirmationSection.ts deleted file mode 100644 index 76ff837b9908..000000000000 --- a/libs/application/templates/financial-aid/src/forms/SpouseForm/spouseConfirmationSection.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { - buildAlertMessageField, - buildDescriptionField, - buildImageField, - buildMultiField, - buildSection, -} from '@island.is/application/core' -import * as m from '../../lib/messages' -import { Routes } from '../../lib/constants' -import { ConfirmationImage } from '../../assets/ConfirmationImage' -import { Application, FormValue } from '@island.is/application/types' -import { - getSpouseNextStepsDescription, - hasSpouseIncomeFiles, -} from '../../lib/utils' -import { getNextPeriod } from '@island.is/financial-aid/shared/lib' - -export const spouseConfirmationSection = buildSection({ - id: Routes.SPOUSECONFIRMATION, - title: m.confirmation.general.sectionTitle, - children: [ - buildMultiField({ - id: Routes.SPOUSECONFIRMATION, - title: m.confirmation.general.pageTitle, - children: [ - buildAlertMessageField({ - id: 'confirmationAlert', - title: m.confirmation.alertMessages.success, - alertType: 'success', - condition: (formValue) => { - return hasSpouseIncomeFiles(formValue) - }, - }), - buildAlertMessageField({ - id: 'confirmationAlert', - title: m.confirmation.alertMessages.dataNeeded, - message: m.confirmation.alertMessages.dataNeededText, - alertType: 'warning', - condition: (formValue) => { - return !hasSpouseIncomeFiles(formValue) - }, - }), - buildDescriptionField({ - id: 'confirmationDescription', - title: m.confirmation.nextSteps.title, - titleVariant: 'h3', - marginBottom: 2, - }), - buildDescriptionField({ - id: 'confirmationDescriptionFirstBullet', - title: '', - description: (formValue: Application) => - getSpouseNextStepsDescription( - formValue.answers, - formValue.externalData, - ), - }), - buildDescriptionField({ - id: 'confirmationDescriptionBullets', - title: '', - description: () => { - return { - ...m.confirmation.nextSteps.content, - values: { - nextMonth: getNextPeriod().month, - }, - } - }, - marginBottom: 5, - }), - buildDescriptionField({ - id: 'confirmationLinks', - title: m.confirmation.links.title, - titleVariant: 'h3', - description: m.confirmation.links.content, - marginTop: 5, - marginBottom: 5, - }), - buildImageField({ - id: 'confirmationImage', - title: '', - image: ConfirmationImage, - marginBottom: 5, - }), - ], - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/SpouseForm/spouseContactInfoSection.ts b/libs/application/templates/financial-aid/src/forms/SpouseForm/spouseContactInfoSection.ts deleted file mode 100644 index f78bdbcb624c..000000000000 --- a/libs/application/templates/financial-aid/src/forms/SpouseForm/spouseContactInfoSection.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { - buildMultiField, - buildSection, - buildTextField, -} from '@island.is/application/core' -import { Routes } from '../../lib/constants' -import * as m from '../../lib/messages' - -export const spouseContactInfoSection = buildSection({ - id: Routes.SPOUSECONTACTINFO, - title: m.contactInfo.general.sectionTitle, - children: [ - buildMultiField({ - id: Routes.SPOUSECONTACTINFO, - title: m.contactInfo.general.pageTitle, - description: m.contactInfo.general.description, - children: [ - buildTextField({ - id: `${Routes.SPOUSECONTACTINFO}.email`, - title: m.contactInfo.emailInput.label, - placeholder: m.contactInfo.emailInput.placeholder, - }), - buildTextField({ - id: `${Routes.SPOUSECONTACTINFO}.phone`, - title: m.contactInfo.phoneInput.label, - placeholder: '000-0000', - format: '###-####', - }), - ], - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/SpouseForm/spouseIncomeFilesSection.ts b/libs/application/templates/financial-aid/src/forms/SpouseForm/spouseIncomeFilesSection.ts deleted file mode 100644 index c595cce82ef0..000000000000 --- a/libs/application/templates/financial-aid/src/forms/SpouseForm/spouseIncomeFilesSection.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { - buildCustomField, - buildSection, - getValueViaPath, -} from '@island.is/application/core' -import { ApproveOptions } from '../../lib/types' -import { Routes } from '../../lib/constants' -import * as m from '../../lib/messages' - -export const spouseIncomeFilesSection = buildSection({ - condition: (answers) => { - const income = getValueViaPath(answers, 'spouseIncome.type') - return income === ApproveOptions.Yes - }, - id: Routes.SPOUSEINCOMEFILES, - title: m.incomeFilesForm.general.sectionTitle, - children: [ - buildCustomField({ - id: Routes.SPOUSEINCOMEFILES, - title: m.incomeFilesForm.general.pageTitle, - component: 'IncomeFilesForm', - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/SpouseForm/spouseIncomeSection.ts b/libs/application/templates/financial-aid/src/forms/SpouseForm/spouseIncomeSection.ts deleted file mode 100644 index 0a9e0d55c351..000000000000 --- a/libs/application/templates/financial-aid/src/forms/SpouseForm/spouseIncomeSection.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { - buildDescriptionField, - buildMultiField, - buildRadioField, - buildSection, -} from '@island.is/application/core' -import * as m from '../../lib/messages' -import { Routes } from '../../lib/constants' -import { getIncomeOptions } from '../../lib/utils/getIncomeOptions' - -export const spouseIncomeSection = buildSection({ - id: Routes.SPOUSEINCOME, - title: m.incomeForm.general.sectionTitle, - children: [ - buildMultiField({ - id: Routes.SPOUSEINCOME, - title: m.incomeForm.general.pageTitle, - children: [ - buildRadioField({ - id: `${Routes.SPOUSEINCOME}.type`, - title: '', - width: 'half', - options: getIncomeOptions(), - }), - buildDescriptionField({ - id: `${Routes.SPOUSEINCOME}.bullets`, - title: m.incomeForm.bulletList.headline, - titleVariant: 'h3', - marginTop: 3, - description: m.incomeForm.examplesOfIncome.incomeExampleList, - }), - ], - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/SpouseForm/spouseSumarySection.ts b/libs/application/templates/financial-aid/src/forms/SpouseForm/spouseSumarySection.ts deleted file mode 100644 index 338fb247c27c..000000000000 --- a/libs/application/templates/financial-aid/src/forms/SpouseForm/spouseSumarySection.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { - buildCustomField, - buildMultiField, - buildSection, - buildSubmitField, -} from '@island.is/application/core' -import * as m from '../../lib/messages' -import { Routes } from '../../lib/constants' -import { DefaultEvents } from '@island.is/application/types' - -export const spouseSummarySection = buildSection({ - id: Routes.SPOUSESUMMARY, - title: m.summaryForm.general.sectionTitle, - children: [ - buildMultiField({ - id: Routes.SPOUSESUMMARY, - title: m.summaryForm.general.pageTitle, - children: [ - buildCustomField({ - id: Routes.SPOUSESUMMARY, - title: m.summaryForm.general.pageTitle, - component: 'SpouseSummaryForm', - }), - buildSubmitField({ - id: 'submitApplication', - title: '', - actions: [ - { - event: DefaultEvents.SUBMIT, - name: m.summaryForm.general.submit, - type: 'primary', - }, - ], - }), - ], - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/SpouseForm/spouseTaxReturnFilesSection.ts b/libs/application/templates/financial-aid/src/forms/SpouseForm/spouseTaxReturnFilesSection.ts deleted file mode 100644 index 20a6b88a1273..000000000000 --- a/libs/application/templates/financial-aid/src/forms/SpouseForm/spouseTaxReturnFilesSection.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { - buildCustomField, - buildDescriptionField, - buildFileUploadField, - buildMultiField, - buildSection, - getValueViaPath, -} from '@island.is/application/core' -import { FILE_SIZE_LIMIT, Routes, UPLOAD_ACCEPT } from '../../lib/constants' -import * as m from '../../lib/messages' -import { ExternalData } from '@island.is/application/types' - -export const spouseTaxReturnFilesSection = buildSection({ - condition: (_, externalData) => { - const spouseTaxSuccess = getValueViaPath( - externalData, - 'taxDataSpouse.data.municipalitiesDirectTaxPayments.success', - ) - const spouseTaxReturn = getValueViaPath( - externalData, - 'taxDataSpouse.data.municipalitiesPersonalTaxReturn.personalTaxReturn', - ) - return spouseTaxSuccess === false || spouseTaxReturn == null - }, - id: Routes.SPOUSETAXRETURNFILES, - title: m.taxReturnForm.general.sectionTitle, - children: [ - buildCustomField({ - id: Routes.SPOUSETAXRETURNFILES, - title: m.taxReturnForm.general.pageTitle, - component: 'TaxReturnFilesForm', - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/SpouseSubmitted.ts b/libs/application/templates/financial-aid/src/forms/SpouseSubmitted.ts new file mode 100644 index 000000000000..a66001769230 --- /dev/null +++ b/libs/application/templates/financial-aid/src/forms/SpouseSubmitted.ts @@ -0,0 +1,64 @@ +import { + buildCustomField, + buildForm, + buildMultiField, + buildSubmitField, +} from '@island.is/application/core' +import { DefaultEvents, Form } from '@island.is/application/types' +import { Routes } from '../lib/constants' + +import * as m from '../lib/messages' + +export const SpouseSubmitted: Form = buildForm({ + id: 'FinancialAidApplication', + title: m.status.sectionTitle, + children: [ + buildMultiField({ + id: Routes.SPOUSESTATUS, + title: m.status.pageTitle, + children: [ + buildCustomField({ + id: Routes.SPOUSESTATUS, + title: m.status.spousePageTitle, + component: 'SpouseStatus', + }), + // Empty submit field to hide all buttons in the footer + buildSubmitField({ + id: '', + title: '', + actions: [], + }), + ], + }), + buildMultiField({ + id: Routes.MISSINGFILES, + title: m.missingFiles.general.pageTitle, + children: [ + buildCustomField( + { + id: Routes.MISSINGFILES, + title: m.missingFiles.general.pageTitle, + component: 'MissingFiles', + }, + { isSpouse: true }, + ), + buildSubmitField({ + id: 'missingFilesSubmit', + title: '', + actions: [ + { + event: DefaultEvents.EDIT, + name: m.missingFiles.general.submit, + type: 'primary', + }, + ], + }), + ], + }), + buildCustomField({ + id: Routes.MISSINGFILESCONFIRMATION, + title: m.missingFiles.confirmation.title, + component: 'MissingFilesConfirmation', + }), + ], +}) diff --git a/libs/application/templates/financial-aid/src/forms/SpouseSubmittedForm/MissingFiles.ts b/libs/application/templates/financial-aid/src/forms/SpouseSubmittedForm/MissingFiles.ts deleted file mode 100644 index 7fc1b6835e58..000000000000 --- a/libs/application/templates/financial-aid/src/forms/SpouseSubmittedForm/MissingFiles.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { - buildCustomField, - buildDescriptionField, - buildFileUploadField, - buildMultiField, - buildSection, - buildSubmitField, - buildTextField, -} from '@island.is/application/core' -import { FILE_SIZE_LIMIT, Routes, UPLOAD_ACCEPT } from '../../lib/constants' -import { DefaultEvents } from '@island.is/application/types' -import * as m from '../../lib/messages' - -export const MissingFiles = buildSection({ - id: Routes.MISSINGFILESSPOUSE, - title: m.missingFiles.general.pageTitle, - children: [ - buildMultiField({ - id: Routes.MISSINGFILESSPOUSE, - title: m.missingFiles.general.pageTitle, - description: m.missingFiles.general.description, - children: [ - buildCustomField( - { - id: Routes.MISSINGFILES, - title: m.missingFiles.general.pageTitle, - component: 'MissingFiles', - }, - { isSpouse: true }, - ), - buildSubmitField({ - id: 'missingFilesSubmit', - title: '', - actions: [ - { - event: DefaultEvents.EDIT, - name: m.missingFiles.general.submit, - type: 'primary', - }, - ], - }), - ], - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/SpouseSubmittedForm/MissingFilesConfirmation.ts b/libs/application/templates/financial-aid/src/forms/SpouseSubmittedForm/MissingFilesConfirmation.ts deleted file mode 100644 index f2e55cc6a8b4..000000000000 --- a/libs/application/templates/financial-aid/src/forms/SpouseSubmittedForm/MissingFilesConfirmation.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { - buildCustomField, - buildMessageWithLinkButtonField, - buildMultiField, - buildSection, -} from '@island.is/application/core' -import { Routes } from '../../lib/constants' -import * as m from '../../lib/messages' - -export const MissingFilesConfirmation = buildSection({ - id: Routes.MISSINGFILESCONFIRMATION, - title: m.missingFiles.confirmation.sectionTitle, - children: [ - buildMultiField({ - id: Routes.SPOUSESTATUS, - title: m.missingFiles.confirmation.sectionTitle, - description: m.missingFiles.confirmation.subtitle, - children: [ - buildCustomField( - { - id: Routes.SPOUSESTATUS, - title: '', - component: 'MissingFilesConfirmation', - }, - { isSpouse: true }, - ), - buildMessageWithLinkButtonField({ - id: 'goToServicePortal', - title: '', - url: '/minarsidur/umsoknir', - buttonTitle: m.missingFiles.confirmation.openServicePortalButtonTitle, - message: m.missingFiles.confirmation.openServicePortalMessageText, - marginTop: 6, - }), - ], - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/SpouseSubmittedForm/SpouseStatus.ts b/libs/application/templates/financial-aid/src/forms/SpouseSubmittedForm/SpouseStatus.ts deleted file mode 100644 index 18f7993df8b0..000000000000 --- a/libs/application/templates/financial-aid/src/forms/SpouseSubmittedForm/SpouseStatus.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { buildCustomField, buildSection } from '@island.is/application/core' -import { Routes } from '../../lib/constants' -import * as m from '../../lib/messages' - -export const SpouseStatus = buildSection({ - id: Routes.SPOUSESTATUS, - title: m.status.pageTitle, - children: [ - buildCustomField({ - id: Routes.SPOUSESTATUS, - title: m.status.spousePageTitle, - component: 'SpouseStatus', - }), - ], -}) diff --git a/libs/application/templates/financial-aid/src/forms/SpouseSubmittedForm/index.ts b/libs/application/templates/financial-aid/src/forms/SpouseSubmittedForm/index.ts deleted file mode 100644 index aa76330d23fc..000000000000 --- a/libs/application/templates/financial-aid/src/forms/SpouseSubmittedForm/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { buildForm } from '@island.is/application/core' -import { Application, Form } from '@island.is/application/types' - -import * as m from '../../lib/messages' -import { createElement } from 'react' -import { Logo } from '../../components/Logo/Logo' -import { SpouseStatus } from './SpouseStatus' -import { MissingFiles } from './MissingFiles' -import { MissingFilesConfirmation } from './MissingFilesConfirmation' - -export const SpouseSubmitted: Form = buildForm({ - id: 'FinancialAidApplication', - title: m.status.sectionTitle, - logo: (application: Application) => { - const logo = createElement(Logo, { application }) - return () => logo - }, - children: [SpouseStatus, MissingFiles, MissingFilesConfirmation], -}) diff --git a/libs/application/templates/financial-aid/src/lib/FinancialAidTemplate.ts b/libs/application/templates/financial-aid/src/lib/FinancialAidTemplate.ts index 0c19192d553d..77dae941748a 100644 --- a/libs/application/templates/financial-aid/src/lib/FinancialAidTemplate.ts +++ b/libs/application/templates/financial-aid/src/lib/FinancialAidTemplate.ts @@ -7,16 +7,19 @@ import { DefaultEvents, Application, } from '@island.is/application/types' + import { assign } from 'xstate' + import { Roles, ApplicationStates, ONE_DAY, ONE_MONTH } from './constants' + import { application, stateDescriptions } from './messages' import { dataSchema } from './dataSchema' import { - isMunicipalityNotRegistered, + isMuncipalityNotRegistered, hasActiveCurrentApplication, hasSpouseCheck, } from './utils' -import { FinancialAidAnswers, FinancialAidExternalData } from '..' +import { FAApplication } from '..' import { CreateApplicationApi, CurrentApplicationApi, @@ -62,8 +65,8 @@ const FinancialAidTemplate: ApplicationTemplate< { id: Roles.APPLICANT, formLoader: () => - import('../forms/PrerequisitesForm').then((module) => - Promise.resolve(module.PrerequisitesForm), + import('../forms/Prerequisites').then((module) => + Promise.resolve(module.Prerequisites), ), write: 'all', delete: true, @@ -81,8 +84,8 @@ const FinancialAidTemplate: ApplicationTemplate< on: { SUBMIT: [ { - target: ApplicationStates.MUNICIPALITYNOTREGISTERED, - cond: isMunicipalityNotRegistered, + target: ApplicationStates.MUNCIPALITYNOTREGISTERED, + cond: isMuncipalityNotRegistered, }, { target: ApplicationStates.SUBMITTED, @@ -106,8 +109,8 @@ const FinancialAidTemplate: ApplicationTemplate< { id: Roles.APPLICANT, formLoader: () => - import('../forms/ApplicationForm').then((module) => - Promise.resolve(module.ApplicationForm), + import('../forms/Application').then((module) => + Promise.resolve(module.Application), ), read: 'all', write: 'all', @@ -139,8 +142,8 @@ const FinancialAidTemplate: ApplicationTemplate< { id: Roles.SPOUSE, formLoader: () => - import('../forms/PrerequisitesSpouseForm').then((module) => - Promise.resolve(module.PrerequisitesSpouseForm), + import('../forms/PrerequisitesSpouse').then((module) => + Promise.resolve(module.PrerequisitesSpouse), ), read: 'all', write: 'all', @@ -149,7 +152,7 @@ const FinancialAidTemplate: ApplicationTemplate< { id: Roles.APPLICANT, formLoader: () => - import('../forms/ApplicantSubmittedForm').then((module) => + import('../forms/ApplicantSubmitted').then((module) => Promise.resolve(module.ApplicantSubmitted), ), read: 'all', @@ -163,8 +166,8 @@ const FinancialAidTemplate: ApplicationTemplate< target: ApplicationStates.SUBMITTED, cond: hasActiveCurrentApplication, }, + { target: ApplicationStates.SPOUSE }, ], - EDIT: { target: ApplicationStates.SPOUSE }, }, }, [ApplicationStates.SPOUSE]: { @@ -179,8 +182,8 @@ const FinancialAidTemplate: ApplicationTemplate< { id: Roles.SPOUSE, formLoader: () => - import('../forms/SpouseForm').then((module) => - Promise.resolve(module.spouseForm), + import('../forms/Spouse').then((module) => + Promise.resolve(module.Spouse), ), read: 'all', write: 'all', @@ -188,7 +191,7 @@ const FinancialAidTemplate: ApplicationTemplate< { id: Roles.APPLICANT, formLoader: () => - import('../forms/ApplicantSubmittedForm').then((module) => + import('../forms/ApplicantSubmitted').then((module) => Promise.resolve(module.ApplicantSubmitted), ), read: 'all', @@ -213,7 +216,7 @@ const FinancialAidTemplate: ApplicationTemplate< { id: Roles.APPLICANT, formLoader: () => - import('../forms/ApplicantSubmittedForm').then((module) => + import('../forms/ApplicantSubmitted').then((module) => Promise.resolve(module.ApplicantSubmitted), ), read: 'all', @@ -222,7 +225,7 @@ const FinancialAidTemplate: ApplicationTemplate< { id: Roles.SPOUSE, formLoader: () => - import('../forms/SpouseSubmittedForm').then((module) => + import('../forms/SpouseSubmitted').then((module) => Promise.resolve(module.SpouseSubmitted), ), read: 'all', @@ -234,7 +237,7 @@ const FinancialAidTemplate: ApplicationTemplate< EDIT: { target: ApplicationStates.SUBMITTED }, }, }, - [ApplicationStates.MUNICIPALITYNOTREGISTERED]: { + [ApplicationStates.MUNCIPALITYNOTREGISTERED]: { meta: { name: application.name.defaultMessage, status: 'rejected', @@ -247,10 +250,8 @@ const FinancialAidTemplate: ApplicationTemplate< { id: Roles.APPLICANT, formLoader: () => - import( - '../forms/MunicipalityNotRegisteredForm/MunicipalityNotRegistered' - ).then((module) => - Promise.resolve(module.MunicipalityNotRegistered), + import('../forms/MuncipalityNotRegistered').then((module) => + Promise.resolve(module.MuncipalityNotRegistered), ), read: 'all', }, @@ -262,13 +263,11 @@ const FinancialAidTemplate: ApplicationTemplate< stateMachineOptions: { actions: { assignToSpouse: assign((context) => { - const { externalData, answers } = context.application - const answersSchema = answers as unknown as FinancialAidAnswers - const externalDataSchema = - externalData as unknown as FinancialAidExternalData + const { externalData, answers } = + context.application as unknown as FAApplication const spouse = - externalDataSchema.nationalRegistrySpouse.data?.nationalId || - answersSchema.relationshipStatus.spouseNationalId + externalData.nationalRegistrySpouse.data?.nationalId || + answers.relationshipStatus.spouseNationalId if (spouse) { return { diff --git a/libs/application/templates/financial-aid/src/lib/constants.ts b/libs/application/templates/financial-aid/src/lib/constants.ts index 07f2a51a5bad..a41797cc3785 100644 --- a/libs/application/templates/financial-aid/src/lib/constants.ts +++ b/libs/application/templates/financial-aid/src/lib/constants.ts @@ -4,7 +4,7 @@ export enum ApplicationStates { SUBMITTED = 'submitted', SPOUSE = 'spouse', PREREQUISITESSPOUSE = 'prerequisitesSpouse', - MUNICIPALITYNOTREGISTERED = 'municipalityNotRegistered', + MUNCIPALITYNOTREGISTERED = 'muncipalityNotRegistered', } export enum Roles { @@ -16,14 +16,12 @@ export const ONE_MONTH = 24 * 3600 * 1000 * 31 export const ONE_DAY = 24 * 3600 * 1000 export enum Routes { - PERSONALINTEREST = 'personalInterest', ACCECPTCONTRACT = 'acceptContract', INRELATIONSHIP = 'inRelationship', UNKNOWNRELATIONSHIP = 'unknownRelationship', HOMECIRCUMSTANCES = 'homeCircumstances', STUDENT = 'student', EMPLOYMENT = 'employment', - FINANCES = 'finances', INCOME = 'income', PERSONALTAXCREDIT = 'personalTaxCredit', BANKINFO = 'bankInfo', @@ -42,7 +40,6 @@ export enum Routes { SPOUSESUMMARY = 'spouseSummary', SPOUSECONFIRMATION = 'spouseConfirmation', MISSINGFILES = 'missingFiles', - MISSINGFILESSPOUSE = 'missingFilesSpouse', APPLICANTSTATUS = 'applicantStatus', MISSINGFILESCONFIRMATION = 'missingFilesConfirmation', SPOUSESTATUS = 'spouseStatus', @@ -57,6 +54,14 @@ export enum ApiActions { SENDSPOUSEEMAIL = 'sendSpouseEmail', } -export const UPLOAD_ACCEPT = '.pdf, .doc, .docx, .rtf, .jpg, .jpeg, .png, .heic' - +export const UPLOAD_ACCEPT = [ + '.pdf', + '.doc', + '.docx', + '.rtf', + '.jpg', + '.jpeg', + '.png', + '.heic', +] export const FILE_SIZE_LIMIT = 10000000 // 10MB diff --git a/libs/application/templates/financial-aid/src/lib/dataSchema.ts b/libs/application/templates/financial-aid/src/lib/dataSchema.ts index 76990442bfad..fc7ea183384c 100644 --- a/libs/application/templates/financial-aid/src/lib/dataSchema.ts +++ b/libs/application/templates/financial-aid/src/lib/dataSchema.ts @@ -7,167 +7,144 @@ import { import { isValidEmail, isValidNationalId, isValidPhone } from './utils' import { ApproveOptions } from './types' -const fileSchema = z.object({ - name: z.string(), - key: z.string(), - url: z.string().optional(), -}) - -const approveExternalDataSchema = z.boolean().refine((v) => v, { - params: error.validation.dataGathering, -}) - -const spouseSchema = z.object({ - email: z.string().refine((v) => isValidEmail(v), { - params: error.validation.email, +export const dataSchema = z.object({ + approveExternalData: z.boolean().refine((v) => v, { + params: error.validation.dataGathering, }), - approveTerms: z.array(z.string()).refine((v) => v && v.length === 1, { - params: error.validation.approveSpouse, + approveExternalDataSpouse: z.boolean().refine((v) => v, { + params: error.validation.dataGathering, }), -}) - -const childSchoolInfoSchema = z.object({ - fullName: z.string(), - nationalId: z.string(), - school: z.string(), - livesWithApplicant: z.boolean(), - livesWithBothParents: z.boolean(), -}) - -const relationshipStatusScema = z - .object({ - unregisteredCohabitation: z.nativeEnum(ApproveOptions).refine((v) => v, { - params: error.validation.radioErrorMessage, - }), - spouseEmail: z.string().optional(), - spouseNationalId: z.string().optional(), - spouseApproveTerms: z.array(z.string()).optional(), - }) - .refine( - (v) => - v.unregisteredCohabitation === ApproveOptions.Yes - ? v.spouseEmail && - isValidEmail(v.spouseEmail) && - v.spouseNationalId && - isValidNationalId(v.spouseNationalId) && - v.spouseApproveTerms && - v.spouseApproveTerms.length === 1 - : true, - { - //More detailed error messages are in the UnknownRelationshipForm component + spouse: z.object({ + email: z.string().refine((v) => isValidEmail(v), { params: error.validation.email, - }, - ) - -const studentSchema = z - .object({ - isStudent: z - .enum([ApproveOptions.Yes, ApproveOptions.No]) - .refine((v) => v, { - params: error.validation.radioErrorMessage, - }), - custom: z.string().optional(), - }) - .refine((v) => (v.isStudent === ApproveOptions.Yes ? v.custom : true), { - params: error.validation.inputErrorMessage, - }) - -const homeCircumstancesSchema = z - .object({ - type: z.nativeEnum(HomeCircumstances).refine((v) => v, { - params: error.validation.radioErrorMessage, }), - custom: z.string().optional(), - }) - .refine((v) => (v.type === HomeCircumstances.OTHER ? v.custom : true), { - params: error.validation.inputErrorMessage, - path: ['custom'], - }) - -const incomeSchema = z.object({ - type: z.nativeEnum(ApproveOptions).refine((v) => v, { - params: error.validation.radioErrorMessage, + approveTerms: z.array(z.string()).refine((v) => v && v.length === 1, { + params: error.validation.approveSpouse, + }), }), -}) - -const incomeFilesSchema = z - .array(fileSchema) - .refine((v) => v.length > 0, { params: error.validation.missingFiles }) - -const employmentSchema = z - .object({ - type: z.nativeEnum(Employment).refine((v) => v, { - params: error.validation.radioErrorMessage, + childrenSchoolInfo: z.array( + z.object({ + fullName: z.string(), + nationalId: z.string(), + school: z.string(), + livesWithApplicant: z.boolean(), + livesWithBothParents: z.boolean(), }), - custom: z.string().optional(), - }) - .refine((v) => (v.type === Employment.OTHER ? v.custom : true), { - params: error.validation.inputErrorMessage, - path: ['custom'], - }) - -const bankInfoSchema = z.object({ - bankNumber: z.string().optional(), - ledger: z.string().optional(), - accountNumber: z.string().optional(), -}) - -const personalTexCreditSchema = z.object({ - type: z.nativeEnum(ApproveOptions).refine((v) => v, { + ), + childrenComment: z.string().optional(), + relationshipStatus: z + .object({ + unregisteredCohabitation: z + .enum([ApproveOptions.Yes, ApproveOptions.No]) + .refine((v) => v, { + params: error.validation.radioErrorMessage, + }), + spouseEmail: z.string().optional(), + spouseNationalId: z.string().optional(), + spouseApproveTerms: z.array(z.string()).optional(), + }) + .refine( + (v) => + v.unregisteredCohabitation === ApproveOptions.Yes + ? v.spouseEmail && + isValidEmail(v.spouseEmail) && + v.spouseNationalId && + isValidNationalId(v.spouseNationalId) && + v.spouseApproveTerms && + v.spouseApproveTerms.length === 1 + : true, + { + //More detailed error messages are in the UnknownRelationshipForm component + params: error.validation.email, + }, + ), + student: z + .object({ + isStudent: z + .enum([ApproveOptions.Yes, ApproveOptions.No]) + .refine((v) => v, { + params: error.validation.radioErrorMessage, + }), + custom: z.string().optional(), + }) + .refine((v) => (v.isStudent === ApproveOptions.Yes ? v.custom : true), { + params: error.validation.inputErrorMessage, + }), + homeCircumstances: z + .object({ + type: z + .enum([ + HomeCircumstances.WITHPARENTS, + HomeCircumstances.WITHOTHERS, + HomeCircumstances.OWNPLACE, + HomeCircumstances.REGISTEREDLEASE, + HomeCircumstances.UNREGISTEREDLEASE, + HomeCircumstances.OTHER, + ]) + .refine((v) => v, { + params: error.validation.radioErrorMessage, + }), + custom: z.string().optional(), + }) + .refine((v) => (v.type === HomeCircumstances.OTHER ? v.custom : true), { + params: error.validation.inputErrorMessage, + path: ['custom'], + }), + income: z.enum([ApproveOptions.Yes, ApproveOptions.No]).refine((v) => v, { params: error.validation.radioErrorMessage, }), -}) - -const contactInfoSchema = z.object({ - email: z.string().refine((v) => isValidEmail(v), { - params: error.validation.email, - }), - phone: z.string().refine((v) => isValidPhone(v), { - params: error.validation.phone, - }), -}) - -const spouseIncomeSchema = z.object({ - type: z.nativeEnum(ApproveOptions).refine((v) => v, { - params: error.validation.radioErrorMessage, + employment: z + .object({ + type: z + .enum([ + Employment.WORKING, + Employment.UNEMPLOYED, + Employment.CANNOTWORK, + Employment.OTHER, + ]) + .refine((v) => v, { + params: error.validation.radioErrorMessage, + }), + custom: z.string().optional(), + }) + .refine((v) => (v.type === Employment.OTHER ? v.custom : true), { + params: error.validation.inputErrorMessage, + path: ['custom'], + }), + bankInfo: z.object({ + bankNumber: z.string().optional(), + ledger: z.string().optional(), + accountNumber: z.string().optional(), }), -}) - -const spouseContactInfoSchema = z.object({ - email: z.string().refine((v) => isValidEmail(v), { - params: error.validation.email, + personalTaxCredit: z + .enum([ApproveOptions.Yes, ApproveOptions.No]) + .refine((v) => v, { + params: error.validation.radioErrorMessage, + }), + formComment: z.string().optional(), + contactInfo: z.object({ + email: z.string().refine((v) => isValidEmail(v), { + params: error.validation.email, + }), + phone: z.string().refine((v) => isValidPhone(v), { + params: error.validation.phone, + }), }), - phone: z.string().refine((v) => isValidPhone(v), { - params: error.validation.phone, + spouseIncome: z + .enum([ApproveOptions.Yes, ApproveOptions.No]) + .refine((v) => v, { + params: error.validation.radioErrorMessage, + }), + spouseContactInfo: z.object({ + email: z.string().refine((v) => isValidEmail(v), { + params: error.validation.email, + }), + phone: z.string().refine((v) => isValidPhone(v), { + params: error.validation.phone, + }), }), -}) - -export const dataSchema = z.object({ - // Validation for ApplicationForm - approveExternalData: approveExternalDataSchema, - spouse: spouseSchema, - childrenSchoolInfo: z.array(childSchoolInfoSchema), - childrenComment: z.string().optional(), - relationshipStatus: relationshipStatusScema, - student: studentSchema, - homeCircumstances: homeCircumstancesSchema, - income: incomeSchema, - incomeFiles: incomeFilesSchema, - taxReturnFiles: z.array(fileSchema).optional(), - spouseIncomeFiles: z.array(fileSchema).optional(), - spouseTaxReturnFiles: z.array(fileSchema).optional(), - childrenFiles: z.array(fileSchema).optional(), - employment: employmentSchema, - bankInfo: bankInfoSchema, - personalTaxCredit: personalTexCreditSchema, - contactInfo: contactInfoSchema, - formComment: z.string().optional(), - // Validation for SpouseForm - approveExternalDataSpouse: approveExternalDataSchema, - spouseIncome: spouseIncomeSchema, - spouseContactInfo: spouseContactInfoSchema, spouseFormComment: z.string().optional(), spouseName: z.string().optional(), }) -export type AnswersSchema = z.infer +export type answersSchema = z.infer diff --git a/libs/application/templates/financial-aid/src/lib/formatters.ts b/libs/application/templates/financial-aid/src/lib/formatters.ts index 1bd6b8510865..df808d6f5357 100644 --- a/libs/application/templates/financial-aid/src/lib/formatters.ts +++ b/libs/application/templates/financial-aid/src/lib/formatters.ts @@ -12,15 +12,14 @@ import format from 'date-fns/format' import * as m from './messages' import { Routes } from './constants' -import { ApproveOptions } from './types' -import { findFamilyStatus } from './utils' import { + ApproveOptions, ExternalData, - FormValue, - NationalRegistryIndividual, -} from '@island.is/application/types' -import { AnswersSchema } from './dataSchema' -import { getValueViaPath } from '@island.is/application/core' + OverrideAnswerSchema, + SchoolType, +} from './types' +import { findFamilyStatus } from './utils' +import { NationalRegistryIndividual } from '@island.is/application/types' export const getMessageHomeCircumstances: KeyMapping< HomeCircumstances, @@ -90,70 +89,64 @@ export const formatBankInfo = (bankInfo: { bankInfo?.accountNumber : '' -export const formItems = (answers: FormValue, externalData: ExternalData) => { - const answersSchema = answers as AnswersSchema - const homeCircumstancesType = answersSchema?.homeCircumstances?.type - const isStudent = answersSchema?.student?.isStudent - - return [ - { - route: Routes.INRELATIONSHIP, - label: m.inRelationship.general.sectionTitle, - info: getMessageFamilyStatus[findFamilyStatus(answers, externalData)], - }, - { - route: Routes.HOMECIRCUMSTANCES, - label: m.homeCircumstancesForm.general.sectionTitle, - info: - homeCircumstancesType === HomeCircumstances.OTHER - ? answersSchema?.homeCircumstances?.custom - : getMessageHomeCircumstances[homeCircumstancesType], - }, - { - route: Routes.STUDENT, - label: m.studentForm.general.sectionTitle, - info: getMessageApproveOptions[isStudent], - comment: - isStudent === ApproveOptions.Yes - ? answersSchema?.student?.custom - : undefined, - }, - { - route: Routes.EMPLOYMENT, - label: m.employmentForm.general.sectionTitle, - info: - answersSchema?.employment?.type === Employment.OTHER - ? answersSchema?.employment.custom - : getMessageEmploymentStatus[answersSchema?.employment?.type], - }, - { - route: Routes.INCOME, - label: m.incomeForm.general.sectionTitle, - info: getMessageApproveOptionsForIncome[answersSchema?.income.type], - }, - { - route: Routes.PERSONALTAXCREDIT, - label: m.summaryForm.formInfo.personalTaxCreditTitle, - info: getMessageApproveOptions[answersSchema?.personalTaxCredit.type], - }, - { - route: Routes.BANKINFO, - label: m.bankInfoForm.general.sectionTitle, - info: formatBankInfo(answersSchema?.bankInfo), - }, - ] -} - -export const spouseFormItems = (answers: FormValue) => { - const type = getValueViaPath(answers, 'spouseIncome.type') - return [ - { - route: Routes.SPOUSEINCOME, - label: m.incomeForm.general.sectionTitle, - info: getMessageApproveOptionsForIncome[type ?? ApproveOptions.No], - }, - ] -} +export const formItems = ( + answers: OverrideAnswerSchema, + externalData: ExternalData, +) => [ + { + route: Routes.INRELATIONSHIP, + label: m.inRelationship.general.sectionTitle, + info: getMessageFamilyStatus[findFamilyStatus(answers, externalData)], + }, + { + route: Routes.HOMECIRCUMSTANCES, + label: m.homeCircumstancesForm.general.sectionTitle, + info: + answers?.homeCircumstances?.type === HomeCircumstances.OTHER + ? answers?.homeCircumstances?.custom + : getMessageHomeCircumstances[answers?.homeCircumstances?.type], + }, + { + route: Routes.STUDENT, + label: m.studentForm.general.sectionTitle, + info: getMessageApproveOptions[answers?.student?.isStudent], + comment: + answers?.student?.isStudent === ApproveOptions.Yes + ? answers?.student?.custom + : undefined, + }, + { + route: Routes.EMPLOYMENT, + label: m.employmentForm.general.sectionTitle, + info: + answers?.employment?.type === Employment.OTHER + ? answers?.employment.custom + : getMessageEmploymentStatus[answers?.employment?.type], + }, + { + route: Routes.INCOME, + label: m.incomeForm.general.sectionTitle, + info: getMessageApproveOptionsForIncome[answers?.income], + }, + { + route: Routes.PERSONALTAXCREDIT, + label: m.summaryForm.formInfo.personalTaxCreditTitle, + info: getMessageApproveOptions[answers?.personalTaxCredit], + }, + { + route: Routes.BANKINFO, + label: m.bankInfoForm.general.sectionTitle, + info: formatBankInfo(answers?.bankInfo), + }, +] + +export const spouseFormItems = (answers: OverrideAnswerSchema) => [ + { + route: Routes.SPOUSEINCOME, + label: m.incomeForm.general.sectionTitle, + info: getMessageApproveOptionsForIncome[answers?.spouseIncome], + }, +] export const getStateMessageAndColor: KeyMapping< ApplicationState, diff --git a/libs/application/templates/financial-aid/src/lib/hooks/useApplication.ts b/libs/application/templates/financial-aid/src/lib/hooks/useApplication.ts index 02bc73e79e9a..f9c95db835ef 100644 --- a/libs/application/templates/financial-aid/src/lib/hooks/useApplication.ts +++ b/libs/application/templates/financial-aid/src/lib/hooks/useApplication.ts @@ -1,8 +1,8 @@ import { gql, useMutation, useQuery } from '@apollo/client' import { + Application, ApplicationEventType, ApplicationState, - Application as FinancialAidAnswers, } from '@island.is/financial-aid/shared/lib' export const ApplicationQuery = gql` @@ -57,7 +57,7 @@ export const ApplicationMutation = gql` const useApplication = (id?: string) => { const { data, loading } = useQuery<{ - municipalitiesFinancialAidApplication: FinancialAidAnswers + municipalitiesFinancialAidApplication: Application }>(ApplicationQuery, { variables: { input: { id } }, fetchPolicy: 'no-cache', @@ -65,7 +65,7 @@ const useApplication = (id?: string) => { }) const [updateApplicationMutation] = useMutation<{ - updateMunicipalitiesFinancialAidApplication: FinancialAidAnswers + updateMunicipalitiesFinancialAidApplication: Application }>(ApplicationMutation) const updateApplication = async ( diff --git a/libs/application/templates/financial-aid/src/lib/messages/aboutForm.ts b/libs/application/templates/financial-aid/src/lib/messages/aboutForm.ts index 7f11e8e37130..f1a73448235d 100644 --- a/libs/application/templates/financial-aid/src/lib/messages/aboutForm.ts +++ b/libs/application/templates/financial-aid/src/lib/messages/aboutForm.ts @@ -13,7 +13,7 @@ export const aboutForm = { description: 'About form page title', }, description: { - id: 'fa.application:section.aboutForm.general.description#markdown', + id: 'fa.application:section.aboutForm.general.description', defaultMessage: 'ÞĂș ert aĂ° sĂŠkja um fjĂĄrhagsaĂ°stoĂ° hjĂĄ ĂŸĂ­nu sveitarfĂ©lagi fyrir {currentMonth} mĂĄnuĂ°. Áður en ĂŸĂș heldur ĂĄfram er gott aĂ° hafa eftirfarandi Ă­ huga:', description: 'About form page description', diff --git a/libs/application/templates/financial-aid/src/lib/messages/aboutSpouseForm.ts b/libs/application/templates/financial-aid/src/lib/messages/aboutSpouseForm.ts index 0096f39d76db..f43987110246 100644 --- a/libs/application/templates/financial-aid/src/lib/messages/aboutSpouseForm.ts +++ b/libs/application/templates/financial-aid/src/lib/messages/aboutSpouseForm.ts @@ -15,7 +15,7 @@ export const aboutSpouseForm = { description: { id: 'fa.application:section.aboutSpouseForm.general.description#markdown', defaultMessage: - 'Maki ĂŸinn ({spouseName}) hefur sĂłtt um fjĂĄrhagsaĂ°stoĂ° fyrir {currentMonth} mĂĄnuĂ°.\n\n Svo hĂŠgt sĂ© aĂ° klĂĄra umsĂłknina ĂŸurfum viĂ° aĂ° fĂĄ ĂŸig til aĂ° hlaĂ°a upp **tekjugögnum** til aĂ° reikna Ășt fjĂĄrhagsaĂ°stoĂ° til ĂștgreiĂ°slu Ă­ byrjun {nextMonth}.', + 'Maki ĂŸinn ({spouseName}) hefur sĂłtt um fjĂĄrhagsaĂ°stoĂ° fyrir {currentMonth} mĂĄnuĂ°.\n\n Svo hĂŠgt sĂ© aĂ° klĂĄra umsĂłknina ĂŸurfum viĂ° aĂ° fĂĄ ĂŸig til aĂ° hlaĂ°a upp **tekjuagögnum** til aĂ° reikna Ășt fjĂĄrhagsaĂ°stoĂ° til ĂștgreiĂ°slu Ă­ byrjun {nextMonth}.', description: 'About spouse form page description', }, }), diff --git a/libs/application/templates/financial-aid/src/lib/messages/error.ts b/libs/application/templates/financial-aid/src/lib/messages/error.ts index c124efdf0204..2cb9a24dc01a 100644 --- a/libs/application/templates/financial-aid/src/lib/messages/error.ts +++ b/libs/application/templates/financial-aid/src/lib/messages/error.ts @@ -32,11 +32,6 @@ export const error = { defaultMessage: 'AthugaĂ°u hvort kennitala sĂ© rĂ©tt slegin inn', description: 'Error message when national id is invalid or not present', }, - missingFiles: { - id: 'fa.application:error.missingFile', - defaultMessage: 'ViĂ°hengi vantar', - description: 'Error message when there are no files', - }, approveSpouse: { id: 'fa.application:error.approveSpouse', defaultMessage: 'ÞĂș ĂŸarft aĂ° samĂŸykkja', diff --git a/libs/application/templates/financial-aid/src/lib/messages/incomeForm.ts b/libs/application/templates/financial-aid/src/lib/messages/incomeForm.ts index d1f7215d114f..b0051a6d96a3 100644 --- a/libs/application/templates/financial-aid/src/lib/messages/incomeForm.ts +++ b/libs/application/templates/financial-aid/src/lib/messages/incomeForm.ts @@ -21,11 +21,19 @@ export const incomeForm = { }, }), examplesOfIncome: defineMessages({ - incomeExampleList: { - id: 'fa.application:section.incomeForm.examplesOfIncome.incomeExampleList#markdown', + leftSidedList: { + id: 'fa.application:section.incomeForm.examplesOfIncome.leftSidedList#markdown', defaultMessage: - '* GreiĂ°slur frĂĄ atvinnurekanda \n* GreiĂ°slur frĂĄ VinnumĂĄlastofnun \n* GreiĂ°slur frĂĄ Tryggingastofnun \n* GreiĂ°slur frĂĄ fĂŠĂ°ingarorlofssjóði \n* GreiĂ°slur frĂĄ SjĂșkratryggingum Íslands \n* Styrkir frĂĄ lĂ­feyrissjóðum', - description: 'Income form bullet list of examples of income', + '* GreiĂ°slur frĂĄ atvinnurekanda \n* GreiĂ°slur frĂĄ VinnumĂĄlastofnun \n* GreiĂ°slur frĂĄ Tryggingastofnun', + description: + 'Income form bullet list of examples of income, list is on the left side until window size is mobile', + }, + rightSidedList: { + id: 'fa.application:section.incomeForm.examplesOfIncome.rightSidedList#markdown', + defaultMessage: + '* GreiĂ°slur frĂĄ fĂŠĂ°ingarorlofssjóði \n* GreiĂ°slur frĂĄ SjĂșkratryggingum Íslands \n* Styrkir frĂĄ lĂ­feyrissjóðum', + description: + 'Income form bullet list of examples of income, list is on the right side until window size is mobile', }, }), summary: defineMessages({ diff --git a/libs/application/templates/financial-aid/src/lib/messages/missingFiles.ts b/libs/application/templates/financial-aid/src/lib/messages/missingFiles.ts index 25e74a9b73b8..20d227008478 100644 --- a/libs/application/templates/financial-aid/src/lib/messages/missingFiles.ts +++ b/libs/application/templates/financial-aid/src/lib/messages/missingFiles.ts @@ -57,14 +57,9 @@ export const missingFiles = { }, }), confirmation: defineMessages({ - sectionTitle: { - id: 'fa.application:section.missingFiles.confirmation.sectionTitle', - defaultMessage: 'Yfirlit', - description: 'Title of the confirmation page', - }, title: { id: 'fa.application:section.missingFiles.confirmation.title', - defaultMessage: 'Yfirlit', + defaultMessage: 'Senda inn gögn', description: 'Title of the confirmation page', }, subtitle: { @@ -87,18 +82,5 @@ export const missingFiles = { defaultMessage: 'Skjal', description: 'Text for the file title', }, - openServicePortalMessageText: { - id: 'application.system:openServicePortal.messageText', - defaultMessage: - 'UpplĂœsingar Ă­ mĂ­num sĂ­Ă°um og Ă­ appi hefur ĂŸĂș aĂ°gang aĂ° margvĂ­slegum upplĂœsingum s.s stafrĂŠnt pĂłsthĂłlf, ĂŸĂ­nar upplĂœsingar, fjĂĄrmĂĄl, umsĂłknir, menntun, fasteignir, ökutĂŠki, skĂ­rteini, starfsleyfi ofl.', - description: - 'Text for form builder component left side of button to go to the service portal', - }, - openServicePortalButtonTitle: { - id: 'application.system:openServicePortal.buttonTitle', - defaultMessage: 'Áfram', - description: - 'Button text for form builder component, go to service portal', - }, }), } diff --git a/libs/application/templates/financial-aid/src/lib/messages/privacyPolicyAccordion.ts b/libs/application/templates/financial-aid/src/lib/messages/privacyPolicyAccordion.ts index 097d12bee15b..c24ed187ec8b 100644 --- a/libs/application/templates/financial-aid/src/lib/messages/privacyPolicyAccordion.ts +++ b/libs/application/templates/financial-aid/src/lib/messages/privacyPolicyAccordion.ts @@ -17,8 +17,20 @@ export const privacyPolicyAccordion = { about: { id: 'fa.application:section.privacyPolicyAccordion.accordion.about#markdown', defaultMessage: - '**UmsĂłkn um fjĂĄrhagsaĂ°stoĂ°**\n\n Til ĂŸess aĂ° geta unniĂ° Ășr umsĂłkn ĂŸinni og lagt mat ĂĄ rĂ©tt ĂŸinn til fjĂĄrhagsaĂ°stoĂ°ar er sveitarfĂ©laginu nauĂ°synlegt aĂ° vinna meĂ° nĂĄnar tilgreindar persĂłnuupplĂœsingar ĂŸĂ­nar. UnniĂ° er meĂ° upplĂœsingar sem ĂŸĂș lĂŠtur af hendi Ă­ umsĂłknarferlinu en einnig aflar sveitarfĂ©lagiĂ° upplĂœsinga um ĂŸig frĂĄ ĂŸriĂ°ju aĂ°ilum.\n\n UpplĂœsingarnar gĂŠtu veriĂ° notaĂ°ar til vinnslu tölfrĂŠĂ°irannsĂłkna.\n\n Í samrĂŠmi viĂ° lög um opinber skjalasöfn ĂŸĂĄ varĂ°veitir sveitarĂ©lagiĂ° upplĂœsingarnar ĂłtĂ­mabundiĂ°.\n\n VerĂ°i umbeĂ°nar nauĂ°synlegar upplĂœsingar ekki veittar kann ĂŸaĂ° aĂ° leiĂ°a til ĂŸess aĂ° ekki er unnt aĂ° verĂ°a viĂ° umsĂłkn ĂŸessari.\n\n **Tilgangur vinnslu og lagagrundvöllur**\n\n PersĂłnuupplĂœsingar ĂŸĂŠr sem ĂłskaĂ° er eftir ĂĄ umsĂłknarformi ĂŸessu og unniĂ° er meĂ°, eru nauĂ°synlegar sveitarfĂ©laginu til aĂ° geta metiĂ° og tryggt rĂ©tt umsĂŠkjanda til ĂŸjĂłnustu Ă­ samrĂŠmi viĂ° umsĂłkn ĂŸessa meĂ° vĂ­san til lagaskyldu samkvĂŠmt lögum um fĂ©lagsĂŸjĂłnustu sveitarfĂ©laga.\n\n **HvaĂ°a upplĂœsingar er unniĂ° meĂ°?**\n\n ÞaĂ° fer ĂŸvĂ­ eftir stöðu ĂŸinni hvaĂ°a upplĂœsingar sveitarfĂ©laginu er nauĂ°synlegt aĂ° vinna meĂ° Ă­ tengslum viĂ° umsĂłkn ĂŸĂ­na. ÁkveĂ°nar grunnupplĂœsingar eru hins vegar unnar um alla umsĂŠkjendur sem Ăłska eftir fjĂĄrhagsaĂ°stoĂ°. UnniĂ° er meĂ° eftirfarandi upplĂœsingar meĂ° hliĂ°sjĂłn af stöðu umsĂŠkjanda.\n\n **Allir umsĂŠkjendur** \n\n Nafn, lögheimili/aĂ°setur, kyn, hjĂșskaparstöðu, fjölskyldunĂșmer, fjölskyldugerĂ°, kennitölu, sĂ­manĂșmer, netfang, stöðu umsĂŠkjanda, hĂșsnĂŠĂ°isstaĂ°a, skattskyldar tekjur ĂĄ yfirstandandi ĂĄri og allt ĂĄriĂ° ĂĄ undan, ĂĄlagningaskrĂĄ: eignir og skuldir auk virĂ°isaukaskattskrĂĄ, upplĂœsingar um ofgreiĂ°slur, bankareikningur, tĂ­mabil umsĂłknar, eĂ°li umsĂłknar og aĂ°rar upplĂœsingar sem umsĂŠkjandi vill koma ĂĄ framfĂŠri Ă­ umsĂłknarferli. Ef umsĂŠkjandi hefur einhvern ĂĄ sinni framfĂŠrslu ĂŸĂĄ eru jafnframt sĂłttar eftirtaldar upplĂœsingar slĂ­kra aĂ°ila frĂĄ ÞjóðskrĂĄ sem eru nafn, kennitala og lögheimili. Ef umsĂŠkjandi greiĂ°ir meĂ°lag er jafnframt ĂłskaĂ° eftir upplĂœsingum um slĂ­kar greiĂ°slur. \n\n **VinnufĂŠrir umsĂŠkjendur** \n\n MinnisblaĂ° atvinnuleitanda sem kallaĂ° er eftir frĂĄ umsĂŠkjanda. StaĂ°festing frĂĄ VinnumĂĄlastofnun um rĂ©tt til atvinnuleysisbĂłta ĂĄsamt staĂ°festingu ĂĄ skrĂĄningu ĂĄ Atvinnutorgi.\n\n **ÓvinnufĂŠrir umsĂŠkjendur**\n\n SjĂșkradagpeningavottorĂ° og lĂŠknisvottorĂ° frĂĄ umsĂŠkjanda auk upplĂœsinga um greiĂ°slur frĂĄ stĂ©ttarfĂ©lagi sem ĂłskaĂ° er eftir frĂĄ RĂ­kisskattstjĂłra.\n\n UmsĂŠkjendur sem eru örorku-, endurhĂŠfingar- eĂ°a ellilĂ­feyrisĂŸegar\n\n SundurliĂ°aĂ°ar tekjur og greiĂ°slur frĂĄ Tryggingastofnun rĂ­kisins Ă­ umsĂłknarmĂĄnuĂ°i og mĂĄnuĂ°i ĂŸar ĂĄ undan. UpplĂœsingar um mĂŠĂ°ra- og feĂ°ralaun og/eĂ°a makabĂŠtur, eftir ĂŸvĂ­ sem viĂ° ĂĄ. \n\n **AnnaĂ°**\n\n Ef umsĂŠkjandi er ĂĄ leigumarkaĂ°i ĂŸĂĄ kann aĂ° vera ĂłskaĂ° eftir afriti af ĂŸinglĂœstum leigusamningi frĂĄ umsĂŠkjanda. DvalarleyfisskĂ­rteini ef umsĂŠkjandi er erlendur rĂ­kisborgari utan EvrĂłpska efnahagssvĂŠĂ°isins. VottorĂ° frĂĄ sĂœslumanni varĂ°andi hjĂșskapar/sambĂșĂ°arslit, ef viĂ° ĂĄ.\n\n **HvaĂ°an koma upplĂœsingarnar?**\n\n Auk ĂŸeirra persĂłnuupplĂœsinga sem ĂŸĂș veitir sveitarfĂ©laginu Ă­ umsĂłknarferlinu ĂŸĂĄ er kallaĂ° eftir grunnupplĂœsingum um ĂŸig frĂĄ ÞjóðskrĂĄ og fjĂĄrhagsupplĂœsingum frĂĄ RĂ­kisskattstjĂłra auk ĂŸess sem auĂ°kenning ĂŸĂ­n er sĂłtt til ĂŸjĂłnustuveitanda auĂ°kenningarĂŸjĂłnustu. Þá kann sveitarfĂ©lagiĂ° aĂ° kalla eftir persĂłnuupplĂœsingum frĂĄ VinnumĂĄlastofnun, Tryggingastofnun rĂ­kisins og sĂœslumanni. \n\n ** NĂĄnar um vinnslu persĂłnuupplĂœsinga** \n\n Frekari upplĂœsingar um vinnslu persĂłnuupplĂœsinga hjĂĄ sveitarfĂ©laginu mĂĄ finna Ă­ persĂłnuverndarstefnu ĂŸess sem aĂ°gengileg er ĂĄ {webInfo}.\n\n.', + '**UmsĂłkn um fjĂĄrhagsaĂ°stoĂ°**\n\n Til ĂŸess aĂ° geta unniĂ° Ășr umsĂłkn ĂŸinni og lagt mat ĂĄ rĂ©tt ĂŸinn til fjĂĄrhagsaĂ°stoĂ°ar er sveitarfĂ©laginu nauĂ°synlegt aĂ° vinna meĂ° nĂĄnar tilgreindar persĂłnuupplĂœsingar ĂŸĂ­nar. UnniĂ° er meĂ° upplĂœsingar sem ĂŸĂș lĂŠtur af hendi Ă­ umsĂłknarferlinu en einnig aflar sveitarfĂ©lagiĂ° upplĂœsinga um ĂŸig frĂĄ ĂŸriĂ°ju aĂ°ilum.\n\n UpplĂœsingarnar gĂŠtu veriĂ° notaĂ°ar til vinnslu tölfrĂŠĂ°irannsĂłkna. \n\n Í samrĂŠmi viĂ° lög um opinber skjalasöfn ĂŸĂĄ varĂ°veitir sveitarĂ©lagiĂ° upplĂœsingarnar ĂłtĂ­mabundiĂ°. \n\n VerĂ°i umbeĂ°nar nauĂ°synlegar upplĂœsingar ekki veittar kann ĂŸaĂ° aĂ° leiĂ°a til ĂŸess aĂ° ekki er unnt aĂ° verĂ°a viĂ° umsĂłkn ĂŸessari.\n\n**Tilgangur vinnslu og lagagrundvöllur** \n\n PersĂłnuupplĂœsingar ĂŸĂŠr sem ĂłskaĂ° er eftir ĂĄ umsĂłknarformi ĂŸessu og unniĂ° er meĂ°, eru nauĂ°synlegar sveitarfĂ©laginu til aĂ° geta metiĂ° og tryggt rĂ©tt umsĂŠkjanda til ĂŸjĂłnustu Ă­ samrĂŠmi viĂ° umsĂłkn ĂŸessa meĂ° vĂ­san til lagaskyldu samkvĂŠmt lögum um fĂ©lagsĂŸjĂłnustu sveitarfĂ©laga. \n\n **HvaĂ°a upplĂœsingar er unniĂ° meĂ°?**\n\n ÞaĂ° fer ĂŸvĂ­ eftir stöðu ĂŸinni hvaĂ°a upplĂœsingar sveitarfĂ©laginu er nauĂ°synlegt aĂ° vinna meĂ° Ă­ tengslum viĂ° umsĂłkn ĂŸĂ­na. ÁkveĂ°nar grunnupplĂœsingar eru hins vegar unnar um alla umsĂŠkjendur sem Ăłska eftir fjĂĄrhagsaĂ°stoĂ°. UnniĂ° er meĂ° eftirfarandi upplĂœsingar meĂ° hliĂ°sjĂłn af stöðu umsĂŠkjanda. \n\n **Allir umsĂŠkjendur** \n\n Nafn, lögheimili/aĂ°setur, kyn, hjĂșskaparstöðu, fjölskyldunĂșmer, fjölskyldugerĂ°, kennitölu, sĂ­manĂșmer, netfang, stöðu umsĂŠkjanda, hĂșsnĂŠĂ°isstaĂ°a, skattskyldar tekjur ĂĄ yfirstandandi ĂĄri og allt ĂĄriĂ° ĂĄ undan, ĂĄlagningaskrĂĄ: eignir og skuldir auk virĂ°isaukaskattskrĂĄ, upplĂœsingar um ofgreiĂ°slur, bankareikningur, tĂ­mabil umsĂłknar, eĂ°li umsĂłknar og aĂ°rar upplĂœsingar sem umsĂŠkjandi vill koma ĂĄ framfĂŠri Ă­ umsĂłknarferli. Ef umsĂŠkjandi hefur einhvern ĂĄ sinni framfĂŠrslu ĂŸĂĄ eru jafnframt sĂłttar eftirtaldar upplĂœsingar slĂ­kra aĂ°ila frĂĄ ÞjóðskrĂĄ sem eru nafn, kennitala og lögheimili. Ef umsĂŠkjandi greiĂ°ir meĂ°lag er jafnframt ĂłskaĂ° eftir upplĂœsingum um slĂ­kar greiĂ°slur. \n\n **VinnufĂŠrir umsĂŠkjendur** \n\n MinnisblaĂ° atvinnuleitanda sem kallaĂ° er eftir frĂĄ umsĂŠkjanda. StaĂ°festing frĂĄ VinnumĂĄlastofnun um rĂ©tt til atvinnuleysisbĂłta ĂĄsamt staĂ°festingu ĂĄ skrĂĄningu ĂĄ Atvinnutorgi.\n\n **ÓvinnufĂŠrir umsĂŠkjendur**\n\nSjĂșkradagpeningavottorĂ° og lĂŠknisvottorĂ° frĂĄ umsĂŠkjanda auk upplĂœsinga um greiĂ°slur frĂĄ stĂ©ttarfĂ©lagi sem ĂłskaĂ° er eftir frĂĄ RĂ­kisskattstjĂłra.\n\nUmsĂŠkjendur sem eru örorku-, endurhĂŠfingar- eĂ°a ellilĂ­feyrisĂŸegar\n\nSundurliĂ°aĂ°ar tekjur og greiĂ°slur frĂĄ Tryggingastofnun rĂ­kisins Ă­ umsĂłknarmĂĄnuĂ°i og mĂĄnuĂ°i ĂŸar ĂĄ undan. UpplĂœsingar um mĂŠĂ°ra- og feĂ°ralaun og/eĂ°a makabĂŠtur, eftir ĂŸvĂ­ sem viĂ° ĂĄ. \n\n**AnnaĂ°**\n\nEf umsĂŠkjandi er ĂĄ leigumarkaĂ°i ĂŸĂĄ kann aĂ° vera ĂłskaĂ° eftir afriti af ĂŸinglĂœstum leigusamningi frĂĄ umsĂŠkjanda. DvalarleyfisskĂ­rteini ef umsĂŠkjandi er erlendur rĂ­kisborgari utan EvrĂłpska efnahagssvĂŠĂ°isins. VottorĂ° frĂĄ sĂœslumanni varĂ°andi hjĂșskapar/sambĂșĂ°arslit, ef viĂ° ĂĄ. \n\n**HvaĂ°an koma upplĂœsingarnar?**\n\nAuk ĂŸeirra persĂłnuupplĂœsinga sem ĂŸĂș veitir sveitarfĂ©laginu Ă­ umsĂłknarferlinu ĂŸĂĄ er kallaĂ° eftir grunnupplĂœsingum um ĂŸig frĂĄ ÞjóðskrĂĄ og fjĂĄrhagsupplĂœsingum frĂĄ RĂ­kisskattstjĂłra auk ĂŸess sem auĂ°kenning ĂŸĂ­n er sĂłtt til ĂŸjĂłnustuveitanda auĂ°kenningarĂŸjĂłnustu. Þá kann sveitarfĂ©lagiĂ° aĂ° kalla eftir persĂłnuupplĂœsingum frĂĄ VinnumĂĄlastofnun, Tryggingastofnun rĂ­kisins og sĂœslumanni. \n\n ** NĂĄnar um vinnslu persĂłnuupplĂœsinga** \n\n', description: 'Privacy policy accordion content', }, + moreInfo: { + id: 'fa.application:section.privacyPolicyAccordion.accordion.moreInfo', + defaultMessage: + 'Frekari upplĂœsingar um vinnslu persĂłnuupplĂœsinga hjĂĄ sveitarfĂ©laginu mĂĄ finna Ă­ persĂłnuverndarstefnu ĂŸess sem aĂ°gengileg er ĂĄ vefsĂ­Ă°u sveitarfĂ©lagsins.', + description: 'Privacy policy accordion more info text', + }, + moreInfoHomepage: { + id: 'fa.application:section.privacyPolicyAccordion.accordion.moreInfoHomepage#markdown', + defaultMessage: + 'Frekari upplĂœsingar um vinnslu persĂłnuupplĂœsinga hjĂĄ sveitarfĂ©laginu mĂĄ finna Ă­ persĂłnuverndarstefnu ĂŸess sem aĂ°gengileg er ĂĄ vefsĂ­Ă°unni [{homePageNameUrl}]({homePageNameUrl}).', + description: 'Privacy policy accordion more info text with homepage', + }, }), } diff --git a/libs/application/templates/financial-aid/src/lib/messages/unknownRelationship.ts b/libs/application/templates/financial-aid/src/lib/messages/unknownRelationship.ts index 45d74f8125d8..6b9a780ae044 100644 --- a/libs/application/templates/financial-aid/src/lib/messages/unknownRelationship.ts +++ b/libs/application/templates/financial-aid/src/lib/messages/unknownRelationship.ts @@ -52,7 +52,7 @@ export const unknownRelationship = { }, spouseNationalIdPlaceholder: { id: 'fa.application:section.personalInterest.unknownRelationship.inputs.spouseNationalIdPlaceholder', - defaultMessage: 'SlĂĄĂ°u inn kennitölu maka', + defaultMessage: 'SlĂĄĂ°u inn netfang maka', description: 'Spouse national id input placeholder', }, spouseEmail: { diff --git a/libs/application/templates/financial-aid/src/lib/types.ts b/libs/application/templates/financial-aid/src/lib/types.ts index 48f62d65f3ee..5ee270b2a4f1 100644 --- a/libs/application/templates/financial-aid/src/lib/types.ts +++ b/libs/application/templates/financial-aid/src/lib/types.ts @@ -1,6 +1,8 @@ import { ApplicantChildCustodyInformation, + Application, ApplicationAnswerFile, + FieldBaseProps, NationalRegistryIndividual, NationalRegistrySpouse, } from '@island.is/application/types' @@ -9,18 +11,18 @@ import { Municipality, PersonalTaxReturn, } from '@island.is/financial-aid/shared/lib' -import { AnswersSchema } from './dataSchema' +import { answersSchema } from './dataSchema' export enum ApproveOptions { Yes = 'Yes', No = 'No', } -export type FinancialAidAnswers = AnswersSchema +type Override = Omit & T2 -export type ErrorSchema = NestedType +export type ErrorSchema = NestedType -export interface FinancialAidExternalData { +export interface ExternalData { nationalRegistry: { data: NationalRegistryIndividual date: string @@ -70,14 +72,27 @@ export type NestedType = { : string } -export type ChildrenSchoolInfo = { - nationalId: string - school: string - fullName: string - livesWithApplicant?: boolean - livesWithBothParents?: boolean +export interface OverrideAnswerSchema extends answersSchema { + incomeFiles: ApplicationAnswerFile[] + taxReturnFiles: ApplicationAnswerFile[] + spouseIncomeFiles: ApplicationAnswerFile[] + spouseTaxReturnFiles: ApplicationAnswerFile[] + childrenFiles: ApplicationAnswerFile[] } +export type FAApplication = Override< + Application, + { + answers: OverrideAnswerSchema + externalData: ExternalData + } +> + +export type FAFieldBaseProps = Override< + FieldBaseProps, + { application: FAApplication; errors: ErrorSchema } +> + export interface TaxData { municipalitiesPersonalTaxReturn: { personalTaxReturn: PersonalTaxReturn | null @@ -105,8 +120,6 @@ export type UploadFileType = | 'taxReturnFiles' | 'spouseIncomeFiles' | 'spouseTaxReturnFiles' - | 'missingFiles' - | 'missingFilesSpouse' export enum SummaryComment { FORMCOMMENT = 'formComment', @@ -119,13 +132,3 @@ export enum SchoolType { ELEMENTARY = 'elementary', HIGHSCHOOL = 'highSchool', } - -export interface TaxData { - municipalitiesPersonalTaxReturn: { - personalTaxReturn: PersonalTaxReturn | null - } - municipalitiesDirectTaxPayments: { - directTaxPayments: DirectTaxPayment[] - success: boolean - } -} diff --git a/libs/application/templates/financial-aid/src/lib/utils.ts b/libs/application/templates/financial-aid/src/lib/utils.ts index c4e3afe6080d..8bee37ef8f45 100644 --- a/libs/application/templates/financial-aid/src/lib/utils.ts +++ b/libs/application/templates/financial-aid/src/lib/utils.ts @@ -4,22 +4,26 @@ import { getValueViaPath } from '@island.is/application/core' import { ApplicantChildCustodyInformation, ApplicationContext, - ExternalData, - FormValue, } from '@island.is/application/types' + import { FamilyStatus, MartialStatusType, martialStatusTypeFromMartialCode, Municipality, } from '@island.is/financial-aid/shared/lib' -import { ApproveOptions, CurrentApplication, UploadFileType } from '..' + +import { + ApproveOptions, + CurrentApplication, + FAApplication, + OverrideAnswerSchema, + SchoolType, + UploadFileType, +} from '..' import { UploadFile } from '@island.is/island-ui/core' import { ApplicationStates } from './constants' import sortBy from 'lodash/sortBy' -import * as m from '../lib/messages' -import { AnswersSchema } from './dataSchema' -import { isRunningOnEnvironment } from '@island.is/shared/utils' const emailRegex = /^[\w!#$%&'*+/=?`{|}~^-]+(?:\.[\w!#$%&'*+/=?`{|}~^-]+)*@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$/i @@ -31,17 +35,33 @@ export const isValidPhone = (value: string) => { export const isValidNationalId = (value: string) => kennitala.isValid(value) export function hasSpouseCheck(context: ApplicationContext) { - const { externalData, answers } = context.application + const { externalData, answers } = + context.application as unknown as FAApplication return hasSpouse(answers, externalData) } -export function isMunicipalityNotRegistered(context: ApplicationContext) { +export const hasSpouse = ( + answers: FAApplication['answers'], + externalData: FAApplication['externalData'], +) => { + const nationalRegistrySpouse = externalData.nationalRegistrySpouse.data + + const unregisteredCohabitation = + answers?.relationshipStatus?.unregisteredCohabitation + + return ( + Boolean(nationalRegistrySpouse) || + unregisteredCohabitation === ApproveOptions.Yes + ) +} + +export function isMuncipalityNotRegistered(context: ApplicationContext) { const { externalData } = context.application - const municipality = getValueViaPath( + const municipality = getValueViaPath( externalData, `municipality.data`, - ) + ) as Municipality | null return municipality == null || !municipality.active } @@ -49,55 +69,39 @@ export const encodeFilenames = (filename: string) => filename && encodeURI(filename.normalize().replace(/ +/g, '_')) export function findFamilyStatus( - answers: FormValue, - externalData: ExternalData, + answers: FAApplication['answers'], + externalData: FAApplication['externalData'], ) { - const maritalStatus = getValueViaPath( - externalData, - 'nationalRegistrySpouse.data.maritalStatus', - ) - const unregisteredCohabitation = getValueViaPath( - answers, - 'relationshipStatus.unregisteredCohabitation', - ) - - if ( - martialStatusTypeFromMartialCode(maritalStatus) === - MartialStatusType.MARRIED - ) { - return FamilyStatus.MARRIED + switch (true) { + case martialStatusTypeFromMartialCode( + externalData.nationalRegistrySpouse.data?.maritalStatus, + ) === MartialStatusType.MARRIED: + return FamilyStatus.MARRIED + case externalData.nationalRegistrySpouse.data != null: + return FamilyStatus.COHABITATION + case answers?.relationshipStatus?.unregisteredCohabitation === + ApproveOptions.Yes: + return FamilyStatus.UNREGISTERED_COBAHITATION + default: + return FamilyStatus.NOT_COHABITATION } - - if (externalData.nationalRegistrySpouse.data != null) { - return FamilyStatus.COHABITATION - } - - if (unregisteredCohabitation === ApproveOptions.Yes) { - return FamilyStatus.UNREGISTERED_COBAHITATION - } - - return FamilyStatus.NOT_COHABITATION } export function hasActiveCurrentApplication(context: ApplicationContext) { - // On prod there should only be one active application per user - // When working with gervimaĂ°ur we might need to have many active applications - const isProd = isRunningOnEnvironment('production') - if (!isProd) { - return false - } - const { externalData } = context.application - const currentApplication = getValueViaPath( + const currentApplication = getValueViaPath( externalData, 'currentApplication.data', - ) + ) as CurrentApplication return currentApplication?.currentApplicationId != null } -export const hasFiles = (fileType: UploadFileType, answers: AnswersSchema) => { - const files = answers[fileType as keyof AnswersSchema] as UploadFile[] +export const hasFiles = ( + fileType: UploadFileType, + answers: OverrideAnswerSchema, +) => { + const files = answers[fileType as keyof OverrideAnswerSchema] as UploadFile[] return files && files.length > 0 } @@ -118,64 +122,3 @@ export const sortChildrenUnderAgeByAge = ( return kennitala.info(child.nationalId)?.birthday }) } - -export const hasSpouse = (answers: FormValue, externalData: ExternalData) => { - const nationalRegistrySpouse = externalData.nationalRegistrySpouse.data - - const unregisteredCohabitation = getValueViaPath( - answers, - 'relationshipStatus.unregisteredCohabitation', - ) - - return ( - Boolean(nationalRegistrySpouse) || - unregisteredCohabitation === ApproveOptions.Yes - ) -} - -export const getNextStepsDescription = ( - answers: FormValue, - externalData: ExternalData, -) => { - const applicantHasSpouse = hasSpouse(answers, externalData) - const missingIncomeFiles = - answers.income === ApproveOptions.Yes && - !hasFiles('incomeFiles', answers as AnswersSchema) - - if (applicantHasSpouse && missingIncomeFiles) { - return m.confirmation.nextSteps.contentBothMissingFiles - } else if (applicantHasSpouse) { - return m.confirmation.nextSteps.contentSpouseMissingFiles - } else if (missingIncomeFiles) { - return m.confirmation.nextSteps.contentMissingFiles - } - - return '' -} - -export const getSpouseNextStepsDescription = ( - answers: FormValue, - externalData: ExternalData, -) => { - const incomeFiles = hasSpouseIncomeFiles(answers) - - return incomeFiles ? '' : m.confirmation.nextSteps.contentMissingFiles -} - -type File = { - name: string - key: string -} - -export const hasIncomeFiles = (formValue: FormValue) => { - const income = formValue.income === ApproveOptions.Yes - const incomeFiles = formValue.incomeFiles as Array - - return income && incomeFiles && incomeFiles.length > 0 -} - -export const hasSpouseIncomeFiles = (formValue: FormValue) => { - const income = formValue.spouseIncomeFiles as Array - - return income && income.length > 0 -} diff --git a/libs/application/templates/financial-aid/src/lib/utils/getApplicantsServiceCenter.ts b/libs/application/templates/financial-aid/src/lib/utils/getApplicantsServiceCenter.ts deleted file mode 100644 index e93f1b7df1a2..000000000000 --- a/libs/application/templates/financial-aid/src/lib/utils/getApplicantsServiceCenter.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { Application } from '@island.is/application/types' -import { serviceCenters } from '@island.is/financial-aid/shared/data' - -export const getApplicantsServiceCenter = (application: Application) => { - const { externalData } = application - const municipalityCode = getValueViaPath( - externalData, - 'nationalRegistry.data.address.municipalityCode', - ) - - const applicantsCenter = serviceCenters.find( - (serviceCenter) => serviceCenter.number === Number(municipalityCode), - ) - return applicantsCenter -} diff --git a/libs/application/templates/financial-aid/src/lib/utils/getEmploymentOptions.ts b/libs/application/templates/financial-aid/src/lib/utils/getEmploymentOptions.ts deleted file mode 100644 index defd8a098f83..000000000000 --- a/libs/application/templates/financial-aid/src/lib/utils/getEmploymentOptions.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Employment } from '@island.is/financial-aid/shared/lib' -import { employmentForm } from '../messages' - -export const getEmploymentOptions = () => { - const options = [ - { - value: Employment.WORKING, - label: employmentForm.employment.working, - }, - { - value: Employment.UNEMPLOYED, - label: employmentForm.employment.unemployed, - }, - { - value: Employment.CANNOTWORK, - label: employmentForm.employment.cannotWork, - }, - { - value: Employment.OTHER, - label: employmentForm.employment.other, - }, - ] - - return options -} diff --git a/libs/application/templates/financial-aid/src/lib/utils/getHomeCircumstancesOptions.ts b/libs/application/templates/financial-aid/src/lib/utils/getHomeCircumstancesOptions.ts deleted file mode 100644 index 76ed5ad46652..000000000000 --- a/libs/application/templates/financial-aid/src/lib/utils/getHomeCircumstancesOptions.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { HomeCircumstances } from '@island.is/financial-aid/shared/lib' -import { homeCircumstancesForm } from '../messages' - -export const getHomeCircumstancesOptions = () => { - const options = [ - { - value: HomeCircumstances.OWNPLACE, - label: homeCircumstancesForm.circumstances.ownPlace, - }, - { - value: HomeCircumstances.REGISTEREDLEASE, - label: homeCircumstancesForm.circumstances.registeredLease, - }, - { - value: HomeCircumstances.UNREGISTEREDLEASE, - label: homeCircumstancesForm.circumstances.unregisteredLease, - }, - - { - value: HomeCircumstances.WITHOTHERS, - label: homeCircumstancesForm.circumstances.withOthers, - }, - { - value: HomeCircumstances.WITHPARENTS, - label: homeCircumstancesForm.circumstances.withParents, - }, - { - value: HomeCircumstances.OTHER, - label: homeCircumstancesForm.circumstances.other, - }, - ] - - return options -} diff --git a/libs/application/templates/financial-aid/src/lib/utils/getIncomeOptions.ts b/libs/application/templates/financial-aid/src/lib/utils/getIncomeOptions.ts deleted file mode 100644 index 5edc40f31baa..000000000000 --- a/libs/application/templates/financial-aid/src/lib/utils/getIncomeOptions.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { incomeForm } from '../messages' -import { ApproveOptions } from '../types' - -export const getIncomeOptions = () => { - const options = [ - { - value: ApproveOptions.Yes, - label: incomeForm.options.yes, - }, - { - value: ApproveOptions.No, - label: incomeForm.options.no, - }, - ] - return options -} diff --git a/libs/application/templates/financial-aid/src/lib/utils/getPersonalTaxCreditOptions.ts b/libs/application/templates/financial-aid/src/lib/utils/getPersonalTaxCreditOptions.ts deleted file mode 100644 index efccd0da38f9..000000000000 --- a/libs/application/templates/financial-aid/src/lib/utils/getPersonalTaxCreditOptions.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { personalTaxCreditForm } from '../messages' -import { ApproveOptions } from '../types' - -export const getPersonalTaxCreditOptions = () => { - const options = [ - { - value: ApproveOptions.Yes, - label: personalTaxCreditForm.radioChoices.useTaxCredit, - }, - { - value: ApproveOptions.No, - label: personalTaxCreditForm.radioChoices.wontUseTaxCredit, - }, - ] - - return options -} diff --git a/libs/application/templates/financial-aid/src/lib/utils/getStudentOptions.ts b/libs/application/templates/financial-aid/src/lib/utils/getStudentOptions.ts deleted file mode 100644 index fe306da75b69..000000000000 --- a/libs/application/templates/financial-aid/src/lib/utils/getStudentOptions.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { approveOptions } from '../messages' -import { ApproveOptions } from '../types' - -export const getStudentOptions = () => { - const options = [ - { - value: ApproveOptions.No, - label: approveOptions.no, - }, - { - value: ApproveOptions.Yes, - label: approveOptions.yes, - }, - ] - - return options -} diff --git a/libs/application/templates/financial-aid/src/lib/utils/getUnknownRelationshipOptions.tsx b/libs/application/templates/financial-aid/src/lib/utils/getUnknownRelationshipOptions.tsx deleted file mode 100644 index 66083f84666d..000000000000 --- a/libs/application/templates/financial-aid/src/lib/utils/getUnknownRelationshipOptions.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { unknownRelationship } from '../messages' -import { ApproveOptions } from '../types' - -export const getUnknownRelationshipOptions = () => { - const options = [ - { - value: ApproveOptions.No, - label: unknownRelationship.form.radioButtonNo, - }, - { - value: ApproveOptions.Yes, - label: unknownRelationship.form.radioButtonYes, - }, - ] - - return options -} diff --git a/libs/application/templates/financial-aid/src/assets/svg/akrahreppur.svg b/libs/application/templates/financial-aid/src/svg/akrahreppur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/akrahreppur.svg rename to libs/application/templates/financial-aid/src/svg/akrahreppur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/akranes.svg b/libs/application/templates/financial-aid/src/svg/akranes.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/akranes.svg rename to libs/application/templates/financial-aid/src/svg/akranes.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/akureyri.svg b/libs/application/templates/financial-aid/src/svg/akureyri.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/akureyri.svg rename to libs/application/templates/financial-aid/src/svg/akureyri.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/arborg.svg b/libs/application/templates/financial-aid/src/svg/arborg.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/arborg.svg rename to libs/application/templates/financial-aid/src/svg/arborg.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/arneshreppur.svg b/libs/application/templates/financial-aid/src/svg/arneshreppur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/arneshreppur.svg rename to libs/application/templates/financial-aid/src/svg/arneshreppur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/asahreppur.svg b/libs/application/templates/financial-aid/src/svg/asahreppur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/asahreppur.svg rename to libs/application/templates/financial-aid/src/svg/asahreppur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/blaskogabyggd.svg b/libs/application/templates/financial-aid/src/svg/blaskogabyggd.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/blaskogabyggd.svg rename to libs/application/templates/financial-aid/src/svg/blaskogabyggd.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/blonduosbaer.svg b/libs/application/templates/financial-aid/src/svg/blonduosbaer.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/blonduosbaer.svg rename to libs/application/templates/financial-aid/src/svg/blonduosbaer.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/bolungarvik.svg b/libs/application/templates/financial-aid/src/svg/bolungarvik.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/bolungarvik.svg rename to libs/application/templates/financial-aid/src/svg/bolungarvik.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/borgarbyggd.svg b/libs/application/templates/financial-aid/src/svg/borgarbyggd.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/borgarbyggd.svg rename to libs/application/templates/financial-aid/src/svg/borgarbyggd.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/dalabyggd.svg b/libs/application/templates/financial-aid/src/svg/dalabyggd.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/dalabyggd.svg rename to libs/application/templates/financial-aid/src/svg/dalabyggd.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/dalvikurbyggd.svg b/libs/application/templates/financial-aid/src/svg/dalvikurbyggd.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/dalvikurbyggd.svg rename to libs/application/templates/financial-aid/src/svg/dalvikurbyggd.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/eyja-og-miklaholtshreppur.svg b/libs/application/templates/financial-aid/src/svg/eyja-og-miklaholtshreppur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/eyja-og-miklaholtshreppur.svg rename to libs/application/templates/financial-aid/src/svg/eyja-og-miklaholtshreppur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/eyjafjardarsveit.svg b/libs/application/templates/financial-aid/src/svg/eyjafjardarsveit.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/eyjafjardarsveit.svg rename to libs/application/templates/financial-aid/src/svg/eyjafjardarsveit.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/fjallabyggd.svg b/libs/application/templates/financial-aid/src/svg/fjallabyggd.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/fjallabyggd.svg rename to libs/application/templates/financial-aid/src/svg/fjallabyggd.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/fjardabyggd.svg b/libs/application/templates/financial-aid/src/svg/fjardabyggd.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/fjardabyggd.svg rename to libs/application/templates/financial-aid/src/svg/fjardabyggd.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/fljotsdalshreppur.svg b/libs/application/templates/financial-aid/src/svg/fljotsdalshreppur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/fljotsdalshreppur.svg rename to libs/application/templates/financial-aid/src/svg/fljotsdalshreppur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/floahreppur.svg b/libs/application/templates/financial-aid/src/svg/floahreppur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/floahreppur.svg rename to libs/application/templates/financial-aid/src/svg/floahreppur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/gardabaer.svg b/libs/application/templates/financial-aid/src/svg/gardabaer.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/gardabaer.svg rename to libs/application/templates/financial-aid/src/svg/gardabaer.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/grimsnes-og-grafningshreppur.svg b/libs/application/templates/financial-aid/src/svg/grimsnes-og-grafningshreppur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/grimsnes-og-grafningshreppur.svg rename to libs/application/templates/financial-aid/src/svg/grimsnes-og-grafningshreppur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/grindavikurbaer.svg b/libs/application/templates/financial-aid/src/svg/grindavikurbaer.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/grindavikurbaer.svg rename to libs/application/templates/financial-aid/src/svg/grindavikurbaer.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/grundafjardarbaer.svg b/libs/application/templates/financial-aid/src/svg/grundafjardarbaer.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/grundafjardarbaer.svg rename to libs/application/templates/financial-aid/src/svg/grundafjardarbaer.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/grytubakkahreppur.svg b/libs/application/templates/financial-aid/src/svg/grytubakkahreppur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/grytubakkahreppur.svg rename to libs/application/templates/financial-aid/src/svg/grytubakkahreppur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/hafnarfjordur.svg b/libs/application/templates/financial-aid/src/svg/hafnarfjordur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/hafnarfjordur.svg rename to libs/application/templates/financial-aid/src/svg/hafnarfjordur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/helgafellssveit.svg b/libs/application/templates/financial-aid/src/svg/helgafellssveit.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/helgafellssveit.svg rename to libs/application/templates/financial-aid/src/svg/helgafellssveit.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/horgarsveit.svg b/libs/application/templates/financial-aid/src/svg/horgarsveit.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/horgarsveit.svg rename to libs/application/templates/financial-aid/src/svg/horgarsveit.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/hornafjordur.svg b/libs/application/templates/financial-aid/src/svg/hornafjordur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/hornafjordur.svg rename to libs/application/templates/financial-aid/src/svg/hornafjordur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/hrunamannahreppur.svg b/libs/application/templates/financial-aid/src/svg/hrunamannahreppur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/hrunamannahreppur.svg rename to libs/application/templates/financial-aid/src/svg/hrunamannahreppur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/hunathing-vestra.svg b/libs/application/templates/financial-aid/src/svg/hunathing-vestra.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/hunathing-vestra.svg rename to libs/application/templates/financial-aid/src/svg/hunathing-vestra.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/hunavatnshreppur.svg b/libs/application/templates/financial-aid/src/svg/hunavatnshreppur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/hunavatnshreppur.svg rename to libs/application/templates/financial-aid/src/svg/hunavatnshreppur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/hvalfjardarsveit.svg b/libs/application/templates/financial-aid/src/svg/hvalfjardarsveit.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/hvalfjardarsveit.svg rename to libs/application/templates/financial-aid/src/svg/hvalfjardarsveit.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/hveragerdisbaer.svg b/libs/application/templates/financial-aid/src/svg/hveragerdisbaer.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/hveragerdisbaer.svg rename to libs/application/templates/financial-aid/src/svg/hveragerdisbaer.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/isafjardarbaer.svg b/libs/application/templates/financial-aid/src/svg/isafjardarbaer.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/isafjardarbaer.svg rename to libs/application/templates/financial-aid/src/svg/isafjardarbaer.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/kaldrananeshreppur.svg b/libs/application/templates/financial-aid/src/svg/kaldrananeshreppur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/kaldrananeshreppur.svg rename to libs/application/templates/financial-aid/src/svg/kaldrananeshreppur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/kjosarhreppur.svg b/libs/application/templates/financial-aid/src/svg/kjosarhreppur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/kjosarhreppur.svg rename to libs/application/templates/financial-aid/src/svg/kjosarhreppur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/kopavogur.svg b/libs/application/templates/financial-aid/src/svg/kopavogur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/kopavogur.svg rename to libs/application/templates/financial-aid/src/svg/kopavogur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/langanesbyggd.svg b/libs/application/templates/financial-aid/src/svg/langanesbyggd.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/langanesbyggd.svg rename to libs/application/templates/financial-aid/src/svg/langanesbyggd.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/mosfellsbaer.svg b/libs/application/templates/financial-aid/src/svg/mosfellsbaer.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/mosfellsbaer.svg rename to libs/application/templates/financial-aid/src/svg/mosfellsbaer.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/mulathing.svg b/libs/application/templates/financial-aid/src/svg/mulathing.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/mulathing.svg rename to libs/application/templates/financial-aid/src/svg/mulathing.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/myrdalshreppur.svg b/libs/application/templates/financial-aid/src/svg/myrdalshreppur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/myrdalshreppur.svg rename to libs/application/templates/financial-aid/src/svg/myrdalshreppur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/nordurthing.svg b/libs/application/templates/financial-aid/src/svg/nordurthing.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/nordurthing.svg rename to libs/application/templates/financial-aid/src/svg/nordurthing.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/olfus.svg b/libs/application/templates/financial-aid/src/svg/olfus.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/olfus.svg rename to libs/application/templates/financial-aid/src/svg/olfus.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/rangarthing-ytra.svg b/libs/application/templates/financial-aid/src/svg/rangarthing-ytra.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/rangarthing-ytra.svg rename to libs/application/templates/financial-aid/src/svg/rangarthing-ytra.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/rangarthing_eystra.svg b/libs/application/templates/financial-aid/src/svg/rangarthing_eystra.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/rangarthing_eystra.svg rename to libs/application/templates/financial-aid/src/svg/rangarthing_eystra.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/reykholahreppur.svg b/libs/application/templates/financial-aid/src/svg/reykholahreppur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/reykholahreppur.svg rename to libs/application/templates/financial-aid/src/svg/reykholahreppur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/reykjanesbaer.svg b/libs/application/templates/financial-aid/src/svg/reykjanesbaer.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/reykjanesbaer.svg rename to libs/application/templates/financial-aid/src/svg/reykjanesbaer.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/sambandid.svg b/libs/application/templates/financial-aid/src/svg/sambandid.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/sambandid.svg rename to libs/application/templates/financial-aid/src/svg/sambandid.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/seltjarnarnes.svg b/libs/application/templates/financial-aid/src/svg/seltjarnarnes.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/seltjarnarnes.svg rename to libs/application/templates/financial-aid/src/svg/seltjarnarnes.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/skaftarhreppur.svg b/libs/application/templates/financial-aid/src/svg/skaftarhreppur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/skaftarhreppur.svg rename to libs/application/templates/financial-aid/src/svg/skaftarhreppur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/skagabyggd.svg b/libs/application/templates/financial-aid/src/svg/skagabyggd.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/skagabyggd.svg rename to libs/application/templates/financial-aid/src/svg/skagabyggd.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/skagafjordur.svg b/libs/application/templates/financial-aid/src/svg/skagafjordur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/skagafjordur.svg rename to libs/application/templates/financial-aid/src/svg/skagafjordur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/skagastrond.svg b/libs/application/templates/financial-aid/src/svg/skagastrond.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/skagastrond.svg rename to libs/application/templates/financial-aid/src/svg/skagastrond.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/skeida-og-gnupverjahreppur.svg b/libs/application/templates/financial-aid/src/svg/skeida-og-gnupverjahreppur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/skeida-og-gnupverjahreppur.svg rename to libs/application/templates/financial-aid/src/svg/skeida-og-gnupverjahreppur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/skorradalur.svg b/libs/application/templates/financial-aid/src/svg/skorradalur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/skorradalur.svg rename to libs/application/templates/financial-aid/src/svg/skorradalur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/skutustadahreppur.svg b/libs/application/templates/financial-aid/src/svg/skutustadahreppur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/skutustadahreppur.svg rename to libs/application/templates/financial-aid/src/svg/skutustadahreppur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/snaefellsbaer.svg b/libs/application/templates/financial-aid/src/svg/snaefellsbaer.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/snaefellsbaer.svg rename to libs/application/templates/financial-aid/src/svg/snaefellsbaer.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/strandabyggd.svg b/libs/application/templates/financial-aid/src/svg/strandabyggd.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/strandabyggd.svg rename to libs/application/templates/financial-aid/src/svg/strandabyggd.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/stykkisholmsbaer.svg b/libs/application/templates/financial-aid/src/svg/stykkisholmsbaer.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/stykkisholmsbaer.svg rename to libs/application/templates/financial-aid/src/svg/stykkisholmsbaer.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/sudavikurhreppur.svg b/libs/application/templates/financial-aid/src/svg/sudavikurhreppur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/sudavikurhreppur.svg rename to libs/application/templates/financial-aid/src/svg/sudavikurhreppur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/sudurnesjabaer.svg b/libs/application/templates/financial-aid/src/svg/sudurnesjabaer.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/sudurnesjabaer.svg rename to libs/application/templates/financial-aid/src/svg/sudurnesjabaer.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/svalbardshreppur.svg b/libs/application/templates/financial-aid/src/svg/svalbardshreppur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/svalbardshreppur.svg rename to libs/application/templates/financial-aid/src/svg/svalbardshreppur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/svalbardsstrandarhreppur.svg b/libs/application/templates/financial-aid/src/svg/svalbardsstrandarhreppur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/svalbardsstrandarhreppur.svg rename to libs/application/templates/financial-aid/src/svg/svalbardsstrandarhreppur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/talknafjardarhreppur.svg b/libs/application/templates/financial-aid/src/svg/talknafjardarhreppur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/talknafjardarhreppur.svg rename to libs/application/templates/financial-aid/src/svg/talknafjardarhreppur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/thingeyjarsveit.svg b/libs/application/templates/financial-aid/src/svg/thingeyjarsveit.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/thingeyjarsveit.svg rename to libs/application/templates/financial-aid/src/svg/thingeyjarsveit.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/tjorneshreppur.svg b/libs/application/templates/financial-aid/src/svg/tjorneshreppur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/tjorneshreppur.svg rename to libs/application/templates/financial-aid/src/svg/tjorneshreppur.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/vestmannaeyjabaer.svg b/libs/application/templates/financial-aid/src/svg/vestmannaeyjabaer.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/vestmannaeyjabaer.svg rename to libs/application/templates/financial-aid/src/svg/vestmannaeyjabaer.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/vesturbyggd.svg b/libs/application/templates/financial-aid/src/svg/vesturbyggd.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/vesturbyggd.svg rename to libs/application/templates/financial-aid/src/svg/vesturbyggd.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/vogar.svg b/libs/application/templates/financial-aid/src/svg/vogar.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/vogar.svg rename to libs/application/templates/financial-aid/src/svg/vogar.svg diff --git a/libs/application/templates/financial-aid/src/assets/svg/vopnafjardarhreppur.svg b/libs/application/templates/financial-aid/src/svg/vopnafjardarhreppur.svg similarity index 100% rename from libs/application/templates/financial-aid/src/assets/svg/vopnafjardarhreppur.svg rename to libs/application/templates/financial-aid/src/svg/vopnafjardarhreppur.svg diff --git a/libs/application/types/src/lib/Fields.ts b/libs/application/types/src/lib/Fields.ts index e5425ca66464..0ff0be0e3bfe 100644 --- a/libs/application/types/src/lib/Fields.ts +++ b/libs/application/types/src/lib/Fields.ts @@ -259,8 +259,6 @@ export enum FieldTypes { FIND_VEHICLE = 'FIND_VEHICLE', VEHICLE_RADIO = 'VEHICLE_RADIO', STATIC_TABLE = 'STATIC_TABLE', - ACCORDION = 'ACCORDION', - BANK_ACCOUNT = 'BANK_ACCOUNT', SLIDER = 'SLIDER', DISPLAY = 'DISPLAY', } @@ -296,8 +294,6 @@ export enum FieldComponents { FIND_VEHICLE = 'FindVehicleFormField', VEHICLE_RADIO = 'VehicleRadioFormField', STATIC_TABLE = 'StaticTableFormField', - ACCORDION = 'AccordionFormField', - BANK_ACCOUNT = 'BankAccountFormField', SLIDER = 'SliderFormField', DISPLAY = 'DisplayFormField', } @@ -545,30 +541,6 @@ export interface ImageField extends BaseField { imagePosition?: ImagePositionProps | Array } -export type AccordionItem = { - itemTitle: FormText - itemContent: FormText -} - -export interface AccordionField extends BaseField { - readonly type: FieldTypes.ACCORDION - component: FieldComponents.ACCORDION - accordionItems: - | Array - | ((application: Application) => Array) - marginTop?: ResponsiveProp - marginBottom?: ResponsiveProp - titleVariant?: TitleVariants -} - -export interface BankAccountField extends BaseField { - readonly type: FieldTypes.BANK_ACCOUNT - component: FieldComponents.BANK_ACCOUNT - marginTop?: ResponsiveProp - marginBottom?: ResponsiveProp - titleVariant?: TitleVariants -} - export interface PdfLinkButtonField extends BaseField { readonly type: FieldTypes.PDF_LINK_BUTTON component: FieldComponents.PDF_LINK_BUTTON @@ -844,7 +816,5 @@ export type Field = | FindVehicleField | VehicleRadioField | StaticTableField - | AccordionField - | BankAccountField | SliderField | DisplayField diff --git a/libs/application/types/src/lib/Form.ts b/libs/application/types/src/lib/Form.ts index e3656d70d405..379ccfa0cce4 100644 --- a/libs/application/types/src/lib/Form.ts +++ b/libs/application/types/src/lib/Form.ts @@ -79,7 +79,7 @@ export interface Form { children: FormChildren[] icon?: string id: string - logo?: FormComponent + logo?: React.FC> mode?: FormModes renderLastScreenBackButton?: boolean renderLastScreenButton?: boolean diff --git a/libs/application/ui-fields/src/lib/AccordionFormField/AccordionFormField.tsx b/libs/application/ui-fields/src/lib/AccordionFormField/AccordionFormField.tsx deleted file mode 100644 index 26365509c62f..000000000000 --- a/libs/application/ui-fields/src/lib/AccordionFormField/AccordionFormField.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { - AccordionField, - AccordionItem as AccordionItemType, - FieldBaseProps, -} from '@island.is/application/types' -import { Accordion, AccordionItem, Box, Text } from '@island.is/island-ui/core' -import { useLocale } from '@island.is/localization' -import { formatText, formatTextWithLocale } from '@island.is/application/core' -import { Markdown } from '@island.is/shared/components' -import { useEffect, useState } from 'react' -import { Locale } from '@island.is/shared/types' - -interface Props extends FieldBaseProps { - field: AccordionField -} - -export const AccordionFormField = ({ field, application }: Props) => { - const [items, setItems] = useState>() - const { formatMessage, lang: locale } = useLocale() - const { accordionItems, marginBottom, marginTop, title, titleVariant } = field - - useEffect(() => { - if (typeof accordionItems === 'function') { - setItems(accordionItems(application)) - } else { - setItems(accordionItems) - } - }, [accordionItems]) - - if (!items || items.length === 0) { - return null - } - - return ( - - {title && ( - - - {formatTextWithLocale( - field.title, - application, - locale as Locale, - formatMessage, - )} - - - )} - - {items.map((item, index) => { - return ( - - - {formatText(item.itemContent, application, formatMessage)} - - - ) - })} - - - ) -} diff --git a/libs/application/ui-fields/src/lib/BankAccountFormField/BankAccountFormField.tsx b/libs/application/ui-fields/src/lib/BankAccountFormField/BankAccountFormField.tsx deleted file mode 100644 index 01b74ba49222..000000000000 --- a/libs/application/ui-fields/src/lib/BankAccountFormField/BankAccountFormField.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { - coreDefaultFieldMessages, - formatText, - formatTextWithLocale, -} from '@island.is/application/core' -import { BankAccountField, FieldBaseProps } from '@island.is/application/types' -import { Box, GridColumn, GridRow, Text } from '@island.is/island-ui/core' -import { useLocale } from '@island.is/localization' -import { InputController } from '@island.is/shared/form-fields' -import { Locale } from '@island.is/shared/types' - -interface Props extends FieldBaseProps { - field: BankAccountField -} - -export const BankAccountFormField = ({ field, application }: Props) => { - const { formatMessage, lang: locale } = useLocale() - const { marginBottom, marginTop, title, titleVariant, id } = field - const bankNumber = formatText( - coreDefaultFieldMessages.defaultBankAccountBankNumber, - application, - formatMessage, - ) - const ledger = formatText( - coreDefaultFieldMessages.defaultBankAccountLedger, - application, - formatMessage, - ) - const accountNumber = formatText( - coreDefaultFieldMessages.defaultBankAccountAccountNumber, - application, - formatMessage, - ) - - return ( - - {title && ( - - - {formatTextWithLocale( - field.title, - application, - locale as Locale, - formatMessage, - )} - - - )} - - - - - - - - - - - - - - - - - - - ) -} diff --git a/libs/application/ui-fields/src/lib/index.ts b/libs/application/ui-fields/src/lib/index.ts index d796e89afbe9..d805491651c5 100644 --- a/libs/application/ui-fields/src/lib/index.ts +++ b/libs/application/ui-fields/src/lib/index.ts @@ -28,7 +28,5 @@ export { FieldsRepeaterFormField } from './FieldsRepeaterFormField/FieldsRepeate export { FindVehicleFormField } from './FindVehicleFormField/FindVehicleFormField' export { VehicleRadioFormField } from './VehicleRadioFormField/VehicleRadioFormField' export { StaticTableFormField } from './StaticTableFormField/StaticTableFormField' -export { AccordionFormField } from './AccordionFormField/AccordionFormField' -export { BankAccountFormField } from './BankAccountFormField/BankAccountFormField' export { SliderFormField } from './SliderFormField/SliderFormField' export { DisplayFormField } from './DisplayFormField/DisplayFormField' diff --git a/libs/application/ui-shell/src/lib/FormShell.tsx b/libs/application/ui-shell/src/lib/FormShell.tsx index f2fc28a32899..d3be0a1e4747 100644 --- a/libs/application/ui-shell/src/lib/FormShell.tsx +++ b/libs/application/ui-shell/src/lib/FormShell.tsx @@ -1,5 +1,4 @@ import React, { FC, useEffect, useReducer, useState } from 'react' - import { Application, Form, @@ -12,7 +11,6 @@ import { GridContainer, GridRow, } from '@island.is/island-ui/core' - import { useLocale } from '@island.is/localization' import { useUserInfo } from '@island.is/react-spa/bff' import { ErrorShell } from '../components/ErrorShell' @@ -26,7 +24,6 @@ import { initializeReducer, } from '../reducer/ApplicationFormReducer' import { ActionTypes } from '../reducer/ReducerTypes' -import { getFormComponent } from '../utils' import * as styles from './FormShell.css' export const FormShell: FC< @@ -69,7 +66,7 @@ export const FormShell: FC< } = state.form const showProgressTag = mode !== FormModes.DRAFT const currentScreen = screens[activeScreen] - const FormLogo = getFormComponent(form.logo, storedApplication) + const FormLogo = form.logo const getDraftSectionCurrentScreen = (): number | undefined => { const currentDraftScreenSection = sections.find( diff --git a/libs/application/ui-shell/src/utils.ts b/libs/application/ui-shell/src/utils.ts index 9fb308f3a1b3..e027cfd073d5 100644 --- a/libs/application/ui-shell/src/utils.ts +++ b/libs/application/ui-shell/src/utils.ts @@ -1,10 +1,8 @@ import { getValueViaPath } from '@island.is/application/core' import { - Application, DataProviderItem, ExternalData, FieldTypes, - FormComponent, FormItemTypes, FormValue, RecordObject, @@ -156,38 +154,3 @@ export const parseMessage = (message?: string) => { return message } - -function isFunctionalComponent( - component: FormComponent | undefined, -): component is React.FC> { - if (!component) return false - return ( - typeof component === 'function' && - !(component.prototype && component.prototype.isReactComponent) && - component.length === 0 - ) -} - -function isFunctionReturningComponent( - component: FormComponent | undefined, -): component is ( - application: Application, -) => React.FC> | null | undefined { - if (!component) return false - return typeof component === 'function' && component.length === 1 -} - -export function getFormComponent( - component: FormComponent | undefined, - application: Application, -) { - if (isFunctionalComponent(component)) { - return component - } - - if (isFunctionReturningComponent(component)) { - return component(application) - } - - return null -} diff --git a/libs/financial-aid/shared/src/lib/formatters.ts b/libs/financial-aid/shared/src/lib/formatters.ts index b9a9aa00d238..46111cb4b699 100644 --- a/libs/financial-aid/shared/src/lib/formatters.ts +++ b/libs/financial-aid/shared/src/lib/formatters.ts @@ -360,12 +360,8 @@ export const applicationStateToFilterEnum: KeyMapping< export const aidCalculator = ( homeCircumstances: HomeCircumstances, - aid?: Aid, -): number | undefined => { - if (!aid) { - return undefined - } - + aid: Aid, +): number => { switch (homeCircumstances) { case 'OwnPlace': return aid.ownPlace diff --git a/libs/shared/components/src/Markdown/markdownOptions.tsx b/libs/shared/components/src/Markdown/markdownOptions.tsx index f5f1b34b5e52..bf1df1be6348 100644 --- a/libs/shared/components/src/Markdown/markdownOptions.tsx +++ b/libs/shared/components/src/Markdown/markdownOptions.tsx @@ -6,6 +6,7 @@ import { Text, } from '@island.is/island-ui/core' import { MarkdownToJSX } from 'markdown-to-jsx' +import React from 'react' import * as styles from './Markdown.css' import { OptionalOverrides } from './Markdown' From 02f2aa441b586ac1edd812384441ed2f5d793385 Mon Sep 17 00:00:00 2001 From: Steinar Freyr Kristinsson <86603428+advaniasteinar@users.noreply.github.com> Date: Tue, 3 Dec 2024 14:58:15 +0100 Subject: [PATCH 15/85] fix(Consultation-portal): KAM-2800 user cannot deregister from "all" cases (#17113) * fix(consultation-portal): Enable user to deregister from "all" cases * chore(consultation-portal): Remove unused hook --- .../SubscriptionTable/SubscriptionTable.tsx | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/apps/consultation-portal/screens/Subscriptions/components/SubscriptionTable/SubscriptionTable.tsx b/apps/consultation-portal/screens/Subscriptions/components/SubscriptionTable/SubscriptionTable.tsx index 1fb8965152a2..f1de635486df 100644 --- a/apps/consultation-portal/screens/Subscriptions/components/SubscriptionTable/SubscriptionTable.tsx +++ b/apps/consultation-portal/screens/Subscriptions/components/SubscriptionTable/SubscriptionTable.tsx @@ -1,10 +1,5 @@ import React from 'react' -import { - Stack, - Table as T, - Text, - useBreakpoint, -} from '@island.is/island-ui/core' +import { Stack, Table as T, Text } from '@island.is/island-ui/core' import { mapIsToEn, sortLocale } from '../../../../utils/helpers' import { SubscriptionArray } from '../../../../types/interfaces' import { Area } from '../../../../types/enums' @@ -37,7 +32,6 @@ const SubscriptionTable = ({ searchValue, isMySubscriptions, }: Props) => { - const { md: mdBreakpoint } = useBreakpoint() const { Table, Body } = T const loc = localization.subscriptionTable const mappedCurrentTab = mapIsToEn[currentTab] @@ -54,8 +48,13 @@ const SubscriptionTable = ({ data: thisData, }) - if ( + const hasNoItemsToShow = dataToRender.length === 0 && + dontShowNew !== false && + dontShowChanges !== false + + if ( + hasNoItemsToShow && !subscribedToAllNewObj.checked && !subscribedToAllChangesObj.checked ) { From 1dfbe91d82d9fe2b09a39d9ce8fa5c493f922048 Mon Sep 17 00:00:00 2001 From: Kristofer Date: Tue, 3 Dec 2024 15:48:45 +0000 Subject: [PATCH 16/85] chore(infra): Move `ARG` declaration for clarity (#17054) * Move ARG usage for cache-bust minimization * Fewer lines changed with `ENV` move * Less-changed ARGs * Add 'ARG APP' to all "Stage-specific ARGs" --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- scripts/ci/Dockerfile | 72 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 12 deletions(-) diff --git a/scripts/ci/Dockerfile b/scripts/ci/Dockerfile index 426f9b8ab8e5..2b9023583788 100644 --- a/scripts/ci/Dockerfile +++ b/scripts/ci/Dockerfile @@ -1,11 +1,17 @@ # This is a multi-stage Dockerfile which contains all CI-related operations as well as images to be deployed in production + +# Global ARGs (used across multiple stages) ARG PLAYWRIGHT_VERSION ARG DOCKER_ECR_REGISTRY=public.ecr.aws/docker ARG DOCKER_IMAGE_REGISTRY=${DOCKER_ECR_REGISTRY%/docker} # Alias DOCKER_IMAGE_REGISTRY to DOCKER_REGISTRY for backwards compatibility ARG DOCKER_REGISTRY=${DOCKER_IMAGE_REGISTRY}/docker ARG NODE_IMAGE_TAG +ARG APP_HOME +ARG APP_DIST_HOME=dist/${APP_HOME} +ARG APP +# Base image for dependencies FROM ${DOCKER_REGISTRY}/library/node:${NODE_IMAGE_TAG} AS deps # hadolint ignore=DL3018 @@ -21,6 +27,7 @@ COPY .yarn/ ./.yarn RUN apk add --update --no-cache python3 build-base gcc && ln -sf /usr/bin/python3 /usr/bin/python RUN CI=true yarn install --immutable +# Image with source code FROM deps AS src RUN wget -O /tmp/jq-linux64 https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 && \ @@ -30,7 +37,11 @@ RUN wget -O /tmp/jq-linux64 https://github.com/stedolan/jq/releases/download/jq- # image with the source code COPY . . + +# Build stage FROM src AS builder + +# Stage-specific ARGs ARG APP ARG APP_DIST_HOME ENV APP=${APP} @@ -39,6 +50,7 @@ ENV NODE_OPTIONS="--max-old-space-size=8192" RUN yarn run build ${APP} --prod + # This is base image for containers that are to be deployed FROM ${DOCKER_REGISTRY}/library/node:${NODE_IMAGE_TAG} AS output-base ARG APP @@ -64,9 +76,14 @@ USER runner FROM output-base-with-pg AS output-express +# Stage-specific ARGs +ARG APP_DIST_HOME +ARG GIT_BRANCH +ARG GIT_COMMIT_SHA +ARG GIT_REPOSITORY_URL + COPY --from=builder /build/${APP_DIST_HOME} /webapp/ -ARG GIT_BRANCH GIT_COMMIT_SHA GIT_REPOSITORY_URL ENV GIT_BRANCH=${GIT_BRANCH} GIT_COMMIT_SHA=${GIT_COMMIT_SHA} GIT_REPOSITORY_URL=${GIT_REPOSITORY_URL} ENV DD_GIT_BRANCH=${GIT_BRANCH} DD_GIT_COMMIT_SHA=${GIT_COMMIT_SHA} DD_GIT_REPOSITORY_URL=${GIT_REPOSITORY_URL} LABEL branch=${GIT_BRANCH} @@ -75,24 +92,35 @@ ENTRYPOINT [] CMD [ "node", "--no-experimental-fetch", "main.js" ] FROM output-base-with-pg AS output-next + +# Stage-specific ARGs +ARG APP +ARG APP_DIST_HOME +ARG GIT_BRANCH +ARG GIT_COMMIT_SHA +ARG GIT_REPOSITORY_URL + ENV PORT=4200 # TODO: smallify COPY --from=deps /build/node_modules /webapp/node_modules COPY --from=builder /build/${APP_DIST_HOME} /webapp/ -ARG GIT_BRANCH GIT_COMMIT_SHA GIT_REPOSITORY_URL ENV GIT_BRANCH=${GIT_BRANCH} GIT_COMMIT_SHA=${GIT_COMMIT_SHA} GIT_REPOSITORY_URL=${GIT_REPOSITORY_URL} ENV DD_GIT_BRANCH=${GIT_BRANCH} DD_GIT_COMMIT_SHA=${GIT_COMMIT_SHA} DD_GIT_REPOSITORY_URL=${GIT_REPOSITORY_URL} LABEL branch=${GIT_BRANCH} LABEL commit=${GIT_COMMIT_SHA} ENTRYPOINT [ "node", "main.js" ] + FROM ${DOCKER_REGISTRY}/library/nginx:1.21-alpine AS output-static + +# Stage-specific ARGs ARG APP ARG APP_DIST_HOME -ENV APP=${APP} -ENV BASEPATH=/ +ARG GIT_BRANCH +ARG GIT_COMMIT_SHA +ARG GIT_REPOSITORY_URL RUN mkdir -p /etc/nginx/templates # hadolint ignore=DL3018 @@ -105,14 +133,23 @@ COPY scripts/dockerfile-assets/bash/extract-environment.sh /docker-entrypoint.d COPY scripts/dockerfile-assets/bash/extract-environment.js /docker-entrypoint.d COPY --from=builder /build/${APP_DIST_HOME} /usr/share/nginx/html -ARG GIT_BRANCH GIT_COMMIT_SHA GIT_REPOSITORY_URL ENV GIT_BRANCH=${GIT_BRANCH} GIT_COMMIT_SHA=${GIT_COMMIT_SHA} GIT_REPOSITORY_URL=${GIT_REPOSITORY_URL} ENV DD_GIT_BRANCH=${GIT_BRANCH} DD_GIT_COMMIT_SHA=${GIT_COMMIT_SHA} DD_GIT_REPOSITORY_URL=${GIT_REPOSITORY_URL} LABEL branch=${GIT_BRANCH} LABEL commit=${GIT_COMMIT_SHA} +ENV APP=${APP} +ENV BASEPATH=/ + FROM output-base AS output-jest +# Stage-specific ARGs +ARG APP +ARG APP_DIST_HOME +ARG GIT_BRANCH +ARG GIT_COMMIT_SHA +ARG GIT_REPOSITORY_URL + RUN echo 'module.exports = {};' > jest.config.js # hadolint ignore=DL3016 @@ -122,7 +159,6 @@ COPY --from=builder /build/${APP_DIST_HOME} /webapp/ USER runner -ARG GIT_BRANCH GIT_COMMIT_SHA GIT_REPOSITORY_URL ENV GIT_BRANCH=${GIT_BRANCH} GIT_COMMIT_SHA=${GIT_COMMIT_SHA} GIT_REPOSITORY_URL=${GIT_REPOSITORY_URL} ENV DD_GIT_BRANCH=${GIT_BRANCH} DD_GIT_COMMIT_SHA=${GIT_COMMIT_SHA} DD_GIT_REPOSITORY_URL=${GIT_REPOSITORY_URL} LABEL branch=${GIT_BRANCH} @@ -130,11 +166,19 @@ LABEL commit=${GIT_COMMIT_SHA} CMD [ "jest", "main.spec.js" ] - FROM mcr.microsoft.com/playwright:v${PLAYWRIGHT_VERSION}-focal AS playwright-base FROM playwright-base AS output-playwright +# Stage-specific ARGs +ARG APP +ARG APP_DIST_HOME +ARG APP_HOME +ARG PLAYWRIGHT_BROWSER=chromium +ARG GIT_BRANCH +ARG GIT_COMMIT_SHA +ARG GIT_REPOSITORY_URL + # TODO: remove awscli dependency (157 MB extra) # hadolint ignore=DL3008 @@ -155,12 +199,10 @@ RUN mkdir ./.yarn COPY .yarn/releases ./.yarn/releases RUN yarn install -ENV PLAYWRIGHT_BROWSER=chromium RUN yarn playwright install ${PLAYWRIGHT_BROWSER} COPY --chown=pwuser:pwuser --chmod=0755 ${APP_HOME}/entrypoint.sh . -ARG GIT_BRANCH GIT_COMMIT_SHA GIT_REPOSITORY_URL ENV GIT_BRANCH=${GIT_BRANCH} GIT_COMMIT_SHA=${GIT_COMMIT_SHA} GIT_REPOSITORY_URL=${GIT_REPOSITORY_URL} ENV DD_GIT_BRANCH=${GIT_BRANCH} DD_GIT_COMMIT_SHA=${GIT_COMMIT_SHA} DD_GIT_REPOSITORY_URL=${GIT_REPOSITORY_URL} LABEL branch=${GIT_BRANCH} @@ -169,12 +211,17 @@ ENTRYPOINT ["./entrypoint.sh"] FROM playwright-base AS output-local -ARG APP_HOME + +# Stage-specific ARGs +ARG APP ARG APP_DIST_HOME +ARG APP_HOME +ARG GIT_BRANCH +ARG GIT_COMMIT_SHA +ARG GIT_REPOSITORY_URL WORKDIR ${APP_DIST_HOME} - # node user exists in the base image RUN mkdir -p /out \ && chown node:node /out @@ -183,12 +230,13 @@ COPY --chown=pwuser:pwuser --chmod=0755 ${APP_HOME}/entrypoint.sh . USER pwuser -ARG GIT_BRANCH GIT_COMMIT_SHA GIT_REPOSITORY_URL ENV GIT_BRANCH=${GIT_BRANCH} GIT_COMMIT_SHA=${GIT_COMMIT_SHA} GIT_REPOSITORY_URL=${GIT_REPOSITORY_URL} ENV DD_GIT_BRANCH=${GIT_BRANCH} DD_GIT_COMMIT_SHA=${GIT_COMMIT_SHA} DD_GIT_REPOSITORY_URL=${GIT_REPOSITORY_URL} LABEL branch=${GIT_BRANCH} LABEL commit=${GIT_COMMIT_SHA} ENTRYPOINT ["./entrypoint.sh"] + FROM output-base AS output-native + RUN echo "not-implemented" From 04edebb832d86bda2a98c3dd98251b055362e0d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3nas=20G=2E=20Sigur=C3=B0sson?= Date: Tue, 3 Dec 2024 17:17:19 +0000 Subject: [PATCH 17/85] feat(app-sys): national-id-with-name fetch form more endpoints (#17101) * feat: fetch from more endpoints * feat: just extra company search * feat: add option to field builder to search for company * feat: error handling for company search * revert example form to the original state * . * chore: edit large margin * . * fix: lint * fix: lint --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../application/core/src/lib/fieldBuilders.ts | 12 ++ libs/application/types/src/lib/Fields.ts | 5 + .../NationalIdWithName/NationalIdWithName.tsx | 109 +++++++++++++++--- .../NationalIdWithName/graphql/queries.ts | 8 ++ .../NationalIdWithNameFormField.tsx | 65 ++++++++--- 5 files changed, 164 insertions(+), 35 deletions(-) diff --git a/libs/application/core/src/lib/fieldBuilders.ts b/libs/application/core/src/lib/fieldBuilders.ts index 1ef54e3fa911..d5390fb6bbb6 100644 --- a/libs/application/core/src/lib/fieldBuilders.ts +++ b/libs/application/core/src/lib/fieldBuilders.ts @@ -748,6 +748,12 @@ export const buildNationalIdWithNameField = ( nameDefaultValue, errorMessage, minAgePerson, + searchPersons, + searchCompanies, + titleVariant, + description, + marginTop, + marginBottom, } = data return { ...extractCommonFields(data), @@ -761,9 +767,15 @@ export const buildNationalIdWithNameField = ( nameDefaultValue, errorMessage, minAgePerson, + searchPersons, + searchCompanies, children: undefined, type: FieldTypes.NATIONAL_ID_WITH_NAME, component: FieldComponents.NATIONAL_ID_WITH_NAME, + titleVariant, + description, + marginTop, + marginBottom, } } diff --git a/libs/application/types/src/lib/Fields.ts b/libs/application/types/src/lib/Fields.ts index 0ff0be0e3bfe..877dc89d4621 100644 --- a/libs/application/types/src/lib/Fields.ts +++ b/libs/application/types/src/lib/Fields.ts @@ -570,6 +570,11 @@ export interface NationalIdWithNameField extends InputField { nameDefaultValue?: string errorMessage?: string minAgePerson?: number + searchPersons?: boolean + searchCompanies?: boolean + titleVariant?: TitleVariants + marginTop?: ResponsiveProp + marginBottom?: ResponsiveProp } type Modify = Omit & R diff --git a/libs/application/ui-components/src/components/NationalIdWithName/NationalIdWithName.tsx b/libs/application/ui-components/src/components/NationalIdWithName/NationalIdWithName.tsx index d232bc7ef1fd..fb42f7b19422 100644 --- a/libs/application/ui-components/src/components/NationalIdWithName/NationalIdWithName.tsx +++ b/libs/application/ui-components/src/components/NationalIdWithName/NationalIdWithName.tsx @@ -1,5 +1,5 @@ import { FC, useEffect, useState } from 'react' -import { Box, GridRow, GridColumn } from '@island.is/island-ui/core' +import { GridRow, GridColumn } from '@island.is/island-ui/core' import { useLocale } from '@island.is/localization' import { coreErrorMessages, @@ -8,12 +8,16 @@ import { } from '@island.is/application/core' import { Application, StaticText } from '@island.is/application/types' import { gql, useLazyQuery } from '@apollo/client' -import { IdentityInput, Query } from '@island.is/api/schema' +import { + IdentityInput, + Query, + RskCompanyInfoInput, +} from '@island.is/api/schema' import { InputController } from '@island.is/shared/form-fields' import { useFormContext } from 'react-hook-form' import * as kennitala from 'kennitala' import debounce from 'lodash/debounce' -import { IDENTITY_QUERY } from './graphql/queries' +import { COMPANY_IDENTITY_QUERY, IDENTITY_QUERY } from './graphql/queries' interface NationalIdWithNameProps { id: string @@ -29,6 +33,8 @@ interface NationalIdWithNameProps { nameDefaultValue?: string errorMessage?: string minAgePerson?: number + searchPersons?: boolean + searchCompanies?: boolean } export const NationalIdWithName: FC< @@ -47,6 +53,8 @@ export const NationalIdWithName: FC< nameDefaultValue, errorMessage, minAgePerson, + searchPersons = true, + searchCompanies = false, }) => { const fieldId = customId.length > 0 ? customId : id const nameField = `${fieldId}.name` @@ -58,6 +66,8 @@ export const NationalIdWithName: FC< formState: { errors }, } = useFormContext() const [nationalIdInput, setNationalIdInput] = useState('') + const [personName, setPersonName] = useState('') + const [companyName, setCompanyName] = useState('') // get name validation errors const nameFieldErrors = errorMessage @@ -101,23 +111,73 @@ export const NationalIdWithName: FC< { onCompleted: (data) => { onNameChange && onNameChange(data.identity?.name ?? '') - setValue(nameField, data.identity?.name ?? undefined) + setPersonName(data.identity?.name ?? '') }, }, ) + // query to get company name by national id + const [ + getCompanyIdentity, + { + data: companyData, + loading: companyQueryLoading, + error: companyQueryError, + }, + ] = useLazyQuery( + gql` + ${COMPANY_IDENTITY_QUERY} + `, + { + onCompleted: (companyData) => { + onNameChange && + onNameChange(companyData.companyRegistryCompany?.name ?? '') + setCompanyName(companyData.companyRegistryCompany?.name ?? '') + }, + }, + ) + // fetch and update name when user has entered a valid national id useEffect(() => { if (nationalIdInput.length === 10 && kennitala.isValid(nationalIdInput)) { - getIdentity({ - variables: { - input: { - nationalId: nationalIdInput, - }, - }, - }) + { + searchPersons && + getIdentity({ + variables: { + input: { + nationalId: nationalIdInput, + }, + }, + }) + } + + { + searchCompanies && + getCompanyIdentity({ + variables: { + input: { + nationalId: nationalIdInput, + }, + }, + }) + } + } + }, [ + nationalIdInput, + getIdentity, + getCompanyIdentity, + searchPersons, + searchCompanies, + ]) + + useEffect(() => { + const nameInAnswers = getValueViaPath(application.answers, nameField) + if (personName && nameInAnswers !== personName) { + setValue(nameField, personName) + } else if (companyName && nameInAnswers !== companyName) { + setValue(nameField, companyName) } - }, [nationalIdInput, getIdentity]) + }, [personName, companyName, setValue, nameField, application.answers]) return ( @@ -138,7 +198,7 @@ export const NationalIdWithName: FC< onNationalIdChange && onNationalIdChange(v.target.value.replace(/\W/g, '')) })} - loading={queryLoading} + loading={searchPersons ? queryLoading : companyQueryLoading} error={nationalIdFieldErrors} disabled={disabled} /> @@ -154,12 +214,23 @@ export const NationalIdWithName: FC< } required={required} error={ - queryError || data?.identity === null - ? formatMessage( - coreErrorMessages.nationalRegistryNameNotFoundForNationalId, - ) - : nameFieldErrors && !data - ? nameFieldErrors + searchPersons + ? queryError || data?.identity === null + ? formatMessage( + coreErrorMessages.nationalRegistryNameNotFoundForNationalId, + ) + : nameFieldErrors && !data + ? nameFieldErrors + : undefined + : searchCompanies + ? companyQueryError || + companyData?.companyRegistryCompany === null + ? formatMessage( + coreErrorMessages.nationalRegistryNameNotFoundForNationalId, + ) + : nameFieldErrors && !companyData + ? nameFieldErrors + : undefined : undefined } disabled diff --git a/libs/application/ui-components/src/components/NationalIdWithName/graphql/queries.ts b/libs/application/ui-components/src/components/NationalIdWithName/graphql/queries.ts index faa1771139c6..38c8d782b2ad 100644 --- a/libs/application/ui-components/src/components/NationalIdWithName/graphql/queries.ts +++ b/libs/application/ui-components/src/components/NationalIdWithName/graphql/queries.ts @@ -6,3 +6,11 @@ export const IDENTITY_QUERY = ` } } ` + +export const COMPANY_IDENTITY_QUERY = ` + query CompanyIdentityQuery($input: RskCompanyInfoInput!) { + companyRegistryCompany(input: $input) { + name + } + } +` diff --git a/libs/application/ui-fields/src/lib/NationalIdWithNameFormField/NationalIdWithNameFormField.tsx b/libs/application/ui-fields/src/lib/NationalIdWithNameFormField/NationalIdWithNameFormField.tsx index 1caf9fd5be81..1eba53e088a2 100644 --- a/libs/application/ui-fields/src/lib/NationalIdWithNameFormField/NationalIdWithNameFormField.tsx +++ b/libs/application/ui-fields/src/lib/NationalIdWithNameFormField/NationalIdWithNameFormField.tsx @@ -1,10 +1,17 @@ -import { buildFieldRequired } from '@island.is/application/core' +import { FC } from 'react' +import { + buildFieldRequired, + formatTextWithLocale, +} from '@island.is/application/core' import { FieldBaseProps, NationalIdWithNameField, } from '@island.is/application/types' import { NationalIdWithName } from '@island.is/application/ui-components' -import { FC } from 'react' +import { Box, Text } from '@island.is/island-ui/core' +import { FieldDescription } from '@island.is/shared/form-fields' +import { Locale } from '@island.is/shared/types' +import { useLocale } from '@island.is/localization' interface Props extends FieldBaseProps { field: NationalIdWithNameField @@ -13,20 +20,46 @@ interface Props extends FieldBaseProps { export const NationalIdWithNameFormField: FC< React.PropsWithChildren > = ({ application, field }) => { + const { formatMessage, lang: locale } = useLocale() + return ( - + + {field.title && ( + + {formatTextWithLocale( + field.title, + application, + locale as Locale, + formatMessage, + )} + + )} + {field.description && ( + + )} + + ) } From 9f4f9aed9dd167566b6d26f6df2853404d726638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Tue, 3 Dec 2024 22:32:33 +0000 Subject: [PATCH 18/85] chore(j-s): Security&Integrity (#16863) * Throws proper exceptions from digital mailbox * Refactors code * Refactors code * Refactors code * Removes unused imports * Removes unused imports * Removes unused import * Removes redundant code * Rewrites internal indictment case endpoint * Adds unit tests * Reorders query conditions * Rewrites defendant indictment cases endpoint * Adds civil claimant exists guard to update and delete civil claimant * Removes unused endpoint * Uses proper subpoena gueard for limited access subpoena controller * Splits subpoena exists guard and reorders controller decorators * Adds unit test * Renames variables for clarity * Uses correct http method when getting cases * Moves subpoena status updates to subpoena module * Removes comments * Removes comments * Adds unit tests * Renames unit test * Updates unit tests * Refactors code * Adds unit tests * Adds unit tests * Fixes subpoena refresh * Cleans up some code * Removes unnecessary export * Rmoves console log. * Update apps/judicial-system/backend/src/app/modules/case/guards/test/indictmentCaseExistsForDefendantGuard.spec.ts * Updates unit test * Fixes type import * Fixes typo * Fixes typo * Fixes type decorators * Fixes typo * Refactors code * Rewrites subpoena info diff --------- Co-authored-by: unakb Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../judicial-system/api/src/app/app.module.ts | 2 + .../app/modules/backend/backend.service.ts | 19 +- .../api/src/app/modules/defendant/index.ts | 1 - .../defendant/models/defendant.model.ts | 2 +- .../api/src/app/modules/index.ts | 1 + .../api/src/app/modules/police/index.ts | 1 - .../police/models/subpoenaStatus.model.ts | 21 -- .../src/app/modules/police/police.resolver.ts | 22 --- .../dto/subpoena.input.ts} | 6 +- .../api/src/app/modules/subpoena/index.ts | 1 + .../models/subpoena.model.ts | 0 .../app/modules/subpoena/subpoena.module.ts | 8 + .../app/modules/subpoena/subpoena.resolver.ts | 53 +++++ .../src/app/modules/case/case.controller.ts | 2 +- .../app/modules/case/dto/internalCases.dto.ts | 16 -- .../app/modules/case/filters/cases.filter.ts | 4 +- .../case/filters/test/cases.filter.spec.ts | 4 +- .../indictmentCaseExistsForDefendant.guard.ts | 37 ++++ .../case/guards/mergedCaseExists.guard.ts | 2 +- ...ictmentCaseExistsForDefendantGuard.spec.ts | 161 +++++++++++++++ .../guards/test/mergedCaseExistsGuard.spec.ts | 130 +++++++++++++ .../modules/case/internalCase.controller.ts | 37 ++-- .../app/modules/case/internalCase.service.ts | 48 ++++- .../case/test/createTestingCaseModule.ts | 4 + .../getIndictmentCaseByIdGuards.spec.ts | 21 ++ .../defendant/civilClaimant.controller.ts | 17 +- .../defendant/civilClaimant.service.ts | 40 ++-- .../modules/defendant/defendant.controller.ts | 2 +- .../modules/defendant/defendant.service.ts | 38 ++-- .../civilClaimantControllerGuards.spec.ts | 18 ++ .../deleteGuards.spec.ts | 7 +- .../civilClaimantController/update.spec.ts | 7 +- .../updateGuards.spec.ts | 7 +- .../modules/event-log/eventLog.controller.ts | 3 +- .../src/app/modules/file/file.controller.ts | 2 +- .../modules/file/internalFile.controller.ts | 2 +- .../file/limitedAccessFile.controller.ts | 2 +- .../indictmentCount.controller.ts | 2 +- .../internalNotification.controller.ts | 2 +- .../notification/notification.controller.ts | 2 +- .../backend/src/app/modules/police/index.ts | 1 + .../police/internalPolice.controller.ts | 34 ---- .../police/models/subpoenaInfo.model.ts | 49 +++++ .../app/modules/police/police.controller.ts | 35 +--- .../src/app/modules/police/police.module.ts | 3 +- .../src/app/modules/police/police.service.ts | 65 +++---- .../subpoena/dto/updateSubpoena.dto.ts | 10 +- .../guards/policeSubpoenaExists.guard.ts | 28 +++ .../subpoena/guards/subpoenaExists.guard.ts | 20 +- .../test/policeSubpoenaExistsGuard.spec.ts | 110 +++++++++++ .../guards/test/subpoenaExistsGuard.spec.ts | 109 +++++++++++ .../subpoena/internalSubpoena.controller.ts | 21 +- .../limitedAccessSubpoena.controller.ts | 6 +- .../modules/subpoena/subpoena.controller.ts | 40 +++- .../app/modules/subpoena/subpoena.service.ts | 183 ++++++++++++------ .../internalSubpoenaControllerGuards.spec.ts | 17 ++ .../updateSubpoeanaGuards.spec.ts | 19 ++ .../getSubpoenaPdfGuards.spec.ts | 4 +- .../src/app/modules/cases/case.controller.ts | 8 +- .../src/app/modules/cases/case.service.ts | 87 ++------- .../modules/defenders/defender.controller.ts | 15 +- .../src/components/FormProvider/case.graphql | 1 + .../FormProvider/limitedAccessCase.graphql | 1 + .../ServiceAnnouncement.tsx | 31 +-- .../Court/Indictments/Overview/Overview.tsx | 17 +- .../Indictments/Overview/Overview.tsx | 17 +- .../useSubpoena/getSubpoenaStatus.graphql | 9 - .../web/src/utils/hooks/useSubpoena/index.ts | 29 ++- .../utils/hooks/useSubpoena/subpoena.graphql | 14 ++ .../xrd-api/src/app/app.controller.ts | 15 +- .../xrd-api/src/app/app.service.ts | 1 + .../audit-trail/src/lib/auditTrail.service.ts | 1 - .../lawyers/src/lib/lawyers.service.ts | 11 +- 73 files changed, 1246 insertions(+), 519 deletions(-) delete mode 100644 apps/judicial-system/api/src/app/modules/police/models/subpoenaStatus.model.ts rename apps/judicial-system/api/src/app/modules/{police/dto/subpoenaStatus.input.ts => subpoena/dto/subpoena.input.ts} (70%) create mode 100644 apps/judicial-system/api/src/app/modules/subpoena/index.ts rename apps/judicial-system/api/src/app/modules/{defendant => subpoena}/models/subpoena.model.ts (100%) create mode 100644 apps/judicial-system/api/src/app/modules/subpoena/subpoena.module.ts create mode 100644 apps/judicial-system/api/src/app/modules/subpoena/subpoena.resolver.ts delete mode 100644 apps/judicial-system/backend/src/app/modules/case/dto/internalCases.dto.ts create mode 100644 apps/judicial-system/backend/src/app/modules/case/guards/indictmentCaseExistsForDefendant.guard.ts create mode 100644 apps/judicial-system/backend/src/app/modules/case/guards/test/indictmentCaseExistsForDefendantGuard.spec.ts create mode 100644 apps/judicial-system/backend/src/app/modules/case/guards/test/mergedCaseExistsGuard.spec.ts create mode 100644 apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/getIndictmentCaseByIdGuards.spec.ts create mode 100644 apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/civilClaimantControllerGuards.spec.ts delete mode 100644 apps/judicial-system/backend/src/app/modules/police/internalPolice.controller.ts create mode 100644 apps/judicial-system/backend/src/app/modules/police/models/subpoenaInfo.model.ts create mode 100644 apps/judicial-system/backend/src/app/modules/subpoena/guards/policeSubpoenaExists.guard.ts create mode 100644 apps/judicial-system/backend/src/app/modules/subpoena/guards/test/policeSubpoenaExistsGuard.spec.ts create mode 100644 apps/judicial-system/backend/src/app/modules/subpoena/guards/test/subpoenaExistsGuard.spec.ts create mode 100644 apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/internalSubpoenaControllerGuards.spec.ts create mode 100644 apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/updateSubpoeanaGuards.spec.ts delete mode 100644 apps/judicial-system/web/src/utils/hooks/useSubpoena/getSubpoenaStatus.graphql create mode 100644 apps/judicial-system/web/src/utils/hooks/useSubpoena/subpoena.graphql diff --git a/apps/judicial-system/api/src/app/app.module.ts b/apps/judicial-system/api/src/app/app.module.ts index d73082ad8cd3..b9d12a0626b1 100644 --- a/apps/judicial-system/api/src/app/app.module.ts +++ b/apps/judicial-system/api/src/app/app.module.ts @@ -34,6 +34,7 @@ import { IndictmentCountModule, InstitutionModule, PoliceModule, + SubpoenaModule, UserModule, } from './modules' @@ -68,6 +69,7 @@ const autoSchemaFile = production CaseListModule, DefendantModule, DefenderModule, + SubpoenaModule, IndictmentCountModule, FileModule, InstitutionModule, diff --git a/apps/judicial-system/api/src/app/modules/backend/backend.service.ts b/apps/judicial-system/api/src/app/modules/backend/backend.service.ts index 806cb2c44ee2..8069fc52faac 100644 --- a/apps/judicial-system/api/src/app/modules/backend/backend.service.ts +++ b/apps/judicial-system/api/src/app/modules/backend/backend.service.ts @@ -41,9 +41,9 @@ import { Institution } from '../institution' import { PoliceCaseFile, PoliceCaseInfo, - SubpoenaStatus, UploadPoliceCaseFileResponse, } from '../police' +import { Subpoena } from '../subpoena' import { backendModuleConfig } from './backend.config' @Injectable() @@ -326,13 +326,6 @@ export class BackendService extends DataSource<{ req: Request }> { return this.get(`case/${caseId}/policeFiles`) } - getSubpoenaStatus( - caseId: string, - subpoenaId: string, - ): Promise { - return this.get(`case/${caseId}/subpoenaStatus/${subpoenaId}`) - } - getPoliceCaseInfo(caseId: string): Promise { return this.get(`case/${caseId}/policeCaseInfo`) } @@ -369,6 +362,16 @@ export class BackendService extends DataSource<{ req: Request }> { return this.delete(`case/${caseId}/defendant/${defendantId}`) } + getSubpoena( + caseId: string, + defendantId: string, + subpoenaId: string, + ): Promise { + return this.get( + `case/${caseId}/defendant/${defendantId}/subpoena/${subpoenaId}`, + ) + } + createCivilClaimant( caseId: string, createCivilClaimant: unknown, diff --git a/apps/judicial-system/api/src/app/modules/defendant/index.ts b/apps/judicial-system/api/src/app/modules/defendant/index.ts index 040ddad841e3..0811956a0ca8 100644 --- a/apps/judicial-system/api/src/app/modules/defendant/index.ts +++ b/apps/judicial-system/api/src/app/modules/defendant/index.ts @@ -2,4 +2,3 @@ export { Defendant } from './models/defendant.model' export { DeleteDefendantResponse } from './models/delete.response' export { CivilClaimant } from './models/civilClaimant.model' export { DeleteCivilClaimantResponse } from './models/deleteCivilClaimant.response' -export { Subpoena } from './models/subpoena.model' diff --git a/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts b/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts index b5276f9bc104..8a692f3a9ade 100644 --- a/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts +++ b/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts @@ -8,7 +8,7 @@ import { SubpoenaType, } from '@island.is/judicial-system/types' -import { Subpoena } from './subpoena.model' +import { Subpoena } from '../../subpoena' registerEnumType(Gender, { name: 'Gender' }) registerEnumType(DefendantPlea, { name: 'DefendantPlea' }) diff --git a/apps/judicial-system/api/src/app/modules/index.ts b/apps/judicial-system/api/src/app/modules/index.ts index de4bc6ecb048..6615aac49117 100644 --- a/apps/judicial-system/api/src/app/modules/index.ts +++ b/apps/judicial-system/api/src/app/modules/index.ts @@ -17,3 +17,4 @@ export { EventLogModule } from './event-log/eventLog.module' export { backendModuleConfig } from './backend/backend.config' export { BackendService } from './backend/backend.service' export { BackendModule } from './backend/backend.module' +export { SubpoenaModule } from './subpoena/subpoena.module' diff --git a/apps/judicial-system/api/src/app/modules/police/index.ts b/apps/judicial-system/api/src/app/modules/police/index.ts index a1fe72f38c8e..c2c2457a57b5 100644 --- a/apps/judicial-system/api/src/app/modules/police/index.ts +++ b/apps/judicial-system/api/src/app/modules/police/index.ts @@ -1,4 +1,3 @@ export { PoliceCaseInfo } from './models/policeCaseInfo.model' -export { SubpoenaStatus } from './models/subpoenaStatus.model' export { PoliceCaseFile } from './models/policeCaseFile.model' export { UploadPoliceCaseFileResponse } from './models/uploadPoliceCaseFile.response' diff --git a/apps/judicial-system/api/src/app/modules/police/models/subpoenaStatus.model.ts b/apps/judicial-system/api/src/app/modules/police/models/subpoenaStatus.model.ts deleted file mode 100644 index 6f7a85461e10..000000000000 --- a/apps/judicial-system/api/src/app/modules/police/models/subpoenaStatus.model.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Field, ObjectType } from '@nestjs/graphql' - -import { ServiceStatus } from '@island.is/judicial-system/types' - -@ObjectType() -export class SubpoenaStatus { - @Field(() => ServiceStatus, { nullable: true }) - readonly serviceStatus?: ServiceStatus - - @Field(() => String, { nullable: true }) - readonly servedBy?: string - - @Field(() => String, { nullable: true }) - readonly comment?: string - - @Field(() => String, { nullable: true }) - readonly serviceDate?: string - - @Field(() => String, { nullable: true }) - readonly defenderNationalId?: string -} diff --git a/apps/judicial-system/api/src/app/modules/police/police.resolver.ts b/apps/judicial-system/api/src/app/modules/police/police.resolver.ts index 1eef19336e76..8d4fbf9cd90a 100644 --- a/apps/judicial-system/api/src/app/modules/police/police.resolver.ts +++ b/apps/judicial-system/api/src/app/modules/police/police.resolver.ts @@ -17,11 +17,9 @@ import type { User } from '@island.is/judicial-system/types' import { BackendService } from '../backend' import { PoliceCaseFilesQueryInput } from './dto/policeCaseFiles.input' import { PoliceCaseInfoQueryInput } from './dto/policeCaseInfo.input' -import { SubpoenaStatusQueryInput } from './dto/subpoenaStatus.input' import { UploadPoliceCaseFileInput } from './dto/uploadPoliceCaseFile.input' import { PoliceCaseFile } from './models/policeCaseFile.model' import { PoliceCaseInfo } from './models/policeCaseInfo.model' -import { SubpoenaStatus } from './models/subpoenaStatus.model' import { UploadPoliceCaseFileResponse } from './models/uploadPoliceCaseFile.response' @UseGuards(JwtGraphQlAuthGuard) @@ -51,26 +49,6 @@ export class PoliceResolver { ) } - @Query(() => SubpoenaStatus, { nullable: true }) - subpoenaStatus( - @Args('input', { type: () => SubpoenaStatusQueryInput }) - input: SubpoenaStatusQueryInput, - @CurrentGraphQlUser() user: User, - @Context('dataSources') - { backendService }: { backendService: BackendService }, - ): Promise { - this.logger.debug( - `Getting subpoena status for subpoena ${input.subpoenaId} of case ${input.caseId}`, - ) - - return this.auditTrailService.audit( - user.id, - AuditedAction.GET_SUBPOENA_STATUS, - backendService.getSubpoenaStatus(input.caseId, input.subpoenaId), - input.caseId, - ) - } - @Query(() => [PoliceCaseInfo], { nullable: true }) policeCaseInfo( @Args('input', { type: () => PoliceCaseInfoQueryInput }) diff --git a/apps/judicial-system/api/src/app/modules/police/dto/subpoenaStatus.input.ts b/apps/judicial-system/api/src/app/modules/subpoena/dto/subpoena.input.ts similarity index 70% rename from apps/judicial-system/api/src/app/modules/police/dto/subpoenaStatus.input.ts rename to apps/judicial-system/api/src/app/modules/subpoena/dto/subpoena.input.ts index dcc11761c0be..4f0e3ca81c1f 100644 --- a/apps/judicial-system/api/src/app/modules/police/dto/subpoenaStatus.input.ts +++ b/apps/judicial-system/api/src/app/modules/subpoena/dto/subpoena.input.ts @@ -3,11 +3,15 @@ import { Allow } from 'class-validator' import { Field, ID, InputType } from '@nestjs/graphql' @InputType() -export class SubpoenaStatusQueryInput { +export class SubpoenaQueryInput { @Allow() @Field(() => ID) readonly caseId!: string + @Allow() + @Field(() => ID) + readonly defendantId!: string + @Allow() @Field(() => ID) readonly subpoenaId!: string diff --git a/apps/judicial-system/api/src/app/modules/subpoena/index.ts b/apps/judicial-system/api/src/app/modules/subpoena/index.ts new file mode 100644 index 000000000000..59471a541a5d --- /dev/null +++ b/apps/judicial-system/api/src/app/modules/subpoena/index.ts @@ -0,0 +1 @@ +export { Subpoena } from './models/subpoena.model' diff --git a/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts b/apps/judicial-system/api/src/app/modules/subpoena/models/subpoena.model.ts similarity index 100% rename from apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts rename to apps/judicial-system/api/src/app/modules/subpoena/models/subpoena.model.ts diff --git a/apps/judicial-system/api/src/app/modules/subpoena/subpoena.module.ts b/apps/judicial-system/api/src/app/modules/subpoena/subpoena.module.ts new file mode 100644 index 000000000000..313600b022e9 --- /dev/null +++ b/apps/judicial-system/api/src/app/modules/subpoena/subpoena.module.ts @@ -0,0 +1,8 @@ +import { Module } from '@nestjs/common' + +import { SubpoenaResolver } from './subpoena.resolver' + +@Module({ + providers: [SubpoenaResolver], +}) +export class SubpoenaModule {} diff --git a/apps/judicial-system/api/src/app/modules/subpoena/subpoena.resolver.ts b/apps/judicial-system/api/src/app/modules/subpoena/subpoena.resolver.ts new file mode 100644 index 000000000000..51752826bec6 --- /dev/null +++ b/apps/judicial-system/api/src/app/modules/subpoena/subpoena.resolver.ts @@ -0,0 +1,53 @@ +import { Inject, UseGuards } from '@nestjs/common' +import { Args, Context, Query, Resolver } from '@nestjs/graphql' + +import type { Logger } from '@island.is/logging' +import { LOGGER_PROVIDER } from '@island.is/logging' + +import { + AuditedAction, + AuditTrailService, +} from '@island.is/judicial-system/audit-trail' +import { + CurrentGraphQlUser, + JwtGraphQlAuthGuard, +} from '@island.is/judicial-system/auth' +import type { User } from '@island.is/judicial-system/types' + +import { BackendService } from '../backend' +import { SubpoenaQueryInput } from './dto/subpoena.input' +import { Subpoena } from './models/subpoena.model' + +@UseGuards(JwtGraphQlAuthGuard) +@Resolver() +export class SubpoenaResolver { + constructor( + private readonly auditTrailService: AuditTrailService, + @Inject(LOGGER_PROVIDER) + private readonly logger: Logger, + ) {} + + @Query(() => Subpoena, { nullable: true }) + subpoena( + @Args('input', { type: () => SubpoenaQueryInput }) + input: SubpoenaQueryInput, + @CurrentGraphQlUser() user: User, + @Context('dataSources') + { backendService }: { backendService: BackendService }, + ): Promise { + this.logger.debug( + `Getting subpoena ${input.subpoenaId} for defendant ${input.defendantId} of case ${input.caseId}`, + ) + + return this.auditTrailService.audit( + user.id, + AuditedAction.GET_SUBPOENA, + backendService.getSubpoena( + input.caseId, + input.defendantId, + input.subpoenaId, + ), + input.caseId, + ) + } +} diff --git a/apps/judicial-system/backend/src/app/modules/case/case.controller.ts b/apps/judicial-system/backend/src/app/modules/case/case.controller.ts index 63b82483e3a9..9728dc420508 100644 --- a/apps/judicial-system/backend/src/app/modules/case/case.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/case/case.controller.ts @@ -354,7 +354,7 @@ export class CaseController { ) @UseInterceptors(CompletedAppealAccessedInterceptor, CaseInterceptor) @Get('case/:caseId') - @ApiOkResponse({ type: Case, description: 'Gets an existing case' }) + @ApiOkResponse({ type: Case, description: 'Gets an existing case by id' }) getById(@Param('caseId') caseId: string, @CurrentCase() theCase: Case): Case { this.logger.debug(`Getting case ${caseId} by id`) diff --git a/apps/judicial-system/backend/src/app/modules/case/dto/internalCases.dto.ts b/apps/judicial-system/backend/src/app/modules/case/dto/internalCases.dto.ts deleted file mode 100644 index d9c3d5c34a08..000000000000 --- a/apps/judicial-system/backend/src/app/modules/case/dto/internalCases.dto.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Transform } from 'class-transformer' -import { IsNotEmpty, IsString, MaxLength, MinLength } from 'class-validator' - -import { ApiProperty } from '@nestjs/swagger' - -import { nationalIdTransformer } from '../../../transformers' - -export class InternalCasesDto { - @IsNotEmpty() - @IsString() - @MinLength(10) - @MaxLength(10) - @Transform(nationalIdTransformer) - @ApiProperty({ type: String }) - readonly nationalId!: string -} diff --git a/apps/judicial-system/backend/src/app/modules/case/filters/cases.filter.ts b/apps/judicial-system/backend/src/app/modules/case/filters/cases.filter.ts index bc5f2f9f3a5f..04c4e62bff2a 100644 --- a/apps/judicial-system/backend/src/app/modules/case/filters/cases.filter.ts +++ b/apps/judicial-system/backend/src/app/modules/case/filters/cases.filter.ts @@ -187,7 +187,6 @@ const getPrisonStaffUserCasesQueryFilter = (): WhereOptions => { return { [Op.and]: [ { is_archived: false }, - { state: CaseState.ACCEPTED }, { type: [ CaseType.CUSTODY, @@ -195,6 +194,7 @@ const getPrisonStaffUserCasesQueryFilter = (): WhereOptions => { CaseType.PAROLE_REVOCATION, ], }, + { state: CaseState.ACCEPTED }, { decision: [CaseDecision.ACCEPTING, CaseDecision.ACCEPTING_PARTIALLY] }, ], } @@ -205,8 +205,8 @@ const getPrisonAdminUserCasesQueryFilter = (): WhereOptions => { is_archived: false, [Op.or]: [ { - state: CaseState.ACCEPTED, type: [...restrictionCases, CaseType.PAROLE_REVOCATION], + state: CaseState.ACCEPTED, }, { type: indictmentCases, diff --git a/apps/judicial-system/backend/src/app/modules/case/filters/test/cases.filter.spec.ts b/apps/judicial-system/backend/src/app/modules/case/filters/test/cases.filter.spec.ts index 4d6027e5d5b4..a67c842f797f 100644 --- a/apps/judicial-system/backend/src/app/modules/case/filters/test/cases.filter.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/filters/test/cases.filter.spec.ts @@ -347,7 +347,6 @@ describe('getCasesQueryFilter', () => { expect(res).toStrictEqual({ [Op.and]: [ { is_archived: false }, - { state: CaseState.ACCEPTED }, { type: [ CaseType.CUSTODY, @@ -355,6 +354,7 @@ describe('getCasesQueryFilter', () => { CaseType.PAROLE_REVOCATION, ], }, + { state: CaseState.ACCEPTED }, { decision: [CaseDecision.ACCEPTING, CaseDecision.ACCEPTING_PARTIALLY], }, @@ -382,8 +382,8 @@ describe('getCasesQueryFilter', () => { [Op.or]: [ { - state: CaseState.ACCEPTED, type: [...restrictionCases, CaseType.PAROLE_REVOCATION], + state: CaseState.ACCEPTED, }, { type: indictmentCases, diff --git a/apps/judicial-system/backend/src/app/modules/case/guards/indictmentCaseExistsForDefendant.guard.ts b/apps/judicial-system/backend/src/app/modules/case/guards/indictmentCaseExistsForDefendant.guard.ts new file mode 100644 index 000000000000..f7165aeb9d29 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/case/guards/indictmentCaseExistsForDefendant.guard.ts @@ -0,0 +1,37 @@ +import { + BadRequestException, + CanActivate, + ExecutionContext, + Injectable, +} from '@nestjs/common' + +import { InternalCaseService } from '../internalCase.service' + +@Injectable() +export class IndictmentCaseExistsForDefendantGuard implements CanActivate { + constructor(private readonly internalCaseService: InternalCaseService) {} + + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest() + + const caseId = request.params.caseId + + if (!caseId) { + throw new BadRequestException('Missing case id') + } + + const defendantNationalId = request.params.defendantNationalId + + if (!defendantNationalId) { + throw new BadRequestException('Missing defendant national id') + } + + request.case = + await this.internalCaseService.findByIdAndDefendantNationalId( + caseId, + defendantNationalId, + ) + + return true + } +} diff --git a/apps/judicial-system/backend/src/app/modules/case/guards/mergedCaseExists.guard.ts b/apps/judicial-system/backend/src/app/modules/case/guards/mergedCaseExists.guard.ts index d3892e9036aa..7e983cf79987 100644 --- a/apps/judicial-system/backend/src/app/modules/case/guards/mergedCaseExists.guard.ts +++ b/apps/judicial-system/backend/src/app/modules/case/guards/mergedCaseExists.guard.ts @@ -34,7 +34,7 @@ export class MergedCaseExistsGuard implements CanActivate { ) if (!mergedCase) { - throw new BadRequestException('Merged case not found') + throw new BadRequestException(`Merged case ${mergedCaseId} not found`) } request.mergedCaseParent = theCase diff --git a/apps/judicial-system/backend/src/app/modules/case/guards/test/indictmentCaseExistsForDefendantGuard.spec.ts b/apps/judicial-system/backend/src/app/modules/case/guards/test/indictmentCaseExistsForDefendantGuard.spec.ts new file mode 100644 index 000000000000..076ac2c882c4 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/case/guards/test/indictmentCaseExistsForDefendantGuard.spec.ts @@ -0,0 +1,161 @@ +import { Op } from 'sequelize' +import { uuid } from 'uuidv4' + +import { + BadRequestException, + ExecutionContext, + NotFoundException, +} from '@nestjs/common' + +import { normalizeAndFormatNationalId } from '@island.is/judicial-system/formatters' +import { CaseState, CaseType } from '@island.is/judicial-system/types' + +import { createTestingCaseModule } from '../../test/createTestingCaseModule' + +import { Defendant } from '../../../defendant' +import { Institution } from '../../../institution' +import { Subpoena } from '../../../subpoena' +import { User } from '../../../user' +import { Case } from '../../models/case.model' +import { DateLog } from '../../models/dateLog.model' +import { IndictmentCaseExistsForDefendantGuard } from '../indictmentCaseExistsForDefendant.guard' + +interface Then { + result: boolean + error: Error +} + +type GivenWhenThen = () => Promise + +describe('Indictment Case Exists For Defendant Guard', () => { + const mockRequest = jest.fn() + let mockCaseModel: typeof Case + let givenWhenThen: GivenWhenThen + + beforeEach(async () => { + const { caseModel, internalCaseService } = await createTestingCaseModule() + + mockCaseModel = caseModel + + givenWhenThen = async (): Promise => { + const guard = new IndictmentCaseExistsForDefendantGuard( + internalCaseService, + ) + const then = {} as Then + + try { + then.result = await guard.canActivate({ + switchToHttp: () => ({ getRequest: mockRequest }), + } as unknown as ExecutionContext) + } catch (error) { + then.error = error as Error + } + + return then + } + }) + + describe('case exists', () => { + const caseId = uuid() + const defendantNationalId = uuid() + const theCase = { id: caseId } + const request = { params: { caseId, defendantNationalId }, case: undefined } + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce(request) + const mockFindOne = mockCaseModel.findOne as jest.Mock + mockFindOne.mockResolvedValueOnce(theCase) + + then = await givenWhenThen() + }) + + it('should activate', () => { + expect(mockCaseModel.findOne).toHaveBeenCalledWith({ + include: [ + { + model: Defendant, + as: 'defendants', + include: [ + { + model: Subpoena, + as: 'subpoenas', + order: [['created', 'DESC']], + }, + ], + }, + { model: Institution, as: 'court' }, + { model: Institution, as: 'prosecutorsOffice' }, + { model: User, as: 'judge' }, + { + model: User, + as: 'prosecutor', + include: [{ model: Institution, as: 'institution' }], + }, + { model: DateLog, as: 'dateLogs' }, + ], + attributes: ['courtCaseNumber', 'id'], + where: { + type: CaseType.INDICTMENT, + id: caseId, + state: { [Op.not]: CaseState.DELETED }, + isArchived: false, + '$defendants.national_id$': + normalizeAndFormatNationalId(defendantNationalId), + }, + }) + expect(then.result).toBe(true) + expect(request.case).toBe(theCase) + }) + }) + + describe('case does not exist', () => { + const caseId = uuid() + const defendantNationalId = uuid() + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce({ + params: { caseId, defendantNationalId }, + }) + + then = await givenWhenThen() + }) + + it('should throw NotFoundException', () => { + expect(then.error).toBeInstanceOf(NotFoundException) + expect(then.error.message).toBe(`Case ${caseId} does not exist`) + }) + }) + + describe('missing case id', () => { + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce({ params: {} }) + + then = await givenWhenThen() + }) + + it('should throw BadRequestException', () => { + expect(then.error).toBeInstanceOf(BadRequestException) + expect(then.error.message).toBe('Missing case id') + }) + }) + + describe('missing defendant national id', () => { + const caseId = uuid() + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce({ params: { caseId } }) + + then = await givenWhenThen() + }) + + it('should throw BadRequestException', () => { + expect(then.error).toBeInstanceOf(BadRequestException) + expect(then.error.message).toBe('Missing defendant national id') + }) + }) +}) diff --git a/apps/judicial-system/backend/src/app/modules/case/guards/test/mergedCaseExistsGuard.spec.ts b/apps/judicial-system/backend/src/app/modules/case/guards/test/mergedCaseExistsGuard.spec.ts new file mode 100644 index 000000000000..9800abe3a1c0 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/case/guards/test/mergedCaseExistsGuard.spec.ts @@ -0,0 +1,130 @@ +import { uuid } from 'uuidv4' + +import { + BadRequestException, + ExecutionContext, + InternalServerErrorException, +} from '@nestjs/common' + +import { createTestingCaseModule } from '../../test/createTestingCaseModule' + +import { MergedCaseExistsGuard } from '../mergedCaseExists.guard' + +interface Then { + result: boolean + error: Error +} + +type GivenWhenThen = () => Promise + +describe('Merged Case Exists Guard', () => { + const mockRequest = jest.fn() + let givenWhenThen: GivenWhenThen + + beforeEach(async () => { + const { caseService } = await createTestingCaseModule() + + givenWhenThen = async (): Promise => { + const guard = new MergedCaseExistsGuard(caseService) + const then = {} as Then + + try { + then.result = await guard.canActivate({ + switchToHttp: () => ({ getRequest: mockRequest }), + } as unknown as ExecutionContext) + } catch (error) { + then.error = error as Error + } + + return then + } + }) + + describe('merged case exists', () => { + const mergedCaseId = uuid() + const mergedCase = { id: mergedCaseId } + const caseId = uuid() + const theCase = { id: caseId, mergedCases: [mergedCase] } + const request = { + params: { caseId, mergedCaseId }, + mergedCaseParent: undefined, + case: theCase, + } + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce(request) + + then = await givenWhenThen() + }) + + it('should activate', () => { + expect(then.result).toBe(true) + expect(request.mergedCaseParent).toBe(theCase) + expect(request.params.caseId).toBe(mergedCaseId) + expect(request.case).toBe(mergedCase) + }) + }) + + describe('no merged case id', () => { + const caseId = uuid() + const theCase = { id: caseId, mergedCases: [] } + const request = { + params: { caseId }, + mergedCaseParent: undefined, + case: theCase, + } + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce(request) + + then = await givenWhenThen() + }) + + it('should activate', () => { + expect(then.result).toBe(true) + expect(request.mergedCaseParent).toBeUndefined() + expect(request.params.caseId).toBe(caseId) + expect(request.case).toBe(theCase) + }) + }) + + describe('merged case not found', () => { + const mergedCaseId = uuid() + const caseId = uuid() + const theCase = { id: caseId, mergedCases: [] } + const request = { + params: { caseId, mergedCaseId }, + mergedCaseParent: undefined, + case: theCase, + } + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce(request) + + then = await givenWhenThen() + }) + + it('should throw NotFoundException', () => { + expect(then.error).toBeInstanceOf(BadRequestException) + expect(then.error.message).toBe(`Merged case ${mergedCaseId} not found`) + }) + }) + + describe('missing case', () => { + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce({ params: { mergedCaseId: uuid() } }) + + then = await givenWhenThen() + }) + + it('should throw BadRequestException', () => { + expect(then.error).toBeInstanceOf(InternalServerErrorException) + expect(then.error.message).toBe('Missing case') + }) + }) +}) diff --git a/apps/judicial-system/backend/src/app/modules/case/internalCase.controller.ts b/apps/judicial-system/backend/src/app/modules/case/internalCase.controller.ts index 0a7dce8d89b7..60991a38b33f 100644 --- a/apps/judicial-system/backend/src/app/modules/case/internalCase.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/case/internalCase.controller.ts @@ -2,6 +2,7 @@ import { BadRequestException, Body, Controller, + Get, Inject, Param, Post, @@ -27,12 +28,12 @@ import { import { EventService } from '../event' import { DeliverDto } from './dto/deliver.dto' import { DeliverCancellationNoticeDto } from './dto/deliverCancellationNotice.dto' -import { InternalCasesDto } from './dto/internalCases.dto' import { InternalCreateCaseDto } from './dto/internalCreateCase.dto' import { CurrentCase } from './guards/case.decorator' import { CaseCompletedGuard } from './guards/caseCompleted.guard' import { CaseExistsGuard } from './guards/caseExists.guard' import { CaseTypeGuard } from './guards/caseType.guard' +import { IndictmentCaseExistsForDefendantGuard } from './guards/indictmentCaseExistsForDefendant.guard' import { CaseInterceptor, CasesInterceptor, @@ -76,38 +77,42 @@ export class InternalCaseController { return this.internalCaseService.archive() } - @Post('cases/indictments') + @Get('cases/indictments/defendant/:defendantNationalId') @ApiOkResponse({ type: Case, isArray: true, - description: 'Gets all indictment cases for digital mailbox', + description: 'Gets all indictment cases for a given defendant', }) @UseInterceptors(CasesInterceptor) - getIndictmentCases( - @Body() internalCasesDto: InternalCasesDto, + getAllDefendantIndictmentCases( + @Param('defendantNationalId') defendantNationalId: string, ): Promise { - this.logger.debug('Getting all indictment cases') + this.logger.debug('Getting all indictment cases for a given defendant') - return this.internalCaseService.getIndictmentCases( - internalCasesDto.nationalId, + return this.internalCaseService.getAllDefendantIndictmentCases( + defendantNationalId, ) } - @Post('case/indictment/:caseId') + @UseGuards(IndictmentCaseExistsForDefendantGuard) + @Get('case/indictment/:caseId/defendant/:defendantNationalId') @ApiOkResponse({ type: Case, - description: 'Gets indictment case by id for digital mailbox', + description: 'Gets an existing indictment case by id for a given defendant', }) @UseInterceptors(CaseInterceptor) - getIndictmentCase( + getDefendantIndictmentCaseById( @Param('caseId') caseId: string, - @Body() internalCasesDto: InternalCasesDto, + @Param('defendantNationalId') defendantNationalId: string, + @CurrentCase() theCase: Case, ): Promise { - this.logger.debug(`Getting indictment case ${caseId}`) + this.logger.debug( + `Getting indictment case ${caseId} by id for a given defendant`, + ) - return this.internalCaseService.getIndictmentCase( - caseId, - internalCasesDto.nationalId, + return this.internalCaseService.getDefendantIndictmentCase( + theCase, + defendantNationalId, ) } diff --git a/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts b/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts index c47fd37ca093..a969cf40351b 100644 --- a/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts +++ b/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts @@ -1,6 +1,7 @@ import CryptoJS from 'crypto-js' import format from 'date-fns/format' import { Base64 } from 'js-base64' +import { Op } from 'sequelize' import { Sequelize } from 'sequelize-typescript' import { @@ -57,7 +58,7 @@ import { CaseFile, FileService } from '../file' import { IndictmentCount, IndictmentCountService } from '../indictment-count' import { Institution } from '../institution' import { PoliceDocument, PoliceDocumentType, PoliceService } from '../police' -import { Subpoena } from '../subpoena' +import { Subpoena, SubpoenaService } from '../subpoena' import { User, UserService } from '../user' import { InternalCreateCaseDto } from './dto/internalCreateCase.dto' import { archiveFilter } from './filters/case.archiveFilter' @@ -171,6 +172,8 @@ export class InternalCaseService { private readonly fileService: FileService, @Inject(forwardRef(() => DefendantService)) private readonly defendantService: DefendantService, + @Inject(forwardRef(() => SubpoenaService)) + private readonly subpoenaService: SubpoenaService, @Inject(forwardRef(() => PdfService)) private readonly pdfService: PdfService, @Inject(LOGGER_PROVIDER) private readonly logger: Logger, @@ -1180,7 +1183,7 @@ export class InternalCaseService { // As this is only currently used by the digital mailbox API // we will only return indictment cases that have a court date - async getIndictmentCases(nationalId: string): Promise { + async getAllDefendantIndictmentCases(nationalId: string): Promise { return this.caseModel.findAll({ include: [ { @@ -1213,8 +1216,11 @@ export class InternalCaseService { }) } - async getIndictmentCase(caseId: string, nationalId: string): Promise { - const caseById = await this.caseModel.findOne({ + async findByIdAndDefendantNationalId( + caseId: string, + defendantNationalId: string, + ): Promise { + const theCase = await this.caseModel.findOne({ include: [ { model: Defendant, @@ -1241,17 +1247,39 @@ export class InternalCaseService { where: { type: CaseType.INDICTMENT, id: caseId, - // The national id could be without a hyphen or with a hyphen so we need to - // search for both - '$defendants.national_id$': normalizeAndFormatNationalId(nationalId), + state: { [Op.not]: CaseState.DELETED }, + isArchived: false, + // This select only defendants with the given national id, other defendants are not included + '$defendants.national_id$': + normalizeAndFormatNationalId(defendantNationalId), }, }) - if (!caseById) { - throw new NotFoundException(`Case ${caseId} not found`) + if (!theCase) { + throw new NotFoundException(`Case ${caseId} does not exist`) + } + + return theCase + } + + async getDefendantIndictmentCase( + theCase: Case, + defendantNationalId: string, + ): Promise { + const subpoena = theCase.defendants?.[0].subpoenas?.[0] + + if (!subpoena) { + return theCase + } + + const latestSubpoena = await this.subpoenaService.getSubpoena(subpoena) + + if (latestSubpoena === subpoena) { + // The subpoena was up to date + return theCase } - return caseById + return this.findByIdAndDefendantNationalId(theCase.id, defendantNationalId) } countIndictmentsWaitingForConfirmation(prosecutorsOfficeId: string) { diff --git a/apps/judicial-system/backend/src/app/modules/case/test/createTestingCaseModule.ts b/apps/judicial-system/backend/src/app/modules/case/test/createTestingCaseModule.ts index 358e3028963a..3ecf8b656acc 100644 --- a/apps/judicial-system/backend/src/app/modules/case/test/createTestingCaseModule.ts +++ b/apps/judicial-system/backend/src/app/modules/case/test/createTestingCaseModule.ts @@ -180,6 +180,9 @@ export const createTestingCaseModule = async () => { const caseService = caseModule.get(CaseService) + const internalCaseService = + caseModule.get(InternalCaseService) + const limitedAccessCaseService = caseModule.get( LimitedAccessCaseService, ) @@ -213,6 +216,7 @@ export const createTestingCaseModule = async () => { caseStringModel, caseConfig, caseService, + internalCaseService, limitedAccessCaseService, caseController, internalCaseController, diff --git a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/getIndictmentCaseByIdGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/getIndictmentCaseByIdGuards.spec.ts new file mode 100644 index 000000000000..5854213f6614 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/getIndictmentCaseByIdGuards.spec.ts @@ -0,0 +1,21 @@ +import { IndictmentCaseExistsForDefendantGuard } from '../../guards/indictmentCaseExistsForDefendant.guard' +import { InternalCaseController } from '../../internalCase.controller' + +describe('InternalCaseController - Get defendant indictment case by id guards', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let guards: any[] + + beforeEach(() => { + guards = Reflect.getMetadata( + '__guards__', + InternalCaseController.prototype.getDefendantIndictmentCaseById, + ) + }) + + it('should have the right guard configuration', () => { + expect(guards).toHaveLength(1) + expect(new guards[0]()).toBeInstanceOf( + IndictmentCaseExistsForDefendantGuard, + ) + }) +}) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/civilClaimant.controller.ts b/apps/judicial-system/backend/src/app/modules/defendant/civilClaimant.controller.ts index 439904735d4e..bd19b67c84f7 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/civilClaimant.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/civilClaimant.controller.ts @@ -28,13 +28,15 @@ import { } from '../../guards' import { Case, CaseExistsGuard, CaseWriteGuard, CurrentCase } from '../case' import { UpdateCivilClaimantDto } from './dto/updateCivilClaimant.dto' +import { CurrentCivilClaimant } from './guards/civilClaimaint.decorator' +import { CivilClaimantExistsGuard } from './guards/civilClaimantExists.guard' import { CivilClaimant } from './models/civilClaimant.model' import { DeleteCivilClaimantResponse } from './models/deleteCivilClaimant.response' import { CivilClaimantService } from './civilClaimant.service' -@UseGuards(JwtAuthGuard, RolesGuard) @Controller('api/case/:caseId/civilClaimant') @ApiTags('civilClaimants') +@UseGuards(JwtAuthGuard, RolesGuard) export class CivilClaimantController { constructor( private readonly civilClaimantService: CivilClaimantService, @@ -63,7 +65,7 @@ export class CivilClaimantController { return this.civilClaimantService.create(theCase) } - @UseGuards(CaseExistsGuard, CaseWriteGuard) + @UseGuards(CaseExistsGuard, CaseWriteGuard, CivilClaimantExistsGuard) @RolesRules( prosecutorRule, prosecutorRepresentativeRule, @@ -79,19 +81,20 @@ export class CivilClaimantController { async update( @Param('caseId') caseId: string, @Param('civilClaimantId') civilClaimantId: string, + @CurrentCivilClaimant() civilClaimant: CivilClaimant, @Body() updateCivilClaimantDto: UpdateCivilClaimantDto, ): Promise { this.logger.debug( - `Updating civil claimant ${civilClaimantId} in case ${caseId}`, + `Updating civil claimant ${civilClaimantId} of case ${caseId}`, ) return this.civilClaimantService.update( caseId, - civilClaimantId, + civilClaimant, updateCivilClaimantDto, ) } - @UseGuards(CaseExistsGuard, CaseWriteGuard) + @UseGuards(CaseExistsGuard, CaseWriteGuard, CivilClaimantExistsGuard) @RolesRules(prosecutorRule, prosecutorRepresentativeRule) @Delete(':civilClaimantId') @ApiOkResponse({ @@ -102,7 +105,9 @@ export class CivilClaimantController { @Param('caseId') caseId: string, @Param('civilClaimantId') civilClaimantId: string, ): Promise { - this.logger.debug(`Deleting civil claimant ${civilClaimantId}`) + this.logger.debug( + `Deleting civil claimant ${civilClaimantId} of case ${caseId}`, + ) const deleted = await this.civilClaimantService.delete( caseId, diff --git a/apps/judicial-system/backend/src/app/modules/defendant/civilClaimant.service.ts b/apps/judicial-system/backend/src/app/modules/defendant/civilClaimant.service.ts index 285092718ba5..c0d676283258 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/civilClaimant.service.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/civilClaimant.service.ts @@ -1,6 +1,10 @@ import { Op } from 'sequelize' -import { Inject, Injectable } from '@nestjs/common' +import { + Inject, + Injectable, + InternalServerErrorException, +} from '@nestjs/common' import { InjectModel } from '@nestjs/sequelize' import type { Logger } from '@island.is/logging' @@ -33,17 +37,18 @@ export class CivilClaimantService { } private async sendUpdateCivilClaimantMessages( - update: UpdateCivilClaimantDto, + oldCivilClaimant: CivilClaimant, updatedCivilClaimant: CivilClaimant, ): Promise { - if (update.isSpokespersonConfirmed === true) { + if ( + updatedCivilClaimant.isSpokespersonConfirmed && + !oldCivilClaimant.isSpokespersonConfirmed + ) { return this.messageService.sendMessagesToQueue([ { type: MessageType.CIVIL_CLAIMANT_NOTIFICATION, caseId: updatedCivilClaimant.caseId, - body: { - type: CivilClaimantNotificationType.SPOKESPERSON_ASSIGNED, - }, + body: { type: CivilClaimantNotificationType.SPOKESPERSON_ASSIGNED }, elementId: updatedCivilClaimant.id, }, ]) @@ -52,13 +57,13 @@ export class CivilClaimantService { async update( caseId: string, - civilClaimantId: string, + civilClaimant: CivilClaimant, update: UpdateCivilClaimantDto, ): Promise { const [numberOfAffectedRows, civilClaimants] = await this.civilClaimantModel.update(update, { where: { - id: civilClaimantId, + id: civilClaimant.id, caseId: caseId, }, returning: true, @@ -66,15 +71,22 @@ export class CivilClaimantService { if (numberOfAffectedRows > 1) { this.logger.error( - `Unexpected number of rows (${numberOfAffectedRows}) affected when updating civil claimant ${civilClaimantId} of case ${caseId}`, + `Unexpected number of rows (${numberOfAffectedRows}) affected when updating civil claimant ${civilClaimant.id} of case ${caseId}`, ) } else if (numberOfAffectedRows < 1) { - throw new Error(`Could not update civil claimant ${civilClaimantId}`) + throw new InternalServerErrorException( + `Could not update civil claimant ${civilClaimant.id} of case ${caseId}`, + ) } - await this.sendUpdateCivilClaimantMessages(update, civilClaimants[0]) + const updatedCivilClaimant = civilClaimants[0] + + await this.sendUpdateCivilClaimantMessages( + civilClaimant, + updatedCivilClaimant, + ) - return civilClaimants[0] + return updatedCivilClaimant } async delete(caseId: string, civilClaimantId: string): Promise { @@ -91,7 +103,9 @@ export class CivilClaimantService { `Unexpected number of rows (${numberOfAffectedRows}) affected when deleting civil claimant ${civilClaimantId} of case ${caseId}`, ) } else if (numberOfAffectedRows < 1) { - throw new Error(`Could not delete civil claimant ${civilClaimantId}`) + throw new InternalServerErrorException( + `Could not delete civil claimant ${civilClaimantId}`, + ) } return true diff --git a/apps/judicial-system/backend/src/app/modules/defendant/defendant.controller.ts b/apps/judicial-system/backend/src/app/modules/defendant/defendant.controller.ts index 83edd4cd8b1e..de64f7538c41 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/defendant.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/defendant.controller.ts @@ -38,9 +38,9 @@ import { Defendant } from './models/defendant.model' import { DeleteDefendantResponse } from './models/delete.response' import { DefendantService } from './defendant.service' -@UseGuards(JwtAuthGuard, RolesGuard) @Controller('api/case/:caseId/defendant') @ApiTags('defendants') +@UseGuards(JwtAuthGuard, RolesGuard) export class DefendantController { constructor( private readonly defendantService: DefendantService, diff --git a/apps/judicial-system/backend/src/app/modules/defendant/defendant.service.ts b/apps/judicial-system/backend/src/app/modules/defendant/defendant.service.ts index fca293e95ffe..6276f0634115 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/defendant.service.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/defendant.service.ts @@ -95,26 +95,6 @@ export class DefendantService { return message } - private getUpdatedDefendant( - numberOfAffectedRows: number, - defendants: Defendant[], - defendantId: string, - caseId: string, - ): Defendant { - if (numberOfAffectedRows > 1) { - // Tolerate failure, but log error - this.logger.error( - `Unexpected number of rows (${numberOfAffectedRows}) affected when updating defendant ${defendantId} of case ${caseId}`, - ) - } else if (numberOfAffectedRows < 1) { - throw new InternalServerErrorException( - `Could not update defendant ${defendantId} of case ${caseId}`, - ) - } - - return defendants[0] - } - private async sendRequestCaseUpdateDefendantMessages( theCase: Case, updatedDefendant: Defendant, @@ -255,12 +235,18 @@ export class DefendantService { { where: { id: defendantId, caseId }, returning: true, transaction }, ) - return this.getUpdatedDefendant( - numberOfAffectedRows, - defendants, - defendantId, - caseId, - ) + if (numberOfAffectedRows > 1) { + // Tolerate failure, but log error + this.logger.error( + `Unexpected number of rows (${numberOfAffectedRows}) affected when updating defendant ${defendantId} of case ${caseId}`, + ) + } else if (numberOfAffectedRows < 1) { + throw new InternalServerErrorException( + `Could not update defendant ${defendantId} of case ${caseId}`, + ) + } + + return defendants[0] } async updateRequestCaseDefendant( diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/civilClaimantControllerGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/civilClaimantControllerGuards.spec.ts new file mode 100644 index 000000000000..a6029164ba58 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/civilClaimantControllerGuards.spec.ts @@ -0,0 +1,18 @@ +import { JwtAuthGuard, RolesGuard } from '@island.is/judicial-system/auth' + +import { CivilClaimantController } from '../../civilClaimant.controller' + +describe('CivilClaimantController - guards', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let guards: any[] + + beforeEach(() => { + guards = Reflect.getMetadata('__guards__', CivilClaimantController) + }) + + it('should have the right guard configuration', () => { + expect(guards).toHaveLength(2) + expect(new guards[0]()).toBeInstanceOf(JwtAuthGuard) + expect(new guards[1]()).toBeInstanceOf(RolesGuard) + }) +}) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/deleteGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/deleteGuards.spec.ts index 20e8ef89e2f6..522d7fa661a7 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/deleteGuards.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/deleteGuards.spec.ts @@ -2,10 +2,15 @@ import { CanActivate } from '@nestjs/common' import { CaseExistsGuard, CaseWriteGuard } from '../../../case' import { CivilClaimantController } from '../../civilClaimant.controller' +import { CivilClaimantExistsGuard } from '../../guards/civilClaimantExists.guard' describe('CivilClaimantController - Delete guards', () => { let guards: Array CanActivate> - const expectedGuards = [CaseExistsGuard, CaseWriteGuard] + const expectedGuards = [ + CaseExistsGuard, + CaseWriteGuard, + CivilClaimantExistsGuard, + ] beforeEach(() => { guards = Reflect.getMetadata( diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/update.spec.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/update.spec.ts index 10eba40190cc..391d99e3eb0a 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/update.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/update.spec.ts @@ -42,7 +42,12 @@ describe('CivilClaimantController - Update', () => { const then = {} as Then await civilClaimantController - .update(caseId, civilClaimantId, updateData) + .update( + caseId, + civilClaimantId, + { id: civilClaimantId } as CivilClaimant, + updateData, + ) .then((result) => (then.result = result)) .catch((error) => (then.error = error)) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/updateGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/updateGuards.spec.ts index d333af01f86f..cc106a2fce27 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/updateGuards.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/updateGuards.spec.ts @@ -2,10 +2,15 @@ import { CanActivate } from '@nestjs/common' import { CaseExistsGuard, CaseWriteGuard } from '../../../case' import { CivilClaimantController } from '../../civilClaimant.controller' +import { CivilClaimantExistsGuard } from '../../guards/civilClaimantExists.guard' describe('CivilClaimantController - Update guards', () => { let guards: Array CanActivate> - const expectedGuards = [CaseExistsGuard, CaseWriteGuard] + const expectedGuards = [ + CaseExistsGuard, + CaseWriteGuard, + CivilClaimantExistsGuard, + ] beforeEach(() => { guards = Reflect.getMetadata( diff --git a/apps/judicial-system/backend/src/app/modules/event-log/eventLog.controller.ts b/apps/judicial-system/backend/src/app/modules/event-log/eventLog.controller.ts index 30af6da23667..853fe1fe0ef3 100644 --- a/apps/judicial-system/backend/src/app/modules/event-log/eventLog.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/event-log/eventLog.controller.ts @@ -1,5 +1,5 @@ import { Body, Controller, Post, UseGuards } from '@nestjs/common' -import { ApiCreatedResponse } from '@nestjs/swagger' +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger' import { TokenGuard } from '@island.is/judicial-system/auth' @@ -7,6 +7,7 @@ import { CreateEventLogDto } from './dto/createEventLog.dto' import { EventLogService } from './eventLog.service' @Controller('api/eventLog') +@ApiTags('eventLogs') export class EventLogController { constructor(private readonly eventLogService: EventLogService) {} diff --git a/apps/judicial-system/backend/src/app/modules/file/file.controller.ts b/apps/judicial-system/backend/src/app/modules/file/file.controller.ts index ca960a15f389..70a0ffe302a9 100644 --- a/apps/judicial-system/backend/src/app/modules/file/file.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/file/file.controller.ts @@ -65,9 +65,9 @@ import { SignedUrl } from './models/signedUrl.model' import { UploadFileToCourtResponse } from './models/uploadFileToCourt.response' import { FileService } from './file.service' -@UseGuards(JwtAuthGuard) @Controller('api/case/:caseId') @ApiTags('files') +@UseGuards(JwtAuthGuard) export class FileController { constructor( private readonly fileService: FileService, diff --git a/apps/judicial-system/backend/src/app/modules/file/internalFile.controller.ts b/apps/judicial-system/backend/src/app/modules/file/internalFile.controller.ts index 6c6f5ddba847..2241f9db7bf7 100644 --- a/apps/judicial-system/backend/src/app/modules/file/internalFile.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/file/internalFile.controller.ts @@ -25,9 +25,9 @@ import { DeliverResponse } from './models/deliver.response' import { CaseFile } from './models/file.model' import { FileService } from './file.service' -@UseGuards(TokenGuard) @Controller('api/internal/case/:caseId') @ApiTags('internal files') +@UseGuards(TokenGuard) export class InternalFileController { constructor( private readonly fileService: FileService, diff --git a/apps/judicial-system/backend/src/app/modules/file/limitedAccessFile.controller.ts b/apps/judicial-system/backend/src/app/modules/file/limitedAccessFile.controller.ts index d6399c0a36a4..5285a7aa0a70 100644 --- a/apps/judicial-system/backend/src/app/modules/file/limitedAccessFile.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/file/limitedAccessFile.controller.ts @@ -55,9 +55,9 @@ import { PresignedPost } from './models/presignedPost.model' import { SignedUrl } from './models/signedUrl.model' import { FileService } from './file.service' -@UseGuards(JwtAuthGuard, RolesGuard, LimitedAccessCaseExistsGuard) @Controller('api/case/:caseId/limitedAccess') @ApiTags('files') +@UseGuards(JwtAuthGuard, RolesGuard, LimitedAccessCaseExistsGuard) export class LimitedAccessFileController { constructor( private readonly fileService: FileService, diff --git a/apps/judicial-system/backend/src/app/modules/indictment-count/indictmentCount.controller.ts b/apps/judicial-system/backend/src/app/modules/indictment-count/indictmentCount.controller.ts index 1c32f7a87303..3303ff096517 100644 --- a/apps/judicial-system/backend/src/app/modules/indictment-count/indictmentCount.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/indictment-count/indictmentCount.controller.ts @@ -27,9 +27,9 @@ import { DeleteIndictmentCountResponse } from './models/delete.response' import { IndictmentCount } from './models/indictmentCount.model' import { IndictmentCountService } from './indictmentCount.service' -@UseGuards(JwtAuthGuard, RolesGuard) @Controller('api/case/:caseId/indictmentCount') @ApiTags('indictment-counts') +@UseGuards(JwtAuthGuard, RolesGuard) export class IndictmentCountController { constructor( private readonly indictmentCountService: IndictmentCountService, diff --git a/apps/judicial-system/backend/src/app/modules/notification/internalNotification.controller.ts b/apps/judicial-system/backend/src/app/modules/notification/internalNotification.controller.ts index 5e03c0f784b3..68131739f280 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/internalNotification.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/internalNotification.controller.ts @@ -41,9 +41,9 @@ import { InstitutionNotificationService } from './services/institutionNotificati import { SubpoenaNotificationService } from './services/subpoenaNotification/subpoenaNotification.service' import { NotificationDispatchService } from './notificationDispatch.service' -@UseGuards(TokenGuard) @Controller('api/internal') @ApiTags('internal notifications') +@UseGuards(TokenGuard) export class InternalNotificationController { constructor( private readonly caseNotificationService: CaseNotificationService, diff --git a/apps/judicial-system/backend/src/app/modules/notification/notification.controller.ts b/apps/judicial-system/backend/src/app/modules/notification/notification.controller.ts index 4b8ce92cb0c2..60f979a3ff9f 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/notification.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/notification.controller.ts @@ -34,9 +34,9 @@ import { import { SendNotificationResponse } from './models/sendNotification.response' import { NotificationService } from './notification.service' -@UseGuards(JwtAuthGuard, RolesGuard, CaseExistsGuard) @Controller('api/case/:caseId/notification') @ApiTags('notifications') +@UseGuards(JwtAuthGuard, RolesGuard, CaseExistsGuard) export class NotificationController { constructor( private readonly notificationService: NotificationService, diff --git a/apps/judicial-system/backend/src/app/modules/police/index.ts b/apps/judicial-system/backend/src/app/modules/police/index.ts index 6f0613c334bc..f269e4fca945 100644 --- a/apps/judicial-system/backend/src/app/modules/police/index.ts +++ b/apps/judicial-system/backend/src/app/modules/police/index.ts @@ -1,5 +1,6 @@ export { PoliceDocumentType, PoliceDocument, + SubpoenaInfo, PoliceService, } from './police.service' diff --git a/apps/judicial-system/backend/src/app/modules/police/internalPolice.controller.ts b/apps/judicial-system/backend/src/app/modules/police/internalPolice.controller.ts deleted file mode 100644 index 4680100a5d85..000000000000 --- a/apps/judicial-system/backend/src/app/modules/police/internalPolice.controller.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Controller, Get, Inject, Param, UseGuards } from '@nestjs/common' -import { ApiOkResponse, ApiTags } from '@nestjs/swagger' - -import { type Logger, LOGGER_PROVIDER } from '@island.is/logging' - -import { TokenGuard } from '@island.is/judicial-system/auth' - -import { Case, CaseExistsGuard, CurrentCase } from '../case' -import { Subpoena } from '../subpoena' -import { PoliceService } from './police.service' - -@UseGuards(TokenGuard, CaseExistsGuard) -@Controller('api/internal/case/:caseId') -@ApiTags('internal police') -export class InternalPoliceController { - constructor( - private readonly policeService: PoliceService, - @Inject(LOGGER_PROVIDER) private readonly logger: Logger, - ) {} - - @Get('subpoenaStatus/:subpoenaId') - @ApiOkResponse({ - type: Subpoena, - description: 'Gets subpoena status', - }) - getSubpoenaStatus( - @Param('subpoenaId') subpoenaId: string, - @CurrentCase() theCase: Case, - ): Promise { - this.logger.debug(`Gets subpoena status in case ${theCase.id}`) - - return this.policeService.getSubpoenaStatus(subpoenaId) - } -} diff --git a/apps/judicial-system/backend/src/app/modules/police/models/subpoenaInfo.model.ts b/apps/judicial-system/backend/src/app/modules/police/models/subpoenaInfo.model.ts new file mode 100644 index 000000000000..2bd84b54109a --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/police/models/subpoenaInfo.model.ts @@ -0,0 +1,49 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger' + +import { ServiceStatus } from '@island.is/judicial-system/types' + +interface SubpoenaUpdate + extends Pick< + SubpoenaInfo, + | 'serviceStatus' + | 'comment' + | 'servedBy' + | 'defenderNationalId' + | 'serviceDate' + > {} + +const subpoenaUpdateKeys: Array = [ + 'serviceStatus', + 'comment', + 'servedBy', + 'defenderNationalId', + 'serviceDate', +] + +export class SubpoenaInfo { + private isNewValueSetAndDifferent = ( + newValue: unknown, + oldValue: unknown, + ): boolean => Boolean(newValue) && newValue !== oldValue + + isSubpoenaInfoChanged(oldSubpoenaInfo: SubpoenaUpdate) { + return subpoenaUpdateKeys.some((key) => + this.isNewValueSetAndDifferent(this[key], oldSubpoenaInfo[key]), + ) + } + + @ApiProperty({ type: ServiceStatus }) + serviceStatus?: ServiceStatus + + @ApiPropertyOptional({ type: String }) + comment?: string + + @ApiPropertyOptional({ type: String }) + servedBy?: string + + @ApiPropertyOptional({ type: String }) + defenderNationalId?: string + + @ApiPropertyOptional({ type: Date }) + serviceDate?: Date +} diff --git a/apps/judicial-system/backend/src/app/modules/police/police.controller.ts b/apps/judicial-system/backend/src/app/modules/police/police.controller.ts index 40c5f1829985..c80db84e51db 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.controller.ts @@ -21,13 +21,7 @@ import { } from '@island.is/judicial-system/auth' import type { User } from '@island.is/judicial-system/types' -import { - districtCourtAssistantRule, - districtCourtJudgeRule, - districtCourtRegistrarRule, - prosecutorRepresentativeRule, - prosecutorRule, -} from '../../guards' +import { prosecutorRepresentativeRule, prosecutorRule } from '../../guards' import { Case, CaseExistsGuard, @@ -36,13 +30,14 @@ import { CaseReadGuard, CurrentCase, } from '../case' -import { Subpoena } from '../subpoena' import { UploadPoliceCaseFileDto } from './dto/uploadPoliceCaseFile.dto' import { PoliceCaseFile } from './models/policeCaseFile.model' import { PoliceCaseInfo } from './models/policeCaseInfo.model' import { UploadPoliceCaseFileResponse } from './models/uploadPoliceCaseFile.response' import { PoliceService } from './police.service' +@Controller('api/case/:caseId') +@ApiTags('police files') @UseGuards( JwtAuthGuard, RolesGuard, @@ -50,8 +45,6 @@ import { PoliceService } from './police.service' CaseReadGuard, CaseNotCompletedGuard, ) -@Controller('api/case/:caseId') -@ApiTags('police files') export class PoliceController { constructor( private readonly policeService: PoliceService, @@ -76,28 +69,6 @@ export class PoliceController { return this.policeService.getAllPoliceCaseFiles(theCase.id, user) } - @RolesRules( - prosecutorRule, - prosecutorRepresentativeRule, - districtCourtJudgeRule, - districtCourtAssistantRule, - districtCourtRegistrarRule, - ) - @Get('subpoenaStatus/:subpoenaId') - @ApiOkResponse({ - type: Subpoena, - description: 'Gets subpoena status', - }) - getSubpoenaStatus( - @Param('subpoenaId') subpoenaId: string, - @CurrentCase() theCase: Case, - @CurrentHttpUser() user: User, - ): Promise { - this.logger.debug(`Gets subpoena status in case ${theCase.id}`) - - return this.policeService.getSubpoenaStatus(subpoenaId, user) - } - @RolesRules(prosecutorRule, prosecutorRepresentativeRule) @UseInterceptors(CaseOriginalAncestorInterceptor) @Get('policeCaseInfo') diff --git a/apps/judicial-system/backend/src/app/modules/police/police.module.ts b/apps/judicial-system/backend/src/app/modules/police/police.module.ts index c6868f89b050..1df72c2b8794 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.module.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.module.ts @@ -1,7 +1,6 @@ import { forwardRef, Module } from '@nestjs/common' import { AwsS3Module, CaseModule, EventModule, SubpoenaModule } from '../index' -import { InternalPoliceController } from './internalPolice.controller' import { PoliceController } from './police.controller' import { PoliceService } from './police.service' @@ -14,6 +13,6 @@ import { PoliceService } from './police.service' ], providers: [PoliceService], exports: [PoliceService], - controllers: [PoliceController, InternalPoliceController], + controllers: [PoliceController], }) export class PoliceModule {} diff --git a/apps/judicial-system/backend/src/app/modules/police/police.service.ts b/apps/judicial-system/backend/src/app/modules/police/police.service.ts index c753e4e4db5e..bdb539eb73cd 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.service.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.service.ts @@ -34,8 +34,7 @@ import { AwsS3Service } from '../aws-s3' import { Case } from '../case' import { Defendant } from '../defendant/models/defendant.model' import { EventService } from '../event' -import { Subpoena, SubpoenaService } from '../subpoena' -import { UpdateSubpoenaDto } from '../subpoena/dto/updateSubpoena.dto' +import { SubpoenaService } from '../subpoena' import { UploadPoliceCaseFileDto } from './dto/uploadPoliceCaseFile.dto' import { CreateSubpoenaResponse } from './models/createSubpoena.response' import { PoliceCaseFile } from './models/policeCaseFile.model' @@ -59,6 +58,14 @@ export interface PoliceDocument { courtDocument: string } +export interface SubpoenaInfo { + serviceStatus?: ServiceStatus + comment?: string + servedBy?: string + defenderNationalId?: string + serviceDate?: Date +} + const getChapter = (category?: string): number | undefined => { if (!category) { return undefined @@ -336,7 +343,10 @@ export class PoliceService { }) } - async getSubpoenaStatus(subpoenaId: string, user?: User): Promise { + async getSubpoenaStatus( + subpoenaId: string, + user?: User, + ): Promise { return this.fetchPoliceDocumentApi( `${this.xRoadPath}/GetSubpoenaStatus?id=${subpoenaId}`, ) @@ -347,37 +357,24 @@ export class PoliceService { this.subpoenaStructure.parse(response) - const subpoenaToUpdate = await this.subpoenaService.findBySubpoenaId( - subpoenaId, - ) - - const serviceStatus = response.deliveredToLawyer - ? ServiceStatus.DEFENDER - : response.prosecutedConfirmedSubpoenaThroughIslandis - ? ServiceStatus.ELECTRONICALLY - : response.deliveredOnPaper || response.delivered === true - ? ServiceStatus.IN_PERSON - : response.acknowledged === false && response.delivered === false - ? ServiceStatus.FAILED - : // TODO: handle expired - undefined - - if (serviceStatus === undefined) { - return subpoenaToUpdate + return { + serviceStatus: response.deliveredToLawyer + ? ServiceStatus.DEFENDER + : response.prosecutedConfirmedSubpoenaThroughIslandis + ? ServiceStatus.ELECTRONICALLY + : response.deliveredOnPaper || response.delivered === true + ? ServiceStatus.IN_PERSON + : response.acknowledged === false && response.delivered === false + ? ServiceStatus.FAILED + : // TODO: handle expired + undefined, + comment: response.comment ?? undefined, + servedBy: response.servedBy ?? undefined, + defenderNationalId: response.defenderNationalId ?? undefined, + serviceDate: response.servedAt + ? new Date(response.servedAt) + : undefined, } - - const updatedSubpoena = await this.subpoenaService.update( - subpoenaToUpdate, - { - comment: response.comment ?? undefined, - servedBy: response.servedBy ?? undefined, - defenderNationalId: response.defenderNationalId ?? undefined, - serviceDate: response.servedAt ?? undefined, - serviceStatus, - } as UpdateSubpoenaDto, - ) - - return updatedSubpoena } const reason = await res.text() @@ -395,7 +392,7 @@ export class PoliceService { } if (reason instanceof ServiceUnavailableException) { - // Act as if the case does not exist + // Act as if the subpoena does not exist throw new NotFoundException({ ...reason, message: `Subpoena ${subpoenaId} does not exist`, diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts b/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts index 2394ff1249f2..dc1e815371e0 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts @@ -1,5 +1,6 @@ -import { Transform } from 'class-transformer' +import { Transform, Type } from 'class-transformer' import { + IsDate, IsEnum, IsOptional, IsString, @@ -26,9 +27,10 @@ export class UpdateSubpoenaDto { readonly servedBy?: string @IsOptional() - @IsString() - @ApiPropertyOptional({ type: String }) - readonly serviceDate?: string + @Type(() => Date) + @IsDate() + @ApiPropertyOptional({ type: Date }) + readonly serviceDate?: Date @IsOptional() @IsString() diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/guards/policeSubpoenaExists.guard.ts b/apps/judicial-system/backend/src/app/modules/subpoena/guards/policeSubpoenaExists.guard.ts new file mode 100644 index 000000000000..e351570b9714 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/subpoena/guards/policeSubpoenaExists.guard.ts @@ -0,0 +1,28 @@ +import { + BadRequestException, + CanActivate, + ExecutionContext, + Injectable, +} from '@nestjs/common' + +import { SubpoenaService } from '../subpoena.service' + +@Injectable() +export class PoliceSubpoenaExistsGuard implements CanActivate { + constructor(private readonly subpoenaService: SubpoenaService) {} + + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest() + + const subpoenaId = request.params.subpoenaId + + if (!subpoenaId) { + throw new BadRequestException('Missing police subpoena id') + } + + // subpoenaId is the external police document id + request.subpoena = await this.subpoenaService.findBySubpoenaId(subpoenaId) + + return true + } +} diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/guards/subpoenaExists.guard.ts b/apps/judicial-system/backend/src/app/modules/subpoena/guards/subpoenaExists.guard.ts index f5dc6c406cb1..e4c02b4dec2f 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/guards/subpoenaExists.guard.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/guards/subpoenaExists.guard.ts @@ -3,34 +3,28 @@ import { CanActivate, ExecutionContext, Injectable, + InternalServerErrorException, } from '@nestjs/common' import { Defendant } from '../../defendant' -import { SubpoenaService } from '../subpoena.service' @Injectable() export class SubpoenaExistsGuard implements CanActivate { - constructor(private readonly subpoenaService: SubpoenaService) {} - async canActivate(context: ExecutionContext): Promise { const request = context.switchToHttp().getRequest() - const subpoenaId = request.params.subpoenaId - - if (!subpoenaId) { - throw new BadRequestException('Missing subpoena id') - } - const defendant: Defendant = request.defendant if (!defendant) { - // subpoenaId is the external police document id - request.subpoena = await this.subpoenaService.findBySubpoenaId(subpoenaId) + throw new InternalServerErrorException('Missing defendant') + } - return true + const subpoenaId = request.params.subpoenaId + + if (!subpoenaId) { + throw new BadRequestException('Missing subpoena id') } - // subpoenaId is the internal subpoena id const subpoena = defendant.subpoenas?.find( (subpoena) => subpoena.id === subpoenaId, ) diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/guards/test/policeSubpoenaExistsGuard.spec.ts b/apps/judicial-system/backend/src/app/modules/subpoena/guards/test/policeSubpoenaExistsGuard.spec.ts new file mode 100644 index 000000000000..c924bb6a938f --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/subpoena/guards/test/policeSubpoenaExistsGuard.spec.ts @@ -0,0 +1,110 @@ +import { uuid } from 'uuidv4' + +import { + BadRequestException, + ExecutionContext, + NotFoundException, +} from '@nestjs/common' + +import { createTestingSubpoenaModule } from '../../test/createTestingSubpoenaModule' + +import { Subpoena } from '../../models/subpoena.model' +import { include } from '../../subpoena.service' +import { PoliceSubpoenaExistsGuard } from '../policeSubpoenaExists.guard' + +interface Then { + result: boolean + error: Error +} + +type GivenWhenThen = () => Promise + +describe('Police Subpoena Exists Guard', () => { + const mockRequest = jest.fn() + let mockSubpoenaModel: typeof Subpoena + let givenWhenThen: GivenWhenThen + + beforeEach(async () => { + const { subpoenaModel, subpoenaService } = + await createTestingSubpoenaModule() + + mockSubpoenaModel = subpoenaModel + + givenWhenThen = async (): Promise => { + const guard = new PoliceSubpoenaExistsGuard(subpoenaService) + const then = {} as Then + + try { + then.result = await guard.canActivate({ + switchToHttp: () => ({ getRequest: mockRequest }), + } as unknown as ExecutionContext) + } catch (error) { + then.error = error as Error + } + + return then + } + }) + + describe('subpoena exists', () => { + const policeSubpoenaId = uuid() + const subpoena = { id: uuid(), subpoenaId: policeSubpoenaId } + const request = { + params: { subpoenaId: policeSubpoenaId }, + subpoena: undefined, + } + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce(request) + const mockFindOne = mockSubpoenaModel.findOne as jest.Mock + mockFindOne.mockResolvedValueOnce(subpoena) + + then = await givenWhenThen() + }) + + it('should activate', () => { + expect(mockSubpoenaModel.findOne).toHaveBeenCalledWith({ + include, + where: { subpoenaId: policeSubpoenaId }, + }) + expect(then.result).toBe(true) + expect(request.subpoena).toBe(subpoena) + }) + }) + + describe('subpoena does not exist', () => { + const policeSubpoenaId = uuid() + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce({ + params: { subpoenaId: policeSubpoenaId }, + }) + + then = await givenWhenThen() + }) + + it('should throw NotFoundException', () => { + expect(then.error).toBeInstanceOf(NotFoundException) + expect(then.error.message).toBe( + `Subpoena with police subpoena id ${policeSubpoenaId} does not exist`, + ) + }) + }) + + describe('missing subpoena id', () => { + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce({ params: {} }) + + then = await givenWhenThen() + }) + + it('should throw BadRequestException', () => { + expect(then.error).toBeInstanceOf(BadRequestException) + expect(then.error.message).toBe('Missing police subpoena id') + }) + }) +}) diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/guards/test/subpoenaExistsGuard.spec.ts b/apps/judicial-system/backend/src/app/modules/subpoena/guards/test/subpoenaExistsGuard.spec.ts new file mode 100644 index 000000000000..504b56c01e61 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/subpoena/guards/test/subpoenaExistsGuard.spec.ts @@ -0,0 +1,109 @@ +import { uuid } from 'uuidv4' + +import { + BadRequestException, + ExecutionContext, + InternalServerErrorException, +} from '@nestjs/common' + +import { SubpoenaExistsGuard } from '../subpoenaExists.guard' + +interface Then { + result: boolean + error: Error +} + +type GivenWhenThen = () => Promise + +describe('Subpoena Exists Guard', () => { + const mockRequest = jest.fn() + let givenWhenThen: GivenWhenThen + + beforeEach(async () => { + givenWhenThen = async (): Promise => { + const guard = new SubpoenaExistsGuard() + const then = {} as Then + + try { + then.result = await guard.canActivate({ + switchToHttp: () => ({ getRequest: mockRequest }), + } as unknown as ExecutionContext) + } catch (error) { + then.error = error as Error + } + + return then + } + }) + + describe('subpoena exists', () => { + const subpoenaId = uuid() + const subpoena = { id: subpoenaId } + const defendantId = uuid() + const defendant = { id: defendantId, subpoenas: [subpoena] } + const request = { params: { subpoenaId }, defendant, subpoena: undefined } + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce(request) + + then = await givenWhenThen() + }) + + it('should activate', () => { + expect(then.result).toBe(true) + expect(request.subpoena).toBe(subpoena) + }) + }) + + describe('subpoena does not exist', () => { + const subpoenaId = uuid() + const defendantId = uuid() + const defendant = { id: defendantId, subpoenas: [] } + const request = { params: { subpoenaId }, defendant, subpoena: undefined } + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce(request) + + then = await givenWhenThen() + }) + + it('should throw BadRequestException', () => { + expect(then.error).toBeInstanceOf(BadRequestException) + expect(then.error.message).toBe( + `Subpoena ${subpoenaId} of defendant ${defendantId} does not exist`, + ) + }) + }) + + describe('missing defendant', () => { + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce({ params: {} }) + + then = await givenWhenThen() + }) + + it('should throw InternalServerErrorException', () => { + expect(then.error).toBeInstanceOf(InternalServerErrorException) + expect(then.error.message).toBe('Missing defendant') + }) + }) + + describe('missing subpoena id', () => { + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce({ params: {}, defendant: { id: uuid() } }) + + then = await givenWhenThen() + }) + + it('should throw BadRequestException', () => { + expect(then.error).toBeInstanceOf(BadRequestException) + expect(then.error.message).toBe('Missing subpoena id') + }) + }) +}) diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/internalSubpoena.controller.ts b/apps/judicial-system/backend/src/app/modules/subpoena/internalSubpoena.controller.ts index 968440871a92..87402f7b3ada 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/internalSubpoena.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/internalSubpoena.controller.ts @@ -1,7 +1,6 @@ import { Body, Controller, - Get, Inject, Param, Patch, @@ -27,6 +26,7 @@ import { DefendantExistsGuard } from '../defendant/guards/defendantExists.guard' import { Defendant } from '../defendant/models/defendant.model' import { DeliverDto } from './dto/deliver.dto' import { UpdateSubpoenaDto } from './dto/updateSubpoena.dto' +import { PoliceSubpoenaExistsGuard } from './guards/policeSubpoenaExists.guard' import { CurrentSubpoena } from './guards/subpoena.decorator' import { SubpoenaExistsGuard } from './guards/subpoenaExists.guard' import { DeliverResponse } from './models/deliver.response' @@ -42,20 +42,9 @@ export class InternalSubpoenaController { @Inject(LOGGER_PROVIDER) private readonly logger: Logger, ) {} - @UseGuards(SubpoenaExistsGuard) - @Get('subpoena/:subpoenaId') - async getSubpoena( - @Param('subpoenaId') subpoenaId: string, - @CurrentSubpoena() subpoena: Subpoena, - ): Promise { - this.logger.debug(`Getting subpoena by subpoena id ${subpoenaId}`) - - return subpoena - } - - @UseGuards(SubpoenaExistsGuard) + @UseGuards(PoliceSubpoenaExistsGuard) @Patch('subpoena/:subpoenaId') - async updateSubpoena( + updateSubpoena( @Param('subpoenaId') subpoenaId: string, @CurrentSubpoena() subpoena: Subpoena, @Body() update: UpdateSubpoenaDto, @@ -80,7 +69,7 @@ export class InternalSubpoenaController { type: DeliverResponse, description: 'Delivers a subpoena to police', }) - async deliverSubpoenaToPolice( + deliverSubpoenaToPolice( @Param('caseId') caseId: string, @Param('defendantId') defendantId: string, @Param('subpoenaId') subpoenaId: string, @@ -93,7 +82,7 @@ export class InternalSubpoenaController { `Delivering subpoena ${subpoenaId} to police for defendant ${defendantId} of case ${caseId}`, ) - return await this.subpoenaService.deliverSubpoenaToPolice( + return this.subpoenaService.deliverSubpoenaToPolice( theCase, defendant, subpoena, diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/limitedAccessSubpoena.controller.ts b/apps/judicial-system/backend/src/app/modules/subpoena/limitedAccessSubpoena.controller.ts index 36e2676df68b..54bfcdacd19a 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/limitedAccessSubpoena.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/limitedAccessSubpoena.controller.ts @@ -32,12 +32,13 @@ import { } from '../case' import { CurrentDefendant, Defendant, DefendantExistsGuard } from '../defendant' import { CurrentSubpoena } from './guards/subpoena.decorator' -import { SubpoenaExistsOptionalGuard } from './guards/subpoenaExists.guard' +import { SubpoenaExistsGuard } from './guards/subpoenaExists.guard' import { Subpoena } from './models/subpoena.model' @Controller([ 'api/case/:caseId/limitedAccess/defendant/:defendantId/subpoena/:subpoenaId', ]) +@ApiTags('limited access subpoenas') @UseGuards( JwtAuthGuard, CaseExistsGuard, @@ -46,14 +47,13 @@ import { Subpoena } from './models/subpoena.model' CaseReadGuard, DefendantExistsGuard, ) -@ApiTags('limited access subpoenas') export class LimitedAccessSubpoenaController { constructor( private readonly pdfService: PdfService, @Inject(LOGGER_PROVIDER) private readonly logger: Logger, ) {} - @UseGuards(SubpoenaExistsOptionalGuard) + @UseGuards(SubpoenaExistsGuard) @RolesRules(defenderGeneratedPdfRule) @Get() @Header('Content-Type', 'application/pdf') diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts index da139a356906..8242d7867970 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts @@ -15,11 +15,16 @@ import { ApiOkResponse, ApiTags } from '@nestjs/swagger' import { type Logger, LOGGER_PROVIDER } from '@island.is/logging' import { + CurrentHttpUser, JwtAuthGuard, RolesGuard, RolesRules, } from '@island.is/judicial-system/auth' -import { indictmentCases, SubpoenaType } from '@island.is/judicial-system/types' +import { + indictmentCases, + SubpoenaType, + type User, +} from '@island.is/judicial-system/types' import { districtCourtAssistantRule, @@ -46,7 +51,10 @@ import { SubpoenaExistsOptionalGuard, } from './guards/subpoenaExists.guard' import { Subpoena } from './models/subpoena.model' +import { SubpoenaService } from './subpoena.service' +@Controller('api/case/:caseId/defendant/:defendantId/subpoena') +@ApiTags('subpoenas') @UseGuards( JwtAuthGuard, RolesGuard, @@ -55,14 +63,40 @@ import { Subpoena } from './models/subpoena.model' CaseReadGuard, DefendantExistsGuard, ) -@Controller('api/case/:caseId/defendant/:defendantId/subpoena') -@ApiTags('subpoenas') export class SubpoenaController { constructor( private readonly pdfService: PdfService, + private readonly subpoenaService: SubpoenaService, @Inject(LOGGER_PROVIDER) private readonly logger: Logger, ) {} + @RolesRules( + prosecutorRule, + prosecutorRepresentativeRule, + districtCourtJudgeRule, + districtCourtAssistantRule, + districtCourtRegistrarRule, + ) + @Get(':subpoenaId') + @UseGuards(SubpoenaExistsGuard) + @ApiOkResponse({ + type: Subpoena, + description: 'Gets the subpoena for a given defendant', + }) + getSubpoena( + @Param('caseId') caseId: string, + @Param('defendantId') defendantId: string, + @Param('subpoenaId') subpoenaId: string, + @CurrentSubpoena() subpoena: Subpoena, + @CurrentHttpUser() user: User, + ): Promise { + this.logger.debug( + `Gets subpoena ${subpoenaId} for defendant ${defendantId} of case ${caseId}`, + ) + + return this.subpoenaService.getSubpoena(subpoena, user) + } + @RolesRules( prosecutorRule, prosecutorRepresentativeRule, diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts index 5c9bea446004..e2d3116d4aa5 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts @@ -1,5 +1,5 @@ import { Base64 } from 'js-base64' -import { Includeable } from 'sequelize' +import { Includeable, Sequelize } from 'sequelize' import { Transaction } from 'sequelize/types' import { @@ -7,8 +7,9 @@ import { Inject, Injectable, InternalServerErrorException, + NotFoundException, } from '@nestjs/common' -import { InjectModel } from '@nestjs/sequelize' +import { InjectConnection, InjectModel } from '@nestjs/sequelize' import type { Logger } from '@island.is/logging' import { LOGGER_PROVIDER } from '@island.is/logging' @@ -34,7 +35,7 @@ import { DefendantService } from '../defendant/defendant.service' import { Defendant } from '../defendant/models/defendant.model' import { EventService } from '../event' import { FileService } from '../file' -import { PoliceService } from '../police' +import { PoliceService, SubpoenaInfo } from '../police' import { User } from '../user' import { UpdateSubpoenaDto } from './dto/updateSubpoena.dto' import { DeliverResponse } from './models/deliver.response' @@ -58,9 +59,31 @@ export const include: Includeable[] = [ { model: Defendant, as: 'defendant' }, ] +const subpoenaInfoKeys: Array = [ + 'serviceStatus', + 'comment', + 'servedBy', + 'defenderNationalId', + 'serviceDate', +] + +const isNewValueSetAndDifferent = ( + newValue: unknown, + oldValue: unknown, +): boolean => Boolean(newValue) && newValue !== oldValue + +export const isSubpoenaInfoChanged = ( + newSubpoenaInfo: SubpoenaInfo, + oldSubpoenaInfo: SubpoenaInfo, +) => + subpoenaInfoKeys.some((key) => + isNewValueSetAndDifferent(newSubpoenaInfo[key], oldSubpoenaInfo[key]), + ) + @Injectable() export class SubpoenaService { constructor( + @InjectConnection() private readonly sequelize: Sequelize, @InjectModel(Subpoena) private readonly subpoenaModel: typeof Subpoena, private readonly pdfService: PdfService, private readonly messageService: MessageService, @@ -145,7 +168,6 @@ export class SubpoenaService { async update( subpoena: Subpoena, update: UpdateSubpoenaDto, - transaction?: Transaction, ): Promise { const { defenderChoice, @@ -159,61 +181,63 @@ export class SubpoenaService { requestedDefenderName, } = update - const [numberOfAffectedRows] = await this.subpoenaModel.update(update, { - where: { subpoenaId: subpoena.subpoenaId }, - returning: true, - transaction, - }) + await this.sequelize.transaction(async (transaction) => { + const [numberOfAffectedRows] = await this.subpoenaModel.update(update, { + where: { id: subpoena.id }, + returning: true, + transaction, + }) - if (numberOfAffectedRows > 1) { - // Tolerate failure, but log error - this.logger.error( - `Unexpected number of rows ${numberOfAffectedRows} affected when updating subpoena`, - ) - } + if (numberOfAffectedRows > 1) { + // Tolerate failure, but log error + this.logger.error( + `Unexpected number of rows ${numberOfAffectedRows} affected when updating subpoena`, + ) + } - if ( - subpoena.case && - subpoena.defendant && - (defenderChoice || - defenderNationalId || - defenderName || - defenderEmail || - defenderPhoneNumber || - requestedDefenderChoice || - requestedDefenderNationalId || - requestedDefenderName) - ) { - // If there is a change in the defender choice after the judge has confirmed the choice, - // we need to set the isDefenderChoiceConfirmed to false - const resetDefenderChoiceConfirmed = - subpoena.defendant?.isDefenderChoiceConfirmed && - ((defenderChoice && - subpoena.defendant?.defenderChoice !== defenderChoice) || - (defenderNationalId && - subpoena.defendant?.defenderNationalId !== defenderNationalId)) - - // FĂŠra message handling Ă­ defendant service - await this.defendantService.updateRestricted( - subpoena.case, - subpoena.defendant, - { - defenderChoice, - defenderNationalId, - defenderName, - defenderEmail, - defenderPhoneNumber, - requestedDefenderChoice, - requestedDefenderNationalId, - requestedDefenderName, - }, - resetDefenderChoiceConfirmed ? false : undefined, - transaction, - ) - } + if ( + subpoena.case && + subpoena.defendant && + (defenderChoice || + defenderNationalId || + defenderName || + defenderEmail || + defenderPhoneNumber || + requestedDefenderChoice || + requestedDefenderNationalId || + requestedDefenderName) + ) { + // If there is a change in the defender choice after the judge has confirmed the choice, + // we need to set the isDefenderChoiceConfirmed to false + const resetDefenderChoiceConfirmed = + subpoena.defendant?.isDefenderChoiceConfirmed && + ((defenderChoice && + subpoena.defendant?.defenderChoice !== defenderChoice) || + (defenderNationalId && + subpoena.defendant?.defenderNationalId !== defenderNationalId)) + + // FĂŠra message handling Ă­ defendant service + await this.defendantService.updateRestricted( + subpoena.case, + subpoena.defendant, + { + defenderChoice, + defenderNationalId, + defenderName, + defenderEmail, + defenderPhoneNumber, + requestedDefenderChoice, + requestedDefenderNationalId, + requestedDefenderName, + }, + resetDefenderChoiceConfirmed ? false : undefined, + transaction, + ) + } + }) // No need to wait for this to finish - this.addMessagesForSubpoenaUpdateToQueue(subpoena, serviceStatus) + await this.addMessagesForSubpoenaUpdateToQueue(subpoena, serviceStatus) if ( update.serviceStatus && @@ -228,21 +252,32 @@ export class SubpoenaService { ) } - return this.findBySubpoenaId(subpoena.subpoenaId) + return this.findById(subpoena.id) } - async findBySubpoenaId(subpoenaId?: string): Promise { - if (!subpoenaId) { - throw new Error('Missing subpoena id') + async findById(subpoenaId: string): Promise { + const subpoena = await this.subpoenaModel.findOne({ + include, + where: { id: subpoenaId }, + }) + + if (!subpoena) { + throw new NotFoundException(`Subpoena ${subpoenaId} does not exist`) } + return subpoena + } + + async findBySubpoenaId(policeSubpoenaId?: string): Promise { const subpoena = await this.subpoenaModel.findOne({ include, - where: { subpoenaId }, + where: { subpoenaId: policeSubpoenaId }, }) if (!subpoena) { - throw new Error(`Subpoena with subpoena id ${subpoenaId} not found`) + throw new NotFoundException( + `Subpoena with police subpoena id ${policeSubpoenaId} does not exist`, + ) } return subpoena @@ -320,4 +355,30 @@ export class SubpoenaService { return { delivered: false } } } + + async getSubpoena(subpoena: Subpoena, user?: TUser): Promise { + if (!subpoena.subpoenaId) { + // The subpoena has not been delivered to the police + return subpoena + } + + if (subpoena.serviceStatus) { + // The subpoena has already been served to the defendant + return subpoena + } + + // We don't know if the subpoena has been served to the defendant + // so we need to check the police service + const subpoenaInfo = await this.policeService.getSubpoenaStatus( + subpoena.subpoenaId, + user, + ) + + if (!isSubpoenaInfoChanged(subpoenaInfo, subpoena)) { + // The subpoena has not changed + return subpoena + } + + return this.update(subpoena, subpoenaInfo) + } } diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/internalSubpoenaControllerGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/internalSubpoenaControllerGuards.spec.ts new file mode 100644 index 000000000000..09ed4d41ebe3 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/internalSubpoenaControllerGuards.spec.ts @@ -0,0 +1,17 @@ +import { TokenGuard } from '@island.is/judicial-system/auth' + +import { InternalSubpoenaController } from '../../internalSubpoena.controller' + +describe('InternalSubpoenaController - guards', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let guards: any[] + + beforeEach(() => { + guards = Reflect.getMetadata('__guards__', InternalSubpoenaController) + }) + + it('should have the right guard configuration', () => { + expect(guards).toHaveLength(1) + expect(new guards[0]()).toBeInstanceOf(TokenGuard) + }) +}) diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/updateSubpoeanaGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/updateSubpoeanaGuards.spec.ts new file mode 100644 index 000000000000..1cad7bd28cdb --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/updateSubpoeanaGuards.spec.ts @@ -0,0 +1,19 @@ +import { PoliceSubpoenaExistsGuard } from '../../guards/policeSubpoenaExists.guard' +import { InternalSubpoenaController } from '../../internalSubpoena.controller' + +describe('InternalSubpoenaController - Update subpoena guards', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let guards: any[] + + beforeEach(() => { + guards = Reflect.getMetadata( + '__guards__', + InternalSubpoenaController.prototype.updateSubpoena, + ) + }) + + it('should have the right guard configuration', () => { + expect(guards).toHaveLength(1) + expect(new guards[0]()).toBeInstanceOf(PoliceSubpoenaExistsGuard) + }) +}) diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/getSubpoenaPdfGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/getSubpoenaPdfGuards.spec.ts index 085cc4d55b71..9ed23d7309b2 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/getSubpoenaPdfGuards.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/getSubpoenaPdfGuards.spec.ts @@ -1,4 +1,4 @@ -import { SubpoenaExistsOptionalGuard } from '../../guards/subpoenaExists.guard' +import { SubpoenaExistsGuard } from '../../guards/subpoenaExists.guard' import { SubpoenaController } from '../../subpoena.controller' describe('SubpoenaController - Get subpoena pdf guards', () => { @@ -14,6 +14,6 @@ describe('SubpoenaController - Get subpoena pdf guards', () => { it('should have the right guard configuration', () => { expect(guards).toHaveLength(1) - expect(new guards[0]()).toBeInstanceOf(SubpoenaExistsOptionalGuard) + expect(new guards[0]()).toBeInstanceOf(SubpoenaExistsGuard) }) }) diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.controller.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.controller.ts index 3088b8a144f1..6663f8cd5c21 100644 --- a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.controller.ts +++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.controller.ts @@ -65,7 +65,7 @@ export class CaseController { }) @CommonApiResponses @ApiLocaleQuery - async getAllCases( + getAllCases( @CurrentUser() user: User, @Query() query?: { locale: string }, ): Promise { @@ -85,7 +85,7 @@ export class CaseController { description: 'Case for given case id and authenticated user not found', }) @ApiLocaleQuery - async getCase( + getCase( @Param('caseId', new ParseUUIDPipe()) caseId: string, @CurrentUser() user: User, @Query() query?: { locale: string }, @@ -106,7 +106,7 @@ export class CaseController { description: 'Subpoena for given case id and authenticated user not found', }) @ApiLocaleQuery - async getSubpoena( + getSubpoena( @Param('caseId', new ParseUUIDPipe()) caseId: string, @CurrentUser() user: User, @Query() query?: { locale: string }, @@ -131,7 +131,7 @@ export class CaseController { description: 'User is not allowed to update subpoena', }) @ApiLocaleQuery - async updateSubpoena( + updateSubpoena( @CurrentUser() user: User, @Param('caseId', new ParseUUIDPipe()) caseId: string, @Body() defenderAssignment: UpdateSubpoenaDto, diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts index 80fe904f3ff2..6f4274fcbe56 100644 --- a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts +++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts @@ -11,7 +11,6 @@ import { AuditedAction, AuditTrailService, } from '@island.is/judicial-system/audit-trail' -import { normalizeAndFormatNationalId } from '@island.is/judicial-system/formatters' import { LawyersService } from '@island.is/judicial-system/lawyers' import { DefenderChoice } from '@island.is/judicial-system/types' @@ -43,15 +42,15 @@ export class CaseService { } async getCase( - id: string, + caseId: string, nationalId: string, lang?: string, ): Promise { return this.auditTrailService.audit( 'digital-mailbox-api', AuditedAction.GET_INDICTMENT, - this.getCaseInfo(id, nationalId, lang), - () => id, + this.getCaseInfo(caseId, nationalId, lang), + () => caseId, ) } @@ -92,27 +91,11 @@ export class CaseService { } private async getCaseInfo( - id: string, + caseId: string, nationalId: string, lang?: string, ): Promise { - const response = await this.fetchCase(id, nationalId) - const defendantInfo = response.defendants.find( - (defendant) => - defendant.nationalId && - normalizeAndFormatNationalId(nationalId).includes(defendant.nationalId), - ) - - if (!defendantInfo) { - throw new NotFoundException('Defendant not found') - } - - if ( - defendantInfo.subpoenas?.[0]?.subpoenaId && - !defendantInfo.subpoenas[0].serviceStatus - ) { - await this.fetchServiceStatus(id, defendantInfo.subpoenas[0].subpoenaId) - } + const response = await this.fetchCase(caseId, nationalId) return CaseResponse.fromInternalCaseResponse(response, lang) } @@ -168,6 +151,7 @@ export class CaseService { requestedDefenderName: chosenLawyer?.Name, } await this.patchDefenseInfo(defendantNationalId, caseId, defenderChoice) + const updatedCase = await this.fetchCase(caseId, defendantNationalId) return SubpoenaResponse.fromInternalCaseResponse( @@ -182,9 +166,9 @@ export class CaseService { ): Promise { try { const res = await fetch( - `${this.config.backendUrl}/api/internal/cases/indictments`, + `${this.config.backendUrl}/api/internal/cases/indictments/defendant/${nationalId}`, { - method: 'POST', + method: 'GET', headers: { 'Content-Type': 'application/json', authorization: `Bearer ${this.config.secretToken}`, @@ -206,58 +190,12 @@ export class CaseService { } private async fetchCase( - id: string, - nationalId: string, - ): Promise { - try { - const res = await fetch( - `${this.config.backendUrl}/api/internal/case/indictment/${id}`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - authorization: `Bearer ${this.config.secretToken}`, - }, - body: JSON.stringify({ nationalId }), - }, - ) - - if (!res.ok) { - if (res.status === 404) { - throw new NotFoundException(`Case ${id} not found`) - } - - const reason = await res.text() - - throw new BadGatewayException( - reason || 'Unexpected error occurred while fetching case by ID', - ) - } - - const caseData = await res.json() - - return caseData - } catch (reason) { - if ( - reason instanceof BadGatewayException || - reason instanceof NotFoundException - ) { - throw reason - } - - throw new BadGatewayException( - `Failed to fetch case by id: ${reason.message}`, - ) - } - } - - private async fetchServiceStatus( caseId: string, - subpoenaId: string, + nationalId: string, ): Promise { try { const res = await fetch( - `${this.config.backendUrl}/api/internal/case/${caseId}/subpoenaStatus/${subpoenaId}`, + `${this.config.backendUrl}/api/internal/case/indictment/${caseId}/defendant/${nationalId}`, { method: 'GET', headers: { @@ -275,8 +213,7 @@ export class CaseService { const reason = await res.text() throw new BadGatewayException( - reason || - 'Unexpected error occurred while fetching serviceStatus by subpoenaID', + reason || 'Unexpected error occurred while fetching case by ID', ) } @@ -292,7 +229,7 @@ export class CaseService { } throw new BadGatewayException( - `Failed to fetch serviceStatus by subpoenaId: ${reason.message}`, + `Failed to fetch case by id: ${reason.message}`, ) } } diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/defenders/defender.controller.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/defenders/defender.controller.ts index 24162164dc56..3e9b45a2b3ac 100644 --- a/apps/judicial-system/digital-mailbox-api/src/app/modules/defenders/defender.controller.ts +++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/defenders/defender.controller.ts @@ -1,9 +1,10 @@ import { CacheInterceptor } from '@nestjs/cache-manager' import { + BadGatewayException, Controller, Get, Inject, - InternalServerErrorException, + NotFoundException, Param, UseInterceptors, } from '@nestjs/common' @@ -29,7 +30,7 @@ export class DefenderController { type: [Defender], description: 'Returns a list of defenders', }) - @ApiResponse({ status: 500, description: 'Failed to retrieve defenders' }) + @ApiResponse({ status: 502, description: 'Failed to retrieve defenders' }) async getLawyers(): Promise { try { this.logger.debug('Retrieving litigators from lawyer registry') @@ -45,7 +46,7 @@ export class DefenderController { })) } catch (error) { this.logger.error('Failed to retrieve lawyers', error) - throw new InternalServerErrorException('Failed to retrieve lawyers') + throw new BadGatewayException('Failed to retrieve lawyers') } } @@ -54,6 +55,8 @@ export class DefenderController { type: Defender, description: 'Retrieves a defender by national id', }) + @ApiResponse({ status: 404, description: 'Defender not found' }) + @ApiResponse({ status: 502, description: 'Failed to retrieve defender' }) async getLawyer(@Param('nationalId') nationalId: string): Promise { try { this.logger.debug(`Retrieving lawyer by national id ${nationalId}`) @@ -65,8 +68,12 @@ export class DefenderController { practice: lawyer.Practice, } } catch (error) { + if (error instanceof NotFoundException) { + throw error + } + this.logger.error('Failed to retrieve lawyer', error) - throw new InternalServerErrorException('Failed to retrieve lawyer') + throw new BadGatewayException('Failed to retrieve lawyer') } } } diff --git a/apps/judicial-system/web/src/components/FormProvider/case.graphql b/apps/judicial-system/web/src/components/FormProvider/case.graphql index f2a5e28a0aa0..341c18c355fc 100644 --- a/apps/judicial-system/web/src/components/FormProvider/case.graphql +++ b/apps/judicial-system/web/src/components/FormProvider/case.graphql @@ -44,6 +44,7 @@ query Case($input: CaseQueryInput!) { comment defenderNationalId caseId + defendantId subpoenaId } } diff --git a/apps/judicial-system/web/src/components/FormProvider/limitedAccessCase.graphql b/apps/judicial-system/web/src/components/FormProvider/limitedAccessCase.graphql index df8b33ad5c86..b61738d433a9 100644 --- a/apps/judicial-system/web/src/components/FormProvider/limitedAccessCase.graphql +++ b/apps/judicial-system/web/src/components/FormProvider/limitedAccessCase.graphql @@ -56,6 +56,7 @@ query LimitedAccessCase($input: CaseQueryInput!) { comment defenderNationalId caseId + defendantId subpoenaId } } diff --git a/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx b/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx index 2c2b09df085b..8dac19a8c448 100644 --- a/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx +++ b/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx @@ -94,10 +94,7 @@ interface ServiceAnnouncementProps { const ServiceAnnouncement: FC = (props) => { const { subpoena: localSubpoena, defendantName } = props - const [subpoena, setSubpoena] = useState() - - const { subpoenaStatus, subpoenaStatusLoading, subpoenaStatusError } = - useSubpoena(localSubpoena.caseId, localSubpoena.subpoenaId) + const { subpoena, subpoenaLoading } = useSubpoena(localSubpoena) const { formatMessage } = useIntl() @@ -111,38 +108,22 @@ const ServiceAnnouncement: FC = (props) => { ? mapServiceStatusMessages(subpoena, formatMessage, lawyer) : [] - // Use data from RLS but fallback to local data - useEffect(() => { - if (subpoenaStatusError || localSubpoena.serviceStatus) { - setSubpoena(localSubpoena) - } else { - setSubpoena({ - ...localSubpoena, - servedBy: subpoenaStatus?.subpoenaStatus?.servedBy, - serviceStatus: subpoenaStatus?.subpoenaStatus?.serviceStatus, - serviceDate: subpoenaStatus?.subpoenaStatus?.serviceDate, - comment: subpoenaStatus?.subpoenaStatus?.comment, - defenderNationalId: subpoenaStatus?.subpoenaStatus?.defenderNationalId, - }) - } - }, [localSubpoena, subpoenaStatus, subpoenaStatusError]) - - return !subpoena && !subpoenaStatusLoading ? ( - {renderError(formatMessage)} - ) : subpoenaStatusLoading && !localSubpoena.serviceStatus ? ( + return subpoenaLoading ? ( + ) : !subpoena ? ( + {renderError(formatMessage)} ) : ( {messages.map((msg) => ( - + {msg} ))} diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx index f22e56f7832d..d8528fc3bef7 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx @@ -79,16 +79,13 @@ const IndictmentOverview = () => { {formatMessage(strings.inProgressTitle)} {workingCase.defendants?.map((defendant) => - defendant.subpoenas?.map( - (subpoena) => - subpoena.subpoenaId && ( - - ), - ), + defendant.subpoenas?.map((subpoena) => ( + + )), )} {workingCase.court && latestDate?.date && diff --git a/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Overview/Overview.tsx b/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Overview/Overview.tsx index 3f1c8110e6b9..2640b512c74d 100644 --- a/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Overview/Overview.tsx +++ b/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Overview/Overview.tsx @@ -191,16 +191,13 @@ const Overview: FC = () => { {formatMessage(strings.heading)} {workingCase.defendants?.map((defendant) => - defendant.subpoenas?.map( - (subpoena) => - subpoena.subpoenaId && ( - - ), - ), + defendant.subpoenas?.map((subpoena) => ( + + )), )} {workingCase.court && latestDate?.date && diff --git a/apps/judicial-system/web/src/utils/hooks/useSubpoena/getSubpoenaStatus.graphql b/apps/judicial-system/web/src/utils/hooks/useSubpoena/getSubpoenaStatus.graphql deleted file mode 100644 index a87df3e15366..000000000000 --- a/apps/judicial-system/web/src/utils/hooks/useSubpoena/getSubpoenaStatus.graphql +++ /dev/null @@ -1,9 +0,0 @@ -query SubpoenaStatus($input: SubpoenaStatusQueryInput!) { - subpoenaStatus(input: $input) { - serviceStatus - servedBy - comment - serviceDate - defenderNationalId - } -} diff --git a/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts b/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts index 3d2ab133f847..de5fc94af08d 100644 --- a/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts +++ b/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts @@ -1,20 +1,29 @@ -import { useSubpoenaStatusQuery } from './getSubpoenaStatus.generated' +import { Subpoena } from '@island.is/judicial-system-web/src/graphql/schema' -const useSubpoena = (caseId?: string | null, subpoenaId?: string | null) => { - const { - data: subpoenaStatus, - loading: subpoenaStatusLoading, - error: subpoenaStatusError, - } = useSubpoenaStatusQuery({ +import { useSubpoenaQuery } from './subpoena.generated' + +const useSubpoena = (subpoena: Subpoena) => { + // Skip if the subpoena has not been sent to the police + // or if the subpoena already has a service status + const skip = !subpoena.subpoenaId || Boolean(subpoena.serviceStatus) + + const { data, loading, error } = useSubpoenaQuery({ + skip, variables: { input: { - caseId: caseId ?? '', - subpoenaId: subpoenaId ?? '', + caseId: subpoena?.caseId ?? '', + defendantId: subpoena?.defendantId ?? '', + subpoenaId: subpoena?.id ?? '', }, }, + fetchPolicy: 'no-cache', + errorPolicy: 'all', }) - return { subpoenaStatus, subpoenaStatusLoading, subpoenaStatusError } + return { + subpoena: skip || error ? subpoena : data?.subpoena, + subpoenaLoading: skip ? false : loading, + } } export default useSubpoena diff --git a/apps/judicial-system/web/src/utils/hooks/useSubpoena/subpoena.graphql b/apps/judicial-system/web/src/utils/hooks/useSubpoena/subpoena.graphql new file mode 100644 index 000000000000..745293ed61eb --- /dev/null +++ b/apps/judicial-system/web/src/utils/hooks/useSubpoena/subpoena.graphql @@ -0,0 +1,14 @@ +query Subpoena($input: SubpoenaQueryInput!) { + subpoena(input: $input) { + id + created + serviceStatus + serviceDate + servedBy + comment + defenderNationalId + caseId + defendantId + subpoenaId + } +} diff --git a/apps/judicial-system/xrd-api/src/app/app.controller.ts b/apps/judicial-system/xrd-api/src/app/app.controller.ts index 5e49256d0d48..639611f2242f 100644 --- a/apps/judicial-system/xrd-api/src/app/app.controller.ts +++ b/apps/judicial-system/xrd-api/src/app/app.controller.ts @@ -1,8 +1,8 @@ import { + BadGatewayException, Body, Controller, Inject, - InternalServerErrorException, Param, ParseUUIDPipe, Patch, @@ -10,7 +10,7 @@ import { UseInterceptors, } from '@nestjs/common' import { Get } from '@nestjs/common' -import { ApiCreatedResponse, ApiResponse } from '@nestjs/swagger' +import { ApiCreatedResponse, ApiOkResponse, ApiResponse } from '@nestjs/swagger' import type { Logger } from '@island.is/logging' import { LOGGER_PROVIDER } from '@island.is/logging' @@ -45,7 +45,11 @@ export class AppController { } @Get('defenders') - @ApiResponse({ status: 500, description: 'Failed to retrieve defenders' }) + @ApiOkResponse({ + type: [Defender], + description: 'Returns a list of defenders', + }) + @ApiResponse({ status: 502, description: 'Failed to retrieve defenders' }) async getLawyers(): Promise { try { this.logger.debug('Retrieving litigators from lawyer registry') @@ -61,12 +65,13 @@ export class AppController { })) } catch (error) { this.logger.error('Failed to retrieve lawyers', error) - throw new InternalServerErrorException('Failed to retrieve lawyers') + throw new BadGatewayException('Failed to retrieve lawyers') } } @Patch('subpoena/:subpoenaId') - @ApiResponse({ status: 500, description: 'Failed to update subpoena' }) + @ApiResponse({ status: 400, description: 'Invalid input' }) + @ApiResponse({ status: 502, description: 'Failed to update subpoena' }) async updateSubpoena( @Param('subpoenaId', new ParseUUIDPipe()) subpoenaId: string, @Body() updateSubpoena: UpdateSubpoenaDto, diff --git a/apps/judicial-system/xrd-api/src/app/app.service.ts b/apps/judicial-system/xrd-api/src/app/app.service.ts index 57648e9254f7..2cdd54556f9b 100644 --- a/apps/judicial-system/xrd-api/src/app/app.service.ts +++ b/apps/judicial-system/xrd-api/src/app/app.service.ts @@ -132,6 +132,7 @@ export class AppService { `Failed to retrieve lawyer with national id ${updateSubpoena.defenderNationalId}`, reason, ) + throw new BadRequestException('Lawyer not found') } } diff --git a/libs/judicial-system/audit-trail/src/lib/auditTrail.service.ts b/libs/judicial-system/audit-trail/src/lib/auditTrail.service.ts index d20af4e3c6c7..70b82ad7d3ef 100644 --- a/libs/judicial-system/audit-trail/src/lib/auditTrail.service.ts +++ b/libs/judicial-system/audit-trail/src/lib/auditTrail.service.ts @@ -41,7 +41,6 @@ export enum AuditedAction { GET_SERVICE_CERTIFICATE_PDF = 'GET_SERVICE_CERTIFICATE', GET_ALL_FILES_ZIP = 'GET_ALL_FILES_ZIP', GET_INSTITUTIONS = 'GET_INSTITUTIONS', - GET_SUBPOENA_STATUS = 'GET_SUBPOENA_STATUS', CREATE_PRESIGNED_POST = 'CREATE_PRESIGNED_POST', CREATE_FILE = 'CREATE_FILE', UPDATE_FILES = 'UPDATE_FILES', diff --git a/libs/judicial-system/lawyers/src/lib/lawyers.service.ts b/libs/judicial-system/lawyers/src/lib/lawyers.service.ts index 4c89ce69bb67..251de7a67721 100644 --- a/libs/judicial-system/lawyers/src/lib/lawyers.service.ts +++ b/libs/judicial-system/lawyers/src/lib/lawyers.service.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common' +import { Inject, Injectable, NotFoundException } from '@nestjs/common' import type { Logger } from '@island.is/logging' import { LOGGER_PROVIDER } from '@island.is/logging' @@ -31,7 +31,7 @@ export class LawyersService { ) if (response.ok) { - return await response.json() + return response.json() } const reason = await response.text() @@ -51,11 +51,16 @@ export class LawyersService { ) if (response.ok) { - return await response.json() + return response.json() } const reason = await response.text() this.logger.info('Failed to get lawyer from lawyer registry:', reason) + + if (response.status === 404) { + throw new NotFoundException('Lawyer not found') + } + throw new Error(reason) } } From 14b2142ca50081758a68fcd7a3c95a546b530ba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Eorkell=20M=C3=A1ni=20=C3=9Eorkelsson?= Date: Wed, 4 Dec 2024 09:45:29 +0000 Subject: [PATCH 19/85] chore(web): switch line and text around (#17120) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../web/screens/Grants/Grant/GrantSidebar.tsx | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/apps/web/screens/Grants/Grant/GrantSidebar.tsx b/apps/web/screens/Grants/Grant/GrantSidebar.tsx index e923ccbb7395..c785d595a2b1 100644 --- a/apps/web/screens/Grants/Grant/GrantSidebar.tsx +++ b/apps/web/screens/Grants/Grant/GrantSidebar.tsx @@ -40,19 +40,19 @@ export const GrantSidebar = ({ grant, locale }: Props) => { generateLine( formatMessage(m.single.fund), grant?.fund?.link?.slug ? ( - - + + {grant.fund.title} - - + + ) : undefined, ), generateLine( From 4dec76c3b462ab2e78191c380276c081c06c4a8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Eorkell=20M=C3=A1ni=20=C3=9Eorkelsson?= Date: Wed, 4 Dec 2024 10:33:06 +0000 Subject: [PATCH 20/85] fix(web): order filter (#17121) * chore: switch line and text around * fix: init --- .../SearchResults/SearchResultsFilter.tsx | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/apps/web/screens/Grants/SearchResults/SearchResultsFilter.tsx b/apps/web/screens/Grants/SearchResults/SearchResultsFilter.tsx index e8045f00d55b..058fa195b758 100644 --- a/apps/web/screens/Grants/SearchResults/SearchResultsFilter.tsx +++ b/apps/web/screens/Grants/SearchResults/SearchResultsFilter.tsx @@ -1,4 +1,5 @@ import { useIntl } from 'react-intl' +import sortBy from 'lodash/sortBy' import { Box, @@ -30,13 +31,17 @@ export const GrantsSearchResultsFilter = ({ variant = 'default', }: Props) => { const { formatMessage } = useIntl() - const categoryFilters = tags?.filter( - (t) => t.genericTagGroup?.slug === 'grant-category', - ) - const typeFilters = tags?.filter( - (t) => t.genericTagGroup?.slug === 'grant-type', - ) + const sortedFilters = { + categories: sortBy( + tags?.filter((t) => t.genericTagGroup?.slug === 'grant-category'), + 'title', + ), + types: sortBy( + tags?.filter((t) => t.genericTagGroup?.slug === 'grant-type'), + 'title', + ), + } return ( ({ + filters: sortedFilters.categories.map((t) => ({ value: t.slug, label: t.title, })), } : undefined, - typeFilters + sortedFilters.types ? { id: 'type', label: formatMessage(m.search.type), selected: searchState?.['type'] ?? [], - filters: typeFilters.map((t) => ({ + filters: sortedFilters.types.map((t) => ({ value: t.slug, label: t.title, })), From 4a229a48d71bac8032a268e5d072982bbe07cef4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A6ring=20Gunnar=20Stein=C3=BE=C3=B3rsson?= Date: Wed, 4 Dec 2024 11:27:10 +0000 Subject: [PATCH 21/85] feat(cms): Be able to query organization by national id (#17122) * Be able to query organization by national id * Refactor * Be able to conditionally require field value to match code before refactor --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- libs/cms/src/lib/cms.contentful.service.ts | 54 +++++++++------------- 1 file changed, 23 insertions(+), 31 deletions(-) diff --git a/libs/cms/src/lib/cms.contentful.service.ts b/libs/cms/src/lib/cms.contentful.service.ts index f1e40230d6d8..15ba9425ed37 100644 --- a/libs/cms/src/lib/cms.contentful.service.ts +++ b/libs/cms/src/lib/cms.contentful.service.ts @@ -348,18 +348,20 @@ export class CmsContentfulService { ) } - async getOrganization( - slug: string, + private async getOrganizationBy( + fieldName: string, + fieldValue: string, lang: string, + requireFieldValue = false, ): Promise { - if (!slug) { + if (requireFieldValue && !fieldValue) { return null } const params = { ['content_type']: 'organization', include: 10, - 'fields.slug': slug, + [`fields.${fieldName}`]: fieldValue, limit: 1, } @@ -372,42 +374,32 @@ export class CmsContentfulService { ) } + async getOrganization( + slug: string, + lang: string, + ): Promise { + return this.getOrganizationBy('slug', slug, lang, true) + } + async getOrganizationByTitle( title: string, lang: string, - ): Promise { - const params = { - ['content_type']: 'organization', - include: 10, - 'fields.title[match]': title, - } - - const result = await this.contentfulRepository - .getLocalizedEntries(lang, params) - .catch(errorHandler('getOrganization')) - - return ( - (result.items as types.IOrganization[]).map(mapOrganization)[0] ?? null - ) + ): Promise { + return this.getOrganizationBy('title[match]', title, lang) } async getOrganizationByReferenceId( referenceId: string, lang: string, - ): Promise { - const params = { - ['content_type']: 'organization', - include: 10, - 'fields.referenceIdentifier': referenceId, - } - - const result = await this.contentfulRepository - .getLocalizedEntries(lang, params) - .catch(errorHandler('getOrganization')) + ): Promise { + return this.getOrganizationBy('referenceIdentifier', referenceId, lang) + } - return ( - (result.items as types.IOrganization[]).map(mapOrganization)[0] ?? null - ) + async getOrganizationByNationalId( + nationalId: string, + lang: string, + ): Promise { + return this.getOrganizationBy('kennitala', nationalId, lang, true) } async getOrganizationPage( From be2291a8b6d0a2d6cb840cdde88d73712486b4f9 Mon Sep 17 00:00:00 2001 From: berglindoma13 Date: Wed, 4 Dec 2024 13:55:27 +0000 Subject: [PATCH 22/85] fix(citizenship): error indexing child (#17106) * fix * updates to application * boundary check * fix --- .../citizenship/citizenship.service.ts | 17 ++++++------ .../fields/Review/ChildrenPassportReview.tsx | 2 +- .../src/fields/Review/DocumentReview.tsx | 2 +- .../src/fields/SelectChildren/index.tsx | 5 +++- .../ChildrenOtherDocumentsSubSection.ts | 20 +++++++------- .../ChildrenPassportSubSection.ts | 26 +++++++++++-------- .../PassportSubSection.ts | 9 +------ .../citizenship/src/lib/dataSchema.ts | 2 +- .../citizenship/src/utils/childrenInfo.ts | 12 +++++---- .../src/utils/selectedIndividual.ts | 4 ++- 10 files changed, 53 insertions(+), 46 deletions(-) diff --git a/libs/application/template-api-modules/src/lib/modules/templates/directorate-of-immigration/citizenship/citizenship.service.ts b/libs/application/template-api-modules/src/lib/modules/templates/directorate-of-immigration/citizenship/citizenship.service.ts index 9e8087e553fd..b91c5902f984 100644 --- a/libs/application/template-api-modules/src/lib/modules/templates/directorate-of-immigration/citizenship/citizenship.service.ts +++ b/libs/application/template-api-modules/src/lib/modules/templates/directorate-of-immigration/citizenship/citizenship.service.ts @@ -283,6 +283,7 @@ export class CitizenshipService extends BaseTemplateApiService { }) } } + const nonNullPassports = answers.childrenPassport?.filter((x) => !!x) // Submit the application await this.directorateOfImmigrationClient.submitApplicationForCitizenship( @@ -392,14 +393,14 @@ export class CitizenshipService extends BaseTemplateApiService { familyName: c.familyName, })) || [], childrenPassport: await Promise.all( - answers.childrenPassport?.map(async (p) => ({ - nationalId: p.nationalId, - dateOfIssue: new Date(p.publishDate), - dateOfExpiry: new Date(p.expirationDate), - passportNumber: p.passportNumber, - passportTypeId: parseInt(p.passportTypeId), - countryIdOfIssuer: p.countryOfIssuerId, - file: await this.getUrlForAttachment(application, p.attachment), + nonNullPassports?.map(async (p) => ({ + nationalId: p?.nationalId ?? '', + dateOfIssue: new Date(p?.publishDate ?? ''), + dateOfExpiry: new Date(p?.expirationDate ?? ''), + passportNumber: p?.passportNumber ?? '', + passportTypeId: parseInt(p?.passportTypeId ?? ''), + countryIdOfIssuer: p?.countryOfIssuerId ?? '', + file: await this.getUrlForAttachment(application, p?.attachment), })) || [], ), childrenSupportingDocuments: await Promise.all( diff --git a/libs/application/templates/directorate-of-immigration/citizenship/src/fields/Review/ChildrenPassportReview.tsx b/libs/application/templates/directorate-of-immigration/citizenship/src/fields/Review/ChildrenPassportReview.tsx index 969a0c47e721..ce664998619e 100644 --- a/libs/application/templates/directorate-of-immigration/citizenship/src/fields/Review/ChildrenPassportReview.tsx +++ b/libs/application/templates/directorate-of-immigration/citizenship/src/fields/Review/ChildrenPassportReview.tsx @@ -40,7 +40,7 @@ export const ChildrenPassportReview: FC = ({ ) as OptionSetItem[] const passport = answers.childrenPassport?.find( - (x) => x.nationalId === child.nationalId, + (x) => x?.nationalId === child.nationalId, ) if (passport) { diff --git a/libs/application/templates/directorate-of-immigration/citizenship/src/fields/Review/DocumentReview.tsx b/libs/application/templates/directorate-of-immigration/citizenship/src/fields/Review/DocumentReview.tsx index 6e76745fe1b0..ed73191e0c25 100644 --- a/libs/application/templates/directorate-of-immigration/citizenship/src/fields/Review/DocumentReview.tsx +++ b/libs/application/templates/directorate-of-immigration/citizenship/src/fields/Review/DocumentReview.tsx @@ -109,7 +109,7 @@ export const DocumentReview: FC = ({ }, })} {answers.childrenPassport?.map((passportFiles) => { - return passportFiles.attachment?.map((file) => { + return passportFiles?.attachment?.map((file) => { return {file.name} }) })} diff --git a/libs/application/templates/directorate-of-immigration/citizenship/src/fields/SelectChildren/index.tsx b/libs/application/templates/directorate-of-immigration/citizenship/src/fields/SelectChildren/index.tsx index bf3331ca01fa..227f281c57cf 100644 --- a/libs/application/templates/directorate-of-immigration/citizenship/src/fields/SelectChildren/index.tsx +++ b/libs/application/templates/directorate-of-immigration/citizenship/src/fields/SelectChildren/index.tsx @@ -29,7 +29,10 @@ export const SelectChildren: FC = ({ field, application }) => { return { value: child.nationalId, - label: `${child.givenName} ${child.familyName}`, + label: + child.givenName && child.familyName + ? `${child.givenName} ${child.familyName}` + : `${child.fullName}`, subLabel: child.otherParent ? `${formatMessage(selectChildren.checkboxes.subLabel)} ${ child.otherParent?.fullName diff --git a/libs/application/templates/directorate-of-immigration/citizenship/src/forms/CitizenshipForm/ChildrenSupportingDocuments/ChildrenOtherDocumentsSubSection.ts b/libs/application/templates/directorate-of-immigration/citizenship/src/forms/CitizenshipForm/ChildrenSupportingDocuments/ChildrenOtherDocumentsSubSection.ts index cb942a3ec657..8249d7eb819e 100644 --- a/libs/application/templates/directorate-of-immigration/citizenship/src/forms/CitizenshipForm/ChildrenSupportingDocuments/ChildrenOtherDocumentsSubSection.ts +++ b/libs/application/templates/directorate-of-immigration/citizenship/src/forms/CitizenshipForm/ChildrenSupportingDocuments/ChildrenOtherDocumentsSubSection.ts @@ -12,6 +12,7 @@ import { supportingDocuments } from '../../../lib/messages' import { Application, FormValue } from '@island.is/application/types' import { Citizenship } from '../../../lib/dataSchema' import { + getSelectedCustodyChild, getSelectedIndividualAge, getSelectedIndividualName, } from '../../../utils' @@ -48,16 +49,17 @@ export const ChildrenOtherDocumentsSubSection = (index: number) => titleVariant: 'h5', title: '', }), - buildCustomField( - { - id: `${Routes.CHILDSUPPORTINGDOCUMENTS}[${index}].nationalId`, - title: '', - component: 'HiddenTextInput', - }, - { - index: index, + buildHiddenInput({ + id: `${Routes.CHILDSUPPORTINGDOCUMENTS}[${index}].nationalId`, + defaultValue: (application: Application) => { + const selectedChild = getSelectedCustodyChild( + application.externalData, + application.answers, + index, + ) + return selectedChild?.nationalId }, - ), + }), buildFileUploadField({ id: `${Routes.CHILDSUPPORTINGDOCUMENTS}[${index}].birthCertificate`, title: diff --git a/libs/application/templates/directorate-of-immigration/citizenship/src/forms/CitizenshipForm/ChildrenSupportingDocuments/ChildrenPassportSubSection.ts b/libs/application/templates/directorate-of-immigration/citizenship/src/forms/CitizenshipForm/ChildrenSupportingDocuments/ChildrenPassportSubSection.ts index bb5e6e8e0f91..4b592446262d 100644 --- a/libs/application/templates/directorate-of-immigration/citizenship/src/forms/CitizenshipForm/ChildrenSupportingDocuments/ChildrenPassportSubSection.ts +++ b/libs/application/templates/directorate-of-immigration/citizenship/src/forms/CitizenshipForm/ChildrenSupportingDocuments/ChildrenPassportSubSection.ts @@ -7,11 +7,14 @@ import { buildFileUploadField, buildSelectField, getValueViaPath, - buildCustomField, + buildHiddenInput, } from '@island.is/application/core' import { supportingDocuments } from '../../../lib/messages' import { Application } from '@island.is/application/types' -import { getSelectedIndividualName } from '../../../utils' +import { + getSelectedCustodyChild, + getSelectedIndividualName, +} from '../../../utils' import { OptionSetItem } from '@island.is/clients/directorate-of-immigration' import { Routes } from '../../../lib/constants' import { FILE_TYPES_ALLOWED } from '../../../shared' @@ -45,16 +48,17 @@ export const ChildrenPassportSubSection = (index: number) => title: supportingDocuments.labels.passport.title, titleVariant: 'h5', }), - buildCustomField( - { - id: `${Routes.CHILDRENPASSPORT}[${index}].nationalId`, - title: '', - component: 'HiddenTextInput', - }, - { - index: index, + buildHiddenInput({ + id: `${Routes.CHILDRENPASSPORT}[${index}].nationalId`, + defaultValue: (application: Application) => { + const selectedChild = getSelectedCustodyChild( + application.externalData, + application.answers, + index, + ) + return selectedChild?.nationalId }, - ), + }), buildDateField({ id: `${Routes.CHILDRENPASSPORT}[${index}].publishDate`, title: supportingDocuments.labels.passport.publishDate, diff --git a/libs/application/templates/directorate-of-immigration/citizenship/src/forms/CitizenshipForm/SupportingDocumentsSection/PassportSubSection.ts b/libs/application/templates/directorate-of-immigration/citizenship/src/forms/CitizenshipForm/SupportingDocumentsSection/PassportSubSection.ts index 60596972fa6f..8a11eea3f82f 100644 --- a/libs/application/templates/directorate-of-immigration/citizenship/src/forms/CitizenshipForm/SupportingDocumentsSection/PassportSubSection.ts +++ b/libs/application/templates/directorate-of-immigration/citizenship/src/forms/CitizenshipForm/SupportingDocumentsSection/PassportSubSection.ts @@ -137,14 +137,7 @@ export const PassportSubSection = buildSubSection({ const countryStr = JSON.stringify(countryOptions) const countryParsed = JSON.parse(countryStr) as OptionSetItem[] - const sortedCountryOptions = countryParsed.sort((x, y) => { - const splitX = x.name?.split(/-(.*)/s)[1].replace(' ', '') - const splitY = y.name?.split(/-(.*)/s)[1].replace(' ', '') - if (splitX && splitY && splitX > splitY) return 1 - else return -1 - }) - - return sortedCountryOptions.map(({ id, name }) => ({ + return countryParsed.map(({ id, name }) => ({ value: id?.toString() || '', label: name || '', })) diff --git a/libs/application/templates/directorate-of-immigration/citizenship/src/lib/dataSchema.ts b/libs/application/templates/directorate-of-immigration/citizenship/src/lib/dataSchema.ts index 4a976db39636..736359993cd1 100644 --- a/libs/application/templates/directorate-of-immigration/citizenship/src/lib/dataSchema.ts +++ b/libs/application/templates/directorate-of-immigration/citizenship/src/lib/dataSchema.ts @@ -347,7 +347,7 @@ export const CitizenshipSchema = z.object({ countriesOfResidence: CountriesOfResidenceSchema, staysAbroad: StaysAbroadSchema, passport: PassportSchema, - childrenPassport: z.array(ChildrenPassportSchema).optional(), + childrenPassport: z.array(ChildrenPassportSchema.nullable()).optional(), maritalStatus: MaritalStatusSchema, formerIcelander: z.enum([YES, NO]), //.refine((v) => v === YES) // TODO REVERT WHEN UTL FIXED SERVICES supportingDocuments: SupportingDocumentsSchema, diff --git a/libs/application/templates/directorate-of-immigration/citizenship/src/utils/childrenInfo.ts b/libs/application/templates/directorate-of-immigration/citizenship/src/utils/childrenInfo.ts index 3657196ad917..26e728b99e98 100644 --- a/libs/application/templates/directorate-of-immigration/citizenship/src/utils/childrenInfo.ts +++ b/libs/application/templates/directorate-of-immigration/citizenship/src/utils/childrenInfo.ts @@ -50,16 +50,18 @@ export const getSelectedCustodyChild = ( const custodyChildren = getValueViaPath( externalData, 'childrenCustodyInformation.data', - undefined, - ) as ApplicantChildCustodyInformation[] | undefined + [], + ) as ApplicantChildCustodyInformation[] const childInfo = childNationalId ? custodyChildren?.find((x) => x.nationalId === childNationalId) - : custodyChildren?.[index] - + : index < custodyChildren.length + ? custodyChildren[index] + : undefined const selectedChildren = (answers as Citizenship).selectedChildren const isSelected = - selectedChildren && selectedChildren.find((sc) => sc === childNationalId) + selectedChildren && + selectedChildren.find((sc) => sc === childInfo?.nationalId) return isSelected ? childInfo : undefined } diff --git a/libs/application/templates/directorate-of-immigration/citizenship/src/utils/selectedIndividual.ts b/libs/application/templates/directorate-of-immigration/citizenship/src/utils/selectedIndividual.ts index 17d2e64602da..1d6eca44648e 100644 --- a/libs/application/templates/directorate-of-immigration/citizenship/src/utils/selectedIndividual.ts +++ b/libs/application/templates/directorate-of-immigration/citizenship/src/utils/selectedIndividual.ts @@ -28,7 +28,9 @@ export const getSelectedIndividualName = ( ) if (selectedChild) { - return `${selectedChild.givenName} ${selectedChild.familyName}` + return selectedChild.givenName && selectedChild.familyName + ? `${selectedChild.givenName} ${selectedChild.familyName}` + : `${selectedChild.fullName}` } return undefined From 0b31a03d6750eee272cb25b819e07d999f0d024e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gunnlaugur=20Gu=C3=B0mundsson?= <34029342+GunnlaugurG@users.noreply.github.com> Date: Wed, 4 Dec 2024 14:22:51 +0000 Subject: [PATCH 23/85] fix(auth-admin): Allow delegation-admin into domains controller (#17125) * allow delegation admin into domain controller * fix scopes for find all --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../delegation-api/src/app/domains/domains.controller.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/services/auth/delegation-api/src/app/domains/domains.controller.ts b/apps/services/auth/delegation-api/src/app/domains/domains.controller.ts index 29aa5f7b6480..a34e1075f2b3 100644 --- a/apps/services/auth/delegation-api/src/app/domains/domains.controller.ts +++ b/apps/services/auth/delegation-api/src/app/domains/domains.controller.ts @@ -15,7 +15,7 @@ import { ScopesGuard, User, } from '@island.is/auth-nest-tools' -import { delegationScopes } from '@island.is/auth/scopes' +import { AdminPortalScope, delegationScopes } from '@island.is/auth/scopes' import { Audit } from '@island.is/nest/audit' import { Documentation } from '@island.is/nest/swagger' @@ -92,6 +92,11 @@ export class DomainsController { @Audit({ resources: (domains) => domains.map((domain) => domain.name), }) + @Scopes( + ...delegationScopes, + AdminPortalScope.delegationSystemAdmin, + AdminPortalScope.delegationSystem, + ) findAll( @CurrentUser() user: User, @Query('lang') language?: string, From f79ff7606e51f69f192c38abb17b8f5fe2e5cf64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAnar=20Vestmann?= <43557895+RunarVestmann@users.noreply.github.com> Date: Wed, 4 Dec 2024 14:43:27 +0000 Subject: [PATCH 24/85] fix(web): In case no organization page is found we still want to potentially render a frontpage (#17116) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- apps/web/pages/s/[...slugs]/index.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/web/pages/s/[...slugs]/index.tsx b/apps/web/pages/s/[...slugs]/index.tsx index 9b72d7cda505..9eca5605ac4c 100644 --- a/apps/web/pages/s/[...slugs]/index.tsx +++ b/apps/web/pages/s/[...slugs]/index.tsx @@ -181,6 +181,14 @@ Component.getProps = async (context) => { }) if (!organizationPage) { + if (slugs.length === 1) { + return { + page: { + type: PageType.FRONTPAGE, + props: await Home.getProps(context), + }, + } + } throw new CustomNextError(404, 'Organization page was not found') } From 7c45c9744b792a859b62af38cbc4ce0be740ad8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAnar=20Vestmann?= <43557895+RunarVestmann@users.noreply.github.com> Date: Wed, 4 Dec 2024 15:01:30 +0000 Subject: [PATCH 25/85] feat(web): Add namespace for 'samgongustofa' web chat config (#17123) * Add namespace for 'samgongustofa' * Update namespace key --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- apps/web/components/ChatPanel/types.ts | 6 +++++- apps/web/components/Organization/Wrapper/config.ts | 4 ++-- apps/web/components/ServiceWeb/config.ts | 4 ++-- .../Article/components/ArticleChatPanel/config.ts | 12 ++++++------ 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/apps/web/components/ChatPanel/types.ts b/apps/web/components/ChatPanel/types.ts index 86aee28e8b9b..f512038a7fdc 100644 --- a/apps/web/components/ChatPanel/types.ts +++ b/apps/web/components/ChatPanel/types.ts @@ -24,7 +24,11 @@ export interface WatsonChatPanelProps { carbonTheme?: string // What key in the 'ChatPanels' UI Configuration in Contentful stores the language pack for this chat bot - namespaceKey?: 'default' | 'ukrainian-citizens' | 'skatturinn' + namespaceKey?: + | 'default' + | 'ukrainian-citizens' + | 'skatturinn' + | 'samgongustofa' // eslint-disable-next-line @typescript-eslint/no-explicit-any onLoad?: (instance: any) => void diff --git a/apps/web/components/Organization/Wrapper/config.ts b/apps/web/components/Organization/Wrapper/config.ts index fea6d72ffdd7..51aea21ac53c 100644 --- a/apps/web/components/Organization/Wrapper/config.ts +++ b/apps/web/components/Organization/Wrapper/config.ts @@ -116,7 +116,7 @@ export const watsonConfig: Record< serviceInstanceID: 'bc3d8312-d862-4750-b8bf-529db282050a', showLauncher: false, carbonTheme: 'g10', - namespaceKey: 'default', + namespaceKey: 'samgongustofa', }, }, is: { @@ -127,7 +127,7 @@ export const watsonConfig: Record< serviceInstanceID: 'bc3d8312-d862-4750-b8bf-529db282050a', showLauncher: false, carbonTheme: 'g10', - namespaceKey: 'default', + namespaceKey: 'samgongustofa', }, // Skatturinn - Organization diff --git a/apps/web/components/ServiceWeb/config.ts b/apps/web/components/ServiceWeb/config.ts index f545bb261ff3..0decdaf0ae43 100644 --- a/apps/web/components/ServiceWeb/config.ts +++ b/apps/web/components/ServiceWeb/config.ts @@ -113,7 +113,7 @@ export const watsonConfig: Record< serviceInstanceID: 'bc3d8312-d862-4750-b8bf-529db282050a', showLauncher: false, carbonTheme: 'g10', - namespaceKey: 'default', + namespaceKey: 'samgongustofa', }, }, en: { @@ -147,7 +147,7 @@ export const watsonConfig: Record< serviceInstanceID: 'bc3d8312-d862-4750-b8bf-529db282050a', showLauncher: false, carbonTheme: 'g10', - namespaceKey: 'default', + namespaceKey: 'samgongustofa', }, }, } diff --git a/apps/web/screens/Article/components/ArticleChatPanel/config.ts b/apps/web/screens/Article/components/ArticleChatPanel/config.ts index 8b4f165bb28e..7a70dae30f12 100644 --- a/apps/web/screens/Article/components/ArticleChatPanel/config.ts +++ b/apps/web/screens/Article/components/ArticleChatPanel/config.ts @@ -59,7 +59,7 @@ export const watsonConfig: Record< serviceInstanceID: 'bc3d8312-d862-4750-b8bf-529db282050a', showLauncher: false, carbonTheme: 'g10', - namespaceKey: 'default', + namespaceKey: 'samgongustofa', onLoad(instance) { setupOneScreenWatsonChatBot( instance, @@ -287,7 +287,7 @@ export const watsonConfig: Record< serviceInstanceID: 'bc3d8312-d862-4750-b8bf-529db282050a', showLauncher: false, carbonTheme: 'g10', - namespaceKey: 'default', + namespaceKey: 'samgongustofa', }, }, is: { @@ -298,7 +298,7 @@ export const watsonConfig: Record< serviceInstanceID: 'bc3d8312-d862-4750-b8bf-529db282050a', showLauncher: false, carbonTheme: 'g10', - namespaceKey: 'default', + namespaceKey: 'samgongustofa', }, // Uppfletting Ă­ ökutĂŠkjaskrĂĄ @@ -309,7 +309,7 @@ export const watsonConfig: Record< serviceInstanceID: 'bc3d8312-d862-4750-b8bf-529db282050a', showLauncher: false, carbonTheme: 'g10', - namespaceKey: 'default', + namespaceKey: 'samgongustofa', onLoad(instance) { setupOneScreenWatsonChatBot( instance, @@ -327,7 +327,7 @@ export const watsonConfig: Record< serviceInstanceID: 'bc3d8312-d862-4750-b8bf-529db282050a', showLauncher: false, carbonTheme: 'g10', - namespaceKey: 'default', + namespaceKey: 'samgongustofa', onLoad(instance) { setupOneScreenWatsonChatBot( instance, @@ -345,7 +345,7 @@ export const watsonConfig: Record< serviceInstanceID: 'bc3d8312-d862-4750-b8bf-529db282050a', showLauncher: false, carbonTheme: 'g10', - namespaceKey: 'default', + namespaceKey: 'samgongustofa', onLoad(instance) { setupOneScreenWatsonChatBot( instance, From 12f597ffbfa9e370445db4a614e30ade5e0fd034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAnar=20Vestmann?= <43557895+RunarVestmann@users.noreply.github.com> Date: Wed, 4 Dec 2024 15:50:41 +0000 Subject: [PATCH 26/85] feat(web): Latest generic list items - Item count is now editable in the CMS (#17128) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- libs/cms/src/lib/generated/contentfulTypes.d.ts | 3 +++ libs/cms/src/lib/models/latestGenericListItems.model.ts | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/libs/cms/src/lib/generated/contentfulTypes.d.ts b/libs/cms/src/lib/generated/contentfulTypes.d.ts index aa7d3875ab85..368f8fc63417 100644 --- a/libs/cms/src/lib/generated/contentfulTypes.d.ts +++ b/libs/cms/src/lib/generated/contentfulTypes.d.ts @@ -2127,6 +2127,9 @@ export interface ILatestGenericListItemsFields { /** See more link text */ seeMoreLinkText?: string | undefined + + /** Item Count */ + itemCount?: number | undefined } export interface ILatestGenericListItems diff --git a/libs/cms/src/lib/models/latestGenericListItems.model.ts b/libs/cms/src/lib/models/latestGenericListItems.model.ts index 3fe7a7a43103..3a7b5b189285 100644 --- a/libs/cms/src/lib/models/latestGenericListItems.model.ts +++ b/libs/cms/src/lib/models/latestGenericListItems.model.ts @@ -80,7 +80,7 @@ export const mapLatestGenericListItems = ({ ? 'is' : (sys.locale as ElasticsearchIndexLocale), page: 1, - size: 2, + size: fields.itemCount ?? 2, } : null, }) From 21ed9427df84da9e3a8e6329ef43cc4b3a1b6ab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eir=C3=ADkur=20Hei=C3=B0ar=20Nilsson?= Date: Wed, 4 Dec 2024 16:38:38 +0000 Subject: [PATCH 27/85] feat(core): Context support in logger + logging code owners (#17092) * feat(core): Context support in logger including code owner support * feat(core): NestJS interceptor and decorator for dynamic code owners * feat(core): Support for configuring codeOwner in infra dsl * chore(core): Configure code owner for some apps * feat(api): Configure dynamic code owner in some api domains * chore: charts update dirty files * chore(core): Add codeOwner to workers * chore: charts update dirty files * feat: Move code owner logic to datadog tags * fix: Unit tests --------- Co-authored-by: andes-it Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- apps/api/src/app/app.module.ts | 6 ++ .../api/infra/application-system-api.ts | 9 +- apps/auth-admin-web/infra/auth-admin-web.ts | 3 +- apps/consultation-portal/infra/samradsgatt.ts | 8 +- .../auth/admin-api/infra/auth-admin-api.ts | 2 + .../delegation-api/infra/delegation-api.ts | 2 + apps/services/auth/ids-api/infra/ids-api.ts | 8 +- .../auth/public-api/infra/auth-public-api.ts | 8 +- apps/services/bff/infra/admin-portal.infra.ts | 8 +- .../api/infra/endorsement-system-api.ts | 7 +- .../services/form-system/infra/form-system.ts | 7 +- .../services/license-api/infra/license-api.ts | 10 ++- .../infra/regulations-admin-backend.ts | 7 +- .../infra/search-indexer-service.ts | 8 +- apps/services/sessions/infra/sessions.ts | 9 +- .../infra/university-gateway.ts | 8 +- .../infra/user-notification.ts | 4 + .../user-profile/infra/service-portal-api.ts | 8 +- apps/web/infra/web.ts | 8 +- charts/identity-server/values.dev.yaml | 5 ++ charts/identity-server/values.prod.yaml | 5 ++ charts/identity-server/values.staging.yaml | 5 ++ charts/islandis/values.dev.yaml | 18 ++++ charts/islandis/values.prod.yaml | 18 ++++ charts/islandis/values.staging.yaml | 18 ++++ .../values.dev.yaml | 1 + .../values.prod.yaml | 1 + .../values.staging.yaml | 1 + .../application-system-api/values.dev.yaml | 1 + .../application-system-api/values.prod.yaml | 1 + .../values.staging.yaml | 1 + .../services/auth-admin-web/values.dev.yaml | 1 + .../services/auth-admin-web/values.prod.yaml | 1 + .../auth-admin-web/values.staging.yaml | 1 + .../consultation-portal/values.dev.yaml | 1 + .../consultation-portal/values.prod.yaml | 1 + .../consultation-portal/values.staging.yaml | 1 + .../endorsement-system-api/values.dev.yaml | 1 + .../endorsement-system-api/values.prod.yaml | 1 + .../values.staging.yaml | 1 + charts/services/license-api/values.dev.yaml | 1 + charts/services/license-api/values.prod.yaml | 1 + .../services/license-api/values.staging.yaml | 1 + .../regulations-admin-backend/values.dev.yaml | 1 + .../values.prod.yaml | 1 + .../values.staging.yaml | 1 + .../search-indexer-service/values.dev.yaml | 1 + .../search-indexer-service/values.prod.yaml | 1 + .../values.staging.yaml | 1 + .../service-portal-api/values.dev.yaml | 1 + .../service-portal-api/values.prod.yaml | 1 + .../service-portal-api/values.staging.yaml | 1 + .../services-auth-admin-api/values.dev.yaml | 1 + .../services-auth-admin-api/values.prod.yaml | 1 + .../values.staging.yaml | 1 + .../values.dev.yaml | 1 + .../values.prod.yaml | 1 + .../values.staging.yaml | 1 + .../services-auth-ids-api/values.dev.yaml | 1 + .../services-auth-ids-api/values.prod.yaml | 1 + .../services-auth-ids-api/values.staging.yaml | 1 + .../services-auth-public-api/values.dev.yaml | 1 + .../services-auth-public-api/values.prod.yaml | 1 + .../values.staging.yaml | 1 + .../values.dev.yaml | 1 + .../values.prod.yaml | 1 + .../values.staging.yaml | 1 + .../services-sessions-cleanup/values.dev.yaml | 1 + .../values.prod.yaml | 1 + .../values.staging.yaml | 1 + .../services-sessions-worker/values.dev.yaml | 1 + .../services-sessions-worker/values.prod.yaml | 1 + .../values.staging.yaml | 1 + .../services-sessions/values.dev.yaml | 1 + .../services-sessions/values.prod.yaml | 1 + .../services-sessions/values.staging.yaml | 1 + .../values.dev.yaml | 1 + .../values.prod.yaml | 1 + .../values.staging.yaml | 1 + .../values.dev.yaml | 1 + .../values.prod.yaml | 1 + .../values.staging.yaml | 1 + .../values.dev.yaml | 1 + .../values.prod.yaml | 1 + .../values.staging.yaml | 1 + .../user-notification-worker/values.dev.yaml | 1 + .../user-notification-worker/values.prod.yaml | 1 + .../values.staging.yaml | 1 + .../user-notification/values.dev.yaml | 1 + .../user-notification/values.prod.yaml | 1 + .../user-notification/values.staging.yaml | 1 + charts/services/web/values.dev.yaml | 1 + charts/services/web/values.prod.yaml | 1 + charts/services/web/values.staging.yaml | 1 + infra/src/dsl/dsl.ts | 8 ++ .../src/lib/application.resolver.ts | 25 +++--- .../src/lib/forms/forms.resolver.ts | 25 +++--- .../src/lib/groups/groups.resolver.ts | 3 + .../src/lib/inputs/inputs.resolver.ts | 3 + .../organizations/organizations.resolver.ts | 3 + .../src/lib/services/services.resolver.ts | 3 + .../src/lib/steps/steps.resolver.ts | 3 + .../resolvers/licenseCollection.resolver.ts | 14 ++- .../src/lib/resolvers/pkPass.resolver.ts | 3 + .../src/lib/resolvers/provider.resolver.ts | 3 + .../src/lib/resolvers/userLicense.resolver.ts | 3 + libs/infra-tracing/jest.config.ts | 1 + libs/infra-tracing/project.json | 2 +- libs/infra-tracing/src/index.ts | 1 + libs/infra-tracing/src/lib/code-owner.spec.ts | 49 +++++++++++ libs/infra-tracing/src/lib/code-owner.ts | 22 +++++ libs/infra-tracing/src/lib/datadog-tracer.ts | 7 +- libs/logging/jest.config.ts | 1 + libs/logging/src/index.ts | 1 + libs/logging/src/lib/context.spec.ts | 77 ++++++++++++++++ libs/logging/src/lib/context.ts | 28 ++++++ libs/logging/src/lib/logging.ts | 4 + libs/nest/core/src/index.ts | 2 + .../lib/code-owner/code-owner.decorator.ts | 7 ++ .../code-owner/code-owner.interceptor.spec.ts | 87 +++++++++++++++++++ .../lib/code-owner/code-owner.interceptor.ts | 28 ++++++ libs/shared/constants/src/index.ts | 1 + libs/shared/constants/src/lib/codeOwners.ts | 18 ++++ 123 files changed, 656 insertions(+), 44 deletions(-) create mode 100644 libs/infra-tracing/src/lib/code-owner.spec.ts create mode 100644 libs/infra-tracing/src/lib/code-owner.ts create mode 100644 libs/logging/src/lib/context.spec.ts create mode 100644 libs/logging/src/lib/context.ts create mode 100644 libs/nest/core/src/lib/code-owner/code-owner.decorator.ts create mode 100644 libs/nest/core/src/lib/code-owner/code-owner.interceptor.spec.ts create mode 100644 libs/nest/core/src/lib/code-owner/code-owner.interceptor.ts create mode 100644 libs/shared/constants/src/lib/codeOwners.ts diff --git a/apps/api/src/app/app.module.ts b/apps/api/src/app/app.module.ts index 7431d688223a..0cae9d7fa483 100644 --- a/apps/api/src/app/app.module.ts +++ b/apps/api/src/app/app.module.ts @@ -154,6 +154,7 @@ import { IdsClientConfig, XRoadConfig, } from '@island.is/nest/config' +import { CodeOwnerInterceptor } from '@island.is/nest/core' import { DataLoaderInterceptor } from '@island.is/nest/dataloader' import { FeatureFlagConfig } from '@island.is/nest/feature-flags' import { ProblemModule } from '@island.is/nest/problem' @@ -175,6 +176,7 @@ import { FormSystemModule } from '@island.is/api/domains/form-system' import { HealthDirectorateModule } from '@island.is/api/domains/health-directorate' import { VehiclesMileageClientConfig } from '@island.is/clients/vehicles-mileage' + import { getConfig } from './environments' import { GraphqlOptionsFactory } from './graphql-options.factory' import { GraphQLConfig } from './graphql.config' @@ -212,6 +214,10 @@ const environment = getConfig provide: APP_INTERCEPTOR, useClass: DataLoaderInterceptor, }, + { + provide: APP_INTERCEPTOR, + useClass: CodeOwnerInterceptor, + }, ], imports: [ GraphQLModule.forRootAsync({ diff --git a/apps/application-system/api/infra/application-system-api.ts b/apps/application-system/api/infra/application-system-api.ts index e4c51330e256..0c3b5c273739 100644 --- a/apps/application-system/api/infra/application-system-api.ts +++ b/apps/application-system/api/infra/application-system-api.ts @@ -1,4 +1,9 @@ -import { ref, service, ServiceBuilder } from '../../../../infra/src/dsl/dsl' +import { + CodeOwners, + ref, + service, + ServiceBuilder, +} from '../../../../infra/src/dsl/dsl' import { Base, ChargeFjsV2, @@ -52,6 +57,7 @@ export const workerSetup = .db() .serviceAccount('application-system-api-worker') .redis() + .codeOwner(CodeOwners.NordaApplications) .env({ IDENTITY_SERVER_CLIENT_ID: '@island.is/clients/application-system', IDENTITY_SERVER_ISSUER_URL: { @@ -131,6 +137,7 @@ export const serviceSetup = (services: { .serviceAccount(serviceAccount) .command('node') .redis() + .codeOwner(CodeOwners.NordaApplications) .args('main.js') .env({ EMAIL_REGION: 'eu-west-1', diff --git a/apps/auth-admin-web/infra/auth-admin-web.ts b/apps/auth-admin-web/infra/auth-admin-web.ts index 44299b219a8d..476fde40c532 100644 --- a/apps/auth-admin-web/infra/auth-admin-web.ts +++ b/apps/auth-admin-web/infra/auth-admin-web.ts @@ -1,4 +1,4 @@ -import { service, ServiceBuilder } from '../../../infra/src/dsl/dsl' +import { CodeOwners, service, ServiceBuilder } from '../../../infra/src/dsl/dsl' const extraAnnotations = { 'nginx.ingress.kubernetes.io/proxy-buffer-size': '16k', @@ -12,6 +12,7 @@ export const serviceSetup = (): ServiceBuilder<'auth-admin-web'> => service('auth-admin-web') .namespace('identity-server-admin') .image('auth-admin-web') + .codeOwner(CodeOwners.Aranja) .env({ NEXT_PUBLIC_BACKEND_URL: '/backend', IDENTITYSERVER_DOMAIN: { diff --git a/apps/consultation-portal/infra/samradsgatt.ts b/apps/consultation-portal/infra/samradsgatt.ts index e7ed848cbab4..e079e70cce85 100644 --- a/apps/consultation-portal/infra/samradsgatt.ts +++ b/apps/consultation-portal/infra/samradsgatt.ts @@ -1,4 +1,9 @@ -import { ref, service, ServiceBuilder } from '../../../infra/src/dsl/dsl' +import { + CodeOwners, + ref, + service, + ServiceBuilder, +} from '../../../infra/src/dsl/dsl' export const serviceSetup = (services: { api: ServiceBuilder<'api'> @@ -7,6 +12,7 @@ export const serviceSetup = (services: { consultationService .image('consultation-portal') .namespace('consultation-portal') + .codeOwner(CodeOwners.Advania) .liveness('/liveness') .readiness('/liveness') .replicaCount({ diff --git a/apps/services/auth/admin-api/infra/auth-admin-api.ts b/apps/services/auth/admin-api/infra/auth-admin-api.ts index cf773f0dbd94..e93b1e2e1759 100644 --- a/apps/services/auth/admin-api/infra/auth-admin-api.ts +++ b/apps/services/auth/admin-api/infra/auth-admin-api.ts @@ -1,4 +1,5 @@ import { + CodeOwners, json, ref, service, @@ -27,6 +28,7 @@ export const serviceSetup = (): ServiceBuilder<'services-auth-admin-api'> => { return service('services-auth-admin-api') .namespace('identity-server-admin') .image('services-auth-admin-api') + .codeOwner(CodeOwners.Aranja) .db({ name: 'servicesauth', }) diff --git a/apps/services/auth/delegation-api/infra/delegation-api.ts b/apps/services/auth/delegation-api/infra/delegation-api.ts index 1dc2615d28d6..e1e98dcd9448 100644 --- a/apps/services/auth/delegation-api/infra/delegation-api.ts +++ b/apps/services/auth/delegation-api/infra/delegation-api.ts @@ -1,4 +1,5 @@ import { + CodeOwners, json, ref, service, @@ -29,6 +30,7 @@ export const serviceSetup = (services: { return service('services-auth-delegation-api') .namespace('identity-server-delegation') .image('services-auth-delegation-api') + .codeOwner(CodeOwners.Aranja) .db({ name: 'servicesauth', }) diff --git a/apps/services/auth/ids-api/infra/ids-api.ts b/apps/services/auth/ids-api/infra/ids-api.ts index 4282d5f39f9e..711f29537f1f 100644 --- a/apps/services/auth/ids-api/infra/ids-api.ts +++ b/apps/services/auth/ids-api/infra/ids-api.ts @@ -1,4 +1,9 @@ -import { json, service, ServiceBuilder } from '../../../../../infra/src/dsl/dsl' +import { + CodeOwners, + json, + service, + ServiceBuilder, +} from '../../../../../infra/src/dsl/dsl' import { Base, Client, @@ -27,6 +32,7 @@ export const serviceSetup = (): ServiceBuilder<'services-auth-ids-api'> => { return service('services-auth-ids-api') .namespace(namespace) .image(imageName) + .codeOwner(CodeOwners.Aranja) .env({ IDENTITY_SERVER_CLIENT_ID: '@island.is/clients/auth-api', IDENTITY_SERVER_ISSUER_URL: { diff --git a/apps/services/auth/public-api/infra/auth-public-api.ts b/apps/services/auth/public-api/infra/auth-public-api.ts index 609588c46271..d8ca744663b9 100644 --- a/apps/services/auth/public-api/infra/auth-public-api.ts +++ b/apps/services/auth/public-api/infra/auth-public-api.ts @@ -1,4 +1,9 @@ -import { json, service, ServiceBuilder } from '../../../../../infra/src/dsl/dsl' +import { + CodeOwners, + json, + service, + ServiceBuilder, +} from '../../../../../infra/src/dsl/dsl' import { Base, Client, @@ -23,6 +28,7 @@ export const serviceSetup = (): ServiceBuilder<'services-auth-public-api'> => { .namespace('identity-server-admin') .image('services-auth-public-api') .db({ name: 'servicesauth' }) + .codeOwner(CodeOwners.Aranja) .env({ IDENTITY_SERVER_CLIENT_ID: '@island.is/clients/auth-api', IDENTITY_SERVER_ISSUER_URL: { diff --git a/apps/services/bff/infra/admin-portal.infra.ts b/apps/services/bff/infra/admin-portal.infra.ts index e7227d3f379a..46e246a554ce 100644 --- a/apps/services/bff/infra/admin-portal.infra.ts +++ b/apps/services/bff/infra/admin-portal.infra.ts @@ -1,5 +1,10 @@ /* eslint-disable @nx/enforce-module-boundaries */ -import { ServiceBuilder, json, service } from '../../../../infra/src/dsl/dsl' +import { + ServiceBuilder, + json, + service, + CodeOwners, +} from '../../../../infra/src/dsl/dsl' import { BffInfraServices } from '../../../../infra/src/dsl/types/input-types' const bffName = 'services-bff' @@ -13,6 +18,7 @@ export const serviceSetup = ( service(serviceName) .namespace(clientName) .image(bffName) + .codeOwner(CodeOwners.Core) .redis() .serviceAccount(bffName) .env({ diff --git a/apps/services/endorsements/api/infra/endorsement-system-api.ts b/apps/services/endorsements/api/infra/endorsement-system-api.ts index 865c1d945b8f..60ffa938b194 100644 --- a/apps/services/endorsements/api/infra/endorsement-system-api.ts +++ b/apps/services/endorsements/api/infra/endorsement-system-api.ts @@ -1,4 +1,8 @@ -import { service, ServiceBuilder } from '../../../../../infra/src/dsl/dsl' +import { + CodeOwners, + service, + ServiceBuilder, +} from '../../../../../infra/src/dsl/dsl' import { Base, Client, @@ -12,6 +16,7 @@ export const serviceSetup = .image('services-endorsements-api') .namespace('endorsement-system') .serviceAccount('endorsement-system-api') + .codeOwner(CodeOwners.Juni) .command('node') .args('--tls-min-v1.0', '--no-experimental-fetch', 'main.js') .db({ name: 'services-endorsements-api' }) diff --git a/apps/services/form-system/infra/form-system.ts b/apps/services/form-system/infra/form-system.ts index 41bacb92c560..9dda94f1db68 100644 --- a/apps/services/form-system/infra/form-system.ts +++ b/apps/services/form-system/infra/form-system.ts @@ -1,9 +1,14 @@ -import { service, ServiceBuilder } from '../../../../infra/src/dsl/dsl' +import { + CodeOwners, + service, + ServiceBuilder, +} from '../../../../infra/src/dsl/dsl' export const serviceSetup = (): ServiceBuilder<'services-form-system'> => service('services-form-system') .image('services-form-system') .namespace('services-form-system') + .codeOwner(CodeOwners.Advania) .env({ IDENTITY_SERVER_ISSUER_URL: { dev: 'https://identity-server.dev01.devland.is', diff --git a/apps/services/license-api/infra/license-api.ts b/apps/services/license-api/infra/license-api.ts index 1b5d32886203..7cca417cc585 100644 --- a/apps/services/license-api/infra/license-api.ts +++ b/apps/services/license-api/infra/license-api.ts @@ -1,16 +1,22 @@ -import { json, service, ServiceBuilder } from '../../../../infra/src/dsl/dsl' +import { + CodeOwners, + json, + service, + ServiceBuilder, +} from '../../../../infra/src/dsl/dsl' import { Base, Client, Disability, - Firearm, DrivingLicense, + Firearm, Hunting, } from '../../../../infra/src/dsl/xroad' export const serviceSetup = (): ServiceBuilder<'license-api'> => service('license-api') .namespace('license-api') + .codeOwner(CodeOwners.Hugsmidjan) .resources({ limits: { cpu: '400m', memory: '512Mi' }, requests: { cpu: '50m', memory: '256Mi' }, diff --git a/apps/services/regulations-admin-backend/infra/regulations-admin-backend.ts b/apps/services/regulations-admin-backend/infra/regulations-admin-backend.ts index 1af574773a05..1b5d7066b614 100644 --- a/apps/services/regulations-admin-backend/infra/regulations-admin-backend.ts +++ b/apps/services/regulations-admin-backend/infra/regulations-admin-backend.ts @@ -1,4 +1,8 @@ -import { service, ServiceBuilder } from '../../../../infra/src/dsl/dsl' +import { + CodeOwners, + service, + ServiceBuilder, +} from '../../../../infra/src/dsl/dsl' import { Base, Client, @@ -9,6 +13,7 @@ export const serviceSetup = (): ServiceBuilder<'regulations-admin-backend'> => service('regulations-admin-backend') .image('regulations-admin-backend') .namespace('regulations-admin') + .codeOwner(CodeOwners.Hugsmidjan) .env({ IDENTITY_SERVER_ISSUER_URL: { dev: 'https://identity-server.dev01.devland.is', diff --git a/apps/services/search-indexer/infra/search-indexer-service.ts b/apps/services/search-indexer/infra/search-indexer-service.ts index 521c9afa4475..a9e3cbd6ea97 100644 --- a/apps/services/search-indexer/infra/search-indexer-service.ts +++ b/apps/services/search-indexer/infra/search-indexer-service.ts @@ -1,4 +1,9 @@ -import { ref, service, ServiceBuilder } from '../../../../infra/src/dsl/dsl' +import { + CodeOwners, + ref, + service, + ServiceBuilder, +} from '../../../../infra/src/dsl/dsl' const envs = { APPLICATION_URL: 'http://search-indexer-service', @@ -33,6 +38,7 @@ export const serviceSetup = (): ServiceBuilder<'search-indexer-service'> => .image('services-search-indexer') .namespace('search-indexer') .serviceAccount('search-indexer') + .codeOwner(CodeOwners.Stefna) .secrets({ CONTENTFUL_ACCESS_TOKEN: '/k8s/search-indexer/CONTENTFUL_ACCESS_TOKEN', API_CMS_SYNC_TOKEN: '/k8s/search-indexer/API_CMS_SYNC_TOKEN', diff --git a/apps/services/sessions/infra/sessions.ts b/apps/services/sessions/infra/sessions.ts index bb771f1b2383..a5c0683246fa 100644 --- a/apps/services/sessions/infra/sessions.ts +++ b/apps/services/sessions/infra/sessions.ts @@ -1,4 +1,8 @@ -import { service, ServiceBuilder } from '../../../../infra/src/dsl/dsl' +import { + CodeOwners, + service, + ServiceBuilder, +} from '../../../../infra/src/dsl/dsl' const namespace = 'services-sessions' const imageName = 'services-sessions' @@ -7,6 +11,7 @@ export const serviceSetup = (): ServiceBuilder<'services-sessions'> => service('services-sessions') .namespace(namespace) .image(imageName) + .codeOwner(CodeOwners.Aranja) .redis() .db({ readOnly: true, @@ -54,6 +59,7 @@ export const workerSetup = (): ServiceBuilder<'services-sessions-worker'> => service('services-sessions-worker') .image(imageName) .namespace(namespace) + .codeOwner(CodeOwners.Aranja) .redis() .serviceAccount('sessions-worker') .command('node') @@ -92,6 +98,7 @@ export const cleanupSetup = (): ServiceBuilder => service(cleanupId) .namespace(namespace) .image(imageName) + .codeOwner(CodeOwners.Aranja) .command('node') .args('main.js', '--job=cleanup') .resources({ diff --git a/apps/services/university-gateway/infra/university-gateway.ts b/apps/services/university-gateway/infra/university-gateway.ts index f56f1cf8f8ae..15357d06e5c3 100644 --- a/apps/services/university-gateway/infra/university-gateway.ts +++ b/apps/services/university-gateway/infra/university-gateway.ts @@ -1,4 +1,8 @@ -import { service, ServiceBuilder } from '../../../../infra/src/dsl/dsl' +import { + CodeOwners, + service, + ServiceBuilder, +} from '../../../../infra/src/dsl/dsl' import { Base, Client, @@ -21,6 +25,7 @@ export const serviceSetup = (): ServiceBuilder => { .serviceAccount(serviceName) .namespace(namespace) .image(imageName) + .codeOwner(CodeOwners.Origo) .command('node') .redis() .args('main.js') @@ -79,6 +84,7 @@ export const workerSetup = (): ServiceBuilder => { .serviceAccount(serviceWorkerName) .namespace(namespace) .image(imageName) + .codeOwner(CodeOwners.Origo) .command('node') .redis() .args('main.js', '--job', 'worker') diff --git a/apps/services/user-notification/infra/user-notification.ts b/apps/services/user-notification/infra/user-notification.ts index d71a994b67c6..96c8788bf22a 100644 --- a/apps/services/user-notification/infra/user-notification.ts +++ b/apps/services/user-notification/infra/user-notification.ts @@ -5,6 +5,7 @@ import { RskCompanyInfo, } from '../../../../infra/src/dsl/xroad' import { + CodeOwners, json, ref, service, @@ -56,6 +57,7 @@ export const userNotificationServiceSetup = (services: { .image(imageName) .namespace(serviceName) .serviceAccount(serviceName) + .codeOwner(CodeOwners.Juni) .db() .command('node') .args('--no-experimental-fetch', 'main.js') @@ -121,6 +123,7 @@ export const userNotificationWorkerSetup = (services: { .image(imageName) .namespace(serviceName) .serviceAccount(serviceWorkerName) + .codeOwner(CodeOwners.Juni) .command('node') .args('--no-experimental-fetch', 'main.js', '--job=worker') .db() @@ -169,6 +172,7 @@ export const userNotificationCleanUpWorkerSetup = (): ServiceBuilder< .image(imageName) .namespace(serviceName) .serviceAccount(serviceCleanupWorkerName) + .codeOwner(CodeOwners.Juni) .command('node') .args('--no-experimental-fetch', 'main.js', '--job=cleanup') .db({ name: 'user-notification' }) diff --git a/apps/services/user-profile/infra/service-portal-api.ts b/apps/services/user-profile/infra/service-portal-api.ts index e8d9d75f88cb..3e070897e824 100644 --- a/apps/services/user-profile/infra/service-portal-api.ts +++ b/apps/services/user-profile/infra/service-portal-api.ts @@ -1,4 +1,9 @@ -import { json, service, ServiceBuilder } from '../../../../infra/src/dsl/dsl' +import { + CodeOwners, + json, + service, + ServiceBuilder, +} from '../../../../infra/src/dsl/dsl' import { EnvironmentVariables, Secrets, @@ -63,6 +68,7 @@ export const serviceSetup = (): ServiceBuilder => .namespace(namespace) .image(imageId) .serviceAccount(serviceId) + .codeOwner(CodeOwners.Aranja) .env(envVariables) .secrets(secrets) .xroad(Base, Client, NationalRegistryB2C) diff --git a/apps/web/infra/web.ts b/apps/web/infra/web.ts index a85284278d41..5e1bf92540dd 100644 --- a/apps/web/infra/web.ts +++ b/apps/web/infra/web.ts @@ -1,4 +1,9 @@ -import { ref, service, ServiceBuilder } from '../../../infra/src/dsl/dsl' +import { + CodeOwners, + ref, + service, + ServiceBuilder, +} from '../../../infra/src/dsl/dsl' export const serviceSetup = (services: { api: ServiceBuilder<'api'> @@ -6,6 +11,7 @@ export const serviceSetup = (services: { const web = service('web') web .namespace('islandis') + .codeOwner(CodeOwners.Stefna) .env({ API_URL: ref((h) => `http://${h.svc(services.api)}`), TRACKING_DOMAIN: { diff --git a/charts/identity-server/values.dev.yaml b/charts/identity-server/values.dev.yaml index 5ab8aa121f50..24f06cb5833f 100644 --- a/charts/identity-server/values.dev.yaml +++ b/charts/identity-server/values.dev.yaml @@ -9,6 +9,7 @@ auth-admin-web: enabled: true env: BASE_URL: 'https://identity-server.dev01.devland.is/admin' + CODE_OWNER: 'aranja' IDENTITYSERVER_DOMAIN: 'identity-server.dev01.devland.is' LOG_LEVEL: 'info' NEXTAUTH_URL: 'https://identity-server.dev01.devland.is/admin/api/auth' @@ -217,6 +218,7 @@ namespaces: services-auth-admin-api: enabled: true env: + CODE_OWNER: 'aranja' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.5fzau3.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-DEV/GOV/10006/Skatturinn/ft-v1' DB_HOST: 'postgres-applications.internal' @@ -314,6 +316,7 @@ services-auth-admin-api: services-auth-delegation-api: enabled: true env: + CODE_OWNER: 'aranja' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.5fzau3.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-DEV/GOV/10006/Skatturinn/ft-v1' DB_HOST: 'postgres-applications.internal' @@ -410,6 +413,7 @@ services-auth-delegation-api: services-auth-ids-api: enabled: true env: + CODE_OWNER: 'aranja' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.5fzau3.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-DEV/GOV/10006/Skatturinn/ft-v1' DB_EXTENSIONS: 'uuid-ossp' @@ -758,6 +762,7 @@ services-auth-personal-representative-public: services-auth-public-api: enabled: true env: + CODE_OWNER: 'aranja' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.5fzau3.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-DEV/GOV/10006/Skatturinn/ft-v1' DB_HOST: 'postgres-applications.internal' diff --git a/charts/identity-server/values.prod.yaml b/charts/identity-server/values.prod.yaml index 7521a7863cfd..e11425f90d02 100644 --- a/charts/identity-server/values.prod.yaml +++ b/charts/identity-server/values.prod.yaml @@ -9,6 +9,7 @@ auth-admin-web: enabled: true env: BASE_URL: 'https://innskra.island.is/admin' + CODE_OWNER: 'aranja' IDENTITYSERVER_DOMAIN: 'innskra.island.is' LOG_LEVEL: 'info' NEXTAUTH_URL: 'https://innskra.island.is/admin/api/auth' @@ -214,6 +215,7 @@ namespaces: services-auth-admin-api: enabled: true env: + CODE_OWNER: 'aranja' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.dnugi2.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS/GOV/5402696029/Skatturinn/ft-v1' DB_HOST: 'postgres-ids.internal' @@ -311,6 +313,7 @@ services-auth-admin-api: services-auth-delegation-api: enabled: true env: + CODE_OWNER: 'aranja' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.dnugi2.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS/GOV/5402696029/Skatturinn/ft-v1' DB_HOST: 'postgres-ids.internal' @@ -407,6 +410,7 @@ services-auth-delegation-api: services-auth-ids-api: enabled: true env: + CODE_OWNER: 'aranja' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.dnugi2.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS/GOV/5402696029/Skatturinn/ft-v1' DB_EXTENSIONS: 'uuid-ossp' @@ -755,6 +759,7 @@ services-auth-personal-representative-public: services-auth-public-api: enabled: true env: + CODE_OWNER: 'aranja' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.dnugi2.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS/GOV/5402696029/Skatturinn/ft-v1' DB_HOST: 'postgres-ids.internal' diff --git a/charts/identity-server/values.staging.yaml b/charts/identity-server/values.staging.yaml index 5c982fb923f9..1a6f05f0253d 100644 --- a/charts/identity-server/values.staging.yaml +++ b/charts/identity-server/values.staging.yaml @@ -9,6 +9,7 @@ auth-admin-web: enabled: true env: BASE_URL: 'https://identity-server.staging01.devland.is/admin' + CODE_OWNER: 'aranja' IDENTITYSERVER_DOMAIN: 'identity-server.staging01.devland.is' LOG_LEVEL: 'info' NEXTAUTH_URL: 'https://identity-server.staging01.devland.is/admin/api/auth' @@ -217,6 +218,7 @@ namespaces: services-auth-admin-api: enabled: true env: + CODE_OWNER: 'aranja' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.ab9ckb.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-TEST/GOV/5402696029/Skatturinn/ft-v1' DB_HOST: 'postgres-applications.internal' @@ -314,6 +316,7 @@ services-auth-admin-api: services-auth-delegation-api: enabled: true env: + CODE_OWNER: 'aranja' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.ab9ckb.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-TEST/GOV/5402696029/Skatturinn/ft-v1' DB_HOST: 'postgres-applications.internal' @@ -410,6 +413,7 @@ services-auth-delegation-api: services-auth-ids-api: enabled: true env: + CODE_OWNER: 'aranja' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.ab9ckb.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-TEST/GOV/5402696029/Skatturinn/ft-v1' DB_EXTENSIONS: 'uuid-ossp' @@ -758,6 +762,7 @@ services-auth-personal-representative-public: services-auth-public-api: enabled: true env: + CODE_OWNER: 'aranja' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.ab9ckb.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-TEST/GOV/5402696029/Skatturinn/ft-v1' DB_HOST: 'postgres-applications.internal' diff --git a/charts/islandis/values.dev.yaml b/charts/islandis/values.dev.yaml index 47abcaf93395..0a6d35e252cf 100644 --- a/charts/islandis/values.dev.yaml +++ b/charts/islandis/values.dev.yaml @@ -586,6 +586,7 @@ application-system-api: APPLICATION_ATTACHMENT_BUCKET: 'island-is-dev-storage-application-system' AUTH_PUBLIC_API_URL: 'https://identity-server.dev01.devland.is/api' CLIENT_LOCATION_ORIGIN: 'https://beta.dev01.devland.is/umsoknir' + CODE_OWNER: 'norda-applications' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.5fzau3.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-DEV/GOV/10006/Skatturinn/ft-v1' CONTENTFUL_HOST: 'preview.contentful.com' @@ -837,6 +838,7 @@ application-system-api-worker: env: APPLICATION_ATTACHMENT_BUCKET: 'island-is-dev-storage-application-system' CLIENT_LOCATION_ORIGIN: 'https://beta.dev01.devland.is/umsoknir' + CODE_OWNER: 'norda-applications' DB_HOST: 'postgres-applications.internal' DB_NAME: 'application_system_api' DB_REPLICAS_HOST: 'postgres-applications-reader.internal' @@ -999,6 +1001,7 @@ consultation-portal: API_URL: 'http://web-api.islandis.svc.cluster.local' BACKEND_DL_URL: 'https://samradapi-test.devland.is/api/Documents/' BASEPATH: '/consultation-portal' + CODE_OWNER: 'Advania' ENVIRONMENT: 'dev' IDENTITY_SERVER_ISSUER_DOMAIN: 'identity-server.dev01.devland.is' LOG_LEVEL: 'info' @@ -1297,6 +1300,7 @@ endorsement-system-api: - 'node' enabled: true env: + CODE_OWNER: 'juni' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_endorsements_api' DB_REPLICAS_HOST: 'postgres-applications-reader.internal' @@ -1687,6 +1691,7 @@ island-ui-storybook: license-api: enabled: true env: + CODE_OWNER: 'hugsmidjan' HUNTING_LICENSE_PASS_TEMPLATE_ID: '1da72d52-a93a-4d0f-8463-1933a2bd210b' IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.dev01.devland.is' LICENSE_SERVICE_REDIS_NODES: '["clustercfg.general-redis-cluster-group.5fzau3.euw1.cache.amazonaws.com:6379"]' @@ -1873,6 +1878,7 @@ portals-admin: regulations-admin-backend: enabled: true env: + CODE_OWNER: 'hugsmidjan' DB_HOST: 'postgres-applications.internal' DB_NAME: 'regulations_admin_backend' DB_REPLICAS_HOST: 'postgres-applications-reader.internal' @@ -1969,6 +1975,7 @@ search-indexer-service: env: AIR_DISCOUNT_SCHEME_FRONTEND_HOSTNAME: 'loftbru.dev01.devland.is' APPLICATION_URL: 'http://search-indexer-service' + CODE_OWNER: 'stefna' CONTENTFUL_ENTRY_FETCH_CHUNK_SIZE: '40' CONTENTFUL_ENVIRONMENT: 'master' CONTENTFUL_HOST: 'preview.contentful.com' @@ -2170,6 +2177,7 @@ service-portal-api: env: AUTH_DELEGATION_API_URL: 'http://web-services-auth-delegation-api.identity-server-delegation.svc.cluster.local' AUTH_DELEGATION_MACHINE_CLIENT_SCOPE: '["@island.is/auth/delegations/index:system"]' + CODE_OWNER: 'aranja' DB_HOST: 'postgres-applications.internal' DB_NAME: 'service_portal_api' DB_REPLICAS_HOST: 'postgres-applications-reader.internal' @@ -2306,6 +2314,7 @@ services-bff-portals-admin: BFF_NAME: 'stjornbord' BFF_PAR_SUPPORT_ENABLED: 'true' BFF_PROXY_API_ENDPOINT: 'http://web-api.islandis.svc.cluster.local/api/graphql' + CODE_OWNER: 'core' IDENTITY_SERVER_CLIENT_ID: '@admin.island.is/bff-stjornbord' IDENTITY_SERVER_CLIENT_SCOPES: '["@admin.island.is/delegations","@admin.island.is/ads","@admin.island.is/regulations","@admin.island.is/regulations:manage","@admin.island.is/icelandic-names-registry","@admin.island.is/application-system:admin","@admin.island.is/application-system:institution","@admin.island.is/document-provider","@admin.island.is/auth","@admin.island.is/auth:admin","@admin.island.is/petitions","@admin.island.is/service-desk","@admin.island.is/ads:explicit","@admin.island.is/signature-collection:manage","@admin.island.is/signature-collection:process","@admin.island.is/form-system","@admin.island.is/form-system:admin","@admin.island.is/delegation-system","@admin.island.is/delegation-system:admin"]' IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.dev01.devland.is' @@ -2544,6 +2553,7 @@ services-documents: services-sessions: enabled: true env: + CODE_OWNER: 'aranja' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_sessions' DB_REPLICAS_HOST: 'postgres-applications-reader.internal' @@ -2616,6 +2626,7 @@ services-sessions-cleanup: - 'node' enabled: true env: + CODE_OWNER: 'aranja' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_sessions' DB_REPLICAS_HOST: 'postgres-applications-reader.internal' @@ -2677,6 +2688,7 @@ services-sessions-worker: - 'node' enabled: true env: + CODE_OWNER: 'aranja' DB_EXTENSIONS: 'uuid-ossp' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_sessions' @@ -2771,6 +2783,7 @@ services-university-gateway: - 'node' enabled: true env: + CODE_OWNER: 'origo' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_university_gateway' DB_REPLICAS_HOST: 'postgres-applications-reader.internal' @@ -2900,6 +2913,7 @@ services-university-gateway-worker: - 'node' enabled: true env: + CODE_OWNER: 'origo' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_university_gateway' DB_REPLICAS_HOST: 'postgres-applications-reader.internal' @@ -3149,6 +3163,7 @@ user-notification: env: AUTH_DELEGATION_API_URL: 'http://web-services-auth-delegation-api.identity-server-delegation.svc.cluster.local' AUTH_DELEGATION_MACHINE_CLIENT_SCOPE: '["@island.is/auth/delegations/index:system"]' + CODE_OWNER: 'juni' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.5fzau3.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-DEV/GOV/10006/Skatturinn/ft-v1' DB_HOST: 'postgres-applications.internal' @@ -3253,6 +3268,7 @@ user-notification-cleanup-worker: - 'node' enabled: true env: + CODE_OWNER: 'juni' DB_HOST: 'postgres-applications.internal' DB_NAME: 'user_notification' DB_REPLICAS_HOST: 'postgres-applications-reader.internal' @@ -3348,6 +3364,7 @@ user-notification-worker: env: AUTH_DELEGATION_API_URL: 'http://web-services-auth-delegation-api.identity-server-delegation.svc.cluster.local' AUTH_DELEGATION_MACHINE_CLIENT_SCOPE: '["@island.is/auth/delegations/index:system"]' + CODE_OWNER: 'juni' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.5fzau3.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-DEV/GOV/10006/Skatturinn/ft-v1' CONTENTFUL_HOST: 'preview.contentful.com' @@ -3461,6 +3478,7 @@ web: enabled: true env: API_URL: 'http://web-api' + CODE_OWNER: 'stefna' DISABLE_API_CATALOGUE: 'false' DISABLE_ORGANIZATION_CHATBOT: 'false' DISABLE_SYSLUMENN_PAGE: 'false' diff --git a/charts/islandis/values.prod.yaml b/charts/islandis/values.prod.yaml index cfe7870b0fc5..cbc83be17d50 100644 --- a/charts/islandis/values.prod.yaml +++ b/charts/islandis/values.prod.yaml @@ -574,6 +574,7 @@ application-system-api: APPLICATION_ATTACHMENT_BUCKET: 'island-is-prod-storage-application-system' AUTH_PUBLIC_API_URL: 'https://innskra.island.is/api' CLIENT_LOCATION_ORIGIN: 'https://island.is/umsoknir' + CODE_OWNER: 'norda-applications' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.whakos.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS/GOV/5402696029/Skatturinn/ft-v1' CONTENTFUL_HOST: 'cdn.contentful.com' @@ -825,6 +826,7 @@ application-system-api-worker: env: APPLICATION_ATTACHMENT_BUCKET: 'island-is-prod-storage-application-system' CLIENT_LOCATION_ORIGIN: 'https://island.is/umsoknir' + CODE_OWNER: 'norda-applications' DB_HOST: 'postgres-applications.internal' DB_NAME: 'application_system_api' DB_REPLICAS_HOST: 'postgres-applications.internal' @@ -990,6 +992,7 @@ consultation-portal: API_URL: 'http://web-api.islandis.svc.cluster.local' BACKEND_DL_URL: 'https://samradapi.island.is/api/Documents/' BASEPATH: '/consultation-portal' + CODE_OWNER: 'Advania' ENVIRONMENT: 'prod' IDENTITY_SERVER_ISSUER_DOMAIN: 'innskra.island.is' LOG_LEVEL: 'info' @@ -1290,6 +1293,7 @@ endorsement-system-api: - 'node' enabled: true env: + CODE_OWNER: 'juni' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_endorsements_api' DB_REPLICAS_HOST: 'postgres-applications.internal' @@ -1553,6 +1557,7 @@ island-ui-storybook: license-api: enabled: true env: + CODE_OWNER: 'hugsmidjan' HUNTING_LICENSE_PASS_TEMPLATE_ID: 'd4ecf781-3764-4063-a4e1-9c3e17cebfba' IDENTITY_SERVER_ISSUER_URL: 'https://innskra.island.is' LICENSE_SERVICE_REDIS_NODES: '["clustercfg.general-redis-cluster-group.whakos.euw1.cache.amazonaws.com:6379"]' @@ -1740,6 +1745,7 @@ portals-admin: regulations-admin-backend: enabled: true env: + CODE_OWNER: 'hugsmidjan' DB_HOST: 'postgres-applications.internal' DB_NAME: 'regulations_admin_backend' DB_REPLICAS_HOST: 'postgres-applications.internal' @@ -1836,6 +1842,7 @@ search-indexer-service: env: AIR_DISCOUNT_SCHEME_FRONTEND_HOSTNAME: 'loftbru.island.is' APPLICATION_URL: 'http://search-indexer-service' + CODE_OWNER: 'stefna' CONTENTFUL_ENTRY_FETCH_CHUNK_SIZE: '40' CONTENTFUL_ENVIRONMENT: 'master' CONTENTFUL_HOST: 'cdn.contentful.com' @@ -2039,6 +2046,7 @@ service-portal-api: env: AUTH_DELEGATION_API_URL: 'https://auth-delegation-api.internal.innskra.island.is' AUTH_DELEGATION_MACHINE_CLIENT_SCOPE: '["@island.is/auth/delegations/index:system"]' + CODE_OWNER: 'aranja' DB_HOST: 'postgres-applications.internal' DB_NAME: 'service_portal_api' DB_REPLICAS_HOST: 'postgres-applications.internal' @@ -2175,6 +2183,7 @@ services-bff-portals-admin: BFF_NAME: 'stjornbord' BFF_PAR_SUPPORT_ENABLED: 'true' BFF_PROXY_API_ENDPOINT: 'http://web-api.islandis.svc.cluster.local/api/graphql' + CODE_OWNER: 'core' IDENTITY_SERVER_CLIENT_ID: '@admin.island.is/bff-stjornbord' IDENTITY_SERVER_CLIENT_SCOPES: '["@admin.island.is/delegations","@admin.island.is/ads","@admin.island.is/regulations","@admin.island.is/regulations:manage","@admin.island.is/icelandic-names-registry","@admin.island.is/application-system:admin","@admin.island.is/application-system:institution","@admin.island.is/document-provider","@admin.island.is/auth","@admin.island.is/auth:admin","@admin.island.is/petitions","@admin.island.is/service-desk","@admin.island.is/ads:explicit","@admin.island.is/signature-collection:manage","@admin.island.is/signature-collection:process","@admin.island.is/form-system","@admin.island.is/form-system:admin","@admin.island.is/delegation-system","@admin.island.is/delegation-system:admin"]' IDENTITY_SERVER_ISSUER_URL: 'https://innskra.island.is' @@ -2417,6 +2426,7 @@ services-documents: services-sessions: enabled: true env: + CODE_OWNER: 'aranja' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_sessions' DB_REPLICAS_HOST: 'postgres-applications.internal' @@ -2489,6 +2499,7 @@ services-sessions-cleanup: - 'node' enabled: true env: + CODE_OWNER: 'aranja' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_sessions' DB_REPLICAS_HOST: 'postgres-applications.internal' @@ -2550,6 +2561,7 @@ services-sessions-worker: - 'node' enabled: true env: + CODE_OWNER: 'aranja' DB_EXTENSIONS: 'uuid-ossp' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_sessions' @@ -2644,6 +2656,7 @@ services-university-gateway: - 'node' enabled: true env: + CODE_OWNER: 'origo' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_university_gateway' DB_REPLICAS_HOST: 'postgres-applications.internal' @@ -2773,6 +2786,7 @@ services-university-gateway-worker: - 'node' enabled: true env: + CODE_OWNER: 'origo' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_university_gateway' DB_REPLICAS_HOST: 'postgres-applications.internal' @@ -3028,6 +3042,7 @@ user-notification: env: AUTH_DELEGATION_API_URL: 'https://auth-delegation-api.internal.innskra.island.is' AUTH_DELEGATION_MACHINE_CLIENT_SCOPE: '["@island.is/auth/delegations/index:system"]' + CODE_OWNER: 'juni' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.whakos.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS/GOV/5402696029/Skatturinn/ft-v1' DB_HOST: 'postgres-applications.internal' @@ -3132,6 +3147,7 @@ user-notification-cleanup-worker: - 'node' enabled: true env: + CODE_OWNER: 'juni' DB_HOST: 'postgres-applications.internal' DB_NAME: 'user_notification' DB_REPLICAS_HOST: 'postgres-applications.internal' @@ -3227,6 +3243,7 @@ user-notification-worker: env: AUTH_DELEGATION_API_URL: 'https://auth-delegation-api.internal.innskra.island.is' AUTH_DELEGATION_MACHINE_CLIENT_SCOPE: '["@island.is/auth/delegations/index:system"]' + CODE_OWNER: 'juni' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.whakos.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS/GOV/5402696029/Skatturinn/ft-v1' CONTENTFUL_HOST: 'cdn.contentful.com' @@ -3340,6 +3357,7 @@ web: enabled: true env: API_URL: 'http://web-api' + CODE_OWNER: 'stefna' DISABLE_API_CATALOGUE: 'false' DISABLE_ORGANIZATION_CHATBOT: 'false' DISABLE_SYSLUMENN_PAGE: 'false' diff --git a/charts/islandis/values.staging.yaml b/charts/islandis/values.staging.yaml index bb265c51b251..36116c21bc12 100644 --- a/charts/islandis/values.staging.yaml +++ b/charts/islandis/values.staging.yaml @@ -583,6 +583,7 @@ application-system-api: APPLICATION_ATTACHMENT_BUCKET: 'island-is-staging-storage-application-system' AUTH_PUBLIC_API_URL: 'https://identity-server.staging01.devland.is/api' CLIENT_LOCATION_ORIGIN: 'https://beta.staging01.devland.is/umsoknir' + CODE_OWNER: 'norda-applications' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.ab9ckb.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-TEST/GOV/5402696029/Skatturinn/ft-v1' CONTENTFUL_HOST: 'cdn.contentful.com' @@ -834,6 +835,7 @@ application-system-api-worker: env: APPLICATION_ATTACHMENT_BUCKET: 'island-is-staging-storage-application-system' CLIENT_LOCATION_ORIGIN: 'https://beta.staging01.devland.is/umsoknir' + CODE_OWNER: 'norda-applications' DB_HOST: 'postgres-applications.internal' DB_NAME: 'application_system_api' DB_REPLICAS_HOST: 'postgres-applications.internal' @@ -996,6 +998,7 @@ consultation-portal: API_URL: 'http://web-api.islandis.svc.cluster.local' BACKEND_DL_URL: 'https://samradapi-test.devland.is/api/Documents/' BASEPATH: '/consultation-portal' + CODE_OWNER: 'Advania' ENVIRONMENT: 'staging' IDENTITY_SERVER_ISSUER_DOMAIN: 'identity-server.staging01.devland.is' LOG_LEVEL: 'info' @@ -1166,6 +1169,7 @@ endorsement-system-api: - 'node' enabled: true env: + CODE_OWNER: 'juni' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_endorsements_api' DB_REPLICAS_HOST: 'postgres-applications.internal' @@ -1429,6 +1433,7 @@ island-ui-storybook: license-api: enabled: true env: + CODE_OWNER: 'hugsmidjan' HUNTING_LICENSE_PASS_TEMPLATE_ID: '1da72d52-a93a-4d0f-8463-1933a2bd210b' IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.staging01.devland.is' LICENSE_SERVICE_REDIS_NODES: '["clustercfg.general-redis-cluster-group.ab9ckb.euw1.cache.amazonaws.com:6379"]' @@ -1611,6 +1616,7 @@ portals-admin: regulations-admin-backend: enabled: true env: + CODE_OWNER: 'hugsmidjan' DB_HOST: 'postgres-applications.internal' DB_NAME: 'regulations_admin_backend' DB_REPLICAS_HOST: 'postgres-applications.internal' @@ -1707,6 +1713,7 @@ search-indexer-service: env: AIR_DISCOUNT_SCHEME_FRONTEND_HOSTNAME: 'loftbru.staging01.devland.is' APPLICATION_URL: 'http://search-indexer-service' + CODE_OWNER: 'stefna' CONTENTFUL_ENTRY_FETCH_CHUNK_SIZE: '40' CONTENTFUL_ENVIRONMENT: 'master' CONTENTFUL_HOST: 'cdn.contentful.com' @@ -1908,6 +1915,7 @@ service-portal-api: env: AUTH_DELEGATION_API_URL: 'http://web-services-auth-delegation-api.identity-server-delegation.svc.cluster.local' AUTH_DELEGATION_MACHINE_CLIENT_SCOPE: '["@island.is/auth/delegations/index:system"]' + CODE_OWNER: 'aranja' DB_HOST: 'postgres-applications.internal' DB_NAME: 'service_portal_api' DB_REPLICAS_HOST: 'postgres-applications.internal' @@ -2044,6 +2052,7 @@ services-bff-portals-admin: BFF_NAME: 'stjornbord' BFF_PAR_SUPPORT_ENABLED: 'true' BFF_PROXY_API_ENDPOINT: 'http://web-api.islandis.svc.cluster.local/api/graphql' + CODE_OWNER: 'core' IDENTITY_SERVER_CLIENT_ID: '@admin.island.is/bff-stjornbord' IDENTITY_SERVER_CLIENT_SCOPES: '["@admin.island.is/delegations","@admin.island.is/ads","@admin.island.is/regulations","@admin.island.is/regulations:manage","@admin.island.is/icelandic-names-registry","@admin.island.is/application-system:admin","@admin.island.is/application-system:institution","@admin.island.is/document-provider","@admin.island.is/auth","@admin.island.is/auth:admin","@admin.island.is/petitions","@admin.island.is/service-desk","@admin.island.is/ads:explicit","@admin.island.is/signature-collection:manage","@admin.island.is/signature-collection:process","@admin.island.is/form-system","@admin.island.is/form-system:admin","@admin.island.is/delegation-system","@admin.island.is/delegation-system:admin"]' IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.staging01.devland.is' @@ -2282,6 +2291,7 @@ services-documents: services-sessions: enabled: true env: + CODE_OWNER: 'aranja' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_sessions' DB_REPLICAS_HOST: 'postgres-applications.internal' @@ -2354,6 +2364,7 @@ services-sessions-cleanup: - 'node' enabled: true env: + CODE_OWNER: 'aranja' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_sessions' DB_REPLICAS_HOST: 'postgres-applications.internal' @@ -2415,6 +2426,7 @@ services-sessions-worker: - 'node' enabled: true env: + CODE_OWNER: 'aranja' DB_EXTENSIONS: 'uuid-ossp' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_sessions' @@ -2509,6 +2521,7 @@ services-university-gateway: - 'node' enabled: true env: + CODE_OWNER: 'origo' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_university_gateway' DB_REPLICAS_HOST: 'postgres-applications.internal' @@ -2638,6 +2651,7 @@ services-university-gateway-worker: - 'node' enabled: true env: + CODE_OWNER: 'origo' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_university_gateway' DB_REPLICAS_HOST: 'postgres-applications.internal' @@ -2887,6 +2901,7 @@ user-notification: env: AUTH_DELEGATION_API_URL: 'http://web-services-auth-delegation-api.identity-server-delegation.svc.cluster.local' AUTH_DELEGATION_MACHINE_CLIENT_SCOPE: '["@island.is/auth/delegations/index:system"]' + CODE_OWNER: 'juni' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.ab9ckb.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-TEST/GOV/5402696029/Skatturinn/ft-v1' DB_HOST: 'postgres-applications.internal' @@ -2991,6 +3006,7 @@ user-notification-cleanup-worker: - 'node' enabled: true env: + CODE_OWNER: 'juni' DB_HOST: 'postgres-applications.internal' DB_NAME: 'user_notification' DB_REPLICAS_HOST: 'postgres-applications.internal' @@ -3086,6 +3102,7 @@ user-notification-worker: env: AUTH_DELEGATION_API_URL: 'http://web-services-auth-delegation-api.identity-server-delegation.svc.cluster.local' AUTH_DELEGATION_MACHINE_CLIENT_SCOPE: '["@island.is/auth/delegations/index:system"]' + CODE_OWNER: 'juni' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.ab9ckb.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-TEST/GOV/5402696029/Skatturinn/ft-v1' CONTENTFUL_HOST: 'cdn.contentful.com' @@ -3200,6 +3217,7 @@ web: enabled: true env: API_URL: 'http://web-api' + CODE_OWNER: 'stefna' DISABLE_API_CATALOGUE: 'false' DISABLE_ORGANIZATION_CHATBOT: 'false' DISABLE_SYSLUMENN_PAGE: 'false' diff --git a/charts/services/application-system-api-worker/values.dev.yaml b/charts/services/application-system-api-worker/values.dev.yaml index ff4bd2d0ebb0..5f8102c047e2 100644 --- a/charts/services/application-system-api-worker/values.dev.yaml +++ b/charts/services/application-system-api-worker/values.dev.yaml @@ -27,6 +27,7 @@ enabled: true env: APPLICATION_ATTACHMENT_BUCKET: 'island-is-dev-storage-application-system' CLIENT_LOCATION_ORIGIN: 'https://beta.dev01.devland.is/umsoknir' + CODE_OWNER: 'norda-applications' DB_HOST: 'postgres-applications.internal' DB_NAME: 'application_system_api' DB_REPLICAS_HOST: 'postgres-applications-reader.internal' diff --git a/charts/services/application-system-api-worker/values.prod.yaml b/charts/services/application-system-api-worker/values.prod.yaml index c03336214cd1..e8c6b233d0c0 100644 --- a/charts/services/application-system-api-worker/values.prod.yaml +++ b/charts/services/application-system-api-worker/values.prod.yaml @@ -27,6 +27,7 @@ enabled: true env: APPLICATION_ATTACHMENT_BUCKET: 'island-is-prod-storage-application-system' CLIENT_LOCATION_ORIGIN: 'https://island.is/umsoknir' + CODE_OWNER: 'norda-applications' DB_HOST: 'postgres-applications.internal' DB_NAME: 'application_system_api' DB_REPLICAS_HOST: 'postgres-applications.internal' diff --git a/charts/services/application-system-api-worker/values.staging.yaml b/charts/services/application-system-api-worker/values.staging.yaml index c45d550e4db3..1b3b2c57186f 100644 --- a/charts/services/application-system-api-worker/values.staging.yaml +++ b/charts/services/application-system-api-worker/values.staging.yaml @@ -27,6 +27,7 @@ enabled: true env: APPLICATION_ATTACHMENT_BUCKET: 'island-is-staging-storage-application-system' CLIENT_LOCATION_ORIGIN: 'https://beta.staging01.devland.is/umsoknir' + CODE_OWNER: 'norda-applications' DB_HOST: 'postgres-applications.internal' DB_NAME: 'application_system_api' DB_REPLICAS_HOST: 'postgres-applications.internal' diff --git a/charts/services/application-system-api/values.dev.yaml b/charts/services/application-system-api/values.dev.yaml index 9cabf0c60fd2..4ebec0038f8a 100644 --- a/charts/services/application-system-api/values.dev.yaml +++ b/charts/services/application-system-api/values.dev.yaml @@ -26,6 +26,7 @@ env: APPLICATION_ATTACHMENT_BUCKET: 'island-is-dev-storage-application-system' AUTH_PUBLIC_API_URL: 'https://identity-server.dev01.devland.is/api' CLIENT_LOCATION_ORIGIN: 'https://beta.dev01.devland.is/umsoknir' + CODE_OWNER: 'norda-applications' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.5fzau3.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-DEV/GOV/10006/Skatturinn/ft-v1' CONTENTFUL_HOST: 'preview.contentful.com' diff --git a/charts/services/application-system-api/values.prod.yaml b/charts/services/application-system-api/values.prod.yaml index bb8e6388c85d..e31f1971a9bc 100644 --- a/charts/services/application-system-api/values.prod.yaml +++ b/charts/services/application-system-api/values.prod.yaml @@ -26,6 +26,7 @@ env: APPLICATION_ATTACHMENT_BUCKET: 'island-is-prod-storage-application-system' AUTH_PUBLIC_API_URL: 'https://innskra.island.is/api' CLIENT_LOCATION_ORIGIN: 'https://island.is/umsoknir' + CODE_OWNER: 'norda-applications' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.whakos.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS/GOV/5402696029/Skatturinn/ft-v1' CONTENTFUL_HOST: 'cdn.contentful.com' diff --git a/charts/services/application-system-api/values.staging.yaml b/charts/services/application-system-api/values.staging.yaml index 4be6d8efdc8e..c47d9cd07c30 100644 --- a/charts/services/application-system-api/values.staging.yaml +++ b/charts/services/application-system-api/values.staging.yaml @@ -26,6 +26,7 @@ env: APPLICATION_ATTACHMENT_BUCKET: 'island-is-staging-storage-application-system' AUTH_PUBLIC_API_URL: 'https://identity-server.staging01.devland.is/api' CLIENT_LOCATION_ORIGIN: 'https://beta.staging01.devland.is/umsoknir' + CODE_OWNER: 'norda-applications' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.ab9ckb.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-TEST/GOV/5402696029/Skatturinn/ft-v1' CONTENTFUL_HOST: 'cdn.contentful.com' diff --git a/charts/services/auth-admin-web/values.dev.yaml b/charts/services/auth-admin-web/values.dev.yaml index 5c3eb4b36803..990c8a57b7ae 100644 --- a/charts/services/auth-admin-web/values.dev.yaml +++ b/charts/services/auth-admin-web/values.dev.yaml @@ -20,6 +20,7 @@ name: 'auth-admin-web' enabled: true env: BASE_URL: 'https://identity-server.dev01.devland.is/admin' + CODE_OWNER: 'aranja' IDENTITYSERVER_DOMAIN: 'identity-server.dev01.devland.is' LOG_LEVEL: 'info' NEXTAUTH_URL: 'https://identity-server.dev01.devland.is/admin/api/auth' diff --git a/charts/services/auth-admin-web/values.prod.yaml b/charts/services/auth-admin-web/values.prod.yaml index 556bdfda6dc3..7adf118589d6 100644 --- a/charts/services/auth-admin-web/values.prod.yaml +++ b/charts/services/auth-admin-web/values.prod.yaml @@ -20,6 +20,7 @@ name: 'auth-admin-web' enabled: true env: BASE_URL: 'https://innskra.island.is/admin' + CODE_OWNER: 'aranja' IDENTITYSERVER_DOMAIN: 'innskra.island.is' LOG_LEVEL: 'info' NEXTAUTH_URL: 'https://innskra.island.is/admin/api/auth' diff --git a/charts/services/auth-admin-web/values.staging.yaml b/charts/services/auth-admin-web/values.staging.yaml index 203226a74c77..846c0af6c998 100644 --- a/charts/services/auth-admin-web/values.staging.yaml +++ b/charts/services/auth-admin-web/values.staging.yaml @@ -20,6 +20,7 @@ name: 'auth-admin-web' enabled: true env: BASE_URL: 'https://identity-server.staging01.devland.is/admin' + CODE_OWNER: 'aranja' IDENTITYSERVER_DOMAIN: 'identity-server.staging01.devland.is' LOG_LEVEL: 'info' NEXTAUTH_URL: 'https://identity-server.staging01.devland.is/admin/api/auth' diff --git a/charts/services/consultation-portal/values.dev.yaml b/charts/services/consultation-portal/values.dev.yaml index 4a88058fbef2..3e06661dab79 100644 --- a/charts/services/consultation-portal/values.dev.yaml +++ b/charts/services/consultation-portal/values.dev.yaml @@ -22,6 +22,7 @@ env: API_URL: 'http://web-api.islandis.svc.cluster.local' BACKEND_DL_URL: 'https://samradapi-test.devland.is/api/Documents/' BASEPATH: '/consultation-portal' + CODE_OWNER: 'Advania' ENVIRONMENT: 'dev' IDENTITY_SERVER_ISSUER_DOMAIN: 'identity-server.dev01.devland.is' LOG_LEVEL: 'info' diff --git a/charts/services/consultation-portal/values.prod.yaml b/charts/services/consultation-portal/values.prod.yaml index 1079e249d7ea..018ba338011c 100644 --- a/charts/services/consultation-portal/values.prod.yaml +++ b/charts/services/consultation-portal/values.prod.yaml @@ -22,6 +22,7 @@ env: API_URL: 'http://web-api.islandis.svc.cluster.local' BACKEND_DL_URL: 'https://samradapi.island.is/api/Documents/' BASEPATH: '/consultation-portal' + CODE_OWNER: 'Advania' ENVIRONMENT: 'prod' IDENTITY_SERVER_ISSUER_DOMAIN: 'innskra.island.is' LOG_LEVEL: 'info' diff --git a/charts/services/consultation-portal/values.staging.yaml b/charts/services/consultation-portal/values.staging.yaml index ab669a620d94..e3547fb54d2e 100644 --- a/charts/services/consultation-portal/values.staging.yaml +++ b/charts/services/consultation-portal/values.staging.yaml @@ -22,6 +22,7 @@ env: API_URL: 'http://web-api.islandis.svc.cluster.local' BACKEND_DL_URL: 'https://samradapi-test.devland.is/api/Documents/' BASEPATH: '/consultation-portal' + CODE_OWNER: 'Advania' ENVIRONMENT: 'staging' IDENTITY_SERVER_ISSUER_DOMAIN: 'identity-server.staging01.devland.is' LOG_LEVEL: 'info' diff --git a/charts/services/endorsement-system-api/values.dev.yaml b/charts/services/endorsement-system-api/values.dev.yaml index df8da5be9bc2..b9bff23c05fe 100644 --- a/charts/services/endorsement-system-api/values.dev.yaml +++ b/charts/services/endorsement-system-api/values.dev.yaml @@ -25,6 +25,7 @@ command: - 'node' enabled: true env: + CODE_OWNER: 'juni' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_endorsements_api' DB_REPLICAS_HOST: 'postgres-applications-reader.internal' diff --git a/charts/services/endorsement-system-api/values.prod.yaml b/charts/services/endorsement-system-api/values.prod.yaml index da5c42fcc716..3bc849455a0c 100644 --- a/charts/services/endorsement-system-api/values.prod.yaml +++ b/charts/services/endorsement-system-api/values.prod.yaml @@ -25,6 +25,7 @@ command: - 'node' enabled: true env: + CODE_OWNER: 'juni' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_endorsements_api' DB_REPLICAS_HOST: 'postgres-applications.internal' diff --git a/charts/services/endorsement-system-api/values.staging.yaml b/charts/services/endorsement-system-api/values.staging.yaml index f4b5b1cc659f..4cc0f489f38b 100644 --- a/charts/services/endorsement-system-api/values.staging.yaml +++ b/charts/services/endorsement-system-api/values.staging.yaml @@ -25,6 +25,7 @@ command: - 'node' enabled: true env: + CODE_OWNER: 'juni' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_endorsements_api' DB_REPLICAS_HOST: 'postgres-applications.internal' diff --git a/charts/services/license-api/values.dev.yaml b/charts/services/license-api/values.dev.yaml index 525b0341c608..a4d52b20c15b 100644 --- a/charts/services/license-api/values.dev.yaml +++ b/charts/services/license-api/values.dev.yaml @@ -19,6 +19,7 @@ global: name: 'license-api' enabled: true env: + CODE_OWNER: 'hugsmidjan' HUNTING_LICENSE_PASS_TEMPLATE_ID: '1da72d52-a93a-4d0f-8463-1933a2bd210b' IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.dev01.devland.is' LICENSE_SERVICE_REDIS_NODES: '["clustercfg.general-redis-cluster-group.5fzau3.euw1.cache.amazonaws.com:6379"]' diff --git a/charts/services/license-api/values.prod.yaml b/charts/services/license-api/values.prod.yaml index 2f003504d184..aee4de56111c 100644 --- a/charts/services/license-api/values.prod.yaml +++ b/charts/services/license-api/values.prod.yaml @@ -19,6 +19,7 @@ global: name: 'license-api' enabled: true env: + CODE_OWNER: 'hugsmidjan' HUNTING_LICENSE_PASS_TEMPLATE_ID: 'd4ecf781-3764-4063-a4e1-9c3e17cebfba' IDENTITY_SERVER_ISSUER_URL: 'https://innskra.island.is' LICENSE_SERVICE_REDIS_NODES: '["clustercfg.general-redis-cluster-group.whakos.euw1.cache.amazonaws.com:6379"]' diff --git a/charts/services/license-api/values.staging.yaml b/charts/services/license-api/values.staging.yaml index d85276d6d82b..6e7db7b9814d 100644 --- a/charts/services/license-api/values.staging.yaml +++ b/charts/services/license-api/values.staging.yaml @@ -19,6 +19,7 @@ global: name: 'license-api' enabled: true env: + CODE_OWNER: 'hugsmidjan' HUNTING_LICENSE_PASS_TEMPLATE_ID: '1da72d52-a93a-4d0f-8463-1933a2bd210b' IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.staging01.devland.is' LICENSE_SERVICE_REDIS_NODES: '["clustercfg.general-redis-cluster-group.ab9ckb.euw1.cache.amazonaws.com:6379"]' diff --git a/charts/services/regulations-admin-backend/values.dev.yaml b/charts/services/regulations-admin-backend/values.dev.yaml index d0fb60d1bca9..922313bd4eb7 100644 --- a/charts/services/regulations-admin-backend/values.dev.yaml +++ b/charts/services/regulations-admin-backend/values.dev.yaml @@ -19,6 +19,7 @@ global: name: 'regulations-admin-backend' enabled: true env: + CODE_OWNER: 'hugsmidjan' DB_HOST: 'postgres-applications.internal' DB_NAME: 'regulations_admin_backend' DB_REPLICAS_HOST: 'postgres-applications-reader.internal' diff --git a/charts/services/regulations-admin-backend/values.prod.yaml b/charts/services/regulations-admin-backend/values.prod.yaml index 1a1eb25213a8..27e0e5a3d396 100644 --- a/charts/services/regulations-admin-backend/values.prod.yaml +++ b/charts/services/regulations-admin-backend/values.prod.yaml @@ -19,6 +19,7 @@ global: name: 'regulations-admin-backend' enabled: true env: + CODE_OWNER: 'hugsmidjan' DB_HOST: 'postgres-applications.internal' DB_NAME: 'regulations_admin_backend' DB_REPLICAS_HOST: 'postgres-applications.internal' diff --git a/charts/services/regulations-admin-backend/values.staging.yaml b/charts/services/regulations-admin-backend/values.staging.yaml index af8acd3ba0f4..8ad0927caa8f 100644 --- a/charts/services/regulations-admin-backend/values.staging.yaml +++ b/charts/services/regulations-admin-backend/values.staging.yaml @@ -19,6 +19,7 @@ global: name: 'regulations-admin-backend' enabled: true env: + CODE_OWNER: 'hugsmidjan' DB_HOST: 'postgres-applications.internal' DB_NAME: 'regulations_admin_backend' DB_REPLICAS_HOST: 'postgres-applications.internal' diff --git a/charts/services/search-indexer-service/values.dev.yaml b/charts/services/search-indexer-service/values.dev.yaml index 6ecb31864bdf..bf1e8184f67c 100644 --- a/charts/services/search-indexer-service/values.dev.yaml +++ b/charts/services/search-indexer-service/values.dev.yaml @@ -21,6 +21,7 @@ enabled: true env: AIR_DISCOUNT_SCHEME_FRONTEND_HOSTNAME: 'loftbru.dev01.devland.is' APPLICATION_URL: 'http://search-indexer-service' + CODE_OWNER: 'stefna' CONTENTFUL_ENTRY_FETCH_CHUNK_SIZE: '40' CONTENTFUL_ENVIRONMENT: 'master' CONTENTFUL_HOST: 'preview.contentful.com' diff --git a/charts/services/search-indexer-service/values.prod.yaml b/charts/services/search-indexer-service/values.prod.yaml index 1b1d10f0dfb4..a203c282692e 100644 --- a/charts/services/search-indexer-service/values.prod.yaml +++ b/charts/services/search-indexer-service/values.prod.yaml @@ -21,6 +21,7 @@ enabled: true env: AIR_DISCOUNT_SCHEME_FRONTEND_HOSTNAME: 'loftbru.island.is' APPLICATION_URL: 'http://search-indexer-service' + CODE_OWNER: 'stefna' CONTENTFUL_ENTRY_FETCH_CHUNK_SIZE: '40' CONTENTFUL_ENVIRONMENT: 'master' CONTENTFUL_HOST: 'cdn.contentful.com' diff --git a/charts/services/search-indexer-service/values.staging.yaml b/charts/services/search-indexer-service/values.staging.yaml index e7f407cf667b..39cebc125692 100644 --- a/charts/services/search-indexer-service/values.staging.yaml +++ b/charts/services/search-indexer-service/values.staging.yaml @@ -21,6 +21,7 @@ enabled: true env: AIR_DISCOUNT_SCHEME_FRONTEND_HOSTNAME: 'loftbru.staging01.devland.is' APPLICATION_URL: 'http://search-indexer-service' + CODE_OWNER: 'stefna' CONTENTFUL_ENTRY_FETCH_CHUNK_SIZE: '40' CONTENTFUL_ENVIRONMENT: 'master' CONTENTFUL_HOST: 'cdn.contentful.com' diff --git a/charts/services/service-portal-api/values.dev.yaml b/charts/services/service-portal-api/values.dev.yaml index dd764effdcae..080007a74e71 100644 --- a/charts/services/service-portal-api/values.dev.yaml +++ b/charts/services/service-portal-api/values.dev.yaml @@ -21,6 +21,7 @@ enabled: true env: AUTH_DELEGATION_API_URL: 'http://web-services-auth-delegation-api.identity-server-delegation.svc.cluster.local' AUTH_DELEGATION_MACHINE_CLIENT_SCOPE: '["@island.is/auth/delegations/index:system"]' + CODE_OWNER: 'aranja' DB_HOST: 'postgres-applications.internal' DB_NAME: 'service_portal_api' DB_REPLICAS_HOST: 'postgres-applications-reader.internal' diff --git a/charts/services/service-portal-api/values.prod.yaml b/charts/services/service-portal-api/values.prod.yaml index 1ec1f781fb66..bf5acdd7d719 100644 --- a/charts/services/service-portal-api/values.prod.yaml +++ b/charts/services/service-portal-api/values.prod.yaml @@ -21,6 +21,7 @@ enabled: true env: AUTH_DELEGATION_API_URL: 'https://auth-delegation-api.internal.innskra.island.is' AUTH_DELEGATION_MACHINE_CLIENT_SCOPE: '["@island.is/auth/delegations/index:system"]' + CODE_OWNER: 'aranja' DB_HOST: 'postgres-applications.internal' DB_NAME: 'service_portal_api' DB_REPLICAS_HOST: 'postgres-applications.internal' diff --git a/charts/services/service-portal-api/values.staging.yaml b/charts/services/service-portal-api/values.staging.yaml index a6c91b5bc4df..f766121d5b5b 100644 --- a/charts/services/service-portal-api/values.staging.yaml +++ b/charts/services/service-portal-api/values.staging.yaml @@ -21,6 +21,7 @@ enabled: true env: AUTH_DELEGATION_API_URL: 'http://web-services-auth-delegation-api.identity-server-delegation.svc.cluster.local' AUTH_DELEGATION_MACHINE_CLIENT_SCOPE: '["@island.is/auth/delegations/index:system"]' + CODE_OWNER: 'aranja' DB_HOST: 'postgres-applications.internal' DB_NAME: 'service_portal_api' DB_REPLICAS_HOST: 'postgres-applications.internal' diff --git a/charts/services/services-auth-admin-api/values.dev.yaml b/charts/services/services-auth-admin-api/values.dev.yaml index a792c06696f7..9817987951f4 100644 --- a/charts/services/services-auth-admin-api/values.dev.yaml +++ b/charts/services/services-auth-admin-api/values.dev.yaml @@ -19,6 +19,7 @@ global: name: 'services-auth-admin-api' enabled: true env: + CODE_OWNER: 'aranja' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.5fzau3.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-DEV/GOV/10006/Skatturinn/ft-v1' DB_HOST: 'postgres-applications.internal' diff --git a/charts/services/services-auth-admin-api/values.prod.yaml b/charts/services/services-auth-admin-api/values.prod.yaml index cc6563a905ed..8a2c4b6576fb 100644 --- a/charts/services/services-auth-admin-api/values.prod.yaml +++ b/charts/services/services-auth-admin-api/values.prod.yaml @@ -19,6 +19,7 @@ global: name: 'services-auth-admin-api' enabled: true env: + CODE_OWNER: 'aranja' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.dnugi2.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS/GOV/5402696029/Skatturinn/ft-v1' DB_HOST: 'postgres-ids.internal' diff --git a/charts/services/services-auth-admin-api/values.staging.yaml b/charts/services/services-auth-admin-api/values.staging.yaml index e67c4abfceac..b1c22013d7d8 100644 --- a/charts/services/services-auth-admin-api/values.staging.yaml +++ b/charts/services/services-auth-admin-api/values.staging.yaml @@ -19,6 +19,7 @@ global: name: 'services-auth-admin-api' enabled: true env: + CODE_OWNER: 'aranja' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.ab9ckb.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-TEST/GOV/5402696029/Skatturinn/ft-v1' DB_HOST: 'postgres-applications.internal' diff --git a/charts/services/services-auth-delegation-api/values.dev.yaml b/charts/services/services-auth-delegation-api/values.dev.yaml index da1613b5c029..3fd4ca9ae4fb 100644 --- a/charts/services/services-auth-delegation-api/values.dev.yaml +++ b/charts/services/services-auth-delegation-api/values.dev.yaml @@ -19,6 +19,7 @@ global: name: 'services-auth-delegation-api' enabled: true env: + CODE_OWNER: 'aranja' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.5fzau3.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-DEV/GOV/10006/Skatturinn/ft-v1' DB_HOST: 'postgres-applications.internal' diff --git a/charts/services/services-auth-delegation-api/values.prod.yaml b/charts/services/services-auth-delegation-api/values.prod.yaml index 51a922f7506b..2e99e7c39b37 100644 --- a/charts/services/services-auth-delegation-api/values.prod.yaml +++ b/charts/services/services-auth-delegation-api/values.prod.yaml @@ -19,6 +19,7 @@ global: name: 'services-auth-delegation-api' enabled: true env: + CODE_OWNER: 'aranja' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.dnugi2.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS/GOV/5402696029/Skatturinn/ft-v1' DB_HOST: 'postgres-ids.internal' diff --git a/charts/services/services-auth-delegation-api/values.staging.yaml b/charts/services/services-auth-delegation-api/values.staging.yaml index 7b1fbb8f3802..6547bd4e464b 100644 --- a/charts/services/services-auth-delegation-api/values.staging.yaml +++ b/charts/services/services-auth-delegation-api/values.staging.yaml @@ -19,6 +19,7 @@ global: name: 'services-auth-delegation-api' enabled: true env: + CODE_OWNER: 'aranja' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.ab9ckb.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-TEST/GOV/5402696029/Skatturinn/ft-v1' DB_HOST: 'postgres-applications.internal' diff --git a/charts/services/services-auth-ids-api/values.dev.yaml b/charts/services/services-auth-ids-api/values.dev.yaml index 3f01536db5bf..7ad3e73b5919 100644 --- a/charts/services/services-auth-ids-api/values.dev.yaml +++ b/charts/services/services-auth-ids-api/values.dev.yaml @@ -19,6 +19,7 @@ global: name: 'services-auth-ids-api' enabled: true env: + CODE_OWNER: 'aranja' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.5fzau3.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-DEV/GOV/10006/Skatturinn/ft-v1' DB_EXTENSIONS: 'uuid-ossp' diff --git a/charts/services/services-auth-ids-api/values.prod.yaml b/charts/services/services-auth-ids-api/values.prod.yaml index 8452923e6595..80f73850121a 100644 --- a/charts/services/services-auth-ids-api/values.prod.yaml +++ b/charts/services/services-auth-ids-api/values.prod.yaml @@ -19,6 +19,7 @@ global: name: 'services-auth-ids-api' enabled: true env: + CODE_OWNER: 'aranja' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.dnugi2.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS/GOV/5402696029/Skatturinn/ft-v1' DB_EXTENSIONS: 'uuid-ossp' diff --git a/charts/services/services-auth-ids-api/values.staging.yaml b/charts/services/services-auth-ids-api/values.staging.yaml index 0ed369b6862e..be6dc7b16fcb 100644 --- a/charts/services/services-auth-ids-api/values.staging.yaml +++ b/charts/services/services-auth-ids-api/values.staging.yaml @@ -19,6 +19,7 @@ global: name: 'services-auth-ids-api' enabled: true env: + CODE_OWNER: 'aranja' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.ab9ckb.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-TEST/GOV/5402696029/Skatturinn/ft-v1' DB_EXTENSIONS: 'uuid-ossp' diff --git a/charts/services/services-auth-public-api/values.dev.yaml b/charts/services/services-auth-public-api/values.dev.yaml index 9b4cc091ee46..3f7a25390e7f 100644 --- a/charts/services/services-auth-public-api/values.dev.yaml +++ b/charts/services/services-auth-public-api/values.dev.yaml @@ -19,6 +19,7 @@ global: name: 'services-auth-public-api' enabled: true env: + CODE_OWNER: 'aranja' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.5fzau3.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-DEV/GOV/10006/Skatturinn/ft-v1' DB_HOST: 'postgres-applications.internal' diff --git a/charts/services/services-auth-public-api/values.prod.yaml b/charts/services/services-auth-public-api/values.prod.yaml index 13e897e62429..5cfbd4ffd7ac 100644 --- a/charts/services/services-auth-public-api/values.prod.yaml +++ b/charts/services/services-auth-public-api/values.prod.yaml @@ -19,6 +19,7 @@ global: name: 'services-auth-public-api' enabled: true env: + CODE_OWNER: 'aranja' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.dnugi2.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS/GOV/5402696029/Skatturinn/ft-v1' DB_HOST: 'postgres-ids.internal' diff --git a/charts/services/services-auth-public-api/values.staging.yaml b/charts/services/services-auth-public-api/values.staging.yaml index ba19f2a7a3bf..31a9e6949e53 100644 --- a/charts/services/services-auth-public-api/values.staging.yaml +++ b/charts/services/services-auth-public-api/values.staging.yaml @@ -19,6 +19,7 @@ global: name: 'services-auth-public-api' enabled: true env: + CODE_OWNER: 'aranja' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.ab9ckb.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-TEST/GOV/5402696029/Skatturinn/ft-v1' DB_HOST: 'postgres-applications.internal' diff --git a/charts/services/services-bff-portals-admin/values.dev.yaml b/charts/services/services-bff-portals-admin/values.dev.yaml index 53970ff7cecc..ad41b2594e0f 100644 --- a/charts/services/services-bff-portals-admin/values.dev.yaml +++ b/charts/services/services-bff-portals-admin/values.dev.yaml @@ -31,6 +31,7 @@ env: BFF_NAME: 'stjornbord' BFF_PAR_SUPPORT_ENABLED: 'true' BFF_PROXY_API_ENDPOINT: 'http://web-api.islandis.svc.cluster.local/api/graphql' + CODE_OWNER: 'core' IDENTITY_SERVER_CLIENT_ID: '@admin.island.is/bff-stjornbord' IDENTITY_SERVER_CLIENT_SCOPES: '["@admin.island.is/delegations","@admin.island.is/ads","@admin.island.is/regulations","@admin.island.is/regulations:manage","@admin.island.is/icelandic-names-registry","@admin.island.is/application-system:admin","@admin.island.is/application-system:institution","@admin.island.is/document-provider","@admin.island.is/auth","@admin.island.is/auth:admin","@admin.island.is/petitions","@admin.island.is/service-desk","@admin.island.is/ads:explicit","@admin.island.is/signature-collection:manage","@admin.island.is/signature-collection:process","@admin.island.is/form-system","@admin.island.is/form-system:admin","@admin.island.is/delegation-system","@admin.island.is/delegation-system:admin"]' IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.dev01.devland.is' diff --git a/charts/services/services-bff-portals-admin/values.prod.yaml b/charts/services/services-bff-portals-admin/values.prod.yaml index a5d9d89deff0..85573c54b3f8 100644 --- a/charts/services/services-bff-portals-admin/values.prod.yaml +++ b/charts/services/services-bff-portals-admin/values.prod.yaml @@ -31,6 +31,7 @@ env: BFF_NAME: 'stjornbord' BFF_PAR_SUPPORT_ENABLED: 'true' BFF_PROXY_API_ENDPOINT: 'http://web-api.islandis.svc.cluster.local/api/graphql' + CODE_OWNER: 'core' IDENTITY_SERVER_CLIENT_ID: '@admin.island.is/bff-stjornbord' IDENTITY_SERVER_CLIENT_SCOPES: '["@admin.island.is/delegations","@admin.island.is/ads","@admin.island.is/regulations","@admin.island.is/regulations:manage","@admin.island.is/icelandic-names-registry","@admin.island.is/application-system:admin","@admin.island.is/application-system:institution","@admin.island.is/document-provider","@admin.island.is/auth","@admin.island.is/auth:admin","@admin.island.is/petitions","@admin.island.is/service-desk","@admin.island.is/ads:explicit","@admin.island.is/signature-collection:manage","@admin.island.is/signature-collection:process","@admin.island.is/form-system","@admin.island.is/form-system:admin","@admin.island.is/delegation-system","@admin.island.is/delegation-system:admin"]' IDENTITY_SERVER_ISSUER_URL: 'https://innskra.island.is' diff --git a/charts/services/services-bff-portals-admin/values.staging.yaml b/charts/services/services-bff-portals-admin/values.staging.yaml index 9fe68c571325..5244c4b5efc5 100644 --- a/charts/services/services-bff-portals-admin/values.staging.yaml +++ b/charts/services/services-bff-portals-admin/values.staging.yaml @@ -31,6 +31,7 @@ env: BFF_NAME: 'stjornbord' BFF_PAR_SUPPORT_ENABLED: 'true' BFF_PROXY_API_ENDPOINT: 'http://web-api.islandis.svc.cluster.local/api/graphql' + CODE_OWNER: 'core' IDENTITY_SERVER_CLIENT_ID: '@admin.island.is/bff-stjornbord' IDENTITY_SERVER_CLIENT_SCOPES: '["@admin.island.is/delegations","@admin.island.is/ads","@admin.island.is/regulations","@admin.island.is/regulations:manage","@admin.island.is/icelandic-names-registry","@admin.island.is/application-system:admin","@admin.island.is/application-system:institution","@admin.island.is/document-provider","@admin.island.is/auth","@admin.island.is/auth:admin","@admin.island.is/petitions","@admin.island.is/service-desk","@admin.island.is/ads:explicit","@admin.island.is/signature-collection:manage","@admin.island.is/signature-collection:process","@admin.island.is/form-system","@admin.island.is/form-system:admin","@admin.island.is/delegation-system","@admin.island.is/delegation-system:admin"]' IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.staging01.devland.is' diff --git a/charts/services/services-sessions-cleanup/values.dev.yaml b/charts/services/services-sessions-cleanup/values.dev.yaml index fa54d71f9cc6..ba81b8b2caf5 100644 --- a/charts/services/services-sessions-cleanup/values.dev.yaml +++ b/charts/services/services-sessions-cleanup/values.dev.yaml @@ -24,6 +24,7 @@ command: - 'node' enabled: true env: + CODE_OWNER: 'aranja' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_sessions' DB_REPLICAS_HOST: 'postgres-applications-reader.internal' diff --git a/charts/services/services-sessions-cleanup/values.prod.yaml b/charts/services/services-sessions-cleanup/values.prod.yaml index 2a436d368e82..0559731d6fa4 100644 --- a/charts/services/services-sessions-cleanup/values.prod.yaml +++ b/charts/services/services-sessions-cleanup/values.prod.yaml @@ -24,6 +24,7 @@ command: - 'node' enabled: true env: + CODE_OWNER: 'aranja' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_sessions' DB_REPLICAS_HOST: 'postgres-applications.internal' diff --git a/charts/services/services-sessions-cleanup/values.staging.yaml b/charts/services/services-sessions-cleanup/values.staging.yaml index df9aff0539a1..6ef083a64637 100644 --- a/charts/services/services-sessions-cleanup/values.staging.yaml +++ b/charts/services/services-sessions-cleanup/values.staging.yaml @@ -24,6 +24,7 @@ command: - 'node' enabled: true env: + CODE_OWNER: 'aranja' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_sessions' DB_REPLICAS_HOST: 'postgres-applications.internal' diff --git a/charts/services/services-sessions-worker/values.dev.yaml b/charts/services/services-sessions-worker/values.dev.yaml index b0fcf628cfaf..af020d71fd0a 100644 --- a/charts/services/services-sessions-worker/values.dev.yaml +++ b/charts/services/services-sessions-worker/values.dev.yaml @@ -24,6 +24,7 @@ command: - 'node' enabled: true env: + CODE_OWNER: 'aranja' DB_EXTENSIONS: 'uuid-ossp' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_sessions' diff --git a/charts/services/services-sessions-worker/values.prod.yaml b/charts/services/services-sessions-worker/values.prod.yaml index 430ba3aa97cb..45cf39972d81 100644 --- a/charts/services/services-sessions-worker/values.prod.yaml +++ b/charts/services/services-sessions-worker/values.prod.yaml @@ -24,6 +24,7 @@ command: - 'node' enabled: true env: + CODE_OWNER: 'aranja' DB_EXTENSIONS: 'uuid-ossp' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_sessions' diff --git a/charts/services/services-sessions-worker/values.staging.yaml b/charts/services/services-sessions-worker/values.staging.yaml index 3721d83be564..8637fe36f53a 100644 --- a/charts/services/services-sessions-worker/values.staging.yaml +++ b/charts/services/services-sessions-worker/values.staging.yaml @@ -24,6 +24,7 @@ command: - 'node' enabled: true env: + CODE_OWNER: 'aranja' DB_EXTENSIONS: 'uuid-ossp' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_sessions' diff --git a/charts/services/services-sessions/values.dev.yaml b/charts/services/services-sessions/values.dev.yaml index dfb22e69db69..27b3d9e87183 100644 --- a/charts/services/services-sessions/values.dev.yaml +++ b/charts/services/services-sessions/values.dev.yaml @@ -19,6 +19,7 @@ global: name: 'services-sessions' enabled: true env: + CODE_OWNER: 'aranja' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_sessions' DB_REPLICAS_HOST: 'postgres-applications-reader.internal' diff --git a/charts/services/services-sessions/values.prod.yaml b/charts/services/services-sessions/values.prod.yaml index ac6d8167fb4e..67724998dc78 100644 --- a/charts/services/services-sessions/values.prod.yaml +++ b/charts/services/services-sessions/values.prod.yaml @@ -19,6 +19,7 @@ global: name: 'services-sessions' enabled: true env: + CODE_OWNER: 'aranja' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_sessions' DB_REPLICAS_HOST: 'postgres-applications.internal' diff --git a/charts/services/services-sessions/values.staging.yaml b/charts/services/services-sessions/values.staging.yaml index 5de452280635..adc3e526bdc1 100644 --- a/charts/services/services-sessions/values.staging.yaml +++ b/charts/services/services-sessions/values.staging.yaml @@ -19,6 +19,7 @@ global: name: 'services-sessions' enabled: true env: + CODE_OWNER: 'aranja' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_sessions' DB_REPLICAS_HOST: 'postgres-applications.internal' diff --git a/charts/services/services-university-gateway-worker/values.dev.yaml b/charts/services/services-university-gateway-worker/values.dev.yaml index b219e85aa530..88c17a202e28 100644 --- a/charts/services/services-university-gateway-worker/values.dev.yaml +++ b/charts/services/services-university-gateway-worker/values.dev.yaml @@ -25,6 +25,7 @@ command: - 'node' enabled: true env: + CODE_OWNER: 'origo' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_university_gateway' DB_REPLICAS_HOST: 'postgres-applications-reader.internal' diff --git a/charts/services/services-university-gateway-worker/values.prod.yaml b/charts/services/services-university-gateway-worker/values.prod.yaml index 2827d3e190cc..b03e71fb49b7 100644 --- a/charts/services/services-university-gateway-worker/values.prod.yaml +++ b/charts/services/services-university-gateway-worker/values.prod.yaml @@ -25,6 +25,7 @@ command: - 'node' enabled: true env: + CODE_OWNER: 'origo' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_university_gateway' DB_REPLICAS_HOST: 'postgres-applications.internal' diff --git a/charts/services/services-university-gateway-worker/values.staging.yaml b/charts/services/services-university-gateway-worker/values.staging.yaml index e64ffd48d011..21d70f22db7c 100644 --- a/charts/services/services-university-gateway-worker/values.staging.yaml +++ b/charts/services/services-university-gateway-worker/values.staging.yaml @@ -25,6 +25,7 @@ command: - 'node' enabled: true env: + CODE_OWNER: 'origo' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_university_gateway' DB_REPLICAS_HOST: 'postgres-applications.internal' diff --git a/charts/services/services-university-gateway/values.dev.yaml b/charts/services/services-university-gateway/values.dev.yaml index 3916a66ca657..61eecf2b6b95 100644 --- a/charts/services/services-university-gateway/values.dev.yaml +++ b/charts/services/services-university-gateway/values.dev.yaml @@ -23,6 +23,7 @@ command: - 'node' enabled: true env: + CODE_OWNER: 'origo' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_university_gateway' DB_REPLICAS_HOST: 'postgres-applications-reader.internal' diff --git a/charts/services/services-university-gateway/values.prod.yaml b/charts/services/services-university-gateway/values.prod.yaml index 59ce966458e7..b7dd867587ec 100644 --- a/charts/services/services-university-gateway/values.prod.yaml +++ b/charts/services/services-university-gateway/values.prod.yaml @@ -23,6 +23,7 @@ command: - 'node' enabled: true env: + CODE_OWNER: 'origo' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_university_gateway' DB_REPLICAS_HOST: 'postgres-applications.internal' diff --git a/charts/services/services-university-gateway/values.staging.yaml b/charts/services/services-university-gateway/values.staging.yaml index 688dd16644f0..975b21ae2937 100644 --- a/charts/services/services-university-gateway/values.staging.yaml +++ b/charts/services/services-university-gateway/values.staging.yaml @@ -23,6 +23,7 @@ command: - 'node' enabled: true env: + CODE_OWNER: 'origo' DB_HOST: 'postgres-applications.internal' DB_NAME: 'services_university_gateway' DB_REPLICAS_HOST: 'postgres-applications.internal' diff --git a/charts/services/user-notification-cleanup-worker/values.dev.yaml b/charts/services/user-notification-cleanup-worker/values.dev.yaml index 9e558ec19c87..f59fcf817b4b 100644 --- a/charts/services/user-notification-cleanup-worker/values.dev.yaml +++ b/charts/services/user-notification-cleanup-worker/values.dev.yaml @@ -25,6 +25,7 @@ command: - 'node' enabled: true env: + CODE_OWNER: 'juni' DB_HOST: 'postgres-applications.internal' DB_NAME: 'user_notification' DB_REPLICAS_HOST: 'postgres-applications-reader.internal' diff --git a/charts/services/user-notification-cleanup-worker/values.prod.yaml b/charts/services/user-notification-cleanup-worker/values.prod.yaml index 43ba4e7bf3c7..1bcd7b43b581 100644 --- a/charts/services/user-notification-cleanup-worker/values.prod.yaml +++ b/charts/services/user-notification-cleanup-worker/values.prod.yaml @@ -25,6 +25,7 @@ command: - 'node' enabled: true env: + CODE_OWNER: 'juni' DB_HOST: 'postgres-applications.internal' DB_NAME: 'user_notification' DB_REPLICAS_HOST: 'postgres-applications.internal' diff --git a/charts/services/user-notification-cleanup-worker/values.staging.yaml b/charts/services/user-notification-cleanup-worker/values.staging.yaml index 946da9daabf0..47f68bae0eff 100644 --- a/charts/services/user-notification-cleanup-worker/values.staging.yaml +++ b/charts/services/user-notification-cleanup-worker/values.staging.yaml @@ -25,6 +25,7 @@ command: - 'node' enabled: true env: + CODE_OWNER: 'juni' DB_HOST: 'postgres-applications.internal' DB_NAME: 'user_notification' DB_REPLICAS_HOST: 'postgres-applications.internal' diff --git a/charts/services/user-notification-worker/values.dev.yaml b/charts/services/user-notification-worker/values.dev.yaml index 9b31936004f0..2a0ec43d43f3 100644 --- a/charts/services/user-notification-worker/values.dev.yaml +++ b/charts/services/user-notification-worker/values.dev.yaml @@ -27,6 +27,7 @@ enabled: true env: AUTH_DELEGATION_API_URL: 'http://web-services-auth-delegation-api.identity-server-delegation.svc.cluster.local' AUTH_DELEGATION_MACHINE_CLIENT_SCOPE: '["@island.is/auth/delegations/index:system"]' + CODE_OWNER: 'juni' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.5fzau3.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-DEV/GOV/10006/Skatturinn/ft-v1' CONTENTFUL_HOST: 'preview.contentful.com' diff --git a/charts/services/user-notification-worker/values.prod.yaml b/charts/services/user-notification-worker/values.prod.yaml index 505fd0fc1186..6ab822a0aa50 100644 --- a/charts/services/user-notification-worker/values.prod.yaml +++ b/charts/services/user-notification-worker/values.prod.yaml @@ -27,6 +27,7 @@ enabled: true env: AUTH_DELEGATION_API_URL: 'https://auth-delegation-api.internal.innskra.island.is' AUTH_DELEGATION_MACHINE_CLIENT_SCOPE: '["@island.is/auth/delegations/index:system"]' + CODE_OWNER: 'juni' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.whakos.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS/GOV/5402696029/Skatturinn/ft-v1' CONTENTFUL_HOST: 'cdn.contentful.com' diff --git a/charts/services/user-notification-worker/values.staging.yaml b/charts/services/user-notification-worker/values.staging.yaml index 20c9089348c6..4d38939c4667 100644 --- a/charts/services/user-notification-worker/values.staging.yaml +++ b/charts/services/user-notification-worker/values.staging.yaml @@ -27,6 +27,7 @@ enabled: true env: AUTH_DELEGATION_API_URL: 'http://web-services-auth-delegation-api.identity-server-delegation.svc.cluster.local' AUTH_DELEGATION_MACHINE_CLIENT_SCOPE: '["@island.is/auth/delegations/index:system"]' + CODE_OWNER: 'juni' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.ab9ckb.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-TEST/GOV/5402696029/Skatturinn/ft-v1' CONTENTFUL_HOST: 'cdn.contentful.com' diff --git a/charts/services/user-notification/values.dev.yaml b/charts/services/user-notification/values.dev.yaml index 31c759a45806..2a4c2df02f1d 100644 --- a/charts/services/user-notification/values.dev.yaml +++ b/charts/services/user-notification/values.dev.yaml @@ -26,6 +26,7 @@ enabled: true env: AUTH_DELEGATION_API_URL: 'http://web-services-auth-delegation-api.identity-server-delegation.svc.cluster.local' AUTH_DELEGATION_MACHINE_CLIENT_SCOPE: '["@island.is/auth/delegations/index:system"]' + CODE_OWNER: 'juni' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.5fzau3.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-DEV/GOV/10006/Skatturinn/ft-v1' DB_HOST: 'postgres-applications.internal' diff --git a/charts/services/user-notification/values.prod.yaml b/charts/services/user-notification/values.prod.yaml index b6d09de39826..00cf09f12ae3 100644 --- a/charts/services/user-notification/values.prod.yaml +++ b/charts/services/user-notification/values.prod.yaml @@ -26,6 +26,7 @@ enabled: true env: AUTH_DELEGATION_API_URL: 'https://auth-delegation-api.internal.innskra.island.is' AUTH_DELEGATION_MACHINE_CLIENT_SCOPE: '["@island.is/auth/delegations/index:system"]' + CODE_OWNER: 'juni' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.whakos.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS/GOV/5402696029/Skatturinn/ft-v1' DB_HOST: 'postgres-applications.internal' diff --git a/charts/services/user-notification/values.staging.yaml b/charts/services/user-notification/values.staging.yaml index e6dc9bc23264..9a9c1025e3bc 100644 --- a/charts/services/user-notification/values.staging.yaml +++ b/charts/services/user-notification/values.staging.yaml @@ -26,6 +26,7 @@ enabled: true env: AUTH_DELEGATION_API_URL: 'http://web-services-auth-delegation-api.identity-server-delegation.svc.cluster.local' AUTH_DELEGATION_MACHINE_CLIENT_SCOPE: '["@island.is/auth/delegations/index:system"]' + CODE_OWNER: 'juni' COMPANY_REGISTRY_REDIS_NODES: '["clustercfg.general-redis-cluster-group.ab9ckb.euw1.cache.amazonaws.com:6379"]' COMPANY_REGISTRY_XROAD_PROVIDER_ID: 'IS-TEST/GOV/5402696029/Skatturinn/ft-v1' DB_HOST: 'postgres-applications.internal' diff --git a/charts/services/web/values.dev.yaml b/charts/services/web/values.dev.yaml index a6455c2f5c34..13be4e498ef9 100644 --- a/charts/services/web/values.dev.yaml +++ b/charts/services/web/values.dev.yaml @@ -20,6 +20,7 @@ name: 'web' enabled: true env: API_URL: 'http://web-api' + CODE_OWNER: 'stefna' DISABLE_API_CATALOGUE: 'false' DISABLE_ORGANIZATION_CHATBOT: 'false' DISABLE_SYSLUMENN_PAGE: 'false' diff --git a/charts/services/web/values.prod.yaml b/charts/services/web/values.prod.yaml index a5c86c5c9db1..d86fe89b392b 100644 --- a/charts/services/web/values.prod.yaml +++ b/charts/services/web/values.prod.yaml @@ -20,6 +20,7 @@ name: 'web' enabled: true env: API_URL: 'http://web-api' + CODE_OWNER: 'stefna' DISABLE_API_CATALOGUE: 'false' DISABLE_ORGANIZATION_CHATBOT: 'false' DISABLE_SYSLUMENN_PAGE: 'false' diff --git a/charts/services/web/values.staging.yaml b/charts/services/web/values.staging.yaml index 4a0b40a3fa19..8e672b5827be 100644 --- a/charts/services/web/values.staging.yaml +++ b/charts/services/web/values.staging.yaml @@ -21,6 +21,7 @@ basicAuth: '/k8s/web/basic_auth' enabled: true env: API_URL: 'http://web-api' + CODE_OWNER: 'stefna' DISABLE_API_CATALOGUE: 'false' DISABLE_ORGANIZATION_CHATBOT: 'false' DISABLE_SYSLUMENN_PAGE: 'false' diff --git a/infra/src/dsl/dsl.ts b/infra/src/dsl/dsl.ts index 0427e7f0e29d..1a41e386bf74 100644 --- a/infra/src/dsl/dsl.ts +++ b/infra/src/dsl/dsl.ts @@ -1,4 +1,5 @@ import { merge } from 'lodash' +import { CodeOwners } from '../../../libs/shared/constants/src/lib/codeOwners' import { Context, EnvironmentVariables, @@ -194,6 +195,11 @@ export class ServiceBuilder { return this } + codeOwner(codeOwner: CodeOwners) { + this.serviceDef.env['CODE_OWNER'] = codeOwner + return this + } + /** * X-Road configuration blocks to inject to the container. Types of XroadConfig can contain environment variables and/or secrets that define how to contact an external service through X-Road. * @param ...configs - X-road configs @@ -565,3 +571,5 @@ export const service = ( } export const json = (value: unknown): string => JSON.stringify(value) + +export { CodeOwners } diff --git a/libs/api/domains/application/src/lib/application.resolver.ts b/libs/api/domains/application/src/lib/application.resolver.ts index 53f637de06cd..4efbe4233028 100644 --- a/libs/api/domains/application/src/lib/application.resolver.ts +++ b/libs/api/domains/application/src/lib/application.resolver.ts @@ -1,29 +1,32 @@ -import { Args, Query, Resolver, Mutation } from '@nestjs/graphql' import type { User } from '@island.is/auth-nest-tools' import { + CurrentUser, IdsUserGuard, ScopesGuard, - CurrentUser, } from '@island.is/auth-nest-tools' -import { UseGuards } from '@nestjs/common' +import { CodeOwners } from '@island.is/shared/constants' import type { Locale } from '@island.is/shared/types' +import { UseGuards } from '@nestjs/common' +import { Args, Mutation, Query, Resolver } from '@nestjs/graphql' +import { CodeOwner } from '@island.is/nest/core' +import { Application, ApplicationPayment } from './application.model' import { ApplicationService } from './application.service' -import { Application, ApplicationPayment } from './application.model' -import { CreateApplicationInput } from './dto/createApplication.input' -import { UpdateApplicationInput } from './dto/updateApplication.input' -import { UpdateApplicationExternalDataInput } from './dto/updateApplicationExternalData.input' import { AddAttachmentInput } from './dto/addAttachment.input' -import { DeleteAttachmentInput } from './dto/deleteAttachment.input' -import { SubmitApplicationInput } from './dto/submitApplication.input' -import { AssignApplicationInput } from './dto/assignApplication.input' import { ApplicationApplicationInput } from './dto/applicationApplication.input' import { ApplicationApplicationsInput } from './dto/applicationApplications.input' -import { PresignedUrlResponse } from './dto/presignedUrl.response' +import { AssignApplicationInput } from './dto/assignApplication.input' import { AttachmentPresignedUrlInput } from './dto/AttachmentPresignedUrl.input' +import { CreateApplicationInput } from './dto/createApplication.input' import { DeleteApplicationInput } from './dto/deleteApplication.input' +import { DeleteAttachmentInput } from './dto/deleteAttachment.input' +import { PresignedUrlResponse } from './dto/presignedUrl.response' +import { SubmitApplicationInput } from './dto/submitApplication.input' +import { UpdateApplicationInput } from './dto/updateApplication.input' +import { UpdateApplicationExternalDataInput } from './dto/updateApplicationExternalData.input' @UseGuards(IdsUserGuard, ScopesGuard) +@CodeOwner(CodeOwners.NordaApplications) @Resolver(() => Application) export class ApplicationResolver { constructor(private applicationService: ApplicationService) {} diff --git a/libs/api/domains/form-system/src/lib/forms/forms.resolver.ts b/libs/api/domains/form-system/src/lib/forms/forms.resolver.ts index 982b72da1d8f..59cc3a91105c 100644 --- a/libs/api/domains/form-system/src/lib/forms/forms.resolver.ts +++ b/libs/api/domains/form-system/src/lib/forms/forms.resolver.ts @@ -1,25 +1,28 @@ -import { Args, Mutation, Query, Resolver } from '@nestjs/graphql' -import { FormsService } from './forms.service' -import { FormResponse } from '../../models/formResponse.model' -import { - CreateFormInput, - GetFormInput, - GetFormsInput, - UpdateFormInput, - DeleteFormInput, -} from '../../dto/forms.input' import { CurrentUser, IdsUserGuard, type User, } from '@island.is/auth-nest-tools' -import { FormListResponse } from '../../models/formListResponse.model' import { Audit } from '@island.is/nest/audit' +import { CodeOwner } from '@island.is/nest/core' +import { CodeOwners } from '@island.is/shared/constants' import { UseGuards } from '@nestjs/common' +import { Args, Mutation, Query, Resolver } from '@nestjs/graphql' +import { + CreateFormInput, + DeleteFormInput, + GetFormInput, + GetFormsInput, + UpdateFormInput, +} from '../../dto/forms.input' import { UpdateFormSettingsInput } from '../../dto/updateFormSettings.input' +import { FormListResponse } from '../../models/formListResponse.model' +import { FormResponse } from '../../models/formResponse.model' +import { FormsService } from './forms.service' @Resolver() @UseGuards(IdsUserGuard) +@CodeOwner(CodeOwners.Advania) @Audit({ namespace: '@island.is/api/form-system' }) export class FormsResolver { constructor(private readonly formsService: FormsService) {} diff --git a/libs/api/domains/form-system/src/lib/groups/groups.resolver.ts b/libs/api/domains/form-system/src/lib/groups/groups.resolver.ts index 4c31ecc691d9..62353b74c5cb 100644 --- a/libs/api/domains/form-system/src/lib/groups/groups.resolver.ts +++ b/libs/api/domains/form-system/src/lib/groups/groups.resolver.ts @@ -1,3 +1,5 @@ +import { CodeOwner } from '@island.is/nest/core' +import { CodeOwners } from '@island.is/shared/constants' import { Query, Args, Resolver, Mutation } from '@nestjs/graphql' import { CurrentUser, @@ -17,6 +19,7 @@ import { UseGuards } from '@nestjs/common' @Resolver() @UseGuards(IdsUserGuard) +@CodeOwner(CodeOwners.Advania) @Audit({ namespace: '@island.is/api/form-system' }) export class GroupsResolver { constructor(private readonly groupsService: GroupsService) {} diff --git a/libs/api/domains/form-system/src/lib/inputs/inputs.resolver.ts b/libs/api/domains/form-system/src/lib/inputs/inputs.resolver.ts index 3e6645025689..2596516c1c68 100644 --- a/libs/api/domains/form-system/src/lib/inputs/inputs.resolver.ts +++ b/libs/api/domains/form-system/src/lib/inputs/inputs.resolver.ts @@ -1,3 +1,5 @@ +import { CodeOwner } from '@island.is/nest/core' +import { CodeOwners } from '@island.is/shared/constants' import { Query, Args, Resolver, Mutation } from '@nestjs/graphql' import { CurrentUser, @@ -17,6 +19,7 @@ import { UseGuards } from '@nestjs/common' @Resolver() @UseGuards(IdsUserGuard) +@CodeOwner(CodeOwners.Advania) @Audit({ namespace: '@island.is/api/form-system' }) export class InputsResolver { constructor(private readonly inputsService: InputsService) {} diff --git a/libs/api/domains/form-system/src/lib/organizations/organizations.resolver.ts b/libs/api/domains/form-system/src/lib/organizations/organizations.resolver.ts index ded9859a34e3..ddc2a93a0f15 100644 --- a/libs/api/domains/form-system/src/lib/organizations/organizations.resolver.ts +++ b/libs/api/domains/form-system/src/lib/organizations/organizations.resolver.ts @@ -1,3 +1,5 @@ +import { CodeOwner } from '@island.is/nest/core' +import { CodeOwners } from '@island.is/shared/constants' import { Args, Mutation, Resolver } from '@nestjs/graphql' import { OrganizationsService } from './organizations.services' import { CreateOrganizationInput } from '../../dto/organization.input' @@ -12,6 +14,7 @@ import { UseGuards } from '@nestjs/common' @Resolver() @UseGuards(IdsUserGuard) +@CodeOwner(CodeOwners.Advania) @Audit({ namespace: '@island.is/api/form-system' }) export class OrganizationsResolver { constructor(private readonly organizationsService: OrganizationsService) {} diff --git a/libs/api/domains/form-system/src/lib/services/services.resolver.ts b/libs/api/domains/form-system/src/lib/services/services.resolver.ts index 1ba7e31fdf96..a1f9df7774ec 100644 --- a/libs/api/domains/form-system/src/lib/services/services.resolver.ts +++ b/libs/api/domains/form-system/src/lib/services/services.resolver.ts @@ -1,3 +1,5 @@ +import { CodeOwner } from '@island.is/nest/core' +import { CodeOwners } from '@island.is/shared/constants' import { Query, Args, Resolver } from '@nestjs/graphql' import { CurrentUser, @@ -12,6 +14,7 @@ import { UseGuards } from '@nestjs/common' @Resolver() @UseGuards(IdsUserGuard) +@CodeOwner(CodeOwners.Advania) @Audit({ namespace: '@island.is/api/form-system' }) export class FormSystemServicesResolver { constructor(private readonly formSystemServices: FormSystemService) {} diff --git a/libs/api/domains/form-system/src/lib/steps/steps.resolver.ts b/libs/api/domains/form-system/src/lib/steps/steps.resolver.ts index 88f46a9a5ec6..6fb6cfcc876e 100644 --- a/libs/api/domains/form-system/src/lib/steps/steps.resolver.ts +++ b/libs/api/domains/form-system/src/lib/steps/steps.resolver.ts @@ -1,3 +1,5 @@ +import { CodeOwner } from '@island.is/nest/core' +import { CodeOwners } from '@island.is/shared/constants' import { Query, Args, Resolver, Mutation } from '@nestjs/graphql' import { CurrentUser, @@ -17,6 +19,7 @@ import { UseGuards } from '@nestjs/common' @Resolver() @UseGuards(IdsUserGuard) +@CodeOwner(CodeOwners.Advania) @Audit({ namespace: '@island.is/api/form-system' }) export class StepsResolver { constructor(private readonly stepsService: StepsService) {} diff --git a/libs/api/domains/license-service/src/lib/resolvers/licenseCollection.resolver.ts b/libs/api/domains/license-service/src/lib/resolvers/licenseCollection.resolver.ts index 102d14857c0f..0a78ada05e3f 100644 --- a/libs/api/domains/license-service/src/lib/resolvers/licenseCollection.resolver.ts +++ b/libs/api/domains/license-service/src/lib/resolvers/licenseCollection.resolver.ts @@ -6,19 +6,25 @@ import { ScopesGuard, } from '@island.is/auth-nest-tools' import { ApiScope } from '@island.is/auth/scopes' +import { LOGGER_PROVIDER, type Logger } from '@island.is/logging' import { Audit } from '@island.is/nest/audit' +import { + CodeOwner, + ParsedUserAgent, + type UserAgent, +} from '@island.is/nest/core' +import { CodeOwners } from '@island.is/shared/constants' import type { Locale } from '@island.is/shared/types' import { Inject, UseGuards } from '@nestjs/common' import { Args, Query, Resolver } from '@nestjs/graphql' -import { GetGenericLicensesInput } from '../dto/GetGenericLicenses.input' -import { LicenseService } from '../licenseService.service' import { LicenseCollection } from '../dto/GenericLicenseCollection.dto' import { GenericUserLicense } from '../dto/GenericUserLicense.dto' -import { LOGGER_PROVIDER, type Logger } from '@island.is/logging' -import { ParsedUserAgent, type UserAgent } from '@island.is/nest/core' +import { GetGenericLicensesInput } from '../dto/GetGenericLicenses.input' +import { LicenseService } from '../licenseService.service' @UseGuards(IdsUserGuard, ScopesGuard) +@CodeOwner(CodeOwners.Hugsmidjan) @Scopes(ApiScope.internal, ApiScope.licenses) @Resolver(() => LicenseCollection) @Audit({ namespace: '@island.is/api/license-service' }) diff --git a/libs/api/domains/license-service/src/lib/resolvers/pkPass.resolver.ts b/libs/api/domains/license-service/src/lib/resolvers/pkPass.resolver.ts index d78ea015d5e5..13ec1766bb77 100644 --- a/libs/api/domains/license-service/src/lib/resolvers/pkPass.resolver.ts +++ b/libs/api/domains/license-service/src/lib/resolvers/pkPass.resolver.ts @@ -7,6 +7,8 @@ import { } from '@island.is/auth-nest-tools' import { ApiScope } from '@island.is/auth/scopes' import { Audit } from '@island.is/nest/audit' +import { CodeOwner } from '@island.is/nest/core' +import { CodeOwners } from '@island.is/shared/constants' import { UseGuards } from '@nestjs/common' import { Args, Mutation, Resolver } from '@nestjs/graphql' @@ -20,6 +22,7 @@ import { GenericPkPassVerification } from '../dto/GenericPkPassVerification.dto' import { VerifyPkPassInput } from '../dto/VerifyPkPass.input' @UseGuards(IdsUserGuard, ScopesGuard) +@CodeOwner(CodeOwners.Hugsmidjan) @Resolver(() => GenericPkPass) @Audit({ namespace: '@island.is/api/license-service' }) export class PkPassResolver { diff --git a/libs/api/domains/license-service/src/lib/resolvers/provider.resolver.ts b/libs/api/domains/license-service/src/lib/resolvers/provider.resolver.ts index f9d28c55d613..bcb1657a69e2 100644 --- a/libs/api/domains/license-service/src/lib/resolvers/provider.resolver.ts +++ b/libs/api/domains/license-service/src/lib/resolvers/provider.resolver.ts @@ -1,6 +1,8 @@ import { IdsUserGuard, Scopes, ScopesGuard } from '@island.is/auth-nest-tools' import { ApiScope } from '@island.is/auth/scopes' import { Audit } from '@island.is/nest/audit' +import { CodeOwner } from '@island.is/nest/core' +import { CodeOwners } from '@island.is/shared/constants' import { UseGuards } from '@nestjs/common' import { Parent, ResolveField, Resolver } from '@nestjs/graphql' import { GenericLicenseProvider } from '../dto/GenericLicenseProvider.dto' @@ -13,6 +15,7 @@ import { import { Loader } from '@island.is/nest/dataloader' @UseGuards(IdsUserGuard, ScopesGuard) +@CodeOwner(CodeOwners.Hugsmidjan) @Scopes(ApiScope.internal, ApiScope.licenses) @Resolver(() => GenericLicenseProvider) @Audit({ namespace: '@island.is/api/license-service' }) diff --git a/libs/api/domains/license-service/src/lib/resolvers/userLicense.resolver.ts b/libs/api/domains/license-service/src/lib/resolvers/userLicense.resolver.ts index cc42079d6065..127007366148 100644 --- a/libs/api/domains/license-service/src/lib/resolvers/userLicense.resolver.ts +++ b/libs/api/domains/license-service/src/lib/resolvers/userLicense.resolver.ts @@ -7,6 +7,8 @@ import { } from '@island.is/auth-nest-tools' import { ApiScope, LicenseApiScope } from '@island.is/auth/scopes' import { Audit } from '@island.is/nest/audit' +import { CodeOwner } from '@island.is/nest/core' +import { CodeOwners } from '@island.is/shared/constants' import type { Locale } from '@island.is/shared/types' import { ForbiddenException, UseGuards } from '@nestjs/common' import { Args, Parent, Query, ResolveField, Resolver } from '@nestjs/graphql' @@ -19,6 +21,7 @@ import { logger } from '@island.is/logging' import { ParsedUserAgent, type UserAgent } from '@island.is/nest/core' @UseGuards(IdsUserGuard, ScopesGuard) +@CodeOwner(CodeOwners.Hugsmidjan) @Scopes(ApiScope.internal, ApiScope.licenses) @Resolver(() => GenericUserLicense) @Audit({ namespace: '@island.is/api/license-service' }) diff --git a/libs/infra-tracing/jest.config.ts b/libs/infra-tracing/jest.config.ts index 408bf688f2da..d763876692e5 100644 --- a/libs/infra-tracing/jest.config.ts +++ b/libs/infra-tracing/jest.config.ts @@ -3,6 +3,7 @@ export default { preset: './jest.preset.js', rootDir: '../..', roots: [__dirname], + testEnvironment: 'node', transform: { '^.+\\.[tj]sx?$': [ 'ts-jest', diff --git a/libs/infra-tracing/project.json b/libs/infra-tracing/project.json index ea0b7fb854da..0d44fc1d4663 100644 --- a/libs/infra-tracing/project.json +++ b/libs/infra-tracing/project.json @@ -3,7 +3,7 @@ "$schema": "../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "libs/infra-tracing/src", "projectType": "library", - "tags": ["scope:js", "lib:js"], + "tags": ["scope:node", "lib:node"], "generators": {}, "targets": { "lint": { diff --git a/libs/infra-tracing/src/index.ts b/libs/infra-tracing/src/index.ts index 911527568b58..fedc94d86666 100644 --- a/libs/infra-tracing/src/index.ts +++ b/libs/infra-tracing/src/index.ts @@ -1 +1,2 @@ export * from './lib/datadog-tracer' // must come before importing any instrumented module. +export * from './lib/code-owner' diff --git a/libs/infra-tracing/src/lib/code-owner.spec.ts b/libs/infra-tracing/src/lib/code-owner.spec.ts new file mode 100644 index 000000000000..ed860dbd7a97 --- /dev/null +++ b/libs/infra-tracing/src/lib/code-owner.spec.ts @@ -0,0 +1,49 @@ +import { setCodeOwner } from './code-owner' +import { CodeOwners } from '@island.is/shared/constants' +import { logger } from '@island.is/logging' +import tracer from 'dd-trace' + +jest.mock('dd-trace') +jest.mock('@island.is/logging') + +describe('setCodeOwner', () => { + let mockSpan: { setTag: jest.Mock } + let mockScope: jest.Mock + + beforeEach(() => { + mockSpan = { + setTag: jest.fn(), + } + mockScope = jest.fn(() => ({ + active: () => mockSpan, + })) + ;(tracer.scope as jest.Mock) = mockScope + ;(logger.warn as jest.Mock).mockClear() + }) + + it('should set code owner tag on active span', () => { + // Act + setCodeOwner(CodeOwners.Core) + + // Assert + expect(mockSpan.setTag).toHaveBeenCalledWith('codeOwner', CodeOwners.Core) + expect(logger.warn).not.toHaveBeenCalled() + }) + + it('should log warning when no active span exists', () => { + // Arrange + mockScope = jest.fn(() => ({ + active: () => null, + })) + ;(tracer.scope as jest.Mock) = mockScope + + // Act + setCodeOwner(CodeOwners.Core) + + // Assert + expect(logger.warn).toHaveBeenCalledWith( + 'Setting code owner "core" with no active dd-trace span', + { stack: expect.any(String) }, + ) + }) +}) diff --git a/libs/infra-tracing/src/lib/code-owner.ts b/libs/infra-tracing/src/lib/code-owner.ts new file mode 100644 index 000000000000..7d4d6352fe52 --- /dev/null +++ b/libs/infra-tracing/src/lib/code-owner.ts @@ -0,0 +1,22 @@ +import { logger } from '@island.is/logging' +import { CodeOwners } from '@island.is/shared/constants' +import tracer from 'dd-trace' + +/** + * Sets a code owner for the current dd-trace span. + * + * The assumption here is that each trace / request has only one "dynamic" + * code owner. This way we skip cluttering the trace with extra spans. + */ +export const setCodeOwner = (codeOwner: CodeOwners) => { + const span = tracer.scope().active() + if (span) { + span.setTag('codeOwner', codeOwner) + } else { + const stack = new Error().stack + logger.warn( + `Setting code owner "${codeOwner}" with no active dd-trace span`, + { stack }, + ) + } +} diff --git a/libs/infra-tracing/src/lib/datadog-tracer.ts b/libs/infra-tracing/src/lib/datadog-tracer.ts index e2b5516df49f..21a82abf4502 100644 --- a/libs/infra-tracing/src/lib/datadog-tracer.ts +++ b/libs/infra-tracing/src/lib/datadog-tracer.ts @@ -22,7 +22,12 @@ export const maskSpan = ( } if (process.env.NODE_ENV !== 'development') { - tracer.init({ logInjection: true }) // initialized in a different file to avoid hoisting. + tracer.init({ + logInjection: true, + tags: { + codeOwner: process.env.CODE_OWNER, + }, + }) // initialized in a different file to avoid hoisting. tracer.use('express', { blacklist: ['/liveness', '/readiness', '/metrics'], hooks: { diff --git a/libs/logging/jest.config.ts b/libs/logging/jest.config.ts index 4659aa0dfb12..181249a15e28 100644 --- a/libs/logging/jest.config.ts +++ b/libs/logging/jest.config.ts @@ -3,6 +3,7 @@ export default { preset: './jest.preset.js', rootDir: '../..', roots: [__dirname], + testEnvironment: 'node', transform: { '^.+\\.[tj]sx?$': [ 'ts-jest', diff --git a/libs/logging/src/index.ts b/libs/logging/src/index.ts index 5389a8fd32d0..dd7c324e92ad 100644 --- a/libs/logging/src/index.ts +++ b/libs/logging/src/index.ts @@ -1,4 +1,5 @@ export * from './lib/logging' export * from './lib/nest/logging.module' export * from './lib/monkeyPatch' +export { withLoggingContext } from './lib/context' export type { Logger } from './lib/nest/logging.module' diff --git a/libs/logging/src/lib/context.spec.ts b/libs/logging/src/lib/context.spec.ts new file mode 100644 index 000000000000..ca371ba27376 --- /dev/null +++ b/libs/logging/src/lib/context.spec.ts @@ -0,0 +1,77 @@ +import { includeContextFormatter, withLoggingContext } from './context' + +describe('Winston context', () => { + const originalEnv = process.env + + beforeEach(() => { + process.env = { ...originalEnv } + }) + + afterEach(() => { + process.env = originalEnv + }) + + it('should add context to log info object', () => { + // Arrange + const formatter = includeContextFormatter() + const logInfo = { + level: 'info', + message: 'Test message', + } + const context = { requestId: '123', userId: '456' } + + // Act + let formattedLog: unknown + withLoggingContext(context, () => { + formattedLog = formatter.transform(logInfo) + }) + + // Assert + expect(formattedLog).toEqual({ + level: 'info', + message: 'Test message', + requestId: '123', + userId: '456', + }) + }) + + it('should not modify log info when no context exists', () => { + // Arrange + const formatter = includeContextFormatter() + const logInfo = { + level: 'info', + message: 'Test message', + } + + // Act + const formattedLog = formatter.transform(logInfo) + + // Assert + expect(formattedLog).toEqual(logInfo) + }) + + it('should preserve existing log info properties when adding context', () => { + // Arrange + const formatter = includeContextFormatter() + const logInfo = { + level: 'info', + message: 'Test message', + existingProp: 'should remain', + } + const context = { requestId: '123' } + + // Act + let formattedLog: unknown + withLoggingContext(context, () => { + formattedLog = formatter.transform(logInfo) + }) + + // Assert + expect(formattedLog).toEqual({ + level: 'info', + message: 'Test message', + existingProp: 'should remain', + requestId: '123', + }) + }) +}) diff --git a/libs/logging/src/lib/context.ts b/libs/logging/src/lib/context.ts new file mode 100644 index 000000000000..03034f67d79d --- /dev/null +++ b/libs/logging/src/lib/context.ts @@ -0,0 +1,28 @@ +import { AsyncLocalStorage } from 'async_hooks' +import { format } from 'winston' + +const loggingContextStorage = new AsyncLocalStorage>() + +/** + * Adds context which will be included with all logging inside the callback. + */ +export const withLoggingContext = ( + context: Record, + callback: (...args: TArgs) => R, + ...args: TArgs +): R => { + const extendedContext = { + ...loggingContextStorage.getStore(), + ...context, + } + return loggingContextStorage.run(extendedContext, callback, ...args) +} + +export const includeContextFormatter = format((info) => { + const context = loggingContextStorage.getStore() + + if (context) { + Object.assign(info, context) + } + return info +}) diff --git a/libs/logging/src/lib/logging.ts b/libs/logging/src/lib/logging.ts index b250c005fa65..9576d3ab66bb 100644 --- a/libs/logging/src/lib/logging.ts +++ b/libs/logging/src/lib/logging.ts @@ -1,5 +1,6 @@ import { createLogger, format, LoggerOptions, transports } from 'winston' import { utilities } from 'nest-winston' +import { includeContextFormatter } from './context' import { maskNationalIdFormatter } from './formatters' @@ -8,6 +9,8 @@ let logLevel = 'debug' let logFormat = format.combine( format.errors({ stack: true }), format.timestamp(), + // Disable locally to reduce noise. Can be reconsidered. + // includeContextFormatter(), utilities.format.nestLike('App'), maskNationalIdFormatter(), ) @@ -18,6 +21,7 @@ if (process.env.NODE_ENV === 'production') { logFormat = format.combine( format.errors({ stack: true }), format.timestamp(), + includeContextFormatter(), format.json(), maskNationalIdFormatter(), ) diff --git a/libs/nest/core/src/index.ts b/libs/nest/core/src/index.ts index e6b84b1639ef..123d16158fc1 100644 --- a/libs/nest/core/src/index.ts +++ b/libs/nest/core/src/index.ts @@ -1,3 +1,5 @@ +export * from './lib/code-owner/code-owner.decorator' +export * from './lib/code-owner/code-owner.interceptor' export * from './lib/decorators/nationalId.decorator' export * from './lib/decorators/ParsedUserAgent.decorator' export * from './lib/validators/isPersonNationalId.decorator' diff --git a/libs/nest/core/src/lib/code-owner/code-owner.decorator.ts b/libs/nest/core/src/lib/code-owner/code-owner.decorator.ts new file mode 100644 index 000000000000..37d3f049fa03 --- /dev/null +++ b/libs/nest/core/src/lib/code-owner/code-owner.decorator.ts @@ -0,0 +1,7 @@ +import { SetMetadata } from '@nestjs/common' +import { CodeOwners } from '@island.is/shared/constants' + +export const CODE_OWNER_KEY = 'codeOwner' + +export const CodeOwner = (codeOwner: CodeOwners) => + SetMetadata(CODE_OWNER_KEY, codeOwner) diff --git a/libs/nest/core/src/lib/code-owner/code-owner.interceptor.spec.ts b/libs/nest/core/src/lib/code-owner/code-owner.interceptor.spec.ts new file mode 100644 index 000000000000..038802b02237 --- /dev/null +++ b/libs/nest/core/src/lib/code-owner/code-owner.interceptor.spec.ts @@ -0,0 +1,87 @@ +import { setCodeOwner } from '@island.is/infra-tracing' +import { CodeOwners } from '@island.is/shared/constants' +import { Controller, Get, INestApplication } from '@nestjs/common' +import { APP_INTERCEPTOR } from '@nestjs/core' +import { Test, TestingModule } from '@nestjs/testing' +import request from 'supertest' +import { CodeOwner } from './code-owner.decorator' +import { CodeOwnerInterceptor } from './code-owner.interceptor' + +// Mock the logging module +jest.mock('@island.is/infra-tracing', () => ({ + setCodeOwner: jest.fn(), +})) + +// Test controller with decorated endpoints +@Controller('test') +class TestController { + @CodeOwner(CodeOwners.Core) + @Get('with-owner') + getWithOwner() { + return { message: 'with owner' } + } + + @Get('without-owner') + getWithoutOwner() { + return { message: 'without owner' } + } +} + +describe('CodeOwnerInterceptor', () => { + let app: INestApplication + + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + controllers: [TestController], + providers: [ + { + provide: APP_INTERCEPTOR, + useClass: CodeOwnerInterceptor, + }, + ], + }).compile() + + app = moduleFixture.createNestApplication() + await app.init() + }) + + afterEach(async () => { + await app.close() + jest.clearAllMocks() + }) + + it('should call setCodeOwner when CodeOwner decorator is present', async () => { + // Make request to endpoint with CodeOwner decorator + await request(app.getHttpServer()) + .get('/test/with-owner') + .expect(200) + .expect({ message: 'with owner' }) + + // Verify that setCodeOwner was called with correct parameters + expect(setCodeOwner).toHaveBeenCalledWith(CodeOwners.Core) + }) + + it('should not call setCodeOwner when CodeOwner decorator is not present', async () => { + // Make request to endpoint without CodeOwner decorator + await request(app.getHttpServer()) + .get('/test/without-owner') + .expect(200) + .expect({ message: 'without owner' }) + + // Verify that setCodeOwner was not called + expect(setCodeOwner).not.toHaveBeenCalled() + }) + + it('should handle multiple requests correctly', async () => { + // Make multiple requests to both endpoints + await Promise.all([ + request(app.getHttpServer()).get('/test/with-owner'), + request(app.getHttpServer()).get('/test/without-owner'), + request(app.getHttpServer()).get('/test/with-owner'), + ]) + + // Verify that setCodeOwner was called exactly twice (for the two 'with-owner' requests) + expect(setCodeOwner).toHaveBeenCalledTimes(2) + expect(setCodeOwner).toHaveBeenCalledWith(CodeOwners.Core) + }) +}) diff --git a/libs/nest/core/src/lib/code-owner/code-owner.interceptor.ts b/libs/nest/core/src/lib/code-owner/code-owner.interceptor.ts new file mode 100644 index 000000000000..a9f992fccbc6 --- /dev/null +++ b/libs/nest/core/src/lib/code-owner/code-owner.interceptor.ts @@ -0,0 +1,28 @@ +import { setCodeOwner } from '@island.is/infra-tracing' +import { CodeOwners } from '@island.is/shared/constants' +import { + Injectable, + NestInterceptor, + ExecutionContext, + CallHandler, +} from '@nestjs/common' +import { Reflector } from '@nestjs/core' +import { Observable } from 'rxjs' +import { CODE_OWNER_KEY } from './code-owner.decorator' + +@Injectable() +export class CodeOwnerInterceptor implements NestInterceptor { + constructor(private readonly reflector: Reflector) {} + + intercept(context: ExecutionContext, next: CallHandler): Observable { + const codeOwner = this.reflector.getAllAndOverride( + CODE_OWNER_KEY, + [context.getHandler(), context.getClass()], + ) + + if (codeOwner) { + setCodeOwner(codeOwner) + } + return next.handle() + } +} diff --git a/libs/shared/constants/src/index.ts b/libs/shared/constants/src/index.ts index 01420ce59242..925b5464be6e 100644 --- a/libs/shared/constants/src/index.ts +++ b/libs/shared/constants/src/index.ts @@ -1,5 +1,6 @@ export * from './lib/agent' export * from './lib/cacheControl' +export * from './lib/codeOwners' export * from './lib/locale' export * from './lib/date' export * from './lib/chargeItemCode' diff --git a/libs/shared/constants/src/lib/codeOwners.ts b/libs/shared/constants/src/lib/codeOwners.ts new file mode 100644 index 000000000000..912e323b0b61 --- /dev/null +++ b/libs/shared/constants/src/lib/codeOwners.ts @@ -0,0 +1,18 @@ +/** + * Codeowners for different projects. Should match github team names used in .github/CODEOWNERS. + */ +export enum CodeOwners { + Advania = 'Advania', + Aranja = 'aranja', + Core = 'core', + Deloitte = 'deloitte', + Devops = 'devops', + Hugsmidjan = 'hugsmidjan', + Juni = 'juni', + KolibriJusticeLeague = 'kolibri-justice-league', + KolibriRobinHood = 'kolibri-robin-hood', + NordaApplications = 'norda-applications', + Origo = 'origo', + Programm = 'programm', + Stefna = 'stefna', +} From 7e74f958f89c208dc410b3345206154a91d7963b Mon Sep 17 00:00:00 2001 From: Kristofer Date: Wed, 4 Dec 2024 19:16:24 +0000 Subject: [PATCH 28/85] fix(infra): `run-local-env` bugfixes (#17132) * Make run-local-env strict * Exit, not return in chain --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- infra/src/cli/cli.ts | 1 + infra/src/dsl/value-files-generators/local-setup.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/infra/src/cli/cli.ts b/infra/src/cli/cli.ts index 5b4d08560f0e..21d21dd4a6e0 100644 --- a/infra/src/cli/cli.ts +++ b/infra/src/cli/cli.ts @@ -140,5 +140,6 @@ const cli = yargs(process.argv.slice(2)) }) }, ) + .strict() .demandCommand(1) .parse() diff --git a/infra/src/dsl/value-files-generators/local-setup.ts b/infra/src/dsl/value-files-generators/local-setup.ts index 0ae7abcc702e..7ef9356772d2 100644 --- a/infra/src/dsl/value-files-generators/local-setup.ts +++ b/infra/src/dsl/value-files-generators/local-setup.ts @@ -96,7 +96,7 @@ export const getLocalrunValueFile = async ( `cd "${rootDir}"`, // Change directory to the root directory `. ./.env.${serviceNXName}`, // Source the environment variables for the service `echo "Preparing dev-services for ${name}"`, // Log preparation message - `if yarn nx show projects --with-target dev-services | grep -q '^${serviceNXName}$'; then yarn nx dev-services ${serviceNXName} || return $?; fi`, // Check and set up dev-services if needed + `if yarn nx show projects --with-target dev-services | grep -q '^${serviceNXName}$'; then yarn nx dev-services ${serviceNXName} || exit $?; fi`, // Check and set up dev-services if needed `echo "Starting ${name} in $PWD"`, `yarn nx serve ${serviceNXName}`, // Log start message and start the service ], From 03e105f47c88c650b796fe27a0aa7583a03d9567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAnar=20Vestmann?= <43557895+RunarVestmann@users.noreply.github.com> Date: Wed, 4 Dec 2024 19:29:38 +0000 Subject: [PATCH 29/85] chore(web): Stop printing web reader (#17135) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- apps/web/components/Webreader/Webreader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/components/Webreader/Webreader.tsx b/apps/web/components/Webreader/Webreader.tsx index 1c9793b7abd9..107d8079cbde 100644 --- a/apps/web/components/Webreader/Webreader.tsx +++ b/apps/web/components/Webreader/Webreader.tsx @@ -109,7 +109,7 @@ const Webreader: FC> = ({ : 'Listen to this page using ReadSpeaker' return ( - +
Date: Wed, 4 Dec 2024 19:44:26 +0000 Subject: [PATCH 30/85] chore(consultation-portal): Move to ts, and use `API_URL` (#17133) * Move to ts, and use `API_URL` * More cherry pick from #17008 --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- apps/consultation-portal/infra/samradsgatt.ts | 1 + apps/consultation-portal/proxy.config.json | 6 ------ apps/consultation-portal/proxy.config.ts | 8 ++++++++ apps/consultation-portal/server.ts | 3 ++- 4 files changed, 11 insertions(+), 7 deletions(-) delete mode 100644 apps/consultation-portal/proxy.config.json create mode 100644 apps/consultation-portal/proxy.config.ts diff --git a/apps/consultation-portal/infra/samradsgatt.ts b/apps/consultation-portal/infra/samradsgatt.ts index e079e70cce85..ffbe032843df 100644 --- a/apps/consultation-portal/infra/samradsgatt.ts +++ b/apps/consultation-portal/infra/samradsgatt.ts @@ -34,6 +34,7 @@ export const serviceSetup = (services: { prod: 'innskra.island.is', }, NEXTAUTH_URL: { + local: 'http://localhost:4200/samradsgatt/api/auth', dev: 'https://beta.dev01.devland.is/samradsgatt/api/auth', staging: 'https://beta.staging01.devland.is/samradsgatt/api/auth', prod: 'https://island.is/samradsgatt/api/auth', diff --git a/apps/consultation-portal/proxy.config.json b/apps/consultation-portal/proxy.config.json deleted file mode 100644 index 1ec1f59e0a1a..000000000000 --- a/apps/consultation-portal/proxy.config.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "/api/graphql": { - "target": "http://localhost:4444", - "secure": false - } -} diff --git a/apps/consultation-portal/proxy.config.ts b/apps/consultation-portal/proxy.config.ts new file mode 100644 index 000000000000..f11a5ea0c142 --- /dev/null +++ b/apps/consultation-portal/proxy.config.ts @@ -0,0 +1,8 @@ +const proxyConfig = { + '/api/graphql': { + target: process.env.API_URL ?? 'http://localhost:4444', + secure: false, + }, +} + +export default proxyConfig diff --git a/apps/consultation-portal/server.ts b/apps/consultation-portal/server.ts index c9dfb9850f13..29a716307e84 100644 --- a/apps/consultation-portal/server.ts +++ b/apps/consultation-portal/server.ts @@ -1,5 +1,6 @@ import { bootstrap } from '@island.is/infra-next-server' -import proxyConfig from './proxy.config.json' +import proxyConfig from './proxy.config' + bootstrap({ name: 'consultation-portal', appDir: 'apps/consultation-portal', From 69c02b2336298401bc6593e2860fa03113be63ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAnar=20Vestmann?= <43557895+RunarVestmann@users.noreply.github.com> Date: Thu, 5 Dec 2024 10:49:30 +0000 Subject: [PATCH 31/85] feat(web): WHODAS Calculator - Add text to top of component (#17136) * Add text to top of whodas calculator * Add key to prevent focus on click --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../connected/WHODAS/Calculator.strings.ts | 11 ++ .../connected/WHODAS/Calculator.tsx | 104 ++++++++++-------- 2 files changed, 70 insertions(+), 45 deletions(-) diff --git a/apps/web/components/connected/WHODAS/Calculator.strings.ts b/apps/web/components/connected/WHODAS/Calculator.strings.ts index d1b82b8593f5..5794e1786e71 100644 --- a/apps/web/components/connected/WHODAS/Calculator.strings.ts +++ b/apps/web/components/connected/WHODAS/Calculator.strings.ts @@ -2,6 +2,11 @@ import { defineMessages } from 'react-intl' export const m = { form: defineMessages({ + topDescription: { + id: 'web.whodas.calculator:form.topDescription#markdown', + defaultMessage: 'Merktu viĂ° einn valkost fyrir hverja spurningu.', + description: 'Texti fyrir ofan spurningalista', + }, previousStep: { id: 'web.whodas.calculator:form.previousStep', defaultMessage: 'Fyrra skref', @@ -51,6 +56,12 @@ export const m = { }, }), results: defineMessages({ + topDescription: { + id: 'web.whodas.calculator:results.topDescription#markdown', + defaultMessage: + 'Takk fyrir aĂ° svara spurningalistanum, Mat ĂĄ fĂŠrni ĂŸinni. Mat ĂŸitt ĂĄ fĂŠrni er stuĂ°ningur viĂ° aĂ° meta ĂŸĂ¶rf ĂŸĂ­na fyrir heimaĂŸjĂłnustu. Ef ĂŸĂș hefur ekki svaraĂ° öllum spurningum getur ĂŸaĂ° haft ĂĄhrif ĂĄ niĂ°urstöðuna.', + description: 'Texti fyrir ofan niĂ°urstöðuskjĂĄ', + }, mainHeading: { id: 'web.whodas.calculator:form.mainHeading', defaultMessage: 'NiĂ°urstaĂ°a mats ĂĄ fĂŠrni', diff --git a/apps/web/components/connected/WHODAS/Calculator.tsx b/apps/web/components/connected/WHODAS/Calculator.tsx index 9ee19cc6bf89..99ff866974b7 100644 --- a/apps/web/components/connected/WHODAS/Calculator.tsx +++ b/apps/web/components/connected/WHODAS/Calculator.tsx @@ -257,62 +257,76 @@ export const WHODASCalculator = ({ slice }: WHODASCalculatorProps) => { totalScore += score } return ( - + + + {formatMessage(m.results.topDescription)} + + + ) } return ( - - - - - - {formatMessage(m.form.progress, { - stepIndex: stepIndex + 1, - stepCount: steps.length, - })} - - + + + {formatMessage(m.form.topDescription)} + + + + + + + {formatMessage(m.form.progress, { + stepIndex: stepIndex + 1, + stepCount: steps.length, + })} + + + + - - - - - {stepIndex > 0 && ( + + + {stepIndex > 0 && ( + + )} - )} - - + + ) } From 55aacf7935422425da140c2e254d3d25fdeb2660 Mon Sep 17 00:00:00 2001 From: Ylfa <55542991+ylfahfa@users.noreply.github.com> Date: Thu, 5 Dec 2024 11:39:54 +0000 Subject: [PATCH 32/85] feat(old-age-pension): Show conclusion page based on income plan status (#17087) * start changes to conclusion page * add component for dynamic conclusion page * format * amendments after review plus small text change * remove unused import * chore: nx format:write update dirty files --------- Co-authored-by: andes-it Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../income-plan/src/fields/Review/index.tsx | 4 +- .../income-plan/src/lib/messages.ts | 5 + .../src/dataProviders/index.ts | 6 + .../src/fields/Conclusion/index.tsx | 104 ++++++++++++++++++ .../old-age-pension/src/fields/index.ts | 1 + .../src/forms/OldAgePensionForm.ts | 52 ++++----- .../src/forms/Prerequisites.ts | 5 + .../src/lib/OldAgePensionTemplate.ts | 2 + .../old-age-pension/src/lib/messages.ts | 12 ++ .../src/lib/oldAgePensionUtils.ts | 6 + 10 files changed, 171 insertions(+), 26 deletions(-) create mode 100644 libs/application/templates/social-insurance-administration/old-age-pension/src/fields/Conclusion/index.tsx diff --git a/libs/application/templates/social-insurance-administration/income-plan/src/fields/Review/index.tsx b/libs/application/templates/social-insurance-administration/income-plan/src/fields/Review/index.tsx index 09323c06371b..9a420ccf1e56 100644 --- a/libs/application/templates/social-insurance-administration/income-plan/src/fields/Review/index.tsx +++ b/libs/application/templates/social-insurance-administration/income-plan/src/fields/Review/index.tsx @@ -119,7 +119,9 @@ export const Review: FC = ({ - {formatMessage(inReviewFormMessages.description)} + {state === `${States.TRYGGINGASTOFNUN_SUBMITTED}` + ? formatMessage(inReviewFormMessages.description) + : formatMessage(inReviewFormMessages.reviewDescription)} diff --git a/libs/application/templates/social-insurance-administration/income-plan/src/lib/messages.ts b/libs/application/templates/social-insurance-administration/income-plan/src/lib/messages.ts index f77dd9c57de4..a989acf7c4ad 100644 --- a/libs/application/templates/social-insurance-administration/income-plan/src/lib/messages.ts +++ b/libs/application/templates/social-insurance-administration/income-plan/src/lib/messages.ts @@ -315,6 +315,11 @@ export const inReviewFormMessages = defineMessages({ defaultMessage: 'TekjuĂĄĂŠtlun ĂŸĂ­n hefur ekki veriĂ° tekin til vinnslu.', description: 'Your income plan has not been processed.', }, + reviewDescription: { + id: 'ip.application:inReview.review.description', + defaultMessage: 'TekjuĂĄĂŠtlun ĂŸĂ­n hefur veriĂ° tekin til vinnslu.', + description: 'Your income plan has been processed.', + }, }) export const statesMessages = defineMessages({ diff --git a/libs/application/templates/social-insurance-administration/old-age-pension/src/dataProviders/index.ts b/libs/application/templates/social-insurance-administration/old-age-pension/src/dataProviders/index.ts index e61226395830..8fabc0f9086b 100644 --- a/libs/application/templates/social-insurance-administration/old-age-pension/src/dataProviders/index.ts +++ b/libs/application/templates/social-insurance-administration/old-age-pension/src/dataProviders/index.ts @@ -34,3 +34,9 @@ export const SocialInsuranceAdministrationCurrenciesApi = defineTemplateApi({ externalDataId: 'socialInsuranceAdministrationCurrencies', namespace: 'SocialInsuranceAdministration', }) + +export const SocialInsuranceAdministrationLatestIncomePlan = defineTemplateApi({ + action: 'getLatestIncomePlan', + externalDataId: 'socialInsuranceAdministrationLatestIncomePlan', + namespace: 'SocialInsuranceAdministration', +}) diff --git a/libs/application/templates/social-insurance-administration/old-age-pension/src/fields/Conclusion/index.tsx b/libs/application/templates/social-insurance-administration/old-age-pension/src/fields/Conclusion/index.tsx new file mode 100644 index 000000000000..f9a2da047eab --- /dev/null +++ b/libs/application/templates/social-insurance-administration/old-age-pension/src/fields/Conclusion/index.tsx @@ -0,0 +1,104 @@ +import { + FieldBaseProps, + FieldComponents, + FieldTypes, +} from '@island.is/application/types' +import { Markdown } from '@island.is/shared/components' +import { + Box, + AlertMessage, + AccordionCard, + BulletList, +} from '@island.is/island-ui/core' +import { useLocale } from '@island.is/localization' +import { FC } from 'react' +import { MessageWithLinkButtonFormField } from '@island.is/application/ui-fields' +import { socialInsuranceAdministrationMessage } from '@island.is/application/templates/social-insurance-administration-core/lib/messages' +import { getApplicationExternalData } from '../../lib/oldAgePensionUtils' +import { oldAgePensionFormMessage } from '../../lib/messages' +import { coreMessages } from '@island.is/application/core' + +export const Conclusion: FC> = ( + props, +) => { + const { application } = props + const { formatMessage } = useLocale() + + const { hasIncomePlanStatus } = getApplicationExternalData( + application.externalData, + ) + + return ( + + + + + + + + + {formatMessage( + oldAgePensionFormMessage.conclusionScreen.nextStepsText, + )} + + + + + {formatMessage( + oldAgePensionFormMessage.conclusionScreen.bulletList, + )} + + + + + + + + + ) +} diff --git a/libs/application/templates/social-insurance-administration/old-age-pension/src/fields/index.ts b/libs/application/templates/social-insurance-administration/old-age-pension/src/fields/index.ts index b61a866f7ec2..49440420ab7b 100644 --- a/libs/application/templates/social-insurance-administration/old-age-pension/src/fields/index.ts +++ b/libs/application/templates/social-insurance-administration/old-age-pension/src/fields/index.ts @@ -2,3 +2,4 @@ export { ResidenceHistory } from './ResidenceHistory' export { Review } from './Review' export { default as EmployersOverview } from './EmployersOverview' export { default as EmployersRatioMonthly } from './EmployersRatioMonthly' +export { Conclusion } from './Conclusion' diff --git a/libs/application/templates/social-insurance-administration/old-age-pension/src/forms/OldAgePensionForm.ts b/libs/application/templates/social-insurance-administration/old-age-pension/src/forms/OldAgePensionForm.ts index 2d086a88fa4f..cf01c8a2b190 100644 --- a/libs/application/templates/social-insurance-administration/old-age-pension/src/forms/OldAgePensionForm.ts +++ b/libs/application/templates/social-insurance-administration/old-age-pension/src/forms/OldAgePensionForm.ts @@ -39,10 +39,7 @@ import { NO, YES, } from '@island.is/application/types' -import { - applicantInformationMultiField, - buildFormConclusionSection, -} from '@island.is/application/ui-forms' +import { applicantInformationMultiField } from '@island.is/application/ui-forms' import isEmpty from 'lodash/isEmpty' import { ApplicationType, Employment, RatioType } from '../lib/constants' import { oldAgePensionFormMessage } from '../lib/messages' @@ -787,27 +784,32 @@ export const OldAgePensionForm: Form = buildForm({ }), ], }), - buildFormConclusionSection({ - multiFieldTitle: - socialInsuranceAdministrationMessage.conclusionScreen - .receivedAwaitingIncomePlanTitle, - alertTitle: - socialInsuranceAdministrationMessage.conclusionScreen - .receivedAwaitingIncomePlanTitle, - alertMessage: - socialInsuranceAdministrationMessage.conclusionScreen - .incomePlanAlertMessage, - alertType: 'warning', - expandableDescription: - oldAgePensionFormMessage.conclusionScreen.bulletList, - expandableIntro: oldAgePensionFormMessage.conclusionScreen.nextStepsText, - bottomButtonLink: 'https://minarsidur.tr.is/forsendur/tekjuaetlun', - bottomButtonLabel: - socialInsuranceAdministrationMessage.conclusionScreen - .incomePlanCardLabel, - bottomButtonMessage: - socialInsuranceAdministrationMessage.conclusionScreen - .incomePlanCardText, + buildSection({ + id: 'conclusionSection', + title: socialInsuranceAdministrationMessage.conclusionScreen.section, + children: [ + buildMultiField({ + id: 'conclusion.multifield', + title: (application: Application) => { + const { hasIncomePlanStatus } = getApplicationExternalData( + application.externalData, + ) + return hasIncomePlanStatus + ? socialInsuranceAdministrationMessage.conclusionScreen + .receivedTitle + : socialInsuranceAdministrationMessage.conclusionScreen + .receivedAwaitingIncomePlanTitle + }, + children: [ + buildCustomField({ + component: 'Conclusion', + id: 'conclusion', + title: '', + description: '', + }), + ], + }), + ], }), ], }) diff --git a/libs/application/templates/social-insurance-administration/old-age-pension/src/forms/Prerequisites.ts b/libs/application/templates/social-insurance-administration/old-age-pension/src/forms/Prerequisites.ts index e614d43380c3..66bb30ca3601 100644 --- a/libs/application/templates/social-insurance-administration/old-age-pension/src/forms/Prerequisites.ts +++ b/libs/application/templates/social-insurance-administration/old-age-pension/src/forms/Prerequisites.ts @@ -35,6 +35,7 @@ import { SocialInsuranceAdministrationIsApplicantEligibleApi, SocialInsuranceAdministrationApplicantApi, SocialInsuranceAdministrationCurrenciesApi, + SocialInsuranceAdministrationLatestIncomePlan, } from '../dataProviders' export const PrerequisitesForm: Form = buildForm({ @@ -163,6 +164,10 @@ export const PrerequisitesForm: Form = buildForm({ provider: SocialInsuranceAdministrationCurrenciesApi, title: '', }), + buildDataProviderItem({ + provider: SocialInsuranceAdministrationLatestIncomePlan, + title: '', + }), ], }), ], diff --git a/libs/application/templates/social-insurance-administration/old-age-pension/src/lib/OldAgePensionTemplate.ts b/libs/application/templates/social-insurance-administration/old-age-pension/src/lib/OldAgePensionTemplate.ts index eb2343ca151d..493e6f50f44b 100644 --- a/libs/application/templates/social-insurance-administration/old-age-pension/src/lib/OldAgePensionTemplate.ts +++ b/libs/application/templates/social-insurance-administration/old-age-pension/src/lib/OldAgePensionTemplate.ts @@ -38,6 +38,7 @@ import { SocialInsuranceAdministrationIsApplicantEligibleApi, SocialInsuranceAdministrationApplicantApi, SocialInsuranceAdministrationCurrenciesApi, + SocialInsuranceAdministrationLatestIncomePlan, } from '../dataProviders' import { determineNameFromApplicationAnswers, @@ -97,6 +98,7 @@ const OldAgePensionTemplate: ApplicationTemplate< SocialInsuranceAdministrationIsApplicantEligibleApi, SocialInsuranceAdministrationApplicantApi, SocialInsuranceAdministrationCurrenciesApi, + SocialInsuranceAdministrationLatestIncomePlan, ], delete: true, }, diff --git a/libs/application/templates/social-insurance-administration/old-age-pension/src/lib/messages.ts b/libs/application/templates/social-insurance-administration/old-age-pension/src/lib/messages.ts index 47a792a7f72c..4ecdcbc26838 100644 --- a/libs/application/templates/social-insurance-administration/old-age-pension/src/lib/messages.ts +++ b/libs/application/templates/social-insurance-administration/old-age-pension/src/lib/messages.ts @@ -261,6 +261,18 @@ export const oldAgePensionFormMessage: MessageDir = { }), conclusionScreen: defineMessages({ + title: { + id: 'oap.application:conclusionScreen.expandableDescriptionField.title', + defaultMessage: 'HvaĂ° gerist nĂŠst?', + description: 'What happens next', + }, + alertMessage: { + id: 'oap.application:conclusionScreen.alertMessage', + defaultMessage: + 'UmsĂłkn um ellilĂ­feyri hefur veriĂ° send til Tryggingastofnunar', + description: + 'An application for old age pension has been sent to the Social Insurance Administration', + }, bulletList: { id: `oap.application:conclusionScreen.bulletList#markdown`, defaultMessage: `* ÞĂș verĂ°ur aĂ° skila inn tekjuĂĄĂŠtlun, ef ekki bĂșiĂ° nĂș ĂŸegar.\n* Tryggingastofnun fer yfir umsĂłknina og staĂ°festir aĂ° allar upplĂœsingar eru rĂ©ttar.\n* Ef ĂŸĂ¶rf er ĂĄ er kallaĂ° eftir frekari upplĂœsingum/gögnum.\n* Þegar öll nauĂ°synleg gögn hafa borist, fer Tryggingastofnun yfir umsĂłkn og er afstaĂ°a tekin til elllĂ­feyris. VinnslutĂ­mi umsĂłkna um ellilĂ­feyri er fjĂłrar til sex vikur.\n* **ÞĂș gĂŠtir ĂĄtt rĂ©tt ĂĄ:**\n\t* HeimilisuppbĂłt\n\t* BarnalĂ­feyri\n\t* UppbĂłt ĂĄ lĂ­feyri\n\t* EllilĂ­feyri vegna EES.`, diff --git a/libs/application/templates/social-insurance-administration/old-age-pension/src/lib/oldAgePensionUtils.ts b/libs/application/templates/social-insurance-administration/old-age-pension/src/lib/oldAgePensionUtils.ts index 7ddec140d8ad..23dc874ee38f 100644 --- a/libs/application/templates/social-insurance-administration/old-age-pension/src/lib/oldAgePensionUtils.ts +++ b/libs/application/templates/social-insurance-administration/old-age-pension/src/lib/oldAgePensionUtils.ts @@ -254,6 +254,11 @@ export const getApplicationExternalData = ( 'socialInsuranceAdministrationCurrencies.data', ) as Array + const hasIncomePlanStatus = getValueViaPath( + externalData, + 'socialInsuranceAdministrationLatestIncomePlan.data.status', + ) as string + return { residenceHistory, applicantName, @@ -268,6 +273,7 @@ export const getApplicationExternalData = ( currencies, userProfileEmail, userProfilePhoneNumber, + hasIncomePlanStatus, } } From f3a5069610c8ebfd9b751c3c59014fa1605a0ae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAnar=20Vestmann?= <43557895+RunarVestmann@users.noreply.github.com> Date: Thu, 5 Dec 2024 12:53:23 +0000 Subject: [PATCH 33/85] feat(web): WHODAS calculator - Change how we calculate score (#17139) * Change how we calculate score * Add text to bottom of form * Add fallback for calculating total score * Simplify conditionals * Make sure printing does not make results go on two pages --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../connected/WHODAS/Calculator.css.ts | 14 +- .../connected/WHODAS/Calculator.strings.ts | 22 +-- .../connected/WHODAS/Calculator.tsx | 165 +++++++++++------- 3 files changed, 126 insertions(+), 75 deletions(-) diff --git a/apps/web/components/connected/WHODAS/Calculator.css.ts b/apps/web/components/connected/WHODAS/Calculator.css.ts index c98463701223..81dca8be11dc 100644 --- a/apps/web/components/connected/WHODAS/Calculator.css.ts +++ b/apps/web/components/connected/WHODAS/Calculator.css.ts @@ -2,8 +2,20 @@ import { style } from '@vanilla-extract/css' import { theme } from '@island.is/island-ui/theme' +const leftWidth = 180 + export const breakdownRowContainer = style({ display: 'grid', - gridTemplateColumns: '180px 1fr', + gridTemplateColumns: `${leftWidth}px 1fr`, gap: theme.spacing[5], }) + +export const totalScoreRowContainer = style({ + display: 'grid', + gridTemplateColumns: `${leftWidth + 24}px 1fr`, + gap: theme.spacing[5], +}) + +export const stayOnSinglePageWhenPrinting = style({ + pageBreakInside: 'avoid', +}) diff --git a/apps/web/components/connected/WHODAS/Calculator.strings.ts b/apps/web/components/connected/WHODAS/Calculator.strings.ts index 5794e1786e71..8d81c1bb4750 100644 --- a/apps/web/components/connected/WHODAS/Calculator.strings.ts +++ b/apps/web/components/connected/WHODAS/Calculator.strings.ts @@ -62,6 +62,18 @@ export const m = { 'Takk fyrir aĂ° svara spurningalistanum, Mat ĂĄ fĂŠrni ĂŸinni. Mat ĂŸitt ĂĄ fĂŠrni er stuĂ°ningur viĂ° aĂ° meta ĂŸĂ¶rf ĂŸĂ­na fyrir heimaĂŸjĂłnustu. Ef ĂŸĂș hefur ekki svaraĂ° öllum spurningum getur ĂŸaĂ° haft ĂĄhrif ĂĄ niĂ°urstöðuna.', description: 'Texti fyrir ofan niĂ°urstöðuskjĂĄ', }, + totalScore: { + id: 'web.whodas.calculator:results.totalScore', + defaultMessage: 'Stig samtals', + description: 'Stig samtals', + }, + resultDisclaimer: { + id: 'web.whodas.calculator:results.resultDisclaimer', + defaultMessage: + 'Gott er aĂ° prenta Ășt eĂ°a senda sĂ©r svariĂ°. ÞaĂ° getur gangast viĂ° umsĂłkn um heimaĂŸjĂłnustu.', + description: + 'Gott er aĂ° prenta Ășt eĂ°a senda sĂ©r svariĂ°. ÞaĂ° getur gangast viĂ° umsĂłkn um heimaĂŸjĂłnustu.', + }, mainHeading: { id: 'web.whodas.calculator:form.mainHeading', defaultMessage: 'NiĂ°urstaĂ°a mats ĂĄ fĂŠrni', @@ -77,16 +89,6 @@ export const m = { defaultMessage: 'Heildarstig', description: 'Heildarstig', }, - firstBracketScoreText: { - id: 'web.whodas.calculator:form.firstBracketScoreText', - defaultMessage: '0 til 16,9 stig', - description: 'LĂ­til skerĂ°ing ĂĄ fĂŠrni - "Score" texti', - }, - secondBracketScoreText: { - id: 'web.whodas.calculator:form.secondBracketScoreText', - defaultMessage: '17 til 100 stig', - description: 'TalsverĂ° skerĂ°ing ĂĄ fĂŠrni - "Score" texti', - }, firstBracketInterpretationText: { id: 'web.whodas.calculator:form.firstBracketInterpretationText', defaultMessage: 'LĂ­til skerĂ°ing ĂĄ fĂŠrni', diff --git a/apps/web/components/connected/WHODAS/Calculator.tsx b/apps/web/components/connected/WHODAS/Calculator.tsx index 99ff866974b7..109eb573ccfa 100644 --- a/apps/web/components/connected/WHODAS/Calculator.tsx +++ b/apps/web/components/connected/WHODAS/Calculator.tsx @@ -6,6 +6,7 @@ import { useState, } from 'react' import { useIntl } from 'react-intl' +import round from 'lodash/round' import { Box, @@ -25,6 +26,10 @@ import { MarkdownText } from '../../Organization' import { m } from './Calculator.strings' import * as styles from './Calculator.css' +const formatScore = (score: number) => { + return String(round(score, 1)).replace('.', ',') +} + interface Question { question: string answerOptions: { @@ -41,6 +46,7 @@ interface Step { interface CheckboxState { steps: { title: string + maxScorePossible: number questions: { selectedAnswerIndex: number answerScore: number @@ -119,74 +125,73 @@ interface WHODASResultsProps { steps: (Pick & { scoreForStep: number })[] } bracket: 1 | 2 + totalScore: number } -const WHODASResults = ({ results, bracket }: WHODASResultsProps) => { +const WHODASResults = ({ + results, + bracket, + totalScore, +}: WHODASResultsProps) => { const { format } = useDateUtils() const date = format(new Date(), 'do MMMM yyyy') const { formatMessage } = useIntl() return ( - - - - {formatMessage(m.results.mainHeading)} - - - - {date} - - - {formatMessage(m.results.scoreHeading)} - - + + + + + {formatMessage(m.results.mainHeading)} + + + + {date} + + + {formatMessage(m.results.scoreHeading)} + + + {formatScore(totalScore)} + + + + + {formatMessage(m.results.interpretationHeading)} + {formatMessage( bracket === 1 - ? m.results.firstBracketScoreText - : m.results.secondBracketScoreText, + ? m.results.firstBracketInterpretationText + : m.results.secondBracketInterpretationText, )} - - - - - {formatMessage(m.results.interpretationHeading)} - - - {formatMessage( - bracket === 1 - ? m.results.firstBracketInterpretationText - : m.results.secondBracketInterpretationText, - )} - - - - - - {formatMessage(m.results.adviceHeading)} - - - {formatMessage( - bracket === 1 - ? m.results.firstBracketAdviceText - : m.results.secondBracketAdviceText, - )} - - + - {bracket !== 1 && ( + + {formatMessage(m.results.adviceHeading)} + + + {formatMessage( + bracket === 1 + ? m.results.firstBracketAdviceText + : m.results.secondBracketAdviceText, + )} + + + + {formatMessage(m.results.breakdownHeading)} @@ -195,13 +200,27 @@ const WHODASResults = ({ results, bracket }: WHODASResultsProps) => { {step.title} - {step.scoreForStep} + {formatScore(step.scoreForStep)} ))} + + + {formatMessage(m.results.totalScore)} + + {formatScore(totalScore)} + - )} + + + {formatMessage(m.results.resultDisclaimer)} + ) } @@ -225,6 +244,13 @@ export const WHODASCalculator = ({ slice }: WHODASCalculatorProps) => { steps: steps.map(({ title, description, questions }) => ({ title, description, + maxScorePossible: questions.reduce( + (prev, acc) => + prev + acc.answerOptions.length > 0 + ? acc.answerOptions[acc.answerOptions.length - 1].score + : 0, + 0, + ), questions: questions.map(() => ({ selectedAnswerIndex: 0, answerScore: 0, @@ -245,30 +271,41 @@ export const WHODASCalculator = ({ slice }: WHODASCalculatorProps) => { if (showResults) { let totalScore = 0 + let totalMaxScorePossible = 0 const results: WHODASResultsProps['results'] = { steps: [], } for (const stateStep of state.steps) { + totalMaxScorePossible += stateStep.maxScorePossible let score = 0 for (const question of stateStep.questions) { score += question.answerScore } - results.steps.push({ ...stateStep, scoreForStep: score }) totalScore += score + if (stateStep.maxScorePossible > 0) { + score = (score * 100) / stateStep.maxScorePossible + } + results.steps.push({ ...stateStep, scoreForStep: score }) + } + if (totalMaxScorePossible > 0) { + totalScore = (totalScore * 100) / totalMaxScorePossible } return ( {formatMessage(m.results.topDescription)} - + + + ) } From 4ff38652689212853d7777bc337c640c5368a8e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93lafur=20Bj=C3=B6rn=20Magn=C3=BAsson?= Date: Thu, 5 Dec 2024 13:05:19 +0000 Subject: [PATCH 34/85] chore(application-system-api-worker): Add missing user notification url (#17140) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../api/infra/application-system-api.ts | 151 +++++++++--------- charts/islandis/values.dev.yaml | 1 + charts/islandis/values.prod.yaml | 1 + charts/islandis/values.staging.yaml | 1 + .../values.dev.yaml | 1 + .../values.prod.yaml | 1 + .../values.staging.yaml | 1 + infra/src/uber-charts/islandis.ts | 4 +- 8 files changed, 86 insertions(+), 75 deletions(-) diff --git a/apps/application-system/api/infra/application-system-api.ts b/apps/application-system/api/infra/application-system-api.ts index 0c3b5c273739..9cb0aa3406ea 100644 --- a/apps/application-system/api/infra/application-system-api.ts +++ b/apps/application-system/api/infra/application-system-api.ts @@ -49,80 +49,83 @@ export const GRAPHQL_API_URL_ENV_VAR_NAME = 'GRAPHQL_API_URL' // This property i const namespace = 'application-system' const serviceAccount = 'application-system-api' -export const workerSetup = - (): ServiceBuilder<'application-system-api-worker'> => - service('application-system-api-worker') - .namespace(namespace) - .image('application-system-api') - .db() - .serviceAccount('application-system-api-worker') - .redis() - .codeOwner(CodeOwners.NordaApplications) - .env({ - IDENTITY_SERVER_CLIENT_ID: '@island.is/clients/application-system', - IDENTITY_SERVER_ISSUER_URL: { - dev: 'https://identity-server.dev01.devland.is', - staging: 'https://identity-server.staging01.devland.is', - prod: 'https://innskra.island.is', - }, - XROAD_CHARGE_FJS_V2_PATH: { - dev: 'IS-DEV/GOV/10021/FJS-Public/chargeFJS_v2', - staging: 'IS-TEST/GOV/10021/FJS-Public/chargeFJS_v2', - prod: 'IS/GOV/5402697509/FJS-Public/chargeFJS_v2', - }, - APPLICATION_ATTACHMENT_BUCKET: { - dev: 'island-is-dev-storage-application-system', - staging: 'island-is-staging-storage-application-system', - prod: 'island-is-prod-storage-application-system', - }, - FILE_SERVICE_PRESIGN_BUCKET: { - dev: 'island-is-dev-fs-presign-bucket', - staging: 'island-is-staging-fs-presign-bucket', - prod: 'island-is-prod-fs-presign-bucket', - }, - FILE_STORAGE_UPLOAD_BUCKET: { - dev: 'island-is-dev-upload-api', - staging: 'island-is-staging-upload-api', - prod: 'island-is-prod-upload-api', - }, - CLIENT_LOCATION_ORIGIN: { - dev: 'https://beta.dev01.devland.is/umsoknir', - staging: 'https://beta.staging01.devland.is/umsoknir', - prod: 'https://island.is/umsoknir', - local: 'http://localhost:4200/umsoknir', - }, - }) - .xroad(Base, Client, Payment, Inna, EHIC, WorkMachines) - .secrets({ - IDENTITY_SERVER_CLIENT_SECRET: - '/k8s/application-system/api/IDENTITY_SERVER_CLIENT_SECRET', - SYSLUMENN_HOST: '/k8s/application-system-api/SYSLUMENN_HOST', - SYSLUMENN_USERNAME: '/k8s/application-system/api/SYSLUMENN_USERNAME', - SYSLUMENN_PASSWORD: '/k8s/application-system/api/SYSLUMENN_PASSWORD', - DRIVING_LICENSE_BOOK_XROAD_PATH: - '/k8s/application-system-api/DRIVING_LICENSE_BOOK_XROAD_PATH', - DRIVING_LICENSE_BOOK_USERNAME: - '/k8s/application-system-api/DRIVING_LICENSE_BOOK_USERNAME', - DRIVING_LICENSE_BOOK_PASSWORD: - '/k8s/application-system-api/DRIVING_LICENSE_BOOK_PASSWORD', - DOKOBIT_ACCESS_TOKEN: - '/k8s/application-system/api/DOKOBIT_ACCESS_TOKEN', - DOKOBIT_URL: '/k8s/application-system-api/DOKOBIT_URL', - ARK_BASE_URL: '/k8s/application-system-api/ARK_BASE_URL', - DOMSYSLA_PASSWORD: '/k8s/application-system-api/DOMSYSLA_PASSWORD', - DOMSYSLA_USERNAME: '/k8s/application-system-api/DOMSYSLA_USERNAME', - }) - .args('main.js', '--job', 'worker') - .command('node') - .extraAttributes({ - dev: { schedule: '*/30 * * * *' }, - staging: { schedule: '*/30 * * * *' }, - prod: { schedule: '*/30 * * * *' }, - }) - .resources({ - limits: { cpu: '400m', memory: '768Mi' }, - requests: { cpu: '150m', memory: '384Mi' }, - }) +export const workerSetup = (services: { + userNotificationService: ServiceBuilder<'services-user-notification'> +}): ServiceBuilder<'application-system-api-worker'> => + service('application-system-api-worker') + .namespace(namespace) + .image('application-system-api') + .db() + .serviceAccount('application-system-api-worker') + .redis() + .codeOwner(CodeOwners.NordaApplications) + .env({ + IDENTITY_SERVER_CLIENT_ID: '@island.is/clients/application-system', + IDENTITY_SERVER_ISSUER_URL: { + dev: 'https://identity-server.dev01.devland.is', + staging: 'https://identity-server.staging01.devland.is', + prod: 'https://innskra.island.is', + }, + XROAD_CHARGE_FJS_V2_PATH: { + dev: 'IS-DEV/GOV/10021/FJS-Public/chargeFJS_v2', + staging: 'IS-TEST/GOV/10021/FJS-Public/chargeFJS_v2', + prod: 'IS/GOV/5402697509/FJS-Public/chargeFJS_v2', + }, + APPLICATION_ATTACHMENT_BUCKET: { + dev: 'island-is-dev-storage-application-system', + staging: 'island-is-staging-storage-application-system', + prod: 'island-is-prod-storage-application-system', + }, + FILE_SERVICE_PRESIGN_BUCKET: { + dev: 'island-is-dev-fs-presign-bucket', + staging: 'island-is-staging-fs-presign-bucket', + prod: 'island-is-prod-fs-presign-bucket', + }, + FILE_STORAGE_UPLOAD_BUCKET: { + dev: 'island-is-dev-upload-api', + staging: 'island-is-staging-upload-api', + prod: 'island-is-prod-upload-api', + }, + CLIENT_LOCATION_ORIGIN: { + dev: 'https://beta.dev01.devland.is/umsoknir', + staging: 'https://beta.staging01.devland.is/umsoknir', + prod: 'https://island.is/umsoknir', + local: 'http://localhost:4200/umsoknir', + }, + USER_NOTIFICATION_API_URL: ref( + (h) => `http://${h.svc(services.userNotificationService)}`, + ), + }) + .xroad(Base, Client, Payment, Inna, EHIC, WorkMachines) + .secrets({ + IDENTITY_SERVER_CLIENT_SECRET: + '/k8s/application-system/api/IDENTITY_SERVER_CLIENT_SECRET', + SYSLUMENN_HOST: '/k8s/application-system-api/SYSLUMENN_HOST', + SYSLUMENN_USERNAME: '/k8s/application-system/api/SYSLUMENN_USERNAME', + SYSLUMENN_PASSWORD: '/k8s/application-system/api/SYSLUMENN_PASSWORD', + DRIVING_LICENSE_BOOK_XROAD_PATH: + '/k8s/application-system-api/DRIVING_LICENSE_BOOK_XROAD_PATH', + DRIVING_LICENSE_BOOK_USERNAME: + '/k8s/application-system-api/DRIVING_LICENSE_BOOK_USERNAME', + DRIVING_LICENSE_BOOK_PASSWORD: + '/k8s/application-system-api/DRIVING_LICENSE_BOOK_PASSWORD', + DOKOBIT_ACCESS_TOKEN: '/k8s/application-system/api/DOKOBIT_ACCESS_TOKEN', + DOKOBIT_URL: '/k8s/application-system-api/DOKOBIT_URL', + ARK_BASE_URL: '/k8s/application-system-api/ARK_BASE_URL', + DOMSYSLA_PASSWORD: '/k8s/application-system-api/DOMSYSLA_PASSWORD', + DOMSYSLA_USERNAME: '/k8s/application-system-api/DOMSYSLA_USERNAME', + }) + .args('main.js', '--job', 'worker') + .command('node') + .extraAttributes({ + dev: { schedule: '*/30 * * * *' }, + staging: { schedule: '*/30 * * * *' }, + prod: { schedule: '*/30 * * * *' }, + }) + .resources({ + limits: { cpu: '400m', memory: '768Mi' }, + requests: { cpu: '150m', memory: '384Mi' }, + }) export const serviceSetup = (services: { documentsService: ServiceBuilder<'services-documents'> diff --git a/charts/islandis/values.dev.yaml b/charts/islandis/values.dev.yaml index 0a6d35e252cf..0dd1634ad8ad 100644 --- a/charts/islandis/values.dev.yaml +++ b/charts/islandis/values.dev.yaml @@ -852,6 +852,7 @@ application-system-api-worker: NODE_OPTIONS: '--max-old-space-size=691 -r dd-trace/init' REDIS_URL_NODE_01: '["clustercfg.general-redis-cluster-group.5fzau3.euw1.cache.amazonaws.com:6379"]' SERVERSIDE_FEATURES_ON: '' + USER_NOTIFICATION_API_URL: 'http://web-user-notification.user-notification.svc.cluster.local' XROAD_BASE_PATH: 'http://securityserver.dev01.devland.is' XROAD_BASE_PATH_WITH_ENV: 'http://securityserver.dev01.devland.is/r1/IS-DEV' XROAD_CHARGE_FJS_V2_PATH: 'IS-DEV/GOV/10021/FJS-Public/chargeFJS_v2' diff --git a/charts/islandis/values.prod.yaml b/charts/islandis/values.prod.yaml index cbc83be17d50..04619e6f6b0a 100644 --- a/charts/islandis/values.prod.yaml +++ b/charts/islandis/values.prod.yaml @@ -840,6 +840,7 @@ application-system-api-worker: NODE_OPTIONS: '--max-old-space-size=691 -r dd-trace/init' REDIS_URL_NODE_01: '["clustercfg.general-redis-cluster-group.whakos.euw1.cache.amazonaws.com:6379"]' SERVERSIDE_FEATURES_ON: 'driving-license-use-v1-endpoint-for-v2-comms' + USER_NOTIFICATION_API_URL: 'http://web-user-notification.user-notification.svc.cluster.local' XROAD_BASE_PATH: 'http://securityserver.island.is' XROAD_BASE_PATH_WITH_ENV: 'http://securityserver.island.is/r1/IS' XROAD_CHARGE_FJS_V2_PATH: 'IS/GOV/5402697509/FJS-Public/chargeFJS_v2' diff --git a/charts/islandis/values.staging.yaml b/charts/islandis/values.staging.yaml index 36116c21bc12..a2192f932e41 100644 --- a/charts/islandis/values.staging.yaml +++ b/charts/islandis/values.staging.yaml @@ -849,6 +849,7 @@ application-system-api-worker: NODE_OPTIONS: '--max-old-space-size=691 -r dd-trace/init' REDIS_URL_NODE_01: '["clustercfg.general-redis-cluster-group.ab9ckb.euw1.cache.amazonaws.com:6379"]' SERVERSIDE_FEATURES_ON: '' + USER_NOTIFICATION_API_URL: 'http://web-user-notification.user-notification.svc.cluster.local' XROAD_BASE_PATH: 'http://securityserver.staging01.devland.is' XROAD_BASE_PATH_WITH_ENV: 'http://securityserver.staging01.devland.is/r1/IS-TEST' XROAD_CHARGE_FJS_V2_PATH: 'IS-TEST/GOV/10021/FJS-Public/chargeFJS_v2' diff --git a/charts/services/application-system-api-worker/values.dev.yaml b/charts/services/application-system-api-worker/values.dev.yaml index 5f8102c047e2..91699959fa25 100644 --- a/charts/services/application-system-api-worker/values.dev.yaml +++ b/charts/services/application-system-api-worker/values.dev.yaml @@ -41,6 +41,7 @@ env: NODE_OPTIONS: '--max-old-space-size=691 -r dd-trace/init' REDIS_URL_NODE_01: '["clustercfg.general-redis-cluster-group.5fzau3.euw1.cache.amazonaws.com:6379"]' SERVERSIDE_FEATURES_ON: '' + USER_NOTIFICATION_API_URL: 'http://web-user-notification.user-notification.svc.cluster.local' XROAD_BASE_PATH: 'http://securityserver.dev01.devland.is' XROAD_BASE_PATH_WITH_ENV: 'http://securityserver.dev01.devland.is/r1/IS-DEV' XROAD_CHARGE_FJS_V2_PATH: 'IS-DEV/GOV/10021/FJS-Public/chargeFJS_v2' diff --git a/charts/services/application-system-api-worker/values.prod.yaml b/charts/services/application-system-api-worker/values.prod.yaml index e8c6b233d0c0..e484a5d20bb8 100644 --- a/charts/services/application-system-api-worker/values.prod.yaml +++ b/charts/services/application-system-api-worker/values.prod.yaml @@ -41,6 +41,7 @@ env: NODE_OPTIONS: '--max-old-space-size=691 -r dd-trace/init' REDIS_URL_NODE_01: '["clustercfg.general-redis-cluster-group.whakos.euw1.cache.amazonaws.com:6379"]' SERVERSIDE_FEATURES_ON: 'driving-license-use-v1-endpoint-for-v2-comms' + USER_NOTIFICATION_API_URL: 'http://web-user-notification.user-notification.svc.cluster.local' XROAD_BASE_PATH: 'http://securityserver.island.is' XROAD_BASE_PATH_WITH_ENV: 'http://securityserver.island.is/r1/IS' XROAD_CHARGE_FJS_V2_PATH: 'IS/GOV/5402697509/FJS-Public/chargeFJS_v2' diff --git a/charts/services/application-system-api-worker/values.staging.yaml b/charts/services/application-system-api-worker/values.staging.yaml index 1b3b2c57186f..a654300a66ef 100644 --- a/charts/services/application-system-api-worker/values.staging.yaml +++ b/charts/services/application-system-api-worker/values.staging.yaml @@ -41,6 +41,7 @@ env: NODE_OPTIONS: '--max-old-space-size=691 -r dd-trace/init' REDIS_URL_NODE_01: '["clustercfg.general-redis-cluster-group.ab9ckb.euw1.cache.amazonaws.com:6379"]' SERVERSIDE_FEATURES_ON: '' + USER_NOTIFICATION_API_URL: 'http://web-user-notification.user-notification.svc.cluster.local' XROAD_BASE_PATH: 'http://securityserver.staging01.devland.is' XROAD_BASE_PATH_WITH_ENV: 'http://securityserver.staging01.devland.is/r1/IS-TEST' XROAD_CHARGE_FJS_V2_PATH: 'IS-TEST/GOV/10021/FJS-Public/chargeFJS_v2' diff --git a/infra/src/uber-charts/islandis.ts b/infra/src/uber-charts/islandis.ts index 705e3c1113f0..569b921bc2bf 100644 --- a/infra/src/uber-charts/islandis.ts +++ b/infra/src/uber-charts/islandis.ts @@ -85,7 +85,9 @@ const appSystemApi = appSystemApiSetup({ servicePortalApi, userNotificationService, }) -const appSystemApiWorker = appSystemApiWorkerSetup() +const appSystemApiWorker = appSystemApiWorkerSetup({ + userNotificationService, +}) const nameRegistryBackend = serviceNameRegistryBackendSetup() From 40991f36835b7bf4d96a388fdcb8b0234b2b7dac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAnar=20Vestmann?= <43557895+RunarVestmann@users.noreply.github.com> Date: Thu, 5 Dec 2024 13:16:29 +0000 Subject: [PATCH 35/85] fix(web): Generic List - Reset page number when filters are changed (#17141) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- apps/web/components/GenericList/GenericList.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/web/components/GenericList/GenericList.tsx b/apps/web/components/GenericList/GenericList.tsx index d717d1616388..bbc7592e5172 100644 --- a/apps/web/components/GenericList/GenericList.tsx +++ b/apps/web/components/GenericList/GenericList.tsx @@ -311,6 +311,7 @@ export const GenericList = ({ key={value} active={true} onClick={() => { + setPage(null) setParameters((prevParameters) => { const updatedParameters = { ...prevParameters, @@ -465,6 +466,7 @@ export const GenericList = ({ if (!category) { return } + setPage(null) setParameters((prevParameters) => ({ ...prevParameters, [category]: ( From d5d438371c72772cd483c33d261ff14c5737c836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gunnlaugur=20Gu=C3=B0mundsson?= <34029342+GunnlaugurG@users.noreply.github.com> Date: Thu, 5 Dec 2024 13:49:19 +0000 Subject: [PATCH 36/85] feat(ids-admin): General Mandate delegation for companies (#17026) * initial push for company general mandate * added tests for company general mandate * merge conflict with main resolved * chore: nx format:write update dirty files * fix executor command for dev --------- Co-authored-by: andes-it Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- apps/services/auth/admin-api/project.json | 11 + .../services/auth/delegation-api/project.json | 11 + apps/services/auth/ids-api/project.json | 11 + .../test/delegations.controller.spec.ts | 263 +++++++++++++++--- .../auth/ids-api/test/stubs/genericStubs.ts | 3 + .../admin/delegation-admin-custom.service.ts | 13 +- .../delegations/delegation-scope.service.ts | 10 +- .../delegations-incoming-custom.service.ts | 35 ++- .../delegations-incoming.service.ts | 24 ++ 9 files changed, 339 insertions(+), 42 deletions(-) diff --git a/apps/services/auth/admin-api/project.json b/apps/services/auth/admin-api/project.json index 2866faa0019a..e5d91fd92127 100644 --- a/apps/services/auth/admin-api/project.json +++ b/apps/services/auth/admin-api/project.json @@ -57,6 +57,17 @@ }, "docker-express": { "executor": "Intentionally left blank, only so this target is valid when using `nx show projects --with-target docker-express`" + }, + "dev": { + "executor": "nx:run-commands", + "options": { + "commands": [ + { + "command": "yarn start --project services-auth-admin-api" + } + ], + "parallel": true + } } } } diff --git a/apps/services/auth/delegation-api/project.json b/apps/services/auth/delegation-api/project.json index 366532998b91..66890516fc3c 100644 --- a/apps/services/auth/delegation-api/project.json +++ b/apps/services/auth/delegation-api/project.json @@ -28,6 +28,17 @@ "buildTarget": "services-auth-delegation-api:build" } }, + "dev": { + "executor": "nx:run-commands", + "options": { + "commands": [ + { + "command": "yarn start services-auth-delegation-api" + } + ], + "parallel": true + } + }, "lint": { "executor": "@nx/eslint:lint" }, diff --git a/apps/services/auth/ids-api/project.json b/apps/services/auth/ids-api/project.json index cfefdd77acda..b64e70508680 100644 --- a/apps/services/auth/ids-api/project.json +++ b/apps/services/auth/ids-api/project.json @@ -175,6 +175,17 @@ ], "parallel": true } + }, + "dev": { + "executor": "nx:run-commands", + "options": { + "commands": [ + { + "command": "yarn start services-auth-ids-api" + } + ], + "parallel": true + } } } } diff --git a/apps/services/auth/ids-api/src/app/delegations/test/delegations.controller.spec.ts b/apps/services/auth/ids-api/src/app/delegations/test/delegations.controller.spec.ts index f2dee16513d6..faee06788837 100644 --- a/apps/services/auth/ids-api/src/app/delegations/test/delegations.controller.spec.ts +++ b/apps/services/auth/ids-api/src/app/delegations/test/delegations.controller.spec.ts @@ -1,7 +1,7 @@ import { getModelToken } from '@nestjs/sequelize' -import addDays from 'date-fns/addDays' import request from 'supertest' import { uuid } from 'uuidv4' +import addDays from 'date-fns/addDays' import { ApiScope, @@ -27,12 +27,16 @@ import { } from '@island.is/shared/types' import { createCurrentUser, + createNationalId, createNationalRegistryUser, } from '@island.is/testing/fixtures' import { TestApp } from '@island.is/testing/nest' import { defaultScopes, setupWithAuth } from '../../../../test/setup' -import { getFakeNationalId } from '../../../../test/stubs/genericStubs' +import { + getFakeCompanyNationalId, + getFakeNationalId, +} from '../../../../test/stubs/genericStubs' describe('DelegationsController', () => { describe.each([false, true])( @@ -57,6 +61,8 @@ describe('DelegationsController', () => { clientId: '@island.is/webapp', }) + const representeeNationalId = createNationalId('person') + const scopeValid1 = 'scope/valid1' const scopeValid2 = 'scope/valid2' const scopeValid1and2 = 'scope/valid1and2' @@ -76,7 +82,7 @@ describe('DelegationsController', () => { scopeName: s, })) - const userNationalId = getFakeNationalId() + const userNationalId = createNationalId('person') const user = createCurrentUser({ nationalId: userNationalId, @@ -85,6 +91,8 @@ describe('DelegationsController', () => { }) const domain = createDomain() + let nationalRegistryApiSpy: jest.SpyInstance + let nationalRegistryV3ApiSpy: jest.SpyInstance beforeAll(async () => { app = await setupWithAuth({ @@ -117,36 +125,214 @@ describe('DelegationsController', () => { >(getModelToken(DelegationDelegationType)) nationalRegistryApi = app.get(NationalRegistryClientService) nationalRegistryV3Api = app.get(NationalRegistryV3ClientService) + factory = new FixtureFactory(app) + + client.supportedDelegationTypes = [ + AuthDelegationType.GeneralMandate, + AuthDelegationType.LegalGuardian, + AuthDelegationType.ProcurationHolder, + ] + await factory.createClient(client) + + nationalRegistryApiSpy = jest + .spyOn(nationalRegistryApi, 'getIndividual') + .mockImplementation(async (id) => { + const user = createNationalRegistryUser({ + nationalId: representeeNationalId, + }) + + return user ?? null + }) + + nationalRegistryV3ApiSpy = jest + .spyOn(nationalRegistryV3Api, 'getAllDataIndividual') + .mockImplementation(async () => { + const user = createNationalRegistryUser({ + nationalId: representeeNationalId, + }) + + return { kennitala: user.nationalId, nafn: user.name } + }) + const nationalRegistryV3FeatureService = app.get( NationalRegistryV3FeatureService, ) jest .spyOn(nationalRegistryV3FeatureService, 'getValue') .mockImplementation(async () => featureFlag) - factory = new FixtureFactory(app) }) afterAll(async () => { await app.cleanUp() + nationalRegistryV3ApiSpy.mockClear() + nationalRegistryApiSpy.mockClear() }) - describe('GET with general mandate delegation type', () => { - const representeeNationalId = getFakeNationalId() - let nationalRegistryApiSpy: jest.SpyInstance - let nationalRegistryV3ApiSpy: jest.SpyInstance + describe('GET with general mandate delegation type for company', () => { + const companyNationalId = getFakeCompanyNationalId() + const scopeNames = [ 'api-scope/generalMandate1', 'api-scope/generalMandate2', 'api-scope/generalMandate3', + 'api-scope/procuration1', + 'api-scope/procuration2', ] beforeAll(async () => { - client.supportedDelegationTypes = [ - AuthDelegationType.GeneralMandate, - AuthDelegationType.LegalGuardian, + const delegations = await delegationModel.create({ + id: uuid(), + fromDisplayName: 'Company', + fromNationalId: companyNationalId, + toNationalId: userNationalId, + toName: 'Person', + }) + + await delegationDelegationTypeModel.create({ + delegationId: delegations.id, + delegationTypeId: AuthDelegationType.GeneralMandate, + }) + + await apiScopeModel.bulkCreate( + scopeNames.map((name) => ({ + name, + domainName: domain.name, + enabled: true, + description: `${name}: description`, + displayName: `${name}: display name`, + })), + ) + + await apiScopeDelegationTypeModel.bulkCreate([ + { + apiScopeName: scopeNames[0], + delegationType: AuthDelegationType.GeneralMandate, + }, + { + apiScopeName: scopeNames[1], + delegationType: AuthDelegationType.GeneralMandate, + }, + { + apiScopeName: scopeNames[3], + delegationType: AuthDelegationType.ProcurationHolder, + }, + { + apiScopeName: scopeNames[4], + delegationType: AuthDelegationType.ProcurationHolder, + }, + ]) + }) + + afterAll(async () => { + await apiScopeDelegationTypeModel.destroy({ + where: {}, + cascade: true, + truncate: true, + force: true, + }) + await apiScopeModel.destroy({ + where: {}, + cascade: true, + truncate: true, + force: true, + }) + await delegationDelegationTypeModel.destroy({ + where: {}, + cascade: true, + truncate: true, + force: true, + }) + await delegationModel.destroy({ + where: {}, + cascade: true, + truncate: true, + force: true, + }) + }) + + it('should return mergedDelegationDTO with the generalMandate', async () => { + const response = await server.get('/v2/delegations') + + expect(response.status).toEqual(200) + expect(response.body).toHaveLength(1) + }) + + it('should return all general mandate scopes and other preset scopes', async () => { + const response = await server.get('/delegations/scopes').query({ + fromNationalId: companyNationalId, + delegationType: [ + AuthDelegationType.GeneralMandate, + AuthDelegationType.ProcurationHolder, + ], + }) + + const expected = [ + scopeNames[0], + scopeNames[1], + scopeNames[3], + scopeNames[4], + ] + + expect(response.status).toEqual(200) + expect(response.body).toEqual(expect.arrayContaining(expected)) + expect(response.body).toHaveLength(expected.length) + }) + + it('should return all general mandate scopes and all procuration scopes', async () => { + const response = await server.get('/delegations/scopes').query({ + fromNationalId: companyNationalId, + delegationType: [AuthDelegationType.GeneralMandate], + }) + + const expected = [ + scopeNames[0], + scopeNames[1], + scopeNames[3], + scopeNames[4], ] - await factory.createClient(client) + expect(response.status).toEqual(200) + expect(response.body).toEqual(expect.arrayContaining(expected)) + expect(response.body).toHaveLength(expected.length) + }) + + it('should return all general mandate scopes, and not procuration scopes since from nationalId is person', async () => { + // Assert + const delegation = await delegationModel.create({ + id: uuid(), + fromDisplayName: 'FromPersonPerson', + fromNationalId: representeeNationalId, + toNationalId: userNationalId, + toName: 'Person', + }) + + await delegationDelegationTypeModel.create({ + delegationId: delegation.id, + delegationTypeId: AuthDelegationType.GeneralMandate, + }) + + // Act + const response = await server.get('/delegations/scopes').query({ + fromNationalId: representeeNationalId, + delegationType: [AuthDelegationType.GeneralMandate], + }) + + const expected = [scopeNames[0], scopeNames[1]] + + expect(response.status).toEqual(200) + expect(response.body).toEqual(expect.arrayContaining(expected)) + expect(response.body).toHaveLength(expected.length) + }) + }) + + describe('GET with general mandate delegation type', () => { + const scopeNames = [ + 'api-scope/generalMandate1', + 'api-scope/generalMandate2', + 'api-scope/generalMandate3', + ] + + beforeAll(async () => { const delegations = await delegationModel.create({ id: uuid(), fromDisplayName: 'Test', @@ -187,32 +373,39 @@ describe('DelegationsController', () => { delegationType: AuthDelegationType.GeneralMandate, }, ]) - - nationalRegistryApiSpy = jest - .spyOn(nationalRegistryApi, 'getIndividual') - .mockImplementation(async (id) => { - const user = createNationalRegistryUser({ - nationalId: representeeNationalId, - }) - - return user ?? null - }) - - nationalRegistryV3ApiSpy = jest - .spyOn(nationalRegistryV3Api, 'getAllDataIndividual') - .mockImplementation(async () => { - const user = createNationalRegistryUser({ - nationalId: representeeNationalId, - }) - - return { kennitala: user.nationalId, nafn: user.name } - }) }) afterAll(async () => { - await app.cleanUp() - nationalRegistryApiSpy.mockClear() - nationalRegistryV3ApiSpy.mockClear() + await apiScopeDelegationTypeModel.destroy({ + where: {}, + cascade: true, + truncate: true, + force: true, + }) + await apiScopeModel.destroy({ + where: {}, + cascade: true, + truncate: true, + force: true, + }) + await delegationProviderModel.destroy({ + where: {}, + cascade: true, + truncate: true, + force: true, + }) + await delegationDelegationTypeModel.destroy({ + where: {}, + cascade: true, + truncate: true, + force: true, + }) + await delegationModel.destroy({ + where: {}, + cascade: true, + truncate: true, + force: true, + }) }) it('should return mergedDelegationDTO with the generalMandate', async () => { diff --git a/apps/services/auth/ids-api/test/stubs/genericStubs.ts b/apps/services/auth/ids-api/test/stubs/genericStubs.ts index 91956b00fcc5..177ad31ae023 100644 --- a/apps/services/auth/ids-api/test/stubs/genericStubs.ts +++ b/apps/services/auth/ids-api/test/stubs/genericStubs.ts @@ -6,6 +6,8 @@ export type NameIdTuple = [name: string, id: string] export const getFakeNationalId = () => faker.helpers.replaceSymbolWithNumber('##########') +export const getFakeCompanyNationalId = () => createNationalId('company') + export const getFakeName = () => faker.fake('{{name.firstName}} {{name.lastName}}') @@ -18,4 +20,5 @@ export default { getFakeNationalId, getFakeName, getFakePerson, + getFakeCompanyNationalId, } diff --git a/libs/auth-api-lib/src/lib/delegations/admin/delegation-admin-custom.service.ts b/libs/auth-api-lib/src/lib/delegations/admin/delegation-admin-custom.service.ts index 5cbb11d913b5..e1de9e93df1a 100644 --- a/libs/auth-api-lib/src/lib/delegations/admin/delegation-admin-custom.service.ts +++ b/libs/auth-api-lib/src/lib/delegations/admin/delegation-admin-custom.service.ts @@ -12,6 +12,7 @@ import { TicketStatus, ZendeskService, } from '@island.is/clients/zendesk' +import { CompanyRegistryClientService } from '@island.is/clients/rsk/company-registry' import { Delegation } from '../models/delegation.model' import { DelegationAdminCustomDto } from '../dto/delegation-admin-custom.dto' @@ -45,6 +46,7 @@ export class DelegationAdminCustomService { private delegationScopeService: DelegationScopeService, private namesService: NamesService, private sequelize: Sequelize, + private rskCompanyInfoService: CompanyRegistryClientService, ) {} private getZendeskCustomFields(ticket: Ticket): { @@ -276,9 +278,7 @@ export class DelegationAdminCustomService { }) } - if ( - !(kennitala.isPerson(fromNationalId) && kennitala.isPerson(toNationalId)) - ) { + if (!kennitala.isPerson(toNationalId)) { throw new BadRequestException({ message: 'National Ids are not valid', error: ErrorCodes.INPUT_VALIDATION_INVALID_PERSON, @@ -300,7 +300,11 @@ export class DelegationAdminCustomService { }, ): Promise { const [fromDisplayName, toName] = await Promise.all([ - this.namesService.getPersonName(delegation.fromNationalId), + kennitala.isPerson(delegation.fromNationalId) + ? this.namesService.getPersonName(delegation.fromNationalId) + : this.rskCompanyInfoService + .getCompany(delegation.fromNationalId) + .then((company) => company?.name ?? ''), this.namesService.getPersonName(delegation.toNationalId), ]) @@ -310,6 +314,7 @@ export class DelegationAdminCustomService { { fromNationalId: delegation.fromNationalId, toNationalId: delegation.toNationalId, + domainName: null, }, { referenceId: delegation.referenceId, diff --git a/libs/auth-api-lib/src/lib/delegations/delegation-scope.service.ts b/libs/auth-api-lib/src/lib/delegations/delegation-scope.service.ts index d12b1d8af8e4..bb74ed0a4d6e 100644 --- a/libs/auth-api-lib/src/lib/delegations/delegation-scope.service.ts +++ b/libs/auth-api-lib/src/lib/delegations/delegation-scope.service.ts @@ -5,6 +5,7 @@ import addDays from 'date-fns/addDays' import startOfDay from 'date-fns/startOfDay' import { Op, Transaction } from 'sequelize' import { uuid } from 'uuidv4' +import * as kennitala from 'kennitala' import { SyslumennService } from '@island.is/clients/syslumenn' import { logger } from '@island.is/logging' @@ -209,7 +210,14 @@ export class DelegationScopeService { { model: ApiScopeDelegationType, where: { - delegationType: AuthDelegationType.GeneralMandate, + delegationType: { + [Op.or]: kennitala.isCompany(fromNationalId) + ? [ + AuthDelegationType.GeneralMandate, + AuthDelegationType.ProcurationHolder, + ] + : [AuthDelegationType.GeneralMandate], + }, }, }, ], diff --git a/libs/auth-api-lib/src/lib/delegations/delegations-incoming-custom.service.ts b/libs/auth-api-lib/src/lib/delegations/delegations-incoming-custom.service.ts index ee60fb61c692..e11e509095bd 100644 --- a/libs/auth-api-lib/src/lib/delegations/delegations-incoming-custom.service.ts +++ b/libs/auth-api-lib/src/lib/delegations/delegations-incoming-custom.service.ts @@ -196,17 +196,48 @@ export class DelegationsIncomingCustomService { ) } + /** + * Finds all companies that have a general mandate for the user. + * @param user + * @param clientAllowedApiScopes + * @param requireApiScopes + */ + async findCompanyGeneralMandate( + user: User, + clientAllowedApiScopes: ApiScopeInfo[], + requireApiScopes: boolean, + ): Promise { + const delegations = await this.findAllAvailableGeneralMandate( + user, + clientAllowedApiScopes, + requireApiScopes, + [AuthDelegationType.ProcurationHolder], + ) + + return delegations.filter((d) => kennitala.isCompany(d.fromNationalId)) + } + + /** + * Finds all individuals that have a general mandate for the user. + * @param user + * @param clientAllowedApiScopes + * @param requireApiScopes + * @param supportedDelegationTypes + */ async findAllAvailableGeneralMandate( user: User, clientAllowedApiScopes: ApiScopeInfo[], requireApiScopes: boolean, + supportedDelegationTypes = [AuthDelegationType.GeneralMandate], ): Promise { const customApiScopes = clientAllowedApiScopes.filter( (s) => !s.isAccessControlled && this.filterByCustomScopeRule(s) && - s.supportedDelegationTypes?.some( - (dt) => dt.delegationType === AuthDelegationType.GeneralMandate, + s.supportedDelegationTypes?.some((dt) => + supportedDelegationTypes.includes( + dt.delegationType as AuthDelegationType, + ), ), ) diff --git a/libs/auth-api-lib/src/lib/delegations/delegations-incoming.service.ts b/libs/auth-api-lib/src/lib/delegations/delegations-incoming.service.ts index b2e0775d3b48..05ddc050ee0f 100644 --- a/libs/auth-api-lib/src/lib/delegations/delegations-incoming.service.ts +++ b/libs/auth-api-lib/src/lib/delegations/delegations-incoming.service.ts @@ -176,6 +176,25 @@ export class DelegationsIncomingService { ) } + // If procuration holder is enabled, we need to get the general mandate delegations + if (types?.includes(AuthDelegationType.ProcurationHolder)) { + const isGeneralMandateDelegationEnabled = + await this.featureFlagService.getValue( + Features.isGeneralMandateDelegationEnabled, + false, + user, + ) + if (isGeneralMandateDelegationEnabled) { + delegationPromises.push( + this.delegationsIncomingCustomService.findCompanyGeneralMandate( + user, + clientAllowedApiScopes, + client.requireApiScopes, + ), + ) + } + } + if (providers.includes(AuthDelegationProvider.CompanyRegistry)) { delegationPromises.push( this.incomingDelegationsCompanyService @@ -293,6 +312,11 @@ export class DelegationsIncomingService { new Map(), ) + // Remove duplicate delegationTypes.. + mergedDelegationMap.forEach((delegation) => { + delegation.types = Array.from(new Set(delegation.types)) + }) + return [...mergedDelegationMap.values()] } From 5a1b1a6b3f4e0dae5d177a3a4c20e9772b7cc893 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnea=20R=C3=BAn=20Vignisd=C3=B3ttir?= Date: Thu, 5 Dec 2024 14:41:05 +0000 Subject: [PATCH 37/85] feat(auth-admin): Admin notifications (#16993) * display notifications in admin portal * fix type * chore: nx format:write update dirty files * use correct scope, controller should not return sensitive data * chore: nx format:write update dirty files --------- Co-authored-by: andes-it --- .../me-notifications.controller.ts | 2 +- .../notifications/notifications.controller.ts | 23 ++++ .../notifications/notifications.service.ts | 22 +++- .../tests/notifications.service.spec.ts | 4 +- .../src/lib/notifications.model.ts | 20 +++ .../src/lib/notifications.module.ts | 4 + .../src/lib/notificationsAdmin.resolver.ts | 60 +++++++++ .../src/lib/notificationsAdmin.service.ts | 52 ++++++++ .../notifications/src/utils/helpers.ts | 12 +- libs/nest/pagination/src/lib/paginate.ts | 3 + .../admin/service-desk/src/lib/messages.ts | 8 ++ .../src/screens/User/User.graphql | 21 +++ .../service-desk/src/screens/User/User.tsx | 120 +++++++++++++++++- .../src/screens/Users/Users.action.ts | 5 +- 14 files changed, 342 insertions(+), 14 deletions(-) create mode 100644 libs/api/domains/notifications/src/lib/notificationsAdmin.resolver.ts create mode 100644 libs/api/domains/notifications/src/lib/notificationsAdmin.service.ts diff --git a/apps/services/user-notification/src/app/modules/notifications/me-notifications.controller.ts b/apps/services/user-notification/src/app/modules/notifications/me-notifications.controller.ts index da5d5c7e63a9..26deb3a189a6 100644 --- a/apps/services/user-notification/src/app/modules/notifications/me-notifications.controller.ts +++ b/apps/services/user-notification/src/app/modules/notifications/me-notifications.controller.ts @@ -52,7 +52,7 @@ export class MeNotificationsController { @CurrentUser() user: User, @Query() query: ExtendedPaginationDto, ): Promise { - return this.notificationService.findMany(user, query) + return this.notificationService.findManyWithTemplate(user.nationalId, query) } @Get('/unread-count') diff --git a/apps/services/user-notification/src/app/modules/notifications/notifications.controller.ts b/apps/services/user-notification/src/app/modules/notifications/notifications.controller.ts index a1425841a8b8..c8f578ce6397 100644 --- a/apps/services/user-notification/src/app/modules/notifications/notifications.controller.ts +++ b/apps/services/user-notification/src/app/modules/notifications/notifications.controller.ts @@ -3,10 +3,13 @@ import { Body, Controller, Get, + Headers, + HttpStatus, Inject, Param, Post, Query, + UseGuards, Version, } from '@nestjs/common' import { ApiTags } from '@nestjs/swagger' @@ -19,6 +22,12 @@ import { CreateHnippNotificationDto } from './dto/createHnippNotification.dto' import { HnippTemplate } from './dto/hnippTemplate.response' import { NotificationsService } from './notifications.service' import type { Locale } from '@island.is/shared/types' +import { + ExtendedPaginationDto, + PaginatedNotificationDto, +} from './dto/notification.dto' +import { IdsUserGuard, Scopes, ScopesGuard } from '@island.is/auth-nest-tools' +import { AdminPortalScope } from '@island.is/auth/scopes' @Controller('notifications') @ApiTags('notifications') @@ -85,6 +94,20 @@ export class NotificationsController { return await this.notificationsService.getTemplate(templateId, locale) } + @UseGuards(IdsUserGuard, ScopesGuard) + @Scopes(AdminPortalScope.serviceDesk) + @Get('/') + @Documentation({ + summary: 'Returns a paginated list of notifications for a national id', + response: { status: HttpStatus.OK, type: PaginatedNotificationDto }, + }) + findMany( + @Headers('X-Query-National-Id') nationalId: string, + @Query() query: ExtendedPaginationDto, + ): Promise { + return this.notificationsService.findMany(nationalId, query) + } + @Documentation({ summary: 'Creates a new notification and adds to queue', includeNoContentResponse: true, diff --git a/apps/services/user-notification/src/app/modules/notifications/notifications.service.ts b/apps/services/user-notification/src/app/modules/notifications/notifications.service.ts index 8d8e937ff0f7..4116120d2744 100644 --- a/apps/services/user-notification/src/app/modules/notifications/notifications.service.ts +++ b/apps/services/user-notification/src/app/modules/notifications/notifications.service.ts @@ -260,8 +260,24 @@ export class NotificationsService { ) } - async findMany( - user: User, + findMany( + nationalId: string, + query: ExtendedPaginationDto, + ): Promise { + return paginate({ + Model: this.notificationModel, + limit: query.limit || 10, + after: query.after || '', + before: query.before, + primaryKeyField: 'id', + orderOption: [['id', 'DESC']], + where: { recipient: nationalId }, + attributes: ['id', 'messageId', 'senderId', 'created', 'updated'], + }) + } + + async findManyWithTemplate( + nationalId: string, query: ExtendedPaginationDto, ): Promise { const locale = mapToLocale(query.locale as Locale) @@ -273,7 +289,7 @@ export class NotificationsService { before: query.before, primaryKeyField: 'id', orderOption: [['id', 'DESC']], - where: { recipient: user.nationalId }, + where: { recipient: nationalId }, }) const formattedNotifications = await Promise.all( diff --git a/apps/services/user-notification/src/app/modules/notifications/tests/notifications.service.spec.ts b/apps/services/user-notification/src/app/modules/notifications/tests/notifications.service.spec.ts index 8820d8ddd2cb..8456f9a5333e 100644 --- a/apps/services/user-notification/src/app/modules/notifications/tests/notifications.service.spec.ts +++ b/apps/services/user-notification/src/app/modules/notifications/tests/notifications.service.spec.ts @@ -159,7 +159,9 @@ describe('NotificationsService', () => { .spyOn(service, 'findMany') .mockImplementation(async () => mockedResponse) - expect(await service.findMany(user, query)).toBe(mockedResponse) + expect(await service.findMany(user.nationalId, query)).toBe( + mockedResponse, + ) }) }) diff --git a/libs/api/domains/notifications/src/lib/notifications.model.ts b/libs/api/domains/notifications/src/lib/notifications.model.ts index 64436d89583a..2c83f4a8107c 100644 --- a/libs/api/domains/notifications/src/lib/notifications.model.ts +++ b/libs/api/domains/notifications/src/lib/notifications.model.ts @@ -89,6 +89,21 @@ export class Notification { message!: NotificationMessage } +@ObjectType() +export class AdminNotification { + @Field(() => Int) + id!: number + + @Field(() => ID) + notificationId!: string + + @Field(() => NotificationSender) + sender!: NotificationSender + + @Field(() => GraphQLISODateTime) + sent!: Date +} + @InputType() export class NotificationsInput extends PaginationInput() {} @@ -101,6 +116,11 @@ export class NotificationsResponse extends PaginatedResponse(Notification) { unseenCount?: number } +@ObjectType('AdminNotifications') +export class AdminNotificationsResponse extends PaginatedResponse( + AdminNotification, +) {} + @ObjectType() export class NotificationResponse { @Field(() => Notification) diff --git a/libs/api/domains/notifications/src/lib/notifications.module.ts b/libs/api/domains/notifications/src/lib/notifications.module.ts index 745702eb28bb..231c0d96c879 100644 --- a/libs/api/domains/notifications/src/lib/notifications.module.ts +++ b/libs/api/domains/notifications/src/lib/notifications.module.ts @@ -8,6 +8,8 @@ import { NotificationSenderResolver, } from './notificationsList.resolver' import { NotificationsService } from './notifications.service' +import { NotificationsAdminResolver } from './notificationsAdmin.resolver' +import { NotificationsAdminService } from './notificationsAdmin.service' @Module({ imports: [UserNotificationClientModule], @@ -15,7 +17,9 @@ import { NotificationsService } from './notifications.service' NotificationsResolver, NotificationsListResolver, NotificationSenderResolver, + NotificationsAdminResolver, NotificationsService, + NotificationsAdminService, ], exports: [], }) diff --git a/libs/api/domains/notifications/src/lib/notificationsAdmin.resolver.ts b/libs/api/domains/notifications/src/lib/notificationsAdmin.resolver.ts new file mode 100644 index 000000000000..9afd668e3173 --- /dev/null +++ b/libs/api/domains/notifications/src/lib/notificationsAdmin.resolver.ts @@ -0,0 +1,60 @@ +import { Args, Query, Resolver } from '@nestjs/graphql' +import { IdsUserGuard, CurrentUser } from '@island.is/auth-nest-tools' +import type { User } from '@island.is/auth-nest-tools' +import { Audit } from '@island.is/nest/audit' +import { Inject, UseGuards } from '@nestjs/common' + +import { NotificationsAdminService } from './notificationsAdmin.service' +import { + NotificationsInput, + AdminNotificationsResponse, +} from './notifications.model' +import type { Locale } from '@island.is/shared/types' +import { LOGGER_PROVIDER, type Logger } from '@island.is/logging' + +const LOG_CATEGORY = 'notification-admin-resolver' +export const AUDIT_NAMESPACE = 'notifications-admin-resolver' + +@UseGuards(IdsUserGuard) +@Resolver(() => AdminNotificationsResponse) +@Audit({ namespace: AUDIT_NAMESPACE }) +export class NotificationsAdminResolver { + constructor( + private readonly service: NotificationsAdminService, + @Inject(LOGGER_PROVIDER) private readonly logger: Logger, + ) {} + + @Query(() => AdminNotificationsResponse, { + name: 'adminNotifications', + nullable: true, + }) + @Audit() + async getNotifications( + @Args('nationalId') nationalId: string, + @Args('input', { type: () => NotificationsInput, nullable: true }) + input: NotificationsInput, + @CurrentUser() user: User, + @Args('locale', { type: () => String, nullable: true }) + locale: Locale = 'is', + ): Promise { + let notifications: AdminNotificationsResponse | null + + try { + notifications = await this.service.getNotifications( + locale, + nationalId, + user, + input, + ) + } catch (e) { + this.logger.error('failed to get admin notifications', { + locale, + category: LOG_CATEGORY, + error: e, + }) + throw e + } + + return notifications + } +} diff --git a/libs/api/domains/notifications/src/lib/notificationsAdmin.service.ts b/libs/api/domains/notifications/src/lib/notificationsAdmin.service.ts new file mode 100644 index 000000000000..76089eff1156 --- /dev/null +++ b/libs/api/domains/notifications/src/lib/notificationsAdmin.service.ts @@ -0,0 +1,52 @@ +import { Auth, AuthMiddleware, User } from '@island.is/auth-nest-tools' +import { LOGGER_PROVIDER } from '@island.is/logging' +import type { Logger } from '@island.is/logging' +import { Inject, Injectable } from '@nestjs/common' +import { NotificationsApi } from '@island.is/clients/user-notification' +import type { Locale } from '@island.is/shared/types' +import { + AdminNotificationsResponse, + NotificationsInput, +} from './notifications.model' +import { adminNotificationMapper } from '../utils/helpers' + +@Injectable() +export class NotificationsAdminService { + constructor( + @Inject(LOGGER_PROVIDER) + private logger: Logger, + private notificationsApi: NotificationsApi, + ) {} + + notificationsWAuth(auth: Auth) { + return this.notificationsApi.withMiddleware(new AuthMiddleware(auth)) + } + + async getNotifications( + locale: Locale, + nationalId: string, + user: User, + input?: NotificationsInput, + ): Promise { + const notifications = await this.notificationsWAuth( + user, + ).notificationsControllerFindMany({ + xQueryNationalId: nationalId, + locale, + limit: input?.limit, + before: input?.before, + after: input?.after, + }) + + if (!notifications.data) { + this.logger.debug('no admin notification found') + return null + } + + return { + data: notifications.data.map((item) => adminNotificationMapper(item)), + totalCount: notifications.totalCount, + pageInfo: notifications.pageInfo, + } + } +} diff --git a/libs/api/domains/notifications/src/utils/helpers.ts b/libs/api/domains/notifications/src/utils/helpers.ts index 3baac1e1b7fc..87932276f1e9 100644 --- a/libs/api/domains/notifications/src/utils/helpers.ts +++ b/libs/api/domains/notifications/src/utils/helpers.ts @@ -1,5 +1,5 @@ import { RenderedNotificationDto } from '@island.is/clients/user-notification' -import { Notification } from '../lib/notifications.model' +import { AdminNotification, Notification } from '../lib/notifications.model' const cleanString = (str?: string) => { if (!str) { @@ -40,3 +40,13 @@ export const notificationMapper = ( }, }, }) +export const adminNotificationMapper = ( + notification: RenderedNotificationDto, +): AdminNotification => ({ + id: notification.id, + notificationId: notification.messageId, + sent: notification.created, + sender: { + id: notification.senderId, + }, +}) diff --git a/libs/nest/pagination/src/lib/paginate.ts b/libs/nest/pagination/src/lib/paginate.ts index e289af92c053..87410488a3f0 100644 --- a/libs/nest/pagination/src/lib/paginate.ts +++ b/libs/nest/pagination/src/lib/paginate.ts @@ -117,6 +117,7 @@ export interface PaginateInput { primaryKeyField: string orderOption: any where?: any + attributes?: any after: string before?: string limit: number @@ -138,6 +139,7 @@ export async function paginate({ after, before, limit, + attributes, ...queryArgs }: PaginateInput): Promise<{ totalCount: number @@ -164,6 +166,7 @@ export async function paginate({ where: paginationWhere, limit, order, + attributes, ...queryArgs, } diff --git a/libs/portals/admin/service-desk/src/lib/messages.ts b/libs/portals/admin/service-desk/src/lib/messages.ts index 8560dcf08ef2..9e9c4008fcfd 100644 --- a/libs/portals/admin/service-desk/src/lib/messages.ts +++ b/libs/portals/admin/service-desk/src/lib/messages.ts @@ -65,6 +65,14 @@ export const m = defineMessages({ id: 'admin-portal.service-desk:info', defaultMessage: 'UpplĂœsingar: ', }, + notifications: { + id: 'admin-portal.service-desk:notifications', + defaultMessage: 'Tilkynningar: ', + }, + loadMore: { + id: 'admin-portal.service-desk:load-more', + defaultMessage: 'SjĂĄ meira', + }, email: { id: 'admin-portal.service-desk:email', defaultMessage: 'Netfang', diff --git a/libs/portals/admin/service-desk/src/screens/User/User.graphql b/libs/portals/admin/service-desk/src/screens/User/User.graphql index 1d65b549f8b6..dd1f8410069b 100644 --- a/libs/portals/admin/service-desk/src/screens/User/User.graphql +++ b/libs/portals/admin/service-desk/src/screens/User/User.graphql @@ -32,3 +32,24 @@ mutation UpdateUserProfile( locale } } + +query GetAdminNotifications($nationalId: String!, $input: NotificationsInput!) { + adminNotifications(nationalId: $nationalId, input: $input) { + data { + id + notificationId + sender { + id + logoUrl + } + sent + } + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + totalCount + } +} diff --git a/libs/portals/admin/service-desk/src/screens/User/User.tsx b/libs/portals/admin/service-desk/src/screens/User/User.tsx index 7b16758ac36a..3a132e6512df 100644 --- a/libs/portals/admin/service-desk/src/screens/User/User.tsx +++ b/libs/portals/admin/service-desk/src/screens/User/User.tsx @@ -1,18 +1,34 @@ import format from 'date-fns/format' import { useLoaderData, useNavigate, useRevalidator } from 'react-router-dom' -import { ActionCard, Box, Stack, Text } from '@island.is/island-ui/core' +import { + ActionCard, + Box, + Stack, + Text, + Table as T, + LoadingDots, + SkeletonLoader, +} from '@island.is/island-ui/core' import { useLocale } from '@island.is/localization' import { BackButton } from '@island.is/portals/admin/core' import { IntroHeader, formatNationalId } from '@island.is/portals/core' import { dateFormat } from '@island.is/shared/constants' +import InfiniteScroll from 'react-infinite-scroller' -import { ServiceDeskPaths } from '../../lib/paths' -import { UserProfileResult } from './User.loader' import { m } from '../../lib/messages' -import { useUpdateUserProfileMutation } from './User.generated' +import { + GetAdminNotificationsQuery, + useUpdateUserProfileMutation, +} from './User.generated' import { UpdateUserProfileInput } from '@island.is/api/schema' import React from 'react' +import { isValidDate } from '@island.is/shared/utils' +import { useGetAdminNotificationsQuery } from './User.generated' +import { UserProfileResult } from './User.loader' +import { Problem } from '@island.is/react-spa/shared' + +const DEFAULT_PAGE_SIZE = 10 const User = () => { const { formatMessage } = useLocale() @@ -22,6 +38,18 @@ const User = () => { const [updateProfile] = useUpdateUserProfileMutation() const { revalidate } = useRevalidator() + const { + data: notifications, + loading, + error, + fetchMore, + } = useGetAdminNotificationsQuery({ + variables: { + nationalId: user.nationalId, + input: { limit: DEFAULT_PAGE_SIZE }, + }, + }) + const handleUpdateProfile = async (input: UpdateUserProfileInput) => { try { const updatedProfile = await updateProfile({ @@ -39,6 +67,38 @@ const User = () => { } } + const loadMore = async () => { + if ( + loading || + !notifications || + !notifications?.adminNotifications?.pageInfo.hasNextPage + ) { + return + } + + await fetchMore({ + variables: { + nationalId: user.nationalId, + input: { + limit: DEFAULT_PAGE_SIZE, + after: + notifications?.adminNotifications?.pageInfo.endCursor ?? undefined, + }, + }, + updateQuery: (prev, { fetchMoreResult }): GetAdminNotificationsQuery => { + return { + adminNotifications: { + ...fetchMoreResult.adminNotifications, + data: [ + ...(prev.adminNotifications?.data || []), + ...(fetchMoreResult.adminNotifications?.data || []), + ], + } as GetAdminNotificationsQuery['adminNotifications'], + } + }, + }) + } + return ( { + + {formatMessage(m.notifications)} + {error ? ( + + ) : loading ? ( + + ) : ( + + + + } + > + + + + ID + Message ID + Sender ID + Sent + + + + {notifications?.adminNotifications?.data.map( + (notification, index) => ( + + {notification.id} + {notification.notificationId} + {notification.sender.id} + + {notification.sent && + isValidDate(new Date(notification.sent)) + ? format(new Date(notification.sent), 'dd.MM.yyyy') + : ''} + + + ), + )} + + + + )} + ) } diff --git a/libs/portals/admin/service-desk/src/screens/Users/Users.action.ts b/libs/portals/admin/service-desk/src/screens/Users/Users.action.ts index 13454e204aaa..bfe8cbfb1104 100644 --- a/libs/portals/admin/service-desk/src/screens/Users/Users.action.ts +++ b/libs/portals/admin/service-desk/src/screens/Users/Users.action.ts @@ -1,23 +1,20 @@ import { z } from 'zod' -import { redirect } from 'react-router-dom' import { RawRouterActionResponse, WrappedActionFn, } from '@island.is/portals/core' import { - replaceParams, validateFormData, ValidateFormDataResult, } from '@island.is/react-spa/shared' -import { maskString, isSearchTermValid } from '@island.is/shared/utils' +import { isSearchTermValid } from '@island.is/shared/utils' import { GetPaginatedUserProfilesDocument, GetPaginatedUserProfilesQuery, type GetPaginatedUserProfilesQueryVariables, } from './Users.generated' -import { ServiceDeskPaths } from '../../lib/paths' export enum ErrorType { // Add more error types here when needed From 4704971a14bff6c75849976a5830465f239bb380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9E=C3=B3rey=20J=C3=B3na?= Date: Thu, 5 Dec 2024 15:06:00 +0000 Subject: [PATCH 38/85] feat(native-app): update vehicle list query in app (#16824) * feat: update vehicleList endpoint to V2 * fix: remove comment * fix: update typo after fix from Hugsmidjan --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../fragments/vehicle.fragment.graphql | 37 ++----------------- .../app/src/graphql/queries/vehicles.graphql | 4 +- apps/native/app/src/screens/home/home.tsx | 6 +-- .../app/src/screens/home/vehicles-module.tsx | 14 +++---- .../vehicles/components/vehicle-item.tsx | 14 +++---- .../app/src/screens/vehicles/vehicles.tsx | 28 +++++++------- 6 files changed, 36 insertions(+), 67 deletions(-) diff --git a/apps/native/app/src/graphql/fragments/vehicle.fragment.graphql b/apps/native/app/src/graphql/fragments/vehicle.fragment.graphql index 244ad841d50a..0ab686f97ab0 100644 --- a/apps/native/app/src/graphql/fragments/vehicle.fragment.graphql +++ b/apps/native/app/src/graphql/fragments/vehicle.fragment.graphql @@ -1,39 +1,10 @@ -fragment VehicleFragment on VehiclesVehicle { - isCurrent +fragment VehicleFragment on VehicleListed { permno regno - vin - type - color - firstRegDate + make + colorName modelYear - productYear - registrationType - role - operatorStartDate - operatorEndDate - outOfUse - otherOwners - termination - buyerPersidno - ownerPersidno - vehicleStatus - useGroup - vehGroup - plateStatus - nextInspection { - nextInspectionDate - nextInspectionDateIfPassedInspectionToday - } - operatorNumber - primaryOperator - ownerSsid - ownerName - lastInspectionResult - lastInspectionDate - lastInspectionType - nextInspectionDate - nextAvailableMileageReadDate requiresMileageRegistration canRegisterMileage + nextMainInspection } diff --git a/apps/native/app/src/graphql/queries/vehicles.graphql b/apps/native/app/src/graphql/queries/vehicles.graphql index da2ad3c69212..a88bb380e3b7 100644 --- a/apps/native/app/src/graphql/queries/vehicles.graphql +++ b/apps/native/app/src/graphql/queries/vehicles.graphql @@ -1,5 +1,5 @@ -query ListVehicles($input: GetVehiclesForUserInput!) { - vehiclesList(input: $input) { +query ListVehiclesV2($input: GetVehiclesListV2Input!) { + vehiclesListV2(input: $input) { vehicleList { ...VehicleFragment } diff --git a/apps/native/app/src/screens/home/home.tsx b/apps/native/app/src/screens/home/home.tsx index 057b658de585..f228c0843a88 100644 --- a/apps/native/app/src/screens/home/home.tsx +++ b/apps/native/app/src/screens/home/home.tsx @@ -55,7 +55,7 @@ import { } from './licenses-module' import { OnboardingModule } from './onboarding-module' import { - useListVehiclesQuery, + useListVehiclesV2Query, validateVehiclesInitialData, VehiclesModule, } from './vehicles-module' @@ -174,13 +174,11 @@ export const MainHomeScreen: NavigationFunctionComponent = ({ skip: !airDiscountWidgetEnabled, }) - const vehiclesRes = useListVehiclesQuery({ + const vehiclesRes = useListVehiclesV2Query({ variables: { input: { page: 1, pageSize: 15, - showDeregeristered: false, - showHistory: false, }, }, skip: !vehiclesWidgetEnabled, diff --git a/apps/native/app/src/screens/home/vehicles-module.tsx b/apps/native/app/src/screens/home/vehicles-module.tsx index 02975f50a8e2..34c5c709c86d 100644 --- a/apps/native/app/src/screens/home/vehicles-module.tsx +++ b/apps/native/app/src/screens/home/vehicles-module.tsx @@ -17,8 +17,8 @@ import illustrationSrc from '../../assets/illustrations/le-moving-s4.png' import { navigateTo } from '../../lib/deep-linking' import { VehicleItem } from '../vehicles/components/vehicle-item' import { - ListVehiclesQuery, - useListVehiclesQuery, + ListVehiclesV2Query, + useListVehiclesV2Query, } from '../../graphql/types/schema' import { screenWidth } from '../../utils/dimensions' @@ -30,7 +30,7 @@ const validateVehiclesInitialData = ({ data, loading, }: { - data: ListVehiclesQuery | undefined + data: ListVehiclesV2Query | undefined loading: boolean }) => { if (loading) { @@ -38,7 +38,7 @@ const validateVehiclesInitialData = ({ } // Only show widget initially if there are vehicles that require mileage registration if ( - data?.vehiclesList?.vehicleList?.some( + data?.vehiclesListV2?.vehicleList?.some( (vehicle) => vehicle.requiresMileageRegistration, ) ) { @@ -49,7 +49,7 @@ const validateVehiclesInitialData = ({ } interface VehiclesModuleProps { - data: ListVehiclesQuery | undefined + data: ListVehiclesV2Query | undefined loading: boolean error?: ApolloError | undefined } @@ -59,7 +59,7 @@ const VehiclesModule = React.memo( const theme = useTheme() const intl = useIntl() - const vehicles = data?.vehiclesList?.vehicleList + const vehicles = data?.vehiclesListV2?.vehicleList // Reorder vehicles so vehicles that require mileage registration are shown first const reorderedVehicles = useMemo( @@ -170,4 +170,4 @@ const VehiclesModule = React.memo( }, ) -export { VehiclesModule, validateVehiclesInitialData, useListVehiclesQuery } +export { VehiclesModule, validateVehiclesInitialData, useListVehiclesV2Query } diff --git a/apps/native/app/src/screens/vehicles/components/vehicle-item.tsx b/apps/native/app/src/screens/vehicles/components/vehicle-item.tsx index 100dc377366b..afe6b8c607a5 100644 --- a/apps/native/app/src/screens/vehicles/components/vehicle-item.tsx +++ b/apps/native/app/src/screens/vehicles/components/vehicle-item.tsx @@ -3,7 +3,7 @@ import React from 'react' import { FormattedDate, FormattedMessage } from 'react-intl' import { SafeAreaView, TouchableHighlight, View, ViewStyle } from 'react-native' import styled, { useTheme } from 'styled-components/native' -import { ListVehiclesQuery } from '../../../graphql/types/schema' +import { ListVehiclesV2Query } from '../../../graphql/types/schema' import { navigateTo } from '../../../lib/deep-linking' function differenceInMonths(a: Date, b: Date) { @@ -11,7 +11,7 @@ function differenceInMonths(a: Date, b: Date) { } type VehicleListItem = NonNullable< - NonNullable['vehicleList'] + NonNullable['vehicleList'] >[0] const Cell = styled(TouchableHighlight)` @@ -31,8 +31,8 @@ export const VehicleItem = React.memo( style?: ViewStyle }) => { const theme = useTheme() - const nextInspection = item?.nextInspection?.nextInspectionDate - ? new Date(item?.nextInspection.nextInspectionDate) + const nextInspection = item?.nextMainInspection + ? new Date(item?.nextMainInspection) : null const isInspectionDeadline = @@ -51,14 +51,14 @@ export const VehicleItem = React.memo( onPress={() => { navigateTo(`/vehicle/`, { id: item.permno, - title: item.type, + title: item.make, }) }} > ['vehicleList'] + NonNullable['vehicleList'] >[0] type ListItem = @@ -61,8 +61,6 @@ const Empty = () => ( const input = { page: 1, pageSize: 10, - showDeregeristered: false, - showHistory: false, } export const VehiclesScreen: NavigationFunctionComponent = ({ @@ -77,7 +75,7 @@ export const VehiclesScreen: NavigationFunctionComponent = ({ const scrollY = useRef(new Animated.Value(0)).current const loadingTimeout = useRef>() - const res = useListVehiclesQuery({ + const res = useListVehiclesV2Query({ variables: { input, }, @@ -135,7 +133,9 @@ export const VehiclesScreen: NavigationFunctionComponent = ({ // Extract key of data const keyExtractor = useCallback( (item: ListItem, index: number) => - item.__typename === 'Skeleton' ? String(item.id) : `${item.vin}${index}`, + item.__typename === 'Skeleton' + ? String(item.id) + : `${item.permno}${index}`, [], ) @@ -147,7 +147,7 @@ export const VehiclesScreen: NavigationFunctionComponent = ({ __typename: 'Skeleton', })) } - return res?.data?.vehiclesList?.vehicleList || [] + return res?.data?.vehiclesListV2?.vehicleList || [] }, [res.data, res.loading]) return ( @@ -184,8 +184,8 @@ export const VehiclesScreen: NavigationFunctionComponent = ({ if (res.loading) { return } - const pageNumber = res.data?.vehiclesList?.paging?.pageNumber ?? 1 - const totalPages = res.data?.vehiclesList?.paging?.totalPages ?? 1 + const pageNumber = res.data?.vehiclesListV2?.paging?.pageNumber ?? 1 + const totalPages = res.data?.vehiclesListV2?.paging?.totalPages ?? 1 if (pageNumber >= totalPages) { return } @@ -200,11 +200,11 @@ export const VehiclesScreen: NavigationFunctionComponent = ({ }, updateQuery(prev, { fetchMoreResult }) { return { - vehiclesList: { - ...fetchMoreResult.vehiclesList, + vehiclesListV2: { + ...fetchMoreResult.vehiclesListV2, vehicleList: [ - ...(prev.vehiclesList?.vehicleList ?? []), - ...(fetchMoreResult.vehiclesList?.vehicleList ?? []), + ...(prev.vehiclesListV2?.vehicleList ?? []), + ...(fetchMoreResult.vehiclesListV2?.vehicleList ?? []), ], }, } From 2804be70aa9f78dbdd1c81a6c9038305d0033575 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3n=20Bjarni=20=C3=93lafsson?= <92530555+jonbjarnio@users.noreply.github.com> Date: Thu, 5 Dec 2024 15:24:25 +0000 Subject: [PATCH 39/85] fix(ojoi): Updated controllers and bugfixes (#17146) * Updated data schema to make less api calls, fixed bug when user selected a previous advert the inputs fields were not updating. Switched out controller components since we have our own. * Returning false instead of undefined. --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../components/input/OJOIInputController.tsx | 5 +- .../components/input/OJOISelectController.tsx | 68 ++++++---- .../src/fields/Advert.tsx | 96 +++++++------- .../src/fields/AdvertModal.tsx | 122 ++++++------------ .../src/fields/Preview.tsx | 11 +- .../src/fields/Publishing.tsx | 58 ++++----- .../src/fields/Summary.tsx | 38 ++---- .../src/hooks/useTypes.ts | 38 ++++-- .../src/lib/OJOIApplication.ts | 2 +- .../src/lib/dataSchema.ts | 43 +++--- .../src/lib/types.ts | 9 +- .../src/lib/utils.ts | 6 + .../src/screens/AdvertScreen.tsx | 15 +-- 13 files changed, 234 insertions(+), 277 deletions(-) diff --git a/libs/application/templates/official-journal-of-iceland/src/components/input/OJOIInputController.tsx b/libs/application/templates/official-journal-of-iceland/src/components/input/OJOIInputController.tsx index 44bee990cc2e..ce66ffa0bc15 100644 --- a/libs/application/templates/official-journal-of-iceland/src/components/input/OJOIInputController.tsx +++ b/libs/application/templates/official-journal-of-iceland/src/components/input/OJOIInputController.tsx @@ -1,6 +1,5 @@ -import { SkeletonLoader } from '@island.is/island-ui/core' +import { Input, SkeletonLoader } from '@island.is/island-ui/core' import { useLocale } from '@island.is/localization' -import { InputController } from '@island.is/shared/form-fields' import { MessageDescriptor } from 'react-intl' import { OJOI_INPUT_HEIGHT } from '../../lib/constants' import { useApplication } from '../../hooks/useUpdateApplication' @@ -60,7 +59,7 @@ export const OJOIInputController = ({ } return ( - = { label: string - value: string + value: T } -type Props = { +type Props = { name: string label: string | MessageDescriptor placeholder: string | MessageDescriptor - options?: OJOISelectControllerOption[] - defaultValue?: string + options?: SelectOption[] + defaultValue?: T loading?: boolean applicationId: string disabled?: boolean - onChange?: (label: string, value: string) => void + onBeforeChange?: (answers: OJOIApplication['answers'], value: T) => void + onChange?: (value: T) => void } -export const OJOISelectController = ({ +export const OJOISelectController = ({ name, label, placeholder, @@ -33,30 +36,41 @@ export const OJOISelectController = ({ loading, applicationId, disabled, + onBeforeChange, onChange, -}: Props) => { +}: Props) => { const { formatMessage: f } = useLocale() - const { updateApplication, application } = useApplication({ applicationId }) + const { updateApplication, application } = useApplication({ + applicationId, + }) + + const { setValue } = useFormContext() const placeholderText = typeof placeholder === 'string' ? placeholder : f(placeholder) const labelText = typeof label === 'string' ? label : f(label) - const handleChange = (label: string, value: string) => { + const handleChange = (value: T) => { const currentAnswers = structuredClone(application.answers) const newAnswers = set(currentAnswers, name, value) + onBeforeChange && onBeforeChange(newAnswers, value) - // we must reset the selected typeId if the department changes - if (name === InputFields.advert.departmentId) { - set(newAnswers, InputFields.advert.typeId, '') - } - + setValue(name, value) updateApplication(newAnswers) - onChange && onChange(label, value) + onChange && onChange(value) } + const defaultVal = getValueViaPath(application.answers, name, defaultValue) + const defaultOpt = options?.find((opt) => { + if (isBaseEntity(opt.value) && isBaseEntity(defaultVal)) { + return opt.value.id === defaultVal.id + } + + return false + }) + if (loading) { return ( handleChange(opt.label, opt.value)} + defaultValue={defaultOpt} + onChange={(opt) => { + if (!opt?.value) return + return handleChange(opt.value) + }} /> ) } diff --git a/libs/application/templates/official-journal-of-iceland/src/fields/Advert.tsx b/libs/application/templates/official-journal-of-iceland/src/fields/Advert.tsx index fe9cc36440ae..9cb1b1371782 100644 --- a/libs/application/templates/official-journal-of-iceland/src/fields/Advert.tsx +++ b/libs/application/templates/official-journal-of-iceland/src/fields/Advert.tsx @@ -1,4 +1,3 @@ -import { useCallback } from 'react' import { InputFields, OJOIFieldBaseProps } from '../lib/types' import { Box } from '@island.is/island-ui/core' import { FormGroup } from '../components/form/FormGroup' @@ -15,86 +14,80 @@ import set from 'lodash/set' import { HTMLEditor } from '../components/htmlEditor/HTMLEditor' import { getAdvertMarkup } from '../lib/utils' -type Props = OJOIFieldBaseProps & { - timeStamp: string -} - -export const Advert = ({ application, timeStamp }: Props) => { +export const Advert = ({ application }: OJOIFieldBaseProps) => { const { setValue } = useFormContext() - const { application: currentApplication, updateApplication } = useApplication( - { - applicationId: application.id, - }, - ) + const { application: currentApplication } = useApplication({ + applicationId: application.id, + }) const { departments, loading: loadingDepartments } = useDepartments() const { - useLazyTypes, + getLazyTypes, types, loading: loadingTypes, } = useTypes({ - initalDepartmentId: application.answers?.advert?.departmentId, + initalDepartmentId: application.answers?.advert?.department?.id, }) - const handleDepartmentChange = useCallback( - (value: string) => { - // eslint-disable-next-line react-hooks/rules-of-hooks - useLazyTypes({ - params: { - department: value, - pageSize: 100, - }, - }) - }, - [useLazyTypes], - ) - - const updateTypeHandler = (name: string, id: string) => { - let currentAnswers = structuredClone(currentApplication.answers) - currentAnswers = set(currentAnswers, InputFields.advert.typeName, name) - - currentAnswers = set(currentAnswers, InputFields.advert.typeId, id) - - updateApplication(currentAnswers) - } - const titlePreview = getAdvertMarkup({ - type: currentApplication.answers.advert?.typeName, + type: currentApplication.answers.advert?.type?.title, title: currentApplication.answers.advert?.title, }) + const departmentOptions = departments?.map((d) => ({ + label: d.title, + value: { + id: d.id, + title: d.title, + slug: d.slug, + }, + })) + + const typeOptions = types?.map((d) => ({ + label: d.title, + value: { + id: d.id, + title: d.title, + slug: d.slug, + }, + })) + return ( <> ({ - label: d.title, - value: d.id, - }))} - onChange={(_, value) => handleDepartmentChange(value)} + options={departmentOptions} + defaultValue={application.answers?.advert?.department} + onBeforeChange={(answers) => { + setValue(InputFields.advert.type, null) + set(answers, InputFields.advert.type, null) + }} + onChange={(value) => + getLazyTypes({ + variables: { + params: { + department: value.id, + pageSize: 100, + }, + }, + }) + } /> ({ - label: d.title, - value: d.id, - }))} - onChange={(label, value) => { - updateTypeHandler(label, value) - }} + options={typeOptions} /> @@ -132,7 +125,6 @@ export const Advert = ({ application, timeStamp }: Props) => { applicationId={application.id} name={InputFields.advert.html} defaultValue={currentApplication.answers?.advert?.html} - editorKey={timeStamp} // we have use setValue from useFormContext to update the value // because this is not a controlled component onChange={(value) => setValue(InputFields.advert.html, value)} diff --git a/libs/application/templates/official-journal-of-iceland/src/fields/AdvertModal.tsx b/libs/application/templates/official-journal-of-iceland/src/fields/AdvertModal.tsx index ba50ca2999dc..8327cefa5e93 100644 --- a/libs/application/templates/official-journal-of-iceland/src/fields/AdvertModal.tsx +++ b/libs/application/templates/official-journal-of-iceland/src/fields/AdvertModal.tsx @@ -1,5 +1,4 @@ import { - AlertMessage, Box, Button, Icon, @@ -23,11 +22,10 @@ import { DEFAULT_PAGE_SIZE, OJOI_INPUT_HEIGHT, } from '../lib/constants' -import { useAdvert } from '../hooks/useAdvert' import debounce from 'lodash/debounce' -import set from 'lodash/set' import { InputFields } from '../lib/types' import { useFormContext } from 'react-hook-form' +import { OfficialJournalOfIcelandAdvert } from '@island.is/api/schema' type Props = { applicationId: string visible: boolean @@ -35,55 +33,30 @@ type Props = { onConfirmChange?: () => void } -type UpdateAdvertFields = { - title: string - departmentId: string - typeId: string - html: string - categories: string[] -} - export const AdvertModal = ({ applicationId, visible, setVisible, onConfirmChange, }: Props) => { - const [page, setPage] = useState(DEFAULT_PAGE) - const [search, setSearch] = useState('') - const [selectedAdvertId, setSelectedAdvertId] = useState(null) - const { formatMessage: f } = useLocale() const { setValue } = useFormContext() const { application, updateApplication } = useApplication({ applicationId, }) + const [page, setPage] = useState(DEFAULT_PAGE) + const [search, setSearch] = useState('') + const [selectedAdvert, setSelectedAdvert] = + useState(null) + const { adverts, paging, loading } = useAdverts({ page: page, search: search, }) - const [updateAdvertFields, setUpdateAdvertFields] = - useState(null) - - const { loading: loadingAdvert, error: advertError } = useAdvert({ - advertId: selectedAdvertId, - onCompleted: (ad) => { - setUpdateAdvertFields({ - title: ad.title, - departmentId: ad.department.id, - typeId: ad.type.id, - html: ad.document.html, - categories: ad.categories.map((c) => c.id), - }) - }, - }) - - const disableConfirmButton = !selectedAdvertId || !!advertError - - const onSelectAdvert = (advertId: string) => { - setSelectedAdvertId(advertId) + const onSelectAdvert = (advert: OfficialJournalOfIcelandAdvert) => { + setSelectedAdvert(advert) } const onSearchChange = (value: string) => { @@ -97,46 +70,41 @@ export const AdvertModal = ({ debouncedSearch(e.target.value) } - const onConfirm = () => { - if (!updateAdvertFields) { + const onConfirm = (advert: OfficialJournalOfIcelandAdvert | null) => { + if (!advert) { return } - const currentAnswers = structuredClone(application.answers) + const clean = (obj: { + __typename?: string + id: string + title: string + slug: string + }) => { + const { __typename: _, ...rest } = obj + return rest + } + + const department = clean(advert.department) + const type = clean(advert.type) - let updatedAnswers = set( - currentAnswers, - InputFields.advert.title, - updateAdvertFields.title, - ) - updatedAnswers = set( - updatedAnswers, - InputFields.advert.departmentId, - updateAdvertFields.departmentId, - ) - updatedAnswers = set( - updatedAnswers, - InputFields.advert.typeId, - updateAdvertFields.typeId, - ) - updatedAnswers = set( - updatedAnswers, - InputFields.advert.html, - updateAdvertFields.html, - ) - updatedAnswers = set( - updatedAnswers, - InputFields.advert.categories, - updateAdvertFields.categories, - ) + const categories = advert.categories.map((category) => clean(category)) - setValue(InputFields.advert.title, updateAdvertFields.title) - setValue(InputFields.advert.departmentId, updateAdvertFields.departmentId) - setValue(InputFields.advert.typeId, updateAdvertFields.typeId) - setValue(InputFields.advert.html, updateAdvertFields.html) - setValue(InputFields.advert.categories, updateAdvertFields.categories) + setValue(InputFields.advert.department, department) + setValue(InputFields.advert.type, type) + setValue(InputFields.advert.title, advert.title) + + updateApplication({ + ...application.answers, + advert: { + department, + type, + categories, + title: advert.title, + html: advert.document.html, + }, + }) - updateApplication(updatedAnswers) onConfirmChange && onConfirmChange() setVisible(false) } @@ -173,13 +141,6 @@ export const AdvertModal = ({ onChange={handleSearchChange} /> - {!!advertError && ( - - )} onSelectAdvert(advert.id)} + checked={selectedAdvert?.id === advert.id} + onChange={() => onSelectAdvert(advert)} /> ))} @@ -233,9 +194,8 @@ export const AdvertModal = ({ {f(general.cancel)} diff --git a/libs/application/templates/official-journal-of-iceland/src/fields/Preview.tsx b/libs/application/templates/official-journal-of-iceland/src/fields/Preview.tsx index 3255b2e091e5..82257eeee648 100644 --- a/libs/application/templates/official-journal-of-iceland/src/fields/Preview.tsx +++ b/libs/application/templates/official-journal-of-iceland/src/fields/Preview.tsx @@ -21,7 +21,6 @@ import { import { Routes, SignatureTypes } from '../lib/constants' import { useApplication } from '../hooks/useUpdateApplication' import { advert, error, preview, signatures } from '../lib/messages' -import { useType } from '../hooks/useType' import { previewValidationSchema, signatureValidationSchema, @@ -39,10 +38,6 @@ export const Preview = ({ application, goToScreen }: OJOIFieldBaseProps) => { const { formatMessage: f } = useLocale() - const { type } = useType({ - typeId: currentApplication.answers.advert?.typeId, - }) - const { fetchPdf, error: pdfError, @@ -60,7 +55,7 @@ export const Preview = ({ application, goToScreen }: OJOIFieldBaseProps) => { const url = URL.createObjectURL(blob) let downloadName - const type = currentApplication.answers.advert?.typeName + const type = currentApplication.answers.advert?.type?.title if (type) { downloadName = type.replace('.', '') } @@ -103,14 +98,14 @@ export const Preview = ({ application, goToScreen }: OJOIFieldBaseProps) => { }) const advertMarkup = getAdvertMarkup({ - type: type?.title, + type: currentApplication.answers.advert?.type?.title, title: currentApplication.answers.advert?.title, html: currentApplication.answers.advert?.html, }) const hasMarkup = !!currentApplication.answers.advert?.html || - type?.title || + currentApplication.answers.advert?.type?.title || currentApplication.answers.advert?.title const combinedHtml = hasMarkup diff --git a/libs/application/templates/official-journal-of-iceland/src/fields/Publishing.tsx b/libs/application/templates/official-journal-of-iceland/src/fields/Publishing.tsx index b2f7d27726a9..2b2b845b4092 100644 --- a/libs/application/templates/official-journal-of-iceland/src/fields/Publishing.tsx +++ b/libs/application/templates/official-journal-of-iceland/src/fields/Publishing.tsx @@ -8,6 +8,7 @@ import { AlertMessage, Box, Icon, + Inline, Select, SkeletonLoader, Tag, @@ -18,6 +19,8 @@ import set from 'lodash/set' import addYears from 'date-fns/addYears' import { addWeekdays, getFastTrack, getWeekendDates } from '../lib/utils' import { useState } from 'react' +import { baseEntitySchema } from '../lib/dataSchema' +import { z } from 'zod' export const Publishing = ({ application }: OJOIFieldBaseProps) => { const { formatMessage: f } = useLocale() @@ -52,7 +55,7 @@ export const Publishing = ({ application }: OJOIFieldBaseProps) => { getFastTrack(new Date(defaultDate)).fastTrack, ) - const onCategoryChange = (value?: string) => { + const onCategoryChange = (value?: z.infer) => { setIsUpdatingCategory(true) if (!value) { setIsUpdatingCategory(false) @@ -62,8 +65,8 @@ export const Publishing = ({ application }: OJOIFieldBaseProps) => { const currentAnswers = structuredClone(currentApplication.answers) const selectedCategories = currentAnswers.advert?.categories || [] - const newCategories = selectedCategories.includes(value) - ? selectedCategories.filter((c) => c !== value) + const newCategories = selectedCategories.find((cat) => cat.id === value.id) + ? selectedCategories.filter((c) => c.id !== value.id) : [...selectedCategories, value] const updatedAnswers = set( @@ -77,19 +80,12 @@ export const Publishing = ({ application }: OJOIFieldBaseProps) => { }) } - const defaultCategory = { - label: f(publishing.inputs.contentCategories.placeholder), - value: '', - } - const mappedCategories = categories?.map((c) => ({ label: c.title, - value: c.id, + value: c, })) - const selectedCategories = categories?.filter((c) => - currentApplication.answers.advert?.categories?.includes(c.id), - ) + const selectedCategories = currentApplication.answers.advert?.categories return ( @@ -124,30 +120,26 @@ export const Publishing = ({ application }: OJOIFieldBaseProps) => { size="sm" label={f(publishing.inputs.contentCategories.label)} backgroundColor="blue" - defaultValue={defaultCategory} options={mappedCategories} + defaultValue={mappedCategories?.[0]} onChange={(opt) => onCategoryChange(opt?.value)} /> - - {selectedCategories?.map((c) => ( - onCategoryChange(c.id)} - outlined - key={c.id} - > - - {c.title} - - - - ))} + + + {selectedCategories?.map((c) => ( + onCategoryChange(c)} + outlined + key={c.id} + > + + {c.title} + + + + ))} + )} diff --git a/libs/application/templates/official-journal-of-iceland/src/fields/Summary.tsx b/libs/application/templates/official-journal-of-iceland/src/fields/Summary.tsx index 946abdbf7d5b..dc1799e2a3ef 100644 --- a/libs/application/templates/official-journal-of-iceland/src/fields/Summary.tsx +++ b/libs/application/templates/official-journal-of-iceland/src/fields/Summary.tsx @@ -14,12 +14,9 @@ import { useUserInfo } from '@island.is/react-spa/bff' import { useEffect } from 'react' import { ZodCustomIssue } from 'zod' import { Property } from '../components/property/Property' -import { useCategories } from '../hooks/useCategories' -import { useDepartment } from '../hooks/useDepartment' import { usePrice } from '../hooks/usePrice' -import { useType } from '../hooks/useType' import { useApplication } from '../hooks/useUpdateApplication' -import { MINIMUM_WEEKDAYS, Routes } from '../lib/constants' +import { Routes } from '../lib/constants' import { advertValidationSchema, publishingValidationSchema, @@ -28,7 +25,7 @@ import { import { advert, error, publishing, summary } from '../lib/messages' import { signatures } from '../lib/messages/signatures' import { OJOIFieldBaseProps } from '../lib/types' -import { addWeekdays, getFastTrack, parseZodIssue } from '../lib/utils' +import { getFastTrack, parseZodIssue } from '../lib/utils' export const Summary = ({ application, @@ -42,26 +39,11 @@ export const Summary = ({ const user = useUserInfo() - const { type, loading: loadingType } = useType({ - typeId: currentApplication.answers.advert?.typeId, - }) - const { price, loading: loadingPrice } = usePrice({ applicationId: application.id, }) - const { department, loading: loadingDepartment } = useDepartment({ - departmentId: currentApplication.answers.advert?.departmentId, - }) - - const { categories, loading: loadingCategories } = useCategories() - - const selectedCategories = categories?.filter((c) => - currentApplication.answers?.advert?.categories?.includes(c.id), - ) - - const today = new Date() - const estimatedDate = addWeekdays(today, MINIMUM_WEEKDAYS) + const selectedCategories = application.answers?.advert?.categories const advertValidationCheck = advertValidationSchema.safeParse( currentApplication.answers, @@ -86,6 +68,10 @@ export const Summary = ({ } else { setSubmitButtonDisabled && setSubmitButtonDisabled(true) } + + return () => { + setSubmitButtonDisabled && setSubmitButtonDisabled(false) + } }, [ advertValidationCheck, signatureValidationCheck, @@ -227,18 +213,16 @@ export const Summary = ({ value={user.profile.name} /> c.title).join(', ')} /> @@ -289,7 +272,6 @@ export const Summary = ({ } /> diff --git a/libs/application/templates/official-journal-of-iceland/src/hooks/useTypes.ts b/libs/application/templates/official-journal-of-iceland/src/hooks/useTypes.ts index ca15965c5c74..8cd4ff5678ed 100644 --- a/libs/application/templates/official-journal-of-iceland/src/hooks/useTypes.ts +++ b/libs/application/templates/official-journal-of-iceland/src/hooks/useTypes.ts @@ -1,4 +1,4 @@ -import { NetworkStatus, useQuery } from '@apollo/client' +import { useLazyQuery, useQuery } from '@apollo/client' import { OfficialJournalOfIcelandAdvertsTypesResponse } from '@island.is/api/schema' import { TYPES_QUERY } from '../graphql/queries' @@ -40,21 +40,35 @@ export const useTypes = ({ params.pageSize = 1000 } - const { data, loading, error, refetch, networkStatus } = useQuery< - TypesResponse, - TypesVariables - >(TYPES_QUERY, { - variables: { - params: params, + const { data, loading, error } = useQuery( + TYPES_QUERY, + { + variables: { + params: params, + }, + onCompleted: onCompleted, }, - notifyOnNetworkStatusChange: true, - onCompleted: onCompleted, + ) + + const [ + getLazyTypes, + { data: lazyTypes, loading: lazyTypesLoading, error: lazyTypesError }, + ] = useLazyQuery(TYPES_QUERY, { + fetchPolicy: 'network-only', }) + const currentTypes = lazyTypes + ? lazyTypes.officialJournalOfIcelandTypes.types + : data?.officialJournalOfIcelandTypes.types + return { - useLazyTypes: refetch, - types: data?.officialJournalOfIcelandTypes.types, - loading: loading || networkStatus === NetworkStatus.refetch, + lazyTypes: lazyTypes?.officialJournalOfIcelandTypes.types, + lazyTypesLoading, + lazyTypesError, + getLazyTypes, + types: currentTypes, + initalTypes: data?.officialJournalOfIcelandTypes.types, + loading: loading || lazyTypesLoading, error, } } diff --git a/libs/application/templates/official-journal-of-iceland/src/lib/OJOIApplication.ts b/libs/application/templates/official-journal-of-iceland/src/lib/OJOIApplication.ts index 3e7cc7fc9289..5baf45b63a8d 100644 --- a/libs/application/templates/official-journal-of-iceland/src/lib/OJOIApplication.ts +++ b/libs/application/templates/official-journal-of-iceland/src/lib/OJOIApplication.ts @@ -41,7 +41,7 @@ const getApplicationName = (application: Application) => { const type = getValueViaPath( application.answers, - InputFields.advert.typeName, + `${InputFields.advert.type}.title`, '', ) diff --git a/libs/application/templates/official-journal-of-iceland/src/lib/dataSchema.ts b/libs/application/templates/official-journal-of-iceland/src/lib/dataSchema.ts index f617921467b5..fed28c133461 100644 --- a/libs/application/templates/official-journal-of-iceland/src/lib/dataSchema.ts +++ b/libs/application/templates/official-journal-of-iceland/src/lib/dataSchema.ts @@ -43,6 +43,12 @@ export const regularSignatureSchema = z .array(regularSignatureItemSchema) .optional() +export const baseEntitySchema = z.object({ + id: z.string(), + title: z.string(), + slug: z.string(), +}) + export const signatureInstitutionSchema = z.enum(['institution', 'date']) export const committeeSignatureSchema = regularSignatureItemSchema @@ -61,13 +67,12 @@ export const channelSchema = z const advertSchema = z .object({ - departmentId: z.string().optional(), - typeName: z.string().optional(), - typeId: z.string().optional(), + department: baseEntitySchema.optional(), + type: baseEntitySchema.optional().nullable(), title: z.string().optional(), html: z.string().optional(), requestedDate: z.string().optional(), - categories: z.array(z.string()).optional(), + categories: z.array(baseEntitySchema).optional(), channels: z.array(channelSchema).optional(), message: z.string().optional(), additions: additionSchema.optional(), @@ -108,16 +113,16 @@ export const partialSchema = z.object({ // We make properties optional to throw custom error messages export const advertValidationSchema = z.object({ advert: z.object({ - departmentId: z - .string() + department: baseEntitySchema .optional() - .refine((value) => value && value.length > 0, { + .nullable() + .refine((value) => value !== null && value !== undefined, { params: error.missingDepartment, }), - typeId: z - .string() + type: baseEntitySchema .optional() - .refine((value) => value && value.length > 0, { + .nullable() + .refine((value) => value !== null && value !== undefined, { params: error.missingType, }), title: z @@ -137,17 +142,17 @@ export const advertValidationSchema = z.object({ export const previewValidationSchema = z.object({ advert: z.object({ - departmentId: z - .string() + department: baseEntitySchema .optional() - .refine((value) => value && value.length > 0, { - params: error.missingPreviewDepartment, + .nullable() + .refine((value) => value !== null && value !== undefined, { + params: error.missingDepartment, }), - typeId: z - .string() + type: baseEntitySchema .optional() - .refine((value) => value && value.length > 0, { - params: error.missingPreviewType, + .nullable() + .refine((value) => value !== null && value !== undefined, { + params: error.missingType, }), title: z .string() @@ -173,7 +178,7 @@ export const publishingValidationSchema = z.object({ params: error.missingRequestedDate, }), categories: z - .array(z.string()) + .array(baseEntitySchema) .optional() .refine((value) => Array.isArray(value) && value.length > 0, { params: error.noCategorySelected, diff --git a/libs/application/templates/official-journal-of-iceland/src/lib/types.ts b/libs/application/templates/official-journal-of-iceland/src/lib/types.ts index ccae26abdb38..385aec190a50 100644 --- a/libs/application/templates/official-journal-of-iceland/src/lib/types.ts +++ b/libs/application/templates/official-journal-of-iceland/src/lib/types.ts @@ -11,9 +11,8 @@ export const InputFields = { approveExternalData: 'requirements.approveExternalData', }, [Routes.ADVERT]: { - departmentId: 'advert.departmentId', - typeName: 'advert.typeName', - typeId: 'advert.typeId', + department: 'advert.department', + type: 'advert.type', title: 'advert.title', html: 'advert.html', requestedDate: 'advert.requestedDate', @@ -39,8 +38,8 @@ export const InputFields = { export const RequiredInputFieldsNames = { [Routes.ADVERT]: { - departmentId: 'Deild', - typeId: 'Tegund', + department: 'Deild', + type: 'Tegund', title: 'Titill', html: 'AuglĂœsing', requestedDate: 'ÚtgĂĄfudagur', diff --git a/libs/application/templates/official-journal-of-iceland/src/lib/utils.ts b/libs/application/templates/official-journal-of-iceland/src/lib/utils.ts index 2e6f17b53156..96d1b2fb0893 100644 --- a/libs/application/templates/official-journal-of-iceland/src/lib/utils.ts +++ b/libs/application/templates/official-journal-of-iceland/src/lib/utils.ts @@ -3,6 +3,7 @@ import addYears from 'date-fns/addYears' import { z } from 'zod' import { additionSchema, + baseEntitySchema, committeeSignatureSchema, memberItemSchema, partialSchema, @@ -130,6 +131,11 @@ export const getSignatureDefaultValues = (signature: any, index?: number) => { return { institution: signature.institution, date: signature.date } } +export const isBaseEntity = ( + entity: unknown, +): entity is z.infer => + baseEntitySchema.safeParse(entity).success + export const isAddition = ( addition: unknown, ): addition is z.infer => diff --git a/libs/application/templates/official-journal-of-iceland/src/screens/AdvertScreen.tsx b/libs/application/templates/official-journal-of-iceland/src/screens/AdvertScreen.tsx index 56a115f61241..92d9a3911d77 100644 --- a/libs/application/templates/official-journal-of-iceland/src/screens/AdvertScreen.tsx +++ b/libs/application/templates/official-journal-of-iceland/src/screens/AdvertScreen.tsx @@ -11,13 +11,6 @@ export const AdvertScreen = (props: OJOIFieldBaseProps) => { const { formatMessage: f } = useLocale() const [modalVisible, setModalVisability] = useState(false) - const generateTimestamp = () => new Date().toISOString() - - /** - * This state here is for force rerendering of the HTML editor when a value is received from the modal - */ - const [timestamp, setTimestamp] = useState(generateTimestamp()) - return ( { } > - + setTimestamp(generateTimestamp())} + onConfirmChange={() => { + setTimeout(() => { + props.refetch && props.refetch() + }, 300) + }} /> ) From 4281d75e88c2fb187652ecaddb129f1274c37680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAnar=20Vestmann?= <43557895+RunarVestmann@users.noreply.github.com> Date: Thu, 5 Dec 2024 16:15:26 +0000 Subject: [PATCH 40/85] fix(web): WHODAS calculator - Add parenthesis to max score calculation (#17142) * Add parenthesis to equation * Remove console.log * Hide breakdown for bracket 1 --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../connected/WHODAS/Calculator.tsx | 67 ++++++++++--------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/apps/web/components/connected/WHODAS/Calculator.tsx b/apps/web/components/connected/WHODAS/Calculator.tsx index 109eb573ccfa..53d09cfad3cb 100644 --- a/apps/web/components/connected/WHODAS/Calculator.tsx +++ b/apps/web/components/connected/WHODAS/Calculator.tsx @@ -191,36 +191,42 @@ const WHODASResults = ({ - - - {formatMessage(m.results.breakdownHeading)} - - - {results.steps.map((step) => ( - - - {step.title} - {formatScore(step.scoreForStep)} + {bracket > 1 && ( + + + + + {formatMessage(m.results.breakdownHeading)} + + + {results.steps.map((step) => ( + + + {step.title} + {formatScore(step.scoreForStep)} + + + ))} + + + + {formatMessage(m.results.totalScore)} + + {formatScore(totalScore)} - - ))} - - - - {formatMessage(m.results.totalScore)} - - {formatScore(totalScore)} - - + + + + {formatMessage(m.results.resultDisclaimer)} + + + )} - - {formatMessage(m.results.resultDisclaimer)} - ) } @@ -246,9 +252,10 @@ export const WHODASCalculator = ({ slice }: WHODASCalculatorProps) => { description, maxScorePossible: questions.reduce( (prev, acc) => - prev + acc.answerOptions.length > 0 + prev + + (acc.answerOptions.length > 0 ? acc.answerOptions[acc.answerOptions.length - 1].score - : 0, + : 0), 0, ), questions: questions.map(() => ({ From 2421d10c9a6d64308c465ce22f0a40b3415437ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Thu, 5 Dec 2024 21:26:35 +0000 Subject: [PATCH 41/85] feat(j-s): Upload Additional Files to Court (#17152) --- .../src/app/modules/case/case.service.ts | 4 ++++ .../src/app/modules/file/file.service.ts | 23 +++++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/case/case.service.ts b/apps/judicial-system/backend/src/app/modules/case/case.service.ts index 6454603536c8..d20ffd16d104 100644 --- a/apps/judicial-system/backend/src/app/modules/case/case.service.ts +++ b/apps/judicial-system/backend/src/app/modules/case/case.service.ts @@ -775,12 +775,16 @@ export class CaseService { CaseFileCategory.CRIMINAL_RECORD, CaseFileCategory.COST_BREAKDOWN, CaseFileCategory.CASE_FILE, + CaseFileCategory.PROSECUTOR_CASE_FILE, + CaseFileCategory.DEFENDANT_CASE_FILE, ] : [ CaseFileCategory.INDICTMENT, CaseFileCategory.CRIMINAL_RECORD, CaseFileCategory.COST_BREAKDOWN, CaseFileCategory.CASE_FILE, + CaseFileCategory.PROSECUTOR_CASE_FILE, + CaseFileCategory.DEFENDANT_CASE_FILE, ] const deliverCaseFileToCourtMessages = diff --git a/apps/judicial-system/backend/src/app/modules/file/file.service.ts b/apps/judicial-system/backend/src/app/modules/file/file.service.ts index 397f488a5c0c..fbf3b50a9d5e 100644 --- a/apps/judicial-system/backend/src/app/modules/file/file.service.ts +++ b/apps/judicial-system/backend/src/app/modules/file/file.service.ts @@ -148,12 +148,12 @@ export class FileService { courtDocumentFolder = CourtDocumentFolder.INDICTMENT_DOCUMENTS break case CaseFileCategory.COURT_RECORD: - courtDocumentFolder = CourtDocumentFolder.COURT_DOCUMENTS - break case CaseFileCategory.RULING: courtDocumentFolder = CourtDocumentFolder.COURT_DOCUMENTS break case CaseFileCategory.CASE_FILE: + case CaseFileCategory.PROSECUTOR_CASE_FILE: + case CaseFileCategory.DEFENDANT_CASE_FILE: case undefined: case null: courtDocumentFolder = CourtDocumentFolder.CASE_DOCUMENTS @@ -388,6 +388,25 @@ export class FileService { }, ]) } + + if ( + isIndictmentCase(theCase.type) && + file.category && + [ + CaseFileCategory.PROSECUTOR_CASE_FILE, + CaseFileCategory.DEFENDANT_CASE_FILE, + ].includes(file.category) + ) { + await this.messageService.sendMessagesToQueue([ + { + type: MessageType.DELIVERY_TO_COURT_CASE_FILE, + user, + caseId: theCase.id, + elementId: file.id, + }, + ]) + } + return file } From 090c75d91795f6e683820c35d697400d8e4779f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Thu, 5 Dec 2024 22:11:10 +0000 Subject: [PATCH 42/85] feat(j-s): Show indictment decision type to public prosecutor users (#17079) * Add court abbr to getAllCases call * Refactoring * Refactoring * Refactoring * Refactoring * Add indictment decision to tables --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../case-list/models/caseList.model.ts | 3 + .../src/app/modules/case/case.service.ts | 1 + .../case/interceptors/caseList.interceptor.ts | 2 +- .../web/messages/Core/tables.ts | 11 ++++ .../web/src/components/Table/Table.tsx | 40 ++++++++----- .../Tables/CasesAwaitingReview.tsx | 26 +++++++-- .../Tables/CasesForReview.tsx | 58 ++++++++++++++----- .../PublicProsecutor/Tables/CasesReviewed.tsx | 50 ++++++++++++---- .../web/src/routes/Shared/Cases/cases.graphql | 4 ++ .../formatters/src/lib/formatters.ts | 23 ++++++++ 10 files changed, 172 insertions(+), 46 deletions(-) diff --git a/apps/judicial-system/api/src/app/modules/case-list/models/caseList.model.ts b/apps/judicial-system/api/src/app/modules/case-list/models/caseList.model.ts index 3b2982563026..6372ac2e17d2 100644 --- a/apps/judicial-system/api/src/app/modules/case-list/models/caseList.model.ts +++ b/apps/judicial-system/api/src/app/modules/case-list/models/caseList.model.ts @@ -77,6 +77,9 @@ export class CaseListEntry { @Field(() => String, { nullable: true }) readonly prosecutorPostponedAppealDate?: string + @Field(() => Institution, { nullable: true }) + readonly court?: Institution + @Field(() => User, { nullable: true }) readonly creatingProsecutor?: User diff --git a/apps/judicial-system/backend/src/app/modules/case/case.service.ts b/apps/judicial-system/backend/src/app/modules/case/case.service.ts index d20ffd16d104..097e9c4d5b4a 100644 --- a/apps/judicial-system/backend/src/app/modules/case/case.service.ts +++ b/apps/judicial-system/backend/src/app/modules/case/case.service.ts @@ -409,6 +409,7 @@ export const include: Includeable[] = [ ] export const caseListInclude: Includeable[] = [ + { model: Institution, as: 'court' }, { model: Institution, as: 'prosecutorsOffice' }, { model: Defendant, diff --git a/apps/judicial-system/backend/src/app/modules/case/interceptors/caseList.interceptor.ts b/apps/judicial-system/backend/src/app/modules/case/interceptors/caseList.interceptor.ts index dbc56797bcb1..bee2da1a9852 100644 --- a/apps/judicial-system/backend/src/app/modules/case/interceptors/caseList.interceptor.ts +++ b/apps/judicial-system/backend/src/app/modules/case/interceptors/caseList.interceptor.ts @@ -23,7 +23,6 @@ export class CaseListInterceptor implements NestInterceptor { // WARNING: Be careful when adding to this list. No sensitive information should be returned. // If you need to add sensitive information, then you should consider adding a new endpoint // for defenders and other user roles that are not allowed to see sensitive information. - return { id: theCase.id, created: theCase.created, @@ -65,6 +64,7 @@ export class CaseListInterceptor implements NestInterceptor { indictmentRulingDecision: theCase.indictmentRulingDecision, courtSessionType: theCase.courtSessionType, eventLogs: theCase.eventLogs, + court: theCase.court, } }), ), diff --git a/apps/judicial-system/web/messages/Core/tables.ts b/apps/judicial-system/web/messages/Core/tables.ts index 66879b64ce6a..b2b2500d65bb 100644 --- a/apps/judicial-system/web/messages/Core/tables.ts +++ b/apps/judicial-system/web/messages/Core/tables.ts @@ -134,4 +134,15 @@ export const tables = defineMessages({ defaultMessage: 'Sent', description: 'NotaĂ°ur sem titill fyrir sent dĂĄlk Ă­ lista yfir mĂĄl.', }, + fineTag: { + id: 'judicial.system.core:tables.fine_tag', + defaultMessage: 'ViĂ°urlagaĂĄkvörĂ°un', + description: + 'NotaĂ°ir sem texti Ă­ tagg ĂŸegar mĂĄl endar sem viĂ°urlagaĂĄkvörĂ°un', + }, + rulingTag: { + id: 'judicial.system.core:tables.ruling_tag', + defaultMessage: 'DĂłmur', + description: 'NotaĂ°ir sem texti Ă­ tagg ĂŸegar mĂĄl endar sem dĂłmur', + }, }) diff --git a/apps/judicial-system/web/src/components/Table/Table.tsx b/apps/judicial-system/web/src/components/Table/Table.tsx index 45b6398dfe04..8e572422391c 100644 --- a/apps/judicial-system/web/src/components/Table/Table.tsx +++ b/apps/judicial-system/web/src/components/Table/Table.tsx @@ -6,7 +6,10 @@ import { AnimatePresence, motion } from 'framer-motion' import { Box, Text } from '@island.is/island-ui/core' import { theme } from '@island.is/island-ui/theme' -import { formatDate } from '@island.is/judicial-system/formatters' +import { + districtCourtAbbreviation, + formatDate, +} from '@island.is/judicial-system/formatters' import { CaseType, isCompletedCase, @@ -168,25 +171,30 @@ const Table: FC = (props) => { return null } + const getColumnValue = ( + entry: CaseListEntry, + column: keyof CaseListEntry, + ) => { + const courtAbbreviation = districtCourtAbbreviation(entry.court?.name) + + switch (column) { + case 'defendants': + return entry.defendants?.[0]?.name ?? '' + case 'courtCaseNumber': + return courtAbbreviation + ? `${courtAbbreviation}: ${entry.courtCaseNumber}` + : entry.courtCaseNumber ?? '' + default: + return entry[column]?.toString() ?? '' + } + } + useMemo(() => { if (sortConfig) { data.sort((a: CaseListEntry, b: CaseListEntry) => { - const getColumnValue = (entry: CaseListEntry) => { - if ( - sortConfig.column === 'defendants' && - entry.defendants && - entry.defendants.length > 0 && - entry.defendants[0].name - ) { - return entry.defendants[0].name - } - - return entry[sortConfig.column]?.toString() - } - const compareResult = compareLocaleIS( - getColumnValue(a), - getColumnValue(b), + getColumnValue(a, sortConfig.column), + getColumnValue(b, sortConfig.column), ) return sortConfig.direction === 'ascending' diff --git a/apps/judicial-system/web/src/routes/PublicProsecutor/Tables/CasesAwaitingReview.tsx b/apps/judicial-system/web/src/routes/PublicProsecutor/Tables/CasesAwaitingReview.tsx index 5f1cc2d3dac2..ec030bbaa80b 100644 --- a/apps/judicial-system/web/src/routes/PublicProsecutor/Tables/CasesAwaitingReview.tsx +++ b/apps/judicial-system/web/src/routes/PublicProsecutor/Tables/CasesAwaitingReview.tsx @@ -2,7 +2,7 @@ import { FC } from 'react' import { useIntl } from 'react-intl' import { AnimatePresence } from 'framer-motion' -import { Text } from '@island.is/island-ui/core' +import { Tag, Text } from '@island.is/island-ui/core' import { capitalize, formatDate } from '@island.is/judicial-system/formatters' import { core, tables } from '@island.is/judicial-system-web/messages' import { SectionHeading } from '@island.is/judicial-system-web/src/components' @@ -18,7 +18,10 @@ import TableInfoContainer from '@island.is/judicial-system-web/src/components/Ta import TagCaseState, { mapIndictmentCaseStateToTagVariant, } from '@island.is/judicial-system-web/src/components/TagCaseState/TagCaseState' -import { CaseListEntry } from '@island.is/judicial-system-web/src/graphql/schema' +import { + CaseIndictmentRulingDecision, + CaseListEntry, +} from '@island.is/judicial-system-web/src/graphql/schema' import { strings } from './CasesAwaitingReview.strings' @@ -48,6 +51,7 @@ const CasesForReview: FC = ({ loading, cases }) => { ), sortable: { isSortable: true, key: 'defendants' }, }, + { title: formatMessage(tables.type) }, { title: formatMessage(tables.state) }, { title: formatMessage(tables.deadline), @@ -58,9 +62,9 @@ const CasesForReview: FC = ({ loading, cases }) => { }, ]} data={cases} - generateContextMenuItems={(row) => { - return [openCaseInNewTabMenuItem(row.id)] - }} + generateContextMenuItems={(row) => [ + openCaseInNewTabMenuItem(row.id), + ]} columns={[ { cell: (row) => ( @@ -74,6 +78,18 @@ const CasesForReview: FC = ({ loading, cases }) => { { cell: (row) => , }, + { + cell: (row) => ( + + {formatMessage( + row.indictmentRulingDecision === + CaseIndictmentRulingDecision.FINE + ? tables.fineTag + : tables.rulingTag, + )} + + ), + }, { cell: (row) => ( = ({ loading, cases }) => { thead={[ { title: formatMessage(tables.caseNumber), + sortable: { + isSortable: true, + key: 'courtCaseNumber', + }, }, { title: capitalize( @@ -51,6 +62,7 @@ const CasesForReview: FC = ({ loading, cases }) => { key: 'defendants', }, }, + { title: formatMessage(tables.type) }, { title: formatMessage(tables.state) }, { title: formatMessage(tables.prosecutorName) }, { @@ -62,22 +74,42 @@ const CasesForReview: FC = ({ loading, cases }) => { }, ]} data={cases} - generateContextMenuItems={(row) => { - return [openCaseInNewTabMenuItem(row.id)] - }} + generateContextMenuItems={(row) => [ + openCaseInNewTabMenuItem(row.id), + ]} columns={[ { - cell: (row) => ( - - ), + cell: (row) => { + const courtAbbreviation = districtCourtAbbreviation( + row.court?.name, + ) + + return ( + + ) + }, }, { cell: (row) => , }, + { + cell: (row) => ( + + {formatMessage( + row.indictmentRulingDecision === + CaseIndictmentRulingDecision.FINE + ? tables.fineTag + : tables.rulingTag, + )} + + ), + }, { cell: (row) => ( = ({ loading, cases }) => { thead={[ { title: formatMessage(tables.caseNumber), + sortable: { + isSortable: true, + key: 'courtCaseNumber', + }, }, { title: capitalize( @@ -96,27 +103,48 @@ const CasesReviewed: FC = ({ loading, cases }) => { ), sortable: { isSortable: true, key: 'defendants' }, }, + { title: formatMessage(tables.type) }, { title: formatMessage(tables.reviewDecision) }, { title: formatMessage(tables.verdictViewState) }, { title: formatMessage(tables.prosecutorName) }, ]} data={cases} - generateContextMenuItems={(row) => { - return [openCaseInNewTabMenuItem(row.id)] - }} + generateContextMenuItems={(row) => [ + openCaseInNewTabMenuItem(row.id), + ]} columns={[ { - cell: (row) => ( - - ), + cell: (row) => { + const courtAbbreviation = districtCourtAbbreviation( + row.court?.name, + ) + + return ( + + ) + }, }, { cell: (row) => , }, + { + cell: (row) => ( + + {formatMessage( + row.indictmentRulingDecision === + CaseIndictmentRulingDecision.FINE + ? tables.fineTag + : tables.rulingTag, + )} + + ), + }, { cell: (row) => ( diff --git a/apps/judicial-system/web/src/routes/Shared/Cases/cases.graphql b/apps/judicial-system/web/src/routes/Shared/Cases/cases.graphql index d35f7fb9af54..4001b83c6fcb 100644 --- a/apps/judicial-system/web/src/routes/Shared/Cases/cases.graphql +++ b/apps/judicial-system/web/src/routes/Shared/Cases/cases.graphql @@ -34,6 +34,10 @@ query Cases { initialRulingDate rulingDate rulingSignatureDate + court { + id + name + } judge { id created diff --git a/libs/judicial-system/formatters/src/lib/formatters.ts b/libs/judicial-system/formatters/src/lib/formatters.ts index 7d9d260ddd88..91cc3013da3a 100644 --- a/libs/judicial-system/formatters/src/lib/formatters.ts +++ b/libs/judicial-system/formatters/src/lib/formatters.ts @@ -218,6 +218,29 @@ export const indictmentSubtypes: IndictmentSubtypes = { THEFT: 'ĂŸjĂłfnaĂ°ur', } +export const districtCourtAbbreviation = (courtName?: string | null) => { + switch (courtName) { + case 'HĂ©raĂ°sdĂłmur ReykjavĂ­kur': + return 'HDR' + case 'HĂ©raĂ°sdĂłmur Reykjaness': + return 'HDRN' + case 'HĂ©raĂ°sdĂłmur Vesturlands': + return 'HDV' + case 'HĂ©raĂ°sdĂłmur SuĂ°urlands': + return 'HDS' + case 'HĂ©raĂ°sdĂłmur NorĂ°urlands eystra': + return 'HDNE' + case 'HĂ©raĂ°sdĂłmur NorĂ°urlands vestra': + return 'HDNV' + case 'HĂ©raĂ°sdĂłmur Austurlands': + return 'HDA' + case 'HĂ©raĂ°sdĂłmur VestfjarĂ°a': + return 'HDVF' + default: + return '' + } +} + export const getAppealResultTextByValue = ( value?: CaseAppealRulingDecision | null, ) => { From ab805859a074eef75377529844381d5a46b38aff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Thu, 5 Dec 2024 22:43:17 +0000 Subject: [PATCH 43/85] fix(j-s): Fix Empty National Id (#17149) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../Indictments/Advocates/SelectDefender.tsx | 10 +++++----- .../Indictments/Defendant/Defendant.tsx | 19 ++----------------- .../InvestigationCase/Defendant/Defendant.tsx | 18 ++---------------- 3 files changed, 9 insertions(+), 38 deletions(-) diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Advocates/SelectDefender.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Advocates/SelectDefender.tsx index 70711fe486d7..56033fefc230 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Advocates/SelectDefender.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Advocates/SelectDefender.tsx @@ -43,16 +43,16 @@ const SelectDefender: FC = ({ defendant }) => { caseId, defendantId: defendant.id, defenderNationalId: defendantWaivesRightToCounsel - ? '' - : defendant.defenderNationalId, + ? null + : defendant.defenderNationalId || null, defenderName: defendantWaivesRightToCounsel - ? '' + ? null : defendant.defenderName, defenderEmail: defendantWaivesRightToCounsel - ? '' + ? null : defendant.defenderEmail, defenderPhoneNumber: defendantWaivesRightToCounsel - ? '' + ? null : defendant.defenderPhoneNumber, defenderChoice: defendantWaivesRightToCounsel === true diff --git a/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Defendant/Defendant.tsx b/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Defendant/Defendant.tsx index 2bae92a4bbd9..5f5e15f5b14a 100644 --- a/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Defendant/Defendant.tsx +++ b/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Defendant/Defendant.tsx @@ -370,7 +370,6 @@ const Defendant = () => { router.push(`${destination}/${createdCase.id}`) } else { toast.error(formatMessage(errors.createCase)) - return } } else { router.push(`${destination}/${workingCase.id}`) @@ -416,14 +415,7 @@ const Defendant = () => { const handleCreateDefendantClick = async () => { if (workingCase.id) { - const defendantId = await createDefendant({ - caseId: workingCase.id, - gender: undefined, - name: '', - address: '', - nationalId: null, - citizenship: '', - }) + const defendantId = await createDefendant({ caseId: workingCase.id }) createEmptyDefendant(defendantId) } else { @@ -438,14 +430,7 @@ const Defendant = () => { ...prevWorkingCase, defendants: prevWorkingCase.defendants && [ ...prevWorkingCase.defendants, - { - id: defendantId || uuid(), - gender: undefined, - name: '', - nationalId: null, - address: '', - citizenship: '', - } as TDefendant, + { id: defendantId || uuid() }, ], })) } diff --git a/apps/judicial-system/web/src/routes/Prosecutor/InvestigationCase/Defendant/Defendant.tsx b/apps/judicial-system/web/src/routes/Prosecutor/InvestigationCase/Defendant/Defendant.tsx index 2be75ba20374..119bca8574ae 100644 --- a/apps/judicial-system/web/src/routes/Prosecutor/InvestigationCase/Defendant/Defendant.tsx +++ b/apps/judicial-system/web/src/routes/Prosecutor/InvestigationCase/Defendant/Defendant.tsx @@ -192,14 +192,7 @@ const Defendant = () => { const handleCreateDefendantClick = async () => { if (workingCase.id) { - const defendantId = await createDefendant({ - caseId: workingCase.id, - gender: undefined, - name: '', - address: '', - nationalId: '', - citizenship: '', - }) + const defendantId = await createDefendant({ caseId: workingCase.id }) createEmptyDefendant(defendantId) } else { @@ -214,14 +207,7 @@ const Defendant = () => { ...prevWorkingCase, defendants: prevWorkingCase.defendants && [ ...prevWorkingCase.defendants, - { - id: defendantId || uuid(), - gender: undefined, - name: '', - nationalId: '', - address: '', - citizenship: '', - } as TDefendant, + { id: defendantId || uuid() }, ], })) } From 500eef824b9de6fdb6103581647365773d06921c Mon Sep 17 00:00:00 2001 From: unakb Date: Thu, 5 Dec 2024 22:51:05 +0000 Subject: [PATCH 44/85] fix(j-s): Display confirmation modal before completing case (#17112) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(j-s): Display confirmation modal before completing case * Update apps/judicial-system/web/src/routes/Court/Indictments/Summary/Summary.strings.ts --------- Co-authored-by: Ívar Oddsson Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../Indictments/Summary/Summary.strings.ts | 32 +++++++++++++------ .../Court/Indictments/Summary/Summary.tsx | 26 ++++++++------- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Summary/Summary.strings.ts b/apps/judicial-system/web/src/routes/Court/Indictments/Summary/Summary.strings.ts index 0d0db14b68a4..e5c50b59327c 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Summary/Summary.strings.ts +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Summary/Summary.strings.ts @@ -28,14 +28,28 @@ export const strings = defineMessages({ description: 'NotaĂ°ur sem titill ĂĄ StaĂ°festa takka ĂĄ SamantektarskjĂĄ ĂĄkĂŠru.', }, - completedCaseModalTitle: { - id: 'judicial.system.core:indictments.summary.completed_case_modal_title', - defaultMessage: 'MĂĄli hefur veriĂ° lokiĂ°', - description: 'NotaĂ°ur sem titill ĂĄ staĂ°festingarglugga um aĂ° mĂĄl sĂ© lokiĂ°.', - }, - completedCaseModalBody: { - id: 'judicial.system.core:indictments.summary.completed_case_modal_body_v2', - defaultMessage: 'Gögn hafa veriĂ° send ĂĄkĂŠranda og verjanda.', - description: 'NotaĂ°ur sem texti Ă­ staĂ°festingarglugga um aĂ° mĂĄl sĂ© lokiĂ°.', + completeCaseModalTitle: { + id: 'judicial.system.core:indictments.summary.complete_case_modal_title', + defaultMessage: 'Viltu ljĂșka mĂĄli?', + description: + 'NotaĂ°ur sem titill ĂĄ staĂ°festingarglugga um aĂ° hvort eigi aĂ° ljĂșka mĂĄli.', + }, + completeCaseModalBody: { + id: 'judicial.system.core:indictments.summary.complete_case_modal_body', + defaultMessage: 'NiĂ°urstaĂ°a mĂĄlsins verĂ°ur send ĂĄkĂŠranda og verjanda.', + description: + 'NotaĂ°ur sem texti Ă­ staĂ°festingarglugga um hvort eigi aĂ° ljĂșka mĂĄli.', + }, + completeCaseModalPrimaryButton: { + id: 'judicial.system.core:indictments.summary.complete_case_modal_primary_button', + defaultMessage: 'JĂĄ, ljĂșka mĂĄli', + description: + 'NotaĂ°ur sem texti ĂĄ aĂ°alhnapp Ă­ staĂ°festingarglugga um hvort eigi aĂ° ljĂșka mĂĄli.', + }, + completeCaseModalSecondaryButton: { + id: 'judicial.system.core:indictments.summary.complete_case_modal_secondary_button', + defaultMessage: 'HĂŠtta viĂ°', + description: + 'NotaĂ°ur sem texti ĂĄ aukahnapp Ă­ staĂ°festingarglugga um hvort eigi aĂ° ljĂșka mĂĄli.', }, }) diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Summary/Summary.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Summary/Summary.tsx index d1186e033ce4..51d109336b5e 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Summary/Summary.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Summary/Summary.tsx @@ -52,7 +52,7 @@ const Summary: FC = () => { return router.push(`${destination}/${workingCase.id}`) } - const handleNextButtonClick = async () => { + const handleModalPrimaryButtonClick = async () => { const transitionSuccess = await transitionCase( workingCase.id, CaseTransition.COMPLETE, @@ -63,7 +63,7 @@ const Summary: FC = () => { return } - setModalVisible('CONFIRM_INDICTMENT') + router.push(`${constants.INDICTMENTS_COMPLETED_ROUTE}/${workingCase.id}`) } const [courtRecordFiles, rulingFiles] = (workingCase.caseFiles || []).reduce( @@ -158,20 +158,24 @@ const Summary: FC = () => { previousUrl={`${constants.INDICTMENTS_CONCLUSION_ROUTE}/${workingCase.id}`} nextButtonIcon="checkmark" nextButtonText={formatMessage(strings.nextButtonText)} - onNextButtonClick={async () => await handleNextButtonClick()} - nextIsDisabled={isTransitioningCase} + onNextButtonClick={() => setModalVisible('CONFIRM_INDICTMENT')} /> {modalVisible === 'CONFIRM_INDICTMENT' && ( - router.push( - `${constants.INDICTMENTS_COMPLETED_ROUTE}/${workingCase.id}`, - ) + title={formatMessage(strings.completeCaseModalTitle)} + text={formatMessage(strings.completeCaseModalBody)} + primaryButtonText={formatMessage( + strings.completeCaseModalPrimaryButton, + )} + onPrimaryButtonClick={async () => + await handleModalPrimaryButtonClick() } + secondaryButtonText={formatMessage( + strings.completeCaseModalSecondaryButton, + )} + onSecondaryButtonClick={() => setModalVisible(undefined)} + isPrimaryButtonLoading={isTransitioningCase} /> )} From 3d206080776affaba7099b772783b0d0a9dc4a74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Thu, 5 Dec 2024 23:31:19 +0000 Subject: [PATCH 45/85] feat(j-s): Upload Confirmed Court Record to Court (#17150) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../backend/src/app/modules/case/case.service.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/case/case.service.ts b/apps/judicial-system/backend/src/app/modules/case/case.service.ts index 097e9c4d5b4a..c89bbc8c0e0c 100644 --- a/apps/judicial-system/backend/src/app/modules/case/case.service.ts +++ b/apps/judicial-system/backend/src/app/modules/case/case.service.ts @@ -932,12 +932,10 @@ export class CaseService { (caseFile) => caseFile.state === CaseFileState.STORED_IN_RVG && caseFile.key && - caseFile.category === CaseFileCategory.RULING && - theCase.indictmentRulingDecision && - [ - CaseIndictmentRulingDecision.RULING, - CaseIndictmentRulingDecision.DISMISSAL, - ].includes(theCase.indictmentRulingDecision), + caseFile.category && + [CaseFileCategory.COURT_RECORD, CaseFileCategory.RULING].includes( + caseFile.category, + ), ) .map((caseFile) => ({ type: MessageType.DELIVERY_TO_COURT_CASE_FILE, From 28a77112a633f8567b27b77a228c0c4ded80a8a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAnar=20Vestmann?= <43557895+RunarVestmann@users.noreply.github.com> Date: Fri, 6 Dec 2024 10:06:18 +0000 Subject: [PATCH 46/85] feat(web): Organization parent subpage - Use organization layout in case theme is not set to 'standalone' (#17144) * Add different layout in case theme is not set to 'standalone' * Simplify translation code --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../Wrapper/OrganizationWrapper.tsx | 2 +- apps/web/pages/s/[...slugs]/index.tsx | 38 +++++- .../screens/Organization/ParentSubpage.tsx | 123 ++++++++++++++++++ .../Organization/Standalone/ParentSubpage.tsx | 4 +- apps/web/screens/Organization/SubPage.tsx | 44 ++++--- 5 files changed, 185 insertions(+), 26 deletions(-) create mode 100644 apps/web/screens/Organization/ParentSubpage.tsx diff --git a/apps/web/components/Organization/Wrapper/OrganizationWrapper.tsx b/apps/web/components/Organization/Wrapper/OrganizationWrapper.tsx index d42d13d63913..44e38cb37d59 100644 --- a/apps/web/components/Organization/Wrapper/OrganizationWrapper.tsx +++ b/apps/web/components/Organization/Wrapper/OrganizationWrapper.tsx @@ -128,7 +128,7 @@ interface NavigationData { interface WrapperProps { pageTitle: string pageDescription?: string - pageFeaturedImage?: Image + pageFeaturedImage?: Image | null organizationPage: OrganizationPage breadcrumbItems?: BreadCrumbItem[] mainContent?: ReactNode diff --git a/apps/web/pages/s/[...slugs]/index.tsx b/apps/web/pages/s/[...slugs]/index.tsx index 9eca5605ac4c..e5bee55e773d 100644 --- a/apps/web/pages/s/[...slugs]/index.tsx +++ b/apps/web/pages/s/[...slugs]/index.tsx @@ -25,6 +25,9 @@ import OrganizationNewsArticle, { import OrganizationNewsList, { type OrganizationNewsListProps, } from '@island.is/web/screens/Organization/OrganizationNews/OrganizationNewsList' +import OrganizationParentSubpage, { + type OrganizationParentSubpageProps, +} from '@island.is/web/screens/Organization/ParentSubpage' import PublishedMaterial, { type PublishedMaterialProps, } from '@island.is/web/screens/Organization/PublishedMaterial/PublishedMaterial' @@ -54,6 +57,7 @@ enum PageType { STANDALONE_PARENT_SUBPAGE = 'standalone-parent-subpage', STANDALONE_LEVEL1_SITEMAP = 'standalone-level1-sitemap', STANDALONE_LEVEL2_SITEMAP = 'standalone-level2-sitemap', + PARENT_SUBPAGE = 'parent-subpage', SUBPAGE = 'subpage', ALL_NEWS = 'news', PUBLISHED_MATERIAL = 'published-material', @@ -76,6 +80,9 @@ const pageMap: Record> = { [PageType.STANDALONE_LEVEL2_SITEMAP]: (props) => ( ), + [PageType.PARENT_SUBPAGE]: (props) => ( + + ), [PageType.SUBPAGE]: (props) => , [PageType.ALL_NEWS]: (props) => , [PageType.PUBLISHED_MATERIAL]: (props) => , @@ -112,6 +119,13 @@ interface Props { type: PageType.STANDALONE_LEVEL2_SITEMAP props: StandaloneLevel2SitemapProps } + | { + type: PageType.PARENT_SUBPAGE + props: { + layoutProps: LayoutProps + componentProps: OrganizationParentSubpageProps + } + } | { type: PageType.SUBPAGE props: { @@ -282,10 +296,18 @@ Component.getProps = async (context) => { } try { + if (isStandaloneTheme) { + return { + page: { + type: PageType.STANDALONE_PARENT_SUBPAGE, + props: await StandaloneParentSubpage.getProps(modifiedContext), + }, + } + } return { page: { - type: PageType.STANDALONE_PARENT_SUBPAGE, - props: await StandaloneParentSubpage.getProps(modifiedContext), + type: PageType.PARENT_SUBPAGE, + props: await OrganizationParentSubpage.getProps(modifiedContext), }, } } catch (error) { @@ -360,10 +382,18 @@ Component.getProps = async (context) => { } try { + if (isStandaloneTheme) { + return { + page: { + type: PageType.STANDALONE_PARENT_SUBPAGE, + props: await StandaloneParentSubpage.getProps(modifiedContext), + }, + } + } return { page: { - type: PageType.STANDALONE_PARENT_SUBPAGE, - props: await StandaloneParentSubpage.getProps(modifiedContext), + type: PageType.PARENT_SUBPAGE, + props: await OrganizationParentSubpage.getProps(modifiedContext), }, } } catch (error) { diff --git a/apps/web/screens/Organization/ParentSubpage.tsx b/apps/web/screens/Organization/ParentSubpage.tsx new file mode 100644 index 000000000000..3ae364be44fb --- /dev/null +++ b/apps/web/screens/Organization/ParentSubpage.tsx @@ -0,0 +1,123 @@ +import { useRouter } from 'next/router' + +import { + Box, + GridColumn, + GridContainer, + GridRow, + Stack, + TableOfContents, + Text, +} from '@island.is/island-ui/core' +import { OrganizationWrapper } from '@island.is/web/components' +import { Query } from '@island.is/web/graphql/schema' +import { useLinkResolver, useNamespace } from '@island.is/web/hooks' +import useContentfulId from '@island.is/web/hooks/useContentfulId' +import { useI18n } from '@island.is/web/i18n' +import { withMainLayout } from '@island.is/web/layouts/main' +import type { Screen, ScreenContext } from '@island.is/web/types' + +import { + getProps, + StandaloneParentSubpageProps, +} from './Standalone/ParentSubpage' +import { getSubpageNavList, SubPageContent } from './SubPage' + +type OrganizationParentSubpageScreenContext = ScreenContext & { + organizationPage?: Query['getOrganizationPage'] +} + +export type OrganizationParentSubpageProps = StandaloneParentSubpageProps + +const OrganizationParentSubpage: Screen< + OrganizationParentSubpageProps, + OrganizationParentSubpageScreenContext +> = ({ + organizationPage, + parentSubpage, + selectedHeadingId, + subpage, + tableOfContentHeadings, + namespace, +}) => { + const router = useRouter() + const { activeLocale } = useI18n() + const { linkResolver } = useLinkResolver() + const n = useNamespace(namespace) + useContentfulId(organizationPage.id, parentSubpage.id, subpage.id) + + return ( + + + + + + + {parentSubpage.childLinks.length > 1 && ( + + + {parentSubpage.title} + + { + const href = tableOfContentHeadings.find( + (heading) => heading.headingId === headingId, + )?.href + if (href) { + router.push(href) + } + }} + tableOfContentsTitle={ + namespace?.['OrganizationTableOfContentsTitle'] ?? + activeLocale === 'is' + ? 'Efnisyfirlit' + : 'Table of contents' + } + selectedHeadingId={selectedHeadingId} + /> + + )} + + + + + 1 ? 'h2' : 'h1' + } + /> + + + ) +} + +OrganizationParentSubpage.getProps = getProps + +export default withMainLayout(OrganizationParentSubpage) diff --git a/apps/web/screens/Organization/Standalone/ParentSubpage.tsx b/apps/web/screens/Organization/Standalone/ParentSubpage.tsx index 9f30ee2920a4..7191f959e12a 100644 --- a/apps/web/screens/Organization/Standalone/ParentSubpage.tsx +++ b/apps/web/screens/Organization/Standalone/ParentSubpage.tsx @@ -138,7 +138,7 @@ const StandaloneParentSubpage: Screen< ) } -StandaloneParentSubpage.getProps = async ({ +export const getProps: typeof StandaloneParentSubpage['getProps'] = async ({ apolloClient, locale, query, @@ -262,4 +262,6 @@ StandaloneParentSubpage.getProps = async ({ } } +StandaloneParentSubpage.getProps = getProps + export default StandaloneParentSubpage diff --git a/apps/web/screens/Organization/SubPage.tsx b/apps/web/screens/Organization/SubPage.tsx index 6d81780073b2..dd2850892088 100644 --- a/apps/web/screens/Organization/SubPage.tsx +++ b/apps/web/screens/Organization/SubPage.tsx @@ -1,4 +1,4 @@ -import { useRouter } from 'next/router' +import { NextRouter, useRouter } from 'next/router' import { ParsedUrlQuery } from 'querystring' import { SliceType } from '@island.is/island-ui/contentful' @@ -27,6 +27,7 @@ import { import { SLICE_SPACING } from '@island.is/web/constants' import { ContentLanguage, + OrganizationPage, Query, QueryGetNamespaceArgs, QueryGetOrganizationPageArgs, @@ -214,6 +215,27 @@ export const SubPageContent = ({ ) } +export const getSubpageNavList = ( + organizationPage: OrganizationPage | null | undefined, + router: NextRouter, +): NavigationItem[] => { + const pathname = new URL(router.asPath, 'https://island.is').pathname + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore make web strict + return organizationPage?.menuLinks.map(({ primaryLink, childrenLinks }) => ({ + title: primaryLink?.text, + href: primaryLink?.url, + active: + primaryLink?.url === pathname || + childrenLinks.some((link) => link.url === pathname), + items: childrenLinks.map(({ text, url }) => ({ + title: text, + href: url, + active: url === pathname, + })), + })) +} + type SubPageScreenContext = ScreenContext & { organizationPage?: Query['getOrganizationPage'] } @@ -239,24 +261,6 @@ const SubPage: Screen = ({ useContentfulId(...contentfulIds) - const pathname = new URL(router.asPath, 'https://island.is').pathname - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore make web strict - const navList: NavigationItem[] = organizationPage?.menuLinks.map( - ({ primaryLink, childrenLinks }) => ({ - title: primaryLink?.text, - href: primaryLink?.url, - active: - primaryLink?.url === pathname || - childrenLinks.some((link) => link.url === pathname), - items: childrenLinks.map(({ text, url }) => ({ - title: text, - href: url, - active: url === pathname, - })), - }), - ) - return ( = ({ } navigationData={{ title: n('navigationTitle', 'Efnisyfirlit'), - items: navList, + items: getSubpageNavList(organizationPage, router), }} > {customContent ? ( From dfc8becb82e6d54cbd90c0f708586f93f6f0f6d6 Mon Sep 17 00:00:00 2001 From: valurefugl <65780958+valurefugl@users.noreply.github.com> Date: Fri, 6 Dec 2024 10:24:15 +0000 Subject: [PATCH 47/85] chore(ids-api): Try fix flaky tests (#17154) * Increase timeout for beforeAll hook. * Reset mocks. * Ensure person nationalIds. --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- ...personal-representative.controller.spec.ts | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/apps/services/auth/ids-api/src/app/delegations/delegations-personal-representative.controller.spec.ts b/apps/services/auth/ids-api/src/app/delegations/delegations-personal-representative.controller.spec.ts index 5c07ba95334d..bcf7ed73dab1 100644 --- a/apps/services/auth/ids-api/src/app/delegations/delegations-personal-representative.controller.spec.ts +++ b/apps/services/auth/ids-api/src/app/delegations/delegations-personal-representative.controller.spec.ts @@ -36,16 +36,13 @@ import { } from '@island.is/shared/types' import { createCurrentUser, + createNationalId, createNationalRegistryUser, } from '@island.is/testing/fixtures' import { TestApp } from '@island.is/testing/nest' import { defaultScopes, setupWithAuth } from '../../../test/setup' -import { - getFakeName, - getFakeNationalId, - NameIdTuple, -} from '../../../test/stubs/genericStubs' +import { getFakeName, NameIdTuple } from '../../../test/stubs/genericStubs' import { delegationTypes, getPersonalRepresentativeRelationship, @@ -55,6 +52,10 @@ import { personalRepresentativeType, } from '../../../test/stubs/personalRepresentativeStubs' +const getFakeNationalId = () => createNationalId('person') + +const setupHookTimeout = 10000 + describe('Personal Representative DelegationsController', () => { describe.each([false, true])( 'national registry v3 featureflag: %s', @@ -73,6 +74,7 @@ describe('Personal Representative DelegationsController', () => { let prTypeModel: typeof PersonalRepresentativeType let prDelegationTypeModel: typeof PersonalRepresentativeDelegationTypeModel let delegationTypeModel: typeof DelegationTypeModel + let nationalRegistryV3FeatureService: NationalRegistryV3FeatureService let nationalRegistryApi: NationalRegistryClientService let nationalRegistryV3Api: NationalRegistryV3ClientService let delegationProviderModel: typeof DelegationProviderModel @@ -152,17 +154,14 @@ describe('Personal Representative DelegationsController', () => { getModelToken(DelegationProviderModel), ) clientModel = app.get(getModelToken(Client)) + nationalRegistryV3FeatureService = app.get( + NationalRegistryV3FeatureService, + ) nationalRegistryApi = app.get(NationalRegistryClientService) nationalRegistryV3Api = app.get(NationalRegistryV3ClientService) delegationIndexService = app.get(DelegationsIndexService) - const nationalRegistryV3FeatureService = app.get( - NationalRegistryV3FeatureService, - ) - jest - .spyOn(nationalRegistryV3FeatureService, 'getValue') - .mockImplementation(async () => featureFlag) factory = new FixtureFactory(app) - }) + }, setupHookTimeout) const createDelegationTypeAndProvider = async (rightCode: string[]) => { const newDelegationProvider = await delegationProviderModel.create({ @@ -403,6 +402,10 @@ describe('Personal Representative DelegationsController', () => { ), ] + jest + .spyOn(nationalRegistryV3FeatureService, 'getValue') + .mockImplementation(async () => featureFlag) + nationalRegistryApiSpy = jest .spyOn(nationalRegistryApi, 'getIndividual') .mockImplementation(async (id) => { @@ -461,7 +464,7 @@ describe('Personal Representative DelegationsController', () => { }) afterAll(async () => { - jest.clearAllMocks() + jest.resetAllMocks() await prRightsModel.destroy({ where: {}, cascade: true, From a135b0fba6e2521f6ea7751f48aa5d3c48bcdc44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnea=20R=C3=BAn=20Vignisd=C3=B3ttir?= Date: Fri, 6 Dec 2024 11:08:36 +0000 Subject: [PATCH 48/85] feat(delegation-api): Use national registry v3 for name service (#17088) * use national regestry v3 for name service * chore: nx format:write update dirty files * uncomment code not supposed to be commented out * remove duplicate * Update app.module.ts * Update app.module.ts * use nafn instead of fulltNafn * fix test --------- Co-authored-by: andes-it Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../app/v2/delegations/test/delegation-admin.spec.ts | 8 ++++---- libs/auth-api-lib/src/lib/delegations/names.service.ts | 10 ++++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/apps/services/auth/admin-api/src/app/v2/delegations/test/delegation-admin.spec.ts b/apps/services/auth/admin-api/src/app/v2/delegations/test/delegation-admin.spec.ts index 6d0637535db9..f4c0faeccff7 100644 --- a/apps/services/auth/admin-api/src/app/v2/delegations/test/delegation-admin.spec.ts +++ b/apps/services/auth/admin-api/src/app/v2/delegations/test/delegation-admin.spec.ts @@ -24,7 +24,7 @@ import { AuthDelegationType } from '@island.is/shared/types' import { getModelToken } from '@nestjs/sequelize' import { faker } from '@island.is/shared/mocking' import { TicketStatus, ZendeskService } from '@island.is/clients/zendesk' -import { NationalRegistryClientService } from '@island.is/clients/national-registry-v2' +import { NationalRegistryV3ClientService } from '@island.is/clients/national-registry-v3' import { ErrorCodes } from '@island.is/shared/utils' const currentUser = createCurrentUser({ @@ -36,7 +36,7 @@ describe('DelegationAdmin - With authentication', () => { let server: request.SuperTest let factory: FixtureFactory let zendeskService: ZendeskService - let nationalRegistryApi: NationalRegistryClientService + let nationalRegistryApi: NationalRegistryV3ClientService let delegationIndexServiceApi: DelegationsIndexService beforeEach(async () => { @@ -51,7 +51,7 @@ describe('DelegationAdmin - With authentication', () => { factory = new FixtureFactory(app) zendeskService = app.get(ZendeskService) - nationalRegistryApi = app.get(NationalRegistryClientService) + nationalRegistryApi = app.get(NationalRegistryV3ClientService) delegationIndexServiceApi = app.get(DelegationsIndexService) jest @@ -212,7 +212,7 @@ describe('DelegationAdmin - With authentication', () => { const mockNationalRegistryService = () => { nationalRegistryApiSpy = jest - .spyOn(nationalRegistryApi, 'getIndividual') + .spyOn(nationalRegistryApi, 'getAllDataIndividual') .mockImplementation(async (id) => { const user = createNationalRegistryUser({ nationalId: id, diff --git a/libs/auth-api-lib/src/lib/delegations/names.service.ts b/libs/auth-api-lib/src/lib/delegations/names.service.ts index 463f412a1ffd..43a87f8dcb9c 100644 --- a/libs/auth-api-lib/src/lib/delegations/names.service.ts +++ b/libs/auth-api-lib/src/lib/delegations/names.service.ts @@ -6,7 +6,7 @@ import { createEnhancedFetch, EnhancedFetchAPI, } from '@island.is/clients/middlewares' -import { NationalRegistryClientService } from '@island.is/clients/national-registry-v2' +import { NationalRegistryV3ClientService } from '@island.is/clients/national-registry-v3' import { DelegationConfig } from './DelegationConfig' @@ -17,7 +17,7 @@ export class NamesService { constructor( @Inject(DelegationConfig.KEY) private delegationConfig: ConfigType, - private nationalRegistryClient: NationalRegistryClientService, + private nationalRegistryClient: NationalRegistryV3ClientService, ) { this.authFetch = createEnhancedFetch({ name: 'delegation-auth-client' }) } @@ -32,12 +32,14 @@ export class NamesService { } async getPersonName(nationalId: string) { - const person = await this.nationalRegistryClient.getIndividual(nationalId) + const person = await this.nationalRegistryClient.getAllDataIndividual( + nationalId, + ) if (!person) { throw new BadRequestException( `A person with nationalId<${nationalId}> could not be found`, ) } - return person.fullName ?? person.name + return person.nafn ?? '' } } From d1f95c5f173ea4f84e92d269e11f838ecc529fa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9E=C3=B3rey=20J=C3=B3na?= Date: Fri, 6 Dec 2024 11:27:26 +0000 Subject: [PATCH 49/85] fix(native-app): change border radius circle to be full (#17155) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- apps/native/app/src/ui/utils/theme.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/native/app/src/ui/utils/theme.ts b/apps/native/app/src/ui/utils/theme.ts index ce97652ea88a..baec29a5de00 100644 --- a/apps/native/app/src/ui/utils/theme.ts +++ b/apps/native/app/src/ui/utils/theme.ts @@ -82,7 +82,7 @@ export const theme = { standard: '4px', large: '8px', extraLarge: '16px', - circle: '100px', + full: '100px', }, width: { standard: 1, From 202d2b1d90f1042084c5ad4887161df09e95c7fe Mon Sep 17 00:00:00 2001 From: unakb Date: Fri, 6 Dec 2024 11:53:59 +0000 Subject: [PATCH 50/85] fix(j-s): Fix subpoena pdf (#17158) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../judicial-system/api/src/app/modules/file/file.controller.ts | 2 +- .../backend/src/app/modules/subpoena/subpoena.controller.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/judicial-system/api/src/app/modules/file/file.controller.ts b/apps/judicial-system/api/src/app/modules/file/file.controller.ts index a729124f5f6e..283b548d0efa 100644 --- a/apps/judicial-system/api/src/app/modules/file/file.controller.ts +++ b/apps/judicial-system/api/src/app/modules/file/file.controller.ts @@ -196,7 +196,7 @@ export class FileController { } for defendant ${defendantId} of case ${id} as a pdf document`, ) - const subpoenaIdInjection = subpoenaId ? `/${subpoenaId}` : '' + const subpoenaIdInjection = subpoenaId ? `/${subpoenaId}/pdf` : '' const queryInjection = arraignmentDate ? `?arraignmentDate=${arraignmentDate}&location=${location}&subpoenaType=${subpoenaType}` : '' diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts index 8242d7867970..ed673bbf76b5 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts @@ -105,7 +105,7 @@ export class SubpoenaController { districtCourtRegistrarRule, districtCourtAssistantRule, ) - @Get(['', ':subpoenaId']) + @Get(['', ':subpoenaId/pdf']) @UseGuards(SubpoenaExistsOptionalGuard) @Header('Content-Type', 'application/pdf') @ApiOkResponse({ From c4685ef0d0b59ad4e838c8bfa9976da1438a66f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9E=C3=B3r=C3=B0ur=20H?= Date: Fri, 6 Dec 2024 12:34:20 +0000 Subject: [PATCH 51/85] fix(documents): Sort by publication v1 and v2 by default (#17157) * Sort by publication v1 and v2 by default * Remove logg --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../domains/documents/src/lib/dto/getDocumentListInput.ts | 2 +- .../documents/src/lib/models/v2/documents.input.ts | 8 ++++++-- .../documents-v2/src/lib/dto/listDocuments.input.ts | 2 +- libs/clients/documents/src/lib/documentClient.ts | 2 +- libs/clients/documents/src/lib/models/DocumentInput.ts | 2 +- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/libs/api/domains/documents/src/lib/dto/getDocumentListInput.ts b/libs/api/domains/documents/src/lib/dto/getDocumentListInput.ts index e0900dd2d914..0ab2fcd80590 100644 --- a/libs/api/domains/documents/src/lib/dto/getDocumentListInput.ts +++ b/libs/api/domains/documents/src/lib/dto/getDocumentListInput.ts @@ -21,7 +21,7 @@ export class GetDocumentListInput { typeId?: string @Field({ nullable: true }) - sortBy?: 'Date' | 'Category' | 'Type' | 'Subject' | 'Sender' + sortBy?: 'Date' | 'Category' | 'Type' | 'Subject' | 'Sender' | 'Publication' @Field({ nullable: true }) order?: 'Ascending' | 'Descending' diff --git a/libs/api/domains/documents/src/lib/models/v2/documents.input.ts b/libs/api/domains/documents/src/lib/models/v2/documents.input.ts index f8615073aa99..ed3eb3d52849 100644 --- a/libs/api/domains/documents/src/lib/models/v2/documents.input.ts +++ b/libs/api/domains/documents/src/lib/models/v2/documents.input.ts @@ -17,7 +17,8 @@ import { } from 'class-validator' export enum DocumentPageSort { - Date = 'Date', + Date = 'Date', // Date is document date + Publication = 'Publication', // Publication is document publication date (default) Category = 'Category', Type = 'Type', Sender = 'Sender', @@ -79,7 +80,10 @@ export class DocumentsInput { @IsBoolean() readonly opened?: boolean - @Field(() => DocumentPageSort, { nullable: true, defaultValue: 'Date' }) + @Field(() => DocumentPageSort, { + nullable: true, + defaultValue: 'Publication', + }) @IsOptional() @IsEnum(DocumentPageSort) readonly sortBy?: DocumentPageSort diff --git a/libs/clients/documents-v2/src/lib/dto/listDocuments.input.ts b/libs/clients/documents-v2/src/lib/dto/listDocuments.input.ts index 3070a27c91ba..5da074477c8e 100644 --- a/libs/clients/documents-v2/src/lib/dto/listDocuments.input.ts +++ b/libs/clients/documents-v2/src/lib/dto/listDocuments.input.ts @@ -8,7 +8,7 @@ export type ListDocumentsInputDto = { typeId?: string subjectContains?: string archived?: boolean - sortBy?: 'Date' | 'Category' | 'Type' | 'Sender' | 'Subject' + sortBy?: 'Date' | 'Category' | 'Type' | 'Sender' | 'Subject' | 'Publication' order?: 'Ascending' | 'Descending' opened?: boolean page?: number diff --git a/libs/clients/documents/src/lib/documentClient.ts b/libs/clients/documents/src/lib/documentClient.ts index 476024d24c5a..6b9d130b18a7 100644 --- a/libs/clients/documents/src/lib/documentClient.ts +++ b/libs/clients/documents/src/lib/documentClient.ts @@ -133,7 +133,7 @@ export class DocumentClient { type ExcludesFalse = (x: T | null | undefined | false | '') => x is T const inputs = [ - sortBy ? `sortBy=${sortBy}` : 'sortBy=Date', // first in array to skip & + sortBy ? `sortBy=${sortBy}` : 'sortBy=Publication', // first in array to skip & order ? `orderBy=${order}` : 'order=Descending', page ? `page=${page}` : 'page=1', pageSize ? `pageSize=${pageSize}` : 'pageSize=15', diff --git a/libs/clients/documents/src/lib/models/DocumentInput.ts b/libs/clients/documents/src/lib/models/DocumentInput.ts index 59af47786535..35f861663e37 100644 --- a/libs/clients/documents/src/lib/models/DocumentInput.ts +++ b/libs/clients/documents/src/lib/models/DocumentInput.ts @@ -5,7 +5,7 @@ export type GetDocumentListInput = { categoryId?: string subjectContains?: string typeId?: string - sortBy?: 'Date' | 'Category' | 'Type' | 'Subject' | 'Sender' + sortBy?: 'Date' | 'Category' | 'Type' | 'Subject' | 'Sender' | 'Publication' order?: 'Ascending' | 'Descending' opened?: boolean archived?: boolean From d8a037915a8ad469d7f3ae96432033745a1b34fa Mon Sep 17 00:00:00 2001 From: albinagu <47886428+albinagu@users.noreply.github.com> Date: Fri, 6 Dec 2024 12:45:04 +0000 Subject: [PATCH 52/85] fix(inheritance-report): adding funeral service cost (#17159) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../inheritance-report/src/fields/FuneralCost/index.tsx | 4 +++- .../src/fields/Overview/OverviewDebts/index.tsx | 6 ++++++ .../src/forms/sections/debtsAndFuneralCost.ts | 4 ++++ .../templates/inheritance-report/src/lib/messages.ts | 7 ++++++- 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/libs/application/templates/inheritance-report/src/fields/FuneralCost/index.tsx b/libs/application/templates/inheritance-report/src/fields/FuneralCost/index.tsx index 3cfe9050e922..6222ecda7f0b 100644 --- a/libs/application/templates/inheritance-report/src/fields/FuneralCost/index.tsx +++ b/libs/application/templates/inheritance-report/src/fields/FuneralCost/index.tsx @@ -76,6 +76,7 @@ export const FuneralCost: FC< const rentCost = valueToNumber(values?.rent) const foodCost = valueToNumber(values?.food) const tombstoneCost = valueToNumber(values?.tombstone) + const servicesCost = valueToNumber(values?.service) let total = buildCost + @@ -85,7 +86,8 @@ export const FuneralCost: FC< musicCost + rentCost + foodCost + - tombstoneCost + tombstoneCost + + servicesCost if (hasOther) { total += valueToNumber(values?.other) diff --git a/libs/application/templates/inheritance-report/src/fields/Overview/OverviewDebts/index.tsx b/libs/application/templates/inheritance-report/src/fields/Overview/OverviewDebts/index.tsx index 8ed7095c84c0..b7367e2a7eca 100644 --- a/libs/application/templates/inheritance-report/src/fields/Overview/OverviewDebts/index.tsx +++ b/libs/application/templates/inheritance-report/src/fields/Overview/OverviewDebts/index.tsx @@ -94,6 +94,12 @@ export const OverviewDebts: FC> = ({ getValueViaPath(answers, 'funeralCost.tombstone') || '0', )} /> + (answers, 'funeralCost.service') || '0', + )} + /> Date: Fri, 6 Dec 2024 13:12:45 +0000 Subject: [PATCH 53/85] chore(inao-pp): refactor out custom components and enhance validation (#17137) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: suggestion of new look * chore: refactor out custom components * chore: revert changes to cemetery application * chore: remove console.log * chore: use getvalueviapath to get values for overview * Update libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/conclusionSection/index.ts Co-authored-by: Þórarinn Gunnar Árnason --------- Co-authored-by: Þórarinn Gunnar Árnason Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../application/core/src/lib/fieldBuilders.ts | 2 + ...ncial-statement-political-party.service.ts | 4 +- .../mappers/mapValuesToPartyTypes.ts | 47 ++-- .../components/AssetDebtEquityOverview.tsx | 43 +++- .../src/components/Total.tsx | 39 ---- .../ElectionEquities/ElectionEquities.tsx | 213 ------------------ .../ElectionStatement/ElectionStatement.tsx | 84 ------- .../KeyNumbersCapital/KeyNumbersCapital.tsx | 93 -------- .../src/fields/Overview/Overview.tsx | 170 -------------- .../PartyOperatingIncome/PartyExpenses.tsx | 56 ----- .../PartyOperatingIncome/PartyIncome.tsx | 206 ----------------- .../PartyOperatingIncome.tsx | 89 -------- .../fields/PartyOverview/PartyOverview.tsx | 46 ---- .../src/fields/Success/Success.tsx | 54 ----- .../src/fields/index.ts | 6 - .../conclusionSection/index.ts | 19 ++ .../financialStatementSection/index.ts | 4 +- .../src/forms/applicationForm/index.ts | 8 +- .../capitalNumbersSubsection.ts | 43 ++++ .../capitalNumbersSubsection/index.ts | 28 --- .../equitiesAndLiabilitiesSubsection.ts | 122 ++++++++++ .../equitiesAndLiabilitiesSubsection/index.ts | 17 -- .../keyNumbersSection/index.ts | 4 +- .../operatingCostSubsection.ts | 129 +++++++++++ .../operatingCostSubsection/index.ts | 17 -- .../overviewSection/index.ts | 9 - .../overviewSection/overviewMultiField.ts | 37 --- .../applicationForm/overviewSection/index.ts | 51 +++++ .../src/forms/done/conclusionSection/index.ts | 24 -- .../src/forms/done/index.ts | 2 +- .../src/lib/dataSchema.ts | 55 +++-- .../src/lib/messages.ts | 17 +- .../src/utils/constants.ts | 10 +- .../src/utils/helpers.ts | 177 ++++++++++++++- libs/application/types/src/lib/Fields.ts | 1 + .../lib/DisplayFormField/DisplayFormField.tsx | 99 ++++---- .../formConclusionSection.ts | 28 ++- 37 files changed, 749 insertions(+), 1304 deletions(-) delete mode 100644 libs/application/templates/inao/financial-statement-political-party/src/components/Total.tsx delete mode 100644 libs/application/templates/inao/financial-statement-political-party/src/fields/ElectionEquities/ElectionEquities.tsx delete mode 100644 libs/application/templates/inao/financial-statement-political-party/src/fields/ElectionStatement/ElectionStatement.tsx delete mode 100644 libs/application/templates/inao/financial-statement-political-party/src/fields/KeyNumbersCapital/KeyNumbersCapital.tsx delete mode 100644 libs/application/templates/inao/financial-statement-political-party/src/fields/Overview/Overview.tsx delete mode 100644 libs/application/templates/inao/financial-statement-political-party/src/fields/PartyOperatingIncome/PartyExpenses.tsx delete mode 100644 libs/application/templates/inao/financial-statement-political-party/src/fields/PartyOperatingIncome/PartyIncome.tsx delete mode 100644 libs/application/templates/inao/financial-statement-political-party/src/fields/PartyOperatingIncome/PartyOperatingIncome.tsx delete mode 100644 libs/application/templates/inao/financial-statement-political-party/src/fields/Success/Success.tsx create mode 100644 libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/conclusionSection/index.ts rename libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/{keyNumbersSection => }/financialStatementSection/index.ts (88%) create mode 100644 libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/capitalNumbersSubsection.ts delete mode 100644 libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/capitalNumbersSubsection/index.ts create mode 100644 libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/equitiesAndLiabilitiesSubsection.ts delete mode 100644 libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/equitiesAndLiabilitiesSubsection/index.ts create mode 100644 libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/operatingCostSubsection.ts delete mode 100644 libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/operatingCostSubsection/index.ts delete mode 100644 libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/overviewSection/index.ts delete mode 100644 libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/overviewSection/overviewMultiField.ts create mode 100644 libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/overviewSection/index.ts delete mode 100644 libs/application/templates/inao/financial-statement-political-party/src/forms/done/conclusionSection/index.ts diff --git a/libs/application/core/src/lib/fieldBuilders.ts b/libs/application/core/src/lib/fieldBuilders.ts index d5390fb6bbb6..a1035cbc3b55 100644 --- a/libs/application/core/src/lib/fieldBuilders.ts +++ b/libs/application/core/src/lib/fieldBuilders.ts @@ -991,6 +991,7 @@ export const buildDisplayField = ( value, suffix, rightAlign, + halfWidthOwnline, } = data return { ...extractCommonFields(data), @@ -1006,5 +1007,6 @@ export const buildDisplayField = ( value, suffix, rightAlign, + halfWidthOwnline, } } diff --git a/libs/application/template-api-modules/src/lib/modules/templates/financial-statement-political-party/financial-statement-political-party.service.ts b/libs/application/template-api-modules/src/lib/modules/templates/financial-statement-political-party/financial-statement-political-party.service.ts index 0d4ddd01cad9..3118c379c23e 100644 --- a/libs/application/template-api-modules/src/lib/modules/templates/financial-statement-political-party/financial-statement-political-party.service.ts +++ b/libs/application/template-api-modules/src/lib/modules/templates/financial-statement-political-party/financial-statement-political-party.service.ts @@ -140,11 +140,11 @@ export class FinancialStatementPoliticalPartyTemplateService extends BaseTemplat } private getOperatingYear(application: Application) { - const year = getValueViaPath( + const year = getValueViaPath( application.answers, 'conditionalAbout.operatingYear', ) - if (typeof year !== 'string') { + if (!year) { throw new Error('Operating year not found or invalid') } return year diff --git a/libs/application/template-api-modules/src/lib/modules/templates/financial-statement-political-party/mappers/mapValuesToPartyTypes.ts b/libs/application/template-api-modules/src/lib/modules/templates/financial-statement-political-party/mappers/mapValuesToPartyTypes.ts index bcd5b4a2a2b4..79aa40980f38 100644 --- a/libs/application/template-api-modules/src/lib/modules/templates/financial-statement-political-party/mappers/mapValuesToPartyTypes.ts +++ b/libs/application/template-api-modules/src/lib/modules/templates/financial-statement-political-party/mappers/mapValuesToPartyTypes.ts @@ -4,46 +4,59 @@ import { FormValue } from '@island.is/application/types' export const mapValuesToPartyTypes = (answers: FormValue) => { return { contributionsFromTheTreasury: Number( - getValueViaPath(answers, 'partyIncome.contributionsFromTheTreasury'), + getValueViaPath( + answers, + 'partyIncome.contributionsFromTheTreasury', + ), ), parliamentaryPartySupport: Number( - getValueViaPath(answers, 'partyIncome.parliamentaryPartySupport'), + getValueViaPath(answers, 'partyIncome.parliamentaryPartySupport'), ), municipalContributions: Number( - getValueViaPath(answers, 'partyIncome.municipalContributions'), + getValueViaPath(answers, 'partyIncome.municipalContributions'), ), contributionsFromLegalEntities: Number( - getValueViaPath(answers, 'partyIncome.contributionsFromLegalEntities'), + getValueViaPath( + answers, + 'partyIncome.contributionsFromLegalEntities', + ), ), contributionsFromIndividuals: Number( - getValueViaPath(answers, 'partyIncome.contributionsFromIndividuals'), + getValueViaPath( + answers, + 'partyIncome.contributionsFromIndividuals', + ), ), generalMembershipFees: Number( - getValueViaPath(answers, 'partyIncome.generalMembershipFees'), + getValueViaPath(answers, 'partyIncome.generalMembershipFees'), ), - otherIncome: Number(getValueViaPath(answers, 'partyIncome.otherIncome')), - capitalIncome: Number( - getValueViaPath(answers, 'capitalNumbers.capitalIncome'), + otherIncome: Number( + getValueViaPath(answers, 'partyIncome.otherIncome'), ), officeOperations: Number( - getValueViaPath(answers, 'partyExpense.electionOffice'), + getValueViaPath(answers, 'partyExpense.electionOffice'), ), otherOperatingExpenses: Number( - getValueViaPath(answers, 'partyExpense.otherCost'), + getValueViaPath(answers, 'partyExpense.otherCost'), + ), + capitalIncome: Number( + getValueViaPath(answers, 'capitalNumbers.capitalIncome'), ), financialExpenses: Number( - getValueViaPath(answers, 'capitalNumbers.capitalCost'), + getValueViaPath(answers, 'capitalNumbers.capitalCost'), ), fixedAssetsTotal: Number( - getValueViaPath(answers, 'asset.fixedAssetsTotal'), + getValueViaPath(answers, 'asset.fixedAssetsTotal'), + ), + currentAssets: Number( + getValueViaPath(answers, 'asset.currentAssets'), ), - currentAssets: Number(getValueViaPath(answers, 'asset.currentAssets')), longTermLiabilitiesTotal: Number( - getValueViaPath(answers, 'liability.longTerm'), + getValueViaPath(answers, 'liability.longTerm'), ), shortTermLiabilitiesTotal: Number( - getValueViaPath(answers, 'liability.shortTerm'), + getValueViaPath(answers, 'liability.shortTerm'), ), - equityTotal: Number(getValueViaPath(answers, 'equity.totalEquity')), + equityTotal: Number(getValueViaPath(answers, 'equity.totalEquity')), } } diff --git a/libs/application/templates/inao/financial-statement-political-party/src/components/AssetDebtEquityOverview.tsx b/libs/application/templates/inao/financial-statement-political-party/src/components/AssetDebtEquityOverview.tsx index 2f4c1335863a..330aa0ed82b8 100644 --- a/libs/application/templates/inao/financial-statement-political-party/src/components/AssetDebtEquityOverview.tsx +++ b/libs/application/templates/inao/financial-statement-political-party/src/components/AssetDebtEquityOverview.tsx @@ -5,6 +5,7 @@ import { formatCurrency } from '../utils/helpers' import { m } from '../lib/messages' import { FinancialStatementPoliticalParty } from '../lib/dataSchema' import { sectionColumn } from './css/overviewStyles.css' +import { getValueViaPath } from '@island.is/application/core' type Props = { answers: FinancialStatementPoliticalParty @@ -13,6 +14,32 @@ type Props = { export const AssetDebtEquityOverview = ({ answers }: Props) => { const { formatMessage } = useLocale() + const fixedAssetsTotal = getValueViaPath( + answers, + 'asset.fixedAssetsTotal', + ) + const currentAssets = getValueViaPath(answers, 'asset.currentAssets') + const assetsTotal = getValueViaPath( + answers, + 'equityAndLiabilitiesTotals.assetsTotal', + ) + const longTermLiability = getValueViaPath( + answers, + 'liability.longTerm', + ) + const shortTermLiability = getValueViaPath( + answers, + 'liability.shortTerm', + ) + const totalLiabilities = getValueViaPath( + answers, + 'equityAndLiabilitiesTotals.liabilitiesTotal', + ) + const totalEquity = getValueViaPath(answers, 'equity.totalEquity') + const equityAndLiabilitiesTotal = getValueViaPath( + answers, + 'equityAndLiabilitiesTotals.equityAndLiabilitiesTotal', + ) return ( @@ -23,15 +50,15 @@ export const AssetDebtEquityOverview = ({ answers }: Props) => { @@ -44,25 +71,25 @@ export const AssetDebtEquityOverview = ({ answers }: Props) => { diff --git a/libs/application/templates/inao/financial-statement-political-party/src/components/Total.tsx b/libs/application/templates/inao/financial-statement-political-party/src/components/Total.tsx deleted file mode 100644 index f0cd9b51798f..000000000000 --- a/libs/application/templates/inao/financial-statement-political-party/src/components/Total.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { useEffect } from 'react' -import { Box, Text } from '@island.is/island-ui/core' -import { InputController } from '@island.is/shared/form-fields' -import { useFormContext } from 'react-hook-form' - -type Props = { - name: string - total: number - label: string - title?: string -} - -export const Total = ({ name, total, label, title }: Props) => { - const { setValue } = useFormContext() - - useEffect(() => { - setValue(name, total.toString()) - }, [total]) - - return ( - - {title ? ( - - {title} - - ) : null} - - - - ) -} diff --git a/libs/application/templates/inao/financial-statement-political-party/src/fields/ElectionEquities/ElectionEquities.tsx b/libs/application/templates/inao/financial-statement-political-party/src/fields/ElectionEquities/ElectionEquities.tsx deleted file mode 100644 index cedb1027a555..000000000000 --- a/libs/application/templates/inao/financial-statement-political-party/src/fields/ElectionEquities/ElectionEquities.tsx +++ /dev/null @@ -1,213 +0,0 @@ -import { useState, useEffect } from 'react' -import debounce from 'lodash/debounce' -import { useFormContext } from 'react-hook-form' -import { FieldBaseProps } from '@island.is/application/types' -import { - AlertBanner, - Box, - GridColumn, - GridContainer, - GridRow, - Text, -} from '@island.is/island-ui/core' -import { useLocale } from '@island.is/localization' -import { InputController } from '@island.is/shared/form-fields' -import { getErrorViaPath } from '@island.is/application/core' -import { m } from '../../lib/messages' -import { useTotals } from '../../hooks/useTotals' -import { - EQUITIESANDLIABILITIESIDS, - INPUTCHANGEINTERVAL, - VALIDATOR, -} from '../../utils/constants' -import { getTotal } from '../../utils/helpers' -import { Total } from '../../components/Total' - -export const ElectionEquities = ({ - setBeforeSubmitCallback, -}: FieldBaseProps) => { - const { formatMessage } = useLocale() - - const { - formState: { errors }, - clearErrors, - getValues, - setError, - } = useFormContext() - - const [getTotalEquity, totalEquity] = useTotals( - EQUITIESANDLIABILITIESIDS.equityPrefix, - ) - const [getTotalAssets, totalAssets] = useTotals( - EQUITIESANDLIABILITIESIDS.assetPrefix, - ) - const [getTotalLiabilities, totalLiabilities] = useTotals( - EQUITIESANDLIABILITIESIDS.liabilityPrefix, - ) - - const [equityAndDebts, setEquityAndDebts] = useState(0) - - useEffect(() => { - const total = totalEquity + totalLiabilities - setEquityAndDebts(total) - }, [totalEquity, totalLiabilities]) - - useEffect(() => { - clearErrors(VALIDATOR) - }, [totalEquity, totalLiabilities, totalAssets, clearErrors]) - - setBeforeSubmitCallback && - setBeforeSubmitCallback(async () => { - const values = getValues() - const assets = getTotal(values, 'asset') - const liabilties = getTotal(values, 'liability') - const equities = getTotal(values, 'equity') - - // assets should equal liabilties + equities - const isValid = liabilties + equities === assets - if (!isValid) { - setError(VALIDATOR, { - type: 'custom', - message: formatMessage(m.equityDebtsAssetsValidatorError), - }) - return [false, formatMessage(m.equityDebtsAssetsValidatorError)] - } - return [true, null] - }) - - return ( - - - - - {formatMessage(m.properties)} - - - { - getTotalAssets() - clearErrors(EQUITIESANDLIABILITIESIDS.fixedAssetsTotal) - }, INPUTCHANGEINTERVAL)} - backgroundColor="blue" - currency - /> - - - { - getTotalAssets() - clearErrors(EQUITIESANDLIABILITIESIDS.currentAssets) - }, INPUTCHANGEINTERVAL)} - label={formatMessage(m.currentAssets)} - backgroundColor="blue" - currency - /> - - - - - - {formatMessage(m.debtsAndEquity)} - - - { - getTotalLiabilities() - clearErrors(EQUITIESANDLIABILITIESIDS.longTerm) - }, INPUTCHANGEINTERVAL)} - error={ - errors && - getErrorViaPath(errors, EQUITIESANDLIABILITIESIDS.longTerm) - } - label={formatMessage(m.longTerm)} - backgroundColor="blue" - currency - /> - - - { - getTotalLiabilities() - clearErrors(EQUITIESANDLIABILITIESIDS.shortTerm) - }, INPUTCHANGEINTERVAL)} - error={ - errors && - getErrorViaPath(errors, EQUITIESANDLIABILITIESIDS.shortTerm) - } - label={formatMessage(m.shortTerm)} - backgroundColor="blue" - currency - /> - - - - { - getTotalEquity() - clearErrors(EQUITIESANDLIABILITIESIDS.totalEquity) - }, INPUTCHANGEINTERVAL)} - error={ - errors && - getErrorViaPath(errors, EQUITIESANDLIABILITIESIDS.totalEquity) - } - label={formatMessage(m.equity)} - backgroundColor="blue" - currency - /> - - - - - - - {errors && errors.validator ? ( - - - - ) : null} - - ) -} diff --git a/libs/application/templates/inao/financial-statement-political-party/src/fields/ElectionStatement/ElectionStatement.tsx b/libs/application/templates/inao/financial-statement-political-party/src/fields/ElectionStatement/ElectionStatement.tsx deleted file mode 100644 index 89b4df521eaa..000000000000 --- a/libs/application/templates/inao/financial-statement-political-party/src/fields/ElectionStatement/ElectionStatement.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { getErrorViaPath, getValueViaPath } from '@island.is/application/core' -import { AlertBanner, Box, InputError, Text } from '@island.is/island-ui/core' -import { m } from '../../lib/messages' -import { useLocale } from '@island.is/localization' -import { DefaultEvents, FieldBaseProps } from '@island.is/application/types' -import { format as formatNationalId } from 'kennitala' -import { useSubmitApplication } from '../../hooks/useSubmitApplication' -import BottomBar from '../../components/BottomBar' -import { useFormContext } from 'react-hook-form' -import { FinancialStatementPoliticalParty } from '../../lib/dataSchema' -import { ELECTIONLIMIT, GREATER } from '../../utils/constants' -import { formatNumber } from '../../utils/helpers' - -export const ElectionStatement = ({ - application, - goToScreen, - refetch, -}: FieldBaseProps) => { - const { formatMessage } = useLocale() - const { - formState: { errors }, - } = useFormContext() - const answers = application.answers as FinancialStatementPoliticalParty - const email = getValueViaPath(answers, 'about.email') - const [submitApplication, { loading }] = useSubmitApplication({ - application, - refetch, - event: DefaultEvents.SUBMIT, - }) - - const onBackButtonClick = () => { - const incomeLimit = getValueViaPath(answers, 'election.incomeLimit') - - if (incomeLimit === GREATER) { - goToScreen?.('attachments.file') - } else { - goToScreen?.('election') - } - } - - const onSendButtonClick = () => { - submitApplication() - } - - return ( - - - - {`${answers.about.fullName}, - ${formatMessage(m.nationalId)}: ${formatNationalId( - answers.about.nationalId, - )}, ${formatMessage(m.participated)} - ${answers.election.genitiveName}`} - - - - {`${formatMessage(m.electionDeclare)} ${formatNumber( - ELECTIONLIMIT, - )}`} - - - {formatMessage(m.electionStatementLaw)} - - - - - {errors && getErrorViaPath(errors, 'applicationApprove') ? ( - - ) : null} - - - ) -} diff --git a/libs/application/templates/inao/financial-statement-political-party/src/fields/KeyNumbersCapital/KeyNumbersCapital.tsx b/libs/application/templates/inao/financial-statement-political-party/src/fields/KeyNumbersCapital/KeyNumbersCapital.tsx deleted file mode 100644 index 17329d216f88..000000000000 --- a/libs/application/templates/inao/financial-statement-political-party/src/fields/KeyNumbersCapital/KeyNumbersCapital.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { useState, useEffect } from 'react' -import { - Box, - GridColumn, - GridContainer, - GridRow, -} from '@island.is/island-ui/core' -import { getValueViaPath } from '@island.is/application/core' -import debounce from 'lodash/debounce' -import { useFormContext } from 'react-hook-form' -import { useLocale } from '@island.is/localization' -import { InputController } from '@island.is/shared/form-fields' -import { m } from '../../lib/messages' -import { getErrorViaPath } from '@island.is/application/core' -import { CAPITALNUMBERS, INPUTCHANGEINTERVAL } from '../../utils/constants' -import { Total } from '../../components/Total' - -export const KeyNumbersCapital = () => { - const { formatMessage } = useLocale() - const [totalCapital, setTotalCapital] = useState(0) - const { - clearErrors, - formState: { errors }, - getValues, - } = useFormContext() - - const getTotalCapital = () => { - const values = getValues() - - const income = getValueViaPath(values, CAPITALNUMBERS.capitalIncome) || '0' - const expense = getValueViaPath(values, CAPITALNUMBERS.capitalCost) || '0' - const total = Number(income) - Number(expense) - setTotalCapital(total) - } - - useEffect(() => { - getTotalCapital() - }, [getTotalCapital]) - - return ( - - - - - { - getTotalCapital() - clearErrors(CAPITALNUMBERS.capitalIncome) - }, INPUTCHANGEINTERVAL)} - label={formatMessage(m.capitalIncome)} - backgroundColor="blue" - rightAlign - currency - /> - - - - - { - getTotalCapital() - clearErrors(CAPITALNUMBERS.capitalCost) - }, INPUTCHANGEINTERVAL)} - label={formatMessage(m.capitalCost)} - error={ - errors && getErrorViaPath(errors, CAPITALNUMBERS.capitalCost) - } - backgroundColor="blue" - rightAlign - currency - /> - - - - - - - - - - ) -} diff --git a/libs/application/templates/inao/financial-statement-political-party/src/fields/Overview/Overview.tsx b/libs/application/templates/inao/financial-statement-political-party/src/fields/Overview/Overview.tsx deleted file mode 100644 index 5748df5a3806..000000000000 --- a/libs/application/templates/inao/financial-statement-political-party/src/fields/Overview/Overview.tsx +++ /dev/null @@ -1,170 +0,0 @@ -import { useState } from 'react' -import { DefaultEvents, FieldBaseProps } from '@island.is/application/types' - -import { - AlertBanner, - Box, - Checkbox, - Divider, - GridColumn, - GridRow, - InputError, - Text, -} from '@island.is/island-ui/core' -import { Controller, useFormContext } from 'react-hook-form' -import { getErrorViaPath, getValueViaPath } from '@island.is/application/core' -import { useLocale } from '@island.is/localization' -import { m } from '../../lib/messages' -import { useSubmitApplication } from '../../hooks/useSubmitApplication' -import BottomBar from '../../components/BottomBar' -import { FinancialStatementPoliticalParty } from '../../lib/dataSchema' -import { AboutOverview } from '../../components/AboutOverview' -import { GREATER } from '../../utils/constants' -import { - sectionColumn, - starterColumnStyle, -} from '../../components/css/overviewStyles.css' -import { CapitalNumberOverview } from '../../components/CapitalNumberOverview' -import { AssetDebtEquityOverview } from '../../components/AssetDebtEquityOverview' -import { FileValueLine } from '../../components/FileValueLine' - -export const Overview = ({ - application, - goToScreen, - refetch, -}: FieldBaseProps) => { - const { formatMessage } = useLocale() - const { - formState: { errors }, - setError, - setValue, - } = useFormContext() - - const [approveOverview, setApproveOverview] = useState(false) - - const answers = application.answers as FinancialStatementPoliticalParty - const fileName = answers.attachments?.file?.[0]?.name - - const [submitApplication, { error: submitError, loading }] = - useSubmitApplication({ - application, - refetch, - event: DefaultEvents.SUBMIT, - }) - - const onBackButtonClick = () => { - const incomeLimit = getValueViaPath(answers, 'election.incomeLimit') - - if (incomeLimit === GREATER) { - goToScreen?.('attachments.file') - } else { - goToScreen?.('election') - } - } - - const onSendButtonClick = () => { - if (approveOverview) { - submitApplication() - } else { - setError('applicationApprove', { - type: 'error', - }) - } - } - - return ( - - - - - - - - - - {formatMessage(m.expensesIncome)} - - - - - - - {formatMessage(m.expenses)} - - - - - - - - - - - - - - {formatMessage(m.propertiesAndDebts)} - - - - - - - - {fileName ? ( - <> - - - - ) : null} - - - - - {formatMessage(m.overview)} - - - - { - return ( - { - onChange(e.target.checked) - setApproveOverview(e.target.checked) - setValue('applicationApprove' as string, e.target.checked) - }} - checked={value} - name="applicationApprove" - id="applicationApprove" - label={formatMessage(m.overviewCorrect)} - large - /> - ) - }} - /> - - {errors && getErrorViaPath(errors, 'applicationApprove') ? ( - - ) : null} - {submitError ? ( - - - - ) : null} - - - ) -} diff --git a/libs/application/templates/inao/financial-statement-political-party/src/fields/PartyOperatingIncome/PartyExpenses.tsx b/libs/application/templates/inao/financial-statement-political-party/src/fields/PartyOperatingIncome/PartyExpenses.tsx deleted file mode 100644 index 5a3eadacb74b..000000000000 --- a/libs/application/templates/inao/financial-statement-political-party/src/fields/PartyOperatingIncome/PartyExpenses.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import React, { Fragment } from 'react' -import debounce from 'lodash/debounce' -import { RecordObject } from '@island.is/application/types' -import { InputController } from '@island.is/shared/form-fields' -import { Box } from '@island.is/island-ui/core' -import { useFormContext } from 'react-hook-form' -import { useLocale } from '@island.is/localization' -import { getErrorViaPath } from '@island.is/application/core' -import { m } from '../../lib/messages' -import { INPUTCHANGEINTERVAL, PARTYOPERATIONIDS } from '../../utils/constants' - -interface PropTypes { - getSum: () => void - errors: RecordObject | undefined -} - -export const PartyExpenses = ({ errors, getSum }: PropTypes): JSX.Element => { - const { formatMessage } = useLocale() - const { clearErrors } = useFormContext() - - const onInputChange = debounce((fieldId: string) => { - getSum() - clearErrors(fieldId) - }, INPUTCHANGEINTERVAL) - - return ( - - - onInputChange(PARTYOPERATIONIDS.electionOffice)} - error={ - errors && getErrorViaPath(errors, PARTYOPERATIONIDS.electionOffice) - } - rightAlign - backgroundColor="blue" - currency - /> - - - onInputChange(PARTYOPERATIONIDS.otherCost)} - error={errors && getErrorViaPath(errors, PARTYOPERATIONIDS.otherCost)} - rightAlign - backgroundColor="blue" - currency - /> - - - ) -} diff --git a/libs/application/templates/inao/financial-statement-political-party/src/fields/PartyOperatingIncome/PartyIncome.tsx b/libs/application/templates/inao/financial-statement-political-party/src/fields/PartyOperatingIncome/PartyIncome.tsx deleted file mode 100644 index 26c702129598..000000000000 --- a/libs/application/templates/inao/financial-statement-political-party/src/fields/PartyOperatingIncome/PartyIncome.tsx +++ /dev/null @@ -1,206 +0,0 @@ -import React, { Fragment, useEffect } from 'react' -import debounce from 'lodash/debounce' -import { RecordObject } from '@island.is/application/types' -import { Box } from '@island.is/island-ui/core' -import { InputController } from '@island.is/shared/form-fields' -import { useFormContext } from 'react-hook-form' -import { useLocale } from '@island.is/localization' -import { getErrorViaPath, getValueViaPath } from '@island.is/application/core' -import { m } from '../../lib/messages' -import { FinancialStatementsInaoTaxInfo } from '@island.is/api/schema' -import { INPUTCHANGEINTERVAL, PARTYOPERATIONIDS } from '../../utils/constants' - -interface PropTypes { - data?: { - financialStatementsInaoTaxInfo: FinancialStatementsInaoTaxInfo[] - } | null - loading: boolean - getSum: () => void - errors: RecordObject | undefined -} - -export const PartyIncome = ({ - data, - loading, - errors, - getSum, -}: PropTypes): JSX.Element => { - const { formatMessage } = useLocale() - const { clearErrors, getValues, setValue } = useFormContext() - useEffect(() => { - const values = getValues() - - const contributionsFromTheTreasury = getValueViaPath( - values, - PARTYOPERATIONIDS.contributionsFromTheTreasury, - ) - const parliamentaryPartySupport = getValueViaPath( - values, - PARTYOPERATIONIDS.parliamentaryPartySupport, - ) - const municipalContributions = getValueViaPath( - values, - PARTYOPERATIONIDS.municipalContributions, - ) - - if (data?.financialStatementsInaoTaxInfo) { - if (!contributionsFromTheTreasury) { - setValue( - PARTYOPERATIONIDS.contributionsFromTheTreasury, - data.financialStatementsInaoTaxInfo?.[0]?.value?.toString() ?? '', - ) - } - if (!parliamentaryPartySupport) { - setValue( - PARTYOPERATIONIDS.parliamentaryPartySupport, - data.financialStatementsInaoTaxInfo?.[1]?.value?.toString() ?? '', - ) - } - if (!municipalContributions) { - setValue( - PARTYOPERATIONIDS.municipalContributions, - data.financialStatementsInaoTaxInfo?.[2]?.value?.toString() ?? '', - ) - } - } - getSum() - }, [data, getSum, setValue]) - - const onInputChange = debounce((fieldId: string) => { - getSum() - clearErrors(fieldId) - }, INPUTCHANGEINTERVAL) - - return ( - - - - onInputChange(PARTYOPERATIONIDS.contributionsFromTheTreasury) - } - rightAlign - backgroundColor="blue" - loading={loading} - currency - error={ - errors && - getErrorViaPath( - errors, - PARTYOPERATIONIDS.contributionsFromTheTreasury, - ) - } - /> - - - - onInputChange(PARTYOPERATIONIDS.parliamentaryPartySupport) - } - loading={loading} - backgroundColor="blue" - rightAlign - currency - error={ - errors && - getErrorViaPath(errors, PARTYOPERATIONIDS.parliamentaryPartySupport) - } - /> - - - - onInputChange(PARTYOPERATIONIDS.municipalContributions) - } - loading={loading} - backgroundColor="blue" - rightAlign - currency - error={ - errors && - getErrorViaPath(errors, PARTYOPERATIONIDS.municipalContributions) - } - /> - - - - onInputChange(PARTYOPERATIONIDS.contributionsFromLegalEntities) - } - backgroundColor="blue" - currency - error={ - errors && - getErrorViaPath( - errors, - PARTYOPERATIONIDS.contributionsFromLegalEntities, - ) - } - /> - - - - onInputChange(PARTYOPERATIONIDS.contributionsFromIndividuals) - } - backgroundColor="blue" - currency - error={ - errors && - getErrorViaPath( - errors, - PARTYOPERATIONIDS.contributionsFromIndividuals, - ) - } - /> - - - - onInputChange(PARTYOPERATIONIDS.generalMembershipFees) - } - rightAlign - backgroundColor="blue" - currency - error={ - errors && - getErrorViaPath(errors, PARTYOPERATIONIDS.generalMembershipFees) - } - /> - - - onInputChange(PARTYOPERATIONIDS.otherIncome)} - rightAlign - backgroundColor="blue" - currency - error={ - errors && getErrorViaPath(errors, PARTYOPERATIONIDS.otherIncome) - } - /> - - - ) -} diff --git a/libs/application/templates/inao/financial-statement-political-party/src/fields/PartyOperatingIncome/PartyOperatingIncome.tsx b/libs/application/templates/inao/financial-statement-political-party/src/fields/PartyOperatingIncome/PartyOperatingIncome.tsx deleted file mode 100644 index 14c43d56e667..000000000000 --- a/libs/application/templates/inao/financial-statement-political-party/src/fields/PartyOperatingIncome/PartyOperatingIncome.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import React from 'react' -import { useFormContext } from 'react-hook-form' -import { useQuery } from '@apollo/client' -import { - GridColumn, - GridContainer, - GridRow, - Text, -} from '@island.is/island-ui/core' -import { useLocale } from '@island.is/localization' - -import { m } from '../../lib/messages' -import { PartyIncome } from './PartyIncome' -import { PartyExpenses } from './PartyExpenses' - -import { TaxInfoQuery } from '../../graphql' -import { getValueViaPath } from '@island.is/application/core' -import { FieldBaseProps } from '@island.is/application/types' -import { Total } from '../../components/Total' -import { useTotals } from '../../hooks/useTotals' -import { OPERATINGCOST, PARTYOPERATIONIDS } from '../../utils/constants' - -export const PartyOperatingIncome = ({ application }: FieldBaseProps) => { - const { answers } = application - const operatingYear = getValueViaPath( - answers, - 'conditionalAbout.operatingYear', - ) - - const { data, loading } = useQuery(TaxInfoQuery, { - variables: { year: operatingYear }, - }) - - const { - formState: { errors }, - } = useFormContext() - - const [getTotalIncome, totalIncome] = useTotals( - PARTYOPERATIONIDS.incomePrefix, - ) - const [getTotalExpense, totalExpense] = useTotals( - PARTYOPERATIONIDS.expensePrefix, - ) - const { formatMessage } = useLocale() - - return ( - - - - - {formatMessage(m.income)} - - - - - - - {formatMessage(m.expenses)} - - - - - - - - - - - - ) -} diff --git a/libs/application/templates/inao/financial-statement-political-party/src/fields/PartyOverview/PartyOverview.tsx b/libs/application/templates/inao/financial-statement-political-party/src/fields/PartyOverview/PartyOverview.tsx index 363492b42d03..ed5081de3586 100644 --- a/libs/application/templates/inao/financial-statement-political-party/src/fields/PartyOverview/PartyOverview.tsx +++ b/libs/application/templates/inao/financial-statement-political-party/src/fields/PartyOverview/PartyOverview.tsx @@ -171,52 +171,6 @@ export const PartyOverview = ({ ) : null} - - - {formatMessage(m.overview)} - - - - { - return ( - { - onChange(e.target.checked) - setApproveOverview(e.target.checked) - setValue('applicationApprove' as string, e.target.checked) - }} - checked={value} - name="applicationApprove" - id="applicationApprove" - label={formatMessage(m.overviewCorrect)} - large - /> - ) - }} - /> - - {errors && getErrorViaPath(errors, 'applicationApprove') ? ( - - ) : null} - {submitError ? ( - - - - ) : null} - ) } diff --git a/libs/application/templates/inao/financial-statement-political-party/src/fields/Success/Success.tsx b/libs/application/templates/inao/financial-statement-political-party/src/fields/Success/Success.tsx deleted file mode 100644 index 1603815ae9d1..000000000000 --- a/libs/application/templates/inao/financial-statement-political-party/src/fields/Success/Success.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { - Box, - ContentBlock, - ActionCard, - AlertMessage, -} from '@island.is/island-ui/core' -import { useLocale } from '@island.is/localization' -import { CustomField, FieldBaseProps } from '@island.is/application/types' -import format from 'date-fns/format' -import { m } from '../../lib/messages' -import { FinancialStatementPoliticalParty } from '../../lib/dataSchema' - -interface PropTypes extends FieldBaseProps { - field: CustomField -} - -export const Success = ({ application }: PropTypes) => { - const applicationAnswers = - application.answers as FinancialStatementPoliticalParty - const { formatMessage } = useLocale() - - const getDescriptionText = () => { - const currentDate = format(new Date(), "dd.MM.yyyy 'kl.' kk:mm") - return `${formatMessage(m.operatingYearMsgFirst)} ${ - applicationAnswers.conditionalAbout.operatingYear - } - ${formatMessage(m.individualReceivedMsgSecond)} ${currentDate}` - } - - return ( - - - - - - - window.open('/minarsidur/postholf', '_blank'), - }} - backgroundColor="blue" - /> - - - - ) -} diff --git a/libs/application/templates/inao/financial-statement-political-party/src/fields/index.ts b/libs/application/templates/inao/financial-statement-political-party/src/fields/index.ts index 4d9e7b24f065..9665239da2a3 100644 --- a/libs/application/templates/inao/financial-statement-political-party/src/fields/index.ts +++ b/libs/application/templates/inao/financial-statement-political-party/src/fields/index.ts @@ -1,9 +1,3 @@ export { OperatingYear } from './OperatingYear/OperatingYear' export { PowerOfAttorneyFields } from './PowerOfAttorneyFields/PowerOfAttorneyFields' -export { PartyOperatingIncome } from './PartyOperatingIncome/PartyOperatingIncome' -export { KeyNumbersCapital } from './KeyNumbersCapital/KeyNumbersCapital' -export { ElectionEquities } from './ElectionEquities/ElectionEquities' export { PartyOverview } from './PartyOverview/PartyOverview' -export { Overview } from './Overview/Overview' -export { ElectionStatement } from './ElectionStatement/ElectionStatement' -export { Success } from './Success/Success' diff --git a/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/conclusionSection/index.ts b/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/conclusionSection/index.ts new file mode 100644 index 000000000000..1ca0e1529209 --- /dev/null +++ b/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/conclusionSection/index.ts @@ -0,0 +1,19 @@ +import { buildFormConclusionSection } from '@island.is/application/ui-forms' +import { m } from '../../../lib/messages' +import { getValueViaPath } from '@island.is/application/core' + +export const conclusionSection = buildFormConclusionSection({ + multiFieldTitle: m.received, + alertTitle: m.returned, + alertMessage: (application) => { + const year = getValueViaPath( + application.answers, + 'conditionalAbout.operatingYear', + ) + return { + ...m.conclusionAlertMessage, + values: { value1: year }, + } + }, + accordion: false, +}) diff --git a/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/financialStatementSection/index.ts b/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/financialStatementSection/index.ts similarity index 88% rename from libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/financialStatementSection/index.ts rename to libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/financialStatementSection/index.ts index 6f40b92fc14d..5ff1a5cf83d2 100644 --- a/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/financialStatementSection/index.ts +++ b/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/financialStatementSection/index.ts @@ -3,8 +3,8 @@ import { buildSection, getValueViaPath, } from '@island.is/application/core' -import { m } from '../../../../lib/messages' -import { LESS } from '../../../../utils/constants' +import { LESS } from '../../../utils/constants' +import { m } from '../../../lib/messages' export const financialStatementSection = buildSection({ id: 'documents', diff --git a/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/index.ts b/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/index.ts index bca7ac51bc34..58fe15ed2a7e 100644 --- a/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/index.ts +++ b/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/index.ts @@ -4,19 +4,21 @@ import { Form, FormModes } from '@island.is/application/types' import Logo from '../../components/Logo' import { clientInfoSection } from './clientInfoSection' import { keyNumbersSection } from './keyNumbersSection' -import { financialStatementSection } from './keyNumbersSection/financialStatementSection' -import { overviewSection } from './keyNumbersSection/overviewSection' +import { financialStatementSection } from './financialStatementSection' +import { overviewSection } from './overviewSection' +import { conclusionSection } from './conclusionSection' export const FinancialStatementPoliticalPartyForm: Form = buildForm({ id: 'FinancialStatementPoliticalPartyForm', title: m.applicationTitle, mode: FormModes.DRAFT, - renderLastScreenButton: false, + renderLastScreenButton: true, logo: Logo, children: [ clientInfoSection, keyNumbersSection, financialStatementSection, overviewSection, + conclusionSection, ], }) diff --git a/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/capitalNumbersSubsection.ts b/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/capitalNumbersSubsection.ts new file mode 100644 index 000000000000..14d7b80a02f2 --- /dev/null +++ b/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/capitalNumbersSubsection.ts @@ -0,0 +1,43 @@ +import { + buildDisplayField, + buildMultiField, + buildSubSection, + buildTextField, +} from '@island.is/application/core' +import { m } from '../../../lib/messages' +import { CAPITALNUMBERS } from '../../../utils/constants' +import { sumCapitalNumbers } from '../../../utils/helpers' + +export const capitalNumbersSubsection = buildSubSection({ + id: 'keynumbers.capitalNumbers', + title: m.capitalNumbers, + children: [ + buildMultiField({ + id: 'capitalNumber', + title: m.capitalNumbersSectionTitle, + children: [ + buildTextField({ + id: CAPITALNUMBERS.capitalIncome, + title: m.capitalIncome, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildTextField({ + id: CAPITALNUMBERS.capitalCost, + title: m.capitalCost, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildDisplayField({ + id: CAPITALNUMBERS.total, + title: m.totalCapital, + value: sumCapitalNumbers, + variant: 'currency', + rightAlign: true, + }), + ], + }), + ], +}) diff --git a/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/capitalNumbersSubsection/index.ts b/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/capitalNumbersSubsection/index.ts deleted file mode 100644 index 42e21bf1259e..000000000000 --- a/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/capitalNumbersSubsection/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { - buildCustomField, - buildMultiField, - buildSubSection, -} from '@island.is/application/core' -import { m } from '../../../../lib/messages' -import { CAPITALNUMBERS } from '../../../../utils/constants' - -export const capitalNumbersSubsection = buildSubSection({ - id: 'keynumbers.capitalNumbers', - title: m.capitalNumbers, - children: [ - buildMultiField({ - id: 'capitalNumber', - title: m.capitalNumbersSectionTitle, - description: m.fillOutAppopriate, - children: [ - buildCustomField({ - id: 'capitalNumberField', - title: '', - description: '', - component: 'KeyNumbersCapital', - childInputIds: Object.values(CAPITALNUMBERS), - }), - ], - }), - ], -}) diff --git a/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/equitiesAndLiabilitiesSubsection.ts b/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/equitiesAndLiabilitiesSubsection.ts new file mode 100644 index 000000000000..fcd85ee165de --- /dev/null +++ b/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/equitiesAndLiabilitiesSubsection.ts @@ -0,0 +1,122 @@ +import { + buildAlertMessageField, + buildDescriptionField, + buildDisplayField, + buildMultiField, + buildSubSection, + buildTextField, +} from '@island.is/application/core' +import { m } from '../../../lib/messages' +import { + EQUITIESANDLIABILITIESIDS, + EQUITYANDLIABILITIESTOTALS, +} from '../../../utils/constants' +import { + showEquitiesAndLiabilitiesAlert, + sumDebts, + sumEquityAndDebts, + sumProperties, +} from '../../../utils/helpers' + +export const equitiesAndLiabilitiesSubsection = buildSubSection({ + id: 'keyNumbers.equitiesAndLiabilities', + title: m.propertiesAndDebts, + children: [ + buildMultiField({ + id: 'equitiesAndLiabilitiesMultiField', + title: m.keyNumbersDebt, + children: [ + // Assets + buildDescriptionField({ + id: 'propertiesDescription', + title: m.properties, + titleVariant: 'h3', + }), + buildTextField({ + id: EQUITIESANDLIABILITIESIDS.fixedAssetsTotal, + title: m.fixedAssetsTotal, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildTextField({ + id: EQUITIESANDLIABILITIESIDS.currentAssets, + title: m.currentAssets, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildDisplayField({ + id: EQUITYANDLIABILITIESTOTALS.assetsTotal, + title: '', + label: m.totalAssets, + value: sumProperties, + variant: 'currency', + rightAlign: true, + }), + + // Debts + buildDescriptionField({ + id: 'debtsDescription', + title: m.debts, + titleVariant: 'h3', + }), + buildTextField({ + id: EQUITIESANDLIABILITIESIDS.longTerm, + title: m.longTerm, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildTextField({ + id: EQUITIESANDLIABILITIESIDS.shortTerm, + title: m.shortTerm, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildDisplayField({ + id: EQUITYANDLIABILITIESTOTALS.liabilitiesTotal, + title: '', + label: m.totalDebts, + value: sumDebts, + variant: 'currency', + rightAlign: true, + }), + + // Equity + buildDescriptionField({ + id: 'equityDescription', + title: m.equity, + titleVariant: 'h3', + }), + buildTextField({ + id: EQUITIESANDLIABILITIESIDS.totalEquity, + title: m.equity, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + + // Total Equity and Liabilities + buildDisplayField({ + id: EQUITYANDLIABILITIESTOTALS.equityAndLiabilitiesTotal, + title: m.debtsAndCash, + titleVariant: 'h3', + value: sumEquityAndDebts, + variant: 'currency', + rightAlign: true, + }), + + // Alert if total equity and liabilities do not match total assets + buildAlertMessageField({ + condition: showEquitiesAndLiabilitiesAlert, + id: 'equitiesAndLiabilitiesAlert', + title: m.equityErrorTitle, + message: m.equityDebtsAssetsValidatorError, + alertType: 'error', + }), + ], + }), + ], +}) diff --git a/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/equitiesAndLiabilitiesSubsection/index.ts b/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/equitiesAndLiabilitiesSubsection/index.ts deleted file mode 100644 index 6920f444dac6..000000000000 --- a/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/equitiesAndLiabilitiesSubsection/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { buildCustomField, buildSubSection } from '@island.is/application/core' -import { m } from '../../../../lib/messages' -import { EQUITIESANDLIABILITIESIDS } from '../../../../utils/constants' - -export const equitiesAndLiabilitiesSubsection = buildSubSection({ - id: 'keyNumbers.equitiesAndLiabilities', - title: m.propertiesAndDebts, - children: [ - buildCustomField({ - id: 'equitiesAndLiabilities', - title: m.keyNumbersDebt, - description: m.fillOutAppopriate, - component: 'ElectionEquities', - childInputIds: Object.values(EQUITIESANDLIABILITIESIDS), - }), - ], -}) diff --git a/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/index.ts b/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/index.ts index dee0b831047e..d23e0707d9c1 100644 --- a/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/index.ts +++ b/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/index.ts @@ -1,8 +1,8 @@ import { buildSection } from '@island.is/application/core' import { m } from '../../../lib/messages' -import { operatingCostSubsection } from './operatingCostSubsection' -import { capitalNumbersSubsection } from './capitalNumbersSubsection' import { equitiesAndLiabilitiesSubsection } from './equitiesAndLiabilitiesSubsection' +import { capitalNumbersSubsection } from './capitalNumbersSubsection' +import { operatingCostSubsection } from './operatingCostSubsection' export const keyNumbersSection = buildSection({ id: 'keyNumbers', diff --git a/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/operatingCostSubsection.ts b/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/operatingCostSubsection.ts new file mode 100644 index 000000000000..5ea3e3b7e771 --- /dev/null +++ b/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/operatingCostSubsection.ts @@ -0,0 +1,129 @@ +import { + buildDescriptionField, + buildDisplayField, + buildMultiField, + buildSubSection, + buildTextField, +} from '@island.is/application/core' +import { m } from '../../../lib/messages' +import { OPERATINGCOST, PARTYOPERATIONIDS } from '../../../utils/constants' +import { sumExpenses, sumIncome, sumTotal } from '../../../utils/helpers' + +export const operatingCostSubsection = buildSubSection({ + id: 'operatingCost', + title: m.expensesIncome, + children: [ + buildMultiField({ + id: 'operatingCostMultiField', + title: m.keyNumbersIncomeAndExpenses, + children: [ + // Income + buildDescriptionField({ + id: 'incomeDescription', + title: m.income, + titleVariant: 'h3', + }), + buildTextField({ + id: PARTYOPERATIONIDS.contributionsFromTheTreasury, + title: m.contributionsFromTheTreasury, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildTextField({ + id: PARTYOPERATIONIDS.parliamentaryPartySupport, + title: m.parliamentaryPartySupport, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildTextField({ + id: PARTYOPERATIONIDS.municipalContributions, + title: m.municipalContributions, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildTextField({ + id: PARTYOPERATIONIDS.contributionsFromLegalEntities, + title: m.contributionsFromLegalEntities, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildTextField({ + id: PARTYOPERATIONIDS.contributionsFromIndividuals, + title: m.contributionsFromIndividuals, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildTextField({ + id: PARTYOPERATIONIDS.generalMembershipFees, + title: m.generalMembershipFees, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildTextField({ + id: PARTYOPERATIONIDS.otherIncome, + title: m.otherIncome, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildDisplayField({ + id: PARTYOPERATIONIDS.totalIncome, + title: '', + label: m.totalIncome, + value: sumIncome, + variant: 'currency', + rightAlign: true, + }), + + // Expenses + buildDescriptionField({ + id: 'expensesDescription', + title: m.expenses, + titleVariant: 'h3', + }), + buildTextField({ + id: PARTYOPERATIONIDS.electionOffice, + title: m.electionOffice, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildTextField({ + id: PARTYOPERATIONIDS.otherCost, + title: m.otherOperationalCost, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildDisplayField({ + id: PARTYOPERATIONIDS.totalExpense, + title: '', + label: m.totalExpenses, + value: sumExpenses, + variant: 'currency', + rightAlign: true, + }), + + // Total + buildDescriptionField({ + id: 'totalDescription', + title: m.operatingCost, + titleVariant: 'h3', + }), + buildDisplayField({ + id: OPERATINGCOST.total, + title: '', + value: sumTotal, + variant: 'currency', + rightAlign: true, + }), + ], + }), + ], +}) diff --git a/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/operatingCostSubsection/index.ts b/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/operatingCostSubsection/index.ts deleted file mode 100644 index fbc76af55a1b..000000000000 --- a/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/operatingCostSubsection/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { buildCustomField, buildSubSection } from '@island.is/application/core' -import { m } from '../../../../lib/messages' -import { PARTYOPERATIONIDS } from '../../../../utils/constants' - -export const operatingCostSubsection = buildSubSection({ - id: 'operatingCost', - title: m.expensesIncome, - children: [ - buildCustomField({ - id: 'partyOperations', - title: m.keyNumbersIncomeAndExpenses, - description: m.fillOutAppopriate, - component: 'PartyOperatingIncome', - childInputIds: Object.values(PARTYOPERATIONIDS), - }), - ], -}) diff --git a/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/overviewSection/index.ts b/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/overviewSection/index.ts deleted file mode 100644 index 543a96b5fa01..000000000000 --- a/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/overviewSection/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { buildSection } from '@island.is/application/core' -import { overviewMultiField } from './overviewMultiField' -import { m } from '../../../../lib/messages' - -export const overviewSection = buildSection({ - id: 'overviewSection', - title: m.overviewSectionTitle, - children: [overviewMultiField], -}) diff --git a/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/overviewSection/overviewMultiField.ts b/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/overviewSection/overviewMultiField.ts deleted file mode 100644 index d466187af16c..000000000000 --- a/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/keyNumbersSection/overviewSection/overviewMultiField.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { - buildCustomField, - buildMultiField, - getValueViaPath, -} from '@island.is/application/core' -import { m } from '../../../../lib/messages' -import { GREATER, LESS } from '../../../../utils/constants' - -export const overviewMultiField = buildMultiField({ - id: 'overview', - title: m.yearlyOverview, - description: m.review, - children: [ - buildCustomField({ - id: 'overviewPartyField', - title: '', - doesNotRequireAnswer: true, - component: 'PartyOverview', - }), - buildCustomField({ - id: 'overviewField', - title: '', - condition: (answers) => - getValueViaPath(answers, 'election.incomeLimit') === GREATER, - doesNotRequireAnswer: true, - component: 'Overview', - }), - buildCustomField({ - id: 'overviewStatementField', - title: '', - condition: (answers) => - getValueViaPath(answers, 'election.incomeLimit') === LESS, - doesNotRequireAnswer: true, - component: 'ElectionStatement', - }), - ], -}) diff --git a/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/overviewSection/index.ts b/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/overviewSection/index.ts new file mode 100644 index 000000000000..c118b90933cc --- /dev/null +++ b/libs/application/templates/inao/financial-statement-political-party/src/forms/applicationForm/overviewSection/index.ts @@ -0,0 +1,51 @@ +import { + buildCheckboxField, + buildCustomField, + buildMultiField, + buildSection, + buildSubmitField, + YES, +} from '@island.is/application/core' +import { m } from '../../../lib/messages' +import { DefaultEvents } from '@island.is/application/types' + +export const overviewSection = buildSection({ + id: 'overviewSection', + title: m.overviewSectionTitle, + children: [ + buildMultiField({ + id: 'overview', + title: m.yearlyOverview, + description: m.review, + children: [ + buildCustomField({ + id: 'overviewPartyField', + title: '', + doesNotRequireAnswer: true, + component: 'PartyOverview', + }), + buildCheckboxField({ + id: 'approveOverview', + title: '', + options: [ + { + label: m.overviewCorrect, + value: YES, + }, + ], + }), + buildSubmitField({ + id: 'overview.submit', + title: '', + actions: [ + { + event: DefaultEvents.SUBMIT, + name: m.send, + type: 'primary', + }, + ], + }), + ], + }), + ], +}) diff --git a/libs/application/templates/inao/financial-statement-political-party/src/forms/done/conclusionSection/index.ts b/libs/application/templates/inao/financial-statement-political-party/src/forms/done/conclusionSection/index.ts deleted file mode 100644 index d6b9add9c2e4..000000000000 --- a/libs/application/templates/inao/financial-statement-political-party/src/forms/done/conclusionSection/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { - buildCustomField, - buildMultiField, - buildSection, -} from '@island.is/application/core' -import { m } from '../../../lib/messages' - -export const conclusionSection = buildSection({ - id: 'conclusionSection', - title: '', - children: [ - buildMultiField({ - id: 'conclusion', - title: m.received, - children: [ - buildCustomField({ - id: 'overview', - component: 'Success', - title: m.applicationAccept, - }), - ], - }), - ], -}) diff --git a/libs/application/templates/inao/financial-statement-political-party/src/forms/done/index.ts b/libs/application/templates/inao/financial-statement-political-party/src/forms/done/index.ts index bfb95dec1cee..2f4ee479f333 100644 --- a/libs/application/templates/inao/financial-statement-political-party/src/forms/done/index.ts +++ b/libs/application/templates/inao/financial-statement-political-party/src/forms/done/index.ts @@ -1,6 +1,6 @@ import { buildForm } from '@island.is/application/core' import { Form, FormModes } from '@island.is/application/types' -import { conclusionSection } from './conclusionSection' +import { conclusionSection } from '../applicationForm/conclusionSection' export const done: Form = buildForm({ id: 'done', diff --git a/libs/application/templates/inao/financial-statement-political-party/src/lib/dataSchema.ts b/libs/application/templates/inao/financial-statement-political-party/src/lib/dataSchema.ts index 33f3ea983270..cb85a78c893a 100644 --- a/libs/application/templates/inao/financial-statement-political-party/src/lib/dataSchema.ts +++ b/libs/application/templates/inao/financial-statement-political-party/src/lib/dataSchema.ts @@ -3,6 +3,7 @@ import { m } from './messages' import * as kennitala from 'kennitala' import { parsePhoneNumberFromString } from 'libphonenumber-js/min' import { checkIfNegative } from '../utils/helpers' +import { YES } from '@island.is/application/core' const requiredNonNegativeString = z .string() @@ -16,9 +17,11 @@ const FileSchema = z.object({ key: z.string(), url: z.string().optional(), }) + const conditionalAbout = z.object({ operatingYear: requiredString, }) + const about = z.object({ nationalId: z .string() @@ -38,17 +41,7 @@ const about = z.object({ email: z.string().email(), }) -const election = z.object({ - selectElection: z.string().optional(), - electionName: z.string().optional(), - genitiveName: z.string().optional(), - incomeLimit: requiredString, -}) - -const operatingCost = z.object({ - total: requiredString, -}) - +// Key numbers - operating cost -income const partyIncome = z.object({ contributionsFromTheTreasury: requiredNonNegativeString, parliamentaryPartySupport: requiredNonNegativeString, @@ -60,54 +53,70 @@ const partyIncome = z.object({ total: z.string(), }) +// Key numbers - operating cost - expenses const partyExpense = z.object({ electionOffice: requiredNonNegativeString, otherCost: requiredNonNegativeString, total: z.string(), }) +// Key numbers - operating cost - total +const operatingCost = z.object({ + total: requiredString, +}) + +// Key numbers - capital numbers const capitalNumbers = z.object({ capitalIncome: requiredNonNegativeString, capitalCost: requiredNonNegativeString, total: z.string(), }) -const equityAndLiabilities = z.object({ - total: z.string(), -}) - +// Key numbers - equities and liabilities - assets const asset = z.object({ currentAssets: requiredNonNegativeString, fixedAssetsTotal: requiredNonNegativeString, - total: requiredString, -}) - -const equity = z.object({ - totalEquity: requiredString, }) +// Key numbers - equities and liabilities - liabilities const liability = z.object({ longTerm: requiredNonNegativeString, shortTerm: requiredNonNegativeString, - total: requiredNonNegativeString, }) +// Key numbers - equities and liabilities - equity +const equity = z.object({ + totalEquity: requiredString, +}) + +// Key numbers - equities and liabilities - total +const equityAndLiabilitiesTotals = z + .object({ + assetsTotal: z.string(), + liabilitiesTotal: z.string(), + equityAndLiabilitiesTotal: z.string(), + }) + .refine((x) => x.assetsTotal === x.equityAndLiabilitiesTotal, { + message: 'equityAndLiabilities.total must match assets.total', + path: ['equityAndLiabilitiesTotals', 'equityAndLiabilitiesTotal'], + }) + export const dataSchema = z.object({ approveExternalData: z.literal(true), conditionalAbout, about, - election, // Needed?? operatingCost, partyIncome, partyExpense, capitalNumbers, - equityAndLiabilities, + equityAndLiabilitiesTotals, asset, equity, liability, attachments: z.object({ file: z.array(FileSchema).nonempty(), }), + approveOverview: z.array(z.literal(YES)).length(1), }) export type FinancialStatementPoliticalParty = z.TypeOf diff --git a/libs/application/templates/inao/financial-statement-political-party/src/lib/messages.ts b/libs/application/templates/inao/financial-statement-political-party/src/lib/messages.ts index 917fa4927876..846a336cbe53 100644 --- a/libs/application/templates/inao/financial-statement-political-party/src/lib/messages.ts +++ b/libs/application/templates/inao/financial-statement-political-party/src/lib/messages.ts @@ -242,7 +242,7 @@ export const m = defineMessages({ }, capitalNumbersSectionTitle: { id: 'fspp.application:income.capitalNumbersSectionTitle', - defaultMessage: 'Lykiltölur FjĂĄrmagnsliĂ°ir', + defaultMessage: 'Lykiltölur - FjĂĄrmagnsliĂ°ir', description: 'capital numbers', }, capitalIncome: { @@ -267,12 +267,12 @@ export const m = defineMessages({ }, keyNumbersDebt: { id: 'fspp.application:keyNumbers.debt', - defaultMessage: 'Lykiltölur - Eignir, Skuldir og eigiĂ° fĂ©', + defaultMessage: 'Lykiltölur - Eignir, skuldir og eigiĂ° fĂ©', description: 'Statement debts', }, equityDebtsAssetsValidatorError: { id: 'fspp.application:equityValidatorError', - defaultMessage: 'Skuldir og eigiĂ° fĂ© ĂŸarf aĂ° vera jafnt og eignir samtals', + defaultMessage: 'Skuldir og eigiĂ° fĂ© ĂŸurfa aĂ° vera jöfn og eignum samtals', description: 'Equity + debts shout equal assets', }, properties: { @@ -305,6 +305,11 @@ export const m = defineMessages({ defaultMessage: 'Eignir samtals', description: 'Total assets', }, + debts: { + id: 'fspp.application:keyNumbers.debts', + defaultMessage: 'Skuldir', + description: 'debts', + }, debtsAndEquity: { id: 'fspp.application:keyNumbers.debtsAndEquity', defaultMessage: 'Skuldir og eigiĂ° fĂ©', @@ -570,4 +575,10 @@ export const m = defineMessages({ 'Ef ĂŸĂș telur aĂ° ĂŸessi kennitala ĂŠtti aĂ° vera skrĂĄĂ° sem stjĂłrnmĂĄlasamtök ĂŸĂĄ bendum viĂ° ĂŸĂ©r ĂĄ aĂ° hafa samband viĂ° RĂ­kisendurskoĂ°un Ă­ sĂ­ma 448 8800', description: 'Descriptionwhen user is not allowed to apply', }, + conclusionAlertMessage: { + id: 'fspp.application:conclusionAlertMessage', + defaultMessage: + 'Ársreikning fyrir rekstrarĂĄriĂ° {value1} hefur veriĂ° skilaĂ°', + description: 'Conclusion alert message', + }, }) diff --git a/libs/application/templates/inao/financial-statement-political-party/src/utils/constants.ts b/libs/application/templates/inao/financial-statement-political-party/src/utils/constants.ts index 6a4de5c9f7b5..3027e4f74b38 100644 --- a/libs/application/templates/inao/financial-statement-political-party/src/utils/constants.ts +++ b/libs/application/templates/inao/financial-statement-political-party/src/utils/constants.ts @@ -51,17 +51,21 @@ export const EQUITIESANDLIABILITIESIDS = { assetPrefix: 'asset', currentAssets: 'asset.currentAssets', fixedAssetsTotal: 'asset.fixedAssetsTotal', - assetTotal: 'asset.total', liabilityPrefix: 'liability', longTerm: 'liability.longTerm', shortTerm: 'liability.shortTerm', asset: 'liability.asset', - totalLiability: 'liability.total', operationResult: 'equity.operationResult', equityPrefix: 'equity', totalEquity: 'equity.totalEquity', totalCash: 'equity.total', - totalEquityAndLiabilities: 'equityAndLiabilities.total', +} + +export const EQUITYANDLIABILITIESTOTALS = { + assetsTotal: 'equityAndLiabilitiesTotals.assetsTotal', + liabilitiesTotal: 'equityAndLiabilitiesTotals.liabilitiesTotal', + equityAndLiabilitiesTotal: + 'equityAndLiabilitiesTotals.equityAndLiabilitiesTotal', } // Error helpers diff --git a/libs/application/templates/inao/financial-statement-political-party/src/utils/helpers.ts b/libs/application/templates/inao/financial-statement-political-party/src/utils/helpers.ts index 82ac09edeb37..723bad714d36 100644 --- a/libs/application/templates/inao/financial-statement-political-party/src/utils/helpers.ts +++ b/libs/application/templates/inao/financial-statement-political-party/src/utils/helpers.ts @@ -1,7 +1,15 @@ import { Config } from '../types/types' import subYears from 'date-fns/subYears' import getYear from 'date-fns/getYear' -import { TOTAL } from './constants' +import { + CAPITALNUMBERS, + EQUITIESANDLIABILITIESIDS, + EQUITYANDLIABILITIESTOTALS, + PARTYOPERATIONIDS, + TOTAL, +} from './constants' +import { FormValue } from '@island.is/application/types' +import { getValueViaPath } from '@island.is/application/core' export const getConfigInfoForKey = (config: Config[], configKey: string) => { return config?.filter((config: Config) => config.key === configKey)[0].value @@ -42,3 +50,170 @@ export const formatCurrency = (answer: string) => export const formatNumber = (num: number) => num.toLocaleString('de-DE') export const checkIfNegative = (inputNumber: string) => Number(inputNumber) >= 0 + +export const sumIncome = (answers: FormValue) => { + const contributionsFromTheTreasury = getValueViaPath( + answers, + PARTYOPERATIONIDS.contributionsFromTheTreasury, + ) + const parliamentaryPartySupport = getValueViaPath( + answers, + PARTYOPERATIONIDS.parliamentaryPartySupport, + ) + const municipalContributions = getValueViaPath( + answers, + PARTYOPERATIONIDS.municipalContributions, + ) + const contributionsFromLegalEntities = getValueViaPath( + answers, + PARTYOPERATIONIDS.contributionsFromLegalEntities, + ) + const contributionsFromIndividuals = getValueViaPath( + answers, + PARTYOPERATIONIDS.contributionsFromIndividuals, + ) + const generalMembershipFees = getValueViaPath( + answers, + PARTYOPERATIONIDS.generalMembershipFees, + ) + const otherIncome = getValueViaPath( + answers, + PARTYOPERATIONIDS.otherIncome, + ) + + return `${ + parseInt(contributionsFromTheTreasury || '0') + + parseInt(parliamentaryPartySupport || '0') + + parseInt(municipalContributions || '0') + + parseInt(contributionsFromLegalEntities || '0') + + parseInt(contributionsFromIndividuals || '0') + + parseInt(generalMembershipFees || '0') + + parseInt(otherIncome || '0') + }` +} + +export const sumExpenses = (answers: FormValue) => { + const electionOffice = getValueViaPath( + answers, + PARTYOPERATIONIDS.electionOffice, + ) + const otherCost = getValueViaPath( + answers, + PARTYOPERATIONIDS.otherCost, + ) + + return `${parseInt(electionOffice || '0') + parseInt(otherCost || '0')}` +} + +export const sumTotal = (answers: FormValue) => { + const income = getValueViaPath(answers, PARTYOPERATIONIDS.totalIncome) + const expenses = getValueViaPath( + answers, + PARTYOPERATIONIDS.totalExpense, + ) + return `${parseInt(income || '0') - parseInt(expenses || '0')}` +} + +export const sumCapitalNumbers = (answers: FormValue) => { + const capitalIncome = getValueViaPath( + answers, + CAPITALNUMBERS.capitalIncome, + ) + const capitalCost = getValueViaPath( + answers, + CAPITALNUMBERS.capitalCost, + ) + return `${parseInt(capitalIncome || '0') - parseInt(capitalCost || '0')}` +} + +export const sumProperties = (answers: FormValue) => { + const fixedAssetsTotal = getValueViaPath( + answers, + EQUITIESANDLIABILITIESIDS.fixedAssetsTotal, + ) + const currentAssets = getValueViaPath( + answers, + EQUITIESANDLIABILITIESIDS.currentAssets, + ) + return `${parseInt(fixedAssetsTotal || '0') + parseInt(currentAssets || '0')}` +} + +export const sumDebts = (answers: FormValue) => { + const longTerm = getValueViaPath( + answers, + EQUITIESANDLIABILITIESIDS.longTerm, + ) + const shortTerm = getValueViaPath( + answers, + EQUITIESANDLIABILITIESIDS.shortTerm, + ) + return `${parseInt(longTerm || '0') + parseInt(shortTerm || '0')}` +} + +export const sumEquityAndDebts = (answers: FormValue) => { + const totalEquity = getValueViaPath( + answers, + EQUITIESANDLIABILITIESIDS.totalEquity, + ) + const totalLiability = getValueViaPath( + answers, + EQUITYANDLIABILITIESTOTALS.liabilitiesTotal, + ) + return `${parseInt(totalEquity || '0') + parseInt(totalLiability || '0')}` +} + +export const showEquitiesAndLiabilitiesAlert = (answers: FormValue) => { + // Assets + const fixedAssetsTotal = getValueViaPath( + answers, + EQUITIESANDLIABILITIESIDS.fixedAssetsTotal, + ) + const currentAssets = getValueViaPath( + answers, + EQUITIESANDLIABILITIESIDS.currentAssets, + ) + const totalAssets = getValueViaPath( + answers, + EQUITYANDLIABILITIESTOTALS.assetsTotal, + ) + + // Debts + const longTerm = getValueViaPath( + answers, + EQUITIESANDLIABILITIESIDS.longTerm, + ) + const shortTerm = getValueViaPath( + answers, + EQUITIESANDLIABILITIESIDS.shortTerm, + ) + const totalLiability = getValueViaPath( + answers, + EQUITYANDLIABILITIESTOTALS.liabilitiesTotal, + ) + + // Equity + const totalEquity = getValueViaPath( + answers, + EQUITIESANDLIABILITIESIDS.totalEquity, + ) + + // Total Equity and Liabilities + const totalEquityAndLiabilities = getValueViaPath( + answers, + EQUITYANDLIABILITIESTOTALS.equityAndLiabilitiesTotal, + ) + + if ( + !fixedAssetsTotal || + !currentAssets || + !totalAssets || + !longTerm || + !shortTerm || + !totalLiability || + !totalEquity || + !totalEquityAndLiabilities + ) { + return false + } + return totalAssets !== totalEquityAndLiabilities +} diff --git a/libs/application/types/src/lib/Fields.ts b/libs/application/types/src/lib/Fields.ts index 877dc89d4621..f89d41400fcf 100644 --- a/libs/application/types/src/lib/Fields.ts +++ b/libs/application/types/src/lib/Fields.ts @@ -783,6 +783,7 @@ export interface DisplayField extends BaseField { titleVariant?: TitleVariants suffix?: MessageDescriptor | string rightAlign?: boolean + halfWidthOwnline?: boolean variant?: TextFieldVariant label?: MessageDescriptor | string value: (answers: FormValue) => string diff --git a/libs/application/ui-fields/src/lib/DisplayFormField/DisplayFormField.tsx b/libs/application/ui-fields/src/lib/DisplayFormField/DisplayFormField.tsx index ae6faba48fed..58e29e6d6bcf 100644 --- a/libs/application/ui-fields/src/lib/DisplayFormField/DisplayFormField.tsx +++ b/libs/application/ui-fields/src/lib/DisplayFormField/DisplayFormField.tsx @@ -21,6 +21,7 @@ export const DisplayFormField = ({ field, application }: Props) => { variant, suffix, rightAlign = false, + halfWidthOwnline = false, } = field const { watch, setValue } = useFormContext() const allValues = watch() @@ -29,52 +30,66 @@ export const DisplayFormField = ({ field, application }: Props) => { useEffect(() => { const newDisplayValue = value(allValues) - setDisplayValue(newDisplayValue) - setValue(id, newDisplayValue) + if (newDisplayValue !== displayValue) { + setDisplayValue(newDisplayValue) + setValue(id, newDisplayValue) + } }, [allValues]) return ( - - {title ? ( - - {formatTextWithLocale( - title, - application, - locale as Locale, - formatMessage, - )} - - ) : null} + + + {title ? ( + + {formatTextWithLocale( + title, + application, + locale as Locale, + formatMessage, + )} + + ) : null} - + + ) } diff --git a/libs/application/ui-forms/src/lib/formConclusionSection/formConclusionSection.ts b/libs/application/ui-forms/src/lib/formConclusionSection/formConclusionSection.ts index 28f3428b7347..fc5669aaee68 100644 --- a/libs/application/ui-forms/src/lib/formConclusionSection/formConclusionSection.ts +++ b/libs/application/ui-forms/src/lib/formConclusionSection/formConclusionSection.ts @@ -18,6 +18,7 @@ type Props = Partial<{ secondButtonLink: StaticText secondButtonLabel: StaticText secondButtonMessage: StaticText + accordion?: boolean expandableHeader: FormText expandableIntro: FormText expandableDescription: FormText @@ -38,6 +39,7 @@ type Props = Partial<{ * @param alertMessage The message inside the green alert box. * @param alertType The type of alert, can be success, warning, error, info. * JUST ADDED * * @param multiFieldTitle Title of the conclusion section. * JUST ADDED * + * @param accordion If false, there will be no accordion. * @param expandableHeader Header of the expandable description section. * @param expandableIntro Intro text of the expandable description section. * @param expandableDescription Markdown code for the expandable description section, most applications use bulletpoints. @@ -54,6 +56,7 @@ export const buildFormConclusionSection = ({ alertMessage = conclusion.alertMessageField.message, alertType = 'success', multiFieldTitle = conclusion.information.formTitle, + accordion = true, expandableHeader = conclusion.expandableDescriptionField.title, expandableIntro = conclusion.expandableDescriptionField.introText, expandableDescription = conclusion.expandableDescriptionField.description, @@ -64,8 +67,20 @@ export const buildFormConclusionSection = ({ bottomButtonLink = '/minarsidur/umsoknir', bottomButtonLabel = coreMessages.openServicePortalButtonTitle, bottomButtonMessage = coreMessages.openServicePortalMessageText, -}: Props) => - buildSection({ +}: Props) => { + const expandableDescriptionField = accordion + ? [ + buildExpandableDescriptionField({ + id: 'uiForms.conclusionExpandableDescription', + title: expandableHeader, + introText: expandableIntro, + description: expandableDescription, + startExpanded: true, + }), + ] + : [] + + return buildSection({ id: 'uiForms.conclusionSection', title: sectionTitle, children: [ @@ -88,13 +103,7 @@ export const buildFormConclusionSection = ({ alertType: alertType, message: alertMessage, }), - buildExpandableDescriptionField({ - id: 'uiForms.conclusionExpandableDescription', - title: expandableHeader, - introText: expandableIntro, - description: expandableDescription, - startExpanded: true, - }), + ...expandableDescriptionField, buildMessageWithLinkButtonField({ id: 'uiForms.conclusionBottomLink', title: '', @@ -107,3 +116,4 @@ export const buildFormConclusionSection = ({ }), ], }) +} From 429a55f4ac2c5fae81299e4b7981f7e1035c6fba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9E=C3=B3r=C3=B0ur=20H?= Date: Fri, 6 Dec 2024 13:40:58 +0000 Subject: [PATCH 54/85] fix(domains-education): Update education inna resolver (#17156) * Inna education scope * remove flag --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- libs/api/domains/education/src/lib/graphql/inna.resolver.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libs/api/domains/education/src/lib/graphql/inna.resolver.ts b/libs/api/domains/education/src/lib/graphql/inna.resolver.ts index 3c8479c3063b..fc1c2b3e9d27 100644 --- a/libs/api/domains/education/src/lib/graphql/inna.resolver.ts +++ b/libs/api/domains/education/src/lib/graphql/inna.resolver.ts @@ -23,8 +23,7 @@ import { DiplomaModel } from './inna/diplomas.model' @Resolver() @UseGuards(IdsUserGuard, ScopesGuard, FeatureFlagGuard) -@Scopes(ApiScope.internal) -@FeatureFlag(Features.servicePortalSecondaryEducationPages) +@Scopes(ApiScope.education) @Audit({ namespace: '@island.is/api/education-inna' }) export class InnaResolver { constructor(private innaService: InnaClientService) {} From 8d1ad4163342b868dcf36b3703b294adf27576e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAnar=20Vestmann?= <43557895+RunarVestmann@users.noreply.github.com> Date: Fri, 6 Dec 2024 18:30:03 +0000 Subject: [PATCH 55/85] feat(web): WHODAS calculator - Remove total score in breakdown and update texts (#17163) * Remove total score in breakdown and update texts * Remove unused translation string --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../components/connected/WHODAS/Calculator.css.ts | 6 ------ .../connected/WHODAS/Calculator.strings.ts | 10 +++++----- .../components/connected/WHODAS/Calculator.tsx | 15 +++++++-------- 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/apps/web/components/connected/WHODAS/Calculator.css.ts b/apps/web/components/connected/WHODAS/Calculator.css.ts index 81dca8be11dc..773ba0039ecb 100644 --- a/apps/web/components/connected/WHODAS/Calculator.css.ts +++ b/apps/web/components/connected/WHODAS/Calculator.css.ts @@ -10,12 +10,6 @@ export const breakdownRowContainer = style({ gap: theme.spacing[5], }) -export const totalScoreRowContainer = style({ - display: 'grid', - gridTemplateColumns: `${leftWidth + 24}px 1fr`, - gap: theme.spacing[5], -}) - export const stayOnSinglePageWhenPrinting = style({ pageBreakInside: 'avoid', }) diff --git a/apps/web/components/connected/WHODAS/Calculator.strings.ts b/apps/web/components/connected/WHODAS/Calculator.strings.ts index 8d81c1bb4750..03fc832b8a23 100644 --- a/apps/web/components/connected/WHODAS/Calculator.strings.ts +++ b/apps/web/components/connected/WHODAS/Calculator.strings.ts @@ -62,11 +62,6 @@ export const m = { 'Takk fyrir aĂ° svara spurningalistanum, Mat ĂĄ fĂŠrni ĂŸinni. Mat ĂŸitt ĂĄ fĂŠrni er stuĂ°ningur viĂ° aĂ° meta ĂŸĂ¶rf ĂŸĂ­na fyrir heimaĂŸjĂłnustu. Ef ĂŸĂș hefur ekki svaraĂ° öllum spurningum getur ĂŸaĂ° haft ĂĄhrif ĂĄ niĂ°urstöðuna.', description: 'Texti fyrir ofan niĂ°urstöðuskjĂĄ', }, - totalScore: { - id: 'web.whodas.calculator:results.totalScore', - defaultMessage: 'Stig samtals', - description: 'Stig samtals', - }, resultDisclaimer: { id: 'web.whodas.calculator:results.resultDisclaimer', defaultMessage: @@ -124,5 +119,10 @@ export const m = { defaultMessage: 'SundurliĂ°un ĂĄ niĂ°urstöðum', description: 'SundurliĂ°un ĂĄ niĂ°urstöðum', }, + outOf100: { + id: 'web.whodas.calculator:results.outOf100', + defaultMessage: 'af 100', + description: 'af 100', + }, }), } diff --git a/apps/web/components/connected/WHODAS/Calculator.tsx b/apps/web/components/connected/WHODAS/Calculator.tsx index 53d09cfad3cb..fc96577ef4f3 100644 --- a/apps/web/components/connected/WHODAS/Calculator.tsx +++ b/apps/web/components/connected/WHODAS/Calculator.tsx @@ -162,7 +162,9 @@ const WHODASResults = ({ {formatMessage(m.results.scoreHeading)} - {formatScore(totalScore)} + + {formatScore(totalScore)} {formatMessage(m.results.outOf100)} + @@ -203,17 +205,14 @@ const WHODASResults = ({ {step.title} - {formatScore(step.scoreForStep)} + + {formatScore(step.scoreForStep)}{' '} + {formatMessage(m.results.outOf100)} + ))} - - - {formatMessage(m.results.totalScore)} - - {formatScore(totalScore)} - Date: Fri, 6 Dec 2024 18:43:43 +0000 Subject: [PATCH 56/85] fix: add missing datadog ns grant to ids web (#17167) * fix: add missing datadog ns grant to ids web * chore: charts update dirty files * chore: nx format:write update dirty files * added grant for remainder of services in ns * chore: charts update dirty files --------- Co-authored-by: andes-it Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- apps/services/auth/ids-api/infra/identity-server.ts | 1 + apps/services/auth/ids-api/infra/ids-api.ts | 2 +- charts/identity-server/values.dev.yaml | 3 +++ charts/identity-server/values.prod.yaml | 3 +++ charts/identity-server/values.staging.yaml | 3 +++ charts/services/identity-server/values.dev.yaml | 1 + charts/services/identity-server/values.prod.yaml | 1 + charts/services/identity-server/values.staging.yaml | 1 + charts/services/services-auth-ids-api-cleanup/values.dev.yaml | 1 + charts/services/services-auth-ids-api-cleanup/values.prod.yaml | 1 + .../services/services-auth-ids-api-cleanup/values.staging.yaml | 1 + charts/services/services-auth-ids-api/values.dev.yaml | 1 + charts/services/services-auth-ids-api/values.prod.yaml | 1 + charts/services/services-auth-ids-api/values.staging.yaml | 1 + 14 files changed, 20 insertions(+), 1 deletion(-) diff --git a/apps/services/auth/ids-api/infra/identity-server.ts b/apps/services/auth/ids-api/infra/identity-server.ts index 0e953612dc11..60fe5358f7b4 100644 --- a/apps/services/auth/ids-api/infra/identity-server.ts +++ b/apps/services/auth/ids-api/infra/identity-server.ts @@ -196,5 +196,6 @@ export const serviceSetup = (services: { 'nginx-ingress-external', 'user-notification', 'portals-admin', + 'datadog', ) } diff --git a/apps/services/auth/ids-api/infra/ids-api.ts b/apps/services/auth/ids-api/infra/ids-api.ts index 711f29537f1f..1afda547b717 100644 --- a/apps/services/auth/ids-api/infra/ids-api.ts +++ b/apps/services/auth/ids-api/infra/ids-api.ts @@ -137,7 +137,7 @@ export const serviceSetup = (): ServiceBuilder<'services-auth-ids-api'> => { min: 2, max: 15, }) - .grantNamespaces('nginx-ingress-external', 'user-notification') + .grantNamespaces('nginx-ingress-external', 'user-notification', 'datadog') } const cleanupId = 'services-auth-ids-api-cleanup' diff --git a/charts/identity-server/values.dev.yaml b/charts/identity-server/values.dev.yaml index 24f06cb5833f..a5d5ce51e239 100644 --- a/charts/identity-server/values.dev.yaml +++ b/charts/identity-server/values.dev.yaml @@ -129,6 +129,7 @@ identity-server: - 'nginx-ingress-external' - 'user-notification' - 'portals-admin' + - 'datadog' grantNamespacesEnabled: true healthCheck: liveness: @@ -457,6 +458,7 @@ services-auth-ids-api: grantNamespaces: - 'nginx-ingress-external' - 'user-notification' + - 'datadog' grantNamespacesEnabled: true healthCheck: liveness: @@ -565,6 +567,7 @@ services-auth-ids-api-cleanup: - 'nginx-ingress-external' - 'user-notification' - 'portals-admin' + - 'datadog' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/identity-server/values.prod.yaml b/charts/identity-server/values.prod.yaml index e11425f90d02..2f51a29e6326 100644 --- a/charts/identity-server/values.prod.yaml +++ b/charts/identity-server/values.prod.yaml @@ -127,6 +127,7 @@ identity-server: - 'nginx-ingress-external' - 'user-notification' - 'portals-admin' + - 'datadog' grantNamespacesEnabled: true healthCheck: liveness: @@ -454,6 +455,7 @@ services-auth-ids-api: grantNamespaces: - 'nginx-ingress-external' - 'user-notification' + - 'datadog' grantNamespacesEnabled: true healthCheck: liveness: @@ -562,6 +564,7 @@ services-auth-ids-api-cleanup: - 'nginx-ingress-external' - 'user-notification' - 'portals-admin' + - 'datadog' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/identity-server/values.staging.yaml b/charts/identity-server/values.staging.yaml index 1a6f05f0253d..00cbc00e4a90 100644 --- a/charts/identity-server/values.staging.yaml +++ b/charts/identity-server/values.staging.yaml @@ -129,6 +129,7 @@ identity-server: - 'nginx-ingress-external' - 'user-notification' - 'portals-admin' + - 'datadog' grantNamespacesEnabled: true healthCheck: liveness: @@ -457,6 +458,7 @@ services-auth-ids-api: grantNamespaces: - 'nginx-ingress-external' - 'user-notification' + - 'datadog' grantNamespacesEnabled: true healthCheck: liveness: @@ -565,6 +567,7 @@ services-auth-ids-api-cleanup: - 'nginx-ingress-external' - 'user-notification' - 'portals-admin' + - 'datadog' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/services/identity-server/values.dev.yaml b/charts/services/identity-server/values.dev.yaml index ea8cd4ed5200..50d9763ee45a 100644 --- a/charts/services/identity-server/values.dev.yaml +++ b/charts/services/identity-server/values.dev.yaml @@ -59,6 +59,7 @@ grantNamespaces: - 'nginx-ingress-external' - 'user-notification' - 'portals-admin' + - 'datadog' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/services/identity-server/values.prod.yaml b/charts/services/identity-server/values.prod.yaml index d086237e7757..7daf65fea874 100644 --- a/charts/services/identity-server/values.prod.yaml +++ b/charts/services/identity-server/values.prod.yaml @@ -59,6 +59,7 @@ grantNamespaces: - 'nginx-ingress-external' - 'user-notification' - 'portals-admin' + - 'datadog' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/services/identity-server/values.staging.yaml b/charts/services/identity-server/values.staging.yaml index aa4536101391..ebe78fbb22a2 100644 --- a/charts/services/identity-server/values.staging.yaml +++ b/charts/services/identity-server/values.staging.yaml @@ -59,6 +59,7 @@ grantNamespaces: - 'nginx-ingress-external' - 'user-notification' - 'portals-admin' + - 'datadog' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/services/services-auth-ids-api-cleanup/values.dev.yaml b/charts/services/services-auth-ids-api-cleanup/values.dev.yaml index 5a4276deb188..7922277ce890 100644 --- a/charts/services/services-auth-ids-api-cleanup/values.dev.yaml +++ b/charts/services/services-auth-ids-api-cleanup/values.dev.yaml @@ -36,6 +36,7 @@ grantNamespaces: - 'nginx-ingress-external' - 'user-notification' - 'portals-admin' + - 'datadog' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/services/services-auth-ids-api-cleanup/values.prod.yaml b/charts/services/services-auth-ids-api-cleanup/values.prod.yaml index 88701a214d23..0dbd5e238973 100644 --- a/charts/services/services-auth-ids-api-cleanup/values.prod.yaml +++ b/charts/services/services-auth-ids-api-cleanup/values.prod.yaml @@ -36,6 +36,7 @@ grantNamespaces: - 'nginx-ingress-external' - 'user-notification' - 'portals-admin' + - 'datadog' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/services/services-auth-ids-api-cleanup/values.staging.yaml b/charts/services/services-auth-ids-api-cleanup/values.staging.yaml index 62add51ed018..9bc51cf470c9 100644 --- a/charts/services/services-auth-ids-api-cleanup/values.staging.yaml +++ b/charts/services/services-auth-ids-api-cleanup/values.staging.yaml @@ -36,6 +36,7 @@ grantNamespaces: - 'nginx-ingress-external' - 'user-notification' - 'portals-admin' + - 'datadog' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/services/services-auth-ids-api/values.dev.yaml b/charts/services/services-auth-ids-api/values.dev.yaml index 7ad3e73b5919..e1461ecdd4a9 100644 --- a/charts/services/services-auth-ids-api/values.dev.yaml +++ b/charts/services/services-auth-ids-api/values.dev.yaml @@ -63,6 +63,7 @@ env: grantNamespaces: - 'nginx-ingress-external' - 'user-notification' + - 'datadog' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/services/services-auth-ids-api/values.prod.yaml b/charts/services/services-auth-ids-api/values.prod.yaml index 80f73850121a..48b414b22bb9 100644 --- a/charts/services/services-auth-ids-api/values.prod.yaml +++ b/charts/services/services-auth-ids-api/values.prod.yaml @@ -63,6 +63,7 @@ env: grantNamespaces: - 'nginx-ingress-external' - 'user-notification' + - 'datadog' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/services/services-auth-ids-api/values.staging.yaml b/charts/services/services-auth-ids-api/values.staging.yaml index be6dc7b16fcb..5b620fa22960 100644 --- a/charts/services/services-auth-ids-api/values.staging.yaml +++ b/charts/services/services-auth-ids-api/values.staging.yaml @@ -63,6 +63,7 @@ env: grantNamespaces: - 'nginx-ingress-external' - 'user-notification' + - 'datadog' grantNamespacesEnabled: true healthCheck: liveness: From beb3272057a574fbde9a708a868c46ec79742a72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Fri, 6 Dec 2024 18:59:02 +0000 Subject: [PATCH 57/85] feat(j-s): Show district court abbreviation on mobile (#17075) * Add court abbr to getAllCases call * Refactoring * Refactoring * Refactoring * Refactoring * Add abbr to mobile as well * >merge --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../web/src/routes/Shared/Cases/MobileCase.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/judicial-system/web/src/routes/Shared/Cases/MobileCase.tsx b/apps/judicial-system/web/src/routes/Shared/Cases/MobileCase.tsx index 391d004d76d4..a14415196497 100644 --- a/apps/judicial-system/web/src/routes/Shared/Cases/MobileCase.tsx +++ b/apps/judicial-system/web/src/routes/Shared/Cases/MobileCase.tsx @@ -7,6 +7,7 @@ import { AnimatePresence } from 'framer-motion' import { Box, FocusableBox, Text } from '@island.is/island-ui/core' import { displayFirstPlusRemaining, + districtCourtAbbreviation, formatDOB, } from '@island.is/judicial-system/formatters' import { tables } from '@island.is/judicial-system-web/messages' @@ -69,6 +70,7 @@ const MobileCase: FC> = ({ isLoading = false, }) => { const { formatMessage } = useIntl() + const courtAbbreviation = districtCourtAbbreviation(theCase.court?.name) return ( > = ({ {displayFirstPlusRemaining(theCase.policeCaseNumbers)} - {theCase.courtCaseNumber && {theCase.courtCaseNumber}} + + {theCase.courtCaseNumber && ( + {`${courtAbbreviation ? `${courtAbbreviation}: ` : ''}${ + theCase.courtCaseNumber + }`} + )}
{theCase.defendants && theCase.defendants.length > 0 && ( <> From 1a6abd60efcab054af021f7136d56a20ef14ff3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3n=20Bjarni=20=C3=93lafsson?= <92530555+jonbjarnio@users.noreply.github.com> Date: Fri, 6 Dec 2024 19:13:04 +0000 Subject: [PATCH 58/85] fix(ojoi): Input validation and bugfixes (#17124) * Updating select inputs to filter from the start. * Updated the UI header component to support larger titles. * Signatures are now hyphenated and max length for each field set to 100. * Fixed state issue when users changed the type of additions * Fixed 0 rendering in an conditional check * Fixed issue where date parameters were not properly working. * Fix typo --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../OJOIAdvertCard.tsx | 2 +- .../OfficialJournalOfIceland/OJOISearch.tsx | 24 +++------- .../hooks/useAdverts.ts | 4 +- .../src/lib/models/advert.input.ts | 4 +- .../src/components/additions/Additions.tsx | 18 ++++---- .../components/input/OJOIInputController.tsx | 3 ++ .../components/input/OJOISelectController.tsx | 2 + .../src/components/signatures/Institution.tsx | 1 + .../src/components/signatures/Member.tsx | 1 + .../src/fields/Advert.tsx | 1 + .../src/fields/Attachments.tsx | 45 ++++++++++++++----- .../src/fields/Publishing.tsx | 3 ++ .../src/lib/dataSchema.ts | 2 + .../src/lib/messages/attachments.ts | 2 +- .../src/lib/types.ts | 2 + .../src/lib/utils.ts | 25 +++++++---- .../core/src/lib/Header/Header.css.ts | 4 ++ libs/island-ui/core/src/lib/Header/Header.tsx | 11 ++--- libs/island-ui/core/src/lib/Hyphen/Hyphen.tsx | 2 +- .../lib/SelectController/SelectController.tsx | 10 ++++- 20 files changed, 109 insertions(+), 57 deletions(-) diff --git a/apps/web/components/OfficialJournalOfIceland/OJOIAdvertCard.tsx b/apps/web/components/OfficialJournalOfIceland/OJOIAdvertCard.tsx index fe375bbe25fa..089ebaf2630a 100644 --- a/apps/web/components/OfficialJournalOfIceland/OJOIAdvertCard.tsx +++ b/apps/web/components/OfficialJournalOfIceland/OJOIAdvertCard.tsx @@ -66,7 +66,7 @@ export const OJOIAdvertCard = ({ marginTop={2} rowGap={1} > - {categories && categories.length && ( + {categories && categories.length > 0 && ( {categories.map((cat) => { return ( diff --git a/apps/web/screens/OfficialJournalOfIceland/OJOISearch.tsx b/apps/web/screens/OfficialJournalOfIceland/OJOISearch.tsx index 86a586ccc44d..a3c4371e88d3 100644 --- a/apps/web/screens/OfficialJournalOfIceland/OJOISearch.tsx +++ b/apps/web/screens/OfficialJournalOfIceland/OJOISearch.tsx @@ -105,12 +105,8 @@ const OJOISearchPage: CustomScreen = ({ category: [defaultSearchParams.malaflokkur], involvedParty: [defaultSearchParams.stofnun], type: [defaultSearchParams.tegund], - dateFrom: defaultSearchParams.dagsFra - ? new Date(defaultSearchParams.dagsFra) - : undefined, - dateTo: defaultSearchParams.dagsTil - ? new Date(defaultSearchParams.dagsTil) - : undefined, + dateFrom: defaultSearchParams.dagsFra, + dateTo: defaultSearchParams.dagsTil, search: defaultSearchParams.q, page: defaultSearchParams.sida, pageSize: defaultSearchParams.staerd, @@ -176,12 +172,8 @@ const OJOISearchPage: CustomScreen = ({ category: [searchValues.malaflokkur], involvedParty: [searchValues.stofnun], type: [searchValues.tegund], - dateFrom: searchValues.dagsFra - ? new Date(searchValues.dagsFra) - : undefined, - dateTo: searchValues.dagsTil - ? new Date(searchValues.dagsTil) - : undefined, + dateFrom: searchValues.dagsFra, + dateTo: searchValues.dagsTil, search: searchValues.q, page: searchValues.sida, pageSize: searchValues.staerd, @@ -596,12 +588,8 @@ OJOISearch.getProps = async ({ apolloClient, locale, query }) => { variables: { input: { category: [defaultParams.malaflokkur], - dateFrom: defaultParams.dagsFra - ? new Date(defaultParams.dagsFra) - : undefined, - dateTo: defaultParams.dagsTil - ? new Date(defaultParams.dagsTil) - : undefined, + dateFrom: defaultParams.dagsFra, + dateTo: defaultParams.dagsTil, department: [defaultParams.deild], involvedParty: [defaultParams.stofnun], page: defaultParams.sida, diff --git a/apps/web/screens/OfficialJournalOfIceland/hooks/useAdverts.ts b/apps/web/screens/OfficialJournalOfIceland/hooks/useAdverts.ts index 0d4c3092886b..235c26397441 100644 --- a/apps/web/screens/OfficialJournalOfIceland/hooks/useAdverts.ts +++ b/apps/web/screens/OfficialJournalOfIceland/hooks/useAdverts.ts @@ -20,8 +20,8 @@ export type UseAdvertsVariables = { type?: Array category?: Array involvedParty?: Array - dateFrom?: Date - dateTo?: Date + dateFrom?: string + dateTo?: string } export type UseAdvertsInput = { diff --git a/libs/api/domains/official-journal-of-iceland/src/lib/models/advert.input.ts b/libs/api/domains/official-journal-of-iceland/src/lib/models/advert.input.ts index c00db538679f..868f78ae3130 100644 --- a/libs/api/domains/official-journal-of-iceland/src/lib/models/advert.input.ts +++ b/libs/api/domains/official-journal-of-iceland/src/lib/models/advert.input.ts @@ -28,10 +28,10 @@ export class AdvertsInput { @Field(() => [String], { nullable: true }) involvedParty?: string[] - @Field(() => Date, { nullable: true }) + @Field(() => String, { nullable: true }) dateFrom?: string - @Field(() => Date, { nullable: true }) + @Field(() => String, { nullable: true }) dateTo?: string } diff --git a/libs/application/templates/official-journal-of-iceland/src/components/additions/Additions.tsx b/libs/application/templates/official-journal-of-iceland/src/components/additions/Additions.tsx index fc94f0a5f275..072b2aca724e 100644 --- a/libs/application/templates/official-journal-of-iceland/src/components/additions/Additions.tsx +++ b/libs/application/templates/official-journal-of-iceland/src/components/additions/Additions.tsx @@ -33,7 +33,9 @@ type Props = { type Addition = z.infer[number] export const Additions = ({ application }: Props) => { - const [asRoman, setAsRoman] = useState(false) + const [asRoman, setAsRoman] = useState( + application.answers.misc?.asRoman ?? false, + ) const { formatMessage: f } = useLocale() const { setValue } = useFormContext() @@ -57,8 +59,6 @@ export const Additions = ({ application }: Props) => { const onRemoveAddition = (index: number) => { const filtered = additions.filter((_, i) => i !== index) const mapped = filtered.map((addition, i) => { - if (addition.type !== 'html') return addition - const title = f(attachments.additions.title, { index: asRoman ? convertNumberToRoman(i + 1) : i + 1, }) @@ -83,10 +83,8 @@ export const Additions = ({ application }: Props) => { const onRomanChange = (val: boolean) => { const handleTitleChange = (addition: Addition, i: number) => { - if (addition.type !== 'html') return addition - const title = f(attachments.additions.title, { - index: asRoman ? convertNumberToRoman(i + 1) : i + 1, + index: val ? convertNumberToRoman(i + 1) : i + 1, }) return { ...addition, @@ -97,14 +95,17 @@ export const Additions = ({ application }: Props) => { const currentAnswers = structuredClone(currentApplication.answers) const updatedAdditions = additions.map(handleTitleChange) - const updatedAnswers = set( + let updatedAnswers = set( currentAnswers, InputFields.advert.additions, updatedAdditions, ) + updatedAnswers = set(updatedAnswers, InputFields.misc.asRoman, val) + setAsRoman(val) setValue(InputFields.advert.additions, updatedAdditions) + setValue(InputFields.misc.asRoman, val) updateApplication(updatedAnswers) } @@ -206,7 +207,8 @@ export const Additions = ({ application }: Props) => { - {!asAddition ? ( - - ) : ( + {asDocument ? ( { icon: 'blue200', }} /> + ) : ( + )} ) diff --git a/libs/application/templates/official-journal-of-iceland/src/fields/Publishing.tsx b/libs/application/templates/official-journal-of-iceland/src/fields/Publishing.tsx index 2b2b845b4092..1e6430800d5f 100644 --- a/libs/application/templates/official-journal-of-iceland/src/fields/Publishing.tsx +++ b/libs/application/templates/official-journal-of-iceland/src/fields/Publishing.tsx @@ -123,6 +123,9 @@ export const Publishing = ({ application }: OJOIFieldBaseProps) => { options={mappedCategories} defaultValue={mappedCategories?.[0]} onChange={(opt) => onCategoryChange(opt?.value)} + filterConfig={{ + matchFrom: 'start', + }} /> diff --git a/libs/application/templates/official-journal-of-iceland/src/lib/dataSchema.ts b/libs/application/templates/official-journal-of-iceland/src/lib/dataSchema.ts index fed28c133461..046cbeed2664 100644 --- a/libs/application/templates/official-journal-of-iceland/src/lib/dataSchema.ts +++ b/libs/application/templates/official-journal-of-iceland/src/lib/dataSchema.ts @@ -83,6 +83,8 @@ const miscSchema = z .object({ signatureType: z.string().optional(), selectedTemplate: z.string().optional(), + asDocument: z.boolean().optional(), + asRoman: z.boolean().optional(), }) .partial() diff --git a/libs/application/templates/official-journal-of-iceland/src/lib/messages/attachments.ts b/libs/application/templates/official-journal-of-iceland/src/lib/messages/attachments.ts index 0046056207a4..e8a0f839b7b6 100644 --- a/libs/application/templates/official-journal-of-iceland/src/lib/messages/attachments.ts +++ b/libs/application/templates/official-journal-of-iceland/src/lib/messages/attachments.ts @@ -34,7 +34,7 @@ export const attachments = { }, asAttachment: { id: 'ojoi.application:attachments.buttons.additionType.asAttachment', - defaultMessage: 'Hlaða upp skjölum', + defaultMessage: 'BÊta við viðauka', description: 'Label of the button to upload attachments', }, removeAddition: { diff --git a/libs/application/templates/official-journal-of-iceland/src/lib/types.ts b/libs/application/templates/official-journal-of-iceland/src/lib/types.ts index 385aec190a50..1529e2d56442 100644 --- a/libs/application/templates/official-journal-of-iceland/src/lib/types.ts +++ b/libs/application/templates/official-journal-of-iceland/src/lib/types.ts @@ -33,6 +33,8 @@ export const InputFields = { [Routes.MISC]: { signatureType: 'misc.signatureType', selectedTemplate: 'misc.selectedTemplate', + asDocument: 'misc.asDocument', + asRoman: 'misc.asRoman', }, } diff --git a/libs/application/templates/official-journal-of-iceland/src/lib/utils.ts b/libs/application/templates/official-journal-of-iceland/src/lib/utils.ts index 96d1b2fb0893..15699f900241 100644 --- a/libs/application/templates/official-journal-of-iceland/src/lib/utils.ts +++ b/libs/application/templates/official-journal-of-iceland/src/lib/utils.ts @@ -18,6 +18,8 @@ import is from 'date-fns/locale/is' import { SignatureTypes, OJOI_DF, FAST_TRACK_DAYS } from './constants' import { MessageDescriptor } from 'react-intl' import { v4 as uuid } from 'uuid' +import Hypher from 'hypher' +import { hyphenateText } from '@island.is/island-ui/core' export const countDaysAgo = (date: Date) => { const now = new Date() @@ -190,6 +192,8 @@ export const getRegularAnswers = (answers: OJOIApplication['answers']) => { signature: null, } } +const hyphenate = (text = '') => + hyphenateText(text, { locale: 'is', minLeft: 4, minRight: 4 }) const getMembersMarkup = (member: z.infer) => { if (!member.name) return '' @@ -198,18 +202,21 @@ const getMembersMarkup = (member: z.infer) => { marginBottom: member.below ? '0' : '1.5em', } - const aboveMarkup = member.above - ? `

${member.above}

` - : '' - const afterMarkup = member.after ? ` ${member.after}` : '' - const belowMarkup = member.below - ? `

${member.below}

` + const name = hyphenate(member.name) + const above = hyphenate(member.above) + const after = hyphenate(member.after) + const below = hyphenate(member.below) + + const aboveMarkup = above + ? `

${above}

` : '' + const afterMarkup = after ? ` ${after}` : '' + const belowMarkup = below ? `

${below}

` : '' return `
${aboveMarkup} -

${member.name}${afterMarkup}

+

${name}${afterMarkup}

${belowMarkup}
` @@ -261,7 +268,9 @@ const signatureTemplate = ( .join('') const additionalMarkup = additionalSignature - ? `

${additionalSignature}

` + ? `

${hyphenate( + additionalSignature, + )}

` : '' return `${markup}${additionalMarkup}` as HTMLText diff --git a/libs/island-ui/core/src/lib/Header/Header.css.ts b/libs/island-ui/core/src/lib/Header/Header.css.ts index cf8a3492b455..c580b98e9c28 100644 --- a/libs/island-ui/core/src/lib/Header/Header.css.ts +++ b/libs/island-ui/core/src/lib/Header/Header.css.ts @@ -21,10 +21,14 @@ export const infoDescription = style({ fontWeight: 300, lineHeight: 1.5, fontSize: 14, + maxHeight: 40, + position: 'relative', + overflow: 'auto', ...themeUtils.responsiveStyle({ md: { fontSize: 18, + maxHeight: 66, }, }), }) diff --git a/libs/island-ui/core/src/lib/Header/Header.tsx b/libs/island-ui/core/src/lib/Header/Header.tsx index 15a28c13f892..1d6a01230f6d 100644 --- a/libs/island-ui/core/src/lib/Header/Header.tsx +++ b/libs/island-ui/core/src/lib/Header/Header.tsx @@ -93,6 +93,7 @@ export const Header = ({ height="full" marginLeft={[1, 1, 2, 4]} marginRight="auto" + paddingRight={[1, 1, 2, 4]} > {info.title} @@ -128,14 +129,14 @@ export const Header = ({ alignItems="center" justifyContent="spaceBetween" > - - {renderLogo()} + + {renderLogo()} {renderInfo()} - - + + {renderOldDropdown()} {headerItems} -
+
) } diff --git a/libs/island-ui/core/src/lib/Hyphen/Hyphen.tsx b/libs/island-ui/core/src/lib/Hyphen/Hyphen.tsx index 9af8249a592e..d6f0a607b944 100644 --- a/libs/island-ui/core/src/lib/Hyphen/Hyphen.tsx +++ b/libs/island-ui/core/src/lib/Hyphen/Hyphen.tsx @@ -12,7 +12,7 @@ type HyphenateText = ( ) => string // TODO: import patterns dynamically -const hyphenateText: HyphenateText = ( +export const hyphenateText: HyphenateText = ( content, { minLeft, minRight, locale = 'is' }, ) => { diff --git a/libs/shared/form-fields/src/lib/SelectController/SelectController.tsx b/libs/shared/form-fields/src/lib/SelectController/SelectController.tsx index d5ec6a74d63d..06639d1913c8 100644 --- a/libs/shared/form-fields/src/lib/SelectController/SelectController.tsx +++ b/libs/shared/form-fields/src/lib/SelectController/SelectController.tsx @@ -1,7 +1,12 @@ import React from 'react' import { Controller, useFormContext, RegisterOptions } from 'react-hook-form' -import { Select, Option, InputBackgroundColor } from '@island.is/island-ui/core' +import { + Select, + SelectProps, + Option, + InputBackgroundColor, +} from '@island.is/island-ui/core' import { TestSupport } from '@island.is/island-ui/utils' import { MultiValue, SingleValue } from 'react-select' @@ -26,6 +31,7 @@ interface SelectControllerProps { rules?: RegisterOptions size?: 'xs' | 'sm' | 'md' internalKey?: string + filterConfig?: SelectProps['filterConfig'] } export const SelectController = ({ @@ -47,6 +53,7 @@ export const SelectController = ({ rules, size, internalKey, + filterConfig, }: SelectControllerProps & TestSupport) => { const { clearErrors } = useFormContext() @@ -91,6 +98,7 @@ export const SelectController = ({ placeholder={placeholder} value={getValue(value)} isSearchable={isSearchable} + filterConfig={filterConfig} isMulti={isMulti} isClearable={isClearable} size={size} From 2692a1f93e65b41ce9cb9cfb58acfe84e5d5fd6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9E=C3=B3rey=20J=C3=B3na?= Date: Mon, 9 Dec 2024 08:21:09 +0000 Subject: [PATCH 59/85] feat(native-app): use national registry person endpoint (V3) (#16877) * feat: use national registry person endpoint (V3) * feat: change Avatar to have letters of first and last name not first and second * fix: improve types * fix: improve types * fix: make sure we have the correct person for the details * fix: remove typename from queries * fix: use theme where relevant --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- apps/native/app/src/graphql/client.ts | 3 + .../graphql/queries/national-registry.graphql | 98 ++++++++--- .../app/src/screens/family/family-details.tsx | 113 +++++++++--- .../src/screens/family/family-overview.tsx | 165 +++++++++++------- .../app/src/screens/more/personal-info.tsx | 2 +- apps/native/app/src/ui/lib/avatar/avatar.tsx | 14 +- 6 files changed, 271 insertions(+), 124 deletions(-) diff --git a/apps/native/app/src/graphql/client.ts b/apps/native/app/src/graphql/client.ts index 2e4e29eb5d34..fe5e2ea8205b 100644 --- a/apps/native/app/src/graphql/client.ts +++ b/apps/native/app/src/graphql/client.ts @@ -164,6 +164,9 @@ const cache = new InMemoryCache({ getUserProfile: { merge: true, }, + nationalRegistryPerson: { + merge: false, + }, }, }, DocumentV2: { diff --git a/apps/native/app/src/graphql/queries/national-registry.graphql b/apps/native/app/src/graphql/queries/national-registry.graphql index 84777c6e116c..764939d31273 100644 --- a/apps/native/app/src/graphql/queries/national-registry.graphql +++ b/apps/native/app/src/graphql/queries/national-registry.graphql @@ -1,5 +1,5 @@ query NationalRegistryUser { - nationalRegistryUser { + nationalRegistryPerson { nationalId fullName gender @@ -23,37 +23,83 @@ query NationalRegistryUser { } } -query NationalRegistryChildren { - nationalRegistryUser { +query NationalRegistryPerson { + nationalRegistryPerson { nationalId - fullName + name { + fullName + } spouse { - name nationalId + fullName + } + childCustody { + nationalId + fullName + } + biologicalChildren { + nationalId + fullName } } - nationalRegistryChildren { +} + +query NationalRegistryBioChild($childNationalId: String) { + nationalRegistryPerson { nationalId fullName - displayName - genderDisplay - birthplace - custody1 - custodyText1 - nameCustody1 - custody2 - custodyText2 - nameCustody2 - parent1 - nameParent1 - parent2 - nameParent2 - homeAddress - religion - nationality - religion - homeAddress - nationality - legalResidence + biologicalChildren(childNationalId: $childNationalId) { + details { + nationalId + fullName + baseId + housing { + address { + streetAddress + } + } + } + } + } +} + +query NationalRegistryChildCustody($childNationalId: String) { + nationalRegistryPerson { + nationalId + fullName + childCustody(childNationalId: $childNationalId) { + details { + nationalId + fullName + baseId + gender + religion + housing { + address { + streetAddress + postalCode + city + } + } + birthplace { + location + } + citizenship { + code + name + } + } + } + } +} + +query NationalRegistrySpouse { + nationalRegistryPerson { + nationalId + spouse { + maritalStatus + nationalId + fullName + } } } diff --git a/apps/native/app/src/screens/family/family-details.tsx b/apps/native/app/src/screens/family/family-details.tsx index 154a056f051e..e4cf541c22cd 100644 --- a/apps/native/app/src/screens/family/family-details.tsx +++ b/apps/native/app/src/screens/family/family-details.tsx @@ -6,10 +6,40 @@ import { Navigation, NavigationFunctionComponent, } from 'react-native-navigation' -import { useNationalRegistryChildrenQuery } from '../../graphql/types/schema' +import { + NationalRegistryBioChildQuery, + NationalRegistryChildCustodyQuery, + NationalRegistrySpouseQuery, + useNationalRegistryBioChildQuery, + useNationalRegistryChildCustodyQuery, + useNationalRegistrySpouseQuery, +} from '../../graphql/types/schema' import { createNavigationOptionHooks } from '../../hooks/create-navigation-option-hooks' import { formatNationalId } from '../../lib/format-national-id' import { testIDs } from '../../utils/test-ids' +import { useTheme } from 'styled-components' + +type ChildCustodyDetails = NonNullable< + NonNullable< + NonNullable< + NonNullable + >['childCustody'] + >[number] +>['details'] + +type BioChildDetails = NonNullable< + NonNullable< + NonNullable< + NonNullable + >['biologicalChildren'] + >[number] +>['details'] + +type SpouseDetails = NonNullable< + NonNullable +>['spouse'] + +type Person = ChildCustodyDetails | BioChildDetails | SpouseDetails const { getNavigationOptions, useNavigationOptions } = createNavigationOptionHooks(() => ({ @@ -20,25 +50,39 @@ const { getNavigationOptions, useNavigationOptions } = export const FamilyDetailScreen: NavigationFunctionComponent<{ id: string - type: string + type: 'bioChild' | 'custodyChild' | 'spouse' }> = ({ componentId, id, type }) => { useNavigationOptions(componentId) const intl = useIntl() + const theme = useTheme() - const { data, loading, error } = useNationalRegistryChildrenQuery({ - fetchPolicy: 'cache-first', + const bioChildRes = useNationalRegistryBioChildQuery({ + variables: { childNationalId: id }, + skip: type !== 'bioChild', }) - const { nationalRegistryUser, nationalRegistryChildren = [] } = data || {} - const listOfPeople = [ - { ...(nationalRegistryUser?.spouse ?? {}), type: 'spouse' }, - ...(nationalRegistryChildren ?? []).map((item: any) => ({ - ...item, - type: 'child', - })), - ].filter((item) => item.nationalId) + const custodyChildRes = useNationalRegistryChildCustodyQuery({ + variables: { childNationalId: id }, + skip: type !== 'custodyChild', + }) + + const spouseRes = useNationalRegistrySpouseQuery({ + skip: type !== 'spouse', + }) - const person = listOfPeople?.find((x) => x.nationalId === id) || null + const person: Person = + bioChildRes.data?.nationalRegistryPerson?.biologicalChildren?.find( + (child) => child.details?.nationalId === id, + )?.details || + custodyChildRes?.data?.nationalRegistryPerson?.childCustody?.find( + (child) => child.details?.nationalId === id, + )?.details || + spouseRes.data?.nationalRegistryPerson?.spouse || + null + + const loading = + bioChildRes.loading || custodyChildRes.loading || spouseRes.loading + const error = bioChildRes.error || custodyChildRes.error || spouseRes.error if (!person) return null @@ -53,7 +97,11 @@ export const FamilyDetailScreen: NavigationFunctionComponent<{ {intl.formatMessage({ id: 'familyDetail.description' })} @@ -64,13 +112,12 @@ export const FamilyDetailScreen: NavigationFunctionComponent<{ label={intl.formatMessage({ id: 'familyDetail.natreg.displayName', })} - value={person?.name || person?.displayName} + value={person?.fullName} loading={loading} error={!!error} size="big" /> - - {person?.nationality ? ( + {'citizenship' in person && person?.citizenship ? ( ) : null} - {person?.legalResidence ? ( + {'housing' in person && + person?.housing && + 'address' in person.housing && + person.housing.address ? ( @@ -118,20 +178,23 @@ export const FamilyDetailScreen: NavigationFunctionComponent<{ ) : null} - {person?.genderDisplay ? ( + {'gender' in person && person.gender ? ( ) : null} - {person?.birthplace ? ( + {'birthplace' in person && person?.birthplace ? ( diff --git a/apps/native/app/src/screens/family/family-overview.tsx b/apps/native/app/src/screens/family/family-overview.tsx index 91dd32bf52fb..f2bb50879b2a 100644 --- a/apps/native/app/src/screens/family/family-overview.tsx +++ b/apps/native/app/src/screens/family/family-overview.tsx @@ -1,4 +1,4 @@ -import { EmptyList, FamilyMemberCard, Skeleton, TopLine } from '@ui' +import { EmptyList, FamilyMemberCard, Problem, Skeleton, TopLine } from '@ui' import React, { useCallback, useRef, useState } from 'react' import { useIntl } from 'react-intl' import { @@ -14,13 +14,29 @@ import { NavigationFunctionComponent } from 'react-native-navigation' import { useTheme } from 'styled-components/native' import illustrationSrc from '../../assets/illustrations/hero_spring.png' import { BottomTabsIndicator } from '../../components/bottom-tabs-indicator/bottom-tabs-indicator' -import { useNationalRegistryChildrenQuery } from '../../graphql/types/schema' +import { + NationalRegistryChildCustody, + NationalRegistrySpouse, + useNationalRegistryPersonQuery, +} from '../../graphql/types/schema' import { createNavigationOptionHooks } from '../../hooks/create-navigation-option-hooks' import { useConnectivityIndicator } from '../../hooks/use-connectivity-indicator' import { navigateTo } from '../../lib/deep-linking' import { formatNationalId } from '../../lib/format-national-id' import { testIDs } from '../../utils/test-ids' +type ChildItem = NationalRegistryChildCustody & { + type: 'custodyChild' | 'bioChild' +} + +type SpouseItem = NationalRegistrySpouse & { type: 'spouse' } + +type FamilyListItem = + | ChildItem + | SpouseItem + | { type: 'skeleton'; id: string } + | { type: 'empty'; id: string } + const { useNavigationOptions, getNavigationOptions } = createNavigationOptionHooks((theme, intl) => ({ topBar: { @@ -30,32 +46,34 @@ const { useNavigationOptions, getNavigationOptions } = }, })) -const FamilyMember = React.memo(({ item }: { item: any }) => { - const theme = useTheme() - - return ( - - { - navigateTo(`/family/${item.type}/${item.nationalId}`, { - id: item?.nationalId, - }) - }} - > - - - - - - ) -}) +const FamilyMember = React.memo( + ({ item }: { item: ChildItem | SpouseItem }) => { + const theme = useTheme() + + return ( + + { + navigateTo(`/family/${item.type}/${item.nationalId}`, { + id: item?.nationalId, + }) + }} + > + + + + + + ) + }, +) export const FamilyOverviewScreen: NavigationFunctionComponent = ({ componentId, @@ -68,7 +86,7 @@ export const FamilyOverviewScreen: NavigationFunctionComponent = ({ const theme = useTheme() const scrollY = useRef(new Animated.Value(0)).current const loadingTimeout = useRef() - const familyRes = useNationalRegistryChildrenQuery() + const familyRes = useNationalRegistryPersonQuery() useConnectivityIndicator({ componentId, @@ -76,14 +94,23 @@ export const FamilyOverviewScreen: NavigationFunctionComponent = ({ refetching, }) - const { nationalRegistryUser, nationalRegistryChildren = [] } = - familyRes?.data || {} + const { biologicalChildren, spouse, childCustody } = + familyRes.data?.nationalRegistryPerson || {} + + // Filter out bio children with custody so we don't show them twice + const bioChildren = biologicalChildren?.filter( + (child) => !childCustody?.some((c) => c.nationalId === child.nationalId), + ) const listOfPeople = [ - { ...(nationalRegistryUser?.spouse ?? {}), type: 'spouse' }, - ...(nationalRegistryChildren ?? []).map((item: any) => ({ + { ...(spouse ?? {}), type: 'spouse' }, + ...(childCustody ?? []).map((item: NationalRegistryChildCustody) => ({ ...item, - type: 'child', + type: 'custodyChild', + })), + ...(bioChildren ?? []).map((item: NationalRegistryChildCustody) => ({ + ...item, + type: 'bioChild', })), ].filter((item) => item.nationalId) @@ -110,10 +137,10 @@ export const FamilyOverviewScreen: NavigationFunctionComponent = ({ } }, []) - const renderItem = ({ item }: { item: any }) => { + const renderItem = ({ item }: { item: FamilyListItem }) => { if (item.type === 'skeleton') { return ( - + @@ -137,7 +164,7 @@ export const FamilyOverviewScreen: NavigationFunctionComponent = ({ if (item.type === 'empty') { return ( - + - - } - scrollEventThrottle={16} - scrollToOverflowEnabled={true} - onScroll={Animated.event( - [{ nativeEvent: { contentOffset: { y: scrollY } } }], - { - useNativeDriver: true, - }, - )} - data={isSkeleton ? skeletonItems : isEmpty ? emptyItems : listOfPeople} - keyExtractor={keyExtractor} - renderItem={renderItem} - /> + {(familyRes.data || familyRes.loading) && ( + + } + scrollEventThrottle={16} + scrollToOverflowEnabled={true} + onScroll={Animated.event( + [{ nativeEvent: { contentOffset: { y: scrollY } } }], + { + useNativeDriver: true, + }, + )} + data={ + isSkeleton ? skeletonItems : isEmpty ? emptyItems : listOfPeople + } + keyExtractor={keyExtractor} + renderItem={renderItem} + /> + )} + {familyRes.error && !familyRes.data && } diff --git a/apps/native/app/src/screens/more/personal-info.tsx b/apps/native/app/src/screens/more/personal-info.tsx index f0b7eec2784e..87417710ad0c 100644 --- a/apps/native/app/src/screens/more/personal-info.tsx +++ b/apps/native/app/src/screens/more/personal-info.tsx @@ -27,7 +27,7 @@ export const PersonalInfoScreen: NavigationFunctionComponent = ({ const intl = useIntl() const { dismiss, dismissed } = usePreferencesStore() const natRegRes = useNationalRegistryUserQuery() - const natRegData = natRegRes?.data?.nationalRegistryUser + const natRegData = natRegRes?.data?.nationalRegistryPerson const errorNatReg = !!natRegRes.error && !natRegData const loadingNatReg = natRegRes.loading && !natRegData diff --git a/apps/native/app/src/ui/lib/avatar/avatar.tsx b/apps/native/app/src/ui/lib/avatar/avatar.tsx index 0debd370fd8a..d3a77094e7a7 100644 --- a/apps/native/app/src/ui/lib/avatar/avatar.tsx +++ b/apps/native/app/src/ui/lib/avatar/avatar.tsx @@ -38,13 +38,15 @@ interface AvatarProps { export function Avatar({ name, isSmall }: AvatarProps) { function getFirstLetters(str: string) { - const firstLetters = str - .split(' ') - .slice(0, 2) - .map((word) => word[0]) - .join('') + const names = str.split(' ') - return firstLetters + let initials = names[0].substring(0, 1).toUpperCase() + + if (names.length > 1) { + initials += names[names.length - 1].substring(0, 1).toUpperCase() + } + + return initials } return ( From 2e1b8a0c4c909e47ab1fa1638f8fbc6d8fb29612 Mon Sep 17 00:00:00 2001 From: valurefugl <65780958+valurefugl@users.noreply.github.com> Date: Mon, 9 Dec 2024 09:33:19 +0000 Subject: [PATCH 60/85] fix(ids-api): Use name field instead of fullname. (#17164) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../auth/ids-api/src/app/user-profile/user-profile.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/services/auth/ids-api/src/app/user-profile/user-profile.service.ts b/apps/services/auth/ids-api/src/app/user-profile/user-profile.service.ts index 9e310e1513c9..626b782bb437 100644 --- a/apps/services/auth/ids-api/src/app/user-profile/user-profile.service.ts +++ b/apps/services/auth/ids-api/src/app/user-profile/user-profile.service.ts @@ -130,7 +130,7 @@ export class UserProfileService { } return { - name: individual.fulltNafn?.fulltNafn ?? individual.nafn ?? undefined, + name: individual.nafn ?? undefined, givenName: individual.fulltNafn?.eiginNafn ?? undefined, familyName: individual.fulltNafn?.kenniNafn ?? undefined, middleName: individual.fulltNafn?.milliNafn ?? undefined, From f6eea1c7ff8226b04dfae0af98063547c20326c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9E=C3=B3rey=20J=C3=B3na?= Date: Mon, 9 Dec 2024 10:20:51 +0000 Subject: [PATCH 61/85] feat(native-app): support type: 'Table' in field render - do not merge (#16908) * feat: support type: 'Table' in field render * fix: use theme when relevant --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../wallet-pass/components/field-render.tsx | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/apps/native/app/src/screens/wallet-pass/components/field-render.tsx b/apps/native/app/src/screens/wallet-pass/components/field-render.tsx index 7355b4f118b6..7e1eebbb79e1 100644 --- a/apps/native/app/src/screens/wallet-pass/components/field-render.tsx +++ b/apps/native/app/src/screens/wallet-pass/components/field-render.tsx @@ -1,6 +1,7 @@ import { Field, FieldCard, FieldGroup, FieldLabel, FieldRow } from '@ui' import React from 'react' import { View } from 'react-native' +import { useTheme } from 'styled-components' import { GenericLicenseDataField, GenericLicenseType, @@ -15,6 +16,7 @@ export const FieldRender = ({ level?: number licenseType?: GenericLicenseType }) => { + const theme = useTheme() return ( <> {(data || []).map( @@ -45,7 +47,44 @@ export const FieldRender = ({ case 'Group': if (label) { return ( - + + {label} + {FieldRender({ + data: fields as GenericLicenseDataField[], + level: 2, + licenseType: licenseType, + })} + + ) + } + return ( + + + {FieldRender({ + data: fields as GenericLicenseDataField[], + level: 2, + licenseType: licenseType, + })} + + + ) + + case 'Table': + if (label) { + return ( + {label} {FieldRender({ data: fields as GenericLicenseDataField[], From e87eb22bed6b8eb70cadb3b1b983baacc6885f4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sn=C3=A6r=20Seljan=20=C3=9E=C3=B3roddsson?= Date: Mon, 9 Dec 2024 11:04:59 +0000 Subject: [PATCH 62/85] fix(services-bff): Disable PAR until ids resolves error (#17176) * fix(services-bff): Disable PAR until ids resolves error * fix tests * chore: charts update dirty files --------- Co-authored-by: andes-it --- charts/islandis/values.dev.yaml | 4 ++-- charts/islandis/values.prod.yaml | 4 ++-- charts/islandis/values.staging.yaml | 4 ++-- charts/services/services-bff-portals-admin/values.dev.yaml | 2 +- charts/services/services-bff-portals-admin/values.prod.yaml | 2 +- .../services/services-bff-portals-admin/values.staging.yaml | 2 +- charts/services/services-bff-portals-my-pages/values.dev.yaml | 2 +- .../services/services-bff-portals-my-pages/values.prod.yaml | 2 +- .../services-bff-portals-my-pages/values.staging.yaml | 2 +- infra/src/dsl/bff.ts | 2 +- infra/src/dsl/feature-values.spec.ts | 2 +- infra/src/dsl/portal-env.spec.ts | 2 +- 12 files changed, 15 insertions(+), 15 deletions(-) diff --git a/charts/islandis/values.dev.yaml b/charts/islandis/values.dev.yaml index 0dd1634ad8ad..3ff1c6643bbd 100644 --- a/charts/islandis/values.dev.yaml +++ b/charts/islandis/values.dev.yaml @@ -2313,7 +2313,7 @@ services-bff-portals-admin: BFF_LOGIN_ATTEMPT_TTL_MS: '604800000' BFF_LOGOUT_REDIRECT_URI: 'https://beta.dev01.devland.is' BFF_NAME: 'stjornbord' - BFF_PAR_SUPPORT_ENABLED: 'true' + BFF_PAR_SUPPORT_ENABLED: 'false' BFF_PROXY_API_ENDPOINT: 'http://web-api.islandis.svc.cluster.local/api/graphql' CODE_OWNER: 'core' IDENTITY_SERVER_CLIENT_ID: '@admin.island.is/bff-stjornbord' @@ -2400,7 +2400,7 @@ services-bff-portals-my-pages: BFF_LOGIN_ATTEMPT_TTL_MS: '604800000' BFF_LOGOUT_REDIRECT_URI: 'https://beta.dev01.devland.is' BFF_NAME: 'minarsidur' - BFF_PAR_SUPPORT_ENABLED: 'true' + BFF_PAR_SUPPORT_ENABLED: 'false' BFF_PROXY_API_ENDPOINT: 'http://web-api.islandis.svc.cluster.local/api/graphql' IDENTITY_SERVER_CLIENT_ID: '@island.is/web' IDENTITY_SERVER_CLIENT_SCOPES: '["api_resource.scope","@island.is/applications:read","@island.is/applications:write","@island.is/user-profile:read","@island.is/user-profile:write","@island.is/auth/actor-delegations","@island.is/auth/delegations:write","@island.is/auth/consents","@skra.is/individuals","@island.is/documents","@island.is/endorsements","@admin.island.is/petitions","@island.is/assets/ip","@island.is/assets","@island.is/education","@island.is/education-license","@island.is/finance:overview","@island.is/finance/salary","@island.is/finance/schedule:read","@island.is/finance/loans","@island.is/internal","@island.is/internal:procuring","@island.is/me:details","@island.is/law-and-order","@island.is/licenses","@island.is/licenses:verify","@island.is/company","@island.is/vehicles","@island.is/work-machines","@island.is/health/payments","@island.is/health/medicines","@island.is/health/assistive-devices-and-nutrition","@island.is/health/therapies","@island.is/health/healthcare","@island.is/health/rights-status","@island.is/health/dentists","@island.is/health/organ-donation","@island.is/health/vaccinations","@island.is/signature-collection","@island.is/applications/urvinnslusjodur","@island.is/applications/orkusjodur","@island.is/fishing-license","@island.is/applications/samgongustofa-vehicles","@island.is/applications/ver","@samband.is/financial-aid/applicant","@samband.is/financial-aid:read","@samband.is/financial-aid:write"]' diff --git a/charts/islandis/values.prod.yaml b/charts/islandis/values.prod.yaml index 04619e6f6b0a..3d4924f04ccd 100644 --- a/charts/islandis/values.prod.yaml +++ b/charts/islandis/values.prod.yaml @@ -2182,7 +2182,7 @@ services-bff-portals-admin: BFF_LOGIN_ATTEMPT_TTL_MS: '604800000' BFF_LOGOUT_REDIRECT_URI: 'https://island.is' BFF_NAME: 'stjornbord' - BFF_PAR_SUPPORT_ENABLED: 'true' + BFF_PAR_SUPPORT_ENABLED: 'false' BFF_PROXY_API_ENDPOINT: 'http://web-api.islandis.svc.cluster.local/api/graphql' CODE_OWNER: 'core' IDENTITY_SERVER_CLIENT_ID: '@admin.island.is/bff-stjornbord' @@ -2271,7 +2271,7 @@ services-bff-portals-my-pages: BFF_LOGIN_ATTEMPT_TTL_MS: '604800000' BFF_LOGOUT_REDIRECT_URI: 'https://island.is' BFF_NAME: 'minarsidur' - BFF_PAR_SUPPORT_ENABLED: 'true' + BFF_PAR_SUPPORT_ENABLED: 'false' BFF_PROXY_API_ENDPOINT: 'http://web-api.islandis.svc.cluster.local/api/graphql' IDENTITY_SERVER_CLIENT_ID: '@island.is/web' IDENTITY_SERVER_CLIENT_SCOPES: '["api_resource.scope","@island.is/applications:read","@island.is/applications:write","@island.is/user-profile:read","@island.is/user-profile:write","@island.is/auth/actor-delegations","@island.is/auth/delegations:write","@island.is/auth/consents","@skra.is/individuals","@island.is/documents","@island.is/endorsements","@admin.island.is/petitions","@island.is/assets/ip","@island.is/assets","@island.is/education","@island.is/education-license","@island.is/finance:overview","@island.is/finance/salary","@island.is/finance/schedule:read","@island.is/finance/loans","@island.is/internal","@island.is/internal:procuring","@island.is/me:details","@island.is/law-and-order","@island.is/licenses","@island.is/licenses:verify","@island.is/company","@island.is/vehicles","@island.is/work-machines","@island.is/health/payments","@island.is/health/medicines","@island.is/health/assistive-devices-and-nutrition","@island.is/health/therapies","@island.is/health/healthcare","@island.is/health/rights-status","@island.is/health/dentists","@island.is/health/organ-donation","@island.is/health/vaccinations","@island.is/signature-collection","@island.is/applications/urvinnslusjodur","@island.is/applications/orkusjodur","@island.is/fishing-license","@island.is/applications/samgongustofa-vehicles","@island.is/applications/ver","@samband.is/financial-aid/applicant","@samband.is/financial-aid:read","@samband.is/financial-aid:write"]' diff --git a/charts/islandis/values.staging.yaml b/charts/islandis/values.staging.yaml index a2192f932e41..311d39020959 100644 --- a/charts/islandis/values.staging.yaml +++ b/charts/islandis/values.staging.yaml @@ -2051,7 +2051,7 @@ services-bff-portals-admin: BFF_LOGIN_ATTEMPT_TTL_MS: '604800000' BFF_LOGOUT_REDIRECT_URI: 'https://beta.staging01.devland.is' BFF_NAME: 'stjornbord' - BFF_PAR_SUPPORT_ENABLED: 'true' + BFF_PAR_SUPPORT_ENABLED: 'false' BFF_PROXY_API_ENDPOINT: 'http://web-api.islandis.svc.cluster.local/api/graphql' CODE_OWNER: 'core' IDENTITY_SERVER_CLIENT_ID: '@admin.island.is/bff-stjornbord' @@ -2138,7 +2138,7 @@ services-bff-portals-my-pages: BFF_LOGIN_ATTEMPT_TTL_MS: '604800000' BFF_LOGOUT_REDIRECT_URI: 'https://beta.staging01.devland.is' BFF_NAME: 'minarsidur' - BFF_PAR_SUPPORT_ENABLED: 'true' + BFF_PAR_SUPPORT_ENABLED: 'false' BFF_PROXY_API_ENDPOINT: 'http://web-api.islandis.svc.cluster.local/api/graphql' IDENTITY_SERVER_CLIENT_ID: '@island.is/web' IDENTITY_SERVER_CLIENT_SCOPES: '["api_resource.scope","@island.is/applications:read","@island.is/applications:write","@island.is/user-profile:read","@island.is/user-profile:write","@island.is/auth/actor-delegations","@island.is/auth/delegations:write","@island.is/auth/consents","@skra.is/individuals","@island.is/documents","@island.is/endorsements","@admin.island.is/petitions","@island.is/assets/ip","@island.is/assets","@island.is/education","@island.is/education-license","@island.is/finance:overview","@island.is/finance/salary","@island.is/finance/schedule:read","@island.is/finance/loans","@island.is/internal","@island.is/internal:procuring","@island.is/me:details","@island.is/law-and-order","@island.is/licenses","@island.is/licenses:verify","@island.is/company","@island.is/vehicles","@island.is/work-machines","@island.is/health/payments","@island.is/health/medicines","@island.is/health/assistive-devices-and-nutrition","@island.is/health/therapies","@island.is/health/healthcare","@island.is/health/rights-status","@island.is/health/dentists","@island.is/health/organ-donation","@island.is/health/vaccinations","@island.is/signature-collection","@island.is/applications/urvinnslusjodur","@island.is/applications/orkusjodur","@island.is/fishing-license","@island.is/applications/samgongustofa-vehicles","@island.is/applications/ver","@samband.is/financial-aid/applicant","@samband.is/financial-aid:read","@samband.is/financial-aid:write"]' diff --git a/charts/services/services-bff-portals-admin/values.dev.yaml b/charts/services/services-bff-portals-admin/values.dev.yaml index ad41b2594e0f..6d2cde1b6507 100644 --- a/charts/services/services-bff-portals-admin/values.dev.yaml +++ b/charts/services/services-bff-portals-admin/values.dev.yaml @@ -29,7 +29,7 @@ env: BFF_LOGIN_ATTEMPT_TTL_MS: '604800000' BFF_LOGOUT_REDIRECT_URI: 'https://beta.dev01.devland.is' BFF_NAME: 'stjornbord' - BFF_PAR_SUPPORT_ENABLED: 'true' + BFF_PAR_SUPPORT_ENABLED: 'false' BFF_PROXY_API_ENDPOINT: 'http://web-api.islandis.svc.cluster.local/api/graphql' CODE_OWNER: 'core' IDENTITY_SERVER_CLIENT_ID: '@admin.island.is/bff-stjornbord' diff --git a/charts/services/services-bff-portals-admin/values.prod.yaml b/charts/services/services-bff-portals-admin/values.prod.yaml index 85573c54b3f8..0ac30cab3700 100644 --- a/charts/services/services-bff-portals-admin/values.prod.yaml +++ b/charts/services/services-bff-portals-admin/values.prod.yaml @@ -29,7 +29,7 @@ env: BFF_LOGIN_ATTEMPT_TTL_MS: '604800000' BFF_LOGOUT_REDIRECT_URI: 'https://island.is' BFF_NAME: 'stjornbord' - BFF_PAR_SUPPORT_ENABLED: 'true' + BFF_PAR_SUPPORT_ENABLED: 'false' BFF_PROXY_API_ENDPOINT: 'http://web-api.islandis.svc.cluster.local/api/graphql' CODE_OWNER: 'core' IDENTITY_SERVER_CLIENT_ID: '@admin.island.is/bff-stjornbord' diff --git a/charts/services/services-bff-portals-admin/values.staging.yaml b/charts/services/services-bff-portals-admin/values.staging.yaml index 5244c4b5efc5..a8b5553f847b 100644 --- a/charts/services/services-bff-portals-admin/values.staging.yaml +++ b/charts/services/services-bff-portals-admin/values.staging.yaml @@ -29,7 +29,7 @@ env: BFF_LOGIN_ATTEMPT_TTL_MS: '604800000' BFF_LOGOUT_REDIRECT_URI: 'https://beta.staging01.devland.is' BFF_NAME: 'stjornbord' - BFF_PAR_SUPPORT_ENABLED: 'true' + BFF_PAR_SUPPORT_ENABLED: 'false' BFF_PROXY_API_ENDPOINT: 'http://web-api.islandis.svc.cluster.local/api/graphql' CODE_OWNER: 'core' IDENTITY_SERVER_CLIENT_ID: '@admin.island.is/bff-stjornbord' diff --git a/charts/services/services-bff-portals-my-pages/values.dev.yaml b/charts/services/services-bff-portals-my-pages/values.dev.yaml index e8e6dfa8330e..3c9074dd156a 100644 --- a/charts/services/services-bff-portals-my-pages/values.dev.yaml +++ b/charts/services/services-bff-portals-my-pages/values.dev.yaml @@ -29,7 +29,7 @@ env: BFF_LOGIN_ATTEMPT_TTL_MS: '604800000' BFF_LOGOUT_REDIRECT_URI: 'https://beta.dev01.devland.is' BFF_NAME: 'minarsidur' - BFF_PAR_SUPPORT_ENABLED: 'true' + BFF_PAR_SUPPORT_ENABLED: 'false' BFF_PROXY_API_ENDPOINT: 'http://web-api.islandis.svc.cluster.local/api/graphql' IDENTITY_SERVER_CLIENT_ID: '@island.is/web' IDENTITY_SERVER_CLIENT_SCOPES: '["api_resource.scope","@island.is/applications:read","@island.is/applications:write","@island.is/user-profile:read","@island.is/user-profile:write","@island.is/auth/actor-delegations","@island.is/auth/delegations:write","@island.is/auth/consents","@skra.is/individuals","@island.is/documents","@island.is/endorsements","@admin.island.is/petitions","@island.is/assets/ip","@island.is/assets","@island.is/education","@island.is/education-license","@island.is/finance:overview","@island.is/finance/salary","@island.is/finance/schedule:read","@island.is/finance/loans","@island.is/internal","@island.is/internal:procuring","@island.is/me:details","@island.is/law-and-order","@island.is/licenses","@island.is/licenses:verify","@island.is/company","@island.is/vehicles","@island.is/work-machines","@island.is/health/payments","@island.is/health/medicines","@island.is/health/assistive-devices-and-nutrition","@island.is/health/therapies","@island.is/health/healthcare","@island.is/health/rights-status","@island.is/health/dentists","@island.is/health/organ-donation","@island.is/health/vaccinations","@island.is/signature-collection","@island.is/applications/urvinnslusjodur","@island.is/applications/orkusjodur","@island.is/fishing-license","@island.is/applications/samgongustofa-vehicles","@island.is/applications/ver","@samband.is/financial-aid/applicant","@samband.is/financial-aid:read","@samband.is/financial-aid:write"]' diff --git a/charts/services/services-bff-portals-my-pages/values.prod.yaml b/charts/services/services-bff-portals-my-pages/values.prod.yaml index 2d2a43d9a7eb..930594666907 100644 --- a/charts/services/services-bff-portals-my-pages/values.prod.yaml +++ b/charts/services/services-bff-portals-my-pages/values.prod.yaml @@ -29,7 +29,7 @@ env: BFF_LOGIN_ATTEMPT_TTL_MS: '604800000' BFF_LOGOUT_REDIRECT_URI: 'https://island.is' BFF_NAME: 'minarsidur' - BFF_PAR_SUPPORT_ENABLED: 'true' + BFF_PAR_SUPPORT_ENABLED: 'false' BFF_PROXY_API_ENDPOINT: 'http://web-api.islandis.svc.cluster.local/api/graphql' IDENTITY_SERVER_CLIENT_ID: '@island.is/web' IDENTITY_SERVER_CLIENT_SCOPES: '["api_resource.scope","@island.is/applications:read","@island.is/applications:write","@island.is/user-profile:read","@island.is/user-profile:write","@island.is/auth/actor-delegations","@island.is/auth/delegations:write","@island.is/auth/consents","@skra.is/individuals","@island.is/documents","@island.is/endorsements","@admin.island.is/petitions","@island.is/assets/ip","@island.is/assets","@island.is/education","@island.is/education-license","@island.is/finance:overview","@island.is/finance/salary","@island.is/finance/schedule:read","@island.is/finance/loans","@island.is/internal","@island.is/internal:procuring","@island.is/me:details","@island.is/law-and-order","@island.is/licenses","@island.is/licenses:verify","@island.is/company","@island.is/vehicles","@island.is/work-machines","@island.is/health/payments","@island.is/health/medicines","@island.is/health/assistive-devices-and-nutrition","@island.is/health/therapies","@island.is/health/healthcare","@island.is/health/rights-status","@island.is/health/dentists","@island.is/health/organ-donation","@island.is/health/vaccinations","@island.is/signature-collection","@island.is/applications/urvinnslusjodur","@island.is/applications/orkusjodur","@island.is/fishing-license","@island.is/applications/samgongustofa-vehicles","@island.is/applications/ver","@samband.is/financial-aid/applicant","@samband.is/financial-aid:read","@samband.is/financial-aid:write"]' diff --git a/charts/services/services-bff-portals-my-pages/values.staging.yaml b/charts/services/services-bff-portals-my-pages/values.staging.yaml index f0e31ccc9d58..e4ce58391d55 100644 --- a/charts/services/services-bff-portals-my-pages/values.staging.yaml +++ b/charts/services/services-bff-portals-my-pages/values.staging.yaml @@ -29,7 +29,7 @@ env: BFF_LOGIN_ATTEMPT_TTL_MS: '604800000' BFF_LOGOUT_REDIRECT_URI: 'https://beta.staging01.devland.is' BFF_NAME: 'minarsidur' - BFF_PAR_SUPPORT_ENABLED: 'true' + BFF_PAR_SUPPORT_ENABLED: 'false' BFF_PROXY_API_ENDPOINT: 'http://web-api.islandis.svc.cluster.local/api/graphql' IDENTITY_SERVER_CLIENT_ID: '@island.is/web' IDENTITY_SERVER_CLIENT_SCOPES: '["api_resource.scope","@island.is/applications:read","@island.is/applications:write","@island.is/user-profile:read","@island.is/user-profile:write","@island.is/auth/actor-delegations","@island.is/auth/delegations:write","@island.is/auth/consents","@skra.is/individuals","@island.is/documents","@island.is/endorsements","@admin.island.is/petitions","@island.is/assets/ip","@island.is/assets","@island.is/education","@island.is/education-license","@island.is/finance:overview","@island.is/finance/salary","@island.is/finance/schedule:read","@island.is/finance/loans","@island.is/internal","@island.is/internal:procuring","@island.is/me:details","@island.is/law-and-order","@island.is/licenses","@island.is/licenses:verify","@island.is/company","@island.is/vehicles","@island.is/work-machines","@island.is/health/payments","@island.is/health/medicines","@island.is/health/assistive-devices-and-nutrition","@island.is/health/therapies","@island.is/health/healthcare","@island.is/health/rights-status","@island.is/health/dentists","@island.is/health/organ-donation","@island.is/health/vaccinations","@island.is/signature-collection","@island.is/applications/urvinnslusjodur","@island.is/applications/orkusjodur","@island.is/fishing-license","@island.is/applications/samgongustofa-vehicles","@island.is/applications/ver","@samband.is/financial-aid/applicant","@samband.is/financial-aid:read","@samband.is/financial-aid:write"]' diff --git a/infra/src/dsl/bff.ts b/infra/src/dsl/bff.ts index 76d9aa87a345..f37b23b36e08 100644 --- a/infra/src/dsl/bff.ts +++ b/infra/src/dsl/bff.ts @@ -78,7 +78,7 @@ export const bffConfig = ({ }, BFF_GLOBAL_PREFIX: globalPrefix, BFF_CLIENT_BASE_PATH: `/${key}`, - BFF_PAR_SUPPORT_ENABLED: 'true', + BFF_PAR_SUPPORT_ENABLED: 'false', BFF_CLIENT_BASE_URL: { local: 'http://localhost:4200', dev: ref((ctx) => ctx.svc(getBaseUrl(ctx))), diff --git a/infra/src/dsl/feature-values.spec.ts b/infra/src/dsl/feature-values.spec.ts index 2fbc7bca3530..f3c53c101e09 100644 --- a/infra/src/dsl/feature-values.spec.ts +++ b/infra/src/dsl/feature-values.spec.ts @@ -125,7 +125,7 @@ describe('Feature-deployment support', () => { BFF_NAME: 'stjornbord', BFF_CLIENT_BASE_PATH: '/stjornbord', BFF_GLOBAL_PREFIX: `/stjornbord/bff`, - BFF_PAR_SUPPORT_ENABLED: 'true', + BFF_PAR_SUPPORT_ENABLED: 'false', BFF_ALLOWED_REDIRECT_URIS: json([ 'https://feature-A-beta.dev01.devland.is/stjornbord', ]), diff --git a/infra/src/dsl/portal-env.spec.ts b/infra/src/dsl/portal-env.spec.ts index df39ad3e13bc..9864c0a28c8d 100644 --- a/infra/src/dsl/portal-env.spec.ts +++ b/infra/src/dsl/portal-env.spec.ts @@ -158,7 +158,7 @@ describe('BFF PortalEnv serialization', () => { // BFF BFF_NAME: 'stjornbord', BFF_GLOBAL_PREFIX: `/${key}/bff`, - BFF_PAR_SUPPORT_ENABLED: 'true', + BFF_PAR_SUPPORT_ENABLED: 'false', BFF_ALLOWED_REDIRECT_URIS: json([`https://beta.dev01.devland.is/${key}`]), BFF_CLIENT_BASE_PATH: `/${key}`, BFF_CLIENT_BASE_URL: 'https://beta.dev01.devland.is', From 083feb4b1b79772ceb2410c6c9609e5d07e96d5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAnar=20Vestmann?= <43557895+RunarVestmann@users.noreply.github.com> Date: Mon, 9 Dec 2024 11:10:05 +0000 Subject: [PATCH 63/85] feat(web): Display logos in login button if there is a different top item (#17175) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- apps/web/components/Header/LoginButton.css.ts | 5 + apps/web/components/Header/LoginButton.tsx | 94 +++++++++++-------- 2 files changed, 60 insertions(+), 39 deletions(-) create mode 100644 apps/web/components/Header/LoginButton.css.ts diff --git a/apps/web/components/Header/LoginButton.css.ts b/apps/web/components/Header/LoginButton.css.ts new file mode 100644 index 000000000000..7a7a92b0078b --- /dev/null +++ b/apps/web/components/Header/LoginButton.css.ts @@ -0,0 +1,5 @@ +import { style } from '@vanilla-extract/css' + +export const dropdownMenu = style({ + width: 193, +}) diff --git a/apps/web/components/Header/LoginButton.tsx b/apps/web/components/Header/LoginButton.tsx index 668563c25b76..a7e696641095 100644 --- a/apps/web/components/Header/LoginButton.tsx +++ b/apps/web/components/Header/LoginButton.tsx @@ -1,17 +1,22 @@ import React, { MouseEvent } from 'react' +import { useWindowSize } from 'react-use' +import cn from 'classnames' import { useRouter } from 'next/router' import { Button, ButtonTypes, DropdownMenu, - Hidden, Inline, + Logo, } from '@island.is/island-ui/core' +import { theme } from '@island.is/island-ui/theme' import { webLoginButtonSelect } from '@island.is/plausible' import { useI18n } from '@island.is/web/i18n' import { LayoutProps } from '@island.is/web/layouts/main' +import * as styles from './LoginButton.css' + const minarsidurLink = '/minarsidur/' const minarsidurDelegationsLink = '/minarsidur/login?prompt=select_account' @@ -21,6 +26,7 @@ export function LoginButton(props: { }) { const { t } = useI18n() const router = useRouter() + const { width } = useWindowSize() function trackAndNavigate( buttonType: 'Dropdown - Individuals' | 'Dropdown - Companies' | string, @@ -49,12 +55,38 @@ export function LoginButton(props: { const items = [ { href: minarsidurLink, - title: t.loginIndividuals, + title: ( + + {props.topItem && ( + + )} + {t.loginIndividuals} + + ), onClick: trackAndNavigate.bind(null, 'Dropdown - Individuals'), }, { href: minarsidurDelegationsLink, - title: t.loginDelegations, + title: ( + + {props.topItem && ( + + )} + {t.loginDelegations} + + ), onClick: trackAndNavigate.bind(null, 'Dropdown - Companies'), }, ] @@ -79,42 +111,26 @@ export function LoginButton(props: { }) } + const isMobile = width < theme.breakpoints.md + return ( - <> - - - } - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore make web strict - items={items} - /> - - - - {t.login} - - } - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore make web strict - items={items} - openOnHover - /> - - + + {!isMobile && t.login} + + } + openOnHover={!isMobile} + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore make web strict + items={items} + /> ) } From fb234b4a19fb2bdc1bb2fb467a0ed9fb25756797 Mon Sep 17 00:00:00 2001 From: mannipje <135017126+mannipje@users.noreply.github.com> Date: Mon, 9 Dec 2024 11:42:49 +0000 Subject: [PATCH 64/85] feat(contentful-apps): Add organization parent subpage to link group link field (#17143) * Add organizationParentPage as option for organizationPage menu links * Add organizationParentSubpage to link mapping * Add generated contentful types * Fix subpage type when generating organization subpage link * Minor fix after review --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../pages/fields/link-group-link-field.tsx | 41 +++++++++++-------- .../src/lib/generated/contentfulTypes.d.ts | 15 ++++++- libs/cms/src/lib/models/linkGroup.model.ts | 21 +++++++--- 3 files changed, 54 insertions(+), 23 deletions(-) diff --git a/apps/contentful-apps/pages/fields/link-group-link-field.tsx b/apps/contentful-apps/pages/fields/link-group-link-field.tsx index 635d3eacfc8c..17c2bed45b92 100644 --- a/apps/contentful-apps/pages/fields/link-group-link-field.tsx +++ b/apps/contentful-apps/pages/fields/link-group-link-field.tsx @@ -14,15 +14,18 @@ import { DEFAULT_LOCALE } from '../../constants' const RETRY_COUNT = 5 -const getSubpageContentTypeId = (pageContentTypeId?: string) => { - if (pageContentTypeId === 'organizationPage') return 'organizationSubpage' - if (pageContentTypeId === 'projectPage') return 'projectSubpage' +const getSubpageContentTypeIds = ( + pageContentTypeId?: string, +): string[] | null => { + if (pageContentTypeId === 'organizationPage') + return ['organizationParentSubpage', 'organizationSubpage'] + if (pageContentTypeId === 'projectPage') return ['projectSubpage'] return null } interface SubpageData { pageAbove: EntryProps | null - subpageContentType: ContentTypeProps | null + subpageContentType: ContentTypeProps[] | null loading: boolean } @@ -48,20 +51,25 @@ const useSubpageData = (): SubpageData => { if (pageAboveResponse.items.length > 0) { const pageAboveEntry = pageAboveResponse.items[0] - - const subpageContentTypeId = getSubpageContentTypeId( + const subpageContentTypeIds = getSubpageContentTypeIds( pageAboveEntry.sys.contentType.sys.id, ) + const subpageContentTypes = subpageContentTypeIds + ? await Promise.all( + subpageContentTypeIds.map((id) => + cma.contentType.get({ contentTypeId: id }), + ), + ) + : [] + setData({ loading: false, pageAbove: pageAboveEntry, - subpageContentType: subpageContentTypeId - ? await cma.contentType.get({ - contentTypeId: subpageContentTypeId, - }) - : null, + subpageContentType: + subpageContentTypes.length > 0 ? subpageContentTypes : null, }) + return true } return false @@ -154,7 +162,8 @@ const LinkGroupLinkField = () => { let fields = {} if ( - contentTypeId === 'organizationSubpage' && + (contentTypeId === 'organizationSubpage' || + contentTypeId === 'organizationParentSubpage') && pageAbove?.sys?.contentType?.sys?.id === 'organizationPage' ) { fields = { @@ -209,7 +218,7 @@ const LinkGroupLinkField = () => { } const handleLinkExisting = (props: LinkActionsProps) => { - const contentTypeToSelect = getSubpageContentTypeId( + const contentTypesToSelect = getSubpageContentTypeIds( pageAbove?.sys?.contentType?.sys?.id, ) @@ -219,8 +228,8 @@ const LinkGroupLinkField = () => { : sdk.dialogs.selectMultipleEntries selectEntriesFunction({ - contentTypes: contentTypeToSelect - ? [contentTypeToSelect, 'link'] + contentTypes: contentTypesToSelect + ? [...contentTypesToSelect, 'link'] : ['link'], }).then((entries: EntryProps[]) => { entries = Array.isArray(entries) ? entries : [entries] @@ -238,7 +247,7 @@ const LinkGroupLinkField = () => { } const selectableContentTypes = subpageContentType - ? [subpageContentType, linkContentType] + ? [...subpageContentType, linkContentType] : [linkContentType] if (sdk.ids.field === 'primaryLink') { diff --git a/libs/cms/src/lib/generated/contentfulTypes.d.ts b/libs/cms/src/lib/generated/contentfulTypes.d.ts index 368f8fc63417..a03f5f97171d 100644 --- a/libs/cms/src/lib/generated/contentfulTypes.d.ts +++ b/libs/cms/src/lib/generated/contentfulTypes.d.ts @@ -2348,10 +2348,21 @@ export interface ILinkGroupFields { name: string /** Primary Link */ - primaryLink: ILink | IOrganizationSubpage | IProjectSubpage + primaryLink: + | ILink + | IOrganizationSubpage + | IProjectSubpage + | IOrganizationParentSubpage /** Children Links */ - childrenLinks?: (ILink | IProjectSubpage | IOrganizationSubpage)[] | undefined + childrenLinks?: + | ( + | ILink + | IProjectSubpage + | IOrganizationSubpage + | IOrganizationParentSubpage + )[] + | undefined } export interface ILinkGroup extends Entry { diff --git a/libs/cms/src/lib/models/linkGroup.model.ts b/libs/cms/src/lib/models/linkGroup.model.ts index 8d3ee650dd09..c7483d54e92a 100644 --- a/libs/cms/src/lib/models/linkGroup.model.ts +++ b/libs/cms/src/lib/models/linkGroup.model.ts @@ -8,6 +8,7 @@ import { ILink, ILinkGroup, ILinkGroupFields, + IOrganizationParentSubpage, IOrganizationSubpage, IProjectPage, IProjectSubpage, @@ -32,7 +33,7 @@ export class LinkGroup { type PageAbove = IProjectPage type LinkType = Omit< - ILink | IOrganizationSubpage | IProjectSubpage, + ILink | IOrganizationSubpage | IProjectSubpage | IOrganizationParentSubpage, 'update' | 'toPlainObject' > @@ -63,8 +64,14 @@ export const mapLinkGroup = ({ const mapLinkWrapper = (link: LinkType, pageAbove: PageAbove | undefined) => { const contentTypeId = link?.sys?.contentType?.sys?.id - if (contentTypeId === 'organizationSubpage') { - return generateOrganizationSubpageLink(link as IOrganizationSubpage) + + if ( + contentTypeId === 'organizationSubpage' || + contentTypeId === 'organizationParentSubpage' + ) { + return generateOrganizationSubpageLink( + link as IOrganizationSubpage | IOrganizationParentSubpage, + ) } else if (contentTypeId === 'projectSubpage') { return generateProjectSubpageLink(link as IProjectSubpage, pageAbove) } else if (contentTypeId === 'link') { @@ -73,7 +80,9 @@ const mapLinkWrapper = (link: LinkType, pageAbove: PageAbove | undefined) => { return null } -const generateOrganizationSubpageLink = (subpage: IOrganizationSubpage) => { +const generateOrganizationSubpageLink = ( + subpage: IOrganizationSubpage | IOrganizationParentSubpage, +) => { const prefix = getOrganizationPageUrlPrefix(subpage.sys.locale) return mapLink({ @@ -88,7 +97,9 @@ const generateOrganizationSubpageLink = (subpage: IOrganizationSubpage) => { }, }, fields: { - text: subpage.fields.shortTitle || subpage.fields.title, + text: + (subpage as IOrganizationSubpage).fields.shortTitle || + subpage.fields.title, url: `/${prefix}/${subpage.fields.organizationPage.fields.slug}/${subpage.fields.slug}`, }, }) From 31b2fdbf901de85a6b73f94bbf32a7eb5a4402ec Mon Sep 17 00:00:00 2001 From: albinagu <47886428+albinagu@users.noreply.github.com> Date: Mon, 9 Dec 2024 13:59:00 +0000 Subject: [PATCH 65/85] fix(inheritance-report): adding service to mappers (#17177) * fix(inheritance-report): adding service to mappers * prepaid text --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../templates/inheritance-report/utils/mappers.ts | 1 + .../src/forms/sections/prepaidInheritance/heirs.ts | 2 +- .../templates/inheritance-report/src/lib/dataSchema.ts | 1 + .../templates/inheritance-report/src/lib/messages.ts | 10 ++++++++-- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/libs/application/template-api-modules/src/lib/modules/templates/inheritance-report/utils/mappers.ts b/libs/application/template-api-modules/src/lib/modules/templates/inheritance-report/utils/mappers.ts index b248ea6462c9..b4e0e2ab0f8e 100644 --- a/libs/application/template-api-modules/src/lib/modules/templates/inheritance-report/utils/mappers.ts +++ b/libs/application/template-api-modules/src/lib/modules/templates/inheritance-report/utils/mappers.ts @@ -265,6 +265,7 @@ export const expandAnswers = ( rent: answers?.funeralCost?.rent ?? '', food: answers?.funeralCost?.food ?? '', tombstone: answers?.funeralCost?.tombstone ?? '', + service: answers?.funeralCost?.service ?? '', hasOther: answers?.funeralCost?.hasOther ?? [], other: answers?.funeralCost?.other ?? '', otherDetails: answers?.funeralCost?.otherDetails ?? '', diff --git a/libs/application/templates/inheritance-report/src/forms/sections/prepaidInheritance/heirs.ts b/libs/application/templates/inheritance-report/src/forms/sections/prepaidInheritance/heirs.ts index 8403cdfb24e6..fc80ed76ab7a 100644 --- a/libs/application/templates/inheritance-report/src/forms/sections/prepaidInheritance/heirs.ts +++ b/libs/application/templates/inheritance-report/src/forms/sections/prepaidInheritance/heirs.ts @@ -84,7 +84,7 @@ export const prePaidHeirs = buildSection({ buildMultiField({ id: 'prePaidHeirsAdditionalInfo', title: m.heirAdditionalInfo, - description: m.heirAdditionalInfoDescription, + description: m.heirAdditionalInfoPrePaidDescription, children: [ buildTextField({ id: 'heirsAdditionalInfo', diff --git a/libs/application/templates/inheritance-report/src/lib/dataSchema.ts b/libs/application/templates/inheritance-report/src/lib/dataSchema.ts index 70b60e815a72..3e437681836f 100644 --- a/libs/application/templates/inheritance-report/src/lib/dataSchema.ts +++ b/libs/application/templates/inheritance-report/src/lib/dataSchema.ts @@ -498,6 +498,7 @@ export const inheritanceReportSchema = z.object({ rent: z.string().optional(), food: z.string().optional(), tombstone: z.string().optional(), + service: z.string().optional(), hasOther: z.array(z.enum([YES])).optional(), other: z.string().optional(), otherDetails: z.string().optional(), diff --git a/libs/application/templates/inheritance-report/src/lib/messages.ts b/libs/application/templates/inheritance-report/src/lib/messages.ts index 39003d0532a1..16822da0fc70 100644 --- a/libs/application/templates/inheritance-report/src/lib/messages.ts +++ b/libs/application/templates/inheritance-report/src/lib/messages.ts @@ -1199,7 +1199,7 @@ export const m = defineMessages({ // Assets to share assetsToShareDescription: { - id: 'ir.application:assetsToShareDescription', + id: 'ir.application:assetsToShareDescription#markdown', defaultMessage: 'FrĂĄ dregst bĂșshluti eftirlifandi maka samkvĂŠmt reglum hjĂșskaparlaga nr. 31/1993.', description: '', @@ -1382,7 +1382,7 @@ export const m = defineMessages({ description: '', }, heirsAndPartitionDescription: { - id: 'ir.application:heirsAndPartitionDescription', + id: 'ir.application:heirsAndPartitionDescription#markdown', defaultMessage: 'SkrĂĄ skal netfang erfingja vegna tilkynninga skattstjĂłra skv. 9. og 10. gr. laga nr. 14/2004.', description: '', @@ -1664,6 +1664,12 @@ export const m = defineMessages({ 'SkĂœringar og athugasemdir erfingja og/eĂ°a ĂŸess sem skilar inn erfĂ°afjĂĄrskĂœrslu.', description: '', }, + heirAdditionalInfoPrePaidDescription: { + id: 'ir.application:heirAdditionalInfoPrePaidDescription', + defaultMessage: + 'SkĂœringar og athugasemdir erfingja og/eĂ°a ĂŸess sem skilar inn erfĂ°afjĂĄrskĂœrslu.', + description: '', + }, info: { id: 'ir.application:info', defaultMessage: 'Athugasemdir', From c33dc8068cf8fed457a8d0f3a5a6d170b5dc8c44 Mon Sep 17 00:00:00 2001 From: mannipje <135017126+mannipje@users.noreply.github.com> Date: Mon, 9 Dec 2024 15:31:01 +0000 Subject: [PATCH 66/85] feat(web): Change vinnueftirlitid service web footer (#17179) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../Wrapper/OrganizationWrapper.tsx | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/apps/web/components/Organization/Wrapper/OrganizationWrapper.tsx b/apps/web/components/Organization/Wrapper/OrganizationWrapper.tsx index 44e38cb37d59..9c68d0398745 100644 --- a/apps/web/components/Organization/Wrapper/OrganizationWrapper.tsx +++ b/apps/web/components/Organization/Wrapper/OrganizationWrapper.tsx @@ -1,6 +1,7 @@ import React, { PropsWithChildren, ReactNode, + useContext, useEffect, useMemo, useState, @@ -44,6 +45,7 @@ import { } from '@island.is/web/components' import { DefaultHeader, WatsonChatPanel } from '@island.is/web/components' import { SLICE_SPACING, STICKY_NAV_MAX_WIDTH } from '@island.is/web/constants' +import { GlobalContext } from '@island.is/web/context' import { Image, Organization, @@ -712,6 +714,8 @@ export const OrganizationFooter: React.FC< let OrganizationFooterComponent = null + const { isServiceWeb } = useContext(GlobalContext) + switch (organization?.slug) { case 'syslumenn': case 'district-commissioner': @@ -910,6 +914,29 @@ export const OrganizationFooter: React.FC< ) break + case 'vinnueftirlitid': + case 'aosh': + { + const footerItems = organization?.footerItems ?? [] + if (footerItems.length === 0) break + OrganizationFooterComponent = ( + + ) + } + break default: { const footerItems = organization?.footerItems ?? [] if (footerItems.length === 0) break From 556dc1bc157a4229e55fe04d79ae760ee833c91c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sn=C3=A6r=20Seljan=20=C3=9E=C3=B3roddsson?= Date: Mon, 9 Dec 2024 16:02:39 +0000 Subject: [PATCH 67/85] fix(service-portal): Fixes for third-party paths to old login (#17118) * Fix thirdparty paths and add fallback redirect in bff provider * Update comment * revert notification worker changes * simplify bff startup script * Add backwards compatibility for old login path and add targetLInkUri param if it exists to be included in the login flow * Update links * default to href if param is not found * Update target link uri to support fallback to /login urls * Add support for existing params * Update comment * Add comment for clarity * UseCallback instead of useMemo * Fix notifications third party url * remove console.log * chore: charts update dirty files * update urls * chore: charts update dirty files --------- Co-authored-by: snaerseljan Co-authored-by: andes-it Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- apps/native/app/src/lib/passkeys/helpers.ts | 18 +++---- apps/portals/admin/project.json | 4 +- apps/portals/my-pages/project.json | 4 +- .../infra/user-notification.ts | 19 ++++--- .../notificationsWorker/mocks.ts | 1 + .../notificationsWorker.service.spec.ts | 32 +++++------ .../notificationsWorker.service.ts | 25 +++++---- apps/services/user-notification/src/config.ts | 6 ++- .../user-notification/test/environment.ts | 1 + apps/web/components/Header/LoginButton.tsx | 2 +- charts/islandis/values.dev.yaml | 2 + charts/islandis/values.prod.yaml | 2 + charts/islandis/values.staging.yaml | 2 + .../user-notification-worker/values.dev.yaml | 1 + .../user-notification-worker/values.prod.yaml | 1 + .../values.staging.yaml | 1 + .../user-notification/values.dev.yaml | 1 + .../user-notification/values.prod.yaml | 1 + .../user-notification/values.staging.yaml | 1 + libs/react-spa/bff/src/lib/BffProvider.tsx | 54 ++++++++++++++++--- 20 files changed, 119 insertions(+), 59 deletions(-) diff --git a/apps/native/app/src/lib/passkeys/helpers.ts b/apps/native/app/src/lib/passkeys/helpers.ts index 594f65f393ba..eea55f73f3c3 100644 --- a/apps/native/app/src/lib/passkeys/helpers.ts +++ b/apps/native/app/src/lib/passkeys/helpers.ts @@ -1,6 +1,6 @@ import { - PasskeyRegistrationResult, PasskeyAuthenticationResult, + PasskeyRegistrationResult, } from 'react-native-passkey' import { AuthPasskeyAuthenticationOptions, @@ -106,6 +106,10 @@ export const convertBase64UrlToBase64String = (base64Url: string) => { return base64Url.replace(/-/g, '+').replace(/_/g, '/') } +const MY_PAGES_PATH = '/minarsidur' +const APPLICATIONS_PATH = '/umsoknir' +const allowedPaths = [MY_PAGES_PATH, APPLICATIONS_PATH] + export const addPasskeyAsLoginHint = ( url: string, authenticationResponse: string, @@ -120,14 +124,10 @@ export const addPasskeyAsLoginHint = ( return false } - if (url.includes('/minarsidur')) { - return `${origin}/minarsidur/login?login_hint=passkey:${authenticationResponse}&target_link_uri=${encodeURIComponent( - url, - )}` - } + const matchedPath = allowedPaths.find((path) => url.includes(path)) - if (url.includes('/umsoknir')) { - return `${origin}/umsoknir/login?login_hint=passkey:${authenticationResponse}&target_link_uri=${encodeURIComponent( + if (matchedPath) { + return `${origin}/bff/login?login_hint=passkey:${authenticationResponse}&target_link_uri=${encodeURIComponent( url, )}` } @@ -139,7 +139,7 @@ export const doesUrlSupportPasskey = (url: string): boolean => { (url.startsWith('https://beta.dev01.devland.is') || url.startsWith('https://beta.staging01.devland.is') || url.startsWith('https://island.is')) && - (url.includes('/minarsidur') || url.includes('/umsoknir')) + (url.includes(MY_PAGES_PATH) || url.includes(APPLICATIONS_PATH)) ) { return true } diff --git a/apps/portals/admin/project.json b/apps/portals/admin/project.json index ae909c4cae73..4cbb46369d9e 100644 --- a/apps/portals/admin/project.json +++ b/apps/portals/admin/project.json @@ -96,9 +96,7 @@ "start-bff": { "executor": "nx:run-commands", "options": { - "commands": [ - "node -r esbuild-register src/cli/cli.ts run-local-env services-bff-portals-admin" - ], + "commands": ["yarn infra run-local-env services-bff-portals-admin"], "cwd": "infra" } }, diff --git a/apps/portals/my-pages/project.json b/apps/portals/my-pages/project.json index 285c3d1a352e..592d65f6910d 100644 --- a/apps/portals/my-pages/project.json +++ b/apps/portals/my-pages/project.json @@ -105,9 +105,7 @@ "start-bff": { "executor": "nx:run-commands", "options": { - "commands": [ - "node -r esbuild-register src/cli/cli.ts run-local-env services-bff-portals-my-pages" - ], + "commands": ["yarn infra run-local-env services-bff-portals-my-pages"], "cwd": "infra" } }, diff --git a/apps/services/user-notification/infra/user-notification.ts b/apps/services/user-notification/infra/user-notification.ts index 96c8788bf22a..4cd6e621c5c1 100644 --- a/apps/services/user-notification/infra/user-notification.ts +++ b/apps/services/user-notification/infra/user-notification.ts @@ -1,16 +1,16 @@ -import { - Base, - Client, - NationalRegistryB2C, - RskCompanyInfo, -} from '../../../../infra/src/dsl/xroad' import { CodeOwners, + ServiceBuilder, json, ref, service, - ServiceBuilder, } from '../../../../infra/src/dsl/dsl' +import { + Base, + Client, + NationalRegistryB2C, + RskCompanyInfo, +} from '../../../../infra/src/dsl/xroad' const serviceName = 'user-notification' const serviceWorkerName = `${serviceName}-worker` @@ -42,6 +42,11 @@ const getEnv = (services: { '@island.is/auth/delegations/index:system', ]), SERVICE_PORTAL_CLICK_ACTION_URL: 'https://island.is/minarsidur', + SERVICE_PORTAL_BFF_LOGIN_URL: { + dev: 'https://beta.dev01.devland.is/bff/login', + staging: 'https://beta.staging01.devland.is/bff/login', + prod: 'https://island.is/bff/login', + }, EMAIL_FROM_ADDRESS: { dev: 'development@island.is', staging: 'development@island.is', diff --git a/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/mocks.ts b/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/mocks.ts index 02638dd0f0f3..62af04a27bb1 100644 --- a/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/mocks.ts +++ b/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/mocks.ts @@ -233,6 +233,7 @@ export const MockUserNotificationsConfig: ConfigType< emailFromAddress: 'development@island.is', isConfigured: true, servicePortalClickActionUrl: 'https://island.is/minarsidur', + servicePortalBffLoginUrl: 'https://island.is/bff/login', redis: { nodes: ['node'], ssl: false, diff --git a/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/notificationsWorker.service.spec.ts b/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/notificationsWorker.service.spec.ts index df1a95d6b828..ab618464b2d7 100644 --- a/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/notificationsWorker.service.spec.ts +++ b/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/notificationsWorker.service.spec.ts @@ -1,29 +1,28 @@ -import { getConnectionToken, getModelToken } from '@nestjs/sequelize' import { INestApplication, Type } from '@nestjs/common' +import { getConnectionToken, getModelToken } from '@nestjs/sequelize' import { TestingModuleBuilder } from '@nestjs/testing' import { Sequelize } from 'sequelize-typescript' -import { testServer, truncate, useDatabase } from '@island.is/testing/nest' -import { UserProfileDto, V2UsersApi } from '@island.is/clients/user-profile' -import { getQueueServiceToken, QueueService } from '@island.is/message-queue' -import { FeatureFlagService } from '@island.is/nest/feature-flags' -import { NationalRegistryV3ClientService } from '@island.is/clients/national-registry-v3' -import { EmailService } from '@island.is/email-service' import { DelegationsApi } from '@island.is/clients/auth/delegation-api' import { CmsService } from '@island.is/clients/cms' +import { NationalRegistryV3ClientService } from '@island.is/clients/national-registry-v3' import { CompanyExtendedInfo, CompanyRegistryClientService, } from '@island.is/clients/rsk/company-registry' +import { UserProfileDto, V2UsersApi } from '@island.is/clients/user-profile' +import { EmailService } from '@island.is/email-service' +import { QueueService, getQueueServiceToken } from '@island.is/message-queue' +import { FeatureFlagService } from '@island.is/nest/feature-flags' +import { testServer, truncate, useDatabase } from '@island.is/testing/nest' import { UserNotificationsConfig } from '../../../../config' import { FIREBASE_PROVIDER } from '../../../../constants' import { AppModule } from '../../../app.module' import { SequelizeConfigService } from '../../../sequelizeConfig.service' -import { NotificationDispatchService } from '../notificationDispatch.service' import { Notification } from '../notification.model' +import { NotificationDispatchService } from '../notificationDispatch.service' import { NotificationsService } from '../notifications.service' -import { NotificationsWorkerService } from './notificationsWorker.service' import { wait } from './helpers' import { MockDelegationsService, @@ -31,19 +30,20 @@ import { MockNationalRegistryV3ClientService, MockUserNotificationsConfig, companyUser, - userWithNoEmail, + delegationSubjectId, + getMockHnippTemplate, + mockTemplateId, + userProfiles, userWithDelegations, userWithDelegations2, userWithDocumentNotificationsDisabled, userWithEmailNotificationsDisabled, userWithFeatureFlagDisabled, - userWithSendToDelegationsFeatureFlagDisabled, - getMockHnippTemplate, - mockTemplateId, - delegationSubjectId, userWithNoDelegations, - userProfiles, + userWithNoEmail, + userWithSendToDelegationsFeatureFlagDisabled, } from './mocks' +import { NotificationsWorkerService } from './notificationsWorker.service' const workingHoursDelta = 1000 * 60 * 60 // 1 hour const insideWorkingHours = new Date(2021, 1, 1, 9, 0, 0) @@ -303,7 +303,7 @@ describe('NotificationsWorkerService', () => { expect.objectContaining({ component: 'ImageWithLink', context: expect.objectContaining({ - href: `https://island.is/minarsidur/login?login_hint=${delegationSubjectId}&target_link_uri=https://island.is/minarsidur/postholf`, + href: `https://island.is/bff/login?login_hint=${delegationSubjectId}&target_link_uri=https://island.is/minarsidur/postholf`, }), }), ]), diff --git a/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/notificationsWorker.service.ts b/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/notificationsWorker.service.ts index fd353149f63d..d0d9ae9368fd 100644 --- a/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/notificationsWorker.service.ts +++ b/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/notificationsWorker.service.ts @@ -1,21 +1,20 @@ import { Inject, Injectable, OnApplicationBootstrap } from '@nestjs/common' -import { join } from 'path' import { InjectModel } from '@nestjs/sequelize' import { isCompany } from 'kennitala' +import { join } from 'path' import { User } from '@island.is/auth-nest-tools' import { DocumentsScope } from '@island.is/auth/scopes' +import { DelegationsApi } from '@island.is/clients/auth/delegation-api' import { - EinstaklingurDTONafnAllt, EinstaklingurDTONafnItar, NationalRegistryV3ClientService, } from '@island.is/clients/national-registry-v3' import { + ActorProfileDto, UserProfileDto, V2UsersApi, - ActorProfileDto, } from '@island.is/clients/user-profile' -import { DelegationsApi } from '@island.is/clients/auth/delegation-api' import { Body, EmailService, Message } from '@island.is/email-service' import type { Logger } from '@island.is/logging' import { LOGGER_PROVIDER } from '@island.is/logging' @@ -29,17 +28,17 @@ import { type ConfigType } from '@island.is/nest/config' import { FeatureFlagService, Features } from '@island.is/nest/feature-flags' import type { Locale } from '@island.is/shared/types' -import { UserNotificationsConfig } from '../../../../config' -import { MessageProcessorService } from '../messageProcessor.service' -import { NotificationDispatchService } from '../notificationDispatch.service' -import { CreateHnippNotificationDto } from '../dto/createHnippNotification.dto' -import { NotificationsService } from '../notifications.service' -import { HnippTemplate } from '../dto/hnippTemplate.response' -import { Notification } from '../notification.model' import { CompanyExtendedInfo, CompanyRegistryClientService, } from '@island.is/clients/rsk/company-registry' +import { UserNotificationsConfig } from '../../../../config' +import { CreateHnippNotificationDto } from '../dto/createHnippNotification.dto' +import { HnippTemplate } from '../dto/hnippTemplate.response' +import { MessageProcessorService } from '../messageProcessor.service' +import { Notification } from '../notification.model' +import { NotificationDispatchService } from '../notificationDispatch.service' +import { NotificationsService } from '../notifications.service' type HandleNotification = { profile: { @@ -475,8 +474,8 @@ export class NotificationsWorkerService { return shouldUseThirdPartyLogin ? `${ - this.config.servicePortalClickActionUrl - }/login?login_hint=${subjectId}&target_link_uri=${encodeURI( + this.config.servicePortalBffLoginUrl + }?login_hint=${subjectId}&target_link_uri=${encodeURI( formattedTemplate.clickActionUrl, )}` : formattedTemplate.clickActionUrl diff --git a/apps/services/user-notification/src/config.ts b/apps/services/user-notification/src/config.ts index ee1134be1e34..0ba2d9017bcb 100644 --- a/apps/services/user-notification/src/config.ts +++ b/apps/services/user-notification/src/config.ts @@ -1,13 +1,14 @@ import { z } from 'zod' -import { defineConfig } from '@island.is/nest/config' import { processJob } from '@island.is/infra-nest-server' +import { defineConfig } from '@island.is/nest/config' // Exported for testing purposes export const schema = z.object({ isWorker: z.boolean(), firebaseCredentials: z.string(), servicePortalClickActionUrl: z.string(), + servicePortalBffLoginUrl: z.string(), contentfulAccessToken: z.string(), emailFromAddress: z.string(), redis: z.object({ @@ -27,6 +28,9 @@ export const UserNotificationsConfig = defineConfig({ servicePortalClickActionUrl: env.optional('SERVICE_PORTAL_CLICK_ACTION_URL') ?? 'https://island.is/minarsidur', + servicePortalBffLoginUrl: + env.optional('SERVICE_PORTAL_BFF_LOGIN_URL') ?? + 'https://island.is/bff/login', contentfulAccessToken: env.optional('CONTENTFUL_ACCESS_TOKEN', ''), emailFromAddress: env.required( 'EMAIL_FROM_ADDRESS', diff --git a/apps/services/user-notification/test/environment.ts b/apps/services/user-notification/test/environment.ts index 77956bffa36e..d7605cd63d56 100644 --- a/apps/services/user-notification/test/environment.ts +++ b/apps/services/user-notification/test/environment.ts @@ -5,6 +5,7 @@ export const environment = { SQS_ACCESS_KEY: 'testing', SQS_SECRET_ACCESS_KEY: 'testing', SERVICE_PORTAL_CLICK_ACTION_URL: 'https://island.is/minarsidur', + SERVICE_PORTAL_BFF_LOGIN_URL: 'https://island.is/bff/login', // Disable redis registration during testing REDIS_URL_NODE_01: '[]', } as const diff --git a/apps/web/components/Header/LoginButton.tsx b/apps/web/components/Header/LoginButton.tsx index a7e696641095..726843f7d262 100644 --- a/apps/web/components/Header/LoginButton.tsx +++ b/apps/web/components/Header/LoginButton.tsx @@ -18,7 +18,7 @@ import { LayoutProps } from '@island.is/web/layouts/main' import * as styles from './LoginButton.css' const minarsidurLink = '/minarsidur/' -const minarsidurDelegationsLink = '/minarsidur/login?prompt=select_account' +const minarsidurDelegationsLink = '/bff/login?prompt=select_account' export function LoginButton(props: { colorScheme: ButtonTypes['colorScheme'] diff --git a/charts/islandis/values.dev.yaml b/charts/islandis/values.dev.yaml index 3ff1c6643bbd..e0f5e81d1937 100644 --- a/charts/islandis/values.dev.yaml +++ b/charts/islandis/values.dev.yaml @@ -3184,6 +3184,7 @@ user-notification: REDIS_URL_NODE_01: '["clustercfg.general-redis-cluster-group.5fzau3.euw1.cache.amazonaws.com:6379"]' REDIS_USE_SSL: 'true' SERVERSIDE_FEATURES_ON: '' + SERVICE_PORTAL_BFF_LOGIN_URL: 'https://beta.dev01.devland.is/bff/login' SERVICE_PORTAL_CLICK_ACTION_URL: 'https://island.is/minarsidur' USER_PROFILE_CLIENT_URL: 'http://web-service-portal-api.service-portal.svc.cluster.local' XROAD_BASE_PATH: 'http://securityserver.dev01.devland.is' @@ -3387,6 +3388,7 @@ user-notification-worker: REDIS_URL_NODE_01: '["clustercfg.general-redis-cluster-group.5fzau3.euw1.cache.amazonaws.com:6379"]' REDIS_USE_SSL: 'true' SERVERSIDE_FEATURES_ON: '' + SERVICE_PORTAL_BFF_LOGIN_URL: 'https://beta.dev01.devland.is/bff/login' SERVICE_PORTAL_CLICK_ACTION_URL: 'https://island.is/minarsidur' USER_PROFILE_CLIENT_URL: 'http://web-service-portal-api.service-portal.svc.cluster.local' XROAD_BASE_PATH: 'http://securityserver.dev01.devland.is' diff --git a/charts/islandis/values.prod.yaml b/charts/islandis/values.prod.yaml index 3d4924f04ccd..d08668a62457 100644 --- a/charts/islandis/values.prod.yaml +++ b/charts/islandis/values.prod.yaml @@ -3063,6 +3063,7 @@ user-notification: REDIS_URL_NODE_01: '["clustercfg.general-redis-cluster-group.whakos.euw1.cache.amazonaws.com:6379"]' REDIS_USE_SSL: 'true' SERVERSIDE_FEATURES_ON: 'driving-license-use-v1-endpoint-for-v2-comms' + SERVICE_PORTAL_BFF_LOGIN_URL: 'https://island.is/bff/login' SERVICE_PORTAL_CLICK_ACTION_URL: 'https://island.is/minarsidur' USER_PROFILE_CLIENT_URL: 'http://web-service-portal-api.service-portal.svc.cluster.local' XROAD_BASE_PATH: 'http://securityserver.island.is' @@ -3266,6 +3267,7 @@ user-notification-worker: REDIS_URL_NODE_01: '["clustercfg.general-redis-cluster-group.whakos.euw1.cache.amazonaws.com:6379"]' REDIS_USE_SSL: 'true' SERVERSIDE_FEATURES_ON: 'driving-license-use-v1-endpoint-for-v2-comms' + SERVICE_PORTAL_BFF_LOGIN_URL: 'https://island.is/bff/login' SERVICE_PORTAL_CLICK_ACTION_URL: 'https://island.is/minarsidur' USER_PROFILE_CLIENT_URL: 'http://web-service-portal-api.service-portal.svc.cluster.local' XROAD_BASE_PATH: 'http://securityserver.island.is' diff --git a/charts/islandis/values.staging.yaml b/charts/islandis/values.staging.yaml index 311d39020959..2cd78ddb6714 100644 --- a/charts/islandis/values.staging.yaml +++ b/charts/islandis/values.staging.yaml @@ -2922,6 +2922,7 @@ user-notification: REDIS_URL_NODE_01: '["clustercfg.general-redis-cluster-group.ab9ckb.euw1.cache.amazonaws.com:6379"]' REDIS_USE_SSL: 'true' SERVERSIDE_FEATURES_ON: '' + SERVICE_PORTAL_BFF_LOGIN_URL: 'https://beta.staging01.devland.is/bff/login' SERVICE_PORTAL_CLICK_ACTION_URL: 'https://island.is/minarsidur' USER_PROFILE_CLIENT_URL: 'http://web-service-portal-api.service-portal.svc.cluster.local' XROAD_BASE_PATH: 'http://securityserver.staging01.devland.is' @@ -3125,6 +3126,7 @@ user-notification-worker: REDIS_URL_NODE_01: '["clustercfg.general-redis-cluster-group.ab9ckb.euw1.cache.amazonaws.com:6379"]' REDIS_USE_SSL: 'true' SERVERSIDE_FEATURES_ON: '' + SERVICE_PORTAL_BFF_LOGIN_URL: 'https://beta.staging01.devland.is/bff/login' SERVICE_PORTAL_CLICK_ACTION_URL: 'https://island.is/minarsidur' USER_PROFILE_CLIENT_URL: 'http://web-service-portal-api.service-portal.svc.cluster.local' XROAD_BASE_PATH: 'http://securityserver.staging01.devland.is' diff --git a/charts/services/user-notification-worker/values.dev.yaml b/charts/services/user-notification-worker/values.dev.yaml index 2a0ec43d43f3..ee126139b967 100644 --- a/charts/services/user-notification-worker/values.dev.yaml +++ b/charts/services/user-notification-worker/values.dev.yaml @@ -49,6 +49,7 @@ env: REDIS_URL_NODE_01: '["clustercfg.general-redis-cluster-group.5fzau3.euw1.cache.amazonaws.com:6379"]' REDIS_USE_SSL: 'true' SERVERSIDE_FEATURES_ON: '' + SERVICE_PORTAL_BFF_LOGIN_URL: 'https://beta.dev01.devland.is/bff/login' SERVICE_PORTAL_CLICK_ACTION_URL: 'https://island.is/minarsidur' USER_PROFILE_CLIENT_URL: 'http://web-service-portal-api.service-portal.svc.cluster.local' XROAD_BASE_PATH: 'http://securityserver.dev01.devland.is' diff --git a/charts/services/user-notification-worker/values.prod.yaml b/charts/services/user-notification-worker/values.prod.yaml index 6ab822a0aa50..dc8b5e5d2697 100644 --- a/charts/services/user-notification-worker/values.prod.yaml +++ b/charts/services/user-notification-worker/values.prod.yaml @@ -49,6 +49,7 @@ env: REDIS_URL_NODE_01: '["clustercfg.general-redis-cluster-group.whakos.euw1.cache.amazonaws.com:6379"]' REDIS_USE_SSL: 'true' SERVERSIDE_FEATURES_ON: 'driving-license-use-v1-endpoint-for-v2-comms' + SERVICE_PORTAL_BFF_LOGIN_URL: 'https://island.is/bff/login' SERVICE_PORTAL_CLICK_ACTION_URL: 'https://island.is/minarsidur' USER_PROFILE_CLIENT_URL: 'http://web-service-portal-api.service-portal.svc.cluster.local' XROAD_BASE_PATH: 'http://securityserver.island.is' diff --git a/charts/services/user-notification-worker/values.staging.yaml b/charts/services/user-notification-worker/values.staging.yaml index 4d38939c4667..ce46946924d2 100644 --- a/charts/services/user-notification-worker/values.staging.yaml +++ b/charts/services/user-notification-worker/values.staging.yaml @@ -49,6 +49,7 @@ env: REDIS_URL_NODE_01: '["clustercfg.general-redis-cluster-group.ab9ckb.euw1.cache.amazonaws.com:6379"]' REDIS_USE_SSL: 'true' SERVERSIDE_FEATURES_ON: '' + SERVICE_PORTAL_BFF_LOGIN_URL: 'https://beta.staging01.devland.is/bff/login' SERVICE_PORTAL_CLICK_ACTION_URL: 'https://island.is/minarsidur' USER_PROFILE_CLIENT_URL: 'http://web-service-portal-api.service-portal.svc.cluster.local' XROAD_BASE_PATH: 'http://securityserver.staging01.devland.is' diff --git a/charts/services/user-notification/values.dev.yaml b/charts/services/user-notification/values.dev.yaml index 2a4c2df02f1d..f6ba4ec5469a 100644 --- a/charts/services/user-notification/values.dev.yaml +++ b/charts/services/user-notification/values.dev.yaml @@ -46,6 +46,7 @@ env: REDIS_URL_NODE_01: '["clustercfg.general-redis-cluster-group.5fzau3.euw1.cache.amazonaws.com:6379"]' REDIS_USE_SSL: 'true' SERVERSIDE_FEATURES_ON: '' + SERVICE_PORTAL_BFF_LOGIN_URL: 'https://beta.dev01.devland.is/bff/login' SERVICE_PORTAL_CLICK_ACTION_URL: 'https://island.is/minarsidur' USER_PROFILE_CLIENT_URL: 'http://web-service-portal-api.service-portal.svc.cluster.local' XROAD_BASE_PATH: 'http://securityserver.dev01.devland.is' diff --git a/charts/services/user-notification/values.prod.yaml b/charts/services/user-notification/values.prod.yaml index 00cf09f12ae3..bb75838fa119 100644 --- a/charts/services/user-notification/values.prod.yaml +++ b/charts/services/user-notification/values.prod.yaml @@ -46,6 +46,7 @@ env: REDIS_URL_NODE_01: '["clustercfg.general-redis-cluster-group.whakos.euw1.cache.amazonaws.com:6379"]' REDIS_USE_SSL: 'true' SERVERSIDE_FEATURES_ON: 'driving-license-use-v1-endpoint-for-v2-comms' + SERVICE_PORTAL_BFF_LOGIN_URL: 'https://island.is/bff/login' SERVICE_PORTAL_CLICK_ACTION_URL: 'https://island.is/minarsidur' USER_PROFILE_CLIENT_URL: 'http://web-service-portal-api.service-portal.svc.cluster.local' XROAD_BASE_PATH: 'http://securityserver.island.is' diff --git a/charts/services/user-notification/values.staging.yaml b/charts/services/user-notification/values.staging.yaml index 9a9c1025e3bc..843f3f779e41 100644 --- a/charts/services/user-notification/values.staging.yaml +++ b/charts/services/user-notification/values.staging.yaml @@ -46,6 +46,7 @@ env: REDIS_URL_NODE_01: '["clustercfg.general-redis-cluster-group.ab9ckb.euw1.cache.amazonaws.com:6379"]' REDIS_USE_SSL: 'true' SERVERSIDE_FEATURES_ON: '' + SERVICE_PORTAL_BFF_LOGIN_URL: 'https://beta.staging01.devland.is/bff/login' SERVICE_PORTAL_CLICK_ACTION_URL: 'https://island.is/minarsidur' USER_PROFILE_CLIENT_URL: 'http://web-service-portal-api.service-portal.svc.cluster.local' XROAD_BASE_PATH: 'http://securityserver.staging01.devland.is' diff --git a/libs/react-spa/bff/src/lib/BffProvider.tsx b/libs/react-spa/bff/src/lib/BffProvider.tsx index 7ca9502c62ff..f10534fc550e 100644 --- a/libs/react-spa/bff/src/lib/BffProvider.tsx +++ b/libs/react-spa/bff/src/lib/BffProvider.tsx @@ -42,6 +42,7 @@ export const BffProvider = ({ authState === 'switching' || authState === 'logging-out' const isLoggedIn = authState === 'logged-in' + const oldLoginPath = `${applicationBasePath}/login` const { postMessage } = useBffBroadcaster((event) => { if ( @@ -74,6 +75,39 @@ export const BffProvider = ({ } }, [postMessage, state.userInfo, isLoggedIn]) + /** + * Builds authentication query parameters for login redirection: + * - target_link_uri: Destination URL after successful login + * ‱ Uses URL from query string if provided + * ‱ Falls back to current URL, with '/login' stripped if on legacy login path + * - prompt: Optional authentication prompt type + * - login_hint: Optional suggested account identifier + */ + const getLoginQueryParams = useCallback(() => { + const urlParams = new URLSearchParams(window.location.search) + const targetLinkUri = urlParams.get('target_link_uri') + const prompt = urlParams.get('prompt') + const loginHint = urlParams.get('login_hint') + const url = window.location.href + + const params = { + target_link_uri: + targetLinkUri ?? + (window.location.pathname.startsWith(oldLoginPath) + ? // Remove `/login` from the path to prevent redirect loop + url.replace(oldLoginPath, applicationBasePath) + : url), + ...(prompt && { + prompt, + }), + ...(loginHint && { + login_hint: loginHint, + }), + } + + return params + }, [applicationBasePath, oldLoginPath]) + const checkLogin = async (noRefresh = false) => { dispatch({ type: ActionType.SIGNIN_START, @@ -100,6 +134,13 @@ export const BffProvider = ({ return } + // If user is logged in and on the old login path, then start the sign-in process + if (window.location.pathname.startsWith(oldLoginPath)) { + signIn() + + return + } + const user = await res.json() dispatch({ @@ -119,10 +160,8 @@ export const BffProvider = ({ type: ActionType.SIGNIN_START, }) - window.location.href = bffUrlGenerator('/login', { - target_link_uri: window.location.href, - }) - }, [bffUrlGenerator]) + window.location.href = bffUrlGenerator('/login', getLoginQueryParams()) + }, [bffUrlGenerator, getLoginQueryParams]) const signOut = useCallback(() => { if (!state.userInfo) { @@ -149,14 +188,17 @@ export const BffProvider = ({ type: ActionType.SWITCH_USER, }) + const loginQueryParams = getLoginQueryParams() + const targetLinkUri = loginQueryParams['target_link_uri'] + window.location.href = bffUrlGenerator('/login', { - target_link_uri: window.location.href, + target_link_uri: targetLinkUri, ...(nationalId ? { login_hint: nationalId } : { prompt: 'select_account' }), }) }, - [bffUrlGenerator], + [bffUrlGenerator, getLoginQueryParams], ) const checkQueryStringError = () => { From 24c792bf5f5600a6507b497f9dae2a4ba3904fb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAnar=20Vestmann?= <43557895+RunarVestmann@users.noreply.github.com> Date: Mon, 9 Dec 2024 17:07:36 +0000 Subject: [PATCH 68/85] fix(web): Set DropdownMenu styles to use minWidth (#17185) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- apps/web/components/Header/LoginButton.css.ts | 5 ----- apps/web/components/Header/LoginButton.tsx | 3 --- libs/island-ui/core/src/lib/DropdownMenu/DropdownMenu.css.ts | 3 ++- 3 files changed, 2 insertions(+), 9 deletions(-) delete mode 100644 apps/web/components/Header/LoginButton.css.ts diff --git a/apps/web/components/Header/LoginButton.css.ts b/apps/web/components/Header/LoginButton.css.ts deleted file mode 100644 index 7a7a92b0078b..000000000000 --- a/apps/web/components/Header/LoginButton.css.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { style } from '@vanilla-extract/css' - -export const dropdownMenu = style({ - width: 193, -}) diff --git a/apps/web/components/Header/LoginButton.tsx b/apps/web/components/Header/LoginButton.tsx index 726843f7d262..7dc22605409b 100644 --- a/apps/web/components/Header/LoginButton.tsx +++ b/apps/web/components/Header/LoginButton.tsx @@ -15,8 +15,6 @@ import { webLoginButtonSelect } from '@island.is/plausible' import { useI18n } from '@island.is/web/i18n' import { LayoutProps } from '@island.is/web/layouts/main' -import * as styles from './LoginButton.css' - const minarsidurLink = '/minarsidur/' const minarsidurDelegationsLink = '/bff/login?prompt=select_account' @@ -116,7 +114,6 @@ export function LoginButton(props: { return ( Date: Mon, 9 Dec 2024 18:28:32 +0000 Subject: [PATCH 69/85] feat(j-s): Add indictment count subtypes to IndictmentCount (#17129) * Add frontend changes for selecting indictment subtypes in indictment count * Create migration * Update DB on change * Refactor * Refactor * chore: nx format:write update dirty files * Add feature flag * Refactor * Refactor * Add featur flag --------- Co-authored-by: andes-it Co-authored-by: unakb Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../api/infra/judicial-system-api.ts | 4 +- .../dto/updateIndictmentCount.input.ts | 12 +++- .../models/indictmentCount.model.ts | 9 ++- .../20241204124025-update-indictment-count.js | 25 ++++++++ .../dto/updateIndictmentCount.dto.ts | 11 +++- .../models/indictmentCount.model.ts | 13 +++- .../src/components/FormProvider/case.graphql | 1 + .../IndictmentCaseFilesList.tsx | 8 ++- .../ServiceAnnouncement.tsx | 2 +- .../Indictments/CaseFiles/CaseFiles.tsx | 11 +++- .../Indictments/Indictment/Indictment.tsx | 2 +- .../Indictment/IndictmentCount.css.ts | 15 +++++ .../Indictment/IndictmentCount.strings.ts | 5 ++ .../Indictment/IndictmentCount.tsx | 62 ++++++++++++++++++- .../Indictments/Processing/Processing.tsx | 7 ++- .../web/src/utils/hooks/useCaseList/index.tsx | 7 ++- .../updateIndictmentCount.graphql | 1 + .../web/src/utils/hooks/useSections/index.ts | 8 ++- charts/judicial-system/values.prod.yaml | 2 +- charts/judicial-system/values.staging.yaml | 2 +- .../judicial-system-api/values.prod.yaml | 2 +- .../judicial-system-api/values.staging.yaml | 2 +- libs/judicial-system/types/src/lib/feature.ts | 1 + 23 files changed, 193 insertions(+), 19 deletions(-) create mode 100644 apps/judicial-system/backend/migrations/20241204124025-update-indictment-count.js create mode 100644 apps/judicial-system/web/src/routes/Prosecutor/Indictments/Indictment/IndictmentCount.css.ts diff --git a/apps/judicial-system/api/infra/judicial-system-api.ts b/apps/judicial-system/api/infra/judicial-system-api.ts index efe736fa005b..8f5cabe11e2c 100644 --- a/apps/judicial-system/api/infra/judicial-system-api.ts +++ b/apps/judicial-system/api/infra/judicial-system-api.ts @@ -47,8 +47,8 @@ export const serviceSetup = (services: { }, HIDDEN_FEATURES: { dev: '', - staging: '', - prod: '', + staging: 'MULTIPLE_INDICTMENT_SUBTYPES', + prod: 'MULTIPLE_INDICTMENT_SUBTYPES', }, }) .secrets({ diff --git a/apps/judicial-system/api/src/app/modules/indictment-count/dto/updateIndictmentCount.input.ts b/apps/judicial-system/api/src/app/modules/indictment-count/dto/updateIndictmentCount.input.ts index d4d05cedb578..7e26e223a6b6 100644 --- a/apps/judicial-system/api/src/app/modules/indictment-count/dto/updateIndictmentCount.input.ts +++ b/apps/judicial-system/api/src/app/modules/indictment-count/dto/updateIndictmentCount.input.ts @@ -4,7 +4,10 @@ import { GraphQLJSONObject } from 'graphql-type-json' import { Field, ID, InputType } from '@nestjs/graphql' import type { SubstanceMap } from '@island.is/judicial-system/types' -import { IndictmentCountOffense } from '@island.is/judicial-system/types' +import { + IndictmentCountOffense, + IndictmentSubtype, +} from '@island.is/judicial-system/types' @InputType() export class UpdateIndictmentCountInput { @@ -53,4 +56,11 @@ export class UpdateIndictmentCountInput { @IsOptional() @Field(() => String, { nullable: true }) readonly legalArguments?: string + + @Allow() + @IsOptional() + @IsArray() + @IsEnum(IndictmentSubtype, { each: true }) + @Field(() => [IndictmentSubtype], { nullable: true }) + readonly indictmentCountSubtypes?: IndictmentSubtype[] } diff --git a/apps/judicial-system/api/src/app/modules/indictment-count/models/indictmentCount.model.ts b/apps/judicial-system/api/src/app/modules/indictment-count/models/indictmentCount.model.ts index 3ea6a132945c..f3ef5aa954c6 100644 --- a/apps/judicial-system/api/src/app/modules/indictment-count/models/indictmentCount.model.ts +++ b/apps/judicial-system/api/src/app/modules/indictment-count/models/indictmentCount.model.ts @@ -3,9 +3,13 @@ import { GraphQLJSONObject } from 'graphql-type-json' import { Field, ID, ObjectType, registerEnumType } from '@nestjs/graphql' import type { SubstanceMap } from '@island.is/judicial-system/types' -import { IndictmentCountOffense } from '@island.is/judicial-system/types' +import { + IndictmentCountOffense, + IndictmentSubtype, +} from '@island.is/judicial-system/types' registerEnumType(IndictmentCountOffense, { name: 'IndictmentCountOffense' }) +registerEnumType(IndictmentSubtype, { name: 'IndictmentSubtype' }) @ObjectType() export class IndictmentCount { @@ -41,4 +45,7 @@ export class IndictmentCount { @Field(() => String, { nullable: true }) readonly legalArguments?: string + + @Field(() => [IndictmentSubtype], { nullable: true }) + readonly indictmentCountSubtypes?: IndictmentSubtype[] } diff --git a/apps/judicial-system/backend/migrations/20241204124025-update-indictment-count.js b/apps/judicial-system/backend/migrations/20241204124025-update-indictment-count.js new file mode 100644 index 000000000000..523e416ba23e --- /dev/null +++ b/apps/judicial-system/backend/migrations/20241204124025-update-indictment-count.js @@ -0,0 +1,25 @@ +'use strict' + +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.sequelize.transaction((transaction) => + queryInterface.addColumn( + 'indictment_count', + 'indictment_count_subtypes', + { + type: Sequelize.ARRAY(Sequelize.STRING), + allowNull: true, + defaultValue: [], + }, + { transaction }, + ), + ) + }, + + async down(queryInterface) { + await queryInterface.removeColumn( + 'indictment_count', + 'indictment_count_subtypes', + ) + }, +} diff --git a/apps/judicial-system/backend/src/app/modules/indictment-count/dto/updateIndictmentCount.dto.ts b/apps/judicial-system/backend/src/app/modules/indictment-count/dto/updateIndictmentCount.dto.ts index 0559ffa35277..3b12997e7fed 100644 --- a/apps/judicial-system/backend/src/app/modules/indictment-count/dto/updateIndictmentCount.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/indictment-count/dto/updateIndictmentCount.dto.ts @@ -10,7 +10,10 @@ import { import { ApiPropertyOptional } from '@nestjs/swagger' import type { SubstanceMap } from '@island.is/judicial-system/types' -import { IndictmentCountOffense } from '@island.is/judicial-system/types' +import { + IndictmentCountOffense, + IndictmentSubtype, +} from '@island.is/judicial-system/types' export class UpdateIndictmentCountDto { @IsOptional() @@ -50,4 +53,10 @@ export class UpdateIndictmentCountDto { @IsString() @ApiPropertyOptional({ type: String }) readonly legalArguments?: string + + @IsOptional() + @IsArray() + @IsEnum(IndictmentSubtype, { each: true }) + @ApiPropertyOptional({ enum: IndictmentSubtype, isArray: true }) + readonly indictmentCountSubtypes?: IndictmentSubtype[] } diff --git a/apps/judicial-system/backend/src/app/modules/indictment-count/models/indictmentCount.model.ts b/apps/judicial-system/backend/src/app/modules/indictment-count/models/indictmentCount.model.ts index ae7ec5c59316..03b9d511f2d7 100644 --- a/apps/judicial-system/backend/src/app/modules/indictment-count/models/indictmentCount.model.ts +++ b/apps/judicial-system/backend/src/app/modules/indictment-count/models/indictmentCount.model.ts @@ -11,7 +11,10 @@ import { import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger' import type { SubstanceMap } from '@island.is/judicial-system/types' -import { IndictmentCountOffense } from '@island.is/judicial-system/types' +import { + IndictmentCountOffense, + IndictmentSubtype, +} from '@island.is/judicial-system/types' import { Case } from '../../case/models/case.model' @@ -69,4 +72,12 @@ export class IndictmentCount extends Model { @Column({ type: DataType.TEXT, allowNull: true }) @ApiPropertyOptional({ type: String }) legalArguments?: string + + @Column({ + type: DataType.ARRAY(DataType.ENUM), + allowNull: true, + values: Object.values(IndictmentSubtype), + }) + @ApiPropertyOptional({ enum: IndictmentSubtype, isArray: true }) + indictmentCountSubtypes?: IndictmentSubtype[] } diff --git a/apps/judicial-system/web/src/components/FormProvider/case.graphql b/apps/judicial-system/web/src/components/FormProvider/case.graphql index 341c18c355fc..00c390081d4a 100644 --- a/apps/judicial-system/web/src/components/FormProvider/case.graphql +++ b/apps/judicial-system/web/src/components/FormProvider/case.graphql @@ -216,6 +216,7 @@ query Case($input: CaseQueryInput!) { lawsBroken incidentDescription legalArguments + indictmentCountSubtypes } requestDriversLicenseSuspension appealState diff --git a/apps/judicial-system/web/src/components/IndictmentCaseFilesList/IndictmentCaseFilesList.tsx b/apps/judicial-system/web/src/components/IndictmentCaseFilesList/IndictmentCaseFilesList.tsx index 9dd7bb9cb3bd..b9646bc28671 100644 --- a/apps/judicial-system/web/src/components/IndictmentCaseFilesList/IndictmentCaseFilesList.tsx +++ b/apps/judicial-system/web/src/components/IndictmentCaseFilesList/IndictmentCaseFilesList.tsx @@ -5,6 +5,7 @@ import { AnimatePresence } from 'framer-motion' import { Box, Text } from '@island.is/island-ui/core' import { formatDate } from '@island.is/judicial-system/formatters' import { + Feature, isCompletedCase, isDefenceUser, isDistrictCourtUser, @@ -15,6 +16,7 @@ import { isTrafficViolationCase, } from '@island.is/judicial-system/types' import { + FeatureContext, FileNotFoundModal, PdfButton, SectionHeading, @@ -71,12 +73,16 @@ const IndictmentCaseFilesList: FC = ({ }) => { const { formatMessage } = useIntl() const { user, limitedAccess } = useContext(UserContext) + const { features } = useContext(FeatureContext) const { onOpen, fileNotFound, dismissFileNotFound } = useFileList({ caseId: workingCase.id, connectedCaseParentId, }) - const showTrafficViolationCaseFiles = isTrafficViolationCase(workingCase) + const showTrafficViolationCaseFiles = + features.includes(Feature.MULTIPLE_INDICTMENT_SUBTYPES) || + isTrafficViolationCase(workingCase) + const showSubpoenaPdf = displayGeneratedPDFs && workingCase.defendants?.some( diff --git a/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx b/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx index 8dac19a8c448..f70fe17e3e35 100644 --- a/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx +++ b/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx @@ -1,4 +1,4 @@ -import { FC, useEffect, useState } from 'react' +import { FC } from 'react' import { IntlShape, MessageDescriptor, useIntl } from 'react-intl' import { AlertMessage, Box, LoadingDots, Text } from '@island.is/island-ui/core' diff --git a/apps/judicial-system/web/src/routes/Prosecutor/Indictments/CaseFiles/CaseFiles.tsx b/apps/judicial-system/web/src/routes/Prosecutor/Indictments/CaseFiles/CaseFiles.tsx index 72f3b46677af..e10112fd8b4e 100644 --- a/apps/judicial-system/web/src/routes/Prosecutor/Indictments/CaseFiles/CaseFiles.tsx +++ b/apps/judicial-system/web/src/routes/Prosecutor/Indictments/CaseFiles/CaseFiles.tsx @@ -5,9 +5,13 @@ import router from 'next/router' import { Box, InputFileUpload } from '@island.is/island-ui/core' import { fileExtensionWhitelist } from '@island.is/island-ui/core/types' import * as constants from '@island.is/judicial-system/consts' -import { isTrafficViolationCase } from '@island.is/judicial-system/types' +import { + Feature, + isTrafficViolationCase, +} from '@island.is/judicial-system/types' import { titles } from '@island.is/judicial-system-web/messages' import { + FeatureContext, FormContentContainer, FormContext, FormFooter, @@ -29,6 +33,7 @@ import * as strings from './CaseFiles.strings' const CaseFiles = () => { const { workingCase, isLoadingWorkingCase, caseNotFound } = useContext(FormContext) + const { features } = useContext(FeatureContext) const { formatMessage } = useIntl() const { uploadFiles, @@ -41,7 +46,9 @@ const CaseFiles = () => { workingCase.id, ) - const isTrafficViolationCaseCheck = isTrafficViolationCase(workingCase) + const isTrafficViolationCaseCheck = + features.includes(Feature.MULTIPLE_INDICTMENT_SUBTYPES) || + isTrafficViolationCase(workingCase) const stepIsValid = (isTrafficViolationCaseCheck || diff --git a/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Indictment/Indictment.tsx b/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Indictment/Indictment.tsx index 135bb7a57348..e30f6bd64f9a 100644 --- a/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Indictment/Indictment.tsx +++ b/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Indictment/Indictment.tsx @@ -380,7 +380,7 @@ const Indictment = () => { onChange={handleUpdateIndictmentCount} setWorkingCase={setWorkingCase} updateIndictmentCountState={updateIndictmentCountState} - > + />
diff --git a/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Indictment/IndictmentCount.css.ts b/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Indictment/IndictmentCount.css.ts new file mode 100644 index 000000000000..7671970527a8 --- /dev/null +++ b/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Indictment/IndictmentCount.css.ts @@ -0,0 +1,15 @@ +import { style } from '@vanilla-extract/css' + +import { theme } from '@island.is/island-ui/theme' + +export const indictmentSubtypesContainter = style({ + display: 'flex', + flexWrap: 'wrap', + gap: theme.spacing[1], + marginBottom: theme.spacing[2], +}) + +export const indictmentSubtypesItem = style({ + flex: `1 1 calc(50% - ${theme.spacing[1]}px)`, + whiteSpace: 'nowrap', +}) diff --git a/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Indictment/IndictmentCount.strings.ts b/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Indictment/IndictmentCount.strings.ts index dcea457a39c5..d6b7abe87aff 100644 --- a/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Indictment/IndictmentCount.strings.ts +++ b/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Indictment/IndictmentCount.strings.ts @@ -141,4 +141,9 @@ export const indictmentCount = defineMessages({ 'Telst hĂĄttsemi ĂŸessi varĂ°a viĂ° {articles} umferĂ°arlaga nr. 77/2019.', description: 'NotaĂ°ur sem sjĂĄlfgefinn texti Ă­ heimfĂŠrslu svĂŠĂ°i.', }, + selectIndictmentSubtype: { + id: 'judicial.system.core:indictments_indictment.indictment_count.select_indictment_subtype', + defaultMessage: 'Veldu sakarefni', + description: 'NotaĂ°ur sem titill fyrir "Veldu sakarefni" svĂŠĂ°i.', + }, }) diff --git a/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Indictment/IndictmentCount.tsx b/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Indictment/IndictmentCount.tsx index 995eb38f5d4c..d1c964386e88 100644 --- a/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Indictment/IndictmentCount.tsx +++ b/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Indictment/IndictmentCount.tsx @@ -5,14 +5,20 @@ import { IntlShape, useIntl } from 'react-intl' import { Box, Button, + Checkbox, Icon, Input, Select, Tag, } from '@island.is/island-ui/core' -import { formatDate } from '@island.is/judicial-system/formatters' +import { + capitalize, + formatDate, + indictmentSubtypes, +} from '@island.is/judicial-system/formatters' import { CrimeScene, + IndictmentSubtype, offenseSubstances, Substance, SubstanceMap, @@ -20,6 +26,7 @@ import { import { BlueBox, IndictmentInfo, + SectionHeading, } from '@island.is/judicial-system-web/src/components' import { IndictmentCountOffense } from '@island.is/judicial-system-web/src/graphql/schema' import { @@ -39,6 +46,7 @@ import { Substances as SubstanceChoices } from './Substances/Substances' import { indictmentCount as strings } from './IndictmentCount.strings' import { indictmentCountEnum as enumStrings } from './IndictmentCountEnum.strings' import { indictmentCountSubstanceEnum as substanceStrings } from './IndictmentCountSubstanceEnum.strings' +import * as styles from './IndictmentCount.css' interface Props { indictmentCount: TIndictmentCount @@ -338,6 +346,10 @@ export const IndictmentCount: FC = ({ const [legalArgumentsErrorMessage, setLegalArgumentsErrorMessage] = useState('') + const subtypes = indictmentCount.policeCaseNumber + ? workingCase.indictmentSubtypes[indictmentCount.policeCaseNumber] + : [] + const offensesOptions = useMemo( () => Object.values(IndictmentCountOffense).map((offense) => ({ @@ -400,6 +412,21 @@ export const IndictmentCount: FC = ({ }) } + const handleSubtypeChange = ( + subtype: IndictmentSubtype, + checked: boolean, + ) => { + const currentSubtypes = new Set( + indictmentCount.indictmentCountSubtypes ?? [], + ) + + checked ? currentSubtypes.add(subtype) : currentSubtypes.delete(subtype) + + handleIndictmentCountChanges({ + indictmentCountSubtypes: Array.from(currentSubtypes), + }) + } + return ( {onDelete && ( @@ -449,6 +476,39 @@ export const IndictmentCount: FC = ({ crimeScenes={workingCase.crimeScenes} /> + {subtypes.length > 1 && ( + + +
+ {subtypes.map((subtype: IndictmentSubtype) => ( +
+ { + handleSubtypeChange(subtype, evt.target.checked) + }} + backgroundColor="white" + large + filled + /> +
+ ))} +
+
+ )} { isCaseUpToDate, refreshCase, } = useContext(FormContext) + const { features } = useContext(FeatureContext) const { updateCase, transitionCase, setAndSendCaseToServer } = useCase() const { formatMessage } = useIntl() const { updateDefendant, updateDefendantState } = useDefendants() @@ -73,7 +76,9 @@ const Processing: FC = () => { deleteCivilClaimant, } = useCivilClaimants() const router = useRouter() - const isTrafficViolationCaseCheck = isTrafficViolationCase(workingCase) + const isTrafficViolationCaseCheck = + features.includes(Feature.MULTIPLE_INDICTMENT_SUBTYPES) || + isTrafficViolationCase(workingCase) const [civilClaimantNationalIdUpdate, setCivilClaimantNationalIdUpdate] = useState<{ nationalId: string | null; civilClaimantId: string }>() const [hasCivilClaimantChoice, setHasCivilClaimantChoice] = diff --git a/apps/judicial-system/web/src/utils/hooks/useCaseList/index.tsx b/apps/judicial-system/web/src/utils/hooks/useCaseList/index.tsx index d184a5703fb9..60458c5e195c 100644 --- a/apps/judicial-system/web/src/utils/hooks/useCaseList/index.tsx +++ b/apps/judicial-system/web/src/utils/hooks/useCaseList/index.tsx @@ -10,6 +10,7 @@ import { DEFENDER_ROUTE, } from '@island.is/judicial-system/consts' import { + Feature, isCompletedCase, isCourtOfAppealsUser, isDefenceUser, @@ -23,6 +24,7 @@ import { } from '@island.is/judicial-system/types' import { errors } from '@island.is/judicial-system-web/messages' import { + FeatureContext, FormContext, UserContext, } from '@island.is/judicial-system-web/src/components' @@ -40,6 +42,7 @@ const useCaseList = () => { >([null, false]) const { user, limitedAccess } = useContext(UserContext) const { getCase } = useContext(FormContext) + const { features } = useContext(FeatureContext) const { formatMessage } = useIntl() const { isTransitioningCase, isSendingNotification } = useCase() const router = useRouter() @@ -47,7 +50,9 @@ const useCaseList = () => { const openCase = useCallback( (caseToOpen: Case, openCaseInNewTab?: boolean) => { let routeTo = null - const isTrafficViolation = isTrafficViolationCase(caseToOpen) + const isTrafficViolation = + features.includes(Feature.MULTIPLE_INDICTMENT_SUBTYPES) || + isTrafficViolationCase(caseToOpen) if (isDefenceUser(user)) { if (isRequestCase(caseToOpen.type)) { diff --git a/apps/judicial-system/web/src/utils/hooks/useIndictmentCounts/updateIndictmentCount.graphql b/apps/judicial-system/web/src/utils/hooks/useIndictmentCounts/updateIndictmentCount.graphql index f87f2173bf6f..f8aca1a9268d 100644 --- a/apps/judicial-system/web/src/utils/hooks/useIndictmentCounts/updateIndictmentCount.graphql +++ b/apps/judicial-system/web/src/utils/hooks/useIndictmentCounts/updateIndictmentCount.graphql @@ -11,5 +11,6 @@ mutation UpdateIndictmentCount($input: UpdateIndictmentCountInput!) { lawsBroken incidentDescription legalArguments + indictmentCountSubtypes } } diff --git a/apps/judicial-system/web/src/utils/hooks/useSections/index.ts b/apps/judicial-system/web/src/utils/hooks/useSections/index.ts index 5608d706ab7e..c0bc8bfbb355 100644 --- a/apps/judicial-system/web/src/utils/hooks/useSections/index.ts +++ b/apps/judicial-system/web/src/utils/hooks/useSections/index.ts @@ -1,3 +1,4 @@ +import { useContext } from 'react' import { useIntl } from 'react-intl' import { useRouter } from 'next/router' @@ -7,6 +8,7 @@ import { getAppealResultTextByValue, } from '@island.is/judicial-system/formatters' import { + Feature, isCompletedCase, isCourtOfAppealsUser, isDefenceUser, @@ -18,6 +20,7 @@ import { isTrafficViolationCase, } from '@island.is/judicial-system/types' import { core, sections } from '@island.is/judicial-system-web/messages' +import { FeatureContext } from '@island.is/judicial-system-web/src/components' import { RouteSection } from '@island.is/judicial-system-web/src/components/PageLayout/PageLayout' import { formatCaseResult } from '@island.is/judicial-system-web/src/components/PageLayout/utils' import { @@ -60,6 +63,7 @@ const useSections = ( ) => { const { formatMessage } = useIntl() const router = useRouter() + const { features } = useContext(FeatureContext) const isActive = (pathname: string) => router.pathname.replace(/\/\[\w+\]/g, '') === pathname @@ -402,7 +406,9 @@ const useSections = ( state === CaseState.RECEIVED || state === CaseState.WAITING_FOR_CANCELLATION || router.pathname === `${constants.INDICTMENTS_ADD_FILES_ROUTE}/[id]` - const isTrafficViolation = isTrafficViolationCase(workingCase) + const isTrafficViolation = + features.includes(Feature.MULTIPLE_INDICTMENT_SUBTYPES) || + isTrafficViolationCase(workingCase) return { name: formatMessage(sections.indictmentCaseProsecutorSection.title), diff --git a/charts/judicial-system/values.prod.yaml b/charts/judicial-system/values.prod.yaml index dc74c1e64d61..ffcef752f5ec 100644 --- a/charts/judicial-system/values.prod.yaml +++ b/charts/judicial-system/values.prod.yaml @@ -30,7 +30,7 @@ judicial-system-api: BACKEND_URL: 'http://web-judicial-system-backend' CONTENTFUL_ENVIRONMENT: 'master' CONTENTFUL_HOST: 'cdn.contentful.com' - HIDDEN_FEATURES: '' + HIDDEN_FEATURES: 'MULTIPLE_INDICTMENT_SUBTYPES' IDENTITY_SERVER_ISSUER_URL: 'https://innskra.island.is' LOG_LEVEL: 'info' NODE_OPTIONS: '--max-old-space-size=460 -r dd-trace/init' diff --git a/charts/judicial-system/values.staging.yaml b/charts/judicial-system/values.staging.yaml index 3dacb3f6fb4a..30afa9a56e74 100644 --- a/charts/judicial-system/values.staging.yaml +++ b/charts/judicial-system/values.staging.yaml @@ -30,7 +30,7 @@ judicial-system-api: BACKEND_URL: 'http://web-judicial-system-backend' CONTENTFUL_ENVIRONMENT: 'test' CONTENTFUL_HOST: 'cdn.contentful.com' - HIDDEN_FEATURES: '' + HIDDEN_FEATURES: 'MULTIPLE_INDICTMENT_SUBTYPES' IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.staging01.devland.is' LOG_LEVEL: 'info' NODE_OPTIONS: '--max-old-space-size=460 -r dd-trace/init' diff --git a/charts/services/judicial-system-api/values.prod.yaml b/charts/services/judicial-system-api/values.prod.yaml index 2143dcd8565b..05f04f9d7278 100644 --- a/charts/services/judicial-system-api/values.prod.yaml +++ b/charts/services/judicial-system-api/values.prod.yaml @@ -30,7 +30,7 @@ env: BACKEND_URL: 'http://web-judicial-system-backend' CONTENTFUL_ENVIRONMENT: 'master' CONTENTFUL_HOST: 'cdn.contentful.com' - HIDDEN_FEATURES: '' + HIDDEN_FEATURES: 'MULTIPLE_INDICTMENT_SUBTYPES' IDENTITY_SERVER_ISSUER_URL: 'https://innskra.island.is' LOG_LEVEL: 'info' NODE_OPTIONS: '--max-old-space-size=460 -r dd-trace/init' diff --git a/charts/services/judicial-system-api/values.staging.yaml b/charts/services/judicial-system-api/values.staging.yaml index 3d6002b0a864..7533d12090a6 100644 --- a/charts/services/judicial-system-api/values.staging.yaml +++ b/charts/services/judicial-system-api/values.staging.yaml @@ -30,7 +30,7 @@ env: BACKEND_URL: 'http://web-judicial-system-backend' CONTENTFUL_ENVIRONMENT: 'test' CONTENTFUL_HOST: 'cdn.contentful.com' - HIDDEN_FEATURES: '' + HIDDEN_FEATURES: 'MULTIPLE_INDICTMENT_SUBTYPES' IDENTITY_SERVER_ISSUER_URL: 'https://identity-server.staging01.devland.is' LOG_LEVEL: 'info' NODE_OPTIONS: '--max-old-space-size=460 -r dd-trace/init' diff --git a/libs/judicial-system/types/src/lib/feature.ts b/libs/judicial-system/types/src/lib/feature.ts index 742e56c2f145..dd8f5e437a8f 100644 --- a/libs/judicial-system/types/src/lib/feature.ts +++ b/libs/judicial-system/types/src/lib/feature.ts @@ -1,3 +1,4 @@ export enum Feature { NONE = 'NONE', // must be at least one + MULTIPLE_INDICTMENT_SUBTYPES = 'MULTIPLE_INDICTMENT_SUBTYPES', } From aa1e2542f44afd8ce63f1fa4ffedb21c231bd5ba Mon Sep 17 00:00:00 2001 From: unakb Date: Mon, 9 Dec 2024 19:04:51 +0000 Subject: [PATCH 70/85] feat(j-s): Only registered judge can complete indictment case with ruling (#17052) * feat(j-s): Only registered judge can complete indictment case with ruling * chore: nx format:write update dirty files * feat(j-s): Guards for complex transition rules * Update caseTransition.guard.ts * Update rolesRules.ts * Update transitionGuards.spec.ts --------- Co-authored-by: andes-it Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../src/app/modules/case/case.controller.ts | 9 +- .../case/guards/caseTransition.guard.ts | 37 +++++++ .../case/guards/caseTransitionRules.ts | 41 ++++++++ .../src/app/modules/case/guards/rolesRules.ts | 6 +- .../guards/test/caseTransitionGuard.spec.ts | 98 +++++++++++++++++++ .../caseController/transitionGuards.spec.ts | 16 ++- .../Indictments/Summary/Summary.strings.ts | 6 ++ .../Court/Indictments/Summary/Summary.tsx | 15 +++ 8 files changed, 222 insertions(+), 6 deletions(-) create mode 100644 apps/judicial-system/backend/src/app/modules/case/guards/caseTransition.guard.ts create mode 100644 apps/judicial-system/backend/src/app/modules/case/guards/caseTransitionRules.ts create mode 100644 apps/judicial-system/backend/src/app/modules/case/guards/test/caseTransitionGuard.spec.ts diff --git a/apps/judicial-system/backend/src/app/modules/case/case.controller.ts b/apps/judicial-system/backend/src/app/modules/case/case.controller.ts index 9728dc420508..3f11857ac9c5 100644 --- a/apps/judicial-system/backend/src/app/modules/case/case.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/case/case.controller.ts @@ -70,6 +70,7 @@ import { CurrentCase } from './guards/case.decorator' import { CaseCompletedGuard } from './guards/caseCompleted.guard' import { CaseExistsGuard } from './guards/caseExists.guard' import { CaseReadGuard } from './guards/caseRead.guard' +import { CaseTransitionGuard } from './guards/caseTransition.guard' import { CaseTypeGuard } from './guards/caseType.guard' import { CaseWriteGuard } from './guards/caseWrite.guard' import { MergedCaseExistsGuard } from './guards/mergedCaseExists.guard' @@ -273,7 +274,13 @@ export class CaseController { return this.caseService.update(theCase, update, user) as Promise // Never returns undefined } - @UseGuards(JwtAuthGuard, CaseExistsGuard, RolesGuard, CaseWriteGuard) + @UseGuards( + JwtAuthGuard, + CaseExistsGuard, + RolesGuard, + CaseWriteGuard, + CaseTransitionGuard, + ) @RolesRules( prosecutorTransitionRule, prosecutorRepresentativeTransitionRule, diff --git a/apps/judicial-system/backend/src/app/modules/case/guards/caseTransition.guard.ts b/apps/judicial-system/backend/src/app/modules/case/guards/caseTransition.guard.ts new file mode 100644 index 000000000000..80898c0fc5f2 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/case/guards/caseTransition.guard.ts @@ -0,0 +1,37 @@ +import { + CanActivate, + ExecutionContext, + ForbiddenException, + Injectable, + InternalServerErrorException, +} from '@nestjs/common' + +import { User } from '@island.is/judicial-system/types' + +import { getTransitionRule } from './caseTransitionRules' + +@Injectable() +// Used for more complex cases than just whether a role can perform a +// transition overall, which is handled in the transition roles rules +export class CaseTransitionGuard implements CanActivate { + canActivate(context: ExecutionContext): boolean { + const request = context.switchToHttp().getRequest() + + const { transition } = request.body + const theCase = request.case + const user: User = request.user + + // This shouldn't happen + if (!theCase || !user) { + throw new InternalServerErrorException('Missing case or user') + } + + const transitionRule = getTransitionRule(transition) + + if (!transitionRule(theCase, user)) { + throw new ForbiddenException('Forbidden transition') + } + + return true + } +} diff --git a/apps/judicial-system/backend/src/app/modules/case/guards/caseTransitionRules.ts b/apps/judicial-system/backend/src/app/modules/case/guards/caseTransitionRules.ts new file mode 100644 index 000000000000..605e877e3791 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/case/guards/caseTransitionRules.ts @@ -0,0 +1,41 @@ +import { ForbiddenException } from '@nestjs/common' + +import { + CaseIndictmentRulingDecision, + CaseTransition, + CaseType, + User, +} from '@island.is/judicial-system/types' + +import { Case } from '..' + +type TransitionRule = (theCase: Case, user: User) => boolean + +const defaultTransitionRule: TransitionRule = () => true + +const completeTransitionRule: TransitionRule = (theCase, user) => { + if (theCase.type !== CaseType.INDICTMENT) { + throw new ForbiddenException( + `Forbidden transition for ${theCase.type} cases`, + ) + } + + if ( + theCase.indictmentRulingDecision === CaseIndictmentRulingDecision.RULING || + theCase.indictmentRulingDecision === CaseIndictmentRulingDecision.DISMISSAL + ) { + return user.id === theCase.judgeId + } + + return true +} + +const transitionRuleMap: Partial> = { + [CaseTransition.COMPLETE]: completeTransitionRule, +} + +export const getTransitionRule = ( + transition: CaseTransition, +): TransitionRule => { + return transitionRuleMap[transition] || defaultTransitionRule +} diff --git a/apps/judicial-system/backend/src/app/modules/case/guards/rolesRules.ts b/apps/judicial-system/backend/src/app/modules/case/guards/rolesRules.ts index 2b95c2f46a57..1e4b10ef019f 100644 --- a/apps/judicial-system/backend/src/app/modules/case/guards/rolesRules.ts +++ b/apps/judicial-system/backend/src/app/modules/case/guards/rolesRules.ts @@ -211,7 +211,7 @@ export const prosecutorTransitionRule: RolesRule = { const dto: TransitionCaseDto = request.body const theCase: Case = request.case - // Deny if something is missing - shuould never happen + // Deny if something is missing - should never happen if (!user || !dto || !theCase) { return false } @@ -258,7 +258,7 @@ export const defenderTransitionRule: RolesRule = { const dto: TransitionCaseDto = request.body const theCase: Case = request.case - // Deny if something is missing - shuould never happen + // Deny if something is missing - should never happen if (!dto || !theCase) { return false } @@ -393,7 +393,7 @@ export const districtCourtJudgeSignRulingRule: RolesRule = { const user: User = request.user const theCase: Case = request.case - // Deny if something is missing - shuould never happen + // Deny if something is missing - should never happen if (!user || !theCase) { return false } diff --git a/apps/judicial-system/backend/src/app/modules/case/guards/test/caseTransitionGuard.spec.ts b/apps/judicial-system/backend/src/app/modules/case/guards/test/caseTransitionGuard.spec.ts new file mode 100644 index 000000000000..6c88ea5e5053 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/case/guards/test/caseTransitionGuard.spec.ts @@ -0,0 +1,98 @@ +import { + ExecutionContext, + ForbiddenException, + InternalServerErrorException, +} from '@nestjs/common' + +import { + CaseIndictmentRulingDecision, + CaseTransition, + CaseType, +} from '@island.is/judicial-system/types' + +import { CaseTransitionGuard } from '../caseTransition.guard' + +describe('CaseTransitionGuard', () => { + let guard: CaseTransitionGuard + // eslint-disable-next-line @typescript-eslint/no-unused-vars + let mockRequest: jest.Mock + + beforeEach(() => { + guard = new CaseTransitionGuard() + mockRequest = jest.fn() + }) + + const mockExecutionContext = (requestMock: unknown): ExecutionContext => + ({ + switchToHttp: () => ({ + getRequest: () => requestMock, + }), + } as unknown as ExecutionContext) + + const createMockCase = ( + type: CaseType, + rulingDecision: unknown, + judgeId: string, + ) => ({ + type, + indictmentRulingDecision: rulingDecision, + judgeId, + }) + + it('should activate when the judge is the assigned judge', () => { + const mockCase = createMockCase( + CaseType.INDICTMENT, + CaseIndictmentRulingDecision.RULING, + 'judgeId', + ) + const context = mockExecutionContext({ + body: { transition: CaseTransition.COMPLETE }, + case: mockCase, + user: { id: 'judgeId' }, + }) + + const result = guard.canActivate(context) + + expect(result).toBe(true) + }) + + it('should not activate when the user is not the assigned judge', () => { + const mockCase = createMockCase( + CaseType.INDICTMENT, + CaseIndictmentRulingDecision.RULING, + 'judgeId', + ) + const context = mockExecutionContext({ + body: { transition: CaseTransition.COMPLETE }, + case: mockCase, + user: { id: 'differentJudgeId' }, + }) + + expect(() => guard.canActivate(context)).toThrow(ForbiddenException) + }) + + it('should activate using the default rule for transitions not in the rule map', () => { + const mockCase = createMockCase(CaseType.CUSTODY, null, 'someId') + const context = mockExecutionContext({ + body: { transition: CaseTransition.SUBMIT }, + case: mockCase, + user: { id: 'someId' }, + }) + + const result = guard.canActivate(context) + + expect(result).toBe(true) + }) + + it('should throw InternalServerErrorException when case or user is missing', () => { + const context = mockExecutionContext({ + body: { transition: CaseTransition.COMPLETE }, + case: null, + user: null, + }) + + expect(() => guard.canActivate(context)).toThrow( + InternalServerErrorException, + ) + }) +}) diff --git a/apps/judicial-system/backend/src/app/modules/case/test/caseController/transitionGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/caseController/transitionGuards.spec.ts index 2e7775081e91..1e275969093b 100644 --- a/apps/judicial-system/backend/src/app/modules/case/test/caseController/transitionGuards.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/test/caseController/transitionGuards.spec.ts @@ -4,6 +4,7 @@ import { JwtAuthGuard, RolesGuard } from '@island.is/judicial-system/auth' import { CaseController } from '../../case.controller' import { CaseExistsGuard } from '../../guards/caseExists.guard' +import { CaseTransitionGuard } from '../../guards/caseTransition.guard' import { CaseWriteGuard } from '../../guards/caseWrite.guard' describe('CaseController - Transition guards', () => { @@ -17,8 +18,8 @@ describe('CaseController - Transition guards', () => { ) }) - it('should have four guards', () => { - expect(guards).toHaveLength(4) + it('should have five guards', () => { + expect(guards).toHaveLength(5) }) describe('JwtAuthGuard', () => { @@ -68,4 +69,15 @@ describe('CaseController - Transition guards', () => { expect(guard).toBeInstanceOf(CaseWriteGuard) }) }) + describe('CaseTransitionGuard', () => { + let guard: CanActivate + + beforeEach(() => { + guard = new guards[4]() + }) + + it('should have CaseTransitionGuard as guard 5', () => { + expect(guard).toBeInstanceOf(CaseTransitionGuard) + }) + }) }) diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Summary/Summary.strings.ts b/apps/judicial-system/web/src/routes/Court/Indictments/Summary/Summary.strings.ts index e5c50b59327c..c9c5bdf3cdc2 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Summary/Summary.strings.ts +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Summary/Summary.strings.ts @@ -52,4 +52,10 @@ export const strings = defineMessages({ description: 'NotaĂ°ur sem texti ĂĄ aukahnapp Ă­ staĂ°festingarglugga um hvort eigi aĂ° ljĂșka mĂĄli.', }, + onlyAssignedJudgeCanComplete: { + id: 'judicial.system.core:indictments.summary.only_assigned_judge_can_complete', + defaultMessage: 'Einungis skrĂĄĂ°ur dĂłmari getur lokiĂ° mĂĄlinu', + description: + 'NotaĂ°ur sem texti Ă­ staĂ° "ĂĄfram" takkans ĂĄ samantektarskjĂĄ ĂĄkĂŠru ĂŸegar "ĂĄfram" takkinn er falinn', + }, }) diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Summary/Summary.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Summary/Summary.tsx index 51d109336b5e..2f14057d02bf 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Summary/Summary.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Summary/Summary.tsx @@ -19,6 +19,7 @@ import { PageTitle, RenderFiles, SectionHeading, + UserContext, } from '@island.is/judicial-system-web/src/components' import { Defendants, @@ -43,6 +44,7 @@ const Summary: FC = () => { useContext(FormContext) const { transitionCase, isTransitioningCase } = useCase() const [modalVisible, setModalVisible] = useState<'CONFIRM_INDICTMENT'>() + const { user } = useContext(UserContext) const { onOpen } = useFileList({ caseId: workingCase.id, @@ -90,6 +92,13 @@ const Summary: FC = () => { workingCase.indictmentRulingDecision, ) + const canUserCompleteCase = + (workingCase.indictmentRulingDecision !== + CaseIndictmentRulingDecision.RULING && + workingCase.indictmentRulingDecision !== + CaseIndictmentRulingDecision.DISMISSAL) || + workingCase.judge?.id === user?.id + return ( { nextButtonIcon="checkmark" nextButtonText={formatMessage(strings.nextButtonText)} onNextButtonClick={() => setModalVisible('CONFIRM_INDICTMENT')} + hideNextButton={!canUserCompleteCase} + infoBoxText={ + canUserCompleteCase + ? '' + : formatMessage(strings.onlyAssignedJudgeCanComplete) + } /> {modalVisible === 'CONFIRM_INDICTMENT' && ( From 2036feb9967ca1a4b2f4a23b36086a1fe5bca656 Mon Sep 17 00:00:00 2001 From: albinagu <47886428+albinagu@users.noreply.github.com> Date: Mon, 9 Dec 2024 19:33:35 +0000 Subject: [PATCH 71/85] fix(inheritance-report): deceased share texts (#17182) * fix(inheritance-report): deceased share texts * tweaks - current.enabled * bakka --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../src/fields/CalculateShare/index.tsx | 12 ++++++------ .../src/fields/ReportFieldsRepeater/index.tsx | 4 +++- .../src/forms/sections/deceased.ts | 2 +- .../templates/inheritance-report/src/lib/messages.ts | 7 ++++++- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/libs/application/templates/inheritance-report/src/fields/CalculateShare/index.tsx b/libs/application/templates/inheritance-report/src/fields/CalculateShare/index.tsx index 3a2f0e735eff..c5737912c549 100644 --- a/libs/application/templates/inheritance-report/src/fields/CalculateShare/index.tsx +++ b/libs/application/templates/inheritance-report/src/fields/CalculateShare/index.tsx @@ -166,9 +166,9 @@ export const CalculateShare: FC> = ({ } }) - const inventory: CalcShared = ( - [(answers.assets as unknown as EstateAssets)?.inventory] ?? [] - ).map((item) => { + const inventory: CalcShared = [ + (answers.assets as unknown as EstateAssets)?.inventory ?? [], + ].map((item) => { const value = valueToNumber(item.value) const deceasedShare = valueToNumber(item.deceasedShare) const { shareValue, deceasedShareValue } = getShareValue( @@ -185,9 +185,9 @@ export const CalculateShare: FC> = ({ } }) - const money: CalcShared = ( - [(answers.assets as unknown as EstateAssets)?.money] ?? [] - ).map((item) => { + const money: CalcShared = [ + (answers.assets as unknown as EstateAssets)?.money ?? [], + ].map((item) => { const value = valueToNumber(item.value) const deceasedShare = valueToNumber(item.deceasedShare) const { shareValue, deceasedShareValue } = getShareValue( diff --git a/libs/application/templates/inheritance-report/src/fields/ReportFieldsRepeater/index.tsx b/libs/application/templates/inheritance-report/src/fields/ReportFieldsRepeater/index.tsx index 7529b650832a..08731a1c43ec 100644 --- a/libs/application/templates/inheritance-report/src/fields/ReportFieldsRepeater/index.tsx +++ b/libs/application/templates/inheritance-report/src/fields/ReportFieldsRepeater/index.tsx @@ -85,7 +85,9 @@ export const ReportFieldsRepeater: FC< } const total = values.reduce((acc: number, current: any, index: number) => { - const sumField2 = valueToNumber(current[props?.sumField2], ',') + const sumField2 = current.enabled + ? valueToNumber(current[props?.sumField2], ',') + : 0 let currentValue = valueToNumber( current.enabled ? current[props?.sumField] : 0, ',', diff --git a/libs/application/templates/inheritance-report/src/forms/sections/deceased.ts b/libs/application/templates/inheritance-report/src/forms/sections/deceased.ts index 501dfc9f920c..8cac5ea189ea 100644 --- a/libs/application/templates/inheritance-report/src/forms/sections/deceased.ts +++ b/libs/application/templates/inheritance-report/src/forms/sections/deceased.ts @@ -118,7 +118,7 @@ export const deceased = buildSection({ { name: 'customShare.customSpouseSharePercentage', placeholder: '50%', - label: m.deceasedShare.defaultMessage, + label: m.deceasedSharePercentage.defaultMessage, }, ), buildDescriptionField({ diff --git a/libs/application/templates/inheritance-report/src/lib/messages.ts b/libs/application/templates/inheritance-report/src/lib/messages.ts index 16822da0fc70..84f67d71a06a 100644 --- a/libs/application/templates/inheritance-report/src/lib/messages.ts +++ b/libs/application/templates/inheritance-report/src/lib/messages.ts @@ -1256,9 +1256,14 @@ export const m = defineMessages({ defaultMessage: 'SĂ©reign', description: '', }, + deceasedSharePercentage: { + id: 'ir.application:deceasedSharePercentage', + defaultMessage: 'BĂșshluti maka', + description: '', + }, deceasedShare: { id: 'ir.application:deceasedShare', - defaultMessage: 'BĂșshluti maka', + defaultMessage: 'Hlutfall sĂ©reignar', description: '', }, spousesShareDescription: { From 5f9b084a2c00062c0c8c0743c768314f68fc9bf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93lafur=20Bj=C3=B6rn=20Magn=C3=BAsson?= Date: Mon, 9 Dec 2024 20:03:39 +0000 Subject: [PATCH 72/85] fix(application-system-api-worker): Get template error catch (#17186) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../application-lifecycle.service.ts | 73 +++++++++++-------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/apps/application-system/api/src/app/modules/application/lifecycle/application-lifecycle.service.ts b/apps/application-system/api/src/app/modules/application/lifecycle/application-lifecycle.service.ts index 332c163b2555..ab2dbab36e7d 100644 --- a/apps/application-system/api/src/app/modules/application/lifecycle/application-lifecycle.service.ts +++ b/apps/application-system/api/src/app/modules/application/lifecycle/application-lifecycle.service.ts @@ -151,39 +151,52 @@ export class ApplicationLifeCycleService { private async preparePrunedNotification( application: PruningApplication, ): Promise { - const template = await getApplicationTemplateByTypeId(application.typeId) - const stateConfig = template.stateMachineConfig.states[application.state] - const lifeCycle = stateConfig.meta?.lifecycle - if (lifeCycle && lifeCycle.shouldBePruned && lifeCycle.pruneMessage) { - try { - const pruneMessage = - typeof lifeCycle.pruneMessage === 'function' - ? lifeCycle.pruneMessage(application as ApplicationWithAttachments) - : lifeCycle.pruneMessage - const notification = { - recipient: application.applicant, - templateId: pruneMessage.notificationTemplateId, - args: [ - { - key: 'externalBody', - value: pruneMessage.externalBody || '', - }, - { - key: 'internalBody', - value: pruneMessage.internalBody || '', - }, - ], - } - return notification - } catch (error) { - this.logger.error( - `Failed to prepare pruning notification for application ${application.id}`, - error, - ) + try { + const template = await getApplicationTemplateByTypeId(application.typeId) + if (!template) { return null } + const stateConfig = template.stateMachineConfig.states[application.state] + const lifeCycle = stateConfig.meta?.lifecycle + if (lifeCycle && lifeCycle.shouldBePruned && lifeCycle.pruneMessage) { + try { + const pruneMessage = + typeof lifeCycle.pruneMessage === 'function' + ? lifeCycle.pruneMessage( + application as ApplicationWithAttachments, + ) + : lifeCycle.pruneMessage + const notification = { + recipient: application.applicant, + templateId: pruneMessage.notificationTemplateId, + args: [ + { + key: 'externalBody', + value: pruneMessage.externalBody || '', + }, + { + key: 'internalBody', + value: pruneMessage.internalBody || '', + }, + ], + } + return notification + } catch (error) { + this.logger.error( + `Failed to prepare pruning notification for application ${application.id}`, + error, + ) + return null + } + } + return null + } catch (error) { + this.logger.error( + `Failed to get application template for application typeId ${application.typeId}`, + error, + ) + return null } - return null } private async sendPrunedNotification( From 02d4080788199f29e3eca8c25aa6caaeb21a7d19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Mon, 9 Dec 2024 21:08:03 +0000 Subject: [PATCH 73/85] feat(j-s): Add modal when revoking send to prison admin (#17110) * Add modal when revoking send to prison admin * Update CF ids * Fix lint --- .../web/messages/Core/index.ts | 5 ++++ .../BlueBoxWithDate.strings.ts | 16 ++++++++++++ .../BlueBoxWithIcon/BlueBoxWithDate.tsx | 26 ++++++++++++++++--- .../SendToPrisonAdmin.strings.ts | 2 +- .../web/src/utils/hooks/useCaseList/index.tsx | 2 +- 5 files changed, 46 insertions(+), 5 deletions(-) diff --git a/apps/judicial-system/web/messages/Core/index.ts b/apps/judicial-system/web/messages/Core/index.ts index f783a0114759..efb61f12781d 100644 --- a/apps/judicial-system/web/messages/Core/index.ts +++ b/apps/judicial-system/web/messages/Core/index.ts @@ -209,6 +209,11 @@ export const core = defineMessages({ defaultMessage: 'Halda ĂĄfram', description: 'NotaĂ° fyrir "Halda ĂĄfram" takka Ă­ öllum flĂŠĂ°um.', }, + cancel: { + id: 'judicial.system.core:cancel', + defaultMessage: 'HĂŠtta viĂ°', + description: 'NotaĂ° fyrir "HĂŠtta viĂ°" takka Ă­ öllum flĂŠĂ°um.', + }, createCase: { id: 'judicial.system.core:create_case', defaultMessage: 'Stofna mĂĄl', diff --git a/apps/judicial-system/web/src/components/BlueBoxWithIcon/BlueBoxWithDate.strings.ts b/apps/judicial-system/web/src/components/BlueBoxWithIcon/BlueBoxWithDate.strings.ts index a3f57cbebd4c..282e98b4bb14 100644 --- a/apps/judicial-system/web/src/components/BlueBoxWithIcon/BlueBoxWithDate.strings.ts +++ b/apps/judicial-system/web/src/components/BlueBoxWithIcon/BlueBoxWithDate.strings.ts @@ -83,4 +83,20 @@ export const strings = defineMessages({ description: 'NotaĂ°ur sem titill Ă­ svĂŠĂ°i ĂŸar sem kĂŠrufrestur viĂ°urlagaĂĄkvörĂ°unar er tekinn fram', }, + revokeSendToPrisonAdminModalTitle: { + id: 'judicial.system.core:blue_box_with_date.revoke_send_to_prison_admin_modal_title', + defaultMessage: 'Afturkalla Ășr fullnustu', + description: 'NotaĂ°ur sem titill Ă­ "Afturkalla Ășr fullnustu" modal glugga.', + }, + revokeSendToPrisonAdminModalText: { + id: 'judicial.system.core:blue_box_with_date.revoke_send_to_prison_admin_modal_text', + defaultMessage: + 'MĂĄl {courtCaseNumber} verĂ°ur afturkallaĂ°.\nÁkĂŠrĂ°i: {defendant}.', + description: 'NotaĂ°ur sem texti Ă­ "Afturkalla Ășr fullnustu" modal glugga.', + }, + revoke: { + id: 'judicial.system.core:blue_box_with_date.revoke', + defaultMessage: 'Afturkalla', + description: 'NotaĂ°ur sem texti fyrir aĂ°gerĂ°ina aĂ° afturkalla mĂĄl', + }, }) diff --git a/apps/judicial-system/web/src/components/BlueBoxWithIcon/BlueBoxWithDate.tsx b/apps/judicial-system/web/src/components/BlueBoxWithIcon/BlueBoxWithDate.tsx index 323fe5cf4469..24e179704afe 100644 --- a/apps/judicial-system/web/src/components/BlueBoxWithIcon/BlueBoxWithDate.tsx +++ b/apps/judicial-system/web/src/components/BlueBoxWithIcon/BlueBoxWithDate.tsx @@ -15,7 +15,7 @@ import { import { PUBLIC_PROSECUTOR_STAFF_INDICTMENT_SEND_TO_PRISON_ADMIN_ROUTE } from '@island.is/judicial-system/consts' import { formatDate } from '@island.is/judicial-system/formatters' import { VERDICT_APPEAL_WINDOW_DAYS } from '@island.is/judicial-system/types' -import { errors } from '@island.is/judicial-system-web/messages' +import { core, errors } from '@island.is/judicial-system-web/messages' import { CaseIndictmentRulingDecision, @@ -26,6 +26,7 @@ import { formatDateForServer, useDefendants } from '../../utils/hooks' import DateTime from '../DateTime/DateTime' import { FormContext } from '../FormProvider/FormProvider' import { getAppealExpirationInfo } from '../InfoCard/DefendantInfo/DefendantInfo' +import Modal from '../Modal/Modal' import SectionHeading from '../SectionHeading/SectionHeading' import { strings } from './BlueBoxWithDate.strings' import * as styles from './BlueBoxWithIcon.css' @@ -35,6 +36,8 @@ interface Props { icon?: IconMapIcon } +type VisibleModal = 'REVOKE_SEND_TO_PRISON_ADMIN' + const BlueBoxWithDate: FC = (props) => { const { defendant, icon } = props const { formatMessage } = useIntl() @@ -47,7 +50,8 @@ const BlueBoxWithDate: FC = (props) => { }) const [triggerAnimation, setTriggerAnimation] = useState(false) const [triggerAnimation2, setTriggerAnimation2] = useState(false) - const { setAndSendDefendantToServer } = useDefendants() + const [modalVisible, setModalVisible] = useState() + const { setAndSendDefendantToServer, isUpdatingDefendant } = useDefendants() const { workingCase, setWorkingCase } = useContext(FormContext) const router = useRouter() @@ -111,6 +115,8 @@ const BlueBoxWithDate: FC = (props) => { }, setWorkingCase, ) + + setModalVisible(undefined) } const appealExpirationInfo = useMemo(() => { @@ -335,7 +341,7 @@ const BlueBoxWithDate: FC = (props) => { {defendant.isSentToPrisonAdmin ? ( )} + {modalVisible === 'REVOKE_SEND_TO_PRISON_ADMIN' && ( + setModalVisible(undefined)} + /> + )} ) } diff --git a/apps/judicial-system/web/src/routes/PublicProsecutor/Indictments/SendToPrisonAdmin/SendToPrisonAdmin.strings.ts b/apps/judicial-system/web/src/routes/PublicProsecutor/Indictments/SendToPrisonAdmin/SendToPrisonAdmin.strings.ts index 6a513f738833..44201a0341f3 100644 --- a/apps/judicial-system/web/src/routes/PublicProsecutor/Indictments/SendToPrisonAdmin/SendToPrisonAdmin.strings.ts +++ b/apps/judicial-system/web/src/routes/PublicProsecutor/Indictments/SendToPrisonAdmin/SendToPrisonAdmin.strings.ts @@ -29,7 +29,7 @@ export const strings = defineMessages({ 'NotaĂ°ur sem titill ĂĄ tilkynningarglugga um aĂ° senda til fullnustu.', }, modalText: { - id: 'judicial.system.core:send_to_prison_admin.modal_text', + id: 'judicial.system.core:send_to_prison_admin.modal_text_v1', defaultMessage: 'MĂĄl {courtCaseNumber} verĂ°ur sent til FangelsismĂĄlastofnunar til fullnustu.\nÁkĂŠrĂ°i: {defendant}.', description: diff --git a/apps/judicial-system/web/src/utils/hooks/useCaseList/index.tsx b/apps/judicial-system/web/src/utils/hooks/useCaseList/index.tsx index 60458c5e195c..6629594f8d5f 100644 --- a/apps/judicial-system/web/src/utils/hooks/useCaseList/index.tsx +++ b/apps/judicial-system/web/src/utils/hooks/useCaseList/index.tsx @@ -142,7 +142,7 @@ const useCaseList = () => { router.push(`${routeTo}/${caseToOpen.id}`) } }, - [router, user], + [router, user, features], ) const handleOpenCase = useCallback( From 93800d2e08a31f2bb3b7fbe949d1901b54e95448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Mon, 9 Dec 2024 21:15:43 +0000 Subject: [PATCH 74/85] chore(j-s): Switch indictment confirmation request (#17153) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../Prosecutor/Indictments/Overview/Overview.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Overview/Overview.tsx b/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Overview/Overview.tsx index 2640b512c74d..a7bbf3b8cfa5 100644 --- a/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Overview/Overview.tsx +++ b/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Overview/Overview.tsx @@ -280,20 +280,20 @@ const Overview: FC = () => { setIndictmentConfirmationDecision('confirm')} + label={formatMessage(strings.denyIndictment)} + checked={indictmentConfirmationDecision === 'deny'} + onChange={() => setIndictmentConfirmationDecision('deny')} /> setIndictmentConfirmationDecision('deny')} + label={formatMessage(strings.confirmIndictment)} + checked={indictmentConfirmationDecision === 'confirm'} + onChange={() => setIndictmentConfirmationDecision('confirm')} />
From a043d4cd882da3736e610157bcea72b2380fce0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3nas=20G=2E=20Sigur=C3=B0sson?= Date: Mon, 9 Dec 2024 21:32:16 +0000 Subject: [PATCH 75/85] chore(inao-cem): Refactor out custom components (#17161) * chore: start of custom refactor * chore: remove custom components * chore: refactor out custom components * chore: remove console.log * fix: better id names for descriptions * chore: remove dev condition * chore: added text to readme * fix: typo * fix: wrong id on description * chore: use getValueVia string in the overview component * fix: message key * chore: use foreash instead of map * chore: rename and refactor misleading function name * chore: safer handling of conversion to number * fix: typo * chore: make helper function logic better --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../financial-statement-cemetery/README.md | 4 +- .../src/dataProviders/index.ts | 2 +- .../src/fields/CemeteryEquities/index.tsx | 326 ------------------ .../src/fields/CemeteryIncomeLimit/index.tsx | 4 +- .../CemeteryOperation/CemeteryExpenses.tsx | 168 --------- .../CemeteryOperation/CemeteryIncome.tsx | 147 -------- .../src/fields/CemeteryOperation/index.tsx | 95 ----- .../src/fields/CemeteryOverview/BottomBar.tsx | 37 -- .../src/fields/CemeteryOverview/index.tsx | 286 +++++++-------- .../src/fields/KeyNumbers/index.tsx | 34 -- .../src/fields/KeyNumbersCapital/index.tsx | 94 ----- .../src/fields/OperatingYear/index.tsx | 84 ----- .../src/fields/OperatingYear/styles.css.ts | 5 - .../src/fields/PowerOfAttorney/index.tsx | 2 - .../src/fields/Success/index.tsx | 73 ---- .../src/fields/index.ts | 6 +- .../capitalNumberSubSection.ts | 29 +- .../equityAndLiabilitySubSection.ts | 147 +++++++- .../opperatingCostSubSection.ts | 135 +++++++- .../clientInfoSection/index.ts | 24 +- .../conclusionSection/conclusionSection.ts | 19 + .../src/forms/applicationForm/index.ts | 4 +- .../applicationForm/overviewSection/index.ts | 48 ++- .../overviewSection/oveviewMultiField.ts | 16 - .../src/forms/done/conclusionSection.ts | 24 -- .../src/forms/done/index.ts | 4 +- .../src/forms/notAllowed/index.ts | 22 +- .../src/forms/notAllowed/notAllowedSection.ts | 17 - .../src/forms/prerequisites/index.ts | 62 +++- .../prerequisites/prerequsitesSection.ts | 56 --- .../src/lib/dataSchema.ts | 243 +++++++------ .../lib/financialStatementCemeteryTemplate.ts | 4 +- .../src/lib/messages.ts | 8 +- .../src/types/types.ts | 10 + .../src/utils/constants.ts | 10 +- .../src/utils/helpers.ts | 299 ++++++++++++++-- .../tsconfig.json | 9 +- .../tsconfig.lib.json | 14 +- .../src/dataProviders/index.ts | 2 +- .../prerequisites/prerequisitesSection.ts | 4 +- ...financial-statement-individual-election.ts | 4 +- .../README.md | 4 +- .../src/dataProviders/index.ts | 2 +- .../forms/prerequsites/prerequsitesSection.ts | 4 +- ...inancialStatementPoliticalPartyTemplate.ts | 6 +- 45 files changed, 1052 insertions(+), 1545 deletions(-) delete mode 100644 libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryEquities/index.tsx delete mode 100644 libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryOperation/CemeteryExpenses.tsx delete mode 100644 libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryOperation/CemeteryIncome.tsx delete mode 100644 libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryOperation/index.tsx delete mode 100644 libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryOverview/BottomBar.tsx delete mode 100644 libs/application/templates/inao/financial-statement-cemetery/src/fields/KeyNumbers/index.tsx delete mode 100644 libs/application/templates/inao/financial-statement-cemetery/src/fields/KeyNumbersCapital/index.tsx delete mode 100644 libs/application/templates/inao/financial-statement-cemetery/src/fields/OperatingYear/index.tsx delete mode 100644 libs/application/templates/inao/financial-statement-cemetery/src/fields/OperatingYear/styles.css.ts delete mode 100644 libs/application/templates/inao/financial-statement-cemetery/src/fields/Success/index.tsx create mode 100644 libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/conclusionSection/conclusionSection.ts delete mode 100644 libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/overviewSection/oveviewMultiField.ts delete mode 100644 libs/application/templates/inao/financial-statement-cemetery/src/forms/done/conclusionSection.ts delete mode 100644 libs/application/templates/inao/financial-statement-cemetery/src/forms/notAllowed/notAllowedSection.ts delete mode 100644 libs/application/templates/inao/financial-statement-cemetery/src/forms/prerequisites/prerequsitesSection.ts diff --git a/libs/application/templates/inao/financial-statement-cemetery/README.md b/libs/application/templates/inao/financial-statement-cemetery/README.md index 759b46c0bb58..d8c8a06c518e 100644 --- a/libs/application/templates/inao/financial-statement-cemetery/README.md +++ b/libs/application/templates/inao/financial-statement-cemetery/README.md @@ -1,6 +1,8 @@ # financial-statement-cemetery -This application turns in financial statements for cemeteries to the The Icelandic National Audit Office (Ríkisendurskoðun, inao for short). +This application turns in financial statements for cemeteries to the The Icelandic National Audit Office (Ríkisendurskoðun, INAO for short). Cemeteries are required to turn in their financial statements before the 1. of June each year. + +To test this application in the dev environment, you can log in as Gervimaður FÊreyjar (010-2399) and swap to the procure of Fjårfestingafélagið Blåmi. Blåmi is registered as a cemetery with the INAO dev service and should be able to enter and go through the application. ## Running unit tests diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/dataProviders/index.ts b/libs/application/templates/inao/financial-statement-cemetery/src/dataProviders/index.ts index 91d817999f92..dd52c79ad7f2 100644 --- a/libs/application/templates/inao/financial-statement-cemetery/src/dataProviders/index.ts +++ b/libs/application/templates/inao/financial-statement-cemetery/src/dataProviders/index.ts @@ -3,7 +3,7 @@ import { UserProfileApi } from '@island.is/application/types' export { NationalRegistryUserApi, - IdentityApi as IndentityApiProvider, + IdentityApi as IdentityApiProvider, } from '@island.is/application/types' export const CurrentUserTypeProvider = defineTemplateApi({ diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryEquities/index.tsx b/libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryEquities/index.tsx deleted file mode 100644 index e16606e47af3..000000000000 --- a/libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryEquities/index.tsx +++ /dev/null @@ -1,326 +0,0 @@ -import { useState, useEffect } from 'react' -import { - AlertBanner, - Box, - GridColumn, - GridContainer, - GridRow, - Text, -} from '@island.is/island-ui/core' -import debounce from 'lodash/debounce' -import { useFormContext } from 'react-hook-form' -import { useLocale } from '@island.is/localization' -import { FieldBaseProps } from '@island.is/application/types' -import { InputController } from '@island.is/shared/form-fields' -import { m } from '../../lib/messages' -import { Total } from '../KeyNumbers' -import { getErrorViaPath, getValueViaPath } from '@island.is/application/core' -import { - CAPITALNUMBERS, - CEMETERYEQUITIESANDLIABILITIESIDS, - INPUTCHANGEINTERVAL, - OPERATINGCOST, - VALIDATOR, -} from '../../utils/constants' -import { useTotals } from '../../hooks/useTotals' -import { getTotal } from '../../utils/helpers' - -export const CemeteryEquities = ({ - application, - setBeforeSubmitCallback, -}: FieldBaseProps) => { - const answers = application.answers - const { formatMessage } = useLocale() - const { - clearErrors, - formState: { errors }, - setValue, - getValues, - setError, - } = useFormContext() - - const operatingCostTotal = Number( - getValueViaPath(answers, OPERATINGCOST.total), - ) - - const capitalTotal = Number(getValueViaPath(answers, CAPITALNUMBERS.total)) - - useEffect(() => { - const total = operatingCostTotal + capitalTotal - setValue(CEMETERYEQUITIESANDLIABILITIESIDS.operationResult, total) - setTotalOperatingCost(total) - }, [operatingCostTotal, capitalTotal, setValue]) - - const [totalOperatingCost, setTotalOperatingCost] = useState(0) - const [equityTotal, setEquityTotal] = useState(0) - const [equityAndDebts, setEquityAndDebts] = useState(0) - - const [getTotalAssets, totalAssets] = useTotals( - CEMETERYEQUITIESANDLIABILITIESIDS.assetPrefix, - ) - const [getTotalLiabilities, totalLiabilities] = useTotals( - CEMETERYEQUITIESANDLIABILITIESIDS.liabilityPrefix, - ) - const [getTotalEquity, totalEquity] = useTotals( - CEMETERYEQUITIESANDLIABILITIESIDS.equityPrefix, - ) - - useEffect(() => { - setEquityTotal(totalEquity) - }, [totalEquity, totalOperatingCost]) - - useEffect(() => { - const total = totalEquity + totalLiabilities - setEquityAndDebts(total) - }, [totalEquity, totalLiabilities]) - - useEffect(() => { - clearErrors(VALIDATOR) - }, [totalEquity, totalLiabilities, totalAssets, clearErrors]) - - // we need to validate some info before allowing submission of the current screen data - // since we're comparing values from different objects, doing it via zod is not an option - setBeforeSubmitCallback && - setBeforeSubmitCallback(async () => { - const values = getValues() - const assets = getTotal(values, 'cemetryAsset') - const liabilties = getTotal(values, 'cemetryLiability') - const equities = getTotal(values, 'cemetryEquity') - - // assets should equal liabilties + equities - const isValid = liabilties + equities === assets - if (!isValid) { - setError(VALIDATOR, { - type: 'custom', - message: formatMessage(m.equityDebtsAssetsValidatorError), - }) - return [false, formatMessage(m.equityDebtsAssetsValidatorError)] - } - return [true, null] - }) - - return ( - - - - - {formatMessage(m.properties)} - - - { - getTotalAssets() - clearErrors(CEMETERYEQUITIESANDLIABILITIESIDS.fixedAssetsTotal) - }, INPUTCHANGEINTERVAL)} - label={formatMessage(m.fixedAssetsTotal)} - backgroundColor="blue" - rightAlign - currency - /> - - - { - getTotalAssets() - clearErrors(CEMETERYEQUITIESANDLIABILITIESIDS.currentAssets) - }, INPUTCHANGEINTERVAL)} - label={formatMessage(m.currentAssets)} - error={ - errors && - getErrorViaPath( - errors, - CEMETERYEQUITIESANDLIABILITIESIDS.currentAssets, - ) - } - backgroundColor="blue" - rightAlign - currency - /> - - - - - - {formatMessage(m.debtsAndEquity)} - - - { - getTotalLiabilities() - clearErrors(CEMETERYEQUITIESANDLIABILITIESIDS.longTerm) - }, INPUTCHANGEINTERVAL)} - error={ - errors && - getErrorViaPath( - errors, - CEMETERYEQUITIESANDLIABILITIESIDS.longTerm, - ) - } - label={formatMessage(m.longTerm)} - backgroundColor="blue" - rightAlign - currency - /> - - - { - getTotalLiabilities() - clearErrors(CEMETERYEQUITIESANDLIABILITIESIDS.shortTerm) - }, INPUTCHANGEINTERVAL)} - error={ - errors && - getErrorViaPath( - errors, - CEMETERYEQUITIESANDLIABILITIESIDS.shortTerm, - ) - } - label={formatMessage(m.shortTerm)} - backgroundColor="blue" - rightAlign - currency - /> - - - - { - getTotalEquity() - clearErrors( - CEMETERYEQUITIESANDLIABILITIESIDS.equityAtTheBeginningOfTheYear, - ) - }, INPUTCHANGEINTERVAL)} - error={ - errors && - getErrorViaPath( - errors, - CEMETERYEQUITIESANDLIABILITIESIDS.equityAtTheBeginningOfTheYear, - ) - } - label={formatMessage(m.equityAtTheBeginningOfTheYear)} - backgroundColor="blue" - rightAlign - currency - /> - - - { - getTotalEquity() - clearErrors( - CEMETERYEQUITIESANDLIABILITIESIDS.revaluationDueToPriceChanges, - ) - }, INPUTCHANGEINTERVAL)} - error={ - errors && - getErrorViaPath( - errors, - CEMETERYEQUITIESANDLIABILITIESIDS.revaluationDueToPriceChanges, - ) - } - label={formatMessage(m.revaluationDueToPriceChanges)} - backgroundColor="blue" - rightAlign - currency - /> - - - { - getTotalEquity() - clearErrors(CEMETERYEQUITIESANDLIABILITIESIDS.reevaluateOther) - }, INPUTCHANGEINTERVAL)} - error={ - errors && - getErrorViaPath( - errors, - CEMETERYEQUITIESANDLIABILITIESIDS.reevaluateOther, - ) - } - label={formatMessage(m.reevaluateOther)} - backgroundColor="blue" - rightAlign - currency - /> - - - - - - - - - - - {errors && errors.validator ? ( - - - - ) : null} - - ) -} diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryIncomeLimit/index.tsx b/libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryIncomeLimit/index.tsx index a76d1ed2d9e0..3e78d43e4af1 100644 --- a/libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryIncomeLimit/index.tsx +++ b/libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryIncomeLimit/index.tsx @@ -20,8 +20,8 @@ export const CemeteryIncomeLimit = () => { }) useEffect(() => { - const limit = - data?.financialStatementCemeteryClientFinancialLimit?.toString() + const limit = data?.financialStatementsInaoClientFinancialLimit?.toString() + if (limit) { setValue(CEMETERYOPERATIONIDS.incomeLimit, limit) } diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryOperation/CemeteryExpenses.tsx b/libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryOperation/CemeteryExpenses.tsx deleted file mode 100644 index ef7aa2b9a900..000000000000 --- a/libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryOperation/CemeteryExpenses.tsx +++ /dev/null @@ -1,168 +0,0 @@ -import { useEffect } from 'react' -import { useLocale } from '@island.is/localization' -import { Box } from '@island.is/island-ui/core' -import { InputController } from '@island.is/shared/form-fields' -import { getErrorViaPath, getValueViaPath } from '@island.is/application/core' -import { RecordObject } from '@island.is/application/types' -import debounce from 'lodash/debounce' -import { useFormContext } from 'react-hook-form' -import { m } from '../../lib/messages' - -import { FinancialStatementsInaoTaxInfo } from '@island.is/api/schema' -import { - CEMETERYOPERATIONIDS, - INPUTCHANGEINTERVAL, -} from '../../utils/constants' -type Props = { - data?: { - financialStatementsInaoTaxInfo: FinancialStatementsInaoTaxInfo[] - } | null - loading: boolean - getSum: () => void - errors: RecordObject | undefined -} - -export const CemeteryExpenses = ({ data, loading, errors, getSum }: Props) => { - const { formatMessage } = useLocale() - const { clearErrors, setValue, getValues } = useFormContext() - const values = getValues() - - const donationsToCemeteryFund = getValueViaPath( - values, - CEMETERYOPERATIONIDS.donationsToCemeteryFund, - ) - - useEffect(() => { - if (data?.financialStatementsInaoTaxInfo) { - if (!donationsToCemeteryFund) { - setValue( - CEMETERYOPERATIONIDS.donationsToCemeteryFund, - data.financialStatementsInaoTaxInfo - ?.find((x) => x.key === 334) - ?.value?.toString() ?? '', - ) - } - } - getSum() - }, [data, getSum, setValue]) - - const onInputChange = debounce((fieldId: string) => { - getSum() - clearErrors(fieldId) - }, INPUTCHANGEINTERVAL) - - return ( - <> - - onInputChange(CEMETERYOPERATIONIDS.payroll)} - error={ - errors && getErrorViaPath(errors, CEMETERYOPERATIONIDS.payroll) - } - backgroundColor="blue" - rightAlign - currency - /> - - - onInputChange(CEMETERYOPERATIONIDS.funeralCost)} - error={ - errors && getErrorViaPath(errors, CEMETERYOPERATIONIDS.funeralCost) - } - backgroundColor="blue" - rightAlign - currency - /> - - - onInputChange(CEMETERYOPERATIONIDS.chapelExpense)} - error={ - errors && - getErrorViaPath(errors, CEMETERYOPERATIONIDS.chapelExpense) - } - backgroundColor="blue" - rightAlign - currency - /> - - - - onInputChange(CEMETERYOPERATIONIDS.donationsToCemeteryFund) - } - error={ - errors && - getErrorViaPath( - errors, - CEMETERYOPERATIONIDS.donationsToCemeteryFund, - ) - } - backgroundColor="blue" - rightAlign - currency - /> - - - onInputChange(CEMETERYOPERATIONIDS.donationsToOther)} - error={ - errors && - getErrorViaPath(errors, CEMETERYOPERATIONIDS.donationsToOther) - } - backgroundColor="blue" - rightAlign - currency - /> - - - - onInputChange(CEMETERYOPERATIONIDS.otherOperationCost) - } - error={ - errors && - getErrorViaPath(errors, CEMETERYOPERATIONIDS.otherOperationCost) - } - backgroundColor="blue" - rightAlign - currency - /> - - - onInputChange(CEMETERYOPERATIONIDS.depreciation)} - error={ - errors && getErrorViaPath(errors, CEMETERYOPERATIONIDS.depreciation) - } - backgroundColor="blue" - rightAlign - currency - /> - - - ) -} diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryOperation/CemeteryIncome.tsx b/libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryOperation/CemeteryIncome.tsx deleted file mode 100644 index 46179f7371da..000000000000 --- a/libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryOperation/CemeteryIncome.tsx +++ /dev/null @@ -1,147 +0,0 @@ -import { useEffect } from 'react' -import { Box } from '@island.is/island-ui/core' -import { RecordObject } from '@island.is/application/types' -import debounce from 'lodash/debounce' -import { InputController } from '@island.is/shared/form-fields' -import { useLocale } from '@island.is/localization' -import { useFormContext } from 'react-hook-form' -import { getErrorViaPath, getValueViaPath } from '@island.is/application/core' -import { m } from '../../lib/messages' -import { FinancialStatementsInaoTaxInfo } from '@island.is/api/schema' -import { - CEMETERYOPERATIONIDS, - INPUTCHANGEINTERVAL, - TaxInfoTypes, -} from '../../utils/constants' - -type Props = { - data?: { - financialStatementsInaoTaxInfo: FinancialStatementsInaoTaxInfo[] - } | null - loading: boolean - getSum: () => void - errors: RecordObject | undefined -} - -export const CemetryIncome = ({ data, loading, errors, getSum }: Props) => { - const { formatMessage } = useLocale() - const { clearErrors, getValues, setValue } = useFormContext() - - useEffect(() => { - const values = getValues() - - const careIncome = getValueViaPath(values, CEMETERYOPERATIONIDS.careIncome) - const burialRevenue = getValueViaPath( - values, - CEMETERYOPERATIONIDS.burialRevenue, - ) - const grantFromTheCemeteryFund = getValueViaPath( - values, - CEMETERYOPERATIONIDS.grantFromTheCemeteryFund, - ) - - if (data?.financialStatementsInaoTaxInfo) { - if (!careIncome) { - setValue( - CEMETERYOPERATIONIDS.careIncome, - data.financialStatementsInaoTaxInfo - ?.find((x) => x.key === TaxInfoTypes.CARE_INCOME) - ?.value?.toString() ?? '', - ) - } - if (!burialRevenue) { - setValue( - CEMETERYOPERATIONIDS.burialRevenue, - data.financialStatementsInaoTaxInfo - ?.find((x) => x.key === TaxInfoTypes.BURIAL_REVENUE) - ?.value?.toString() ?? '', - ) - } - if (!grantFromTheCemeteryFund) { - setValue( - CEMETERYOPERATIONIDS.grantFromTheCemeteryFund, - data.financialStatementsInaoTaxInfo - ?.find((x) => x.key === TaxInfoTypes.GRANT_FROM_THE_CEMETERY_FUND) - ?.value?.toString() ?? '', - ) - } - getSum() - } - }, [data, getSum, setValue]) - - const onInputChange = debounce((fieldId: string) => { - getSum() - clearErrors(fieldId) - }, INPUTCHANGEINTERVAL) - - return ( - <> - - onInputChange(CEMETERYOPERATIONIDS.careIncome)} - backgroundColor="blue" - currency - rightAlign - error={ - errors && getErrorViaPath(errors, CEMETERYOPERATIONIDS.careIncome) - } - /> - - - onInputChange(CEMETERYOPERATIONIDS.burialRevenue)} - backgroundColor="blue" - currency - rightAlign - error={ - errors && - getErrorViaPath(errors, CEMETERYOPERATIONIDS.burialRevenue) - } - /> - - - - onInputChange(CEMETERYOPERATIONIDS.grantFromTheCemeteryFund) - } - backgroundColor="blue" - currency - rightAlign - error={ - errors && - getErrorViaPath( - errors, - CEMETERYOPERATIONIDS.grantFromTheCemeteryFund, - ) - } - /> - - - onInputChange(CEMETERYOPERATIONIDS.otherIncome)} - backgroundColor="blue" - currency - rightAlign - error={ - errors && getErrorViaPath(errors, CEMETERYOPERATIONIDS.otherIncome) - } - /> - - - ) -} diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryOperation/index.tsx b/libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryOperation/index.tsx deleted file mode 100644 index 424169cccdfa..000000000000 --- a/libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryOperation/index.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { useFormContext } from 'react-hook-form' -import { - GridColumn, - GridContainer, - GridRow, - Text, -} from '@island.is/island-ui/core' -import { Application } from '@island.is/application/types' -import { useLocale } from '@island.is/localization' -import { m } from '../../lib/messages' -import { Total } from '../KeyNumbers' -import { CemetryIncome } from './CemeteryIncome' -import { CemeteryExpenses } from './CemeteryExpenses' -import { CemeteryIncomeLimit } from '../CemeteryIncomeLimit/index' -import { useQuery } from '@apollo/client' -import { getValueViaPath } from '@island.is/application/core' -import { taxInfoQuery } from '../../graphql' -import { CEMETERYOPERATIONIDS, OPERATINGCOST } from '../../utils/constants' -import { useTotals } from '../../hooks/useTotals' - -export const CemeteryOperation = ({ - application, -}: { - application: Application -}) => { - const { answers } = application - - const operatingYear = - getValueViaPath(answers, 'conditionalAbout.operatingYear') ?? '' - - const { data, loading } = useQuery(taxInfoQuery, { - variables: { year: operatingYear }, - }) - - const { - formState: { errors }, - } = useFormContext() - const { formatMessage } = useLocale() - const [getTotalIncome, totalIncome] = useTotals( - CEMETERYOPERATIONIDS.prefixIncome, - ) - const [getTotalExpense, totalExpense] = useTotals( - CEMETERYOPERATIONIDS.prefixExpense, - ) - - return ( - - - - - - {formatMessage(m.income)} - - - - - - - {formatMessage(m.expenses)} - - - - - - - - - - - - ) -} diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryOverview/BottomBar.tsx b/libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryOverview/BottomBar.tsx deleted file mode 100644 index a014701fc18c..000000000000 --- a/libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryOverview/BottomBar.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { Box, Button, Divider } from '@island.is/island-ui/core' -import { useLocale } from '@island.is/localization' -import { m } from '../../lib/messages' - -type Props = { - onBackButtonClick: () => void - onSendButtonClick: () => void - loading?: boolean - sendText?: string -} - -const BottomBar = ({ - onBackButtonClick, - onSendButtonClick, - loading = false, - sendText, -}: Props) => { - const { formatMessage } = useLocale() - - return ( - <> - - - - - - - - - ) -} - -export default BottomBar diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryOverview/index.tsx b/libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryOverview/index.tsx index 57040fecba5d..0f8c89f2c9f0 100644 --- a/libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryOverview/index.tsx +++ b/libs/application/templates/inao/financial-statement-cemetery/src/fields/CemeteryOverview/index.tsx @@ -1,86 +1,134 @@ -import { useState } from 'react' -import { DefaultEvents, FieldBaseProps } from '@island.is/application/types' -import { getErrorViaPath, getValueViaPath } from '@island.is/application/core' - +import { Fragment } from 'react' +import { FieldBaseProps } from '@island.is/application/types' +import { getValueViaPath } from '@island.is/application/core' import { AlertBanner, Box, - Checkbox, Divider, GridColumn, GridRow, - InputError, Text, } from '@island.is/island-ui/core' -import { Controller, useFormContext } from 'react-hook-form' import { useLocale } from '@island.is/localization' import { format as formatNationalId } from 'kennitala' -import { useSubmitApplication } from '../../hooks/useSubmitApplication' import { m } from '../../lib/messages' import { FinancialStatementCemetery } from '../../lib/dataSchema' -import { currencyStringToNumber, formatCurrency } from '../../utils/helpers' +import { formatCurrency } from '../../utils/helpers' import { AboutOverview } from './AboutOverview' import { ValueLine } from './ValueLine' import { CapitalNumberOverview } from './CapitalNumbersOverview' import { BOARDMEMEBER } from '../../utils/constants' import { FileValueLine } from './FileValueLine' -import BottomBar from './BottomBar' import { columnStyle, sectionColumn, starterColumnStyle, } from './overviewStyles.css' -export const CemeteryOverview = ({ - application, - goToScreen, - refetch, -}: FieldBaseProps) => { - const { - formState: { errors }, - setError, - setValue, - } = useFormContext() +export const CemeteryOverview = ({ application }: FieldBaseProps) => { const { formatMessage } = useLocale() - const [approveOverview, setApproveOverview] = useState(false) - - const [submitApplication, { error: submitError, loading }] = - useSubmitApplication({ - application, - refetch, - event: DefaultEvents.SUBMIT, - }) const answers = application.answers as FinancialStatementCemetery - const fileName = answers.attachments?.file?.[0]?.name - const careTakerLimit = answers.cemeteryOperation?.incomeLimit ?? '0' - const cemeteryIncome = currencyStringToNumber(answers.cemeteryIncome?.total) - const fixedAssetsTotal = answers.cemeteryAsset?.fixedAssetsTotal - const longTermDebt = answers.cemeteryLiability?.longTerm - const email = getValueViaPath(answers, 'about.email') + const file = getValueViaPath>(answers, 'attachments.file') + const fileName = file?.[0]?.name + const incomeLimit = + getValueViaPath(answers, 'cemeteryOperations.incomeLimit') ?? '0' + const email = getValueViaPath(answers, 'about.email') const cemeteryCaretakers = answers.cemeteryCaretaker - const onBackButtonClick = () => { - if ( - Number(cemeteryIncome) < Number(careTakerLimit) && - fixedAssetsTotal === '0' && - longTermDebt === '0' - ) { - goToScreen && goToScreen('caretakers') - } else { - goToScreen && goToScreen('attachments.file') - } - } + const careIncome = getValueViaPath( + answers, + 'cemeteryIncome.careIncome', + ) + const burialRevenue = getValueViaPath( + answers, + 'cemeteryIncome.burialRevenue', + ) + const grantFromTheCemeteryFund = getValueViaPath( + answers, + 'cemeteryIncome.grantFromTheCemeteryFund', + ) + const otherIncome = getValueViaPath( + answers, + 'cemeteryIncome.otherIncome', + ) + const totalIncome = getValueViaPath(answers, 'cemeteryIncome.total') + + const payroll = getValueViaPath(answers, 'cemeteryExpense.payroll') + const funeralCost = getValueViaPath( + answers, + 'cemeteryExpense.funeralCost', + ) + const chapelExpense = getValueViaPath( + answers, + 'cemeteryExpense.chapelExpense', + ) + const donationsToCemeteryFund = getValueViaPath( + answers, + 'cemeteryExpense.cemeteryFundExpense', + ) + const donationsToOther = getValueViaPath( + answers, + 'cemeteryExpense.donationsToOther', + ) + const otherOperationCost = getValueViaPath( + answers, + 'cemeteryExpense.otherOperationCost', + ) + const depreciation = getValueViaPath( + answers, + 'cemeteryExpense.depreciation', + ) + const totalExpenses = getValueViaPath( + answers, + 'cemeteryExpense.total', + ) - const onSendButtonClick = () => { - if (approveOverview) { - submitApplication() - } else { - setError('applicationApprove', { - type: 'error', - }) - } - } + const fixedAssetsTotal = getValueViaPath( + answers, + 'cemeteryAsset.fixedAssetsTotal', + ) + const currentAssets = getValueViaPath( + answers, + 'cemeteryAsset.currentAssets', + ) + const totalAssets = getValueViaPath(answers, 'assetsTotal') + + const longTerm = getValueViaPath( + answers, + 'cemeteryLiability.longTerm', + ) + const shortTerm = getValueViaPath( + answers, + 'cemeteryLiability.shortTerm', + ) + const totalLiabilities = getValueViaPath( + answers, + 'equityAndLiabilitiesTotals.liabilitiesTotal', + ) + + const equityAtTheBeginningOfTheYear = getValueViaPath( + answers, + 'cemeteryEquity.equityAtTheBeginningOfTheYear', + ) + const revaluationDueToPriceChanges = getValueViaPath( + answers, + 'cemeteryEquity.revaluationDueToPriceChanges', + ) + const reevaluateOther = getValueViaPath( + answers, + 'cemeteryEquity.reevaluateOther', + ) + const operationResult = getValueViaPath( + answers, + 'cemeteryEquity.operationResult', + ) + const totalEquity = getValueViaPath(answers, 'cemeteryEquity.total') + + const debtsAndCash = getValueViaPath( + answers, + 'equityAndLiabilitiesTotals.equityAndLiabilitiesTotal', + ) return ( @@ -104,26 +152,24 @@ export const CemeteryOverview = ({ @@ -132,42 +178,35 @@ export const CemeteryOverview = ({ {formatMessage(m.expenses)}
- + @@ -193,16 +232,16 @@ export const CemeteryOverview = ({ @@ -213,18 +252,12 @@ export const CemeteryOverview = ({ {formatMessage(m.debts)}
- - + + @@ -233,39 +266,35 @@ export const CemeteryOverview = ({ - {parseInt(answers.cemeteryIncome?.total, 10) < Number(careTakerLimit) && + {Number(totalIncome) < Number(incomeLimit) && cemeteryCaretakers?.length > 0 ? ( <> @@ -273,8 +302,8 @@ export const CemeteryOverview = ({ {formatMessage(m.cemeteryBoardmembers)} - {cemeteryCaretakers.map((careTaker) => ( - <> + {cemeteryCaretakers.map((careTaker, i) => ( + @@ -303,48 +332,19 @@ export const CemeteryOverview = ({ - + ))} ) : null} {fileName ? ( <> - + ) : null} - - - {formatMessage(m.overview)} - - - - - { - return ( - { - onChange(e.target.checked) - setApproveOverview(e.target.checked) - setValue('applicationApprove' as string, e.target.checked) - }} - checked={value} - name="applicationApprove" - id="applicationApprove" - label={formatMessage(m.overviewCorrect)} - large - /> - ) - }} - /> - - {Number(cemeteryIncome) < Number(careTakerLimit) && + {Number(totalIncome) < Number(incomeLimit) && fixedAssetsTotal === '0' && - longTermDebt === '0' ? ( + longTerm === '0' ? ( ) : null} - {errors && getErrorViaPath(errors, 'applicationApprove') ? ( - - ) : null} - {submitError ? ( - - - - ) : null} - ) } diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/fields/KeyNumbers/index.tsx b/libs/application/templates/inao/financial-statement-cemetery/src/fields/KeyNumbers/index.tsx deleted file mode 100644 index d565106e5e7b..000000000000 --- a/libs/application/templates/inao/financial-statement-cemetery/src/fields/KeyNumbers/index.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { useEffect } from 'react' -import { Box, Text } from '@island.is/island-ui/core' -import { InputController } from '@island.is/shared/form-fields' -import { useFormContext } from 'react-hook-form' - -type PropTypes = { name: string; total: number; label: string; title?: string } - -export const Total = ({ name, total, label, title }: PropTypes) => { - const { setValue } = useFormContext() - - useEffect(() => { - setValue(name, total.toString()) - }, [total]) - - return ( - - {title ? ( - - {title} - - ) : null} - - - - ) -} diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/fields/KeyNumbersCapital/index.tsx b/libs/application/templates/inao/financial-statement-cemetery/src/fields/KeyNumbersCapital/index.tsx deleted file mode 100644 index 23ce3e66e903..000000000000 --- a/libs/application/templates/inao/financial-statement-cemetery/src/fields/KeyNumbersCapital/index.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import React, { useState, useEffect } from 'react' -import { - Box, - GridColumn, - GridContainer, - GridRow, -} from '@island.is/island-ui/core' -import { getValueViaPath } from '@island.is/application/core' -import debounce from 'lodash/debounce' -import { useFormContext } from 'react-hook-form' -import { useLocale } from '@island.is/localization' -import { InputController } from '@island.is/shared/form-fields' -import { m } from '../../lib/messages' -import { Total } from '../KeyNumbers' - -import { getErrorViaPath } from '@island.is/application/core' -import { CAPITALNUMBERS, INPUTCHANGEINTERVAL } from '../../utils/constants' - -export const KeyNumbersCapital = () => { - const { formatMessage } = useLocale() - const [totalCapital, setTotalCapital] = useState(0) - const { - clearErrors, - formState: { errors }, - getValues, - } = useFormContext() - - const getTotalCapital = () => { - const values = getValues() - - const income = getValueViaPath(values, CAPITALNUMBERS.capitalIncome) || '0' - const expense = getValueViaPath(values, CAPITALNUMBERS.capitalCost) || '0' - const total = Number(income) - Number(expense) - setTotalCapital(total) - } - - useEffect(() => { - getTotalCapital() - }, [getTotalCapital]) - - return ( - - - - - { - getTotalCapital() - clearErrors(CAPITALNUMBERS.capitalIncome) - }, INPUTCHANGEINTERVAL)} - label={formatMessage(m.capitalIncome)} - backgroundColor="blue" - rightAlign - currency - /> - - - - - { - getTotalCapital() - clearErrors(CAPITALNUMBERS.capitalCost) - }, INPUTCHANGEINTERVAL)} - label={formatMessage(m.capitalCost)} - error={ - errors && getErrorViaPath(errors, CAPITALNUMBERS.capitalCost) - } - backgroundColor="blue" - rightAlign - currency - /> - - - - - - - - - - ) -} diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/fields/OperatingYear/index.tsx b/libs/application/templates/inao/financial-statement-cemetery/src/fields/OperatingYear/index.tsx deleted file mode 100644 index 87dfb926490b..000000000000 --- a/libs/application/templates/inao/financial-statement-cemetery/src/fields/OperatingYear/index.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { - AlertMessage, - Box, - ContentBlock, - SkeletonLoader, -} from '@island.is/island-ui/core' -import { useLocale } from '@island.is/localization' -import { m } from '../../lib/messages' - -import * as styles from './styles.css' -import { useFormContext } from 'react-hook-form' -import { getErrorViaPath } from '@island.is/application/core' -import { SelectController } from '@island.is/shared/form-fields' -import { useQuery } from '@apollo/client' - -import { getAuditConfig } from '../../graphql' -import { - getConfigInfoForKey, - possibleOperatingYears, -} from '../../utils/helpers' -import { - ABOUTIDS, - CemeteriesBackwardLimit, - CemeteriesYearAllowed, -} from '../../utils/constants' - -export const OperatingYear = () => { - const { data, loading, error } = useQuery(getAuditConfig) - const { formatMessage } = useLocale() - const { - formState: { errors }, - } = useFormContext() - - if (loading) { - return ( - - - - ) - } - - if (error || data?.financialStatementsInaoConfig?.length <= 0) { - return ( - - - - ) - } - - const { financialStatementsInaoConfig } = data - - const backwardsYearLimit = getConfigInfoForKey( - financialStatementsInaoConfig, - CemeteriesBackwardLimit, - ) - - const countYearBackwardsFrom = getConfigInfoForKey( - financialStatementsInaoConfig, - CemeteriesYearAllowed, - ) - - const operatingYear = possibleOperatingYears( - backwardsYearLimit, - countYearBackwardsFrom, - ) - - return ( - - - - ) -} diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/fields/OperatingYear/styles.css.ts b/libs/application/templates/inao/financial-statement-cemetery/src/fields/OperatingYear/styles.css.ts deleted file mode 100644 index c5dd8724f088..000000000000 --- a/libs/application/templates/inao/financial-statement-cemetery/src/fields/OperatingYear/styles.css.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { style } from '@vanilla-extract/css' - -export const selectSpace = style({ - paddingRight: '10px', -}) diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/fields/PowerOfAttorney/index.tsx b/libs/application/templates/inao/financial-statement-cemetery/src/fields/PowerOfAttorney/index.tsx index cdf31e9ad78f..f4dcf6f77248 100644 --- a/libs/application/templates/inao/financial-statement-cemetery/src/fields/PowerOfAttorney/index.tsx +++ b/libs/application/templates/inao/financial-statement-cemetery/src/fields/PowerOfAttorney/index.tsx @@ -11,9 +11,7 @@ import { import { InputController } from '@island.is/shared/form-fields' import { useLocale } from '@island.is/localization' import { IdentityInput, Query } from '@island.is/api/schema' - import { m } from '../../lib/messages' - import { FieldBaseProps } from '@island.is/application/types' import { getErrorViaPath } from '@island.is/application/core' import { IdentityQuery } from '../../graphql' diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/fields/Success/index.tsx b/libs/application/templates/inao/financial-statement-cemetery/src/fields/Success/index.tsx deleted file mode 100644 index e8a6adc55f21..000000000000 --- a/libs/application/templates/inao/financial-statement-cemetery/src/fields/Success/index.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { - Box, - ContentBlock, - ActionCard, - AlertMessage, -} from '@island.is/island-ui/core' -import { useLocale } from '@island.is/localization' -import { CustomField, FieldBaseProps } from '@island.is/application/types' -import format from 'date-fns/format' -import { m } from '../../lib/messages' -import { FinancialStatementCemetery } from '../../lib/dataSchema' -import { - getCurrentUserType, - isCemetryUnderFinancialLimit, -} from '../../utils/helpers' - -interface PropTypes extends FieldBaseProps { - field: CustomField -} - -export const Success = ({ application }: PropTypes) => { - const { answers, externalData } = application - const applicationAnswers = application.answers as FinancialStatementCemetery - const { formatMessage } = useLocale() - - const getDescriptionText = () => { - const currentDate = format(new Date(), "dd.MM.yyyy 'kl.' kk:mm") - return `${formatMessage(m.operatingYearMsgFirst)} ${ - applicationAnswers.conditionalAbout.operatingYear - } - ${formatMessage(m.individualReceivedMsgSecond)} ${currentDate}` - } - - const shouldShowDigitalSigningMessage = () => { - return isCemetryUnderFinancialLimit(answers, externalData) - } - - return ( - - - - - - {shouldShowDigitalSigningMessage() && ( - - - - )} - - window.open('/minarsidur/postholf', '_blank'), - }} - backgroundColor="blue" - /> - - - - ) -} diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/fields/index.ts b/libs/application/templates/inao/financial-statement-cemetery/src/fields/index.ts index 38e7219bec44..9903bafb3734 100644 --- a/libs/application/templates/inao/financial-statement-cemetery/src/fields/index.ts +++ b/libs/application/templates/inao/financial-statement-cemetery/src/fields/index.ts @@ -1,8 +1,4 @@ export { CemeteryCaretaker } from './CemeteryCareteker' -export { CemeteryOperation } from './CemeteryOperation' -export { KeyNumbersCapital } from './KeyNumbersCapital' -export { CemeteryEquities } from './CemeteryEquities' -export { OperatingYear } from './OperatingYear' export { PowerOfAttorneyFields } from './PowerOfAttorney' export { CemeteryOverview } from './CemeteryOverview' -export { Success } from './Success' +export { CemeteryIncomeLimit } from './CemeteryIncomeLimit' diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/cemeteryKeyNumbersSection/capitalNumberSubSection.ts b/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/cemeteryKeyNumbersSection/capitalNumberSubSection.ts index 549bf3b78658..86bc4ae3eaa0 100644 --- a/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/cemeteryKeyNumbersSection/capitalNumberSubSection.ts +++ b/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/cemeteryKeyNumbersSection/capitalNumberSubSection.ts @@ -1,10 +1,12 @@ import { - buildCustomField, + buildDisplayField, buildMultiField, buildSubSection, + buildTextField, } from '@island.is/application/core' import { m } from '../../../lib/messages' import { CAPITALNUMBERS } from '../../../utils/constants' +import { sumCapitalNumbers } from '../../../utils/helpers' export const capitalNumberSubSection = buildSubSection({ id: 'keynumbers.capitalNumbers', @@ -15,12 +17,27 @@ export const capitalNumberSubSection = buildSubSection({ title: m.capitalNumbersSectionTitle, description: m.fillOutAppopriate, children: [ - buildCustomField({ - id: 'capitalNumberField', + buildTextField({ + id: CAPITALNUMBERS.capitalIncome, + title: m.capitalIncome, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildTextField({ + id: CAPITALNUMBERS.capitalCost, + title: m.capitalCost, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildDisplayField({ + id: CAPITALNUMBERS.total, title: '', - description: '', - component: 'KeyNumbersCapital', - childInputIds: Object.values(CAPITALNUMBERS), + label: m.totalCapital, + value: sumCapitalNumbers, + variant: 'currency', + rightAlign: true, }), ], }), diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/cemeteryKeyNumbersSection/equityAndLiabilitySubSection.ts b/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/cemeteryKeyNumbersSection/equityAndLiabilitySubSection.ts index f5cb6fdf8f6e..1e0f6a2987f2 100644 --- a/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/cemeteryKeyNumbersSection/equityAndLiabilitySubSection.ts +++ b/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/cemeteryKeyNumbersSection/equityAndLiabilitySubSection.ts @@ -1,10 +1,24 @@ import { - buildCustomField, + buildAlertMessageField, + buildDescriptionField, + buildDisplayField, buildMultiField, buildSubSection, + buildTextField, } from '@island.is/application/core' import { m } from '../../../lib/messages' -import { CEMETERYEQUITIESANDLIABILITIESIDS } from '../../../utils/constants' +import { + CEMETERYEQUITIESANDLIABILITIESIDS, + EQUITYANDLIABILITIESTOTALS, +} from '../../../utils/constants' +import { + operationResult, + showEquitiesAndLiabilitiesAlert, + sumAssets, + sumLiabilities, + sumTotalEquity, + sumTotalEquityAndLiabilities, +} from '../../../utils/helpers' export const equityAndLiabilitiesSubSection = buildSubSection({ id: 'keyNumbers.cemetryEquitiesAndLiabilities', @@ -15,12 +29,129 @@ export const equityAndLiabilitiesSubSection = buildSubSection({ title: m.keyNumbersDebt, description: m.fillOutAppopriate, children: [ - buildCustomField({ - id: 'cemeteryEquitiesAndLiabilities', - title: m.keyNumbersDebt, - description: m.fillOutAppopriate, - component: 'CemeteryEquities', - childInputIds: Object.values(CEMETERYEQUITIESANDLIABILITIESIDS), + // Assets + buildDescriptionField({ + id: 'assetsDescription', + title: m.properties, + titleVariant: 'h3', + }), + buildTextField({ + id: CEMETERYEQUITIESANDLIABILITIESIDS.fixedAssetsTotal, + title: m.fixedAssetsTotal, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildTextField({ + id: CEMETERYEQUITIESANDLIABILITIESIDS.currentAssets, + title: m.currentAssets, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildDisplayField({ + id: EQUITYANDLIABILITIESTOTALS.assetsTotal, + title: '', + label: m.totalAssets, + value: sumAssets, + variant: 'currency', + rightAlign: true, + }), + + // Debt + buildDescriptionField({ + id: 'debtsDescription', + title: m.debts, + titleVariant: 'h3', + }), + buildTextField({ + id: CEMETERYEQUITIESANDLIABILITIESIDS.longTerm, + title: m.longTerm, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildTextField({ + id: CEMETERYEQUITIESANDLIABILITIESIDS.shortTerm, + title: m.shortTerm, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildDisplayField({ + id: EQUITYANDLIABILITIESTOTALS.liabilitiesTotal, + title: '', + label: m.totalLiabilities, + value: sumLiabilities, + variant: 'currency', + rightAlign: true, + }), + + // Equity + buildDescriptionField({ + id: 'equityDescription', + title: m.equity, + titleVariant: 'h3', + }), + buildTextField({ + id: CEMETERYEQUITIESANDLIABILITIESIDS.equityAtTheBeginningOfTheYear, + title: m.equityAtTheBeginningOfTheYear, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildTextField({ + id: CEMETERYEQUITIESANDLIABILITIESIDS.revaluationDueToPriceChanges, + title: m.revaluationDueToPriceChanges, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildTextField({ + id: CEMETERYEQUITIESANDLIABILITIESIDS.reevaluateOther, + title: m.reevaluateOther, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildDisplayField({ + id: CEMETERYEQUITIESANDLIABILITIESIDS.operationResult, + title: '', + label: m.operationResult, + value: operationResult, + variant: 'currency', + rightAlign: true, + width: 'half', + }), + buildDisplayField({ + id: CEMETERYEQUITIESANDLIABILITIESIDS.equityTotal, + title: '', + label: m.totalEquity, + value: sumTotalEquity, + variant: 'currency', + rightAlign: true, + }), + + // Debts and Equity + buildDescriptionField({ + id: 'debtsAndEquityDescription', + title: m.debtsAndCash, + titleVariant: 'h3', + }), + buildDisplayField({ + id: EQUITYANDLIABILITIESTOTALS.equityAndLiabilitiesTotal, + title: '', + value: sumTotalEquityAndLiabilities, + variant: 'currency', + rightAlign: true, + }), + + buildAlertMessageField({ + condition: showEquitiesAndLiabilitiesAlert, + id: 'equityAndLiabilityError', + title: m.equityErrorTitle, + message: m.equityDebtsAssetsValidatorError, + alertType: 'error', }), ], }), diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/cemeteryKeyNumbersSection/opperatingCostSubSection.ts b/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/cemeteryKeyNumbersSection/opperatingCostSubSection.ts index 66ef7c08bc52..34329fa8ee15 100644 --- a/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/cemeteryKeyNumbersSection/opperatingCostSubSection.ts +++ b/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/cemeteryKeyNumbersSection/opperatingCostSubSection.ts @@ -1,10 +1,18 @@ import { buildCustomField, + buildDescriptionField, + buildDisplayField, buildMultiField, buildSubSection, + buildTextField, } from '@island.is/application/core' -import { CEMETERYOPERATIONIDS } from '../../../utils/constants' +import { CEMETERYOPERATIONIDS, OPERATINGCOST } from '../../../utils/constants' import { m } from '../../../lib/messages' +import { + sumExpenses, + sumIncome, + sumOperatingResults, +} from '../../../utils/helpers' export const opperatingCostSubSection = buildSubSection({ id: 'operatingCost', @@ -16,10 +24,129 @@ export const opperatingCostSubSection = buildSubSection({ description: m.fillOutAppopriate, children: [ buildCustomField({ - id: 'cemetryKeyNumbers', + id: 'cemetryIncomeLimit', + title: '', + component: 'CemeteryIncomeLimit', + }), + // Income + buildDescriptionField({ + id: 'incomeDescription', + title: m.income, + titleVariant: 'h3', + }), + buildTextField({ + id: CEMETERYOPERATIONIDS.careIncome, + title: m.careIncome, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildTextField({ + id: CEMETERYOPERATIONIDS.burialRevenue, + title: m.burialRevenue, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildTextField({ + id: CEMETERYOPERATIONIDS.grantFromTheCemeteryFund, + title: m.grantFromTheCemeteryFund, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildTextField({ + id: CEMETERYOPERATIONIDS.otherIncome, + title: m.otherIncome, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildDisplayField({ + id: CEMETERYOPERATIONIDS.totalIncome, + title: '', + label: m.totalIncome, + value: sumIncome, + variant: 'currency', + rightAlign: true, + }), + + // Expenses + buildDescriptionField({ + id: 'expensesDescription', + title: m.expenses, + titleVariant: 'h3', + }), + buildTextField({ + id: CEMETERYOPERATIONIDS.payroll, + title: m.payroll, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildTextField({ + id: CEMETERYOPERATIONIDS.funeralCost, + title: m.funeralCost, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildTextField({ + id: CEMETERYOPERATIONIDS.chapelExpense, + title: m.chapelExpense, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildTextField({ + id: CEMETERYOPERATIONIDS.donationsToCemeteryFund, + title: m.donationsToCemeteryFund, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildTextField({ + id: CEMETERYOPERATIONIDS.donationsToOther, + title: m.donationsToOther, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildTextField({ + id: CEMETERYOPERATIONIDS.otherOperationCost, + title: m.otherOperationCost, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildTextField({ + id: CEMETERYOPERATIONIDS.depreciation, + title: m.depreciation, + width: 'half', + variant: 'currency', + rightAlign: true, + }), + buildDisplayField({ + id: CEMETERYOPERATIONIDS.totalExpense, + title: '', + label: m.totalExpenses, + value: sumExpenses, + variant: 'currency', + rightAlign: true, + }), + + // Operating results + buildDescriptionField({ + id: 'operatingResultsDescription', + title: m.operatingCost, + titleVariant: 'h3', + }), + buildDisplayField({ + id: OPERATINGCOST.total, title: '', - component: 'CemeteryOperation', - childInputIds: Object.values(CEMETERYOPERATIONIDS), + value: sumOperatingResults, + variant: 'currency', + rightAlign: true, }), ], }), diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/clientInfoSection/index.ts b/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/clientInfoSection/index.ts index 387a2072098f..9916366fd934 100644 --- a/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/clientInfoSection/index.ts +++ b/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/clientInfoSection/index.ts @@ -1,4 +1,5 @@ import { + buildAsyncSelectField, buildCustomField, buildDescriptionField, buildMultiField, @@ -9,6 +10,9 @@ import { Application, UserProfile } from '@island.is/application/types' import { m } from '../../../lib/messages' import { ABOUTIDS } from '../../../utils/constants' import { Identity } from '@island.is/api/schema' +import { getAuditConfig } from '../../../graphql' +import { AuditConfig } from '../../../types/types' +import { getYearOptions } from '../../../utils/helpers' export const clientInfoSection = buildSection({ id: 'info', @@ -19,15 +23,21 @@ export const clientInfoSection = buildSection({ title: m.info, description: m.reviewContact, children: [ - buildDescriptionField({ + buildAsyncSelectField({ id: ABOUTIDS.operatingYear, - title: '', + title: m.operatingYear, + placeholder: m.selectOperatingYear, + width: 'half', + loadOptions: async ({ apolloClient }) => { + const { data } = await apolloClient.query({ + query: getAuditConfig, + }) + return getYearOptions(data) + }, }), - buildCustomField({ - id: 'OperatingYear', - childInputIds: [ABOUTIDS.operatingYear], + buildDescriptionField({ + id: 'about.description', title: '', - component: 'OperatingYear', }), buildTextField({ id: 'about.nationalId', @@ -49,7 +59,7 @@ export const clientInfoSection = buildSection({ }, }), buildDescriptionField({ - id: ABOUTIDS.powerOfAttorneyName, + id: 'about.description2', title: '', }), buildCustomField({ diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/conclusionSection/conclusionSection.ts b/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/conclusionSection/conclusionSection.ts new file mode 100644 index 000000000000..1ca0e1529209 --- /dev/null +++ b/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/conclusionSection/conclusionSection.ts @@ -0,0 +1,19 @@ +import { buildFormConclusionSection } from '@island.is/application/ui-forms' +import { m } from '../../../lib/messages' +import { getValueViaPath } from '@island.is/application/core' + +export const conclusionSection = buildFormConclusionSection({ + multiFieldTitle: m.received, + alertTitle: m.returned, + alertMessage: (application) => { + const year = getValueViaPath( + application.answers, + 'conditionalAbout.operatingYear', + ) + return { + ...m.conclusionAlertMessage, + values: { value1: year }, + } + }, + accordion: false, +}) diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/index.ts b/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/index.ts index bf4875117557..736e879139ee 100644 --- a/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/index.ts +++ b/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/index.ts @@ -7,12 +7,13 @@ import { cemeteryFinancialStatementSection } from './cemeteryFinancialStatementS import Logo from '../../components/Logo' import { clientInfoSection } from './clientInfoSection' import { overviewSection } from './overviewSection' +import { conclusionSection } from './conclusionSection/conclusionSection' export const FinancialStatementCemeteryForm: Form = buildForm({ id: 'FinancialStatementCemeteryForm', title: m.applicationTitle, mode: FormModes.DRAFT, - renderLastScreenButton: false, + renderLastScreenButton: true, logo: Logo, children: [ clientInfoSection, @@ -20,5 +21,6 @@ export const FinancialStatementCemeteryForm: Form = buildForm({ cemeteryCaretekerSection, cemeteryFinancialStatementSection, overviewSection, + conclusionSection, ], }) diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/overviewSection/index.ts b/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/overviewSection/index.ts index 3d1cb64989b8..1bd658de4cfc 100644 --- a/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/overviewSection/index.ts +++ b/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/overviewSection/index.ts @@ -1,9 +1,51 @@ -import { buildSection } from '@island.is/application/core' +import { + buildCheckboxField, + buildCustomField, + buildMultiField, + buildSection, + buildSubmitField, + YES, +} from '@island.is/application/core' import { m } from '../../../lib/messages' -import { overviewMultiField } from './oveviewMultiField' +import { DefaultEvents } from '@island.is/application/types' export const overviewSection = buildSection({ id: 'overviewSection', title: m.overviewSectionTitle, - children: [overviewMultiField], + children: [ + buildMultiField({ + id: 'overview', + title: m.yearlyOverview, + description: m.review, + children: [ + buildCustomField({ + id: 'overviewCemetryField', + title: '', + doesNotRequireAnswer: true, + component: 'CemeteryOverview', + }), + buildCheckboxField({ + id: 'approveOverview', + title: '', + options: [ + { + label: m.overviewCorrect, + value: YES, + }, + ], + }), + buildSubmitField({ + id: 'overview.submit', + title: '', + actions: [ + { + event: DefaultEvents.SUBMIT, + name: m.send, + type: 'primary', + }, + ], + }), + ], + }), + ], }) diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/overviewSection/oveviewMultiField.ts b/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/overviewSection/oveviewMultiField.ts deleted file mode 100644 index 4f93c70dd20a..000000000000 --- a/libs/application/templates/inao/financial-statement-cemetery/src/forms/applicationForm/overviewSection/oveviewMultiField.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { buildCustomField, buildMultiField } from '@island.is/application/core' -import { m } from '../../../lib/messages' - -export const overviewMultiField = buildMultiField({ - id: 'overview', - title: m.yearlyOverview, - description: m.review, - children: [ - buildCustomField({ - id: 'overviewCemetryField', - title: '', - doesNotRequireAnswer: true, - component: 'CemeteryOverview', - }), - ], -}) diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/forms/done/conclusionSection.ts b/libs/application/templates/inao/financial-statement-cemetery/src/forms/done/conclusionSection.ts deleted file mode 100644 index 532136f509bc..000000000000 --- a/libs/application/templates/inao/financial-statement-cemetery/src/forms/done/conclusionSection.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { - buildCustomField, - buildMultiField, - buildSection, -} from '@island.is/application/core' -import { m } from '../../lib/messages' - -export const conclusionSection = buildSection({ - id: 'conclusionSection', - title: '', - children: [ - buildMultiField({ - id: 'conclusion', - title: m.received, - children: [ - buildCustomField({ - id: 'overview', - component: 'Success', - title: m.applicationAccept, - }), - ], - }), - ], -}) diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/forms/done/index.ts b/libs/application/templates/inao/financial-statement-cemetery/src/forms/done/index.ts index e4ea0c21bc77..5c2dd8c0f237 100644 --- a/libs/application/templates/inao/financial-statement-cemetery/src/forms/done/index.ts +++ b/libs/application/templates/inao/financial-statement-cemetery/src/forms/done/index.ts @@ -1,11 +1,13 @@ import { buildForm } from '@island.is/application/core' import { Form, FormModes } from '@island.is/application/types' -import { conclusionSection } from './conclusionSection' import { m } from '../../lib/messages' +import { conclusionSection } from '../applicationForm/conclusionSection/conclusionSection' +import Logo from '../../components/Logo' export const done: Form = buildForm({ id: 'done', title: m.applicationAccept, mode: FormModes.COMPLETED, + logo: Logo, children: [conclusionSection], }) diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/forms/notAllowed/index.ts b/libs/application/templates/inao/financial-statement-cemetery/src/forms/notAllowed/index.ts index 64b658b27a8e..bc8aa7c1ae11 100644 --- a/libs/application/templates/inao/financial-statement-cemetery/src/forms/notAllowed/index.ts +++ b/libs/application/templates/inao/financial-statement-cemetery/src/forms/notAllowed/index.ts @@ -1,10 +1,26 @@ -import { buildForm } from '@island.is/application/core' -import { notAllowedSection } from './notAllowedSection' +import { + buildDescriptionField, + buildForm, + buildSection, +} from '@island.is/application/core' import Logo from '../../components/Logo' +import { m } from '../../lib/messages' export const notAllowedForm = buildForm({ id: 'notAllowedForm', title: '', logo: Logo, - children: [notAllowedSection], + children: [ + buildSection({ + id: 'notAllowedSection', + title: '', + children: [ + buildDescriptionField({ + id: 'notAllowedDescription', + title: m.notAllowedTitle, + description: m.notAllowedDescription, + }), + ], + }), + ], }) diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/forms/notAllowed/notAllowedSection.ts b/libs/application/templates/inao/financial-statement-cemetery/src/forms/notAllowed/notAllowedSection.ts deleted file mode 100644 index 5c8359dd2a4b..000000000000 --- a/libs/application/templates/inao/financial-statement-cemetery/src/forms/notAllowed/notAllowedSection.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { - buildDescriptionField, - buildSection, -} from '@island.is/application/core' -import { m } from '../../lib/messages' - -export const notAllowedSection = buildSection({ - id: 'notAllowedSection', - title: '', - children: [ - buildDescriptionField({ - id: 'notAllowedDescription', - title: m.notAllowedTitle, - description: m.notAllowedDescription, - }), - ], -}) diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/forms/prerequisites/index.ts b/libs/application/templates/inao/financial-statement-cemetery/src/forms/prerequisites/index.ts index e8f8d9631b70..4e69a430c00e 100644 --- a/libs/application/templates/inao/financial-statement-cemetery/src/forms/prerequisites/index.ts +++ b/libs/application/templates/inao/financial-statement-cemetery/src/forms/prerequisites/index.ts @@ -1,7 +1,19 @@ -import { buildForm } from '@island.is/application/core' -import { Form, FormModes } from '@island.is/application/types' +import { + buildDataProviderItem, + buildExternalDataProvider, + buildForm, + buildSection, + buildSubmitField, + coreMessages, +} from '@island.is/application/core' +import { DefaultEvents, Form, FormModes } from '@island.is/application/types' import Logo from '../../components/Logo' -import { prerequisitesSection } from './prerequsitesSection' +import { + CurrentUserTypeProvider, + IdentityApiProvider, + UserInfoApi, +} from '../../dataProviders' +import { m } from '../../lib/messages' export const PrerequisitesForm: Form = buildForm({ id: 'PrerequisitesForm', @@ -9,5 +21,47 @@ export const PrerequisitesForm: Form = buildForm({ mode: FormModes.NOT_STARTED, renderLastScreenButton: true, logo: Logo, - children: [prerequisitesSection], + children: [ + buildSection({ + id: 'ExternalDataSection', + title: '', + children: [ + buildExternalDataProvider({ + id: 'approveExternalData', + title: m.dataCollectionTitleUserCemetery, + checkboxLabel: m.dataCollectionCheckboxLabel, + dataProviders: [ + buildDataProviderItem({ + provider: IdentityApiProvider, + title: m.dataCollectionNationalRegistryTitle, + subTitle: m.dataCollectionNationalRegistrySubtitle, + }), + buildDataProviderItem({ + provider: UserInfoApi, + title: m.dataCollectionUserProfileTitle, + subTitle: m.dataCollectionUserProfileSubtitle, + }), + buildDataProviderItem({ + provider: CurrentUserTypeProvider, + title: m.dataCollectionUserFinancialInfoTitle, + subTitle: m.dataCollectionUserFinancialInfo, + }), + ], + submitField: buildSubmitField({ + id: 'submit', + placement: 'footer', + title: '', + refetchApplicationAfterSubmit: true, + actions: [ + { + event: DefaultEvents.SUBMIT, + name: coreMessages.buttonNext, + type: 'primary', + }, + ], + }), + }), + ], + }), + ], }) diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/forms/prerequisites/prerequsitesSection.ts b/libs/application/templates/inao/financial-statement-cemetery/src/forms/prerequisites/prerequsitesSection.ts deleted file mode 100644 index 6b9bed52110a..000000000000 --- a/libs/application/templates/inao/financial-statement-cemetery/src/forms/prerequisites/prerequsitesSection.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { - buildDataProviderItem, - buildExternalDataProvider, - buildSection, - buildSubmitField, - coreMessages, -} from '@island.is/application/core' -import { m } from '../../lib/messages' -import { - CurrentUserTypeProvider, - IndentityApiProvider, - UserInfoApi, -} from '../../dataProviders' -import { DefaultEvents } from '@island.is/application/types' - -export const prerequisitesSection = buildSection({ - id: 'ExternalDataSection', - title: '', - children: [ - buildExternalDataProvider({ - id: 'approveExternalData', - title: m.dataCollectionTitleUserCemetry, - checkboxLabel: m.dataCollectionCheckboxLabel, - dataProviders: [ - buildDataProviderItem({ - provider: IndentityApiProvider, - title: m.dataCollectionNationalRegistryTitle, - subTitle: m.dataCollectionNationalRegistrySubtitle, - }), - buildDataProviderItem({ - provider: UserInfoApi, - title: m.dataCollectionUserProfileTitle, - subTitle: m.dataCollectionUserProfileSubtitle, - }), - buildDataProviderItem({ - provider: CurrentUserTypeProvider, - title: m.dataCollectionUserFinancialInfoTitle, - subTitle: m.dataCollectionUserFinancialInfo, - }), - ], - submitField: buildSubmitField({ - id: 'submit', - placement: 'footer', - title: '', - refetchApplicationAfterSubmit: true, - actions: [ - { - event: DefaultEvents.SUBMIT, - name: coreMessages.buttonNext, - type: 'primary', - }, - ], - }), - }), - ], -}) diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/lib/dataSchema.ts b/libs/application/templates/inao/financial-statement-cemetery/src/lib/dataSchema.ts index b177ee8daac2..97379678b103 100644 --- a/libs/application/templates/inao/financial-statement-cemetery/src/lib/dataSchema.ts +++ b/libs/application/templates/inao/financial-statement-cemetery/src/lib/dataSchema.ts @@ -3,7 +3,11 @@ import { m } from './messages' import * as kennitala from 'kennitala' import { parsePhoneNumberFromString } from 'libphonenumber-js' import { BOARDMEMEBER, CARETAKER } from '../utils/constants' -import { getBoardmembersAndCaretakers } from '../utils/helpers' +import { + isPositiveNumberInString, + getBoardmembersAndCaretakers, +} from '../utils/helpers' +import { YES } from '@island.is/application/types' const FileSchema = z.object({ name: z.string(), @@ -11,117 +15,191 @@ const FileSchema = z.object({ url: z.string().optional(), }) -const checkIfNegative = (inputNumber: string) => { - if (Number(inputNumber) < 0) { - return false - } else { - return true - } -} - -const conditionalAbout = z.object({ - operatingYear: z.string().refine((x) => !!x, { params: m.required }), -}) - const cemeteryOperation = z.object({ incomeLimit: z.string().optional(), }) -const cemeteryLiability = z.object({ - longTerm: z - .string() - .refine((x) => !!x, { params: m.required }) - .refine((x) => checkIfNegative(x), { params: m.negativeNumberError }), - shortTerm: z - .string() - .refine((x) => !!x, { params: m.required }) - .refine((x) => checkIfNegative(x), { params: m.negativeNumberError }), - total: z.string().refine((x) => !!x, { params: m.required }), +const conditionalAbout = z.object({ + operatingYear: z.string().refine((x) => !!x, { params: m.required }), }) -const cemeteryAsset = z.object({ - currentAssets: z - .string() - .refine((x) => !!x, { params: m.required }) - .refine((x) => checkIfNegative(x), { params: m.negativeNumberError }), - fixedAssetsTotal: z +const about = z.object({ + nationalId: z .string() - .refine((x) => !!x, { params: m.required }) - .refine((x) => checkIfNegative(x), { params: m.negativeNumberError }), - total: z.string(), + .refine((val) => (val ? kennitala.isValid(val) : false), { + params: m.nationalIdError, + }), + fullName: z.string().refine((x) => !!x, { params: m.required }), + powerOfAttorneyNationalId: z.string().optional(), + powerOfAttorneyName: z.string().optional(), + phoneNumber: z.string().refine( + (p) => { + const phoneNumber = parsePhoneNumberFromString(p, 'IS') + return phoneNumber && phoneNumber.isValid() + }, + { params: m.dataSchemePhoneNumber }, + ), + email: z.string().email(), }) +// Key numbers - Income and Expenses - Income const cemeteryIncome = z.object({ careIncome: z .string() .refine((x) => !!x, { params: m.required }) - .refine((x) => checkIfNegative(x), { params: m.negativeNumberError }), + .refine((x) => isPositiveNumberInString(x), { + params: m.negativeNumberError, + }), burialRevenue: z .string() .refine((x) => !!x, { params: m.required }) - .refine((x) => checkIfNegative(x), { params: m.negativeNumberError }), + .refine((x) => isPositiveNumberInString(x), { + params: m.negativeNumberError, + }), grantFromTheCemeteryFund: z .string() .refine((x) => !!x, { params: m.required }) - .refine((x) => checkIfNegative(x), { params: m.negativeNumberError }), + .refine((x) => isPositiveNumberInString(x), { + params: m.negativeNumberError, + }), otherIncome: z .string() .refine((x) => !!x, { params: m.required }) - .refine((x) => checkIfNegative(x), { params: m.negativeNumberError }), + .refine((x) => isPositiveNumberInString(x), { + params: m.negativeNumberError, + }), total: z.string(), }) +// Key numbers - Income and Expenses - Expenses const cemeteryExpense = z.object({ payroll: z .string() .refine((x) => !!x, { params: m.required }) - .refine((x) => checkIfNegative(x), { params: m.negativeNumberError }), + .refine((x) => isPositiveNumberInString(x), { + params: m.negativeNumberError, + }), funeralCost: z .string() .refine((x) => !!x, { params: m.required }) - .refine((x) => checkIfNegative(x), { params: m.negativeNumberError }), + .refine((x) => isPositiveNumberInString(x), { + params: m.negativeNumberError, + }), chapelExpense: z .string() .refine((x) => !!x, { params: m.required }) - .refine((x) => checkIfNegative(x), { params: m.negativeNumberError }), + .refine((x) => isPositiveNumberInString(x), { + params: m.negativeNumberError, + }), donationsToOther: z .string() .refine((x) => !!x, { params: m.required }) - .refine((x) => checkIfNegative(x), { params: m.negativeNumberError }), + .refine((x) => isPositiveNumberInString(x), { + params: m.negativeNumberError, + }), cemeteryFundExpense: z .string() .refine((x) => !!x, { params: m.required }) - .refine((x) => checkIfNegative(x), { params: m.negativeNumberError }), + .refine((x) => isPositiveNumberInString(x), { + params: m.negativeNumberError, + }), otherOperationCost: z .string() .refine((x) => !!x, { params: m.required }) - .refine((x) => checkIfNegative(x), { params: m.negativeNumberError }), + .refine((x) => isPositiveNumberInString(x), { + params: m.negativeNumberError, + }), depreciation: z .string() .refine((x) => !!x, { params: m.required }) - .refine((x) => checkIfNegative(x), { params: m.negativeNumberError }), + .refine((x) => isPositiveNumberInString(x), { + params: m.negativeNumberError, + }), total: z.string(), }) -const about = z.object({ - nationalId: z +// Key numbers - Capital numbers +const capitalNumbers = z.object({ + capitalIncome: z .string() - .refine((val) => (val ? kennitala.isValid(val) : false), { - params: m.nationalIdError, + .refine((x) => !!x, { params: m.required }) + .refine((x) => isPositiveNumberInString(x), { + params: m.negativeNumberError, }), - fullName: z.string().refine((x) => !!x, { params: m.required }), - powerOfAttorneyNationalId: z.string().optional(), - powerOfAttorneyName: z.string().optional(), - phoneNumber: z.string().refine( - (p) => { - const phoneNumber = parsePhoneNumberFromString(p, 'IS') - return phoneNumber && phoneNumber.isValid() - }, - { params: m.dataSchemePhoneNumber }, - ), - email: z.string().email(), + capitalCost: z + .string() + .refine((x) => !!x, { params: m.required }) + .refine((x) => isPositiveNumberInString(x), { + params: m.negativeNumberError, + }), + total: z.string(), }) +// Key numbers - Equity and Liability - Assets +const cemeteryAsset = z.object({ + currentAssets: z + .string() + .refine((x) => !!x, { params: m.required }) + .refine((x) => isPositiveNumberInString(x), { + params: m.negativeNumberError, + }), + fixedAssetsTotal: z + .string() + .refine((x) => !!x, { params: m.required }) + .refine((x) => isPositiveNumberInString(x), { + params: m.negativeNumberError, + }), +}) + +// Key numbers - Equity and Liability - Liabilities +const cemeteryLiability = z.object({ + longTerm: z + .string() + .refine((x) => !!x, { params: m.required }) + .refine((x) => isPositiveNumberInString(x), { + params: m.negativeNumberError, + }), + shortTerm: z + .string() + .refine((x) => !!x, { params: m.required }) + .refine((x) => isPositiveNumberInString(x), { + params: m.negativeNumberError, + }), +}) + +// Key numbers - Equity and Liability - Equity +const cemeteryEquity = z.object({ + equityAtTheBeginningOfTheYear: z + .string() + .refine((x) => !!x, { params: m.required }), + operationResult: z.string(), + revaluationDueToPriceChanges: z + .string() + .refine((x) => !!x, { params: m.required }) + .refine((x) => isPositiveNumberInString(x), { + params: m.negativeNumberError, + }), + reevaluateOther: z + .string() + .refine((x) => !!x, { params: m.required }) + .refine((x) => isPositiveNumberInString(x), { + params: m.negativeNumberError, + }), + total: z.string(), +}) + +// Key numbers - Equity and Liability - Equity and Liabilities totals +const equityAndLiabilitiesTotals = z + .object({ + assetsTotal: z.string(), + liabilitiesTotal: z.string(), + equityAndLiabilitiesTotal: z.string(), + }) + .refine((x) => x.assetsTotal === x.equityAndLiabilitiesTotal, { + message: 'equityAndLiabilities.total must match assets.total', + path: ['equityAndLiabilitiesTotals', 'equityAndLiabilitiesTotal'], + }) + const cemeteryCaretaker = z .array( z.object({ @@ -178,54 +256,23 @@ const cemeteryCaretaker = z { params: m.errormemberNotUnique }, ) -const cemeteryEquity = z.object({ - equityAtTheBeginningOfTheYear: z - .string() - .refine((x) => !!x, { params: m.required }), - operationResult: z.string(), - revaluationDueToPriceChanges: z - .string() - .refine((x) => !!x, { params: m.required }) - .refine((x) => checkIfNegative(x), { params: m.negativeNumberError }), - reevaluateOther: z - .string() - .refine((x) => !!x, { params: m.required }) - .refine((x) => checkIfNegative(x), { params: m.negativeNumberError }), - total: z.string(), -}) - -const equityAndLiabilities = z.object({ - total: z.string(), -}) - -const capitalNumbers = z.object({ - capitalIncome: z - .string() - .refine((x) => !!x, { params: m.required }) - .refine((x) => checkIfNegative(x), { params: m.negativeNumberError }), - capitalCost: z - .string() - .refine((x) => !!x, { params: m.required }) - .refine((x) => checkIfNegative(x), { params: m.negativeNumberError }), - total: z.string(), -}) - export const dataSchema = z.object({ - conditionalAbout, - about, approveExternalData: z.boolean().refine((v) => v), cemeteryOperation, - cemeteryAsset, - cemeteryLiability, + conditionalAbout, + about, cemeteryIncome, cemeteryExpense, + capitalNumbers, + cemeteryAsset, + cemeteryLiability, + cemeteryEquity, + equityAndLiabilitiesTotals, + cemeteryCaretaker, attachments: z.object({ file: z.array(FileSchema).nonempty(), }), - cemeteryCaretaker, - cemeteryEquity, - equityAndLiabilities, - capitalNumbers, + approveOverview: z.array(z.literal(YES)).length(1), }) export type FinancialStatementCemetery = z.TypeOf diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/lib/financialStatementCemeteryTemplate.ts b/libs/application/templates/inao/financial-statement-cemetery/src/lib/financialStatementCemeteryTemplate.ts index a92fbc786cb5..0c851e4c2dd1 100644 --- a/libs/application/templates/inao/financial-statement-cemetery/src/lib/financialStatementCemeteryTemplate.ts +++ b/libs/application/templates/inao/financial-statement-cemetery/src/lib/financialStatementCemeteryTemplate.ts @@ -19,7 +19,7 @@ import { AuthDelegationType } from '@island.is/shared/types' import { dataSchema } from './dataSchema' import { CurrentUserTypeProvider, - IndentityApiProvider, + IdentityApiProvider, NationalRegistryUserApi, UserInfoApi, } from '../dataProviders' @@ -79,7 +79,7 @@ const FinancialStatementCemeteryTemplate: ApplicationTemplate< delete: true, api: [ CurrentUserTypeProvider, - IndentityApiProvider, + IdentityApiProvider, NationalRegistryUserApi, UserInfoApi, ], diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/lib/messages.ts b/libs/application/templates/inao/financial-statement-cemetery/src/lib/messages.ts index a78cd7e4296a..7bce9e4fffe1 100644 --- a/libs/application/templates/inao/financial-statement-cemetery/src/lib/messages.ts +++ b/libs/application/templates/inao/financial-statement-cemetery/src/lib/messages.ts @@ -375,7 +375,7 @@ export const m = defineMessages({ defaultMessage: 'Gagnaöflun', description: 'Title for data collection section', }, - dataCollectionTitleUserCemetry: { + dataCollectionTitleUserCemetery: { id: 'fsc.application:applicationDataCollectionTitleUserCemetry', defaultMessage: 'Gagnaöflun vegna skila ĂĄrsreiknings kirkjugarĂ°s', description: 'Title for data collection section', @@ -682,4 +682,10 @@ export const m = defineMessages({ 'Ef ĂŸĂș telur aĂ° ĂŸessi kennitala ĂŠtti aĂ° vera skrĂĄĂ° sem kirkjugarĂ°ur ĂŸĂĄ bendum viĂ° ĂŸĂ©r ĂĄ aĂ° hafa samband viĂ° RĂ­kisendurskoĂ°un Ă­ sĂ­ma 448 8800', description: 'Descriptionwhen user is not allowed to apply', }, + conclusionAlertMessage: { + id: 'fsc.application:conclusionAlertMessage', + defaultMessage: + 'Ársreikning fyrir rekstrarĂĄriĂ° {value1} hefur veriĂ° skilaĂ°', + description: 'Conclusion alert message', + }, }) diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/types/types.ts b/libs/application/templates/inao/financial-statement-cemetery/src/types/types.ts index 274aeaf840e3..53e29233513f 100644 --- a/libs/application/templates/inao/financial-statement-cemetery/src/types/types.ts +++ b/libs/application/templates/inao/financial-statement-cemetery/src/types/types.ts @@ -36,3 +36,13 @@ export enum Roles { APPLICANT = 'applicant', NOTALLOWED = 'notAllowed', } + +type InaoConfigItem = { + __typename: string + key: string + value: string +} + +export type AuditConfig = { + financialStatementsInaoConfig: Array +} diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/utils/constants.ts b/libs/application/templates/inao/financial-statement-cemetery/src/utils/constants.ts index a7981b212bbc..2c0cb3bb6afe 100644 --- a/libs/application/templates/inao/financial-statement-cemetery/src/utils/constants.ts +++ b/libs/application/templates/inao/financial-statement-cemetery/src/utils/constants.ts @@ -63,16 +63,20 @@ export const CEMETERYEQUITIESANDLIABILITIESIDS = { equityPrefix: 'cemeteryEquity', currentAssets: 'cemeteryAsset.currentAssets', fixedAssetsTotal: 'cemeteryAsset.fixedAssetsTotal', - assetTotal: 'cemeteryAsset.total', longTerm: 'cemeteryLiability.longTerm', shortTerm: 'cemeteryLiability.shortTerm', - liabilityTotal: 'cemeteryLiability.total', equityAtTheBeginningOfTheYear: 'cemeteryEquity.equityAtTheBeginningOfTheYear', revaluationDueToPriceChanges: 'cemeteryEquity.revaluationDueToPriceChanges', reevaluateOther: 'cemeteryEquity.reevaluateOther', operationResult: 'cemeteryEquity.operationResult', equityTotal: 'cemeteryEquity.total', - totalEquityAndLiabilities: 'equityAndLiabilities.total', +} + +export const EQUITYANDLIABILITIESTOTALS = { + assetsTotal: 'equityAndLiabilitiesTotals.assetsTotal', + liabilitiesTotal: 'equityAndLiabilitiesTotals.liabilitiesTotal', + equityAndLiabilitiesTotal: + 'equityAndLiabilitiesTotals.equityAndLiabilitiesTotal', } export const OPERATINGCOST = { diff --git a/libs/application/templates/inao/financial-statement-cemetery/src/utils/helpers.ts b/libs/application/templates/inao/financial-statement-cemetery/src/utils/helpers.ts index c7b382ede33f..aec943d790e8 100644 --- a/libs/application/templates/inao/financial-statement-cemetery/src/utils/helpers.ts +++ b/libs/application/templates/inao/financial-statement-cemetery/src/utils/helpers.ts @@ -1,10 +1,21 @@ import { ExternalData, FormValue } from '@island.is/application/types' import { getValueViaPath } from '@island.is/application/core' -import { BOARDMEMEBER, CARETAKER, TOTAL } from './constants' +import { + BOARDMEMEBER, + CAPITALNUMBERS, + CARETAKER, + CemeteriesBackwardLimit, + CemeteriesYearAllowed, + CEMETERYEQUITIESANDLIABILITIESIDS, + CEMETERYOPERATIONIDS, + EQUITYANDLIABILITIESTOTALS, + OPERATINGCOST, + TOTAL, +} from './constants' import { FinancialStatementCemetery } from '../lib/dataSchema' import getYear from 'date-fns/getYear' import subYears from 'date-fns/subYears' -import { BoardMember, Config, FSIUSERTYPE } from '../types/types' +import { AuditConfig, BoardMember, Config, FSIUSERTYPE } from '../types/types' export const getTotal = (values: Record, key: string) => { if (!values[key]) { @@ -27,23 +38,6 @@ export const currencyStringToNumber = (str: string) => { return parseInt(cleanString, 10) } -export const getCurrentUserType = ( - answers: FormValue, - externalData: ExternalData, -) => { - const fakeUserType: FSIUSERTYPE | undefined = getValueViaPath( - answers, - 'fakeData.options', - ) - - const currentUserType: FSIUSERTYPE | undefined = getValueViaPath( - externalData, - 'getUserType.data.value', - ) - - return fakeUserType ? fakeUserType : currentUserType -} - export const getBoardmembersAndCaretakers = (members: Array) => { const careTakers = members ?.filter((member) => member.role === CARETAKER) @@ -55,30 +49,44 @@ export const getBoardmembersAndCaretakers = (members: Array) => { return { careTakers, boardMembers } } -export const isCemetryUnderFinancialLimit = ( - answers: FormValue, - externalData: ExternalData, -) => { - const userType = getCurrentUserType(answers, externalData) - const applicationAnswers = answers as FinancialStatementCemetery - const careTakerLimit = - applicationAnswers.cemeteryOperation?.incomeLimit ?? '0' - const fixedAssetsTotal = applicationAnswers.cemeteryAsset?.fixedAssetsTotal - const isCemetry = userType === FSIUSERTYPE.CEMETRY - const totalIncome = isCemetry ? applicationAnswers.cemeteryIncome?.total : '0' - const longTermDebt = applicationAnswers.cemeteryLiability?.longTerm - const isUnderLimit = currencyStringToNumber(totalIncome) < careTakerLimit - if ( - isCemetry && - isUnderLimit && - fixedAssetsTotal === '0' && - longTermDebt === '0' - ) { +export const isCemetryUnderFinancialLimit = (answers: FormValue) => { + const totalIncome = + getValueViaPath(answers, 'cemeteryIncome.total') || '0' + const incomeLimit = + getValueViaPath(answers, 'cemeteryOperation.incomeLimit') || '0' + const fixedAssetsTotal = + getValueViaPath(answers, 'cemeteryAsset.fixedAssetsTotal') || '0' + const longTermDebt = + getValueViaPath(answers, 'cemeteryLiability.longTerm') || '0' + const isUnderLimit = Number(totalIncome) < Number(incomeLimit) + + if (isUnderLimit && fixedAssetsTotal === '0' && longTermDebt === '0') { return true } + return false } +export const getYearOptions = (data: AuditConfig) => { + let yearLimit: string | undefined + let countYearBackwardsFrom: string | undefined + data.financialStatementsInaoConfig.forEach((item) => { + if (item.key === CemeteriesBackwardLimit) { + yearLimit = item.value + } + + if (item.key === CemeteriesYearAllowed) { + countYearBackwardsFrom = item.value + } + }) + + if (!countYearBackwardsFrom) { + return [] + } + + return possibleOperatingYears(yearLimit || '1', countYearBackwardsFrom) +} + export const possibleOperatingYears = ( yearLimit: string, countYearBackwardsFrom: string, @@ -103,3 +111,218 @@ export const formatCurrency = (answer?: string) => { if (!answer) return '0. kr' return answer.replace(/\B(?=(\d{3})+(?!\d))/g, '.') + ' kr.' } + +export const isPositiveNumberInString = (input: string) => { + return Number(input) > 0 +} + +export const sumIncome = (answers: FormValue) => { + const careIncome = + getValueViaPath(answers, CEMETERYOPERATIONIDS.careIncome) || '0' + const burialRevenue = + getValueViaPath(answers, CEMETERYOPERATIONIDS.burialRevenue) || '0' + const grantFromTheCemeteryFund = + getValueViaPath( + answers, + CEMETERYOPERATIONIDS.grantFromTheCemeteryFund, + ) || '0' + const otherIncome = + getValueViaPath(answers, CEMETERYOPERATIONIDS.otherIncome) || '0' + + return `${ + Number(careIncome) + + Number(burialRevenue) + + Number(grantFromTheCemeteryFund) + + Number(otherIncome) + }` +} + +export const sumExpenses = (answers: FormValue) => { + const payroll = getValueViaPath(answers, CEMETERYOPERATIONIDS.payroll) + const funeralCost = + getValueViaPath(answers, CEMETERYOPERATIONIDS.funeralCost) || '0' + const chapelExpense = + getValueViaPath(answers, CEMETERYOPERATIONIDS.chapelExpense) || '0' + const donationsToCemeteryFund = + getValueViaPath( + answers, + CEMETERYOPERATIONIDS.donationsToCemeteryFund, + ) || '0' + const donationsToOther = + getValueViaPath(answers, CEMETERYOPERATIONIDS.donationsToOther) || + '0' + const otherOperationCost = + getValueViaPath(answers, CEMETERYOPERATIONIDS.otherOperationCost) || + '0' + const depreciation = + getValueViaPath(answers, CEMETERYOPERATIONIDS.depreciation) || '0' + + return `${ + Number(payroll) + + Number(funeralCost) + + Number(chapelExpense) + + Number(donationsToCemeteryFund) + + Number(donationsToOther) + + Number(otherOperationCost) + + Number(depreciation) + }` +} + +export const sumOperatingResults = (answers: FormValue) => { + const income = + getValueViaPath(answers, CEMETERYOPERATIONIDS.totalIncome) || '0' + const expenses = + getValueViaPath(answers, CEMETERYOPERATIONIDS.totalExpense) || '0' + + return `${Number(income) - Number(expenses)}` +} + +export const sumCapitalNumbers = (answers: FormValue) => { + const capitalIncome = + getValueViaPath(answers, CAPITALNUMBERS.capitalIncome) || '0' + const capitalCost = + getValueViaPath(answers, CAPITALNUMBERS.capitalCost) || '0' + return `${Number(capitalIncome) - Number(capitalCost)}` +} + +export const sumAssets = (answers: FormValue) => { + const fixedAssetsTotal = + getValueViaPath( + answers, + CEMETERYEQUITIESANDLIABILITIESIDS.fixedAssetsTotal, + ) || '0' + const currentAssets = + getValueViaPath( + answers, + CEMETERYEQUITIESANDLIABILITIESIDS.currentAssets, + ) || '0' + return `${Number(fixedAssetsTotal) + Number(currentAssets)}` +} + +export const sumLiabilities = (answers: FormValue) => { + const longTerm = + getValueViaPath( + answers, + CEMETERYEQUITIESANDLIABILITIESIDS.longTerm, + ) || '0' + const shortTerm = + getValueViaPath( + answers, + CEMETERYEQUITIESANDLIABILITIESIDS.shortTerm, + ) || '0' + return `${Number(longTerm) + Number(shortTerm)}` +} + +export const operationResult = (answers: FormValue) => { + const operatingTotalCost = + getValueViaPath(answers, OPERATINGCOST.total) || '0' + const capitalTotal = + getValueViaPath(answers, CAPITALNUMBERS.total) || '0' + return `${Number(operatingTotalCost) + Number(capitalTotal)}` +} + +export const sumTotalEquity = (answers: FormValue) => { + const equityAtTheBeginningOfTheYear = + getValueViaPath( + answers, + CEMETERYEQUITIESANDLIABILITIESIDS.equityAtTheBeginningOfTheYear, + ) || '0' + const revaluationDueToPriceChanges = + getValueViaPath( + answers, + CEMETERYEQUITIESANDLIABILITIESIDS.revaluationDueToPriceChanges, + ) || '0' + const reevaluateOther = + getValueViaPath( + answers, + CEMETERYEQUITIESANDLIABILITIESIDS.reevaluateOther, + ) || '0' + const operationResult = + getValueViaPath( + answers, + CEMETERYEQUITIESANDLIABILITIESIDS.operationResult, + ) || '0' + + return `${ + Number(equityAtTheBeginningOfTheYear) + + Number(revaluationDueToPriceChanges) + + Number(reevaluateOther) + + Number(operationResult) + }` +} + +export const sumTotalEquityAndLiabilities = (answers: FormValue) => { + const liabilityTotal = + getValueViaPath( + answers, + EQUITYANDLIABILITIESTOTALS.liabilitiesTotal, + ) || '0' + const totalEquity = + getValueViaPath( + answers, + CEMETERYEQUITIESANDLIABILITIESIDS.equityTotal, + ) || '0' + + return `${Number(totalEquity) + Number(liabilityTotal)}` +} + +export const showEquitiesAndLiabilitiesAlert = (answers: FormValue) => { + const fixedAssetsTotal = + getValueViaPath( + answers, + CEMETERYEQUITIESANDLIABILITIESIDS.fixedAssetsTotal, + ) || '0' + const currentAssets = + getValueViaPath( + answers, + CEMETERYEQUITIESANDLIABILITIESIDS.currentAssets, + ) || '0' + const longTerm = + getValueViaPath( + answers, + CEMETERYEQUITIESANDLIABILITIESIDS.longTerm, + ) || '0' + const shortTerm = + getValueViaPath( + answers, + CEMETERYEQUITIESANDLIABILITIESIDS.shortTerm, + ) || '0' + const equityAtTheBeginningOfTheYear = + getValueViaPath( + answers, + CEMETERYEQUITIESANDLIABILITIESIDS.equityAtTheBeginningOfTheYear, + ) || '0' + const revaluationDueToPriceChanges = + getValueViaPath( + answers, + CEMETERYEQUITIESANDLIABILITIESIDS.revaluationDueToPriceChanges, + ) || '0' + const reevaluateOther = + getValueViaPath( + answers, + CEMETERYEQUITIESANDLIABILITIESIDS.reevaluateOther, + ) || '0' + + const totalAssets = + getValueViaPath(answers, EQUITYANDLIABILITIESTOTALS.assetsTotal) || + '0' + const totalEquityAndLiabilities = + getValueViaPath( + answers, + EQUITYANDLIABILITIESTOTALS.equityAndLiabilitiesTotal, + ) || '0' + + if ( + !fixedAssetsTotal || + !currentAssets || + !longTerm || + !shortTerm || + !equityAtTheBeginningOfTheYear || + !revaluationDueToPriceChanges || + !reevaluateOther || + !operationResult + ) { + return false + } + return totalAssets !== totalEquityAndLiabilities +} diff --git a/libs/application/templates/inao/financial-statement-cemetery/tsconfig.json b/libs/application/templates/inao/financial-statement-cemetery/tsconfig.json index 52f7c83105fc..04538292f729 100644 --- a/libs/application/templates/inao/financial-statement-cemetery/tsconfig.json +++ b/libs/application/templates/inao/financial-statement-cemetery/tsconfig.json @@ -1,8 +1,10 @@ { - "extends": "../../../../tsconfig.base.json", "compilerOptions": { "jsx": "react-jsx", - "allowJs": false + "allowJs": false, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true }, "files": [], "include": [], @@ -13,5 +15,6 @@ { "path": "./tsconfig.spec.json" } - ] + ], + "extends": "../../../../../tsconfig.base.json" } diff --git a/libs/application/templates/inao/financial-statement-cemetery/tsconfig.lib.json b/libs/application/templates/inao/financial-statement-cemetery/tsconfig.lib.json index 4362bec84a9a..d17804e8805a 100644 --- a/libs/application/templates/inao/financial-statement-cemetery/tsconfig.lib.json +++ b/libs/application/templates/inao/financial-statement-cemetery/tsconfig.lib.json @@ -1,23 +1,13 @@ { "extends": "./tsconfig.json", "compilerOptions": { - "outDir": "../../../../dist/out-tsc", + "outDir": "../../../../../dist/out-tsc", "types": ["node"] }, "files": [ "../../../../../node_modules/@nx/react/typings/cssmodule.d.ts", "../../../../../node_modules/@nx/react/typings/image.d.ts" ], - "exclude": [ - "jest.config.ts", - "src/**/*.spec.ts", - "src/**/*.test.ts", - "src/**/*.spec.tsx", - "src/**/*.test.tsx", - "src/**/*.spec.js", - "src/**/*.test.js", - "src/**/*.spec.jsx", - "src/**/*.test.jsx" - ], + "exclude": ["/**/*.spec.ts", "/**/*.spec.tsx"], "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"] } diff --git a/libs/application/templates/inao/financial-statement-individual-election/src/dataProviders/index.ts b/libs/application/templates/inao/financial-statement-individual-election/src/dataProviders/index.ts index 36303255e230..d537ed3dfa93 100644 --- a/libs/application/templates/inao/financial-statement-individual-election/src/dataProviders/index.ts +++ b/libs/application/templates/inao/financial-statement-individual-election/src/dataProviders/index.ts @@ -3,7 +3,7 @@ import { UserProfileApi } from '@island.is/application/types' export { NationalRegistryUserApi, - IdentityApi as IndentityApiProvider, + IdentityApi as IdentityApiProvider, } from '@island.is/application/types' export const CurrentUserTypeProvider = defineTemplateApi({ action: 'getUserType', diff --git a/libs/application/templates/inao/financial-statement-individual-election/src/forms/prerequisites/prerequisitesSection.ts b/libs/application/templates/inao/financial-statement-individual-election/src/forms/prerequisites/prerequisitesSection.ts index e9820806c3d6..eb7e898460aa 100644 --- a/libs/application/templates/inao/financial-statement-individual-election/src/forms/prerequisites/prerequisitesSection.ts +++ b/libs/application/templates/inao/financial-statement-individual-election/src/forms/prerequisites/prerequisitesSection.ts @@ -8,7 +8,7 @@ import { import { m } from '../../lib/utils/messages' import { CurrentUserTypeProvider, - IndentityApiProvider, + IdentityApiProvider, UserInfoApi, } from '../../dataProviders' import { DefaultEvents } from '@island.is/application/types' @@ -23,7 +23,7 @@ export const prerequisitesSection = buildSection({ checkboxLabel: m.dataCollectionCheckboxLabel, dataProviders: [ buildDataProviderItem({ - provider: IndentityApiProvider, + provider: IdentityApiProvider, title: m.dataCollectionNationalRegistryTitle, subTitle: m.dataCollectionNationalRegistrySubtitle, }), diff --git a/libs/application/templates/inao/financial-statement-individual-election/src/lib/financial-statement-individual-election.ts b/libs/application/templates/inao/financial-statement-individual-election/src/lib/financial-statement-individual-election.ts index 83e1e625a391..db241a94b205 100644 --- a/libs/application/templates/inao/financial-statement-individual-election/src/lib/financial-statement-individual-election.ts +++ b/libs/application/templates/inao/financial-statement-individual-election/src/lib/financial-statement-individual-election.ts @@ -17,7 +17,7 @@ import { import { CurrentUserTypeProvider, - IndentityApiProvider, + IdentityApiProvider, NationalRegistryUserApi, UserInfoApi, } from '../dataProviders' @@ -77,7 +77,7 @@ const FinancialStatementIndividualElectionTemplate: ApplicationTemplate< delete: true, api: [ CurrentUserTypeProvider, - IndentityApiProvider, + IdentityApiProvider, NationalRegistryUserApi, UserInfoApi, ], diff --git a/libs/application/templates/inao/financial-statement-political-party/README.md b/libs/application/templates/inao/financial-statement-political-party/README.md index 3961bb865298..e962ad62bf8e 100644 --- a/libs/application/templates/inao/financial-statement-political-party/README.md +++ b/libs/application/templates/inao/financial-statement-political-party/README.md @@ -1,6 +1,8 @@ # application-templates-inao-financial-statement-political-party -This library was generated with [Nx](https://nx.dev). +This application turns in financial statements for political parties to the The Icelandic National Audit Office (RĂ­kisendurskoĂ°un, INAO for short). Political parties are required to turn in their financial statements before the 31. of October each year. + +To test this application in the dev environment, you can log in as GervimaĂ°ur FĂŠreyjar (010-2399) and swap to the procure of 65° Arctic. Arctic is registered as a political party with the INAO dev service and should be able to enter and go through the application. ## Running unit tests diff --git a/libs/application/templates/inao/financial-statement-political-party/src/dataProviders/index.ts b/libs/application/templates/inao/financial-statement-political-party/src/dataProviders/index.ts index 36303255e230..d537ed3dfa93 100644 --- a/libs/application/templates/inao/financial-statement-political-party/src/dataProviders/index.ts +++ b/libs/application/templates/inao/financial-statement-political-party/src/dataProviders/index.ts @@ -3,7 +3,7 @@ import { UserProfileApi } from '@island.is/application/types' export { NationalRegistryUserApi, - IdentityApi as IndentityApiProvider, + IdentityApi as IdentityApiProvider, } from '@island.is/application/types' export const CurrentUserTypeProvider = defineTemplateApi({ action: 'getUserType', diff --git a/libs/application/templates/inao/financial-statement-political-party/src/forms/prerequsites/prerequsitesSection.ts b/libs/application/templates/inao/financial-statement-political-party/src/forms/prerequsites/prerequsitesSection.ts index 40dca0ccb508..c10d83771cc3 100644 --- a/libs/application/templates/inao/financial-statement-political-party/src/forms/prerequsites/prerequsitesSection.ts +++ b/libs/application/templates/inao/financial-statement-political-party/src/forms/prerequsites/prerequsitesSection.ts @@ -8,7 +8,7 @@ import { import { m } from '../../lib/messages' import { CurrentUserTypeProvider, - IndentityApiProvider, + IdentityApiProvider, UserInfoApi, } from '../../dataProviders' import { DefaultEvents } from '@island.is/application/types' @@ -23,7 +23,7 @@ export const prerequisitesSection = buildSection({ checkboxLabel: m.dataCollectionCheckboxLabel, dataProviders: [ buildDataProviderItem({ - provider: IndentityApiProvider, + provider: IdentityApiProvider, title: m.dataCollectionNationalRegistryTitle, subTitle: m.dataCollectionNationalRegistrySubtitle, }), diff --git a/libs/application/templates/inao/financial-statement-political-party/src/lib/financialStatementPoliticalPartyTemplate.ts b/libs/application/templates/inao/financial-statement-political-party/src/lib/financialStatementPoliticalPartyTemplate.ts index 627860da4841..3025138a3546 100644 --- a/libs/application/templates/inao/financial-statement-political-party/src/lib/financialStatementPoliticalPartyTemplate.ts +++ b/libs/application/templates/inao/financial-statement-political-party/src/lib/financialStatementPoliticalPartyTemplate.ts @@ -19,7 +19,7 @@ import { } from '@island.is/application/core' import { CurrentUserTypeProvider, - IndentityApiProvider, + IdentityApiProvider, NationalRegistryUserApi, UserInfoApi, } from '../dataProviders' @@ -81,7 +81,7 @@ const FinancialStatementPoliticalPartyTemplate: ApplicationTemplate< delete: true, api: [ CurrentUserTypeProvider, - IndentityApiProvider, + IdentityApiProvider, NationalRegistryUserApi, UserInfoApi, ], @@ -122,7 +122,7 @@ const FinancialStatementPoliticalPartyTemplate: ApplicationTemplate< delete: true, api: [ CurrentUserTypeProvider, - IndentityApiProvider, + IdentityApiProvider, NationalRegistryUserApi, UserInfoApi, ], From 1073e70c28f1bdca94f308f2f26fd660716b14b2 Mon Sep 17 00:00:00 2001 From: mannipje <135017126+mannipje@users.noreply.github.com> Date: Tue, 10 Dec 2024 09:16:51 +0000 Subject: [PATCH 76/85] fix(web): Fix query for generic and team member lists (#17115) * Fix query for generic and team member lists * Add tags to be searchable in gengeric list search * Add title to be searchable in team member list search --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- libs/cms/src/lib/cms.elasticsearch.service.ts | 12 ++++-------- .../search/importers/genericListItem.service.ts | 4 ++++ .../src/lib/search/importers/teamList.service.ts | 15 ++++++++++++--- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/libs/cms/src/lib/cms.elasticsearch.service.ts b/libs/cms/src/lib/cms.elasticsearch.service.ts index 6b2b5c5d96a3..e35685d85964 100644 --- a/libs/cms/src/lib/cms.elasticsearch.service.ts +++ b/libs/cms/src/lib/cms.elasticsearch.service.ts @@ -509,7 +509,9 @@ export class CmsElasticsearchService { }, ] - let queryString = input.queryString ? input.queryString.toLowerCase() : '' + let queryString = input.queryString + ? input.queryString.trim().toLowerCase() + : '' if (input.lang === 'is') { queryString = queryString.replace('`', '') @@ -520,6 +522,7 @@ export class CmsElasticsearchService { query: queryString + '*', fields: ['title^100', 'content'], analyze_wildcard: true, + default_operator: 'and', }, }) @@ -531,15 +534,8 @@ export class CmsElasticsearchService { order: SortDirection.DESC, }, }, - // Sort items with equal values by ascending title order - { 'title.sort': { order: SortDirection.ASC } }, ] - // Order by score first in case there is a query string - if (queryString.length > 0 && queryString !== '*') { - sort.unshift('_score') - } - if (input.tags && input.tags.length > 0 && input.tagGroups) { must = must.concat( generateGenericTagGroupQueries(input.tags, input.tagGroups), diff --git a/libs/cms/src/lib/search/importers/genericListItem.service.ts b/libs/cms/src/lib/search/importers/genericListItem.service.ts index a55d8e21905f..adf56bed139e 100644 --- a/libs/cms/src/lib/search/importers/genericListItem.service.ts +++ b/libs/cms/src/lib/search/importers/genericListItem.service.ts @@ -51,6 +51,10 @@ export class GenericListItemSyncService ) } + for (const tag of mapped.filterTags ?? []) { + contentSections.push(tag.title) + } + const content = contentSections.join(' ') const tags: MappedData['tags'] = diff --git a/libs/cms/src/lib/search/importers/teamList.service.ts b/libs/cms/src/lib/search/importers/teamList.service.ts index 86fc4699cf9b..57cf25f54878 100644 --- a/libs/cms/src/lib/search/importers/teamList.service.ts +++ b/libs/cms/src/lib/search/importers/teamList.service.ts @@ -31,9 +31,18 @@ export class TeamListSyncService implements CmsSyncProvider { const memberEntry = teamListEntry.fields.teamMembers?.find( (m) => m.sys.id === member.id, ) - const content = memberEntry?.fields?.intro - ? documentToPlainTextString(memberEntry.fields.intro) - : '' + const contentSection: string[] = [] + + contentSection.push( + memberEntry?.fields?.intro + ? documentToPlainTextString(memberEntry.fields.intro) + : '', + ) + if (member.title) { + contentSection.push(member.title) + } + + const content = contentSection.join(' ') teamMembers.push({ _id: member.id, title: member.name, From 5ddb6629704a86e82cce10106a0ba8a62d5217e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81sd=C3=ADs=20Erna=20Gu=C3=B0mundsd=C3=B3ttir?= Date: Tue, 10 Dec 2024 09:47:18 +0000 Subject: [PATCH 77/85] fix(my-pages): small tweaks for vaccinations (#17138) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../clients/vaccinations/clientConfig.json | 14 +++++++++- .../SortableTable/SortableTable.tsx | 18 +++++++----- .../screens/HealthOverview/HealthOverview.tsx | 28 +++++++++---------- .../Vaccinations/VaccinationsWrapper.tsx | 25 ++++++++--------- .../tables/SortedVaccinationsTable.tsx | 8 +++--- 5 files changed, 53 insertions(+), 40 deletions(-) diff --git a/libs/clients/health-directorate/src/lib/clients/vaccinations/clientConfig.json b/libs/clients/health-directorate/src/lib/clients/vaccinations/clientConfig.json index a17038288d7a..37ea7918d8d7 100644 --- a/libs/clients/health-directorate/src/lib/clients/vaccinations/clientConfig.json +++ b/libs/clients/health-directorate/src/lib/clients/vaccinations/clientConfig.json @@ -597,7 +597,7 @@ "schema": { "type": "string" } }, { - "name": "age", + "name": "agePatient", "required": true, "in": "query", "schema": { "type": "number" } @@ -613,6 +613,12 @@ "required": true, "in": "query", "schema": { "type": "array", "items": { "type": "string" } } + }, + { + "name": "vaccineCodes", + "required": true, + "in": "query", + "schema": { "type": "array", "items": { "type": "string" } } } ], "responses": { @@ -820,6 +826,7 @@ "diseaseId": { "type": "string" }, "order": { "type": "number" }, "type": { "type": "object" }, + "vaccineCodes": { "type": "string" }, "cond1Type": { "type": "string" }, "cond1Min": { "type": "number" }, "cond1Max": { "type": "number" }, @@ -859,6 +866,7 @@ "description": { "type": "string" }, "isFeatured": { "type": "boolean" }, "isVisible": { "type": "boolean" }, + "hideIfNoVaccinations": { "type": "boolean" }, "vaccines": { "type": "array", "items": { "$ref": "#/components/schemas/VaccineDiseaseDto" } @@ -877,6 +885,7 @@ "name", "isFeatured", "isVisible", + "hideIfNoVaccinations", "vaccines", "rules", "translations" @@ -890,6 +899,7 @@ "description": { "type": "string" }, "isFeatured": { "type": "boolean" }, "isVisible": { "type": "boolean" }, + "hideIfNoVaccinations": { "type": "boolean" }, "vaccines": { "type": "array", "items": { "$ref": "#/components/schemas/VaccineDiseaseDto" } @@ -909,6 +919,7 @@ "properties": { "order": { "type": "number" }, "type": { "type": "object" }, + "vaccineCodes": { "type": "string" }, "cond1Type": { "type": "string" }, "cond1Min": { "type": "number" }, "cond1Max": { "type": "number" }, @@ -938,6 +949,7 @@ "diseaseId": { "type": "string" }, "order": { "type": "number" }, "type": { "type": "object" }, + "vaccineCodes": { "type": "string" }, "cond1Type": { "type": "string" }, "cond1Min": { "type": "number" }, "cond1Max": { "type": "number" }, diff --git a/libs/portals/my-pages/core/src/components/SortableTable/SortableTable.tsx b/libs/portals/my-pages/core/src/components/SortableTable/SortableTable.tsx index 1ae2abb073c9..3d9ad89bf3eb 100644 --- a/libs/portals/my-pages/core/src/components/SortableTable/SortableTable.tsx +++ b/libs/portals/my-pages/core/src/components/SortableTable/SortableTable.tsx @@ -1,13 +1,13 @@ -import React, { useMemo, useState } from 'react' import { - Text, - Table as T, Icon, - TagVariant, + Table as T, Tag, + TagVariant, + Text, } from '@island.is/island-ui/core' -import * as styles from './SortableTable.css' +import React, { useMemo, useState } from 'react' import { ExpandHeader, ExpandRow } from '../ExpandableTable' +import * as styles from './SortableTable.css' type ConfigType = { direction: 'ascending' | 'descending'; key: string } @@ -166,7 +166,7 @@ export const SortableTable = (props: SortableTableProps) => { data={valueItems.map((valueItem, i) => ({ value: valueItems.length - 1 === i && tag ? ( - + {valueItem} ) : ( @@ -187,7 +187,11 @@ export const SortableTable = (props: SortableTableProps) => { return ( {lastItem && tag ? ( - + {valueItem} ) : ( diff --git a/libs/portals/my-pages/health/src/screens/HealthOverview/HealthOverview.tsx b/libs/portals/my-pages/health/src/screens/HealthOverview/HealthOverview.tsx index 990a8e0e11e4..b7e5ae865dbe 100644 --- a/libs/portals/my-pages/health/src/screens/HealthOverview/HealthOverview.tsx +++ b/libs/portals/my-pages/health/src/screens/HealthOverview/HealthOverview.tsx @@ -1,20 +1,18 @@ -import { useUserInfo } from '@island.is/react-spa/bff' import { AlertMessage, Box, - Text, Button, GridColumn, GridRow, + Icon, SkeletonLoader, Stack, + Text, toast, - Icon, } from '@island.is/island-ui/core' import { useLocale, useNamespaces } from '@island.is/localization' -import { Problem } from '@island.is/react-spa/shared' import { - IntroHeader, + IntroWrapper, SJUKRATRYGGINGAR_SLUG, StackWithBottomDivider, UserInfoLine, @@ -24,6 +22,8 @@ import { isDateAfterToday, m, } from '@island.is/portals/my-pages/core' +import { useUserInfo } from '@island.is/react-spa/bff' +import { Problem } from '@island.is/react-spa/shared' import { useEffect, useState } from 'react' import { messages } from '../../lib/messages' import { HealthPaths } from '../../lib/paths' @@ -85,15 +85,13 @@ export const HealthOverview = () => { ) return ( - - - - + {error ? ( ) : loading ? ( @@ -209,7 +207,7 @@ export const HealthOverview = () => { )} )} - + ) } diff --git a/libs/portals/my-pages/health/src/screens/Vaccinations/VaccinationsWrapper.tsx b/libs/portals/my-pages/health/src/screens/Vaccinations/VaccinationsWrapper.tsx index 82aa962b2be4..67c57e727ff6 100644 --- a/libs/portals/my-pages/health/src/screens/Vaccinations/VaccinationsWrapper.tsx +++ b/libs/portals/my-pages/health/src/screens/Vaccinations/VaccinationsWrapper.tsx @@ -1,17 +1,17 @@ -import { useLocale, useNamespaces } from '@island.is/localization' import { Box, SkeletonLoader, Tabs } from '@island.is/island-ui/core' +import { useLocale, useNamespaces } from '@island.is/localization' import { + EmptyTable, HEALTH_DIRECTORATE_SLUG, - IntroHeader, + IntroWrapper, LinkButton, - EmptyTable, } from '@island.is/portals/my-pages/core' +import { Problem } from '@island.is/react-spa/shared' +import { isDefined } from '@island.is/shared/utils' import { messages as m } from '../../lib/messages' import { SECTION_GAP } from '../../utils/constants' import { useGetVaccinationsQuery } from './Vaccinations.generated' import { SortedVaccinationsTable } from './tables/SortedVaccinationsTable' -import { isDefined } from '@island.is/shared/utils' -import { Problem } from '@island.is/react-spa/shared' export const VaccinationsWrapper = () => { useNamespaces('sp.health') @@ -39,13 +39,12 @@ export const VaccinationsWrapper = () => { ].filter(isDefined) return ( - - + {/* Buttons */} { /> )} - + ) } export default VaccinationsWrapper diff --git a/libs/portals/my-pages/health/src/screens/Vaccinations/tables/SortedVaccinationsTable.tsx b/libs/portals/my-pages/health/src/screens/Vaccinations/tables/SortedVaccinationsTable.tsx index b0918f667bdd..ad380413cff9 100644 --- a/libs/portals/my-pages/health/src/screens/Vaccinations/tables/SortedVaccinationsTable.tsx +++ b/libs/portals/my-pages/health/src/screens/Vaccinations/tables/SortedVaccinationsTable.tsx @@ -1,3 +1,5 @@ +import { HealthDirectorateVaccination } from '@island.is/api/schema' +import { Box } from '@island.is/island-ui/core' import { useLocale, useNamespaces } from '@island.is/localization' import { EmptyTable, @@ -6,10 +8,8 @@ import { } from '@island.is/portals/my-pages/core' import { messages } from '../../../lib/messages' import { tagSelector } from '../../../utils/tagSelector' -import { VaccinationsDetailTable } from './VaccinationsDetailTable' import { DetailHeader, DetailRow } from '../../../utils/types' -import { HealthDirectorateVaccination } from '@island.is/api/schema' -import { Box } from '@island.is/island-ui/core' +import { VaccinationsDetailTable } from './VaccinationsDetailTable' interface Props { data?: Array @@ -48,7 +48,7 @@ export const SortedVaccinationsTable = ({ data }: Props) => { }} tagOutlined expandable - defaultSortByKey="vaccine" + defaultSortByKey="status" items={ data.map((item, i) => ({ id: item?.id ?? `${i}`, From fcadfe5dc7e799f4cc683ef829b6de25cd7dbfa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9tur=20Neisti=20Erlingsson?= Date: Tue, 10 Dec 2024 09:57:16 +0000 Subject: [PATCH 78/85] chore: Helm template support for ArgoCD (#17000) * chore: Helm template support for ArgoCD * stuff * Fix helm template name for deployment * Fix helm template name for pdb and secrets * Put namespaces in same mono template * Fix namespace template to work without spinnaker --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../libs/api-template/templates/deployment.yaml | 16 ++++++++-------- infra/helm/libs/api-template/templates/hpa.yaml | 7 ++++--- .../libs/api-template/templates/ingress.yaml | 5 +++-- .../libs/api-template/templates/namespaces.yaml | 8 ++++++++ infra/helm/libs/api-template/templates/pdb.yaml | 2 +- .../libs/api-template/templates/secrets.yaml | 4 ++-- .../libs/api-template/templates/service.yaml | 2 +- .../libs/cronjob-template/templates/cronjob.yaml | 12 +++++++----- 8 files changed, 34 insertions(+), 22 deletions(-) create mode 100644 infra/helm/libs/api-template/templates/namespaces.yaml diff --git a/infra/helm/libs/api-template/templates/deployment.yaml b/infra/helm/libs/api-template/templates/deployment.yaml index e1ff2f6676a3..dd7d9fe970f0 100644 --- a/infra/helm/libs/api-template/templates/deployment.yaml +++ b/infra/helm/libs/api-template/templates/deployment.yaml @@ -1,9 +1,9 @@ {{- if .Values.enabled }} -{{- $fullName := include "api-template.fullname" . -}} +{{- $fullName := include "api-template.name" . -}} apiVersion: apps/v1 kind: Deployment metadata: - name: {{ include "api-template.fullname" . }} + name: {{ include "api-template.name" . }} {{- if .Values.namespace }} namespace: {{ .Values.namespace }} {{- end }} @@ -11,7 +11,7 @@ metadata: {{- include "api-template.labels" . | nindent 4 }} tags.datadoghq.com/env: {{ .Values.global.env.name }} tags.datadoghq.com/service: {{ include "api-template.name" . }} - tags.datadoghq.com/version: {{ .Values.image.tag | default .Values.global.image.tag }} + tags.datadoghq.com/version: {{ .Values.image.tag }} spec: replicas: {{ .Values.replicaCount.default }} strategy: @@ -25,7 +25,7 @@ spec: {{- include "api-template.selectorLabels" . | nindent 8 }} tags.datadoghq.com/env: {{ .Values.global.env.name }} tags.datadoghq.com/service: {{ include "api-template.name" . }} - tags.datadoghq.com/version: {{ .Values.image.tag | default .Values.global.image.tag }} + tags.datadoghq.com/version: {{ .Values.image.tag }} annotations: prometheus.io/scrape: "true" prometheus.io/path: /metrics @@ -78,7 +78,7 @@ spec: - name: {{ .name | default "migration" }} securityContext: {{- toYaml $.Values.securityContext | nindent 12 }} - image: "{{ if .image }}{{ .image }}{{ else }}{{ $.Values.image.repository }}:{{ $.Values.image.tag | default $.Values.global.image.tag }}{{ end }}" + image: "{{ if .image }}{{ .image }}{{ else }}{{ $.Values.image.repository }}:{{ $.Values.image.tag }}{{ end }}" command: {{ .command | toJson }} args: {{ .args | toJson }} env: @@ -98,7 +98,7 @@ spec: key: {{ $key }} {{- end }} - name: APP_VERSION - value: {{ $.Values.image.tag | default $.Values.global.image.tag }} + value: {{ $.Values.image.tag }} - name: DD_ENV valueFrom: fieldRef: @@ -132,7 +132,7 @@ spec: - name: {{ .Chart.Name }} securityContext: {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Values.global.image.tag }}" + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" imagePullPolicy: {{ .Values.image.pullPolicy }} {{- if .Values.command }} command: {{ .Values.command | toJson }} @@ -169,7 +169,7 @@ spec: key: {{ $key }} {{- end }} - name: APP_VERSION - value: {{ .Values.image.tag | default .Values.global.image.tag }} + value: {{ .Values.image.tag }} - name: DD_ENV valueFrom: fieldRef: diff --git a/infra/helm/libs/api-template/templates/hpa.yaml b/infra/helm/libs/api-template/templates/hpa.yaml index a8e0b5ebfffb..ec3fabece354 100644 --- a/infra/helm/libs/api-template/templates/hpa.yaml +++ b/infra/helm/libs/api-template/templates/hpa.yaml @@ -1,9 +1,10 @@ {{- if .Values.enabled }} {{- $fullName := include "api-template.fullname" . -}} +{{- $appName := include "api-template.name" . -}} apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: - name: {{ include "api-template.fullname" . }} + name: {{ $appName }} {{- if .Values.namespace }} namespace: {{ .Values.namespace }} {{- end }} @@ -13,7 +14,7 @@ spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment - name: {{ include "api-template.fullname" . }} + name: {{ $appName }} minReplicas: {{ .Values.hpa.scaling.replicas.min }} maxReplicas: {{ .Values.hpa.scaling.replicas.max }} behavior: @@ -37,7 +38,7 @@ spec: name: nginx_ingress_controller_requests_rate selector: matchLabels: - ingress: "{{ $fullName }}-{{ $name }}" + ingress: "{{ $name }}" target: type: AverageValue averageValue: {{ $.Values.hpa.scaling.metric.nginxRequestsIrate }} diff --git a/infra/helm/libs/api-template/templates/ingress.yaml b/infra/helm/libs/api-template/templates/ingress.yaml index 3b53d924d9b7..366293c1948d 100644 --- a/infra/helm/libs/api-template/templates/ingress.yaml +++ b/infra/helm/libs/api-template/templates/ingress.yaml @@ -1,5 +1,6 @@ {{- if .Values.enabled }} {{- $fullName := include "api-template.fullname" . -}} +{{- $appName := include "api-template.name" . -}} {{- $labels := include "api-template.labels" . -}} {{- $svcPort := $.Values.service.port -}} {{- range $name, $ingress := omit .Values.ingress "behindCloudfront" }} @@ -10,7 +11,7 @@ apiVersion: extensions/v1beta1 {{- end }} kind: Ingress metadata: - name: "{{ $fullName }}-{{ $name }}" + name: "{{ $appName }}-{{ $name }}" {{- if $.Values.namespace }} namespace: {{ $.Values.namespace }} {{- end }} @@ -38,7 +39,7 @@ spec: - pathType: Prefix backend: service: - name: {{ $fullName }} + name: {{ $appName }} port: number: {{ $svcPort }} {{- if eq (kindOf .) "map" }} diff --git a/infra/helm/libs/api-template/templates/namespaces.yaml b/infra/helm/libs/api-template/templates/namespaces.yaml new file mode 100644 index 000000000000..4a4755e99d09 --- /dev/null +++ b/infra/helm/libs/api-template/templates/namespaces.yaml @@ -0,0 +1,8 @@ +{{- if .Values.enabled }} +{{- if .Values.namespace}} +apiVersion: v1 +kind: Namespace +metadata: + name: {{ .Values.namespace }} +{{- end }} +{{- end }} diff --git a/infra/helm/libs/api-template/templates/pdb.yaml b/infra/helm/libs/api-template/templates/pdb.yaml index 3b3534fd8eb4..3204babe69f3 100644 --- a/infra/helm/libs/api-template/templates/pdb.yaml +++ b/infra/helm/libs/api-template/templates/pdb.yaml @@ -7,7 +7,7 @@ apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: {{ $serviceName }}-pdb - namespace: {{ $namespace }} + namespace: {{ $namespace }} spec: {{- if hasKey .Values.podDisruptionBudget "minAvailable" }} minAvailable: {{ .Values.podDisruptionBudget.minAvailable }} diff --git a/infra/helm/libs/api-template/templates/secrets.yaml b/infra/helm/libs/api-template/templates/secrets.yaml index 602dc8afb508..2cb9fe9f680a 100644 --- a/infra/helm/libs/api-template/templates/secrets.yaml +++ b/infra/helm/libs/api-template/templates/secrets.yaml @@ -6,7 +6,7 @@ metadata: {{- if .Values.namespace }} namespace: {{ .Values.namespace }} {{- end }} - name: {{ include "api-template.fullname" . }} + name: {{ include "api-template.name" . }} spec: backendType: systemManager data: @@ -23,7 +23,7 @@ metadata: {{- if .Values.namespace }} namespace: {{ .Values.namespace }} {{- end }} - name: {{ include "api-template.fullname" . }}-init-container + name: {{ include "api-template.name" . }}-init-container spec: backendType: systemManager data: diff --git a/infra/helm/libs/api-template/templates/service.yaml b/infra/helm/libs/api-template/templates/service.yaml index 5b7e61223d1b..25bbde4f30c2 100644 --- a/infra/helm/libs/api-template/templates/service.yaml +++ b/infra/helm/libs/api-template/templates/service.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: - name: {{ include "api-template.fullname" . }} + name: {{ include "api-template.name" . }} {{- if .Values.namespace }} namespace: {{ .Values.namespace }} {{- end }} diff --git a/infra/helm/libs/cronjob-template/templates/cronjob.yaml b/infra/helm/libs/cronjob-template/templates/cronjob.yaml index a3892dce80ce..9a10878ca56f 100644 --- a/infra/helm/libs/cronjob-template/templates/cronjob.yaml +++ b/infra/helm/libs/cronjob-template/templates/cronjob.yaml @@ -1,5 +1,6 @@ +{{- if .Values.schedule }} {{- if .Values.enabled }} -{{- $fullName := include "cronjob-template.fullname" . -}} +{{- $fullName := include "api-template.fullname" . -}} apiVersion: batch/v1 kind: CronJob metadata: @@ -12,7 +13,7 @@ metadata: chart: "{{ $.Chart.Name }}-{{ $.Chart.Version | replace "+" "_" }}" tags.datadoghq.com/env: {{ .Values.global.env.name }} tags.datadoghq.com/service: {{ include "cronjob-template.name" . }} - tags.datadoghq.com/version: {{ .Values.image.tag | default .Values.global.image.tag }} + tags.datadoghq.com/version: {{ .Values.image.tag }} spec: concurrencyPolicy: {{ .Values.concurrencyPolicy | default "Allow" }} failedJobsHistoryLimit: {{ .Values.failedJobsHistoryLimit | default 1 }} @@ -52,13 +53,13 @@ spec: chart: "{{ $.Chart.Name }}-{{ $.Chart.Version | replace "+" "_" }}" tags.datadoghq.com/env: {{ .Values.global.env.name }} tags.datadoghq.com/service: {{ include "cronjob-template.name" . }} - tags.datadoghq.com/version: {{ .Values.image.tag | default .Values.global.image.tag }} + tags.datadoghq.com/version: {{ .Values.image.tag }} spec: serviceAccountName: {{ include "cronjob-template.serviceAccountName" $ }} securityContext: {{- toYaml $.Values.podSecurityContext | nindent 12 }} containers: - - image: "{{ $.Values.image.repository }}:{{ $.Values.image.tag | default .Values.global.image.tag }}" + - image: "{{ $.Values.image.repository }}:{{ $.Values.image.tag }}" name: {{ .Chart.Name }} securityContext: {{- toYaml $.Values.securityContext | nindent 14 }} @@ -92,7 +93,7 @@ spec: key: {{ $key }} {{- end }} - name: APP_VERSION - value: {{ .Values.image.tag | default .Values.global.image.tag }} + value: {{ .Values.image.tag }} - name: DD_ENV valueFrom: fieldRef: @@ -145,3 +146,4 @@ spec: {{- end }} restartPolicy: {{ .Values.restartPolicy | default "Never" }} {{- end }} +{{- end }} From 1ebd6719163fca2ac571d7d7b68184f9a58119e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eir=C3=ADkur=20Hei=C3=B0ar=20Nilsson?= Date: Tue, 10 Dec 2024 11:27:11 +0000 Subject: [PATCH 79/85] feat: Add codeOwner to logging context (#17172) * feat: Add codeOwner to logging context * Fix tests * fix: Silence warning in development --- libs/infra-tracing/src/lib/code-owner.spec.ts | 48 +++++++++++++++--- libs/infra-tracing/src/lib/code-owner.ts | 16 +++--- libs/logging/src/lib/context.spec.ts | 49 +++++++++++++++++- libs/logging/src/lib/context.ts | 4 ++ .../code-owner/code-owner.interceptor.spec.ts | 50 +++++++++++++++---- .../lib/code-owner/code-owner.interceptor.ts | 5 +- 6 files changed, 143 insertions(+), 29 deletions(-) diff --git a/libs/infra-tracing/src/lib/code-owner.spec.ts b/libs/infra-tracing/src/lib/code-owner.spec.ts index ed860dbd7a97..3aed2bbb1d55 100644 --- a/libs/infra-tracing/src/lib/code-owner.spec.ts +++ b/libs/infra-tracing/src/lib/code-owner.spec.ts @@ -1,12 +1,12 @@ -import { setCodeOwner } from './code-owner' +import { withCodeOwner } from './code-owner' import { CodeOwners } from '@island.is/shared/constants' -import { logger } from '@island.is/logging' +import { logger, withLoggingContext } from '@island.is/logging' import tracer from 'dd-trace' jest.mock('dd-trace') jest.mock('@island.is/logging') -describe('setCodeOwner', () => { +describe('withCodeOwner', () => { let mockSpan: { setTag: jest.Mock } let mockScope: jest.Mock @@ -19,31 +19,63 @@ describe('setCodeOwner', () => { })) ;(tracer.scope as jest.Mock) = mockScope ;(logger.warn as jest.Mock).mockClear() + ;(withLoggingContext as jest.Mock).mockImplementation( + (_, callback, ...args) => callback(...args), + ) }) - it('should set code owner tag on active span', () => { + it('should set code owner tag on active span and call the callback', () => { + // Arrange + const mockCallback = jest.fn() + const mockArgs = ['arg1', 'arg2'] + // Act - setCodeOwner(CodeOwners.Core) + withCodeOwner(CodeOwners.Core, mockCallback, ...mockArgs) // Assert expect(mockSpan.setTag).toHaveBeenCalledWith('codeOwner', CodeOwners.Core) + expect(withLoggingContext).toHaveBeenCalledWith( + { codeOwner: CodeOwners.Core }, + mockCallback, + ...mockArgs, + ) + expect(mockCallback).toHaveBeenCalledWith(...mockArgs) expect(logger.warn).not.toHaveBeenCalled() }) - it('should log warning when no active span exists', () => { + it('should log warning when no active span exists and still call the callback', () => { // Arrange mockScope = jest.fn(() => ({ active: () => null, })) ;(tracer.scope as jest.Mock) = mockScope + const mockCallback = jest.fn() + const mockArgs = ['arg1', 'arg2'] // Act - setCodeOwner(CodeOwners.Core) + withCodeOwner(CodeOwners.Core, mockCallback, ...mockArgs) // Assert expect(logger.warn).toHaveBeenCalledWith( 'Setting code owner "core" with no active dd-trace span', - { stack: expect.any(String) }, ) + expect(withLoggingContext).toHaveBeenCalledWith( + { codeOwner: CodeOwners.Core }, + mockCallback, + ...mockArgs, + ) + expect(mockCallback).toHaveBeenCalledWith(...mockArgs) + }) + + it('should return the callback result', () => { + // Arrange + const expectedResult = { foo: 'bar' } + const mockCallback = jest.fn().mockReturnValue(expectedResult) + + // Act + const result = withCodeOwner(CodeOwners.Core, mockCallback) + + // Assert + expect(result).toBe(expectedResult) }) }) diff --git a/libs/infra-tracing/src/lib/code-owner.ts b/libs/infra-tracing/src/lib/code-owner.ts index 7d4d6352fe52..f0d085dbf4dc 100644 --- a/libs/infra-tracing/src/lib/code-owner.ts +++ b/libs/infra-tracing/src/lib/code-owner.ts @@ -1,22 +1,26 @@ -import { logger } from '@island.is/logging' +import { logger, withLoggingContext } from '@island.is/logging' import { CodeOwners } from '@island.is/shared/constants' import tracer from 'dd-trace' /** - * Sets a code owner for the current dd-trace span. + * Sets a code owner for the current dd-trace span and all nested log entries. * * The assumption here is that each trace / request has only one "dynamic" * code owner. This way we skip cluttering the trace with extra spans. */ -export const setCodeOwner = (codeOwner: CodeOwners) => { +export const withCodeOwner = ( + codeOwner: CodeOwners, + callback: (...args: TArgs) => R, + ...args: TArgs +) => { const span = tracer.scope().active() if (span) { span.setTag('codeOwner', codeOwner) - } else { - const stack = new Error().stack + } else if (process.env.NODE_ENV !== 'development') { logger.warn( `Setting code owner "${codeOwner}" with no active dd-trace span`, - { stack }, ) } + + return withLoggingContext({ codeOwner }, callback, ...args) } diff --git a/libs/logging/src/lib/context.spec.ts b/libs/logging/src/lib/context.spec.ts index ca371ba27376..28a44634868c 100644 --- a/libs/logging/src/lib/context.spec.ts +++ b/libs/logging/src/lib/context.spec.ts @@ -1,3 +1,4 @@ +import { CodeOwners } from '@island.is/shared/constants' import { includeContextFormatter, withLoggingContext } from './context' describe('Winston context', () => { @@ -11,6 +12,50 @@ describe('Winston context', () => { process.env = originalEnv }) + it('should add default CODE_OWNER when environment variable is set', () => { + // Arrange + process.env.CODE_OWNER = 'default-team' + const formatter = includeContextFormatter() + const logInfo = { + level: 'info', + message: 'Test message', + } + + // Act + const formattedLog = formatter.transform(logInfo) + + // Assert + expect(formattedLog).toEqual({ + level: 'info', + message: 'Test message', + codeOwner: 'default-team', + }) + }) + + it('should override default CODE_OWNER with context codeOwner', () => { + // Arrange + process.env.CODE_OWNER = 'default-team' + const formatter = includeContextFormatter() + const logInfo = { + level: 'info', + message: 'Test message', + } + const context = { codeOwner: 'context-team' as CodeOwners } + + // Act + let formattedLog: unknown + withLoggingContext(context, () => { + formattedLog = formatter.transform(logInfo) + }) + + // Assert + expect(formattedLog).toEqual({ + level: 'info', + message: 'Test message', + codeOwner: 'context-team', + }) + }) + it('should add context to log info object', () => { // Arrange const formatter = includeContextFormatter() @@ -35,7 +80,7 @@ describe('Winston context', () => { }) }) - it('should not modify log info when no context exists', () => { + it('should not modify log info when no context or CODE_OWNER exists', () => { // Arrange const formatter = includeContextFormatter() const logInfo = { @@ -52,6 +97,7 @@ describe('Winston context', () => { it('should preserve existing log info properties when adding context', () => { // Arrange + process.env.CODE_OWNER = 'default-team' const formatter = includeContextFormatter() const logInfo = { level: 'info', @@ -71,6 +117,7 @@ describe('Winston context', () => { level: 'info', message: 'Test message', existingProp: 'should remain', + codeOwner: 'default-team', requestId: '123', }) }) diff --git a/libs/logging/src/lib/context.ts b/libs/logging/src/lib/context.ts index 03034f67d79d..c27e9652305d 100644 --- a/libs/logging/src/lib/context.ts +++ b/libs/logging/src/lib/context.ts @@ -19,8 +19,12 @@ export const withLoggingContext = ( } export const includeContextFormatter = format((info) => { + const defaultCodeOwner = process.env.CODE_OWNER const context = loggingContextStorage.getStore() + if (defaultCodeOwner) { + info.codeOwner = defaultCodeOwner + } if (context) { Object.assign(info, context) } diff --git a/libs/nest/core/src/lib/code-owner/code-owner.interceptor.spec.ts b/libs/nest/core/src/lib/code-owner/code-owner.interceptor.spec.ts index 038802b02237..54ffa4260584 100644 --- a/libs/nest/core/src/lib/code-owner/code-owner.interceptor.spec.ts +++ b/libs/nest/core/src/lib/code-owner/code-owner.interceptor.spec.ts @@ -1,4 +1,4 @@ -import { setCodeOwner } from '@island.is/infra-tracing' +import { withCodeOwner } from '@island.is/infra-tracing' import { CodeOwners } from '@island.is/shared/constants' import { Controller, Get, INestApplication } from '@nestjs/common' import { APP_INTERCEPTOR } from '@nestjs/core' @@ -9,7 +9,7 @@ import { CodeOwnerInterceptor } from './code-owner.interceptor' // Mock the logging module jest.mock('@island.is/infra-tracing', () => ({ - setCodeOwner: jest.fn(), + withCodeOwner: jest.fn((codeOwner, callback) => callback()), })) // Test controller with decorated endpoints @@ -50,26 +50,29 @@ describe('CodeOwnerInterceptor', () => { jest.clearAllMocks() }) - it('should call setCodeOwner when CodeOwner decorator is present', async () => { + it('should call withCodeOwner when CodeOwner decorator is present', async () => { // Make request to endpoint with CodeOwner decorator await request(app.getHttpServer()) .get('/test/with-owner') .expect(200) .expect({ message: 'with owner' }) - // Verify that setCodeOwner was called with correct parameters - expect(setCodeOwner).toHaveBeenCalledWith(CodeOwners.Core) + // Verify that withCodeOwner was called with correct parameters + expect(withCodeOwner).toHaveBeenCalledWith( + CodeOwners.Core, + expect.any(Function), + ) }) - it('should not call setCodeOwner when CodeOwner decorator is not present', async () => { + it('should not call withCodeOwner when CodeOwner decorator is not present', async () => { // Make request to endpoint without CodeOwner decorator await request(app.getHttpServer()) .get('/test/without-owner') .expect(200) .expect({ message: 'without owner' }) - // Verify that setCodeOwner was not called - expect(setCodeOwner).not.toHaveBeenCalled() + // Verify that withCodeOwner was not called + expect(withCodeOwner).not.toHaveBeenCalled() }) it('should handle multiple requests correctly', async () => { @@ -80,8 +83,33 @@ describe('CodeOwnerInterceptor', () => { request(app.getHttpServer()).get('/test/with-owner'), ]) - // Verify that setCodeOwner was called exactly twice (for the two 'with-owner' requests) - expect(setCodeOwner).toHaveBeenCalledTimes(2) - expect(setCodeOwner).toHaveBeenCalledWith(CodeOwners.Core) + // Verify that withCodeOwner was called exactly twice (for the two 'with-owner' requests) + expect(withCodeOwner).toHaveBeenCalledTimes(2) + expect(withCodeOwner).toHaveBeenCalledWith( + CodeOwners.Core, + expect.any(Function), + ) + }) + + it('should properly wrap and execute the handler', async () => { + // Arrange + let handlerExecuted = false + ;(withCodeOwner as jest.Mock).mockImplementation((codeOwner, callback) => { + handlerExecuted = true + return callback() + }) + + // Act + await request(app.getHttpServer()) + .get('/test/with-owner') + .expect(200) + .expect({ message: 'with owner' }) + + // Assert + expect(handlerExecuted).toBe(true) + expect(withCodeOwner).toHaveBeenCalledWith( + CodeOwners.Core, + expect.any(Function), + ) }) }) diff --git a/libs/nest/core/src/lib/code-owner/code-owner.interceptor.ts b/libs/nest/core/src/lib/code-owner/code-owner.interceptor.ts index a9f992fccbc6..89134e97552c 100644 --- a/libs/nest/core/src/lib/code-owner/code-owner.interceptor.ts +++ b/libs/nest/core/src/lib/code-owner/code-owner.interceptor.ts @@ -1,4 +1,4 @@ -import { setCodeOwner } from '@island.is/infra-tracing' +import { withCodeOwner } from '@island.is/infra-tracing' import { CodeOwners } from '@island.is/shared/constants' import { Injectable, @@ -19,9 +19,8 @@ export class CodeOwnerInterceptor implements NestInterceptor { CODE_OWNER_KEY, [context.getHandler(), context.getClass()], ) - if (codeOwner) { - setCodeOwner(codeOwner) + return withCodeOwner(codeOwner, () => next.handle()) } return next.handle() } From 0309242375c9e9b9522fb7a3549a39d0a7dfca2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAnar=20Vestmann?= <43557895+RunarVestmann@users.noreply.github.com> Date: Tue, 10 Dec 2024 11:59:26 +0000 Subject: [PATCH 80/85] feat(web): WHODAS calculator - Allow users to go to previous step using browser back (#17183) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../web/components/connected/WHODAS/Calculator.tsx | 14 ++++++++++++-- package.json | 2 +- yarn.lock | 12 ++++++------ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/apps/web/components/connected/WHODAS/Calculator.tsx b/apps/web/components/connected/WHODAS/Calculator.tsx index fc96577ef4f3..cb2294fda8c5 100644 --- a/apps/web/components/connected/WHODAS/Calculator.tsx +++ b/apps/web/components/connected/WHODAS/Calculator.tsx @@ -7,6 +7,7 @@ import { } from 'react' import { useIntl } from 'react-intl' import round from 'lodash/round' +import { parseAsInteger, useQueryState } from 'next-usequerystate' import { Box, @@ -235,7 +236,15 @@ interface WHODASCalculatorProps { } export const WHODASCalculator = ({ slice }: WHODASCalculatorProps) => { - const [stepIndex, setStepIndex] = useState(0) + const [stepIndex, setStepIndex] = useQueryState( + 'stepIndex', + parseAsInteger + .withOptions({ + history: 'push', + clearOnDefault: true, + }) + .withDefault(0), + ) const formRef = useRef(null) const steps = (slice.json?.steps ?? []) as Step[] const initialRender = useRef(true) @@ -266,6 +275,7 @@ export const WHODASCalculator = ({ slice }: WHODASCalculatorProps) => { useEffect(() => { if (initialRender.current) { + setStepIndex(0) // Reset step index on initial render initialRender.current = false return } @@ -273,7 +283,7 @@ export const WHODASCalculator = ({ slice }: WHODASCalculatorProps) => { behavior: 'smooth', top: formRef.current?.offsetTop ?? 0, }) - }, [stepIndex]) + }, [setStepIndex, stepIndex]) if (showResults) { let totalScore = 0 diff --git a/package.json b/package.json index e78c508c8747..d27381ce9a8a 100644 --- a/package.json +++ b/package.json @@ -228,7 +228,7 @@ "next": "14.2.3", "next-auth": "3.29.10", "next-cookies": "2.0.3", - "next-usequerystate": "1.8.4", + "next-usequerystate": "1.20.0", "node-fetch": "2.6.7", "node-gyp": "9.1.0", "node-html-markdown": "1.3.0", diff --git a/yarn.lock b/yarn.lock index dc4f63250639..23df4b3c706e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -38840,7 +38840,7 @@ __metadata: next-auth: 3.29.10 next-cookies: 2.0.3 next-secure-headers: 2.1.0 - next-usequerystate: 1.8.4 + next-usequerystate: 1.20.0 node-fetch: 2.6.7 node-gyp: 9.1.0 node-html-markdown: 1.3.0 @@ -44538,14 +44538,14 @@ __metadata: languageName: node linkType: hard -"next-usequerystate@npm:1.8.4": - version: 1.8.4 - resolution: "next-usequerystate@npm:1.8.4" +"next-usequerystate@npm:1.20.0": + version: 1.20.0 + resolution: "next-usequerystate@npm:1.20.0" dependencies: mitt: ^3.0.1 peerDependencies: - next: ^13.4 - checksum: 4ab19aa11fd32058246375bdcd7c76fdff357e15a460e80a30835b972b7458ab99e59e8ae38be26b32d27727b156b655c2a0105c200b994408fb26366133b496 + next: ">=13.4 <14.0.2 || ^14.0.3" + checksum: fbe915a99e2374eb43c4b1c7ab9b29f4bf46c4383ee66c61ecb13d768e5552f040a90b33a0df1675c20d0cab64d62035cea1b801512f936648af78726ec6860e languageName: node linkType: hard From 4a6defe9b969bfb01d7b624bdd5ec47638caf99a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Eorkell=20M=C3=A1ni=20=C3=9Eorkelsson?= Date: Tue, 10 Dec 2024 12:30:21 +0000 Subject: [PATCH 81/85] fix(social-insurance-maintenance): Disable payment plan with FF (#17181) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- libs/feature-flags/src/lib/features.ts | 3 + .../src/lib/messages.ts | 5 ++ .../src/screens/IncomePlan/IncomePlan.tsx | 32 --------- .../IncomePlanDetail/IncomePlanDetail.tsx | 65 +++++++++++++++---- 4 files changed, 60 insertions(+), 45 deletions(-) diff --git a/libs/feature-flags/src/lib/features.ts b/libs/feature-flags/src/lib/features.ts index fc77efa64398..5cc65a03a541 100644 --- a/libs/feature-flags/src/lib/features.ts +++ b/libs/feature-flags/src/lib/features.ts @@ -60,6 +60,9 @@ export enum Features { //New License service fetch enabled licensesV2 = 'isLicensesV2Enabled', + //Is social administration payment plan 2025 enabled? + isServicePortalPaymentPlan2025Enabled = 'isServicePortalPaymentPlan2025Enabled', + //Possible universities isUniversityOfAkureyriEnabled = 'isUniversityOfAkureyriEnabled', isAgriculturalUniversityOfIcelandEnabled = 'isAgriculturalUniversityOfIcelandEnabled', diff --git a/libs/portals/my-pages/social-insurance-maintenance/src/lib/messages.ts b/libs/portals/my-pages/social-insurance-maintenance/src/lib/messages.ts index 3cbf39c1838d..f90f69b54bab 100644 --- a/libs/portals/my-pages/social-insurance-maintenance/src/lib/messages.ts +++ b/libs/portals/my-pages/social-insurance-maintenance/src/lib/messages.ts @@ -122,4 +122,9 @@ export const m = defineMessages({ id: 'sp.social-insurance-maintenance:income-plan-link-text', defaultMessage: 'HvaĂ° er tekjuĂĄĂŠtlun?', }, + incomePlanTemporarilyUnavailable: { + id: 'sp.social-insurance-maintenance:income-plan-temporarily-unavailable', + defaultMessage: + 'BrĂĄĂ°abirgĂ°atekjuĂĄĂŠtlun 2025 verĂ°ur tilbĂșin seinni part desember', + }, }) diff --git a/libs/portals/my-pages/social-insurance-maintenance/src/screens/IncomePlan/IncomePlan.tsx b/libs/portals/my-pages/social-insurance-maintenance/src/screens/IncomePlan/IncomePlan.tsx index 1197a088e110..7777a757b3bf 100644 --- a/libs/portals/my-pages/social-insurance-maintenance/src/screens/IncomePlan/IncomePlan.tsx +++ b/libs/portals/my-pages/social-insurance-maintenance/src/screens/IncomePlan/IncomePlan.tsx @@ -37,38 +37,6 @@ const parseSubtext = ( } } -const parseTag = ( - tag: SocialInsuranceIncomePlanStatus, - formatMessage: FormatMessage, -) => { - switch (tag) { - case SocialInsuranceIncomePlanStatus.ACCEPTED: - return { - label: formatMessage(coreMessages.processed), - variant: 'mint' as const, - outlined: false, - } - case SocialInsuranceIncomePlanStatus.IN_PROGRESS: - return { - label: formatMessage(coreMessages.inProgress), - variant: 'purple' as const, - outlined: false, - } - case SocialInsuranceIncomePlanStatus.CANCELLED: - return { - label: formatMessage(coreMessages.rejected), - variant: 'red' as const, - outlined: false, - } - default: - return { - label: formatMessage(coreMessages.unknown), - variant: 'red' as const, - outlined: false, - } - } -} - const IncomePlan = () => { useNamespaces('sp.social-insurance-maintenance') const { formatMessage } = useLocale() diff --git a/libs/portals/my-pages/social-insurance-maintenance/src/screens/IncomePlanDetail/IncomePlanDetail.tsx b/libs/portals/my-pages/social-insurance-maintenance/src/screens/IncomePlanDetail/IncomePlanDetail.tsx index 3f5b207c349d..51878ab260c9 100644 --- a/libs/portals/my-pages/social-insurance-maintenance/src/screens/IncomePlanDetail/IncomePlanDetail.tsx +++ b/libs/portals/my-pages/social-insurance-maintenance/src/screens/IncomePlanDetail/IncomePlanDetail.tsx @@ -1,5 +1,4 @@ import { - Box, Inline, Stack, Button, @@ -10,32 +9,75 @@ import { import { useLocale, useNamespaces } from '@island.is/localization' import { EmptyTable, - FootNote, - IntroHeader, + IntroWrapper, LinkButton, amountFormat, m as coreMessages, } from '@island.is/portals/my-pages/core' import { m } from '../../lib/messages' -import { useGetIncomePlanDetailQuery } from './IncomePlanDetail.generated' +import { + useGetIncomePlanDetailLazyQuery, + useGetIncomePlanDetailQuery, +} from './IncomePlanDetail.generated' import { Problem } from '@island.is/react-spa/shared' +import { useEffect, useState } from 'react' +import { useFeatureFlagClient } from '@island.is/react/feature-flags' const IncomePlanDetail = () => { useNamespaces('sp.social-insurance-maintenance') const { formatMessage } = useLocale() - const { data, loading, error } = useGetIncomePlanDetailQuery() - return ( - - { + const isFlagEnabled = async () => { + const ffEnabled = await featureFlagClient.getValue( + `isServicePortalPaymentPlan2025Enabled`, + false, + ) + if (ffEnabled) { + setDisplayPaymentPlan(ffEnabled as boolean) + } + } + isFlagEnabled() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + useEffect(() => { + if (displayPaymentPlan) { + query() + } + }, [displayPaymentPlan]) + + if (!displayPaymentPlan) { + return ( + + > + + + ) + } + return ( + {error && !loading ? ( ) : !error && !loading && !data?.socialInsuranceIncomePlan ? ( @@ -117,10 +159,7 @@ const IncomePlanDetail = () => { )} )} - - - - + ) } From b3e08784d2ad650f9b3716f81441335cc73f26fc Mon Sep 17 00:00:00 2001 From: HjorturJ <34068269+HjorturJ@users.noreply.github.com> Date: Tue, 10 Dec 2024 13:13:12 +0000 Subject: [PATCH 82/85] fix(core): S3 copy object with foreign characters (#17151) * Testing overwriting the metadata * chore: nx format:write update dirty files * Found a cleaner work around --------- Co-authored-by: andes-it Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- libs/nest/aws/src/lib/s3.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/nest/aws/src/lib/s3.service.ts b/libs/nest/aws/src/lib/s3.service.ts index 0e534dcd33c1..12e5c1439e9e 100644 --- a/libs/nest/aws/src/lib/s3.service.ts +++ b/libs/nest/aws/src/lib/s3.service.ts @@ -63,7 +63,7 @@ export class S3Service { const input: CopyObjectRequest = { Bucket: bucket, Key: key, - CopySource: copySource, + CopySource: encodeURIComponent(copySource), } try { return await this.s3Client.send(new CopyObjectCommand(input)) From 22c5b520743358118ed9b429b3370bb4ca49d678 Mon Sep 17 00:00:00 2001 From: unakb Date: Tue, 10 Dec 2024 13:44:53 +0000 Subject: [PATCH 83/85] feat(j-s): Notifications to prosecutors office in completed indictments (#17174) * fix(j-s): Display confirmation modal before completing case * feat(j-s): Notifications for prosecutor * chore: charts update dirty files * chore: nx format:write update dirty files * fix(j-s): Only send verdict info if there was a ruling * Update eventLog.service.ts --------- Co-authored-by: andes-it Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../backend/infra/judicial-system-backend.ts | 2 + .../backend/src/app/messages/notifications.ts | 2 +- .../app/modules/event-log/eventLog.module.ts | 9 +- .../app/modules/event-log/eventLog.service.ts | 37 ++++- .../dto/indictmentCaseNotification.dto.ts | 12 ++ .../dto/notificationDispatch.dto.ts | 12 +- .../internalNotification.controller.ts | 84 ++++++++-- .../notification/notification.config.ts | 6 + .../notification/notification.module.ts | 2 + .../notificationDispatch.service.ts | 43 +++++ .../indictmentCaseNotification.service.ts | 155 ++++++++++++++++++ .../indictmentCaseNotification.strings.ts | 19 +++ .../test/createTestingNotificationModule.ts | 5 + .../eventNotificationDispatch.spec.ts | 77 +++++++++ ...dIndictmentVerdictInfoNotification.spec.ts | 146 +++++++++++++++++ ...aitingForConfirmationNotifications.spec.ts | 2 +- charts/judicial-system/values.dev.yaml | 1 + charts/judicial-system/values.prod.yaml | 1 + charts/judicial-system/values.staging.yaml | 1 + .../judicial-system-backend/values.dev.yaml | 1 + .../judicial-system-backend/values.prod.yaml | 1 + .../values.staging.yaml | 1 + .../message/src/lib/message.ts | 4 + libs/judicial-system/types/src/index.ts | 2 + .../types/src/lib/notification.ts | 42 +++-- 25 files changed, 630 insertions(+), 37 deletions(-) create mode 100644 apps/judicial-system/backend/src/app/modules/notification/dto/indictmentCaseNotification.dto.ts create mode 100644 apps/judicial-system/backend/src/app/modules/notification/services/indictmentCaseNotification/indictmentCaseNotification.service.ts create mode 100644 apps/judicial-system/backend/src/app/modules/notification/services/indictmentCaseNotification/indictmentCaseNotification.strings.ts create mode 100644 apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/eventNotificationDispatch/eventNotificationDispatch.spec.ts create mode 100644 apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/indictmentCaseNotification/sendIndictmentVerdictInfoNotification.spec.ts diff --git a/apps/judicial-system/backend/infra/judicial-system-backend.ts b/apps/judicial-system/backend/infra/judicial-system-backend.ts index bab01682da23..8d8fb88fcd95 100644 --- a/apps/judicial-system/backend/infra/judicial-system-backend.ts +++ b/apps/judicial-system/backend/infra/judicial-system-backend.ts @@ -68,6 +68,8 @@ export const serviceSetup = (): ServiceBuilder<'judicial-system-backend'> => EMAIL_FROM_NAME: '/k8s/judicial-system/EMAIL_FROM_NAME', EMAIL_REPLY_TO: '/k8s/judicial-system/EMAIL_REPLY_TO', EMAIL_REPLY_TO_NAME: '/k8s/judicial-system/EMAIL_REPLY_TO_NAME', + POLICE_INSTITUTIONS_EMAILS: + '/k8s/judicial-system/POLICE_INSTITUTIONS_EMAILS', PRISON_EMAIL: '/k8s/judicial-system/PRISON_EMAIL', PRISON_ADMIN_EMAIL: '/k8s/judicial-system/PRISON_ADMIN_EMAIL', PRISON_ADMIN_INDICTMENT_EMAILS: diff --git a/apps/judicial-system/backend/src/app/messages/notifications.ts b/apps/judicial-system/backend/src/app/messages/notifications.ts index a7bf8f898acf..43bda5af9fb3 100644 --- a/apps/judicial-system/backend/src/app/messages/notifications.ts +++ b/apps/judicial-system/backend/src/app/messages/notifications.ts @@ -52,7 +52,7 @@ export const notifications = { }), emailWhitelistDomains: defineMessage({ id: 'judicial.system.backend:notifications.email_whitelist_domains', - defaultMessage: 'omnitrix.is,kolibri.is', + defaultMessage: 'omnitrix.is,kolibri.is,dummy.dd', description: 'NotaĂ° til aĂ° tilgreina hvort pĂłstfang sĂ© Ă­ hvĂ­tlista', }), readyForCourt: defineMessages({ diff --git a/apps/judicial-system/backend/src/app/modules/event-log/eventLog.module.ts b/apps/judicial-system/backend/src/app/modules/event-log/eventLog.module.ts index d208d6b47f62..2e23e6aacfa5 100644 --- a/apps/judicial-system/backend/src/app/modules/event-log/eventLog.module.ts +++ b/apps/judicial-system/backend/src/app/modules/event-log/eventLog.module.ts @@ -1,12 +1,17 @@ -import { Module } from '@nestjs/common' +import { forwardRef, Module } from '@nestjs/common' import { SequelizeModule } from '@nestjs/sequelize' +import { MessageModule } from '@island.is/judicial-system/message' + import { EventLog } from './models/eventLog.model' import { EventLogController } from './eventLog.controller' import { EventLogService } from './eventLog.service' @Module({ - imports: [SequelizeModule.forFeature([EventLog])], + imports: [ + forwardRef(() => MessageModule), + SequelizeModule.forFeature([EventLog]), + ], providers: [EventLogService], exports: [EventLogService], controllers: [EventLogController], diff --git a/apps/judicial-system/backend/src/app/modules/event-log/eventLog.service.ts b/apps/judicial-system/backend/src/app/modules/event-log/eventLog.service.ts index c1ee4450f943..52806927363b 100644 --- a/apps/judicial-system/backend/src/app/modules/event-log/eventLog.service.ts +++ b/apps/judicial-system/backend/src/app/modules/event-log/eventLog.service.ts @@ -7,7 +7,11 @@ import { InjectModel } from '@nestjs/sequelize' import type { Logger } from '@island.is/logging' import { LOGGER_PROVIDER } from '@island.is/logging' -import { EventType } from '@island.is/judicial-system/types' +import { MessageService, MessageType } from '@island.is/judicial-system/message' +import { + EventNotificationType, + EventType, +} from '@island.is/judicial-system/types' import { CreateEventLogDto } from './dto/createEventLog.dto' import { EventLog } from './models/eventLog.model' @@ -20,11 +24,19 @@ const allowMultiple: EventType[] = [ EventType.INDICTMENT_CONFIRMED, ] +const eventToNotificationMap: Partial< + Record +> = { + INDICTMENT_SENT_TO_PUBLIC_PROSECUTOR: + EventNotificationType.INDICTMENT_SENT_TO_PUBLIC_PROSECUTOR, +} + @Injectable() export class EventLogService { constructor( @InjectModel(EventLog) private readonly eventLogModel: typeof EventLog, + private readonly messageService: MessageService, @Inject(LOGGER_PROVIDER) private readonly logger: Logger, ) {} @@ -58,6 +70,10 @@ export class EventLogService { // Tolerate failure but log error this.logger.error('Failed to create event log', error) } + + if (caseId) { + this.addEventNotificationToQueue(eventType, caseId) + } } async loginMap( @@ -86,4 +102,23 @@ export class EventLogService { ), ) } + + // Sends events to queue for notification dispatch + private addEventNotificationToQueue(eventType: EventType, caseId: string) { + const notificationType = eventToNotificationMap[eventType] + + if (notificationType) { + try { + this.messageService.sendMessagesToQueue([ + { + type: MessageType.EVENT_NOTIFICATION_DISPATCH, + caseId: caseId, + body: { type: notificationType }, + }, + ]) + } catch (error) { + this.logger.error('Failed to send event notification to queue', error) + } + } + } } diff --git a/apps/judicial-system/backend/src/app/modules/notification/dto/indictmentCaseNotification.dto.ts b/apps/judicial-system/backend/src/app/modules/notification/dto/indictmentCaseNotification.dto.ts new file mode 100644 index 000000000000..201f04c208b5 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/notification/dto/indictmentCaseNotification.dto.ts @@ -0,0 +1,12 @@ +import { IsEnum, IsNotEmpty } from 'class-validator' + +import { ApiProperty } from '@nestjs/swagger' + +import { IndictmentCaseNotificationType } from '@island.is/judicial-system/types' + +export class IndictmentCaseNotificationDto { + @IsNotEmpty() + @IsEnum(IndictmentCaseNotificationType) + @ApiProperty({ enum: IndictmentCaseNotificationType }) + readonly type!: IndictmentCaseNotificationType +} diff --git a/apps/judicial-system/backend/src/app/modules/notification/dto/notificationDispatch.dto.ts b/apps/judicial-system/backend/src/app/modules/notification/dto/notificationDispatch.dto.ts index 017f7a7fde0d..984e356a627a 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/dto/notificationDispatch.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/dto/notificationDispatch.dto.ts @@ -2,7 +2,10 @@ import { IsEnum, IsNotEmpty } from 'class-validator' import { ApiProperty } from '@nestjs/swagger' -import { NotificationDispatchType } from '@island.is/judicial-system/types' +import { + EventNotificationType, + NotificationDispatchType, +} from '@island.is/judicial-system/types' export class NotificationDispatchDto { @IsNotEmpty() @@ -10,3 +13,10 @@ export class NotificationDispatchDto { @ApiProperty({ enum: NotificationDispatchType }) readonly type!: NotificationDispatchType } + +export class EventNotificationDispatchDto { + @IsNotEmpty() + @IsEnum(EventNotificationType) + @ApiProperty({ enum: EventNotificationType }) + readonly type!: EventNotificationType +} diff --git a/apps/judicial-system/backend/src/app/modules/notification/internalNotification.controller.ts b/apps/judicial-system/backend/src/app/modules/notification/internalNotification.controller.ts index 68131739f280..af4c5a235c61 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/internalNotification.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/internalNotification.controller.ts @@ -16,8 +16,9 @@ import { messageEndpoint, MessageType, } from '@island.is/judicial-system/message' +import { indictmentCases } from '@island.is/judicial-system/types' -import { Case, CaseHasExistedGuard, CurrentCase } from '../case' +import { Case, CaseHasExistedGuard, CaseTypeGuard, CurrentCase } from '../case' import { CivilClaimant, CivilClaimantExistsGuard, @@ -30,13 +31,18 @@ import { SubpoenaExistsGuard } from '../subpoena' import { CaseNotificationDto } from './dto/caseNotification.dto' import { CivilClaimantNotificationDto } from './dto/civilClaimantNotification.dto' import { DefendantNotificationDto } from './dto/defendantNotification.dto' +import { IndictmentCaseNotificationDto } from './dto/indictmentCaseNotification.dto' import { InstitutionNotificationDto } from './dto/institutionNotification.dto' -import { NotificationDispatchDto } from './dto/notificationDispatch.dto' +import { + EventNotificationDispatchDto, + NotificationDispatchDto, +} from './dto/notificationDispatch.dto' import { SubpoenaNotificationDto } from './dto/subpoenaNotification.dto' import { DeliverResponse } from './models/deliver.response' import { CaseNotificationService } from './services/caseNotification/caseNotification.service' import { CivilClaimantNotificationService } from './services/civilClaimantNotification/civilClaimantNotification.service' import { DefendantNotificationService } from './services/defendantNotification/defendantNotification.service' +import { IndictmentCaseNotificationService } from './services/indictmentCaseNotification/indictmentCaseNotification.service' import { InstitutionNotificationService } from './services/institutionNotification/institutionNotification.service' import { SubpoenaNotificationService } from './services/subpoenaNotification/subpoenaNotification.service' import { NotificationDispatchService } from './notificationDispatch.service' @@ -52,6 +58,7 @@ export class InternalNotificationController { private readonly subpoenaNotificationService: SubpoenaNotificationService, private readonly defendantNotificationService: DefendantNotificationService, private readonly civilClaimantNotificationService: CivilClaimantNotificationService, + private readonly indictmentCaseNotificationService: IndictmentCaseNotificationService, @Inject(LOGGER_PROVIDER) private readonly logger: Logger, ) {} @@ -77,6 +84,29 @@ export class InternalNotificationController { ) } + @Post( + `case/:caseId/${messageEndpoint[MessageType.INDICTMENT_CASE_NOTIFICATION]}`, + ) + @UseGuards(CaseHasExistedGuard, new CaseTypeGuard(indictmentCases)) + @ApiCreatedResponse({ + type: DeliverResponse, + description: 'Sends a case notification for an existing indictment case', + }) + sendIndictmentCaseNotification( + @Param('caseId') caseId: string, + @CurrentCase() theCase: Case, + @Body() notificationDto: IndictmentCaseNotificationDto, + ): Promise { + this.logger.debug( + `Sending ${notificationDto.type} indictment case notification for case ${caseId}`, + ) + + return this.indictmentCaseNotificationService.sendIndictmentCaseNotification( + notificationDto.type, + theCase, + ) + } + @Post( `case/:caseId/${ messageEndpoint[MessageType.SUBPOENA_NOTIFICATION] @@ -161,34 +191,58 @@ export class InternalNotificationController { ) } - @Post(messageEndpoint[MessageType.NOTIFICATION_DISPATCH]) + @Post(messageEndpoint[MessageType.INSTITUTION_NOTIFICATION]) @ApiCreatedResponse({ type: DeliverResponse, - description: 'Dispatches notifications', + description: 'Sends an institution notification', }) - dispatchNotification( - @Body() notificationDto: NotificationDispatchDto, + sendInstitutionNotification( + @Body() notificationDto: InstitutionNotificationDto, ): Promise { - this.logger.debug(`Dispatching ${notificationDto.type} notification`) + this.logger.debug(`Sending ${notificationDto.type} notification`) - return this.notificationDispatchService.dispatchNotification( + return this.institutionNotificationService.sendNotification( notificationDto.type, + notificationDto.prosecutorsOfficeId, ) } - @Post(messageEndpoint[MessageType.INSTITUTION_NOTIFICATION]) + @Post( + `case/:caseId/${messageEndpoint[MessageType.EVENT_NOTIFICATION_DISPATCH]}`, + ) + @UseGuards(CaseHasExistedGuard) @ApiCreatedResponse({ type: DeliverResponse, - description: 'Sends an institution notification', + description: + 'Dispatches notifications in response to events logged in event log', }) - sendNotification( - @Body() notificationDto: InstitutionNotificationDto, + dispatchEventNotification( + @Param('caseId') caseId: string, + @CurrentCase() theCase: Case, + @Body() notificationDto: EventNotificationDispatchDto, ): Promise { - this.logger.debug(`Sending ${notificationDto.type} notification`) + this.logger.debug( + `Dispatching ${notificationDto.type} event notification for case ${caseId}`, + ) - return this.institutionNotificationService.sendNotification( + return this.notificationDispatchService.dispatchEventNotification( + notificationDto.type, + theCase, + ) + } + + @Post(messageEndpoint[MessageType.NOTIFICATION_DISPATCH]) + @ApiCreatedResponse({ + type: DeliverResponse, + description: 'Dispatches notifications', + }) + dispatchNotification( + @Body() notificationDto: NotificationDispatchDto, + ): Promise { + this.logger.debug(`Dispatching ${notificationDto.type} notification`) + + return this.notificationDispatchService.dispatchNotification( notificationDto.type, - notificationDto.prosecutorsOfficeId, ) } } diff --git a/apps/judicial-system/backend/src/app/modules/notification/notification.config.ts b/apps/judicial-system/backend/src/app/modules/notification/notification.config.ts index aecd5dabb6c9..e91cc08bd354 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/notification.config.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/notification.config.ts @@ -21,6 +21,12 @@ export const notificationModuleConfig = defineConfig({ courtsEmails: env.requiredJSON('COURTS_EMAILS', {}) as { [key: string]: string }, + policeInstitutionEmails: env.requiredJSON( + 'POLICE_INSTITUTIONS_EMAILS', + {}, + ) as { + [key: string]: string + }, }, sms: { courtsMobileNumbers: env.requiredJSON('COURTS_MOBILE_NUMBERS', {}) as { diff --git a/apps/judicial-system/backend/src/app/modules/notification/notification.module.ts b/apps/judicial-system/backend/src/app/modules/notification/notification.module.ts index 101978e3d3ba..47331de9c1c8 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/notification.module.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/notification.module.ts @@ -20,6 +20,7 @@ import { Notification } from './models/notification.model' import { CaseNotificationService } from './services/caseNotification/caseNotification.service' import { CivilClaimantNotificationService } from './services/civilClaimantNotification/civilClaimantNotification.service' import { DefendantNotificationService } from './services/defendantNotification/defendantNotification.service' +import { IndictmentCaseNotificationService } from './services/indictmentCaseNotification/indictmentCaseNotification.service' import { InstitutionNotificationService } from './services/institutionNotification/institutionNotification.service' import { SubpoenaNotificationService } from './services/subpoenaNotification/subpoenaNotification.service' import { InternalNotificationController } from './internalNotification.controller' @@ -47,6 +48,7 @@ import { NotificationDispatchService } from './notificationDispatch.service' CaseNotificationService, CivilClaimantNotificationService, DefendantNotificationService, + IndictmentCaseNotificationService, InstitutionNotificationService, NotificationService, NotificationDispatchService, diff --git a/apps/judicial-system/backend/src/app/modules/notification/notificationDispatch.service.ts b/apps/judicial-system/backend/src/app/modules/notification/notificationDispatch.service.ts index 13884be1e9e0..dbaff1fd065d 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/notificationDispatch.service.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/notificationDispatch.service.ts @@ -3,16 +3,20 @@ import { Injectable, InternalServerErrorException, } from '@nestjs/common' +import { ConfigType } from '@nestjs/config' import { type Logger, LOGGER_PROVIDER } from '@island.is/logging' import { MessageService, MessageType } from '@island.is/judicial-system/message' import { + EventNotificationType, + IndictmentCaseNotificationType, InstitutionNotificationType, InstitutionType, NotificationDispatchType, } from '@island.is/judicial-system/types' +import { Case } from '../case' import { Institution, InstitutionService } from '../institution' import { DeliverResponse } from './models/deliver.response' @@ -63,4 +67,43 @@ export class NotificationDispatchService { return { delivered: true } } + + private async dispatchIndictmentSentToPublicProsecutorNotifications( + theCase: Case, + ): Promise { + return this.messageService.sendMessagesToQueue([ + { + type: MessageType.INDICTMENT_CASE_NOTIFICATION, + caseId: theCase.id, + body: { + type: IndictmentCaseNotificationType.INDICTMENT_VERDICT_INFO, + }, + }, + ]) + } + + async dispatchEventNotification( + type: EventNotificationType, + theCase: Case, + ): Promise { + try { + switch (type) { + case EventNotificationType.INDICTMENT_SENT_TO_PUBLIC_PROSECUTOR: + await this.dispatchIndictmentSentToPublicProsecutorNotifications( + theCase, + ) + break + default: + throw new InternalServerErrorException( + `Invalid notification type ${type}`, + ) + } + } catch (error) { + this.logger.error('Failed to dispatch event notification', error) + + return { delivered: false } + } + + return { delivered: true } + } } diff --git a/apps/judicial-system/backend/src/app/modules/notification/services/indictmentCaseNotification/indictmentCaseNotification.service.ts b/apps/judicial-system/backend/src/app/modules/notification/services/indictmentCaseNotification/indictmentCaseNotification.service.ts new file mode 100644 index 000000000000..821662fa0ca0 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/notification/services/indictmentCaseNotification/indictmentCaseNotification.service.ts @@ -0,0 +1,155 @@ +import { + Inject, + Injectable, + InternalServerErrorException, +} from '@nestjs/common' +import { InjectModel } from '@nestjs/sequelize' + +import { IntlService } from '@island.is/cms-translations' +import { EmailService } from '@island.is/email-service' +import { type Logger, LOGGER_PROVIDER } from '@island.is/logging' +import { type ConfigType } from '@island.is/nest/config' + +import { + CaseIndictmentRulingDecision, + IndictmentCaseNotificationType, + IndictmentDecision, +} from '@island.is/judicial-system/types' + +import { Case } from '../../../case' +import { EventService } from '../../../event' +import { BaseNotificationService } from '../../baseNotification.service' +import { DeliverResponse } from '../../models/deliver.response' +import { Notification, Recipient } from '../../models/notification.model' +import { notificationModuleConfig } from '../../notification.config' +import { strings } from './indictmentCaseNotification.strings' + +@Injectable() +export class IndictmentCaseNotificationService extends BaseNotificationService { + constructor( + @InjectModel(Notification) + notificationModel: typeof Notification, + @Inject(notificationModuleConfig.KEY) + config: ConfigType, + @Inject(LOGGER_PROVIDER) logger: Logger, + intlService: IntlService, + emailService: EmailService, + eventService: EventService, + ) { + super( + notificationModel, + emailService, + intlService, + config, + eventService, + logger, + ) + } + + private async sendEmails( + theCase: Case, + notificationType: IndictmentCaseNotificationType, + subject: string, + body: string, + to: { name?: string; email?: string }[], + ) { + const promises: Promise[] = [] + + for (const recipient of to) { + if (recipient.email && recipient.name) { + promises.push( + this.sendEmail( + subject, + body, + recipient.name, + recipient.email, + undefined, + true, + ), + ) + } + } + + const recipients = await Promise.all(promises) + + return this.recordNotification(theCase.id, notificationType, recipients) + } + + private async sendVerdictInfoNotification( + theCase: Case, + ): Promise { + const institutionId = theCase.prosecutor?.institution?.id + const institutionEmail = + (institutionId && + this.config.email.policeInstitutionEmails[institutionId]) ?? + undefined + + const hasRuling = + theCase.indictmentRulingDecision === CaseIndictmentRulingDecision.RULING + + if (!institutionEmail || !hasRuling) { + // institution does not want to receive these emails or the case does not have a ruling + return { delivered: true } + } + + const formattedSubject = this.formatMessage( + strings.indictmentCompletedWithRuling.subject, + { + courtCaseNumber: theCase.courtCaseNumber, + }, + ) + + const formattedBody = this.formatMessage( + strings.indictmentCompletedWithRuling.body, + { + courtCaseNumber: theCase.courtCaseNumber, + courtName: theCase.court?.name, + serviceRequirement: + theCase.defendants && theCase.defendants[0].serviceRequirement, + caseOrigin: theCase.origin, + }, + ) + + return this.sendEmails( + theCase, + IndictmentCaseNotificationType.INDICTMENT_VERDICT_INFO, + formattedSubject, + formattedBody, + [ + { + name: theCase.prosecutor?.institution?.name, + email: institutionEmail, + }, + ], + ) + } + + private sendNotification( + notificationType: IndictmentCaseNotificationType, + theCase: Case, + ): Promise { + switch (notificationType) { + case IndictmentCaseNotificationType.INDICTMENT_VERDICT_INFO: + return this.sendVerdictInfoNotification(theCase) + + default: + throw new InternalServerErrorException( + `Invalid indictment notification type: ${notificationType}`, + ) + } + } + + async sendIndictmentCaseNotification( + type: IndictmentCaseNotificationType, + theCase: Case, + ): Promise { + await this.refreshFormatMessage() + try { + return await this.sendNotification(type, theCase) + } catch (error) { + this.logger.error('Failed to send indictment case notification', error) + + return { delivered: false } + } + } +} diff --git a/apps/judicial-system/backend/src/app/modules/notification/services/indictmentCaseNotification/indictmentCaseNotification.strings.ts b/apps/judicial-system/backend/src/app/modules/notification/services/indictmentCaseNotification/indictmentCaseNotification.strings.ts new file mode 100644 index 000000000000..c9ed5ec52513 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/notification/services/indictmentCaseNotification/indictmentCaseNotification.strings.ts @@ -0,0 +1,19 @@ +import { defineMessages } from '@formatjs/intl' + +export const strings = { + indictmentCompletedWithRuling: defineMessages({ + subject: { + id: 'judicial.system.backend:indictment_case_notifications.verdict_service.subject', + defaultMessage: 'MĂĄli lokiĂ° {courtCaseNumber}', + description: + 'NotaĂ° sem titill Ă­ tilkynningu um stöðu birtingar dĂłms Ă­ lokinni ĂĄkĂŠru', + }, + body: { + id: 'judicial.system.backend:indictment_case_notifications.verdict_service.body', + defaultMessage: + 'MĂĄli {courtCaseNumber} hjĂĄ {courtName} hefur veriĂ° lokiĂ°.\n\nNiĂ°urstaĂ°a: DĂłmur\n\n{serviceRequirement, select, REQUIRED {Birta skal dĂłmfellda dĂłminn} NOT_REQUIRED {Birting dĂłms ekki ĂŸĂ¶rf} NOT_APPLICABLE {DĂłmfelldi var viĂ°staddur dĂłmsuppkvaĂ°ningu} other {}}\n\n{caseOrigin, select, LOKE {DĂłmur er aĂ°gengilegur Ă­ LÖKE.} other {}}', + description: + 'NotaĂ° sem body Ă­ tilkynningu um stöðu birtingar dĂłms Ă­ lokinni ĂĄkĂŠru', + }, + }), +} diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/createTestingNotificationModule.ts b/apps/judicial-system/backend/src/app/modules/notification/test/createTestingNotificationModule.ts index c5ea4ac34521..fa41ef517272 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/createTestingNotificationModule.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/createTestingNotificationModule.ts @@ -33,6 +33,7 @@ import { NotificationDispatchService } from '../notificationDispatch.service' import { CaseNotificationService } from '../services/caseNotification/caseNotification.service' import { CivilClaimantNotificationService } from '../services/civilClaimantNotification/civilClaimantNotification.service' import { DefendantNotificationService } from '../services/defendantNotification/defendantNotification.service' +import { IndictmentCaseNotificationService } from '../services/indictmentCaseNotification/indictmentCaseNotification.service' import { InstitutionNotificationService } from '../services/institutionNotification/institutionNotification.service' jest.mock('@island.is/judicial-system/message') @@ -130,6 +131,7 @@ export const createTestingNotificationModule = async () => { InstitutionNotificationService, DefendantNotificationService, CivilClaimantNotificationService, + IndictmentCaseNotificationService, ], }) .useMocker((token) => { @@ -158,6 +160,9 @@ export const createTestingNotificationModule = async () => { internalNotificationController: notificationModule.get( InternalNotificationController, ), + indictmentCaseNotificationService: notificationModule.get( + IndictmentCaseNotificationService, + ), } notificationModule.close() diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/eventNotificationDispatch/eventNotificationDispatch.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/eventNotificationDispatch/eventNotificationDispatch.spec.ts new file mode 100644 index 000000000000..490b135e8429 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/eventNotificationDispatch/eventNotificationDispatch.spec.ts @@ -0,0 +1,77 @@ +import { uuid } from 'uuidv4' + +import { MessageService, MessageType } from '@island.is/judicial-system/message' +import { + EventNotificationType, + IndictmentCaseNotificationType, +} from '@island.is/judicial-system/types' + +import { createTestingNotificationModule } from '../../createTestingNotificationModule' + +import { Case } from '../../../../case' +import { InternalNotificationController } from '../../../internalNotification.controller' + +describe('InternalNotificationController - Dispatch event notifications', () => { + const theCase = { id: uuid() } as Case + let mockMessageService: MessageService + let internalNotificationController: InternalNotificationController + + beforeEach(async () => { + const { messageService, internalNotificationController: controller } = + await createTestingNotificationModule() + + mockMessageService = messageService + internalNotificationController = controller + + const mockSendMessagesToQueue = + messageService.sendMessagesToQueue as jest.Mock + mockSendMessagesToQueue.mockResolvedValueOnce(undefined) + }) + + const notificationScenarios = [ + { + notificationType: + EventNotificationType.INDICTMENT_SENT_TO_PUBLIC_PROSECUTOR, + expectedMessage: { + type: MessageType.INDICTMENT_CASE_NOTIFICATION, + caseId: theCase.id, + body: { + type: IndictmentCaseNotificationType.INDICTMENT_VERDICT_INFO, + }, + }, + }, + ] + + it.each( + notificationScenarios.map(({ notificationType, expectedMessage }) => ({ + notificationType, + expectedMessage, + description: `should send message to queue for notification type ${notificationType}`, + })), + )('$description', async ({ notificationType, expectedMessage }) => { + const result = + await internalNotificationController.dispatchEventNotification( + theCase.id, + theCase, + { type: notificationType }, + ) + + expect(mockMessageService.sendMessagesToQueue).toHaveBeenCalledWith([ + expectedMessage, + ]) + expect(result).toEqual({ delivered: true }) + }) + + it('will fail if a new EventNotificationType is missing from the tests', () => { + const allNotificationTypes = Object.values(EventNotificationType) + const testedNotificationTypes = notificationScenarios.map( + (scenario) => scenario.notificationType, + ) + + const missingNotificationTypes = allNotificationTypes.filter( + (type) => !testedNotificationTypes.includes(type), + ) + + expect(missingNotificationTypes).toEqual([]) + }) +}) diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/indictmentCaseNotification/sendIndictmentVerdictInfoNotification.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/indictmentCaseNotification/sendIndictmentVerdictInfoNotification.spec.ts new file mode 100644 index 000000000000..3c4af29a137b --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/indictmentCaseNotification/sendIndictmentVerdictInfoNotification.spec.ts @@ -0,0 +1,146 @@ +import { uuid } from 'uuidv4' + +import { EmailService } from '@island.is/email-service' + +import { + CaseIndictmentRulingDecision, + CaseOrigin, + IndictmentCaseNotificationType, + ServiceRequirement, +} from '@island.is/judicial-system/types' + +import { + createTestingNotificationModule, + createTestUsers, +} from '../../createTestingNotificationModule' + +import { Case } from '../../../../case' +import { DeliverResponse } from '../../../models/deliver.response' + +interface Then { + result: DeliverResponse + error: Error +} + +type GivenWhenThen = ( + theCase: Case, + notificationType: IndictmentCaseNotificationType, +) => Promise + +describe('IndictmentCaseService', () => { + const { prosecutorsOffice, defender } = createTestUsers([ + 'prosecutorsOffice', + 'defender', + ]) + const caseId = uuid() + const courtName = uuid() + const prosecutorsOfficeName = prosecutorsOffice.name + const prosecutorsOfficeEmail = prosecutorsOffice.email + const prosecutorInstitutionId = uuid() + const courtCaseNumber = uuid() + let theCase = { + id: caseId, + court: { name: courtName }, + origin: CaseOrigin.LOKE, + defendants: [ + { + defenderNationalId: defender.nationalId, + defenderName: defender.name, + defenderEmail: defender.email, + serviceRequirement: ServiceRequirement.REQUIRED, + }, + ], + prosecutor: { + institution: { name: prosecutorsOfficeName, id: prosecutorInstitutionId }, + }, + + courtCaseNumber, + } as Case + + let mockEmailService: EmailService + let givenWhenThen: GivenWhenThen + + process.env.POLICE_INSTITUTIONS_EMAILS = `{"${prosecutorInstitutionId}": "${prosecutorsOfficeEmail}"}` + + beforeEach(async () => { + const { emailService, indictmentCaseNotificationService } = + await createTestingNotificationModule() + + mockEmailService = emailService + + givenWhenThen = async ( + theCase: Case, + notificationType: IndictmentCaseNotificationType, + ) => { + const then = {} as Then + + await indictmentCaseNotificationService + .sendIndictmentCaseNotification(notificationType, theCase) + .then((result) => (then.result = result)) + .catch((error) => (then.error = error)) + + return then + } + }) + + describe('notifications sent to institution with registered e-mail', () => { + it('should not send a notification if indictment ruling decision is not RULING', async () => { + const then = await givenWhenThen( + theCase, + IndictmentCaseNotificationType.INDICTMENT_VERDICT_INFO, + ) + + expect(mockEmailService.sendEmail).toBeCalledTimes(0) + expect(then.result.delivered).toEqual(true) + }) + + it('should send a notification when indictment ruling decision is RULING', async () => { + const caseWithRulingDecision = { + ...theCase, + indictmentRulingDecision: CaseIndictmentRulingDecision.RULING, + } as Case + + const then = await givenWhenThen( + caseWithRulingDecision, + IndictmentCaseNotificationType.INDICTMENT_VERDICT_INFO, + ) + + expect(mockEmailService.sendEmail).toBeCalledWith( + expect.objectContaining({ + to: [ + { address: prosecutorsOfficeEmail, name: prosecutorsOfficeName }, + ], + subject: expect.stringContaining(`MĂĄli lokiĂ° ${courtCaseNumber}`), + html: expect.stringContaining( + `MĂĄli ${courtCaseNumber} hjĂĄ ${courtName} hefur veriĂ° lokiĂ°.`, + ), + }), + ) + + expect(then.result).toEqual({ delivered: true }) + }) + }) + + describe('notifications sent to institution without registered e-mail', () => { + it('should not send a notification', async () => { + const invalidProsecutorInstitutionId = uuid() + theCase = { + ...theCase, + prosecutor: { + ...theCase.prosecutor, + institution: { + ...theCase.prosecutor?.institution, + id: invalidProsecutorInstitutionId, + }, + }, + } as Case + + await givenWhenThen( + theCase, + IndictmentCaseNotificationType.INDICTMENT_VERDICT_INFO, + ) + + expect(mockEmailService.sendEmail).not.toBeCalled() + }) + }) +}) diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendIndictmentsWaitingForConfirmationNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendIndictmentsWaitingForConfirmationNotifications.spec.ts index 3c20d0d96a26..86c82c0c7275 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendIndictmentsWaitingForConfirmationNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendIndictmentsWaitingForConfirmationNotifications.spec.ts @@ -60,7 +60,7 @@ describe('InternalNotificationController - Send indictments waiting for confirma const then = {} as Then await internalNotificationController - .sendNotification({ + .sendInstitutionNotification({ type: InstitutionNotificationType.INDICTMENTS_WAITING_FOR_CONFIRMATION, prosecutorsOfficeId, }) diff --git a/charts/judicial-system/values.dev.yaml b/charts/judicial-system/values.dev.yaml index dd58423affdc..c5d0f700d69b 100644 --- a/charts/judicial-system/values.dev.yaml +++ b/charts/judicial-system/values.dev.yaml @@ -225,6 +225,7 @@ judicial-system-backend: NOVA_PASSWORD: '/k8s/judicial-system/NOVA_PASSWORD' NOVA_URL: '/k8s/judicial-system/NOVA_URL' NOVA_USERNAME: '/k8s/judicial-system/NOVA_USERNAME' + POLICE_INSTITUTIONS_EMAILS: '/k8s/judicial-system/POLICE_INSTITUTIONS_EMAILS' PRISON_ADMIN_EMAIL: '/k8s/judicial-system/PRISON_ADMIN_EMAIL' PRISON_ADMIN_INDICTMENT_EMAILS: '/k8s/judicial-system/PRISON_ADMIN_INDICTMENT_EMAILS' PRISON_EMAIL: '/k8s/judicial-system/PRISON_EMAIL' diff --git a/charts/judicial-system/values.prod.yaml b/charts/judicial-system/values.prod.yaml index ffcef752f5ec..e0747b1254c4 100644 --- a/charts/judicial-system/values.prod.yaml +++ b/charts/judicial-system/values.prod.yaml @@ -225,6 +225,7 @@ judicial-system-backend: NOVA_PASSWORD: '/k8s/judicial-system/NOVA_PASSWORD' NOVA_URL: '/k8s/judicial-system/NOVA_URL' NOVA_USERNAME: '/k8s/judicial-system/NOVA_USERNAME' + POLICE_INSTITUTIONS_EMAILS: '/k8s/judicial-system/POLICE_INSTITUTIONS_EMAILS' PRISON_ADMIN_EMAIL: '/k8s/judicial-system/PRISON_ADMIN_EMAIL' PRISON_ADMIN_INDICTMENT_EMAILS: '/k8s/judicial-system/PRISON_ADMIN_INDICTMENT_EMAILS' PRISON_EMAIL: '/k8s/judicial-system/PRISON_EMAIL' diff --git a/charts/judicial-system/values.staging.yaml b/charts/judicial-system/values.staging.yaml index 30afa9a56e74..e6b543a61ce6 100644 --- a/charts/judicial-system/values.staging.yaml +++ b/charts/judicial-system/values.staging.yaml @@ -225,6 +225,7 @@ judicial-system-backend: NOVA_PASSWORD: '/k8s/judicial-system/NOVA_PASSWORD' NOVA_URL: '/k8s/judicial-system/NOVA_URL' NOVA_USERNAME: '/k8s/judicial-system/NOVA_USERNAME' + POLICE_INSTITUTIONS_EMAILS: '/k8s/judicial-system/POLICE_INSTITUTIONS_EMAILS' PRISON_ADMIN_EMAIL: '/k8s/judicial-system/PRISON_ADMIN_EMAIL' PRISON_ADMIN_INDICTMENT_EMAILS: '/k8s/judicial-system/PRISON_ADMIN_INDICTMENT_EMAILS' PRISON_EMAIL: '/k8s/judicial-system/PRISON_EMAIL' diff --git a/charts/services/judicial-system-backend/values.dev.yaml b/charts/services/judicial-system-backend/values.dev.yaml index 8aeb2656041e..df2ab994c0bc 100644 --- a/charts/services/judicial-system-backend/values.dev.yaml +++ b/charts/services/judicial-system-backend/values.dev.yaml @@ -138,6 +138,7 @@ secrets: NOVA_PASSWORD: '/k8s/judicial-system/NOVA_PASSWORD' NOVA_URL: '/k8s/judicial-system/NOVA_URL' NOVA_USERNAME: '/k8s/judicial-system/NOVA_USERNAME' + POLICE_INSTITUTIONS_EMAILS: '/k8s/judicial-system/POLICE_INSTITUTIONS_EMAILS' PRISON_ADMIN_EMAIL: '/k8s/judicial-system/PRISON_ADMIN_EMAIL' PRISON_ADMIN_INDICTMENT_EMAILS: '/k8s/judicial-system/PRISON_ADMIN_INDICTMENT_EMAILS' PRISON_EMAIL: '/k8s/judicial-system/PRISON_EMAIL' diff --git a/charts/services/judicial-system-backend/values.prod.yaml b/charts/services/judicial-system-backend/values.prod.yaml index 2807d821b9d9..d0047fef3de8 100644 --- a/charts/services/judicial-system-backend/values.prod.yaml +++ b/charts/services/judicial-system-backend/values.prod.yaml @@ -138,6 +138,7 @@ secrets: NOVA_PASSWORD: '/k8s/judicial-system/NOVA_PASSWORD' NOVA_URL: '/k8s/judicial-system/NOVA_URL' NOVA_USERNAME: '/k8s/judicial-system/NOVA_USERNAME' + POLICE_INSTITUTIONS_EMAILS: '/k8s/judicial-system/POLICE_INSTITUTIONS_EMAILS' PRISON_ADMIN_EMAIL: '/k8s/judicial-system/PRISON_ADMIN_EMAIL' PRISON_ADMIN_INDICTMENT_EMAILS: '/k8s/judicial-system/PRISON_ADMIN_INDICTMENT_EMAILS' PRISON_EMAIL: '/k8s/judicial-system/PRISON_EMAIL' diff --git a/charts/services/judicial-system-backend/values.staging.yaml b/charts/services/judicial-system-backend/values.staging.yaml index 7221f2acfd51..30787a83c52d 100644 --- a/charts/services/judicial-system-backend/values.staging.yaml +++ b/charts/services/judicial-system-backend/values.staging.yaml @@ -138,6 +138,7 @@ secrets: NOVA_PASSWORD: '/k8s/judicial-system/NOVA_PASSWORD' NOVA_URL: '/k8s/judicial-system/NOVA_URL' NOVA_USERNAME: '/k8s/judicial-system/NOVA_USERNAME' + POLICE_INSTITUTIONS_EMAILS: '/k8s/judicial-system/POLICE_INSTITUTIONS_EMAILS' PRISON_ADMIN_EMAIL: '/k8s/judicial-system/PRISON_ADMIN_EMAIL' PRISON_ADMIN_INDICTMENT_EMAILS: '/k8s/judicial-system/PRISON_ADMIN_INDICTMENT_EMAILS' PRISON_EMAIL: '/k8s/judicial-system/PRISON_EMAIL' diff --git a/libs/judicial-system/message/src/lib/message.ts b/libs/judicial-system/message/src/lib/message.ts index c8650bc09256..c93854bab4e6 100644 --- a/libs/judicial-system/message/src/lib/message.ts +++ b/libs/judicial-system/message/src/lib/message.ts @@ -31,6 +31,8 @@ export enum MessageType { NOTIFICATION_DISPATCH = 'NOTIFICATION_DISPATCH', DEFENDANT_NOTIFICATION = 'DEFENDANT_NOTIFICATION', CIVIL_CLAIMANT_NOTIFICATION = 'CIVIL_CLAIMANT_NOTIFICATION', + INDICTMENT_CASE_NOTIFICATION = 'INDICTMENT_CASE_NOTIFICATION', + EVENT_NOTIFICATION_DISPATCH = 'EVENT_NOTIFICATION_DISPATCH', } export const messageEndpoint: { [key in MessageType]: string } = { @@ -68,6 +70,8 @@ export const messageEndpoint: { [key in MessageType]: string } = { NOTIFICATION_DISPATCH: 'notification/dispatch', DEFENDANT_NOTIFICATION: 'defendantNotification', CIVIL_CLAIMANT_NOTIFICATION: 'civilClaimantNotification', + INDICTMENT_CASE_NOTIFICATION: 'indictmentCaseNotification', + EVENT_NOTIFICATION_DISPATCH: 'eventNotification/dispatch', } export type Message = { diff --git a/libs/judicial-system/types/src/index.ts b/libs/judicial-system/types/src/index.ts index 140f6c9bc92a..82a11272ee06 100644 --- a/libs/judicial-system/types/src/index.ts +++ b/libs/judicial-system/types/src/index.ts @@ -19,6 +19,8 @@ export { NotificationDispatchType, DefendantNotificationType, CivilClaimantNotificationType, + IndictmentCaseNotificationType, + EventNotificationType, notificationTypes, } from './lib/notification' export type { Institution } from './lib/institution' diff --git a/libs/judicial-system/types/src/lib/notification.ts b/libs/judicial-system/types/src/lib/notification.ts index e9b8df060e63..347cef7b5852 100644 --- a/libs/judicial-system/types/src/lib/notification.ts +++ b/libs/judicial-system/types/src/lib/notification.ts @@ -20,6 +20,10 @@ export enum CaseNotificationType { CASE_FILES_UPDATED = 'CASE_FILES_UPDATED', } +export enum IndictmentCaseNotificationType { + INDICTMENT_VERDICT_INFO = 'INDICTMENT_VERDICT_INFO', +} + export enum DefendantNotificationType { DEFENDANT_SELECTED_DEFENDER = 'DEFENDANT_SELECTED_DEFENDER', DEFENDER_ASSIGNED = 'DEFENDER_ASSIGNED', @@ -40,34 +44,40 @@ export enum InstitutionNotificationType { INDICTMENTS_WAITING_FOR_CONFIRMATION = 'INDICTMENTS_WAITING_FOR_CONFIRMATION', } +export enum EventNotificationType { + INDICTMENT_SENT_TO_PUBLIC_PROSECUTOR = 'INDICTMENT_SENT_TO_PUBLIC_PROSECUTOR', +} + export enum NotificationType { - HEADS_UP = CaseNotificationType.HEADS_UP, - READY_FOR_COURT = CaseNotificationType.READY_FOR_COURT, - RECEIVED_BY_COURT = CaseNotificationType.RECEIVED_BY_COURT, - COURT_DATE = CaseNotificationType.COURT_DATE, - RULING = CaseNotificationType.RULING, - MODIFIED = CaseNotificationType.MODIFIED, - REVOKED = CaseNotificationType.REVOKED, ADVOCATE_ASSIGNED = CaseNotificationType.ADVOCATE_ASSIGNED, - DEFENDANTS_NOT_UPDATED_AT_COURT = CaseNotificationType.DEFENDANTS_NOT_UPDATED_AT_COURT, - APPEAL_TO_COURT_OF_APPEALS = CaseNotificationType.APPEAL_TO_COURT_OF_APPEALS, - APPEAL_RECEIVED_BY_COURT = CaseNotificationType.APPEAL_RECEIVED_BY_COURT, - APPEAL_STATEMENT = CaseNotificationType.APPEAL_STATEMENT, + APPEAL_CASE_FILES_UPDATED = CaseNotificationType.APPEAL_CASE_FILES_UPDATED, APPEAL_COMPLETED = CaseNotificationType.APPEAL_COMPLETED, APPEAL_JUDGES_ASSIGNED = CaseNotificationType.APPEAL_JUDGES_ASSIGNED, - APPEAL_CASE_FILES_UPDATED = CaseNotificationType.APPEAL_CASE_FILES_UPDATED, + APPEAL_RECEIVED_BY_COURT = CaseNotificationType.APPEAL_RECEIVED_BY_COURT, + APPEAL_STATEMENT = CaseNotificationType.APPEAL_STATEMENT, + APPEAL_TO_COURT_OF_APPEALS = CaseNotificationType.APPEAL_TO_COURT_OF_APPEALS, APPEAL_WITHDRAWN = CaseNotificationType.APPEAL_WITHDRAWN, - INDICTMENT_DENIED = CaseNotificationType.INDICTMENT_DENIED, - INDICTMENT_RETURNED = CaseNotificationType.INDICTMENT_RETURNED, CASE_FILES_UPDATED = CaseNotificationType.CASE_FILES_UPDATED, + COURT_DATE = CaseNotificationType.COURT_DATE, DEFENDANT_SELECTED_DEFENDER = DefendantNotificationType.DEFENDANT_SELECTED_DEFENDER, + DEFENDANTS_NOT_UPDATED_AT_COURT = CaseNotificationType.DEFENDANTS_NOT_UPDATED_AT_COURT, DEFENDER_ASSIGNED = DefendantNotificationType.DEFENDER_ASSIGNED, + HEADS_UP = CaseNotificationType.HEADS_UP, + INDICTMENT_DENIED = CaseNotificationType.INDICTMENT_DENIED, + INDICTMENT_RETURNED = CaseNotificationType.INDICTMENT_RETURNED, INDICTMENT_SENT_TO_PRISON_ADMIN = DefendantNotificationType.INDICTMENT_SENT_TO_PRISON_ADMIN, + INDICTMENT_SENT_TO_PUBLIC_PROSECUTOR = EventNotificationType.INDICTMENT_SENT_TO_PUBLIC_PROSECUTOR, + INDICTMENT_VERDICT_INFO = IndictmentCaseNotificationType.INDICTMENT_VERDICT_INFO, INDICTMENT_WITHDRAWN_FROM_PRISON_ADMIN = DefendantNotificationType.INDICTMENT_WITHDRAWN_FROM_PRISON_ADMIN, - SERVICE_SUCCESSFUL = SubpoenaNotificationType.SERVICE_SUCCESSFUL, + INDICTMENTS_WAITING_FOR_CONFIRMATION = InstitutionNotificationType.INDICTMENTS_WAITING_FOR_CONFIRMATION, + MODIFIED = CaseNotificationType.MODIFIED, + READY_FOR_COURT = CaseNotificationType.READY_FOR_COURT, + RECEIVED_BY_COURT = CaseNotificationType.RECEIVED_BY_COURT, + REVOKED = CaseNotificationType.REVOKED, + RULING = CaseNotificationType.RULING, SERVICE_FAILED = SubpoenaNotificationType.SERVICE_FAILED, + SERVICE_SUCCESSFUL = SubpoenaNotificationType.SERVICE_SUCCESSFUL, SPOKESPERSON_ASSIGNED = CivilClaimantNotificationType.SPOKESPERSON_ASSIGNED, - INDICTMENTS_WAITING_FOR_CONFIRMATION = InstitutionNotificationType.INDICTMENTS_WAITING_FOR_CONFIRMATION, } export const notificationTypes = Object.values(NotificationType) From 818f74ecfd20365bf41b3414430e4b328f5d4582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAnar=20Vestmann?= <43557895+RunarVestmann@users.noreply.github.com> Date: Tue, 10 Dec 2024 14:12:51 +0000 Subject: [PATCH 84/85] feat(web): Generic List - Stop date from taking up space if it is not present (#17188) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../components/GenericList/GenericList.tsx | 67 +++++++++++++------ 1 file changed, 47 insertions(+), 20 deletions(-) diff --git a/apps/web/components/GenericList/GenericList.tsx b/apps/web/components/GenericList/GenericList.tsx index bbc7592e5172..3bc8dfc8ba48 100644 --- a/apps/web/components/GenericList/GenericList.tsx +++ b/apps/web/components/GenericList/GenericList.tsx @@ -15,6 +15,7 @@ import { GridColumn, GridContainer, GridRow, + Hyphen, Icon, type IconProps, Inline, @@ -85,9 +86,11 @@ export const NonClickableItem = ({ item }: ItemProps) => { - - {item.date && format(new Date(item.date), 'dd.MM.yyyy')} - + {item.date && ( + + {format(new Date(item.date), 'dd.MM.yyyy')} + + )} {item.title} @@ -153,24 +156,48 @@ export const ClickableItem = ({ item, baseUrl }: ClickableItemProps) => { - - - - {item.date && format(new Date(item.date), 'dd.MM.yyyy')} + {item.date && ( + + + + {format(new Date(item.date), 'dd.MM.yyyy')} + + {icon && ( + + )} + + + )} + + + + {item.title} - {icon && ( - - )} - - - - {item.title} - + + {!item.date && icon && ( + + + + + + )} + {item.cardIntro?.length > 0 && ( {webRichText(item.cardIntro ?? [])} From 83ba8fac56aa2cf176515b0736f5c02b75ffffb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9E=C3=B3rey=20J=C3=B3na?= Date: Tue, 10 Dec 2024 15:07:29 +0000 Subject: [PATCH 85/85] feat(native-app): Move app to nx (#17098) * feat: init nx create app stuff * feat: copy src folder from app project * feat: add dependencies * feat: updating podFile * feat copy ios folder from older project * feat: copy android folder * feat: final fixes for ios app working locally * feat: remove old src folder * feat: add readme * feat: add storybook folder * feat: add back test stuff in * feat: add more stuff from old project * feat: add all scripts to package.json * feat: update packages that still have * to proper version * feat: final additions * feat: renaming app folder and removing old native folder * fix: remove mobile from workspaces * fix: remove extra extension * feat: add back root level read me * feat: add prettier and bundle config files * remove google-services.json file * feat: update gitignore file * fix: path for e2e project.json * chore: bump version to 1.4.8 * remove cache: true from nx.json * add tags to project.json * feat: fix yarn.lock * fix: valid json in nx.json * fix: remove app-e2e folder * fix: update package.json * feat: add license to package.json in app * remove private: true to fix license check * chore: nx format:write update dirty files * fix: remove dom from lib in tsconfig.json * update entryFilein project.json * fix: update settings.gradle after android build failing * fix: update import from build.gradle * feat: use relative import for ui * fix: remove duplicates from gitignore * fix: remove from tsconfig things that are already inherited * chore: remove babel-plugin-module-resolver * remove webpack.config.js * fix: update package.json to have same version as package.json in app * fix: update yarn.lock * fix: linting * chore: update react version to match root project * chore: update Podfile * chore: remove patch for old version of rn --------- Co-authored-by: andes-it Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .gitignore | 68 +- .../react-native-npm-0.71.1-f5d237f240.patch | Bin 7111 -> 0 bytes apps/native/app/.babelrc.js | 14 + apps/native/app/.bundle/config | 2 +- apps/native/app/.eslintrc.json | 11 +- apps/native/app/.gitignore | 81 -- apps/native/app/Gemfile | 2 +- apps/native/app/android/app/build.gradle | 4 +- apps/native/app/android/settings.gradle | 4 +- apps/native/app/babel.config.js | 17 - .../ios/IslandApp.xcodeproj/project.pbxproj | 10 +- apps/native/app/ios/IslandApp/Info.plist | 2 +- apps/native/app/ios/Podfile.lock | 416 +++--- apps/native/app/jest.config.js | 3 - apps/native/app/jest.config.ts | 22 + apps/native/app/metro.config.js | 39 +- apps/native/app/package.json | 11 +- apps/native/app/project.json | 76 +- .../bottom-tabs-indicator.tsx | 2 +- .../src/components/offline/offline-banner.tsx | 2 +- .../src/components/pin-keypad/pin-keypad.tsx | 2 +- .../visualized-pin-code.tsx | 2 +- .../src/hooks/use-connectivity-indicator.ts | 3 +- apps/native/app/src/lib/show-picker.ts | 2 +- .../src/screens/air-discount/air-discount.tsx | 22 +- .../air-discount/airfares-usage-table.tsx | 2 +- .../app/src/screens/app-lock/app-lock.tsx | 3 +- .../src/screens/applications/applications.tsx | 2 +- .../components/applications-list.tsx | 19 +- .../components/applications-preview.tsx | 10 +- .../app/src/screens/assets/assets-detail.tsx | 3 +- .../src/screens/assets/assets-overview.tsx | 3 +- .../src/screens/cognito-auth/cognito-auth.tsx | 3 +- .../document-detail/document-detail.tsx | 4 +- .../utils/get-buttons-for-actions.tsx | 2 +- .../app/src/screens/family/family-details.tsx | 3 +- .../src/screens/family/family-overview.tsx | 9 +- .../components/finance-status-card.tsx | 11 +- .../screens/finance/finance-status-detail.tsx | 3 +- .../app/src/screens/finance/finance.tsx | 3 +- .../src/screens/health/health-overview.tsx | 18 +- .../src/screens/home/air-discount-module.tsx | 17 +- .../src/screens/home/applications-module.tsx | 2 +- .../app/src/screens/home/hello-module.tsx | 3 +- .../app/src/screens/home/home-options.tsx | 2 +- apps/native/app/src/screens/home/home.tsx | 2 +- .../app/src/screens/home/inbox-module.tsx | 17 +- .../app/src/screens/home/licenses-module.tsx | 15 +- .../src/screens/home/onboarding-module.tsx | 6 +- .../app/src/screens/home/vehicles-module.tsx | 15 +- .../app/src/screens/inbox/inbox-filter.tsx | 11 +- apps/native/app/src/screens/inbox/inbox.tsx | 21 +- .../license-scanner/license-scan-detail.tsx | 3 +- .../license-scanner/license-scanner.tsx | 3 +- apps/native/app/src/screens/login/login.tsx | 3 +- .../app/src/screens/login/testing-login.tsx | 3 +- apps/native/app/src/screens/more/more.tsx | 3 +- .../app/src/screens/more/personal-info.tsx | 3 +- .../screens/notifications/notifications.tsx | 19 +- .../onboarding/onboarding-biometrics.tsx | 3 +- .../onboarding/onboarding-notifications.tsx | 3 +- .../onboarding/onboarding-pin-code.tsx | 3 +- .../app/src/screens/passkey/passkey.tsx | 3 +- .../src/screens/settings/edit-bank-info.tsx | 3 +- .../app/src/screens/settings/edit-confirm.tsx | 15 +- .../app/src/screens/settings/edit-email.tsx | 3 +- .../app/src/screens/settings/edit-phone.tsx | 3 +- .../app/src/screens/settings/settings.tsx | 15 +- .../app/src/screens/update-app/update-app.tsx | 3 +- .../components/vaccination-card.tsx | 14 +- .../src/screens/vaccinations/vaccinations.tsx | 14 +- .../vehicles/components/mileage-cell.tsx | 3 +- .../vehicles/components/vehicle-item.tsx | 3 +- .../vehicles/vehicle-mileage.screen.tsx | 16 +- .../src/screens/vehicles/vehicles-detail.tsx | 3 +- .../app/src/screens/vehicles/vehicles.tsx | 3 +- .../wallet-pass/components/field-render.tsx | 3 +- .../src/screens/wallet-pass/wallet-pass.tsx | 12 +- .../wallet-passport/wallet-passport.tsx | 25 +- .../screens/wallet/components/wallet-item.tsx | 3 +- apps/native/app/src/screens/wallet/wallet.tsx | 3 +- apps/native/app/src/test-setup.ts | 1 + apps/native/app/src/types/react-native.d.ts | 4 - .../app/src/types/styled-components.d.ts | 2 +- apps/native/app/src/ui/index.ts | 2 + .../app/src/ui/lib/card/license-card.tsx | 5 +- .../src/ui/lib/date-picker/date-picker.tsx | 2 +- apps/native/app/src/ui/lib/detail/header.tsx | 2 +- .../app/src/ui/lib/empty-state/empty-list.tsx | 2 +- .../lib/empty-state/empty-state.stories.tsx | 3 +- apps/native/app/src/ui/lib/input/input.tsx | 3 +- apps/native/app/src/ui/lib/label/label.tsx | 3 +- apps/native/app/src/ui/lib/link/link-text.tsx | 2 +- apps/native/app/src/ui/lib/list/list-item.tsx | 3 +- .../src/ui/lib/problem/problem-template.tsx | 4 +- .../app/src/ui/lib/search-bar/search-bar.tsx | 3 +- .../app/src/utils/applications-utils.ts | 2 +- .../src/utils/get-theme-with-preferences.ts | 3 +- apps/native/app/tsconfig.app.json | 18 + apps/native/app/tsconfig.json | 28 +- apps/native/app/tsconfig.spec.json | 21 + package.json | 14 +- tsconfig.base.json | 1 - yarn.lock | 1138 +++++++++++++++-- 104 files changed, 1774 insertions(+), 711 deletions(-) delete mode 100644 .yarn/patches/react-native-npm-0.71.1-f5d237f240.patch create mode 100644 apps/native/app/.babelrc.js delete mode 100644 apps/native/app/.gitignore delete mode 100644 apps/native/app/babel.config.js delete mode 100644 apps/native/app/jest.config.js create mode 100644 apps/native/app/jest.config.ts create mode 100644 apps/native/app/src/test-setup.ts create mode 100644 apps/native/app/tsconfig.app.json create mode 100644 apps/native/app/tsconfig.spec.json diff --git a/.gitignore b/.gitignore index 9ad6dbd3146e..46c71ffd1796 100644 --- a/.gitignore +++ b/.gitignore @@ -102,4 +102,70 @@ apps/**/index.html # E2E outputs test-results/ playwright-report/ -tmp-sessions/ \ No newline at end of file +tmp-sessions/ + +# React Native + +# Xcode +# +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +*.dSYM.zip +*.xcuserstate +**/.xcode.env.local +ios/*.cer +ios/*.certSigningRequest +ios/*.mobileprovision +ios/*.p12 + +# Android/IntelliJ +# +build/ +.idea +.gradle +local.properties +*.iml +*.hprof +.cxx/ +*.keystore +!debug.keystore +google-services.json +service-account.json + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/ + +**/fastlane/report.xml +**/fastlane/Preview.html +**/fastlane/screenshots +**/fastlane/test_output + +# Bundle artifact +*.jsbundle + +# Ruby / CocoaPods +**/Pods/ +**/vendor/bundle/ + +# Temporary files created by Metro to check the health of the file watcher +.metro-health-check* + +# testing +/coverage diff --git a/.yarn/patches/react-native-npm-0.71.1-f5d237f240.patch b/.yarn/patches/react-native-npm-0.71.1-f5d237f240.patch deleted file mode 100644 index 0c49e9c60fe66c17c545ab9549636c66191766f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7111 zcmeHMQES^U5SF*XinoV8?oot2B&4w&+jWvYr6nEorG;kfY0xRsDfQ-AL3Y}8r9W-# zC+#onWIL>D+Ooo6u<1bhq|@C=`a~j&6RDK6*PEv$Wxh9=o=!`pfzE!m-5f5cOf#UL zlmOZff+UXJG#Buh2EQquH{iiYhC4I4BlnPqOb+x=xN<1y_8+X3zN>3HmR2_eq@QuGPg!saFxgXH_SuLjn|SeUXVPa2Iu zr;4ITN`xqQHdAZXcPotVGr~wt1kvP%zsiim3vxmLhdC+9-d#pGuNBLHWxz6E8L$jk z1|Gpcb9Zh&e}s=UwG3DW{tpJs{{zKhaNJ=1N(U>W$Q8E`fx)5$(=vAJE>HfwHMise0)m7r~9j#YIT zBUYC&cFbk`@`fEXZ5$srXg>!G5mqb%f0+UI9X(PMQx;j817#(JTu>#e1cobaCOb}m z@p3O0TBX9DEz?YRbIkjWbOWoQ~sEa&POzbOFlx+bEWUx5ScOQzd zP0Py#?9xf&+rf}Mic)CaZk?6oqS*EQnTe?M-1{K4ntQ48nJ!a_$Ds73|B0o!uOS0g zfdA@h|M+9Q2w6#rJUaNEM7iy_F)Im>g*!d+9?B!Q0NpP25;|_JJ_miF(-PW}WyMM>0NFOFw%3?y TYp)K$920K1TIv0p?6^Mw@M2M9 diff --git a/apps/native/app/.babelrc.js b/apps/native/app/.babelrc.js new file mode 100644 index 000000000000..01d571a26229 --- /dev/null +++ b/apps/native/app/.babelrc.js @@ -0,0 +1,14 @@ +module.exports = function (api) { + api.cache(true) + + return { + presets: [ + ['module:@react-native/babel-preset', { useTransformReactJSX: true }], + ], + plugins: [ + // react-native-reanimated/plugin has to be listed last. + // Reason: In short, the Reanimated babel plugin automatically converts special JavaScript functions (called worklets) to allow them to be passed and run on the UI thread. + 'react-native-reanimated/plugin', + ], + } +} diff --git a/apps/native/app/.bundle/config b/apps/native/app/.bundle/config index 848943bb5274..d137d242ed74 100644 --- a/apps/native/app/.bundle/config +++ b/apps/native/app/.bundle/config @@ -1,2 +1,2 @@ BUNDLE_PATH: "vendor/bundle" -BUNDLE_FORCE_RUBY_PLATFORM: 1 +BUNDLE_FORCE_RUBY_PLATFORM: 1 \ No newline at end of file diff --git a/apps/native/app/.eslintrc.json b/apps/native/app/.eslintrc.json index d5e992022c7c..d6ea918430f5 100644 --- a/apps/native/app/.eslintrc.json +++ b/apps/native/app/.eslintrc.json @@ -6,7 +6,6 @@ "**/*.stories.tsx", "lib/pdf417/*-min.js" ], - "rules": {}, "overrides": [ { "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], @@ -20,7 +19,13 @@ "func-style": "off" } }, - { "files": ["*.ts", "*.tsx"], "rules": {} }, - { "files": ["*.js", "*.jsx"], "rules": {} } + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } ] } diff --git a/apps/native/app/.gitignore b/apps/native/app/.gitignore deleted file mode 100644 index edf1e8ff9ab0..000000000000 --- a/apps/native/app/.gitignore +++ /dev/null @@ -1,81 +0,0 @@ -# OSX -# -.DS_Store - -# Xcode -# -build/ -*.pbxuser -!default.pbxuser -*.mode1v3 -!default.mode1v3 -*.mode2v3 -!default.mode2v3 -*.perspectivev3 -!default.perspectivev3 -xcuserdata -*.xccheckout -*.moved-aside -DerivedData -*.hmap -*.ipa -*.dSYM.zip -*.xcuserstate -**/.xcode.env.local -ios/*.cer -ios/*.certSigningRequest -ios/*.mobileprovision -ios/*.p12 - -# Android/IntelliJ -# -build/ -.idea -.gradle -local.properties -*.iml -*.hprof -.cxx/ -*.keystore -!debug.keystore -google-services.json -service-account.json - -# node.js -# -node_modules/ -npm-debug.log -yarn-error.log - -# fastlane -# -# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the -# screenshots whenever they are needed. -# For more information about the recommended setup visit: -# https://docs.fastlane.tools/best-practices/source-control/ - -**/fastlane/report.xml -**/fastlane/Preview.html -**/fastlane/screenshots -**/fastlane/test_output - -# Bundle artifact -*.jsbundle - -# Ruby / CocoaPods -**/Pods/ -/vendor/bundle/ - -# Temporary files created by Metro to check the health of the file watcher -.metro-health-check* - -# testing -/coverage - -# Yarn -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/sdks -!.yarn/versions diff --git a/apps/native/app/Gemfile b/apps/native/app/Gemfile index 8d72c37a80c3..ec21e32c9a69 100644 --- a/apps/native/app/Gemfile +++ b/apps/native/app/Gemfile @@ -6,4 +6,4 @@ ruby ">= 2.6.10" # Cocoapods 1.15 introduced a bug which break the build. We will remove the upper # bound in the template on Cocoapods with next React Native release. gem 'cocoapods', '>= 1.13', '< 1.15' -gem 'activesupport', '>= 6.1.7.5', '< 7.1.0' +gem 'activesupport', '>= 6.1.7.5', '< 7.1.0' \ No newline at end of file diff --git a/apps/native/app/android/app/build.gradle b/apps/native/app/android/app/build.gradle index 98b5f346f132..a32464e07a8f 100644 --- a/apps/native/app/android/app/build.gradle +++ b/apps/native/app/android/app/build.gradle @@ -103,7 +103,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode getMyVersionCode(143) - versionName "1.4.7" + versionName "1.4.8" manifestPlaceholders = [ appAuthRedirectScheme: "is.island.app" // project.config.get("BUNDLE_ID_ANDROID") ] @@ -171,4 +171,4 @@ dependencies { } } -apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) +apply from: file("../../../../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) diff --git a/apps/native/app/android/settings.gradle b/apps/native/app/android/settings.gradle index 7ed793eca85f..12979ce6cda2 100644 --- a/apps/native/app/android/settings.gradle +++ b/apps/native/app/android/settings.gradle @@ -1,10 +1,10 @@ rootProject.name = 'IslandApp' -apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); +apply from: file("../../../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) include ':app', ':react-native-code-push' -project(':react-native-code-push').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-code-push/android/app') +project(':react-native-code-push').projectDir = new File(rootProject.projectDir, '../../../../node_modules/react-native-code-push/android/app') include ':react-native-clipboard' project(':react-native-clipboard').projectDir = new File(rootProject.projectDir, '../../node_modules/@react-native-clipboard/clipboard/android') diff --git a/apps/native/app/babel.config.js b/apps/native/app/babel.config.js deleted file mode 100644 index 24924e0cb1b1..000000000000 --- a/apps/native/app/babel.config.js +++ /dev/null @@ -1,17 +0,0 @@ -module.exports = { - presets: ['module:@react-native/babel-preset'], - plugins: [ - [ - 'module-resolver', - { - alias: { - '@ui': './src/ui', - '@island.is/application/types': '../../../libs/application/types/src', - }, - }, - ], - // react-native-reanimated/plugin has to be listed last. - // Reason: In short, the Reanimated babel plugin automatically converts special JavaScript functions (called worklets) to allow them to be passed and run on the UI thread. - 'react-native-reanimated/plugin', - ], -} diff --git a/apps/native/app/ios/IslandApp.xcodeproj/project.pbxproj b/apps/native/app/ios/IslandApp.xcodeproj/project.pbxproj index a6c26ffd0c01..4bcd227127d6 100644 --- a/apps/native/app/ios/IslandApp.xcodeproj/project.pbxproj +++ b/apps/native/app/ios/IslandApp.xcodeproj/project.pbxproj @@ -301,7 +301,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "set -e\n\nWITH_ENVIRONMENT=\"../node_modules/react-native/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"../node_modules/react-native/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; + shellScript = "set -e\n\nWITH_ENVIRONMENT=\"../../../../node_modules/react-native/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"../../../../node_modules/react-native/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; }; 1D5D14183630A5603D9A6D3E /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; @@ -425,7 +425,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open \"$SRCROOT/../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n fi\nfi\n"; + shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../../../../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open \"$SRCROOT/../../../../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n fi\nfi\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -554,7 +554,7 @@ "$(inherited)", " ", ); - REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; + REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; VALIDATE_PRODUCT = YES; @@ -629,7 +629,7 @@ "$(inherited)", " ", ); - REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; + REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; VALIDATE_PRODUCT = YES; @@ -752,7 +752,7 @@ "$(inherited)", " ", ); - REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; + REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; }; diff --git a/apps/native/app/ios/IslandApp/Info.plist b/apps/native/app/ios/IslandApp/Info.plist index 6d9a1a7cf5ac..42e798076119 100644 --- a/apps/native/app/ios/IslandApp/Info.plist +++ b/apps/native/app/ios/IslandApp/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.4.7 + 1.4.8 CFBundleSignature ???? CFBundleURLTypes diff --git a/apps/native/app/ios/Podfile.lock b/apps/native/app/ios/Podfile.lock index 3d5d52150dc7..4a489728f683 100644 --- a/apps/native/app/ios/Podfile.lock +++ b/apps/native/app/ios/Podfile.lock @@ -1,9 +1,9 @@ PODS: - - AppAuth (1.7.5): - - AppAuth/Core (= 1.7.5) - - AppAuth/ExternalUserAgent (= 1.7.5) - - AppAuth/Core (1.7.5) - - AppAuth/ExternalUserAgent (1.7.5): + - AppAuth (1.7.6): + - AppAuth/Core (= 1.7.6) + - AppAuth/ExternalUserAgent (= 1.7.6) + - AppAuth/Core (1.7.6) + - AppAuth/ExternalUserAgent (1.7.6): - AppAuth/Core - Base64 (1.1.2) - boost (1.83.0) @@ -141,7 +141,7 @@ PODS: - GoogleUtilities/ISASwizzler (~> 7.8) - GoogleUtilities/MethodSwizzler (~> 7.8) - nanopb (< 2.30910.0, >= 2.30908.0) - - FirebaseRemoteConfig (10.27.0): + - FirebaseRemoteConfig (10.29.0): - FirebaseABTesting (~> 10.0) - FirebaseCore (~> 10.0) - FirebaseInstallations (~> 10.0) @@ -149,8 +149,8 @@ PODS: - FirebaseSharedSwift (~> 10.0) - GoogleUtilities/Environment (~> 7.8) - "GoogleUtilities/NSData+zlib (~> 7.8)" - - FirebaseRemoteConfigInterop (10.27.0) - - FirebaseSharedSwift (10.27.0) + - FirebaseRemoteConfigInterop (10.29.0) + - FirebaseSharedSwift (10.29.0) - fmt (9.1.0) - glog (0.3.5) - GoogleAppMeasurement (10.3.0): @@ -1567,113 +1567,113 @@ PODS: - Yoga (0.0.0) DEPENDENCIES: - - boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`) - - CodePush (from `../node_modules/react-native-code-push`) - - "DatadogSDKReactNative (from `../node_modules/@datadog/mobile-react-native`)" - - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - - EXApplication (from `../node_modules/expo-application/ios`) - - EXConstants (from `../node_modules/expo-constants/ios`) - - EXNotifications (from `../node_modules/expo-notifications/ios`) - - Expo (from `../node_modules/expo`) - - ExpoAsset (from `../node_modules/expo-asset/ios`) - - ExpoFileSystem (from `../node_modules/expo-file-system/ios`) - - ExpoFont (from `../node_modules/expo-font/ios`) - - ExpoHaptics (from `../node_modules/expo-haptics/ios`) - - ExpoKeepAwake (from `../node_modules/expo-keep-awake/ios`) - - ExpoLocalAuthentication (from `../node_modules/expo-local-authentication/ios`) - - ExpoModulesCore (from `../node_modules/expo-modules-core`) - - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) + - boost (from `../../../../node_modules/react-native/third-party-podspecs/boost.podspec`) + - CodePush (from `../../../../node_modules/react-native-code-push`) + - "DatadogSDKReactNative (from `../../../../node_modules/@datadog/mobile-react-native`)" + - DoubleConversion (from `../../../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) + - EXApplication (from `../../../../node_modules/expo-application/ios`) + - EXConstants (from `../../../../node_modules/expo-constants/ios`) + - EXNotifications (from `../../../../node_modules/expo-notifications/ios`) + - Expo (from `../../../../node_modules/expo`) + - ExpoAsset (from `../../../../node_modules/expo-asset/ios`) + - ExpoFileSystem (from `../../../../node_modules/expo-file-system/ios`) + - ExpoFont (from `../../../../node_modules/expo-font/ios`) + - ExpoHaptics (from `../../../../node_modules/expo-haptics/ios`) + - ExpoKeepAwake (from `../../../../node_modules/expo-keep-awake/ios`) + - ExpoLocalAuthentication (from `../../../../node_modules/expo-local-authentication/ios`) + - ExpoModulesCore (from `../../../../node_modules/expo-modules-core`) + - FBLazyVector (from `../../../../node_modules/react-native/Libraries/FBLazyVector`) - Firebase - FirebaseABTesting - FirebaseCore - FirebaseCoreInternal - FirebaseInstallations - - fmt (from `../node_modules/react-native/third-party-podspecs/fmt.podspec`) - - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) + - fmt (from `../../../../node_modules/react-native/third-party-podspecs/fmt.podspec`) + - glog (from `../../../../node_modules/react-native/third-party-podspecs/glog.podspec`) - GoogleUtilities - - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) - - Interactable (from `../node_modules/react-native-interactable`) - - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - - RCT-Folly/Fabric (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - - RCTDeprecation (from `../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`) - - RCTRequired (from `../node_modules/react-native/Libraries/Required`) - - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`) - - React (from `../node_modules/react-native/`) - - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`) + - hermes-engine (from `../../../../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) + - Interactable (from `../../../../node_modules/react-native-interactable`) + - RCT-Folly (from `../../../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) + - RCT-Folly/Fabric (from `../../../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) + - RCTDeprecation (from `../../../../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`) + - RCTRequired (from `../../../../node_modules/react-native/Libraries/Required`) + - RCTTypeSafety (from `../../../../node_modules/react-native/Libraries/TypeSafety`) + - React (from `../../../../node_modules/react-native/`) + - React-callinvoker (from `../../../../node_modules/react-native/ReactCommon/callinvoker`) - React-Codegen (from `build/generated/ios`) - - React-Core (from `../node_modules/react-native/`) - - React-Core/RCTWebSocket (from `../node_modules/react-native/`) - - React-CoreModules (from `../node_modules/react-native/React/CoreModules`) - - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`) - - React-debug (from `../node_modules/react-native/ReactCommon/react/debug`) - - React-Fabric (from `../node_modules/react-native/ReactCommon`) - - React-FabricImage (from `../node_modules/react-native/ReactCommon`) - - React-featureflags (from `../node_modules/react-native/ReactCommon/react/featureflags`) - - React-graphics (from `../node_modules/react-native/ReactCommon/react/renderer/graphics`) - - React-hermes (from `../node_modules/react-native/ReactCommon/hermes`) - - React-ImageManager (from `../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios`) - - React-jserrorhandler (from `../node_modules/react-native/ReactCommon/jserrorhandler`) - - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`) - - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) - - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector-modern`) - - React-jsitracing (from `../node_modules/react-native/ReactCommon/hermes/executor/`) - - React-logger (from `../node_modules/react-native/ReactCommon/logger`) - - React-Mapbuffer (from `../node_modules/react-native/ReactCommon`) - - react-native-app-auth (from `../node_modules/react-native-app-auth`) - - react-native-blob-util (from `../node_modules/react-native-blob-util`) - - "react-native-cookies (from `../node_modules/@react-native-cookies/cookies`)" - - react-native-date-picker (from `../node_modules/react-native-date-picker`) - - react-native-mmkv-storage (from `../node_modules/react-native-mmkv-storage`) - - "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)" - - react-native-passkey (from `../node_modules/react-native-passkey`) - - react-native-passkit-wallet (from `../node_modules/react-native-passkit-wallet`) - - react-native-pdf (from `../node_modules/react-native-pdf`) - - "react-native-progress-bar-android (from `../node_modules/@react-native-community/progress-bar-android`)" - - "react-native-progress-view (from `../node_modules/@react-native-community/progress-view`)" - - react-native-quick-base64 (from `../node_modules/react-native-quick-base64`) - - react-native-spotlight-search (from `../node_modules/react-native-spotlight-search`) - - react-native-webview (from `../node_modules/react-native-webview`) - - React-nativeconfig (from `../node_modules/react-native/ReactCommon`) - - React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`) - - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`) - - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`) - - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`) - - React-RCTAppDelegate (from `../node_modules/react-native/Libraries/AppDelegate`) - - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`) - - React-RCTFabric (from `../node_modules/react-native/React`) - - React-RCTImage (from `../node_modules/react-native/Libraries/Image`) - - React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`) - - React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`) - - React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`) - - React-RCTText (from `../node_modules/react-native/Libraries/Text`) - - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`) - - React-rendererdebug (from `../node_modules/react-native/ReactCommon/react/renderer/debug`) - - React-rncore (from `../node_modules/react-native/ReactCommon`) - - React-RuntimeApple (from `../node_modules/react-native/ReactCommon/react/runtime/platform/ios`) - - React-RuntimeCore (from `../node_modules/react-native/ReactCommon/react/runtime`) - - React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`) - - React-RuntimeHermes (from `../node_modules/react-native/ReactCommon/react/runtime`) - - React-runtimescheduler (from `../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`) - - React-utils (from `../node_modules/react-native/ReactCommon/react/utils`) - - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) - - ReactNativeKeyboardManager (from `../node_modules/react-native-keyboard-manager`) - - ReactNativeNavigation (from `../node_modules/react-native-navigation`) - - "RNCAsyncStorage (from `../node_modules/@react-native-community/async-storage`)" - - "RNCClipboard (from `../node_modules/@react-native-clipboard/clipboard`)" - - RNDeviceInfo (from `../node_modules/react-native-device-info`) - - "RNFBAnalytics (from `../node_modules/@react-native-firebase/analytics`)" - - "RNFBApp (from `../node_modules/@react-native-firebase/app`)" - - "RNFBMessaging (from `../node_modules/@react-native-firebase/messaging`)" - - "RNFBPerf (from `../node_modules/@react-native-firebase/perf`)" - - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - - RNInAppBrowser (from `../node_modules/react-native-inappbrowser-reborn`) - - RNKeychain (from `../node_modules/react-native-keychain`) - - RNQuickAction (from `../node_modules/react-native-quick-actions`) + - React-Core (from `../../../../node_modules/react-native/`) + - React-Core/RCTWebSocket (from `../../../../node_modules/react-native/`) + - React-CoreModules (from `../../../../node_modules/react-native/React/CoreModules`) + - React-cxxreact (from `../../../../node_modules/react-native/ReactCommon/cxxreact`) + - React-debug (from `../../../../node_modules/react-native/ReactCommon/react/debug`) + - React-Fabric (from `../../../../node_modules/react-native/ReactCommon`) + - React-FabricImage (from `../../../../node_modules/react-native/ReactCommon`) + - React-featureflags (from `../../../../node_modules/react-native/ReactCommon/react/featureflags`) + - React-graphics (from `../../../../node_modules/react-native/ReactCommon/react/renderer/graphics`) + - React-hermes (from `../../../../node_modules/react-native/ReactCommon/hermes`) + - React-ImageManager (from `../../../../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios`) + - React-jserrorhandler (from `../../../../node_modules/react-native/ReactCommon/jserrorhandler`) + - React-jsi (from `../../../../node_modules/react-native/ReactCommon/jsi`) + - React-jsiexecutor (from `../../../../node_modules/react-native/ReactCommon/jsiexecutor`) + - React-jsinspector (from `../../../../node_modules/react-native/ReactCommon/jsinspector-modern`) + - React-jsitracing (from `../../../../node_modules/react-native/ReactCommon/hermes/executor/`) + - React-logger (from `../../../../node_modules/react-native/ReactCommon/logger`) + - React-Mapbuffer (from `../../../../node_modules/react-native/ReactCommon`) + - react-native-app-auth (from `../../../../node_modules/react-native-app-auth`) + - react-native-blob-util (from `../../../../node_modules/react-native-blob-util`) + - "react-native-cookies (from `../../../../node_modules/@react-native-cookies/cookies`)" + - react-native-date-picker (from `../../../../node_modules/react-native-date-picker`) + - react-native-mmkv-storage (from `../../../../node_modules/react-native-mmkv-storage`) + - "react-native-netinfo (from `../../../../node_modules/@react-native-community/netinfo`)" + - react-native-passkey (from `../../../../node_modules/react-native-passkey`) + - react-native-passkit-wallet (from `../../../../node_modules/react-native-passkit-wallet`) + - react-native-pdf (from `../../../../node_modules/react-native-pdf`) + - "react-native-progress-bar-android (from `../../../../node_modules/@react-native-community/progress-bar-android`)" + - "react-native-progress-view (from `../../../../node_modules/@react-native-community/progress-view`)" + - react-native-quick-base64 (from `../../../../node_modules/react-native-quick-base64`) + - react-native-spotlight-search (from `../../../../node_modules/react-native-spotlight-search`) + - react-native-webview (from `../../../../node_modules/react-native-webview`) + - React-nativeconfig (from `../../../../node_modules/react-native/ReactCommon`) + - React-NativeModulesApple (from `../../../../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`) + - React-perflogger (from `../../../../node_modules/react-native/ReactCommon/reactperflogger`) + - React-RCTActionSheet (from `../../../../node_modules/react-native/Libraries/ActionSheetIOS`) + - React-RCTAnimation (from `../../../../node_modules/react-native/Libraries/NativeAnimation`) + - React-RCTAppDelegate (from `../../../../node_modules/react-native/Libraries/AppDelegate`) + - React-RCTBlob (from `../../../../node_modules/react-native/Libraries/Blob`) + - React-RCTFabric (from `../../../../node_modules/react-native/React`) + - React-RCTImage (from `../../../../node_modules/react-native/Libraries/Image`) + - React-RCTLinking (from `../../../../node_modules/react-native/Libraries/LinkingIOS`) + - React-RCTNetwork (from `../../../../node_modules/react-native/Libraries/Network`) + - React-RCTSettings (from `../../../../node_modules/react-native/Libraries/Settings`) + - React-RCTText (from `../../../../node_modules/react-native/Libraries/Text`) + - React-RCTVibration (from `../../../../node_modules/react-native/Libraries/Vibration`) + - React-rendererdebug (from `../../../../node_modules/react-native/ReactCommon/react/renderer/debug`) + - React-rncore (from `../../../../node_modules/react-native/ReactCommon`) + - React-RuntimeApple (from `../../../../node_modules/react-native/ReactCommon/react/runtime/platform/ios`) + - React-RuntimeCore (from `../../../../node_modules/react-native/ReactCommon/react/runtime`) + - React-runtimeexecutor (from `../../../../node_modules/react-native/ReactCommon/runtimeexecutor`) + - React-RuntimeHermes (from `../../../../node_modules/react-native/ReactCommon/react/runtime`) + - React-runtimescheduler (from `../../../../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`) + - React-utils (from `../../../../node_modules/react-native/ReactCommon/react/utils`) + - ReactCommon/turbomodule/core (from `../../../../node_modules/react-native/ReactCommon`) + - ReactNativeKeyboardManager (from `../../../../node_modules/react-native-keyboard-manager`) + - ReactNativeNavigation (from `../../../../node_modules/react-native-navigation`) + - "RNCAsyncStorage (from `../../../../node_modules/@react-native-community/async-storage`)" + - "RNCClipboard (from `../../../../node_modules/@react-native-clipboard/clipboard`)" + - RNDeviceInfo (from `../../../../node_modules/react-native-device-info`) + - "RNFBAnalytics (from `../../../../node_modules/@react-native-firebase/analytics`)" + - "RNFBApp (from `../../../../node_modules/@react-native-firebase/app`)" + - "RNFBMessaging (from `../../../../node_modules/@react-native-firebase/messaging`)" + - "RNFBPerf (from `../../../../node_modules/@react-native-firebase/perf`)" + - RNGestureHandler (from `../../../../node_modules/react-native-gesture-handler`) + - RNInAppBrowser (from `../../../../node_modules/react-native-inappbrowser-reborn`) + - RNKeychain (from `../../../../node_modules/react-native-keychain`) + - RNQuickAction (from `../../../../node_modules/react-native-quick-actions`) - RNReanimated (from `../node_modules/react-native-reanimated`) - - RNShare (from `../node_modules/react-native-share`) - - RNSVG (from `../node_modules/react-native-svg`) + - RNShare (from `../../../../node_modules/react-native-share`) + - RNSVG (from `../../../../node_modules/react-native-svg`) - VisionCamera (from `../node_modules/react-native-vision-camera`) - - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) + - Yoga (from `../../../../node_modules/react-native/ReactCommon/yoga`) SPEC REPOS: trunk: @@ -1709,207 +1709,207 @@ SPEC REPOS: EXTERNAL SOURCES: boost: - :podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec" + :podspec: "../../../../node_modules/react-native/third-party-podspecs/boost.podspec" CodePush: - :path: "../node_modules/react-native-code-push" + :path: "../../../../node_modules/react-native-code-push" DatadogSDKReactNative: - :path: "../node_modules/@datadog/mobile-react-native" + :path: "../../../../node_modules/@datadog/mobile-react-native" DoubleConversion: - :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" + :podspec: "../../../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" EXApplication: - :path: "../node_modules/expo-application/ios" + :path: "../../../../node_modules/expo-application/ios" EXConstants: - :path: "../node_modules/expo-constants/ios" + :path: "../../../../node_modules/expo-constants/ios" EXNotifications: - :path: "../node_modules/expo-notifications/ios" + :path: "../../../../node_modules/expo-notifications/ios" Expo: - :path: "../node_modules/expo" + :path: "../../../../node_modules/expo" ExpoAsset: - :path: "../node_modules/expo-asset/ios" + :path: "../../../../node_modules/expo-asset/ios" ExpoFileSystem: - :path: "../node_modules/expo-file-system/ios" + :path: "../../../../node_modules/expo-file-system/ios" ExpoFont: - :path: "../node_modules/expo-font/ios" + :path: "../../../../node_modules/expo-font/ios" ExpoHaptics: - :path: "../node_modules/expo-haptics/ios" + :path: "../../../../node_modules/expo-haptics/ios" ExpoKeepAwake: - :path: "../node_modules/expo-keep-awake/ios" + :path: "../../../../node_modules/expo-keep-awake/ios" ExpoLocalAuthentication: - :path: "../node_modules/expo-local-authentication/ios" + :path: "../../../../node_modules/expo-local-authentication/ios" ExpoModulesCore: - :path: "../node_modules/expo-modules-core" + :path: "../../../../node_modules/expo-modules-core" FBLazyVector: - :path: "../node_modules/react-native/Libraries/FBLazyVector" + :path: "../../../../node_modules/react-native/Libraries/FBLazyVector" fmt: - :podspec: "../node_modules/react-native/third-party-podspecs/fmt.podspec" + :podspec: "../../../../node_modules/react-native/third-party-podspecs/fmt.podspec" glog: - :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec" + :podspec: "../../../../node_modules/react-native/third-party-podspecs/glog.podspec" hermes-engine: - :podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec" + :podspec: "../../../../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec" :tag: hermes-2024-06-28-RNv0.74.3-7bda0c267e76d11b68a585f84cfdd65000babf85 Interactable: - :path: "../node_modules/react-native-interactable" + :path: "../../../../node_modules/react-native-interactable" RCT-Folly: - :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" + :podspec: "../../../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" RCTDeprecation: - :path: "../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation" + :path: "../../../../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation" RCTRequired: - :path: "../node_modules/react-native/Libraries/Required" + :path: "../../../../node_modules/react-native/Libraries/Required" RCTTypeSafety: - :path: "../node_modules/react-native/Libraries/TypeSafety" + :path: "../../../../node_modules/react-native/Libraries/TypeSafety" React: - :path: "../node_modules/react-native/" + :path: "../../../../node_modules/react-native/" React-callinvoker: - :path: "../node_modules/react-native/ReactCommon/callinvoker" + :path: "../../../../node_modules/react-native/ReactCommon/callinvoker" React-Codegen: :path: build/generated/ios React-Core: - :path: "../node_modules/react-native/" + :path: "../../../../node_modules/react-native/" React-CoreModules: - :path: "../node_modules/react-native/React/CoreModules" + :path: "../../../../node_modules/react-native/React/CoreModules" React-cxxreact: - :path: "../node_modules/react-native/ReactCommon/cxxreact" + :path: "../../../../node_modules/react-native/ReactCommon/cxxreact" React-debug: - :path: "../node_modules/react-native/ReactCommon/react/debug" + :path: "../../../../node_modules/react-native/ReactCommon/react/debug" React-Fabric: - :path: "../node_modules/react-native/ReactCommon" + :path: "../../../../node_modules/react-native/ReactCommon" React-FabricImage: - :path: "../node_modules/react-native/ReactCommon" + :path: "../../../../node_modules/react-native/ReactCommon" React-featureflags: - :path: "../node_modules/react-native/ReactCommon/react/featureflags" + :path: "../../../../node_modules/react-native/ReactCommon/react/featureflags" React-graphics: - :path: "../node_modules/react-native/ReactCommon/react/renderer/graphics" + :path: "../../../../node_modules/react-native/ReactCommon/react/renderer/graphics" React-hermes: - :path: "../node_modules/react-native/ReactCommon/hermes" + :path: "../../../../node_modules/react-native/ReactCommon/hermes" React-ImageManager: - :path: "../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios" + :path: "../../../../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios" React-jserrorhandler: - :path: "../node_modules/react-native/ReactCommon/jserrorhandler" + :path: "../../../../node_modules/react-native/ReactCommon/jserrorhandler" React-jsi: - :path: "../node_modules/react-native/ReactCommon/jsi" + :path: "../../../../node_modules/react-native/ReactCommon/jsi" React-jsiexecutor: - :path: "../node_modules/react-native/ReactCommon/jsiexecutor" + :path: "../../../../node_modules/react-native/ReactCommon/jsiexecutor" React-jsinspector: - :path: "../node_modules/react-native/ReactCommon/jsinspector-modern" + :path: "../../../../node_modules/react-native/ReactCommon/jsinspector-modern" React-jsitracing: - :path: "../node_modules/react-native/ReactCommon/hermes/executor/" + :path: "../../../../node_modules/react-native/ReactCommon/hermes/executor/" React-logger: - :path: "../node_modules/react-native/ReactCommon/logger" + :path: "../../../../node_modules/react-native/ReactCommon/logger" React-Mapbuffer: - :path: "../node_modules/react-native/ReactCommon" + :path: "../../../../node_modules/react-native/ReactCommon" react-native-app-auth: - :path: "../node_modules/react-native-app-auth" + :path: "../../../../node_modules/react-native-app-auth" react-native-blob-util: - :path: "../node_modules/react-native-blob-util" + :path: "../../../../node_modules/react-native-blob-util" react-native-cookies: - :path: "../node_modules/@react-native-cookies/cookies" + :path: "../../../../node_modules/@react-native-cookies/cookies" react-native-date-picker: - :path: "../node_modules/react-native-date-picker" + :path: "../../../../node_modules/react-native-date-picker" react-native-mmkv-storage: - :path: "../node_modules/react-native-mmkv-storage" + :path: "../../../../node_modules/react-native-mmkv-storage" react-native-netinfo: - :path: "../node_modules/@react-native-community/netinfo" + :path: "../../../../node_modules/@react-native-community/netinfo" react-native-passkey: - :path: "../node_modules/react-native-passkey" + :path: "../../../../node_modules/react-native-passkey" react-native-passkit-wallet: - :path: "../node_modules/react-native-passkit-wallet" + :path: "../../../../node_modules/react-native-passkit-wallet" react-native-pdf: - :path: "../node_modules/react-native-pdf" + :path: "../../../../node_modules/react-native-pdf" react-native-progress-bar-android: - :path: "../node_modules/@react-native-community/progress-bar-android" + :path: "../../../../node_modules/@react-native-community/progress-bar-android" react-native-progress-view: - :path: "../node_modules/@react-native-community/progress-view" + :path: "../../../../node_modules/@react-native-community/progress-view" react-native-quick-base64: - :path: "../node_modules/react-native-quick-base64" + :path: "../../../../node_modules/react-native-quick-base64" react-native-spotlight-search: - :path: "../node_modules/react-native-spotlight-search" + :path: "../../../../node_modules/react-native-spotlight-search" react-native-webview: - :path: "../node_modules/react-native-webview" + :path: "../../../../node_modules/react-native-webview" React-nativeconfig: - :path: "../node_modules/react-native/ReactCommon" + :path: "../../../../node_modules/react-native/ReactCommon" React-NativeModulesApple: - :path: "../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios" + :path: "../../../../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios" React-perflogger: - :path: "../node_modules/react-native/ReactCommon/reactperflogger" + :path: "../../../../node_modules/react-native/ReactCommon/reactperflogger" React-RCTActionSheet: - :path: "../node_modules/react-native/Libraries/ActionSheetIOS" + :path: "../../../../node_modules/react-native/Libraries/ActionSheetIOS" React-RCTAnimation: - :path: "../node_modules/react-native/Libraries/NativeAnimation" + :path: "../../../../node_modules/react-native/Libraries/NativeAnimation" React-RCTAppDelegate: - :path: "../node_modules/react-native/Libraries/AppDelegate" + :path: "../../../../node_modules/react-native/Libraries/AppDelegate" React-RCTBlob: - :path: "../node_modules/react-native/Libraries/Blob" + :path: "../../../../node_modules/react-native/Libraries/Blob" React-RCTFabric: - :path: "../node_modules/react-native/React" + :path: "../../../../node_modules/react-native/React" React-RCTImage: - :path: "../node_modules/react-native/Libraries/Image" + :path: "../../../../node_modules/react-native/Libraries/Image" React-RCTLinking: - :path: "../node_modules/react-native/Libraries/LinkingIOS" + :path: "../../../../node_modules/react-native/Libraries/LinkingIOS" React-RCTNetwork: - :path: "../node_modules/react-native/Libraries/Network" + :path: "../../../../node_modules/react-native/Libraries/Network" React-RCTSettings: - :path: "../node_modules/react-native/Libraries/Settings" + :path: "../../../../node_modules/react-native/Libraries/Settings" React-RCTText: - :path: "../node_modules/react-native/Libraries/Text" + :path: "../../../../node_modules/react-native/Libraries/Text" React-RCTVibration: - :path: "../node_modules/react-native/Libraries/Vibration" + :path: "../../../../node_modules/react-native/Libraries/Vibration" React-rendererdebug: - :path: "../node_modules/react-native/ReactCommon/react/renderer/debug" + :path: "../../../../node_modules/react-native/ReactCommon/react/renderer/debug" React-rncore: - :path: "../node_modules/react-native/ReactCommon" + :path: "../../../../node_modules/react-native/ReactCommon" React-RuntimeApple: - :path: "../node_modules/react-native/ReactCommon/react/runtime/platform/ios" + :path: "../../../../node_modules/react-native/ReactCommon/react/runtime/platform/ios" React-RuntimeCore: - :path: "../node_modules/react-native/ReactCommon/react/runtime" + :path: "../../../../node_modules/react-native/ReactCommon/react/runtime" React-runtimeexecutor: - :path: "../node_modules/react-native/ReactCommon/runtimeexecutor" + :path: "../../../../node_modules/react-native/ReactCommon/runtimeexecutor" React-RuntimeHermes: - :path: "../node_modules/react-native/ReactCommon/react/runtime" + :path: "../../../../node_modules/react-native/ReactCommon/react/runtime" React-runtimescheduler: - :path: "../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler" + :path: "../../../../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler" React-utils: - :path: "../node_modules/react-native/ReactCommon/react/utils" + :path: "../../../../node_modules/react-native/ReactCommon/react/utils" ReactCommon: - :path: "../node_modules/react-native/ReactCommon" + :path: "../../../../node_modules/react-native/ReactCommon" ReactNativeKeyboardManager: - :path: "../node_modules/react-native-keyboard-manager" + :path: "../../../../node_modules/react-native-keyboard-manager" ReactNativeNavigation: - :path: "../node_modules/react-native-navigation" + :path: "../../../../node_modules/react-native-navigation" RNCAsyncStorage: - :path: "../node_modules/@react-native-community/async-storage" + :path: "../../../../node_modules/@react-native-community/async-storage" RNCClipboard: - :path: "../node_modules/@react-native-clipboard/clipboard" + :path: "../../../../node_modules/@react-native-clipboard/clipboard" RNDeviceInfo: - :path: "../node_modules/react-native-device-info" + :path: "../../../../node_modules/react-native-device-info" RNFBAnalytics: - :path: "../node_modules/@react-native-firebase/analytics" + :path: "../../../../node_modules/@react-native-firebase/analytics" RNFBApp: - :path: "../node_modules/@react-native-firebase/app" + :path: "../../../../node_modules/@react-native-firebase/app" RNFBMessaging: - :path: "../node_modules/@react-native-firebase/messaging" + :path: "../../../../node_modules/@react-native-firebase/messaging" RNFBPerf: - :path: "../node_modules/@react-native-firebase/perf" + :path: "../../../../node_modules/@react-native-firebase/perf" RNGestureHandler: - :path: "../node_modules/react-native-gesture-handler" + :path: "../../../../node_modules/react-native-gesture-handler" RNInAppBrowser: - :path: "../node_modules/react-native-inappbrowser-reborn" + :path: "../../../../node_modules/react-native-inappbrowser-reborn" RNKeychain: - :path: "../node_modules/react-native-keychain" + :path: "../../../../node_modules/react-native-keychain" RNQuickAction: - :path: "../node_modules/react-native-quick-actions" + :path: "../../../../node_modules/react-native-quick-actions" RNReanimated: :path: "../node_modules/react-native-reanimated" RNShare: - :path: "../node_modules/react-native-share" + :path: "../../../../node_modules/react-native-share" RNSVG: - :path: "../node_modules/react-native-svg" + :path: "../../../../node_modules/react-native-svg" VisionCamera: :path: "../node_modules/react-native-vision-camera" Yoga: - :path: "../node_modules/react-native/ReactCommon/yoga" + :path: "../../../../node_modules/react-native/ReactCommon/yoga" SPEC CHECKSUMS: - AppAuth: 501c04eda8a8d11f179dbe8637b7a91bb7e5d2fa + AppAuth: d4f13a8fe0baf391b2108511793e4b479691fb73 Base64: cecfb41a004124895a7bcee567a89bae5a89d49b boost: d3f49c53809116a5d38da093a8aa78bf551aed09 CodePush: eaa66fc8dd9ff611304ecc48397ce8831ba79ac9 @@ -1938,9 +1938,9 @@ SPEC CHECKSUMS: FirebaseInstallations: 766dabca09fd94aef922538aaf144cc4a6fb6869 FirebaseMessaging: e345b219fd15d325f0cf2fef28cb8ce00d851b3f FirebasePerformance: 8f1c8e5a4fcc5a68400835518ee63a6d63dbff0c - FirebaseRemoteConfig: 37a2ba3c8c454be8553a41ba1a2f4a4f0b845670 - FirebaseRemoteConfigInterop: c55a739f5ab121792776e191d9fd437dc624a541 - FirebaseSharedSwift: a03fe7a59ee646fef71099a887f774fe25d745b8 + FirebaseRemoteConfig: 48ef3f243742a8d72422ccfc9f986e19d7de53fd + FirebaseRemoteConfigInterop: 6efda51fb5e2f15b16585197e26eaa09574e8a4d + FirebaseSharedSwift: 20530f495084b8d840f78a100d8c5ee613375f6e fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120 glog: fdfdfe5479092de0c4bdbebedd9056951f092c4f GoogleAppMeasurement: c7d6fff39bf2d829587d74088d582e32d75133c3 @@ -1962,7 +1962,7 @@ SPEC CHECKSUMS: RCTTypeSafety: f5ecbc86c5c5fa163c05acb7a1c5012e15b5f994 React: fc9fa7258eff606f44d58c5b233a82dc9cf09018 React-callinvoker: e3fab14d69607fb7e8e3a57e5a415aed863d3599 - React-Codegen: 6fa87b7c6b8efcd0cef4bfeaec8c8bc8a6abe75a + React-Codegen: df959b3e7962cad0125362bb448ae59d9c0c8dfe React-Core: 3a5fd9e781cecf87803e5b091496a606a3df774a React-CoreModules: cbf4707dafab8f9f826ac0c63a07d0bf5d01e256 React-cxxreact: 7b188556271e3c7fdf22a04819f6a6225045b9dd @@ -2030,7 +2030,7 @@ SPEC CHECKSUMS: RNInAppBrowser: e36d6935517101ccba0e875bac8ad7b0cb655364 RNKeychain: ff836453cba46938e0e9e4c22e43d43fa2c90333 RNQuickAction: 6d404a869dc872cde841ad3147416a670d13fa93 - RNReanimated: f9192536fa8a312c737eaf15c905f78831193ef1 + RNReanimated: d093daf3973a7ee9f77c10a9337e10390b2969e2 RNShare: 0fad69ae2d71de9d1f7b9a43acf876886a6cb99c RNSVG: 43b64ed39c14ce830d840903774154ca0c1f27ec SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d diff --git a/apps/native/app/jest.config.js b/apps/native/app/jest.config.js deleted file mode 100644 index 1fbafc9cb781..000000000000 --- a/apps/native/app/jest.config.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - preset: 'react-native', -} diff --git a/apps/native/app/jest.config.ts b/apps/native/app/jest.config.ts new file mode 100644 index 000000000000..3e943d58cfc5 --- /dev/null +++ b/apps/native/app/jest.config.ts @@ -0,0 +1,22 @@ +module.exports = { + displayName: 'IslandApp', + preset: 'react-native', + resolver: '@nx/jest/plugins/resolver', + moduleFileExtensions: ['ts', 'js', 'html', 'tsx', 'jsx'], + setupFilesAfterEnv: ['/src/test-setup.ts'], + moduleNameMapper: { + '\\.svg$': '@nx/react-native/plugins/jest/svg-mock', + }, + transform: { + '^.+.(js|ts|tsx)$': [ + 'babel-jest', + { + configFile: __dirname + '/.babelrc.js', + }, + ], + '^.+.(bmp|gif|jpg|jpeg|mp4|png|psd|svg|webp)$': require.resolve( + 'react-native/jest/assetFileTransformer.js', + ), + }, + coverageDirectory: '../../../coverage/apps/native/app', +} diff --git a/apps/native/app/metro.config.js b/apps/native/app/metro.config.js index 491f6980daae..70fab7425451 100644 --- a/apps/native/app/metro.config.js +++ b/apps/native/app/metro.config.js @@ -1,18 +1,8 @@ -const path = require('path') +const { withNxMetro } = require('@nx/react-native') const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config') -const libs = ['application/types'] - -const extraNodeModules = libs.reduce((acc, lib) => { - acc[`@island.is/${lib}`] = path.resolve(__dirname, `../../../libs/${lib}/src`) - return acc -}, {}) - -const watchFolders = [ - ...libs.map((lib) => path.resolve(__dirname, `../../../libs/${lib}/src`)), -] - -const nodeModulesPaths = [path.resolve(path.join(__dirname, './node_modules'))] +const defaultConfig = getDefaultConfig(__dirname) +const { assetExts, sourceExts } = defaultConfig.resolver /** * Metro configuration @@ -20,15 +10,22 @@ const nodeModulesPaths = [path.resolve(path.join(__dirname, './node_modules'))] * * @type {import('metro-config').MetroConfig} */ -const config = { +const customConfig = { + transformer: { + babelTransformerPath: require.resolve('react-native-svg-transformer'), + }, resolver: { - extraNodeModules: { - '@ui': path.resolve(__dirname + '/src/ui'), - ...extraNodeModules, - }, - nodeModulesPaths, + assetExts: assetExts.filter((ext) => ext !== 'svg'), + sourceExts: [...sourceExts, 'cjs', 'mjs', 'svg'], }, - watchFolders, } -module.exports = mergeConfig(getDefaultConfig(__dirname), config) +module.exports = withNxMetro(mergeConfig(defaultConfig, customConfig), { + // Change this to true to see debugging info. + // Useful if you have issues resolving modules + debug: false, + // all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx', 'json' + extensions: [], + // Specify folders to watch, in addition to Nx defaults (workspace libraries and node_modules) + watchFolders: [], +}) diff --git a/apps/native/app/package.json b/apps/native/app/package.json index 3e42a3ae7714..3ebb950c5cba 100644 --- a/apps/native/app/package.json +++ b/apps/native/app/package.json @@ -1,7 +1,7 @@ { "name": "@island.is/native-app", "version": "1.2.5", - "private": true, + "license": "MIT", "scripts": { "release:android": "cd android && ./gradlew bundleProdRelease bundleDevRelease", "android": "react-native run-android --mode devDebug", @@ -51,6 +51,8 @@ "@react-native/eslint-config": "0.74.87", "@react-native/metro-config": "0.74.87", "@react-native/typescript-config": "0.74.87", + "@testing-library/jest-native": "5.4.3", + "@testing-library/react-native": "12.9.0", "apollo3-cache-persist": "0.15.0", "compare-versions": "6.1.1", "configcat-js": "7.0.0", @@ -63,8 +65,9 @@ "expo-notifications": "0.28.9", "intl": "1.2.5", "lodash": "4.17.21", + "metro-config": "0.81.0", "path-to-regexp": "6.2.2", - "react": "18.2.0", + "react": "18.3.1", "react-intl": "5.20.12", "react-native": "0.74.5", "react-native-app-auth": "7.2.0", @@ -96,9 +99,6 @@ "styled-components": "6.1.11", "zustand": "3.5.12" }, - "installConfig": { - "hoistingLimits": "workspaces" - }, "devDependencies": { "@babel/core": "^7.20.0", "@babel/preset-env": "^7.20.0", @@ -112,7 +112,6 @@ "babel-jest": "^29.6.3", "babel-loader": "^8.3.0", "babel-plugin-formatjs": "10.3.9", - "babel-plugin-module-resolver": "5.0.2", "jest": "29.7.0", "react-test-renderer": "18.2.0", "typescript": "5.3.3" diff --git a/apps/native/app/project.json b/apps/native/app/project.json index 337b98afd2fe..a23ee071865a 100644 --- a/apps/native/app/project.json +++ b/apps/native/app/project.json @@ -6,30 +6,82 @@ "tags": ["scope:js"], "generators": {}, "targets": { - "serve": { - "executor": "nx:run-commands", + "start": { + "executor": "@nx/react-native:start", + "dependsOn": [], "options": { - "cwd": "apps/native/app", - "command": "yarn start" + "port": 8081 } }, - "build": { - "executor": "nx:run-commands", + "run-ios": { + "executor": "@nx/react-native:run-ios", + "dependsOn": [], + "options": {} + }, + "bundle-ios": { + "executor": "@nx/react-native:bundle", + "dependsOn": [], + "outputs": ["{options.bundleOutput}"], "options": { - "cwd": "apps/native/app", - "command": "yarn build-mock" + "entryFile": "index.js", + "platform": "ios", + "bundleOutput": "dist/apps/native/app/ios/main.jsbundle" } }, - "test": { - "executor": "nx:run-commands", + "run-android": { + "executor": "@nx/react-native:run-android", + "dependsOn": [], + "options": {} + }, + "build-android": { + "executor": "@nx/react-native:build-android", + "outputs": [ + "{projectRoot}/android/app/build/outputs/bundle", + "{projectRoot}/android/app/build/outputs/apk" + ], + "dependsOn": [], + "options": {} + }, + "build-ios": { + "executor": "@nx/react-native:build-ios", + "outputs": ["{projectRoot}/ios/build/Build"], + "dependsOn": [], + "options": {} + }, + "pod-install": { + "executor": "@nx/react-native:pod-install", + "dependsOn": ["sync-deps"], + "outputs": ["{projectRoot}/ios/Pods", "{projectRoot}/ios/Podfile.lock"], + "options": {} + }, + "upgrade": { + "executor": "@nx/react-native:upgrade", + "options": {} + }, + "bundle-android": { + "executor": "@nx/react-native:bundle", + "dependsOn": [], + "outputs": ["{options.bundleOutput}"], "options": { - "cwd": "apps/native/app", - "command": "yarn test-mock" + "entryFile": "index.js", + "platform": "android", + "bundleOutput": "dist/apps/native/app/android/main.jsbundle" } }, + "sync-deps": { + "executor": "@nx/react-native:sync-deps", + "options": {} + }, "lint": { "executor": "@nx/eslint:lint" }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "apps/native/app/jest.config.ts" + } + }, "codegen/frontend-client": { "executor": "nx:run-commands", "options": { diff --git a/apps/native/app/src/components/bottom-tabs-indicator/bottom-tabs-indicator.tsx b/apps/native/app/src/components/bottom-tabs-indicator/bottom-tabs-indicator.tsx index 27efe493886d..4967aaee8f59 100644 --- a/apps/native/app/src/components/bottom-tabs-indicator/bottom-tabs-indicator.tsx +++ b/apps/native/app/src/components/bottom-tabs-indicator/bottom-tabs-indicator.tsx @@ -1,9 +1,9 @@ -import { dynamicColor } from '@ui' import React, { useEffect, useRef, useState } from 'react' import { Animated, SafeAreaView, View, useWindowDimensions } from 'react-native' import { useTheme } from 'styled-components' import styled from 'styled-components/native' import { useUiStore } from '../../stores/ui-store' +import { dynamicColor } from '../../ui' const Host = styled.View` position: absolute; diff --git a/apps/native/app/src/components/offline/offline-banner.tsx b/apps/native/app/src/components/offline/offline-banner.tsx index 60f01b78f504..04c03fe828b6 100644 --- a/apps/native/app/src/components/offline/offline-banner.tsx +++ b/apps/native/app/src/components/offline/offline-banner.tsx @@ -1,8 +1,8 @@ -import { Alert, DARK_YELLOW_200, dynamicColor } from '@ui' import React, { useEffect, useRef } from 'react' import { Animated, Easing, SafeAreaView } from 'react-native' import { Navigation } from 'react-native-navigation' import styled from 'styled-components/native' +import { Alert, DARK_YELLOW_200, dynamicColor } from '../../ui' import { getIntl } from '../../contexts/i18n-provider' import { useOfflineActions } from '../../stores/offline-store' import { ComponentRegistry as CR } from '../../utils/component-registry' diff --git a/apps/native/app/src/components/pin-keypad/pin-keypad.tsx b/apps/native/app/src/components/pin-keypad/pin-keypad.tsx index c6bf5a106aca..3c2cebe2792f 100644 --- a/apps/native/app/src/components/pin-keypad/pin-keypad.tsx +++ b/apps/native/app/src/components/pin-keypad/pin-keypad.tsx @@ -1,4 +1,3 @@ -import { dynamicColor, font } from '@ui' import React, { useCallback, useEffect, useState } from 'react' import { useIntl } from 'react-intl' import { @@ -10,6 +9,7 @@ import { useWindowDimensions, } from 'react-native' import styled, { useTheme } from 'styled-components/native' +import { dynamicColor, font } from '../../ui' import { testIDs } from '../../utils/test-ids' interface PinKeypadProps { diff --git a/apps/native/app/src/components/visualized-pin-code/visualized-pin-code.tsx b/apps/native/app/src/components/visualized-pin-code/visualized-pin-code.tsx index c74a91d9a201..4385e265feb5 100644 --- a/apps/native/app/src/components/visualized-pin-code/visualized-pin-code.tsx +++ b/apps/native/app/src/components/visualized-pin-code/visualized-pin-code.tsx @@ -1,7 +1,7 @@ -import { dynamicColor } from '@ui' import React, { useCallback, useEffect, useRef } from 'react' import { Animated, ViewStyle } from 'react-native' import styled from 'styled-components/native' +import { dynamicColor } from '../../ui' interface VisualizedPinCodeProps { code: string diff --git a/apps/native/app/src/hooks/use-connectivity-indicator.ts b/apps/native/app/src/hooks/use-connectivity-indicator.ts index 3f06c4b4077e..3e324a320655 100644 --- a/apps/native/app/src/hooks/use-connectivity-indicator.ts +++ b/apps/native/app/src/hooks/use-connectivity-indicator.ts @@ -1,11 +1,10 @@ import { QueryResult } from '@apollo/client' - -import { theme } from '@ui' import isEqual from 'lodash/isEqual' import { useEffect, useRef } from 'react' import { Navigation, OptionsTopBar } from 'react-native-navigation' import { OptionsTopBarButton } from 'react-native-navigation/lib/src/interfaces/Options' +import { theme } from '../ui' import { useOfflineStore } from '../stores/offline-store' import { ButtonRegistry as BR } from '../utils/component-registry' import { isDefined } from '../utils/is-defined' diff --git a/apps/native/app/src/lib/show-picker.ts b/apps/native/app/src/lib/show-picker.ts index 20a41ca0167f..dbc2d73fe856 100644 --- a/apps/native/app/src/lib/show-picker.ts +++ b/apps/native/app/src/lib/show-picker.ts @@ -1,6 +1,6 @@ -import { dynamicColor } from '@ui' import { ActionSheetIOS } from 'react-native' import DialogAndroid from 'react-native-dialogs' +import { dynamicColor } from '../ui' import { uiStore } from '../stores/ui-store' import { isAndroid, isIos } from '../utils/devices' diff --git a/apps/native/app/src/screens/air-discount/air-discount.tsx b/apps/native/app/src/screens/air-discount/air-discount.tsx index 971b7b711686..f74b83dbd9d4 100644 --- a/apps/native/app/src/screens/air-discount/air-discount.tsx +++ b/apps/native/app/src/screens/air-discount/air-discount.tsx @@ -1,12 +1,3 @@ -import { - Alert, - Heading, - Link, - LinkText, - Problem, - Skeleton, - Typography, -} from '@ui' import React from 'react' import { Image, SafeAreaView, ScrollView, View } from 'react-native' import { NavigationFunctionComponent } from 'react-native-navigation' @@ -18,8 +9,17 @@ import { useGetAirDiscountFlightLegsQuery, useGetAirDiscountQuery, } from '../../graphql/types/schema' -import { AirDiscountCard } from '@ui/lib/card/air-discount-card' -import { Bullet } from '@ui/lib/bullet/bullet' +import { + Alert, + Heading, + Link, + LinkText, + Problem, + Skeleton, + Typography, + Bullet, + AirDiscountCard, +} from '../../ui' import { useConnectivityIndicator } from '../../hooks/use-connectivity-indicator' import { AirfaresUsageTable } from './airfares-usage-table' import externalLinkIcon from '../../assets/icons/external-link.png' diff --git a/apps/native/app/src/screens/air-discount/airfares-usage-table.tsx b/apps/native/app/src/screens/air-discount/airfares-usage-table.tsx index 22f60d265286..786660f7771b 100644 --- a/apps/native/app/src/screens/air-discount/airfares-usage-table.tsx +++ b/apps/native/app/src/screens/air-discount/airfares-usage-table.tsx @@ -2,7 +2,7 @@ import React from 'react' import { FormattedDate, FormattedTime } from 'react-intl' import styled from 'styled-components/native' -import { Typography, dynamicColor } from '@ui' +import { Typography, dynamicColor } from '../../ui' import { GetAirDiscountFlightLegsQuery } from '../../graphql/types/schema' import { useTheme } from 'styled-components' diff --git a/apps/native/app/src/screens/app-lock/app-lock.tsx b/apps/native/app/src/screens/app-lock/app-lock.tsx index 56ffc28596e2..28edb1dc7d16 100644 --- a/apps/native/app/src/screens/app-lock/app-lock.tsx +++ b/apps/native/app/src/screens/app-lock/app-lock.tsx @@ -1,4 +1,3 @@ -import { dynamicColor, font } from '@ui' import { selectionAsync } from 'expo-haptics' import { authenticateAsync, @@ -17,6 +16,8 @@ import { useNavigationComponentDidDisappear, } from 'react-native-navigation-hooks/dist' import styled from 'styled-components/native' + +import { dynamicColor, font } from '../../ui' import logo from '../../assets/logo/logo-64w.png' import { PinKeypad } from '../../components/pin-keypad/pin-keypad' import { VisualizedPinCode } from '../../components/visualized-pin-code/visualized-pin-code' diff --git a/apps/native/app/src/screens/applications/applications.tsx b/apps/native/app/src/screens/applications/applications.tsx index f999a0c124a9..b4951062324f 100644 --- a/apps/native/app/src/screens/applications/applications.tsx +++ b/apps/native/app/src/screens/applications/applications.tsx @@ -1,4 +1,3 @@ -import { EmptyList, StatusCardSkeleton } from '@ui' import { useCallback, useMemo, useState } from 'react' import { useIntl } from 'react-intl' import { Image, RefreshControl, ScrollView, View } from 'react-native' @@ -6,6 +5,7 @@ import { NavigationFunctionComponent } from 'react-native-navigation' import { useNavigationComponentDidAppear } from 'react-native-navigation-hooks' import { useTheme } from 'styled-components' +import { EmptyList, StatusCardSkeleton } from '../../ui' import illustrationSrc from '../../assets/illustrations/le-jobs-s3.png' import { Application, diff --git a/apps/native/app/src/screens/applications/components/applications-list.tsx b/apps/native/app/src/screens/applications/components/applications-list.tsx index 4d6234a1777b..e936fe82c504 100644 --- a/apps/native/app/src/screens/applications/components/applications-list.tsx +++ b/apps/native/app/src/screens/applications/components/applications-list.tsx @@ -1,12 +1,3 @@ -import { - Badge, - badgeColorSchemes, - EmptyList, - Problem, - StatusCard, - StatusCardSkeleton, - TopLine, -} from '@ui' import { useCallback, useMemo, useRef, useState } from 'react' import { useIntl } from 'react-intl' import { @@ -18,6 +9,16 @@ import { View, } from 'react-native' import { useTheme } from 'styled-components' + +import { + Badge, + badgeColorSchemes, + EmptyList, + Problem, + StatusCard, + StatusCardSkeleton, + TopLine, +} from '../../../ui' import illustrationSrc from '../../../assets/illustrations/le-jobs-s3.png' import { Application, diff --git a/apps/native/app/src/screens/applications/components/applications-preview.tsx b/apps/native/app/src/screens/applications/components/applications-preview.tsx index d9bcbcfd5c55..357c4f1a4f7d 100644 --- a/apps/native/app/src/screens/applications/components/applications-preview.tsx +++ b/apps/native/app/src/screens/applications/components/applications-preview.tsx @@ -1,3 +1,7 @@ +import { useIntl } from 'react-intl' +import { TouchableOpacity, View } from 'react-native' +import { useTheme } from 'styled-components' + import { Badge, badgeColorSchemes, @@ -6,11 +10,7 @@ import { StatusCard, Typography, ViewPager, -} from '@ui' -import { useIntl } from 'react-intl' -import { TouchableOpacity, View } from 'react-native' -import { useTheme } from 'styled-components' - +} from '../../../ui' import { Application } from '../../../graphql/types/schema' import { getApplicationType } from '../utils/getApplicationType' import { getBadgeVariant } from '../utils/getBadgeVariant' diff --git a/apps/native/app/src/screens/assets/assets-detail.tsx b/apps/native/app/src/screens/assets/assets-detail.tsx index 7c2f03da9c7c..2d39b01c4ac8 100644 --- a/apps/native/app/src/screens/assets/assets-detail.tsx +++ b/apps/native/app/src/screens/assets/assets-detail.tsx @@ -1,4 +1,3 @@ -import { Divider, Input, InputRow, NavigationBarSheet } from '@ui' import React from 'react' import { useIntl } from 'react-intl' import { ScrollView, View } from 'react-native' @@ -6,6 +5,8 @@ import { Navigation, NavigationFunctionComponent, } from 'react-native-navigation' + +import { Divider, Input, InputRow, NavigationBarSheet } from '../../ui' import { useGetAssetQuery } from '../../graphql/types/schema' import { createNavigationOptionHooks } from '../../hooks/create-navigation-option-hooks' import { testIDs } from '../../utils/test-ids' diff --git a/apps/native/app/src/screens/assets/assets-overview.tsx b/apps/native/app/src/screens/assets/assets-overview.tsx index 80ae907a351b..508f83c60f5b 100644 --- a/apps/native/app/src/screens/assets/assets-overview.tsx +++ b/apps/native/app/src/screens/assets/assets-overview.tsx @@ -1,4 +1,3 @@ -import { AssetCard, EmptyList, Skeleton, TopLine } from '@ui' import React, { useCallback, useRef, useState } from 'react' import { useIntl } from 'react-intl' import { @@ -12,6 +11,8 @@ import { } from 'react-native' import { NavigationFunctionComponent } from 'react-native-navigation' import { useTheme } from 'styled-components/native' + +import { AssetCard, EmptyList, Skeleton, TopLine } from '../../ui' import illustrationSrc from '../../assets/illustrations/le-moving-s1.png' import { BottomTabsIndicator } from '../../components/bottom-tabs-indicator/bottom-tabs-indicator' import { useListAssetsQuery } from '../../graphql/types/schema' diff --git a/apps/native/app/src/screens/cognito-auth/cognito-auth.tsx b/apps/native/app/src/screens/cognito-auth/cognito-auth.tsx index d64f21849c18..718232be294b 100644 --- a/apps/native/app/src/screens/cognito-auth/cognito-auth.tsx +++ b/apps/native/app/src/screens/cognito-auth/cognito-auth.tsx @@ -7,7 +7,6 @@ import { } from '@apollo/client/core' import { setContext } from '@apollo/client/link/context' import { useAsyncStorage } from '@react-native-community/async-storage' -import { Button } from '@ui' import { useEffect, useState } from 'react' import { ActionSheetIOS, Linking, Text, View } from 'react-native' import { @@ -19,6 +18,8 @@ import { Navigation, NavigationFunctionComponent, } from 'react-native-navigation' + +import { Button } from '../../ui' import { config } from '../../config' import { openNativeBrowser } from '../../lib/rn-island' import { cognitoAuthUrl, configs } from './config-switcher' diff --git a/apps/native/app/src/screens/document-detail/document-detail.tsx b/apps/native/app/src/screens/document-detail/document-detail.tsx index 9914b0937f78..f12d411990f6 100644 --- a/apps/native/app/src/screens/document-detail/document-detail.tsx +++ b/apps/native/app/src/screens/document-detail/document-detail.tsx @@ -1,6 +1,4 @@ import { useApolloClient, useFragment_experimental } from '@apollo/client' -import { Alert, blue400, dynamicColor, Header, Loader } from '@ui' -import { Problem } from '@ui/lib/problem/problem' import React, { useEffect, useRef, useState } from 'react' import { FormattedDate, useIntl } from 'react-intl' import { @@ -22,6 +20,8 @@ import { import Pdf, { Source } from 'react-native-pdf' import WebView from 'react-native-webview' import styled, { useTheme } from 'styled-components/native' + +import { Alert, blue400, dynamicColor, Header, Loader, Problem } from '../../ui' import { DocumentV2, DocumentV2Action, diff --git a/apps/native/app/src/screens/document-detail/utils/get-buttons-for-actions.tsx b/apps/native/app/src/screens/document-detail/utils/get-buttons-for-actions.tsx index b5baae22f5ea..2dd2b9438144 100644 --- a/apps/native/app/src/screens/document-detail/utils/get-buttons-for-actions.tsx +++ b/apps/native/app/src/screens/document-detail/utils/get-buttons-for-actions.tsx @@ -1,8 +1,8 @@ -import { Button } from '@ui' import styled from 'styled-components' import { View } from 'react-native' import { isValidElement } from 'react' +import { Button } from '../../../ui' import openIcon from '../../../assets/icons/external-link.png' import downloadIcon from '../../../assets/icons/download.png' import { DocumentV2Action } from '../../../graphql/types/schema' diff --git a/apps/native/app/src/screens/family/family-details.tsx b/apps/native/app/src/screens/family/family-details.tsx index e4cf541c22cd..1733c663aea3 100644 --- a/apps/native/app/src/screens/family/family-details.tsx +++ b/apps/native/app/src/screens/family/family-details.tsx @@ -1,4 +1,3 @@ -import { Input, InputRow, NavigationBarSheet, Typography } from '@ui' import React from 'react' import { useIntl } from 'react-intl' import { SafeAreaView, ScrollView, View } from 'react-native' @@ -14,6 +13,8 @@ import { useNationalRegistryChildCustodyQuery, useNationalRegistrySpouseQuery, } from '../../graphql/types/schema' + +import { Input, InputRow, NavigationBarSheet, Typography } from '../../ui' import { createNavigationOptionHooks } from '../../hooks/create-navigation-option-hooks' import { formatNationalId } from '../../lib/format-national-id' import { testIDs } from '../../utils/test-ids' diff --git a/apps/native/app/src/screens/family/family-overview.tsx b/apps/native/app/src/screens/family/family-overview.tsx index f2bb50879b2a..13cd53270394 100644 --- a/apps/native/app/src/screens/family/family-overview.tsx +++ b/apps/native/app/src/screens/family/family-overview.tsx @@ -1,4 +1,3 @@ -import { EmptyList, FamilyMemberCard, Problem, Skeleton, TopLine } from '@ui' import React, { useCallback, useRef, useState } from 'react' import { useIntl } from 'react-intl' import { @@ -12,6 +11,14 @@ import { } from 'react-native' import { NavigationFunctionComponent } from 'react-native-navigation' import { useTheme } from 'styled-components/native' + +import { + EmptyList, + FamilyMemberCard, + Skeleton, + TopLine, + Problem, +} from '../../ui' import illustrationSrc from '../../assets/illustrations/hero_spring.png' import { BottomTabsIndicator } from '../../components/bottom-tabs-indicator/bottom-tabs-indicator' import { diff --git a/apps/native/app/src/screens/finance/components/finance-status-card.tsx b/apps/native/app/src/screens/finance/components/finance-status-card.tsx index e8f884674ca8..2e9688994997 100644 --- a/apps/native/app/src/screens/finance/components/finance-status-card.tsx +++ b/apps/native/app/src/screens/finance/components/finance-status-card.tsx @@ -1,14 +1,15 @@ +import { useState } from 'react' +import { FormattedMessage, useIntl } from 'react-intl' +import { Image, Linking, Pressable, View } from 'react-native' +import styled, { useTheme } from 'styled-components/native' + import { Skeleton, Typography, blue400, dynamicColor, ExpandableCard, -} from '@ui' -import { useState } from 'react' -import { FormattedMessage, useIntl } from 'react-intl' -import { Image, Linking, Pressable, View } from 'react-native' -import styled, { useTheme } from 'styled-components/native' +} from '../../../ui' import chevronDown from '../../../assets/icons/chevron-down.png' import { ChargeType, diff --git a/apps/native/app/src/screens/finance/finance-status-detail.tsx b/apps/native/app/src/screens/finance/finance-status-detail.tsx index 5c8fbf6f166f..b3e89890e86a 100644 --- a/apps/native/app/src/screens/finance/finance-status-detail.tsx +++ b/apps/native/app/src/screens/finance/finance-status-detail.tsx @@ -1,10 +1,11 @@ -import { Input, InputRow, NavigationBarSheet } from '@ui' import { useIntl } from 'react-intl' import { ScrollView, View } from 'react-native' import { Navigation, NavigationFunctionComponent, } from 'react-native-navigation' + +import { Input, InputRow, NavigationBarSheet } from '../../ui' import { useGetFinanceStatusDetailsQuery } from '../../graphql/types/schema' import { createNavigationOptionHooks } from '../../hooks/create-navigation-option-hooks' import { testIDs } from '../../utils/test-ids' diff --git a/apps/native/app/src/screens/finance/finance.tsx b/apps/native/app/src/screens/finance/finance.tsx index 095a70507600..1462b7a6bf2e 100644 --- a/apps/native/app/src/screens/finance/finance.tsx +++ b/apps/native/app/src/screens/finance/finance.tsx @@ -1,8 +1,9 @@ -import { Button, Heading, Skeleton, TableViewCell, Typography } from '@ui' import { FormattedMessage, useIntl } from 'react-intl' import { SafeAreaView, ScrollView } from 'react-native' import { NavigationFunctionComponent } from 'react-native-navigation' import { useTheme } from 'styled-components/native' + +import { Button, Heading, Skeleton, TableViewCell, Typography } from '../../ui' import externalLink from '../../assets/icons/external-link.png' import { getConfig } from '../../config' import { GetFinanceStatus } from '../../graphql/types/finance.types' diff --git a/apps/native/app/src/screens/health/health-overview.tsx b/apps/native/app/src/screens/health/health-overview.tsx index 740cac1bbb45..2c25853fb0f5 100644 --- a/apps/native/app/src/screens/health/health-overview.tsx +++ b/apps/native/app/src/screens/health/health-overview.tsx @@ -1,12 +1,3 @@ -import { - Alert, - Button, - Heading, - Input, - InputRow, - Problem, - Typography, -} from '@ui' import React, { useCallback, useMemo, useState } from 'react' import { FormattedMessage, useIntl } from 'react-intl' import { @@ -22,6 +13,15 @@ import { NavigationFunctionComponent } from 'react-native-navigation' import styled, { useTheme } from 'styled-components/native' import { ApolloError } from '@apollo/client' +import { + Alert, + Button, + Heading, + Input, + InputRow, + Problem, + Typography, +} from '../../ui' import { useGetHealthCenterQuery, useGetHealthInsuranceOverviewQuery, diff --git a/apps/native/app/src/screens/home/air-discount-module.tsx b/apps/native/app/src/screens/home/air-discount-module.tsx index bbabf15460d4..69fb3eefd079 100644 --- a/apps/native/app/src/screens/home/air-discount-module.tsx +++ b/apps/native/app/src/screens/home/air-discount-module.tsx @@ -1,3 +1,9 @@ +import React from 'react' +import { FormattedMessage, useIntl } from 'react-intl' +import { Image, SafeAreaView, TouchableOpacity } from 'react-native' +import styled, { useTheme } from 'styled-components/native' +import { ApolloError } from '@apollo/client' + import { Typography, Heading, @@ -5,21 +11,14 @@ import { ViewPager, EmptyCard, GeneralCardSkeleton, -} from '@ui' - -import React from 'react' -import { FormattedMessage, useIntl } from 'react-intl' -import { Image, SafeAreaView, TouchableOpacity } from 'react-native' -import styled, { useTheme } from 'styled-components/native' -import { ApolloError } from '@apollo/client' - + AirDiscountCard, +} from '../../ui' import illustrationSrc from '../../assets/illustrations/le-jobs-s2.png' import { navigateTo } from '../../lib/deep-linking' import { GetAirDiscountQuery, useGetAirDiscountQuery, } from '../../graphql/types/schema' -import { AirDiscountCard } from '@ui/lib/card/air-discount-card' import { screenWidth } from '../../utils/dimensions' const Host = styled.View` diff --git a/apps/native/app/src/screens/home/applications-module.tsx b/apps/native/app/src/screens/home/applications-module.tsx index 0b0210b7cdae..c916f738a571 100644 --- a/apps/native/app/src/screens/home/applications-module.tsx +++ b/apps/native/app/src/screens/home/applications-module.tsx @@ -1,10 +1,10 @@ -import { EmptyCard, StatusCardSkeleton } from '@ui' import React from 'react' import { useIntl } from 'react-intl' import styled from 'styled-components' import { Image, SafeAreaView, View } from 'react-native' import { ApolloError } from '@apollo/client' +import { EmptyCard, StatusCardSkeleton } from '../../ui' import leJobss3 from '../../assets/illustrations/le-jobs-s3.png' import { Application, diff --git a/apps/native/app/src/screens/home/hello-module.tsx b/apps/native/app/src/screens/home/hello-module.tsx index 7bab98b63b9b..ef94534fdc9e 100644 --- a/apps/native/app/src/screens/home/hello-module.tsx +++ b/apps/native/app/src/screens/home/hello-module.tsx @@ -1,11 +1,10 @@ -import { Typography, Skeleton } from '@ui' import * as FileSystem from 'expo-file-system' - import React, { useEffect } from 'react' import { FormattedMessage } from 'react-intl' import { Image, SafeAreaView } from 'react-native' import styled, { useTheme } from 'styled-components/native' +import { Typography, Skeleton } from '../../ui' import { useAuthStore } from '../../stores/auth-store' import { usePreferencesStore } from '../../stores/preferences-store' import { useGetFrontPageImageQuery } from '../../graphql/types/schema' diff --git a/apps/native/app/src/screens/home/home-options.tsx b/apps/native/app/src/screens/home/home-options.tsx index b79ace437971..153272ea9e0a 100644 --- a/apps/native/app/src/screens/home/home-options.tsx +++ b/apps/native/app/src/screens/home/home-options.tsx @@ -1,10 +1,10 @@ -import { Heading, TableViewCell, Typography } from '@ui' import React from 'react' import { FormattedMessage, useIntl } from 'react-intl' import { Platform, SafeAreaView, ScrollView, Switch } from 'react-native' import { NavigationFunctionComponent } from 'react-native-navigation' import { useTheme } from 'styled-components' +import { Heading, TableViewCell, Typography } from '../../ui' import { createNavigationOptionHooks } from '../../hooks/create-navigation-option-hooks' import { preferencesStore, diff --git a/apps/native/app/src/screens/home/home.tsx b/apps/native/app/src/screens/home/home.tsx index f228c0843a88..2b02f0f5d313 100644 --- a/apps/native/app/src/screens/home/home.tsx +++ b/apps/native/app/src/screens/home/home.tsx @@ -1,4 +1,3 @@ -import { TopLine } from '@ui' import React, { ReactElement, useCallback, @@ -17,6 +16,7 @@ import CodePush from 'react-native-code-push' import { NavigationFunctionComponent } from 'react-native-navigation' import { BottomTabsIndicator } from '../../components/bottom-tabs-indicator/bottom-tabs-indicator' +import { TopLine } from '../../ui' import { createNavigationOptionHooks } from '../../hooks/create-navigation-option-hooks' import { useAndroidNotificationPermission } from '../../hooks/use-android-notification-permission' import { useConnectivityIndicator } from '../../hooks/use-connectivity-indicator' diff --git a/apps/native/app/src/screens/home/inbox-module.tsx b/apps/native/app/src/screens/home/inbox-module.tsx index 73ac02f007c5..a4d3ab7d3efb 100644 --- a/apps/native/app/src/screens/home/inbox-module.tsx +++ b/apps/native/app/src/screens/home/inbox-module.tsx @@ -1,17 +1,17 @@ -import { - Typography, - Heading, - ChevronRight, - ListItemSkeleton, - EmptyCard, -} from '@ui' - import React from 'react' import { FormattedMessage, useIntl } from 'react-intl' import { Image, SafeAreaView, TouchableOpacity } from 'react-native' import styled, { useTheme } from 'styled-components/native' import { ApolloError } from '@apollo/client' +import { + Typography, + Heading, + ChevronRight, + ListItemSkeleton, + EmptyCard, + InboxCard, +} from '../../ui' import leCompanys3 from '../../assets/illustrations/le-company-s3.png' import { navigateTo } from '../../lib/deep-linking' import { @@ -19,7 +19,6 @@ import { useListDocumentsQuery, } from '../../graphql/types/schema' import { useOrganizationsStore } from '../../stores/organizations-store' -import { InboxCard } from '@ui/lib/card/inbox-card' const Host = styled.View` margin-bottom: ${({ theme }) => theme.spacing[2]}px; diff --git a/apps/native/app/src/screens/home/licenses-module.tsx b/apps/native/app/src/screens/home/licenses-module.tsx index b8a3f9ecb108..b3bf4ab5ae49 100644 --- a/apps/native/app/src/screens/home/licenses-module.tsx +++ b/apps/native/app/src/screens/home/licenses-module.tsx @@ -1,3 +1,9 @@ +import React from 'react' +import { FormattedMessage, useIntl } from 'react-intl' +import { Image, SafeAreaView, TouchableOpacity } from 'react-native' +import styled, { useTheme } from 'styled-components/native' +import { ApolloError } from '@apollo/client' + import { Typography, Heading, @@ -5,14 +11,7 @@ import { ViewPager, EmptyCard, GeneralCardSkeleton, -} from '@ui' - -import React from 'react' -import { FormattedMessage, useIntl } from 'react-intl' -import { Image, SafeAreaView, TouchableOpacity } from 'react-native' -import styled, { useTheme } from 'styled-components/native' -import { ApolloError } from '@apollo/client' - +} from '../../ui' import { navigateTo } from '../../lib/deep-linking' import { GenericLicenseType, diff --git a/apps/native/app/src/screens/home/onboarding-module.tsx b/apps/native/app/src/screens/home/onboarding-module.tsx index 62eca38b932a..7e024fecc09f 100644 --- a/apps/native/app/src/screens/home/onboarding-module.tsx +++ b/apps/native/app/src/screens/home/onboarding-module.tsx @@ -1,13 +1,13 @@ -import { Close, Heading, ViewPager, WelcomeCard } from '@ui' import React from 'react' import { SafeAreaView, TouchableOpacity } from 'react-native' import { useTheme } from 'styled-components/native' +import { useIntl } from 'react-intl' + +import { Close, Heading, ViewPager, WelcomeCard } from '../../ui' import illustration1 from '../../assets/illustrations/digital-services-m3.png' import illustration3 from '../../assets/illustrations/le-company-s2.png' import illustration2 from '../../assets/illustrations/le-retirement-s3-large.png' import illustration4 from '../../assets/illustrations/le_jobs_s5.png' - -import { useIntl } from 'react-intl' import { useAuthStore } from '../../stores/auth-store' import { usePreferencesStore } from '../../stores/preferences-store' diff --git a/apps/native/app/src/screens/home/vehicles-module.tsx b/apps/native/app/src/screens/home/vehicles-module.tsx index 34c5c709c86d..7a8686d2cc60 100644 --- a/apps/native/app/src/screens/home/vehicles-module.tsx +++ b/apps/native/app/src/screens/home/vehicles-module.tsx @@ -1,3 +1,9 @@ +import React, { useMemo } from 'react' +import { FormattedMessage, useIntl } from 'react-intl' +import { Image, SafeAreaView, TouchableOpacity } from 'react-native' +import styled, { useTheme } from 'styled-components/native' +import { ApolloError } from '@apollo/client' + import { Typography, Heading, @@ -5,14 +11,7 @@ import { ViewPager, EmptyCard, GeneralCardSkeleton, -} from '@ui' - -import React, { useMemo } from 'react' -import { FormattedMessage, useIntl } from 'react-intl' -import { Image, SafeAreaView, TouchableOpacity } from 'react-native' -import styled, { useTheme } from 'styled-components/native' -import { ApolloError } from '@apollo/client' - +} from '../../ui' import illustrationSrc from '../../assets/illustrations/le-moving-s4.png' import { navigateTo } from '../../lib/deep-linking' import { VehicleItem } from '../vehicles/components/vehicle-item' diff --git a/apps/native/app/src/screens/inbox/inbox-filter.tsx b/apps/native/app/src/screens/inbox/inbox-filter.tsx index 121def2ffdd8..700cc8470dca 100644 --- a/apps/native/app/src/screens/inbox/inbox-filter.tsx +++ b/apps/native/app/src/screens/inbox/inbox-filter.tsx @@ -1,3 +1,8 @@ +import { useEffect, useState } from 'react' +import { useIntl } from 'react-intl' +import { ScrollView, View } from 'react-native' +import { Navigation } from 'react-native-navigation' + import { Accordion, AccordionItem, @@ -5,11 +10,7 @@ import { Checkbox, DatePickerInput, theme, -} from '@ui' -import { useEffect, useState } from 'react' -import { useIntl } from 'react-intl' -import { ScrollView, View } from 'react-native' -import { Navigation } from 'react-native-navigation' +} from '../../ui' import { createNavigationOptionHooks } from '../../hooks/create-navigation-option-hooks' import { useConnectivityIndicator } from '../../hooks/use-connectivity-indicator' import { diff --git a/apps/native/app/src/screens/inbox/inbox.tsx b/apps/native/app/src/screens/inbox/inbox.tsx index ea6c5ab10e02..fb83ce224dd1 100644 --- a/apps/native/app/src/screens/inbox/inbox.tsx +++ b/apps/native/app/src/screens/inbox/inbox.tsx @@ -1,13 +1,3 @@ -import { - Button, - EmptyList, - ListItem, - ListItemSkeleton, - SearchBar, - Tag, - TopLine, - InboxCard, -} from '@ui' import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useIntl } from 'react-intl' import { @@ -26,6 +16,17 @@ import { } from 'react-native-navigation' import { useNavigationComponentDidAppear } from 'react-native-navigation-hooks/dist' import styled, { useTheme } from 'styled-components/native' + +import { + Button, + EmptyList, + ListItem, + ListItemSkeleton, + SearchBar, + Tag, + TopLine, + InboxCard, +} from '../../ui' import filterIcon from '../../assets/icons/filter-icon.png' import inboxReadIcon from '../../assets/icons/inbox-read.png' import illustrationSrc from '../../assets/illustrations/le-company-s3.png' diff --git a/apps/native/app/src/screens/license-scanner/license-scan-detail.tsx b/apps/native/app/src/screens/license-scanner/license-scan-detail.tsx index 0bf8dacd2215..8e1c92ca83e9 100644 --- a/apps/native/app/src/screens/license-scanner/license-scan-detail.tsx +++ b/apps/native/app/src/screens/license-scanner/license-scan-detail.tsx @@ -1,4 +1,3 @@ -import { ScanResultCard, SupportedGenericLicenseTypes } from '@ui' import { useIntl } from 'react-intl' import { Navigation, @@ -7,6 +6,8 @@ import { } from 'react-native-navigation' import { useNavigationButtonPress } from 'react-native-navigation-hooks/dist' import styled from 'styled-components/native' + +import { ScanResultCard, SupportedGenericLicenseTypes } from '../../ui' import { LICENSE_SCANNER_DONE } from '../../constants/navigation-buttons' import { VerifyLicenseBarcodeError, diff --git a/apps/native/app/src/screens/license-scanner/license-scanner.tsx b/apps/native/app/src/screens/license-scanner/license-scanner.tsx index e5bcd13c9be4..2c08022a210b 100644 --- a/apps/native/app/src/screens/license-scanner/license-scanner.tsx +++ b/apps/native/app/src/screens/license-scanner/license-scanner.tsx @@ -1,4 +1,3 @@ -import { Bubble, Button, theme } from '@ui' import { impactAsync, ImpactFeedbackStyle } from 'expo-haptics' import React, { useCallback, useEffect, useRef, useState } from 'react' import { useIntl } from 'react-intl' @@ -34,6 +33,8 @@ import { } from 'react-native-vision-camera' import { CodeScanner, CodeType } from 'react-native-vision-camera' import styled from 'styled-components/native' + +import { Bubble, Button, theme } from '../../ui' import flashligth from '../../assets/icons/flashlight.png' import { LICENSE_SCANNER_DONE } from '../../constants/navigation-buttons' import { diff --git a/apps/native/app/src/screens/login/login.tsx b/apps/native/app/src/screens/login/login.tsx index c2f8a19c3509..2c25f637b067 100644 --- a/apps/native/app/src/screens/login/login.tsx +++ b/apps/native/app/src/screens/login/login.tsx @@ -1,4 +1,3 @@ -import { Button, dynamicColor, font, Illustration } from '@ui' import React, { useEffect, useState } from 'react' import { FormattedMessage, useIntl } from 'react-intl' import { @@ -15,6 +14,8 @@ import { } from 'react-native' import { NavigationFunctionComponent } from 'react-native-navigation' import styled from 'styled-components/native' + +import { Button, dynamicColor, font, Illustration } from '../../ui' import logo from '../../assets/logo/logo-64w.png' import { useBrowser } from '../../lib/use-browser' import { useAuthStore } from '../../stores/auth-store' diff --git a/apps/native/app/src/screens/login/testing-login.tsx b/apps/native/app/src/screens/login/testing-login.tsx index f598ec587f0c..921a4980105a 100644 --- a/apps/native/app/src/screens/login/testing-login.tsx +++ b/apps/native/app/src/screens/login/testing-login.tsx @@ -1,4 +1,3 @@ -import { Button, dynamicColor, font } from '@ui' import React, { useEffect, useState } from 'react' import { FormattedMessage, useIntl } from 'react-intl' import { @@ -15,6 +14,8 @@ import { } from 'react-native' import { NavigationFunctionComponent } from 'react-native-navigation' import styled from 'styled-components/native' + +import { Button, dynamicColor, font } from '../../ui' import logo from '../../assets/logo/logo-64w.png' import testinglogo from '../../assets/logo/testing-logo-64w.png' import { environments, isTestingApp, useConfig } from '../../config' diff --git a/apps/native/app/src/screens/more/more.tsx b/apps/native/app/src/screens/more/more.tsx index 358ae75ae4d3..57fd1d1dbbac 100644 --- a/apps/native/app/src/screens/more/more.tsx +++ b/apps/native/app/src/screens/more/more.tsx @@ -1,10 +1,11 @@ -import { FamilyMemberCard, MoreCard } from '@ui' import React, { useState } from 'react' import { useIntl } from 'react-intl' import { SafeAreaView, ScrollView, TouchableHighlight } from 'react-native' import { NavigationFunctionComponent } from 'react-native-navigation' import { useNavigationComponentDidAppear } from 'react-native-navigation-hooks' import styled, { useTheme } from 'styled-components/native' + +import { FamilyMemberCard, MoreCard } from '../../ui' import assetsIcon from '../../assets/icons/assets.png' import familyIcon from '../../assets/icons/family.png' import financeIcon from '../../assets/icons/finance.png' diff --git a/apps/native/app/src/screens/more/personal-info.tsx b/apps/native/app/src/screens/more/personal-info.tsx index 87417710ad0c..1bb9f87e3b40 100644 --- a/apps/native/app/src/screens/more/personal-info.tsx +++ b/apps/native/app/src/screens/more/personal-info.tsx @@ -1,4 +1,3 @@ -import { Alert, Button, Input, InputRow, NavigationBarSheet } from '@ui' import React from 'react' import { useIntl } from 'react-intl' import { ScrollView, View } from 'react-native' @@ -6,6 +5,8 @@ import { Navigation, NavigationFunctionComponent, } from 'react-native-navigation' + +import { Alert, Button, Input, InputRow, NavigationBarSheet } from '../../ui' import { useNationalRegistryUserQuery } from '../../graphql/types/schema' import { createNavigationOptionHooks } from '../../hooks/create-navigation-option-hooks' import { navigateTo } from '../../lib/deep-linking' diff --git a/apps/native/app/src/screens/notifications/notifications.tsx b/apps/native/app/src/screens/notifications/notifications.tsx index 913c1b6d6d50..0326ee9d0463 100644 --- a/apps/native/app/src/screens/notifications/notifications.tsx +++ b/apps/native/app/src/screens/notifications/notifications.tsx @@ -1,13 +1,4 @@ -import { - Button, - NavigationBarSheet, - NotificationCard, - Problem, - ListItemSkeleton, - EmptyList, -} from '@ui' import { useApolloClient } from '@apollo/client' - import { dismissAllNotificationsAsync } from 'expo-notifications' import React, { useCallback, useEffect, useMemo, useState } from 'react' import { useIntl } from 'react-intl' @@ -24,6 +15,15 @@ import { } from 'react-native-navigation' import { useTheme } from 'styled-components' import styled from 'styled-components/native' + +import { + Button, + NavigationBarSheet, + NotificationCard, + Problem, + ListItemSkeleton, + EmptyList, +} from '../../ui' import { GetUserNotificationsQuery, Notification, @@ -32,7 +32,6 @@ import { useMarkAllNotificationsAsSeenMutation, useMarkUserNotificationAsReadMutation, } from '../../graphql/types/schema' - import { createNavigationOptionHooks } from '../../hooks/create-navigation-option-hooks' import { navigateTo, navigateToUniversalLink } from '../../lib/deep-linking' import { useNotificationsStore } from '../../stores/notifications-store' diff --git a/apps/native/app/src/screens/onboarding/onboarding-biometrics.tsx b/apps/native/app/src/screens/onboarding/onboarding-biometrics.tsx index 96009ba82e73..a8bde5809c16 100644 --- a/apps/native/app/src/screens/onboarding/onboarding-biometrics.tsx +++ b/apps/native/app/src/screens/onboarding/onboarding-biometrics.tsx @@ -1,4 +1,3 @@ -import { Button, CancelButton, Illustration, Onboarding } from '@ui' import { AuthenticationType, authenticateAsync, @@ -8,6 +7,8 @@ import React, { useEffect, useState } from 'react' import { FormattedMessage, useIntl } from 'react-intl' import { AppState, Platform } from 'react-native' import { NavigationFunctionComponent } from 'react-native-navigation' + +import { Button, CancelButton, Illustration, Onboarding } from '../../ui' import finger from '../../assets/icons/finger-16.png' import iris from '../../assets/icons/iris-16.png' import { preferencesStore } from '../../stores/preferences-store' diff --git a/apps/native/app/src/screens/onboarding/onboarding-notifications.tsx b/apps/native/app/src/screens/onboarding/onboarding-notifications.tsx index 6b9a4ec8727a..8b8a59204f6c 100644 --- a/apps/native/app/src/screens/onboarding/onboarding-notifications.tsx +++ b/apps/native/app/src/screens/onboarding/onboarding-notifications.tsx @@ -1,7 +1,8 @@ -import { Button, CancelButton, Illustration, Onboarding } from '@ui' import React from 'react' import { FormattedMessage, useIntl } from 'react-intl' import { NavigationFunctionComponent } from 'react-native-navigation' + +import { Button, CancelButton, Illustration, Onboarding } from '../../ui' import allow from '../../assets/icons/allow.png' import { preferencesStore } from '../../stores/preferences-store' import { nextOnboardingStep } from '../../utils/onboarding' diff --git a/apps/native/app/src/screens/onboarding/onboarding-pin-code.tsx b/apps/native/app/src/screens/onboarding/onboarding-pin-code.tsx index 0c73703ae5c0..132cd806bc37 100644 --- a/apps/native/app/src/screens/onboarding/onboarding-pin-code.tsx +++ b/apps/native/app/src/screens/onboarding/onboarding-pin-code.tsx @@ -1,4 +1,3 @@ -import { CancelButton, dynamicColor, font } from '@ui' import React, { useEffect, useState } from 'react' import { FormattedMessage, useIntl } from 'react-intl' import { Image, SafeAreaView, View } from 'react-native' @@ -8,6 +7,8 @@ import { NavigationFunctionComponent, } from 'react-native-navigation' import styled from 'styled-components/native' + +import { CancelButton, dynamicColor, font } from '../../ui' import logo from '../../assets/logo/logo-64w.png' import { PinKeypad } from '../../components/pin-keypad/pin-keypad' import { VisualizedPinCode } from '../../components/visualized-pin-code/visualized-pin-code' diff --git a/apps/native/app/src/screens/passkey/passkey.tsx b/apps/native/app/src/screens/passkey/passkey.tsx index 6cd5d67130d5..a6ba79951324 100644 --- a/apps/native/app/src/screens/passkey/passkey.tsx +++ b/apps/native/app/src/screens/passkey/passkey.tsx @@ -1,4 +1,3 @@ -import { Button, Typography, NavigationBarSheet, LinkText } from '@ui' import React, { useEffect, useState } from 'react' import { useIntl, FormattedMessage } from 'react-intl' import { @@ -15,6 +14,8 @@ import { Navigation, NavigationFunctionComponent, } from 'react-native-navigation' + +import { Button, Typography, NavigationBarSheet, LinkText } from '../../ui' import { createNavigationOptionHooks } from '../../hooks/create-navigation-option-hooks' import logo from '../../assets/logo/logo-64w.png' import externalLink from '../../assets/icons/external-link.png' diff --git a/apps/native/app/src/screens/settings/edit-bank-info.tsx b/apps/native/app/src/screens/settings/edit-bank-info.tsx index a6de5f0f03e8..ce12824385b5 100644 --- a/apps/native/app/src/screens/settings/edit-bank-info.tsx +++ b/apps/native/app/src/screens/settings/edit-bank-info.tsx @@ -1,4 +1,3 @@ -import { Button, NavigationBarSheet, TextField, Typography } from '@ui' import React, { useEffect } from 'react' import { useIntl } from 'react-intl' import { Alert, ScrollView, View } from 'react-native' @@ -6,6 +5,8 @@ import { Navigation, NavigationFunctionComponent, } from 'react-native-navigation' + +import { Button, NavigationBarSheet, TextField, Typography } from '../../ui' import { useGetProfileQuery } from '../../graphql/types/schema' import { createNavigationOptionHooks } from '../../hooks/create-navigation-option-hooks' import { bankInfoObject, stringifyBankData } from '../../lib/bank-info-helper' diff --git a/apps/native/app/src/screens/settings/edit-confirm.tsx b/apps/native/app/src/screens/settings/edit-confirm.tsx index c624d83e259a..1e201ad189c1 100644 --- a/apps/native/app/src/screens/settings/edit-confirm.tsx +++ b/apps/native/app/src/screens/settings/edit-confirm.tsx @@ -1,10 +1,3 @@ -import { - Button, - CancelButton, - NavigationBarSheet, - TextField, - Typography, -} from '@ui' import React from 'react' import { useIntl } from 'react-intl' import { Alert, ScrollView, View } from 'react-native' @@ -12,6 +5,14 @@ import { Navigation, NavigationFunctionComponent, } from 'react-native-navigation' + +import { + Button, + CancelButton, + NavigationBarSheet, + TextField, + Typography, +} from '../../ui' import { createNavigationOptionHooks } from '../../hooks/create-navigation-option-hooks' import { testIDs } from '../../utils/test-ids' import { useUpdateUserProfile } from './utils/profile-queries' diff --git a/apps/native/app/src/screens/settings/edit-email.tsx b/apps/native/app/src/screens/settings/edit-email.tsx index 12994c6d11f9..649c808bdcd8 100644 --- a/apps/native/app/src/screens/settings/edit-email.tsx +++ b/apps/native/app/src/screens/settings/edit-email.tsx @@ -1,5 +1,4 @@ import { useApolloClient } from '@apollo/client' -import { Button, NavigationBarSheet, TextField, Typography } from '@ui' import React, { useEffect } from 'react' import { useIntl } from 'react-intl' import { Alert, ScrollView, View } from 'react-native' @@ -7,6 +6,8 @@ import { Navigation, NavigationFunctionComponent, } from 'react-native-navigation' + +import { Button, NavigationBarSheet, TextField, Typography } from '../../ui' import { CreateEmailVerificationDocument, CreateEmailVerificationMutation, diff --git a/apps/native/app/src/screens/settings/edit-phone.tsx b/apps/native/app/src/screens/settings/edit-phone.tsx index 88cc19ff8b7b..169e337304ab 100644 --- a/apps/native/app/src/screens/settings/edit-phone.tsx +++ b/apps/native/app/src/screens/settings/edit-phone.tsx @@ -1,5 +1,4 @@ import { useApolloClient } from '@apollo/client' -import { Button, NavigationBarSheet, TextField, Typography } from '@ui' import React, { useEffect, useState } from 'react' import { useIntl } from 'react-intl' import { Alert, ScrollView, View } from 'react-native' @@ -7,6 +6,8 @@ import { Navigation, NavigationFunctionComponent, } from 'react-native-navigation' + +import { Button, NavigationBarSheet, TextField, Typography } from '../../ui' import { CreateSmsVerificationDocument, CreateSmsVerificationMutation, diff --git a/apps/native/app/src/screens/settings/settings.tsx b/apps/native/app/src/screens/settings/settings.tsx index 81f1f49e81c6..3538ec341113 100644 --- a/apps/native/app/src/screens/settings/settings.tsx +++ b/apps/native/app/src/screens/settings/settings.tsx @@ -1,11 +1,4 @@ import { useApolloClient } from '@apollo/client' -import { - Alert, - NavigationBarSheet, - TableViewAccessory, - TableViewCell, - TableViewGroup, -} from '@ui' import { authenticateAsync } from 'expo-local-authentication' import React, { useEffect, useRef, useState } from 'react' import { useIntl } from 'react-intl' @@ -26,6 +19,14 @@ import { NavigationFunctionComponent, } from 'react-native-navigation' import { useTheme } from 'styled-components/native' + +import { + Alert, + NavigationBarSheet, + TableViewAccessory, + TableViewCell, + TableViewGroup, +} from '../../ui' import editIcon from '../../assets/icons/edit.png' import chevronForward from '../../ui/assets/icons/chevron-forward.png' import { PressableHighlight } from '../../components/pressable-highlight/pressable-highlight' diff --git a/apps/native/app/src/screens/update-app/update-app.tsx b/apps/native/app/src/screens/update-app/update-app.tsx index 9f9b06863c1c..b0c498865482 100644 --- a/apps/native/app/src/screens/update-app/update-app.tsx +++ b/apps/native/app/src/screens/update-app/update-app.tsx @@ -1,4 +1,3 @@ -import { Button, Typography, NavigationBarSheet } from '@ui' import React, { useEffect } from 'react' import { useIntl, FormattedMessage } from 'react-intl' import { View, Image, SafeAreaView, Linking } from 'react-native' @@ -7,6 +6,8 @@ import { Navigation, NavigationFunctionComponent, } from 'react-native-navigation' + +import { Button, Typography, NavigationBarSheet } from '../../ui' import { createNavigationOptionHooks } from '../../hooks/create-navigation-option-hooks' import logo from '../../assets/logo/logo-64w.png' import illustrationSrc from '../../assets/illustrations/digital-services-m1-dots.png' diff --git a/apps/native/app/src/screens/vaccinations/components/vaccination-card.tsx b/apps/native/app/src/screens/vaccinations/components/vaccination-card.tsx index 122f512a8ae2..22ed1fc34af6 100644 --- a/apps/native/app/src/screens/vaccinations/components/vaccination-card.tsx +++ b/apps/native/app/src/screens/vaccinations/components/vaccination-card.tsx @@ -1,3 +1,9 @@ +import { useState } from 'react' +import { FormattedMessage, useIntl } from 'react-intl' +import { TouchableOpacity, View } from 'react-native' +import styled, { useTheme } from 'styled-components/native' +import { Markdown } from '../../../ui/lib/markdown/markdown' + import { Badge, ExpandableCard, @@ -5,13 +11,7 @@ import { Skeleton, Typography, dynamicColor, -} from '@ui' -import { useState } from 'react' -import { FormattedMessage, useIntl } from 'react-intl' -import { TouchableOpacity, View } from 'react-native' -import styled, { useTheme } from 'styled-components/native' -import { Markdown } from '../../../ui/lib/markdown/markdown' - +} from '../../../ui' import chevronDown from '../../../assets/icons/chevron-down.png' import clockIcon from '../../../assets/icons/clock.png' import externalLinkIcon from '../../../assets/icons/external-link.png' diff --git a/apps/native/app/src/screens/vaccinations/vaccinations.tsx b/apps/native/app/src/screens/vaccinations/vaccinations.tsx index 61c976ce77a5..388f8a0a98a4 100644 --- a/apps/native/app/src/screens/vaccinations/vaccinations.tsx +++ b/apps/native/app/src/screens/vaccinations/vaccinations.tsx @@ -1,16 +1,16 @@ -import { - GeneralCardSkeleton, - Heading, - Problem, - TabButtons, - Typography, -} from '@ui' import React, { useCallback, useState } from 'react' import { FormattedMessage, useIntl } from 'react-intl' import { RefreshControl, SafeAreaView, ScrollView, View } from 'react-native' import { NavigationFunctionComponent } from 'react-native-navigation' import styled from 'styled-components/native' +import { + GeneralCardSkeleton, + Heading, + Problem, + TabButtons, + Typography, +} from '../../ui' import { useGetVaccinationsQuery } from '../../graphql/types/schema' import { createNavigationOptionHooks } from '../../hooks/create-navigation-option-hooks' import { useConnectivityIndicator } from '../../hooks/use-connectivity-indicator' diff --git a/apps/native/app/src/screens/vehicles/components/mileage-cell.tsx b/apps/native/app/src/screens/vehicles/components/mileage-cell.tsx index 598d50120634..2cb51c87904a 100644 --- a/apps/native/app/src/screens/vehicles/components/mileage-cell.tsx +++ b/apps/native/app/src/screens/vehicles/components/mileage-cell.tsx @@ -1,6 +1,7 @@ -import { Skeleton, Typography, useDynamicColor } from '@ui' import { useIntl } from 'react-intl' import { Image, Pressable, View } from 'react-native' + +import { Skeleton, Typography, useDynamicColor } from '../../../ui' import clock from '../../../assets/icons/clock.png' export function MileageCell({ diff --git a/apps/native/app/src/screens/vehicles/components/vehicle-item.tsx b/apps/native/app/src/screens/vehicles/components/vehicle-item.tsx index afe6b8c607a5..8bb582694dcc 100644 --- a/apps/native/app/src/screens/vehicles/components/vehicle-item.tsx +++ b/apps/native/app/src/screens/vehicles/components/vehicle-item.tsx @@ -1,8 +1,9 @@ -import { Label, VehicleCard } from '@ui' import React from 'react' import { FormattedDate, FormattedMessage } from 'react-intl' import { SafeAreaView, TouchableHighlight, View, ViewStyle } from 'react-native' import styled, { useTheme } from 'styled-components/native' + +import { Label, VehicleCard } from '../../../ui' import { ListVehiclesV2Query } from '../../../graphql/types/schema' import { navigateTo } from '../../../lib/deep-linking' diff --git a/apps/native/app/src/screens/vehicles/vehicle-mileage.screen.tsx b/apps/native/app/src/screens/vehicles/vehicle-mileage.screen.tsx index e6bcd2c9ddf6..b4cfebe8576a 100644 --- a/apps/native/app/src/screens/vehicles/vehicle-mileage.screen.tsx +++ b/apps/native/app/src/screens/vehicles/vehicle-mileage.screen.tsx @@ -1,11 +1,3 @@ -import { - Button, - Divider, - NavigationBarSheet, - TextField, - Typography, - useDynamicColor, -} from '@ui' import { useCallback, useMemo, useState } from 'react' import { FormattedDate, useIntl } from 'react-intl' import { Alert, FlatList, View } from 'react-native' @@ -14,6 +6,14 @@ import { NavigationFunctionComponent, } from 'react-native-navigation' +import { + Button, + Divider, + NavigationBarSheet, + TextField, + Typography, + useDynamicColor, +} from '../../ui' import externalLinkIcon from '../../assets/icons/external-link.png' import { GetVehicleDocument, diff --git a/apps/native/app/src/screens/vehicles/vehicles-detail.tsx b/apps/native/app/src/screens/vehicles/vehicles-detail.tsx index fe31f2262dd8..e580181712ef 100644 --- a/apps/native/app/src/screens/vehicles/vehicles-detail.tsx +++ b/apps/native/app/src/screens/vehicles/vehicles-detail.tsx @@ -1,8 +1,9 @@ -import { Button, Divider, Input, InputRow } from '@ui' import React from 'react' import { useIntl } from 'react-intl' import { ScrollView, Text, View } from 'react-native' import { NavigationFunctionComponent } from 'react-native-navigation' + +import { Button, Divider, Input, InputRow } from '../../ui' import { useGetVehicleQuery } from '../../graphql/types/schema' import { createNavigationOptionHooks } from '../../hooks/create-navigation-option-hooks' import { useConnectivityIndicator } from '../../hooks/use-connectivity-indicator' diff --git a/apps/native/app/src/screens/vehicles/vehicles.tsx b/apps/native/app/src/screens/vehicles/vehicles.tsx index 9922c500edd5..4a4640236ed2 100644 --- a/apps/native/app/src/screens/vehicles/vehicles.tsx +++ b/apps/native/app/src/screens/vehicles/vehicles.tsx @@ -1,4 +1,3 @@ -import { EmptyList, GeneralCardSkeleton, TopLine } from '@ui' import React, { useCallback, useMemo, useRef, useState } from 'react' import { FormattedMessage } from 'react-intl' import { @@ -11,6 +10,8 @@ import { } from 'react-native' import { NavigationFunctionComponent } from 'react-native-navigation' import { useTheme } from 'styled-components/native' + +import { EmptyList, GeneralCardSkeleton, TopLine } from '../../ui' import illustrationSrc from '../../assets/illustrations/le-moving-s4.png' import { BottomTabsIndicator } from '../../components/bottom-tabs-indicator/bottom-tabs-indicator' import { diff --git a/apps/native/app/src/screens/wallet-pass/components/field-render.tsx b/apps/native/app/src/screens/wallet-pass/components/field-render.tsx index 7e1eebbb79e1..70a5e9782072 100644 --- a/apps/native/app/src/screens/wallet-pass/components/field-render.tsx +++ b/apps/native/app/src/screens/wallet-pass/components/field-render.tsx @@ -1,7 +1,8 @@ -import { Field, FieldCard, FieldGroup, FieldLabel, FieldRow } from '@ui' import React from 'react' import { View } from 'react-native' import { useTheme } from 'styled-components' + +import { Field, FieldCard, FieldGroup, FieldLabel, FieldRow } from '../../../ui' import { GenericLicenseDataField, GenericLicenseType, diff --git a/apps/native/app/src/screens/wallet-pass/wallet-pass.tsx b/apps/native/app/src/screens/wallet-pass/wallet-pass.tsx index ed13f963ca81..f8c7ac96c40d 100644 --- a/apps/native/app/src/screens/wallet-pass/wallet-pass.tsx +++ b/apps/native/app/src/screens/wallet-pass/wallet-pass.tsx @@ -1,9 +1,3 @@ -import { - Alert as InfoAlert, - dynamicColor, - LICENSE_CARD_ROW_GAP, - LicenseCard, -} from '@ui' import * as FileSystem from 'expo-file-system' import React, { useCallback, useEffect, useRef, useState } from 'react' import { useIntl } from 'react-intl' @@ -23,6 +17,12 @@ import { NavigationFunctionComponent } from 'react-native-navigation' import PassKit, { AddPassButton } from 'react-native-passkit-wallet' import styled, { useTheme } from 'styled-components/native' +import { + Alert as InfoAlert, + dynamicColor, + LICENSE_CARD_ROW_GAP, + LicenseCard, +} from '../../ui' import { useFeatureFlag } from '../../contexts/feature-flag-provider' import { GenericLicenseType, diff --git a/apps/native/app/src/screens/wallet-passport/wallet-passport.tsx b/apps/native/app/src/screens/wallet-passport/wallet-passport.tsx index a1566d76adca..22d55641864c 100644 --- a/apps/native/app/src/screens/wallet-passport/wallet-passport.tsx +++ b/apps/native/app/src/screens/wallet-passport/wallet-passport.tsx @@ -1,15 +1,3 @@ -import { - Accordion, - AccordionItem, - Alert, - CustomLicenseType, - dynamicColor, - font, - Input, - InputRow, - LicenseCard, - LinkText, -} from '@ui' import React from 'react' import { useIntl } from 'react-intl' import { @@ -21,6 +9,19 @@ import { } from 'react-native' import { NavigationFunctionComponent } from 'react-native-navigation' import styled from 'styled-components/native' + +import { + Accordion, + AccordionItem, + Alert, + CustomLicenseType, + dynamicColor, + font, + Input, + InputRow, + LicenseCard, + LinkText, +} from '../../ui' import IconStatusVerified from '../../assets/icons/valid.png' import IconStatusNonVerified from '../../assets/icons/warning.png' import { useGetIdentityDocumentQuery } from '../../graphql/types/schema' diff --git a/apps/native/app/src/screens/wallet/components/wallet-item.tsx b/apps/native/app/src/screens/wallet/components/wallet-item.tsx index 8a6eec412b70..b510442cbfd7 100644 --- a/apps/native/app/src/screens/wallet/components/wallet-item.tsx +++ b/apps/native/app/src/screens/wallet/components/wallet-item.tsx @@ -1,7 +1,8 @@ -import { CustomLicenseType, LicenseCard } from '@ui' import React from 'react' import { SafeAreaView, ViewStyle } from 'react-native' import styled from 'styled-components/native' + +import { CustomLicenseType, LicenseCard } from '../../../ui' import { Pressable as PressableRaw } from '../../../components/pressable/pressable' import { GenericUserLicense, diff --git a/apps/native/app/src/screens/wallet/wallet.tsx b/apps/native/app/src/screens/wallet/wallet.tsx index 4dd8de8f321d..1e8926e808eb 100644 --- a/apps/native/app/src/screens/wallet/wallet.tsx +++ b/apps/native/app/src/screens/wallet/wallet.tsx @@ -1,5 +1,3 @@ -import { Alert, EmptyList, GeneralCardSkeleton, TopLine } from '@ui' - import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useIntl } from 'react-intl' import { @@ -15,6 +13,7 @@ import SpotlightSearch from 'react-native-spotlight-search' import { useTheme } from 'styled-components/native' import { useNavigationComponentDidAppear } from 'react-native-navigation-hooks' +import { Alert, EmptyList, GeneralCardSkeleton, TopLine } from '../../ui' import illustrationSrc from '../../assets/illustrations/le-retirement-s3.png' import { BottomTabsIndicator } from '../../components/bottom-tabs-indicator/bottom-tabs-indicator' import { diff --git a/apps/native/app/src/test-setup.ts b/apps/native/app/src/test-setup.ts new file mode 100644 index 000000000000..fbf15de85357 --- /dev/null +++ b/apps/native/app/src/test-setup.ts @@ -0,0 +1 @@ +import '@testing-library/jest-native/extend-expect' diff --git a/apps/native/app/src/types/react-native.d.ts b/apps/native/app/src/types/react-native.d.ts index 18e99d85c455..7b0398d64dfc 100644 --- a/apps/native/app/src/types/react-native.d.ts +++ b/apps/native/app/src/types/react-native.d.ts @@ -85,7 +85,3 @@ declare module 'react-native-dialogs' { options: OptionsPrompt, ): Promise } - -declare module '@island.is/application/types/lib/ApplicationTypes' { - export const ApplicationConfigurations: Record -} diff --git a/apps/native/app/src/types/styled-components.d.ts b/apps/native/app/src/types/styled-components.d.ts index 3f627c3a8c23..050eed343750 100644 --- a/apps/native/app/src/types/styled-components.d.ts +++ b/apps/native/app/src/types/styled-components.d.ts @@ -1,5 +1,5 @@ -import { Theme } from '@ui' import 'styled-components' +import { Theme } from '../ui' declare module 'styled-components' { export interface Shade { diff --git a/apps/native/app/src/ui/index.ts b/apps/native/app/src/ui/index.ts index 870051ba7b87..08e58483874e 100644 --- a/apps/native/app/src/ui/index.ts +++ b/apps/native/app/src/ui/index.ts @@ -20,6 +20,7 @@ export * from './lib/card/welcome-card' export * from './lib/card/link-card' export * from './lib/card/inbox-card' export * from './lib/card/more-card' +export * from './lib/card/air-discount-card' export * from './lib/card/expandable-card' export * from './lib/checkbox/checkbox' export * from './lib/date-picker/date-picker' @@ -61,4 +62,5 @@ export * from './lib/scan-result-card/scan-result-card' export * from './lib/label/label' export * from './lib/progress-meter/progress-meter' export * from './lib/tab-buttons/tab-buttons' +export * from './lib/bullet/bullet' export * from './utils/index' diff --git a/apps/native/app/src/ui/lib/card/license-card.tsx b/apps/native/app/src/ui/lib/card/license-card.tsx index 71f6c5cfa98d..4df3e3d0cbd9 100644 --- a/apps/native/app/src/ui/lib/card/license-card.tsx +++ b/apps/native/app/src/ui/lib/card/license-card.tsx @@ -1,5 +1,3 @@ -import { Barcode } from '@ui/lib/barcode/barcode' -import { Skeleton } from '@ui/lib/skeleton/skeleton' import React from 'react' import { FormattedDate, useIntl } from 'react-intl' import { @@ -10,6 +8,9 @@ import { ViewStyle, } from 'react-native' import styled, { useTheme } from 'styled-components/native' + +import { Barcode } from '../barcode/barcode' +import { Skeleton } from '../skeleton/skeleton' import { ExpirationProgressBar } from '../../../components/progress-bar/expiration-progress-bar' import { GenericLicenseType } from '../../../graphql/types/schema' import { isString } from '../../../utils/is-string' diff --git a/apps/native/app/src/ui/lib/date-picker/date-picker.tsx b/apps/native/app/src/ui/lib/date-picker/date-picker.tsx index d31868bd922f..cd7322b451ce 100644 --- a/apps/native/app/src/ui/lib/date-picker/date-picker.tsx +++ b/apps/native/app/src/ui/lib/date-picker/date-picker.tsx @@ -4,7 +4,7 @@ import DatePicker from 'react-native-date-picker' import calendarIcon from '../../assets/icons/calendar.png' import { Typography } from '../typography/typography' -import { dynamicColor } from '@ui/utils/dynamic-color' +import { dynamicColor } from '../../utils/dynamic-color' import { useIntl } from 'react-intl' import { View, Image } from 'react-native' diff --git a/apps/native/app/src/ui/lib/detail/header.tsx b/apps/native/app/src/ui/lib/detail/header.tsx index b92bd609a363..6348a241d54a 100644 --- a/apps/native/app/src/ui/lib/detail/header.tsx +++ b/apps/native/app/src/ui/lib/detail/header.tsx @@ -4,7 +4,7 @@ import styled from 'styled-components/native' import { dynamicColor } from '../../utils/dynamic-color' import { Skeleton } from '../skeleton/skeleton' import { Typography } from '../typography/typography' -import { Label } from '@ui' +import { Label } from '../label/label' const Host = styled.View<{ hasBorder?: boolean }>` padding-bottom: ${({ theme }) => theme.spacing[1]}px; diff --git a/apps/native/app/src/ui/lib/empty-state/empty-list.tsx b/apps/native/app/src/ui/lib/empty-state/empty-list.tsx index f55706c3abf9..1c67450255dd 100644 --- a/apps/native/app/src/ui/lib/empty-state/empty-list.tsx +++ b/apps/native/app/src/ui/lib/empty-state/empty-list.tsx @@ -2,7 +2,7 @@ import React from 'react' import { View } from 'react-native' import styled from 'styled-components/native' -import { dynamicColor } from '@ui/utils' +import { dynamicColor } from '../../utils' import { Typography } from '../typography/typography' const Host = styled.View` diff --git a/apps/native/app/src/ui/lib/empty-state/empty-state.stories.tsx b/apps/native/app/src/ui/lib/empty-state/empty-state.stories.tsx index e57489d3c65e..0f1f778a85e9 100644 --- a/apps/native/app/src/ui/lib/empty-state/empty-state.stories.tsx +++ b/apps/native/app/src/ui/lib/empty-state/empty-state.stories.tsx @@ -1,8 +1,9 @@ import { text, withKnobs } from '@storybook/addon-knobs' import { storiesOf } from '@storybook/react-native' -import { LinkText } from '@ui' import React from 'react' import { Image, View } from 'react-native' + +import { LinkText } from '../link/link-text' import illustrationSrc from '../../assets/empty-list/LE-Company-S3.png' import leJobss4 from '../../assets/illustrations/le-jobs-s4.png' import { EmptyCard } from './empty-card' diff --git a/apps/native/app/src/ui/lib/input/input.tsx b/apps/native/app/src/ui/lib/input/input.tsx index 19b526329285..fdddd77efb76 100644 --- a/apps/native/app/src/ui/lib/input/input.tsx +++ b/apps/native/app/src/ui/lib/input/input.tsx @@ -2,11 +2,12 @@ import Clipboard from '@react-native-clipboard/clipboard' import React from 'react' import { Image, TouchableOpacity, View } from 'react-native' import styled from 'styled-components/native' + +import { Label } from '../label/label' import CopyIcon from '../../assets/icons/copy.png' import { dynamicColor } from '../../utils' import { Skeleton } from '../skeleton/skeleton' import { Typography } from '../typography/typography' -import { Label } from '@ui' const Host = styled.SafeAreaView<{ noBorder: boolean diff --git a/apps/native/app/src/ui/lib/label/label.tsx b/apps/native/app/src/ui/lib/label/label.tsx index c476dfe7d3ab..f08993c00506 100644 --- a/apps/native/app/src/ui/lib/label/label.tsx +++ b/apps/native/app/src/ui/lib/label/label.tsx @@ -1,6 +1,7 @@ -import { dynamicColor } from '@ui/utils' import { Image } from 'react-native' import styled, { DefaultTheme } from 'styled-components/native' + +import { dynamicColor } from '../../utils' import dangerIcon from '../../assets/alert/danger.png' import infoIcon from '../../assets/alert/info-alert.png' import warningIcon from '../../assets/alert/warning.png' diff --git a/apps/native/app/src/ui/lib/link/link-text.tsx b/apps/native/app/src/ui/lib/link/link-text.tsx index e072ff69a06b..68cac93bcee4 100644 --- a/apps/native/app/src/ui/lib/link/link-text.tsx +++ b/apps/native/app/src/ui/lib/link/link-text.tsx @@ -2,7 +2,7 @@ import React from 'react' import { Image } from 'react-native' import styled, { useTheme } from 'styled-components/native' import { ImageSourcePropType, Text } from 'react-native' -import { fontByWeight } from '@ui/utils' +import { fontByWeight } from '../../utils' const Host = styled.View` border-bottom-width: 1px; diff --git a/apps/native/app/src/ui/lib/list/list-item.tsx b/apps/native/app/src/ui/lib/list/list-item.tsx index 7017ff41282b..e40b5ca67893 100644 --- a/apps/native/app/src/ui/lib/list/list-item.tsx +++ b/apps/native/app/src/ui/lib/list/list-item.tsx @@ -1,9 +1,10 @@ -import { Label, Typography } from '@ui' import React, { isValidElement } from 'react' import { FormattedDate, useIntl } from 'react-intl' import { Image, ImageSourcePropType } from 'react-native' import styled from 'styled-components/native' +import { Typography } from '../typography/typography' +import { Label } from '../label/label' import { dynamicColor } from '../../utils' const Host = styled.SafeAreaView<{ unread?: boolean }>` diff --git a/apps/native/app/src/ui/lib/problem/problem-template.tsx b/apps/native/app/src/ui/lib/problem/problem-template.tsx index 1ebe261c581f..8e263176f4a4 100644 --- a/apps/native/app/src/ui/lib/problem/problem-template.tsx +++ b/apps/native/app/src/ui/lib/problem/problem-template.tsx @@ -1,8 +1,10 @@ -import { Colors, Typography } from '@ui' import { ReactNode } from 'react' import { Image, View } from 'react-native' import styled from 'styled-components/native' +import { Typography } from '../typography/typography' +import { Colors } from '../../utils' + type Variant = 'info' | 'error' | 'warning' export type ProblemTemplateBaseProps = { diff --git a/apps/native/app/src/ui/lib/search-bar/search-bar.tsx b/apps/native/app/src/ui/lib/search-bar/search-bar.tsx index 952643326ad4..1ce1609bfbe1 100644 --- a/apps/native/app/src/ui/lib/search-bar/search-bar.tsx +++ b/apps/native/app/src/ui/lib/search-bar/search-bar.tsx @@ -1,4 +1,3 @@ -import { useDynamicColor } from '@ui/utils' import React, { useRef, useState } from 'react' import { Animated, @@ -12,6 +11,8 @@ import { View, } from 'react-native' import styled, { useTheme } from 'styled-components/native' + +import { useDynamicColor } from '../../utils' import closeIcon from '../../assets/icons/close.png' import searchIcon from '../../assets/icons/search.png' import { font } from '../../utils/font' diff --git a/apps/native/app/src/utils/applications-utils.ts b/apps/native/app/src/utils/applications-utils.ts index c1efd10afed6..93a6c31774cc 100644 --- a/apps/native/app/src/utils/applications-utils.ts +++ b/apps/native/app/src/utils/applications-utils.ts @@ -1,4 +1,4 @@ -import { ApplicationConfigurations } from '@island.is/application/types/lib/ApplicationTypes' +import { ApplicationConfigurations } from '@island.is/application/types' import { getConfig } from '../config' import { Application, diff --git a/apps/native/app/src/utils/get-theme-with-preferences.ts b/apps/native/app/src/utils/get-theme-with-preferences.ts index ebc84f0dace3..dbc84ce835f4 100644 --- a/apps/native/app/src/utils/get-theme-with-preferences.ts +++ b/apps/native/app/src/utils/get-theme-with-preferences.ts @@ -1,6 +1,7 @@ -import { theme } from '@ui' import { Appearance, ColorSchemeName } from 'react-native' + import { AppearanceMode, ThemeMode } from '../stores/preferences-store' +import { theme } from '../ui' export const themes = { light: { diff --git a/apps/native/app/tsconfig.app.json b/apps/native/app/tsconfig.app.json new file mode 100644 index 000000000000..7b1b69d710d7 --- /dev/null +++ b/apps/native/app/tsconfig.app.json @@ -0,0 +1,18 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": ["node"] + }, + "files": ["../../../node_modules/@nx/react-native/typings/svg.d.ts"], + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.js", "src/**/*.jsx"], + "exclude": [ + "**/*.stories.tsx", + "**/*.stories.ts", + "src/screens/devtools/storybook.tsx", + "jest.config.ts", + "src/**/*.spec.ts", + "src/**/*.spec.tsx", + "src/test-setup.ts" + ] +} diff --git a/apps/native/app/tsconfig.json b/apps/native/app/tsconfig.json index 12bba9640d48..6089be1e8395 100644 --- a/apps/native/app/tsconfig.json +++ b/apps/native/app/tsconfig.json @@ -1,18 +1,20 @@ { - "extends": "@tsconfig/react-native/tsconfig.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { - "baseUrl": ".", - "strict": true, - "paths": { - "@ui": ["src/ui"], - "@ui/*": ["src/ui/*"], - "@island.is/application/types": ["../../libs/application/types/src"], - "@island.is/application/types/*": ["../../libs/application/types/src/*"] - } + "isolatedModules": true, + "jsx": "react-native", + "lib": ["esnext"], + "resolveJsonModule": true, + "declaration": true }, - "exclude": [ - "**/*.stories.tsx", - "**/*.stories.ts", - "src/screens/devtools/storybook.tsx" + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } ] } diff --git a/apps/native/app/tsconfig.spec.json b/apps/native/app/tsconfig.spec.json new file mode 100644 index 000000000000..8f5c0a003b80 --- /dev/null +++ b/apps/native/app/tsconfig.spec.json @@ -0,0 +1,21 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "files": ["src/test-setup.ts"], + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts" + ] +} diff --git a/package.json b/package.json index d27381ce9a8a..b24e96893b4d 100644 --- a/package.json +++ b/package.json @@ -262,6 +262,7 @@ "react-is": "18.3.1", "react-keyed-flatten-children": "1.2.0", "react-modal": "3.15.1", + "react-native": "0.74.5", "react-number-format": "4.9.1", "react-pdf": "9.1.0", "react-popper": "2.3.0", @@ -346,6 +347,7 @@ "@nestjs/schematics": "10.0.1", "@nestjs/testing": "10.0.5", "@nx/cypress": "19.4.0", + "@nx/devkit": "19.4.0", "@nx/eslint": "19.4.0", "@nx/eslint-plugin": "19.4.0", "@nx/express": "19.4.0", @@ -356,12 +358,16 @@ "@nx/node": "19.4.0", "@nx/playwright": "19.4.0", "@nx/react": "19.4.0", + "@nx/react-native": "19.4.0", "@nx/storybook": "19.4.0", "@nx/web": "19.4.0", "@nx/webpack": "19.4.0", "@nx/workspace": "19.4.0", "@openapitools/openapi-generator-cli": "1.0.15-4.3.1", "@playwright/test": "1.48", + "@react-native-community/cli-platform-android": "~13.6.6", + "@react-native/babel-preset": "0.74.87", + "@react-native/metro-config": "0.74.87", "@storybook/addon-a11y": "7.6.9", "@storybook/addon-essentials": "7.6.9", "@storybook/addon-mdx-gfm": "7.6.9", @@ -375,7 +381,9 @@ "@swc/helpers": "0.5.11", "@testing-library/cypress": "8.0.3", "@testing-library/jest-dom": "5.16.5", + "@testing-library/jest-native": "5.4.3", "@testing-library/react": "15.0.6", + "@testing-library/react-native": "12.9.0", "@testing-library/user-event": "14.4.3", "@types/aws-sdk": "2.7.0", "@types/aws4": "1.5.1", @@ -454,6 +462,7 @@ "jest-environment-jsdom": "29.7.0", "jest-environment-node": "29.7.0", "jest-mock-extended": "3.0.5", + "jest-react-native": "18.0.0", "jest-transform-stub": "2.0.0", "license-checker": "25.0.1", "mailparser": "3.5.0", @@ -461,6 +470,10 @@ "nodemailer-mock": "2.0.6", "nx": "19.4.0", "prettier": "2.6.2", + "react-native-svg": "15.2.0", + "react-native-svg-transformer": "1.3.0", + "react-native-web": "^0.19.11", + "react-test-renderer": "18.2.0", "sequelize-cli": "6.4.1", "sort-paths": "1.1.1", "sqlite3": "5.1.6", @@ -498,7 +511,6 @@ "node-request-interceptor@^0.5.1": "patch:node-request-interceptor@npm%3A0.5.9#./.yarn/patches/node-request-interceptor-npm-0.5.9-77e9d9c058.patch", "dd-trace@5.10.0": "patch:dd-trace@npm%3A5.10.0#./.yarn/patches/dd-trace-npm-5.10.0-184ed36e96.patch", "expo-modules-core@1.1.1": "patch:expo-modules-core@npm%3A1.1.1#./.yarn/patches/expo-modules-core-npm-1.1.1-c3861d47cb.patch", - "react-native@0.71.1": "patch:react-native@npm%3A0.71.1#./.yarn/patches/react-native-npm-0.71.1-f5d237f240.patch", "dd-trace@5.14.1": "patch:dd-trace@npm%3A5.14.1#./.yarn/patches/dd-trace-npm-5.14.1-8d45ad14d6.patch", "react-native-navigation@7.40.0": "patch:react-native-navigation@npm%3A7.40.0#./.yarn/patches/react-native-navigation-npm-7.40.0-68d0a0ab0d.patch", "expo-modules-core@1.12.15": "patch:expo-modules-core@npm%3A1.12.15#./.yarn/patches/expo-modules-core-npm-1.12.15-fdd209ec99.patch" diff --git a/tsconfig.base.json b/tsconfig.base.json index 3cab159e69be..030262426294 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -902,7 +902,6 @@ "@island.is/infra-nest-server": ["libs/infra-nest-server/src/index.ts"], "@island.is/infra-next-server": ["libs/infra-next-server/src/index.ts"], "@island.is/infra-tracing": ["libs/infra-tracing/src/index.ts"], - "@island.is/island-ui-native": ["apps/native/island-ui/src/index.ts"], "@island.is/island-ui/contentful": [ "libs/island-ui/contentful/src/index.ts" ], diff --git a/yarn.lock b/yarn.lock index 23df4b3c706e..e0441eb6d923 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2389,6 +2389,17 @@ __metadata: languageName: node linkType: hard +"@babel/code-frame@npm:^7.25.9, @babel/code-frame@npm:^7.26.0": + version: 7.26.2 + resolution: "@babel/code-frame@npm:7.26.2" + dependencies: + "@babel/helper-validator-identifier": ^7.25.9 + js-tokens: ^4.0.0 + picocolors: ^1.0.0 + checksum: db13f5c42d54b76c1480916485e6900748bbcb0014a8aca87f50a091f70ff4e0d0a6db63cade75eb41fcc3d2b6ba0a7f89e343def4f96f00269b41b8ab8dd7b8 + languageName: node + linkType: hard + "@babel/compat-data@npm:^7.17.10, @babel/compat-data@npm:^7.19.3": version: 7.19.4 resolution: "@babel/compat-data@npm:7.19.4" @@ -2438,6 +2449,13 @@ __metadata: languageName: node linkType: hard +"@babel/compat-data@npm:^7.25.9": + version: 7.26.2 + resolution: "@babel/compat-data@npm:7.26.2" + checksum: d52fae9b0dc59b409d6005ae6b172e89329f46d68136130065ebe923a156fc633e0f1c8600b3e319b9e0f99fd948f64991a5419e2e9431d00d9d235d5f7a7618 + languageName: node + linkType: hard + "@babel/core@npm:7.22.5": version: 7.22.5 resolution: "@babel/core@npm:7.22.5" @@ -2576,6 +2594,29 @@ __metadata: languageName: node linkType: hard +"@babel/core@npm:^7.25.2": + version: 7.26.0 + resolution: "@babel/core@npm:7.26.0" + dependencies: + "@ampproject/remapping": ^2.2.0 + "@babel/code-frame": ^7.26.0 + "@babel/generator": ^7.26.0 + "@babel/helper-compilation-targets": ^7.25.9 + "@babel/helper-module-transforms": ^7.26.0 + "@babel/helpers": ^7.26.0 + "@babel/parser": ^7.26.0 + "@babel/template": ^7.25.9 + "@babel/traverse": ^7.25.9 + "@babel/types": ^7.26.0 + convert-source-map: ^2.0.0 + debug: ^4.1.0 + gensync: ^1.0.0-beta.2 + json5: ^2.2.3 + semver: ^6.3.1 + checksum: b296084cfd818bed8079526af93b5dfa0ba70282532d2132caf71d4060ab190ba26d3184832a45accd82c3c54016985a4109ab9118674347a7e5e9bc464894e6 + languageName: node + linkType: hard + "@babel/eslint-parser@npm:^7.20.0": version: 7.24.7 resolution: "@babel/eslint-parser@npm:7.24.7" @@ -2673,6 +2714,19 @@ __metadata: languageName: node linkType: hard +"@babel/generator@npm:^7.25.9, @babel/generator@npm:^7.26.0": + version: 7.26.2 + resolution: "@babel/generator@npm:7.26.2" + dependencies: + "@babel/parser": ^7.26.2 + "@babel/types": ^7.26.0 + "@jridgewell/gen-mapping": ^0.3.5 + "@jridgewell/trace-mapping": ^0.3.25 + jsesc: ^3.0.2 + checksum: 6ff850b7d6082619f8c2f518d993cf7254cfbaa20b026282cbef5c9b2197686d076a432b18e36c4d1a42721c016df4f77a8f62c67600775d9683621d534b91b4 + languageName: node + linkType: hard + "@babel/helper-annotate-as-pure@npm:^7.16.0, @babel/helper-annotate-as-pure@npm:^7.16.7, @babel/helper-annotate-as-pure@npm:^7.18.6": version: 7.18.6 resolution: "@babel/helper-annotate-as-pure@npm:7.18.6" @@ -2812,6 +2866,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-compilation-targets@npm:^7.25.9": + version: 7.25.9 + resolution: "@babel/helper-compilation-targets@npm:7.25.9" + dependencies: + "@babel/compat-data": ^7.25.9 + "@babel/helper-validator-option": ^7.25.9 + browserslist: ^4.24.0 + lru-cache: ^5.1.1 + semver: ^6.3.1 + checksum: 3af536e2db358b38f968abdf7d512d425d1018fef2f485d6f131a57a7bcaed32c606b4e148bb230e1508fa42b5b2ac281855a68eb78270f54698c48a83201b9b + languageName: node + linkType: hard + "@babel/helper-create-class-features-plugin@npm:^7.18.6, @babel/helper-create-class-features-plugin@npm:^7.21.0": version: 7.21.4 resolution: "@babel/helper-create-class-features-plugin@npm:7.21.4" @@ -3228,6 +3295,16 @@ __metadata: languageName: node linkType: hard +"@babel/helper-module-imports@npm:^7.25.9": + version: 7.25.9 + resolution: "@babel/helper-module-imports@npm:7.25.9" + dependencies: + "@babel/traverse": ^7.25.9 + "@babel/types": ^7.25.9 + checksum: 1b411ce4ca825422ef7065dffae7d8acef52023e51ad096351e3e2c05837e9bf9fca2af9ca7f28dc26d596a588863d0fedd40711a88e350b736c619a80e704e6 + languageName: node + linkType: hard + "@babel/helper-module-transforms@npm:^7.18.0, @babel/helper-module-transforms@npm:^7.19.0": version: 7.19.0 resolution: "@babel/helper-module-transforms@npm:7.19.0" @@ -3335,6 +3412,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-module-transforms@npm:^7.26.0": + version: 7.26.0 + resolution: "@babel/helper-module-transforms@npm:7.26.0" + dependencies: + "@babel/helper-module-imports": ^7.25.9 + "@babel/helper-validator-identifier": ^7.25.9 + "@babel/traverse": ^7.25.9 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 942eee3adf2b387443c247a2c190c17c4fd45ba92a23087abab4c804f40541790d51ad5277e4b5b1ed8d5ba5b62de73857446b7742f835c18ebd350384e63917 + languageName: node + linkType: hard + "@babel/helper-optimise-call-expression@npm:^7.16.7, @babel/helper-optimise-call-expression@npm:^7.18.6": version: 7.18.6 resolution: "@babel/helper-optimise-call-expression@npm:7.18.6" @@ -3682,6 +3772,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-string-parser@npm:^7.25.9": + version: 7.25.9 + resolution: "@babel/helper-string-parser@npm:7.25.9" + checksum: 6435ee0849e101681c1849868278b5aee82686ba2c1e27280e5e8aca6233af6810d39f8e4e693d2f2a44a3728a6ccfd66f72d71826a94105b86b731697cdfa99 + languageName: node + linkType: hard + "@babel/helper-validator-identifier@npm:^7.18.6, @babel/helper-validator-identifier@npm:^7.19.1": version: 7.19.1 resolution: "@babel/helper-validator-identifier@npm:7.19.1" @@ -3710,6 +3807,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-identifier@npm:^7.25.9": + version: 7.25.9 + resolution: "@babel/helper-validator-identifier@npm:7.25.9" + checksum: 5b85918cb1a92a7f3f508ea02699e8d2422fe17ea8e82acd445006c0ef7520fbf48e3dbcdaf7b0a1d571fc3a2715a29719e5226636cb6042e15fe6ed2a590944 + languageName: node + linkType: hard + "@babel/helper-validator-option@npm:^7.18.6": version: 7.18.6 resolution: "@babel/helper-validator-option@npm:7.18.6" @@ -3745,6 +3849,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-option@npm:^7.25.9": + version: 7.25.9 + resolution: "@babel/helper-validator-option@npm:7.25.9" + checksum: 9491b2755948ebbdd68f87da907283698e663b5af2d2b1b02a2765761974b1120d5d8d49e9175b167f16f72748ffceec8c9cf62acfbee73f4904507b246e2b3d + languageName: node + linkType: hard + "@babel/helper-wrap-function@npm:^7.18.9": version: 7.20.5 resolution: "@babel/helper-wrap-function@npm:7.20.5" @@ -3856,6 +3967,16 @@ __metadata: languageName: node linkType: hard +"@babel/helpers@npm:^7.26.0": + version: 7.26.0 + resolution: "@babel/helpers@npm:7.26.0" + dependencies: + "@babel/template": ^7.25.9 + "@babel/types": ^7.26.0 + checksum: d77fe8d45033d6007eadfa440355c1355eed57902d5a302f450827ad3d530343430a21210584d32eef2f216ae463d4591184c6fc60cf205bbf3a884561469200 + languageName: node + linkType: hard + "@babel/highlight@npm:^7.10.4, @babel/highlight@npm:^7.18.6": version: 7.18.6 resolution: "@babel/highlight@npm:7.18.6" @@ -3955,6 +4076,17 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.25.9, @babel/parser@npm:^7.26.0, @babel/parser@npm:^7.26.2": + version: 7.26.2 + resolution: "@babel/parser@npm:7.26.2" + dependencies: + "@babel/types": ^7.26.0 + bin: + parser: ./bin/babel-parser.js + checksum: c88b5ea0adf357ef909cdc2c31e284a154943edc59f63f6e8a4c20bf773a1b2f3d8c2205e59c09ca7cdad91e7466300114548876529277a80651b6436a48d5d9 + languageName: node + linkType: hard + "@babel/parser@npm:^7.9.4": version: 7.20.13 resolution: "@babel/parser@npm:7.20.13" @@ -8001,6 +8133,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.25.0": + version: 7.26.0 + resolution: "@babel/runtime@npm:7.26.0" + dependencies: + regenerator-runtime: ^0.14.0 + checksum: c8e2c0504ab271b3467a261a8f119bf2603eb857a0d71e37791f4e3fae00f681365073cc79f141ddaa90c6077c60ba56448004ad5429d07ac73532be9f7cf28a + languageName: node + linkType: hard + "@babel/template@npm:^7.0.0, @babel/template@npm:^7.20.7": version: 7.20.7 resolution: "@babel/template@npm:7.20.7" @@ -8067,6 +8208,32 @@ __metadata: languageName: node linkType: hard +"@babel/template@npm:^7.25.9": + version: 7.25.9 + resolution: "@babel/template@npm:7.25.9" + dependencies: + "@babel/code-frame": ^7.25.9 + "@babel/parser": ^7.25.9 + "@babel/types": ^7.25.9 + checksum: 103641fea19c7f4e82dc913aa6b6ac157112a96d7c724d513288f538b84bae04fb87b1f1e495ac1736367b1bc30e10f058b30208fb25f66038e1f1eb4e426472 + languageName: node + linkType: hard + +"@babel/traverse--for-generate-function-map@npm:@babel/traverse@^7.25.3, @babel/traverse@npm:^7.18.9, @babel/traverse@npm:^7.23.2, @babel/traverse@npm:^7.24.8, @babel/traverse@npm:^7.25.0, @babel/traverse@npm:^7.25.1, @babel/traverse@npm:^7.25.2, @babel/traverse@npm:^7.25.3": + version: 7.25.3 + resolution: "@babel/traverse@npm:7.25.3" + dependencies: + "@babel/code-frame": ^7.24.7 + "@babel/generator": ^7.25.0 + "@babel/parser": ^7.25.3 + "@babel/template": ^7.25.0 + "@babel/types": ^7.25.2 + debug: ^4.3.1 + globals: ^11.1.0 + checksum: 5661308b1357816f1d4e2813a5dd82c6053617acc08c5c95db051b8b6577d07c4446bc861c9a5e8bf294953ac8266ae13d7d9d856b6b889fc0d34c1f51abbd8c + languageName: node + linkType: hard + "@babel/traverse@npm:7, @babel/traverse@npm:^7.20.0, @babel/traverse@npm:^7.20.5, @babel/traverse@npm:^7.20.7, @babel/traverse@npm:^7.21.0, @babel/traverse@npm:^7.21.2, @babel/traverse@npm:^7.21.4": version: 7.21.4 resolution: "@babel/traverse@npm:7.21.4" @@ -8103,21 +8270,6 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.18.9, @babel/traverse@npm:^7.23.2, @babel/traverse@npm:^7.24.8, @babel/traverse@npm:^7.25.0, @babel/traverse@npm:^7.25.1, @babel/traverse@npm:^7.25.2, @babel/traverse@npm:^7.25.3": - version: 7.25.3 - resolution: "@babel/traverse@npm:7.25.3" - dependencies: - "@babel/code-frame": ^7.24.7 - "@babel/generator": ^7.25.0 - "@babel/parser": ^7.25.3 - "@babel/template": ^7.25.0 - "@babel/types": ^7.25.2 - debug: ^4.3.1 - globals: ^11.1.0 - checksum: 5661308b1357816f1d4e2813a5dd82c6053617acc08c5c95db051b8b6577d07c4446bc861c9a5e8bf294953ac8266ae13d7d9d856b6b889fc0d34c1f51abbd8c - languageName: node - linkType: hard - "@babel/traverse@npm:^7.22.1": version: 7.22.4 resolution: "@babel/traverse@npm:7.22.4" @@ -8190,6 +8342,21 @@ __metadata: languageName: node linkType: hard +"@babel/traverse@npm:^7.25.9": + version: 7.25.9 + resolution: "@babel/traverse@npm:7.25.9" + dependencies: + "@babel/code-frame": ^7.25.9 + "@babel/generator": ^7.25.9 + "@babel/parser": ^7.25.9 + "@babel/template": ^7.25.9 + "@babel/types": ^7.25.9 + debug: ^4.3.1 + globals: ^11.1.0 + checksum: 901d325662ff1dd9bc51de00862e01055fa6bc374f5297d7e3731f2f0e268bbb1d2141f53fa82860aa308ee44afdcf186a948f16c83153927925804b95a9594d + languageName: node + linkType: hard + "@babel/types@npm:^7.0.0, @babel/types@npm:^7.12.11, @babel/types@npm:^7.16.0, @babel/types@npm:^7.16.8, @babel/types@npm:^7.18.10, @babel/types@npm:^7.18.6, @babel/types@npm:^7.18.9, @babel/types@npm:^7.19.0, @babel/types@npm:^7.19.3, @babel/types@npm:^7.19.4, @babel/types@npm:^7.3.0, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": version: 7.19.4 resolution: "@babel/types@npm:7.19.4" @@ -8267,6 +8434,16 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.25.9, @babel/types@npm:^7.26.0": + version: 7.26.0 + resolution: "@babel/types@npm:7.26.0" + dependencies: + "@babel/helper-string-parser": ^7.25.9 + "@babel/helper-validator-identifier": ^7.25.9 + checksum: a3dd37dabac693018872da96edb8c1843a605c1bfacde6c3f504fba79b972426a6f24df70aa646356c0c1b19bdd2c722c623c684a996c002381071680602280d + languageName: node + linkType: hard + "@base2/pretty-print-object@npm:1.0.1": version: 1.0.1 resolution: "@base2/pretty-print-object@npm:1.0.1" @@ -13296,6 +13473,8 @@ __metadata: "@react-native/eslint-config": 0.74.87 "@react-native/metro-config": 0.74.87 "@react-native/typescript-config": 0.74.87 + "@testing-library/jest-native": 5.4.3 + "@testing-library/react-native": 12.9.0 "@tsconfig/react-native": 3.0.5 "@types/intl": ^1 "@types/jest": ^29.2.1 @@ -13306,7 +13485,6 @@ __metadata: babel-jest: ^29.6.3 babel-loader: ^8.3.0 babel-plugin-formatjs: 10.3.9 - babel-plugin-module-resolver: 5.0.2 compare-versions: 6.1.1 configcat-js: 7.0.0 dynamic-color: 0.3.0 @@ -13319,8 +13497,9 @@ __metadata: intl: 1.2.5 jest: 29.7.0 lodash: 4.17.21 + metro-config: 0.81.0 path-to-regexp: 6.2.2 - react: 18.2.0 + react: 18.3.1 react-intl: 5.20.12 react-native: 0.74.5 react-native-app-auth: 7.2.0 @@ -15144,6 +15323,15 @@ __metadata: languageName: node linkType: hard +"@nrwl/react-native@npm:19.4.0": + version: 19.4.0 + resolution: "@nrwl/react-native@npm:19.4.0" + dependencies: + "@nx/react-native": 19.4.0 + checksum: c107fcb4b618d6cd055dbebfab93b2d63429d9a4d417d5337746184245180f2136b98dba36b2a9d6b03969aac71f8b0abb689c5321ef6044c6e007e1f415fa48 + languageName: node + linkType: hard + "@nrwl/react@npm:19.4.0": version: 19.4.0 resolution: "@nrwl/react@npm:19.4.0" @@ -15603,6 +15791,32 @@ __metadata: languageName: node linkType: hard +"@nx/react-native@npm:19.4.0": + version: 19.4.0 + resolution: "@nx/react-native@npm:19.4.0" + dependencies: + "@nrwl/react-native": 19.4.0 + "@nx/devkit": 19.4.0 + "@nx/eslint": 19.4.0 + "@nx/jest": 19.4.0 + "@nx/js": 19.4.0 + "@nx/react": 19.4.0 + "@nx/workspace": 19.4.0 + ajv: ^8.12.0 + chalk: ^4.1.0 + enhanced-resolve: ^5.8.3 + fs-extra: ^11.1.0 + glob: 7.1.4 + ignore: ^5.0.4 + metro-config: ~0.80.4 + metro-resolver: ~0.80.4 + node-fetch: ^2.6.7 + tsconfig-paths: ^4.1.2 + tslib: ^2.3.0 + checksum: 1b6834ff4997932a131b28bfbc898022f00146799291f5d1a1d24b7d9ed0dac8fe79094e4e8a936b6e9a1cd430a8d2bf02acec5ccec2a49c33921ab864994f23 + languageName: node + linkType: hard + "@nx/react@npm:19.4.0": version: 19.4.0 resolution: "@nx/react@npm:19.4.0" @@ -16998,7 +17212,7 @@ __metadata: languageName: node linkType: hard -"@react-native-community/cli-platform-android@npm:13.6.9": +"@react-native-community/cli-platform-android@npm:13.6.9, @react-native-community/cli-platform-android@npm:~13.6.6": version: 13.6.9 resolution: "@react-native-community/cli-platform-android@npm:13.6.9" dependencies: @@ -17444,6 +17658,13 @@ __metadata: languageName: node linkType: hard +"@react-native/normalize-colors@npm:^0.74.1": + version: 0.74.88 + resolution: "@react-native/normalize-colors@npm:0.74.88" + checksum: 348d0f1b9802e824843ec58ed90f72af078b81dd576f72c45caa1ed9846ea733b0dab932e431f88ebc40a186e7443875b64e8e2cf8e669a59abef0aedf2d9aa7 + languageName: node + linkType: hard + "@react-native/typescript-config@npm:0.74.87": version: 0.74.87 resolution: "@react-native/typescript-config@npm:0.74.87" @@ -19988,6 +20209,15 @@ __metadata: languageName: node linkType: hard +"@svgr/babel-plugin-transform-react-native-svg@npm:8.1.0": + version: 8.1.0 + resolution: "@svgr/babel-plugin-transform-react-native-svg@npm:8.1.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 85b434a57572f53bd2b9f0606f253e1fcf57b4a8c554ec3f2d43ed17f50d8cae200cb3aaf1ec9d626e1456e8b135dce530ae047eb0bed6d4bf98a752d6640459 + languageName: node + linkType: hard + "@svgr/babel-plugin-transform-svg-component@npm:8.0.0": version: 8.0.0 resolution: "@svgr/babel-plugin-transform-svg-component@npm:8.0.0" @@ -20015,6 +20245,24 @@ __metadata: languageName: node linkType: hard +"@svgr/babel-preset@npm:8.1.0": + version: 8.1.0 + resolution: "@svgr/babel-preset@npm:8.1.0" + dependencies: + "@svgr/babel-plugin-add-jsx-attribute": 8.0.0 + "@svgr/babel-plugin-remove-jsx-attribute": 8.0.0 + "@svgr/babel-plugin-remove-jsx-empty-expression": 8.0.0 + "@svgr/babel-plugin-replace-jsx-attribute-value": 8.0.0 + "@svgr/babel-plugin-svg-dynamic-title": 8.0.0 + "@svgr/babel-plugin-svg-em-dimensions": 8.0.0 + "@svgr/babel-plugin-transform-react-native-svg": 8.1.0 + "@svgr/babel-plugin-transform-svg-component": 8.0.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 3a67930f080b8891e1e8e2595716b879c944d253112bae763dce59807ba23454d162216c8d66a0a0e3d4f38a649ecd6c387e545d1e1261dd69a68e9a3392ee08 + languageName: node + linkType: hard + "@svgr/core@npm:8.0.0": version: 8.0.0 resolution: "@svgr/core@npm:8.0.0" @@ -20028,6 +20276,19 @@ __metadata: languageName: node linkType: hard +"@svgr/core@npm:^8.1.0": + version: 8.1.0 + resolution: "@svgr/core@npm:8.1.0" + dependencies: + "@babel/core": ^7.21.3 + "@svgr/babel-preset": 8.1.0 + camelcase: ^6.2.0 + cosmiconfig: ^8.1.3 + snake-case: ^3.0.4 + checksum: da4a12865c7dc59829d58df8bd232d6c85b7115fda40da0d2f844a1a51886e2e945560596ecfc0345d37837ac457de86a931e8b8d8550e729e0c688c02250d8a + languageName: node + linkType: hard + "@svgr/hast-util-to-babel-ast@npm:8.0.0": version: 8.0.0 resolution: "@svgr/hast-util-to-babel-ast@npm:8.0.0" @@ -20052,6 +20313,20 @@ __metadata: languageName: node linkType: hard +"@svgr/plugin-jsx@npm:^8.1.0": + version: 8.1.0 + resolution: "@svgr/plugin-jsx@npm:8.1.0" + dependencies: + "@babel/core": ^7.21.3 + "@svgr/babel-preset": 8.1.0 + "@svgr/hast-util-to-babel-ast": 8.0.0 + svg-parser: ^2.0.4 + peerDependencies: + "@svgr/core": "*" + checksum: 0418a9780753d3544912ee2dad5d2cf8d12e1ba74df8053651b3886aeda54d5f0f7d2dece0af5e0d838332c4f139a57f0dabaa3ca1afa4d1a765efce6a7656f2 + languageName: node + linkType: hard + "@svgr/plugin-svgo@npm:8.0.1": version: 8.0.1 resolution: "@svgr/plugin-svgo@npm:8.0.1" @@ -20065,6 +20340,19 @@ __metadata: languageName: node linkType: hard +"@svgr/plugin-svgo@npm:^8.1.0": + version: 8.1.0 + resolution: "@svgr/plugin-svgo@npm:8.1.0" + dependencies: + cosmiconfig: ^8.1.3 + deepmerge: ^4.3.1 + svgo: ^3.0.2 + peerDependencies: + "@svgr/core": "*" + checksum: 59d9d214cebaacca9ca71a561f463d8b7e5a68ca9443e4792a42d903acd52259b1790c0680bc6afecc3f00a255a6cbd7ea278a9f625bac443620ea58a590c2d0 + languageName: node + linkType: hard + "@svgr/webpack@npm:^8.0.1": version: 8.0.1 resolution: "@svgr/webpack@npm:8.0.1" @@ -20692,6 +20980,42 @@ __metadata: languageName: node linkType: hard +"@testing-library/jest-native@npm:5.4.3": + version: 5.4.3 + resolution: "@testing-library/jest-native@npm:5.4.3" + dependencies: + chalk: ^4.1.2 + jest-diff: ^29.0.1 + jest-matcher-utils: ^29.0.1 + pretty-format: ^29.0.3 + redent: ^3.0.0 + peerDependencies: + react: ">=16.0.0" + react-native: ">=0.59" + react-test-renderer: ">=16.0.0" + checksum: 2a4ebfeff09523860771cfddac6fcc3faa2f855dc63255b9efc016e727132320f16f935cec9717d6d79cfa6715fce6ded877215c8ec85d236a5c3136a65b1020 + languageName: node + linkType: hard + +"@testing-library/react-native@npm:12.9.0": + version: 12.9.0 + resolution: "@testing-library/react-native@npm:12.9.0" + dependencies: + jest-matcher-utils: ^29.7.0 + pretty-format: ^29.7.0 + redent: ^3.0.0 + peerDependencies: + jest: ">=28.0.0" + react: ">=16.8.0" + react-native: ">=0.59" + react-test-renderer: ">=16.8.0" + peerDependenciesMeta: + jest: + optional: true + checksum: 88115b22c127f39b2e1e8098dc1c93ea9c7393800a24f4f380bed64425cc685f98cad5b56b9cb48d85f0dbed1f0f208d0de44137c6e789c98161ff2715f70646 + languageName: node + linkType: hard + "@testing-library/react@npm:15.0.6": version: 15.0.6 resolution: "@testing-library/react@npm:15.0.6" @@ -25995,19 +26319,6 @@ __metadata: languageName: node linkType: hard -"babel-plugin-module-resolver@npm:5.0.2": - version: 5.0.2 - resolution: "babel-plugin-module-resolver@npm:5.0.2" - dependencies: - find-babel-config: ^2.1.1 - glob: ^9.3.3 - pkg-up: ^3.1.0 - reselect: ^4.1.7 - resolve: ^1.22.8 - checksum: f1d198acbbbd0b76c9c0c4aacbf9f1ef90f8d36b3d5209d9e7a75cadee2113a73711550ebddeb9464d143b71df19adc75e165dff99ada2614d7ea333affe3b5a - languageName: node - linkType: hard - "babel-plugin-polyfill-corejs2@npm:^0.3.3": version: 0.3.3 resolution: "babel-plugin-polyfill-corejs2@npm:0.3.3" @@ -29625,6 +29936,15 @@ __metadata: languageName: node linkType: hard +"css-in-js-utils@npm:^3.1.0": + version: 3.1.0 + resolution: "css-in-js-utils@npm:3.1.0" + dependencies: + hyphenate-style-name: ^1.0.3 + checksum: 066318e918c04a5e5bce46b38fe81052ea6ac051bcc6d3c369a1d59ceb1546cb2b6086901ab5d22be084122ee3732169996a3dfb04d3406eaee205af77aec61b + languageName: node + linkType: hard + "css-loader@npm:^6.4.0, css-loader@npm:^6.7.1": version: 6.8.1 resolution: "css-loader@npm:6.8.1" @@ -31816,6 +32136,16 @@ __metadata: languageName: node linkType: hard +"enhanced-resolve@npm:^5.8.3": + version: 5.17.1 + resolution: "enhanced-resolve@npm:5.17.1" + dependencies: + graceful-fs: ^4.2.4 + tapable: ^2.2.0 + checksum: 4bc38cf1cea96456f97503db7280394177d1bc46f8f87c267297d04f795ac5efa81e48115a2f5b6273c781027b5b6bfc5f62b54df629e4d25fa7001a86624f59 + languageName: node + linkType: hard + "enquirer@npm:^2.3.6, enquirer@npm:~2.3.6": version: 2.3.6 resolution: "enquirer@npm:2.3.6" @@ -33698,6 +34028,13 @@ __metadata: languageName: node linkType: hard +"exponential-backoff@npm:^3.1.1": + version: 3.1.1 + resolution: "exponential-backoff@npm:3.1.1" + checksum: 3d21519a4f8207c99f7457287291316306255a328770d320b401114ec8481986e4e467e854cb9914dd965e0a1ca810a23ccb559c642c88f4c7f55c55778a9b48 + languageName: node + linkType: hard + "express-validator@npm:6.14.0": version: 6.14.0 resolution: "express-validator@npm:6.14.0" @@ -34019,6 +34356,13 @@ __metadata: languageName: node linkType: hard +"fast-loops@npm:^1.1.3": + version: 1.1.4 + resolution: "fast-loops@npm:1.1.4" + checksum: 8031a20f465ef35ac4ad98258470250636112d34f7e4efcb4ef21f3ced99df95a1ef1f0d6943df729a1e3e12a9df9319f3019df8cc1a0e0ed5a118bd72e505f9 + languageName: node + linkType: hard + "fast-memoize@npm:^2.5.2": version: 2.5.2 resolution: "fast-memoize@npm:2.5.2" @@ -34161,6 +34505,21 @@ __metadata: languageName: node linkType: hard +"fbjs@npm:^3.0.4": + version: 3.0.5 + resolution: "fbjs@npm:3.0.5" + dependencies: + cross-fetch: ^3.1.5 + fbjs-css-vars: ^1.0.0 + loose-envify: ^1.0.0 + object-assign: ^4.1.0 + promise: ^7.1.1 + setimmediate: ^1.0.5 + ua-parser-js: ^1.0.35 + checksum: e609b5b64686bc96495a5c67728ed9b2710b9b3d695c5759c5f5e47c9483d1c323543ac777a86459e3694efc5712c6ce7212e944feb19752867d699568bb0e54 + languageName: node + linkType: hard + "fd-slicer@npm:~1.1.0": version: 1.1.0 resolution: "fd-slicer@npm:1.1.0" @@ -34377,16 +34736,6 @@ __metadata: languageName: node linkType: hard -"find-babel-config@npm:^2.1.1": - version: 2.1.1 - resolution: "find-babel-config@npm:2.1.1" - dependencies: - json5: ^2.2.3 - path-exists: ^4.0.0 - checksum: 4be54397339520e0cd49870acb10366684ffc001fd0b7bffedd0fe9d3e1d82234692d3cb4e5ba95280a35887238ba6f82dc79569a13a3749ae3931c23e0b3a99 - languageName: node - linkType: hard - "find-cache-dir@npm:^2.0.0": version: 2.1.0 resolution: "find-cache-dir@npm:2.1.0" @@ -35656,18 +36005,6 @@ __metadata: languageName: node linkType: hard -"glob@npm:^9.3.3": - version: 9.3.5 - resolution: "glob@npm:9.3.5" - dependencies: - fs.realpath: ^1.0.0 - minimatch: ^8.0.2 - minipass: ^4.2.4 - path-scurry: ^1.6.1 - checksum: 94b093adbc591bc36b582f77927d1fb0dbf3ccc231828512b017601408be98d1fe798fc8c0b19c6f2d1a7660339c3502ce698de475e9d938ccbb69b47b647c84 - languageName: node - linkType: hard - "global-dirs@npm:^3.0.0": version: 3.0.0 resolution: "global-dirs@npm:3.0.0" @@ -36381,6 +36718,20 @@ __metadata: languageName: node linkType: hard +"hermes-estree@npm:0.23.1": + version: 0.23.1 + resolution: "hermes-estree@npm:0.23.1" + checksum: 0f63edc365099304f4cd8e91a3666a4fb5a2a47baee751dc120df9201640112865944cae93617f554af71be9827e96547f9989f4972d6964ecc121527295fec6 + languageName: node + linkType: hard + +"hermes-estree@npm:0.24.0": + version: 0.24.0 + resolution: "hermes-estree@npm:0.24.0" + checksum: 23d09013c824cd4628f6bae50c7a703cbafcc26ff1802cb35547fac41be4aac6e9892656bb6eb495e5c8c4b1287311dad8eab0f541ff8f1d2f0265b75053002e + languageName: node + linkType: hard + "hermes-parser@npm:0.19.1": version: 0.19.1 resolution: "hermes-parser@npm:0.19.1" @@ -36399,6 +36750,24 @@ __metadata: languageName: node linkType: hard +"hermes-parser@npm:0.23.1": + version: 0.23.1 + resolution: "hermes-parser@npm:0.23.1" + dependencies: + hermes-estree: 0.23.1 + checksum: a08008928aea9ea9a2cab2c0fac3cffa21f7869ab3fabb68e5add0fe057737a0c352d7a446426f7956172ccc8f2d4a215b4fc20d1d08354fc8dc16772c248fce + languageName: node + linkType: hard + +"hermes-parser@npm:0.24.0": + version: 0.24.0 + resolution: "hermes-parser@npm:0.24.0" + dependencies: + hermes-estree: 0.24.0 + checksum: c23cb81d320cedc74841c254ea54d94328f65aa6259375d48ab2b5a3ad2b528c55058726d852376811e4018636d8fd9305a4b2bfa5a962297c1baa57444be172 + languageName: node + linkType: hard + "hermes-profile-transformer@npm:^0.0.6": version: 0.0.6 resolution: "hermes-profile-transformer@npm:0.0.6" @@ -36979,6 +37348,13 @@ __metadata: languageName: node linkType: hard +"hyphenate-style-name@npm:^1.0.3": + version: 1.1.0 + resolution: "hyphenate-style-name@npm:1.1.0" + checksum: b9ed74e29181d96bd58a2d0e62fc4a19879db591dba268275829ff0ae595fcdf11faafaeaa63330a45c3004664d7db1f0fc7cdb372af8ee4615ed8260302c207 + languageName: node + linkType: hard + "hypher@npm:0.2.5": version: 0.2.5 resolution: "hypher@npm:0.2.5" @@ -37315,6 +37691,16 @@ __metadata: languageName: node linkType: hard +"inline-style-prefixer@npm:^6.0.1": + version: 6.0.4 + resolution: "inline-style-prefixer@npm:6.0.4" + dependencies: + css-in-js-utils: ^3.1.0 + fast-loops: ^1.1.3 + checksum: caf7a75d18acbedc7e3b8bfac17563082becd2df6b65accad964a6afdf490329b42315c37fe65ba0177cc10fd32809eb40d62aba23a0118c74d87d4fc58defa2 + languageName: node + linkType: hard + "inquirer-select-directory@npm:^1.2.0": version: 1.2.0 resolution: "inquirer-select-directory@npm:1.2.0" @@ -38616,6 +39002,7 @@ __metadata: "@nestjs/terminus": 10.2.0 "@nestjs/testing": 10.0.5 "@nx/cypress": 19.4.0 + "@nx/devkit": 19.4.0 "@nx/eslint": 19.4.0 "@nx/eslint-plugin": 19.4.0 "@nx/express": 19.4.0 @@ -38626,12 +39013,16 @@ __metadata: "@nx/node": 19.4.0 "@nx/playwright": 19.4.0 "@nx/react": 19.4.0 + "@nx/react-native": 19.4.0 "@nx/storybook": 19.4.0 "@nx/web": 19.4.0 "@nx/webpack": 19.4.0 "@nx/workspace": 19.4.0 "@openapitools/openapi-generator-cli": 1.0.15-4.3.1 "@playwright/test": 1.48 + "@react-native-community/cli-platform-android": ~13.6.6 + "@react-native/babel-preset": 0.74.87 + "@react-native/metro-config": 0.74.87 "@react-pdf/renderer": ^3.1.9 "@rehooks/component-size": 1.0.3 "@simplewebauthn/server": 10.0.0 @@ -38651,7 +39042,9 @@ __metadata: "@swc/helpers": 0.5.11 "@testing-library/cypress": 8.0.3 "@testing-library/jest-dom": 5.16.5 + "@testing-library/jest-native": 5.4.3 "@testing-library/react": 15.0.6 + "@testing-library/react-native": 12.9.0 "@testing-library/user-event": 14.4.3 "@types/archiver": 6.0.2 "@types/aws-sdk": 2.7.0 @@ -38815,6 +39208,7 @@ __metadata: jest-environment-jsdom: 29.7.0 jest-environment-node: 29.7.0 jest-mock-extended: 3.0.5 + jest-react-native: 18.0.0 jest-transform-stub: 2.0.0 js-base64: 2.5.2 js-cookie: 2.2.1 @@ -38877,6 +39271,10 @@ __metadata: react-is: 18.3.1 react-keyed-flatten-children: 1.2.0 react-modal: 3.15.1 + react-native: 0.74.5 + react-native-svg: 15.2.0 + react-native-svg-transformer: 1.3.0 + react-native-web: ^0.19.11 react-number-format: 4.9.1 react-pdf: 9.1.0 react-popper: 2.3.0 @@ -38884,6 +39282,7 @@ __metadata: react-router-dom: 6.11.2 react-select: 5.8.2 react-table: 7.7.0 + react-test-renderer: 18.2.0 react-toastify: 6.0.8 react-top-loading-bar: 2.3.1 react-use: 15.3.3 @@ -39334,7 +39733,7 @@ __metadata: languageName: node linkType: hard -"jest-diff@npm:^29.4.1, jest-diff@npm:^29.7.0": +"jest-diff@npm:^29.0.1, jest-diff@npm:^29.4.1, jest-diff@npm:^29.7.0": version: 29.7.0 resolution: "jest-diff@npm:29.7.0" dependencies: @@ -39516,6 +39915,18 @@ __metadata: languageName: node linkType: hard +"jest-matcher-utils@npm:^29.0.1, jest-matcher-utils@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-matcher-utils@npm:29.7.0" + dependencies: + chalk: ^4.0.0 + jest-diff: ^29.7.0 + jest-get-type: ^29.6.3 + pretty-format: ^29.7.0 + checksum: d7259e5f995d915e8a37a8fd494cb7d6af24cd2a287b200f831717ba0d015190375f9f5dc35393b8ba2aae9b2ebd60984635269c7f8cff7d85b077543b7744cd + languageName: node + linkType: hard + "jest-matcher-utils@npm:^29.5.0": version: 29.5.0 resolution: "jest-matcher-utils@npm:29.5.0" @@ -39528,18 +39939,6 @@ __metadata: languageName: node linkType: hard -"jest-matcher-utils@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-matcher-utils@npm:29.7.0" - dependencies: - chalk: ^4.0.0 - jest-diff: ^29.7.0 - jest-get-type: ^29.6.3 - pretty-format: ^29.7.0 - checksum: d7259e5f995d915e8a37a8fd494cb7d6af24cd2a287b200f831717ba0d015190375f9f5dc35393b8ba2aae9b2ebd60984635269c7f8cff7d85b077543b7744cd - languageName: node - linkType: hard - "jest-message-util@npm:^27.5.1": version: 27.5.1 resolution: "jest-message-util@npm:27.5.1" @@ -39626,6 +40025,15 @@ __metadata: languageName: node linkType: hard +"jest-react-native@npm:18.0.0": + version: 18.0.0 + resolution: "jest-react-native@npm:18.0.0" + peerDependencies: + react-native: ">=0.38.0" + checksum: 3c524bbdf4a030d99c8202e0f67427130a092f884935ee4c93bcb5a9e9cca8ad1f5db259315175c9b56c2eae5aefbf12ea7bc532956ba806698fbbe3d0ab2dcf + languageName: node + linkType: hard + "jest-regex-util@npm:^27.5.1": version: 27.5.1 resolution: "jest-regex-util@npm:27.5.1" @@ -40434,6 +40842,15 @@ __metadata: languageName: node linkType: hard +"jsesc@npm:^3.0.2": + version: 3.0.2 + resolution: "jsesc@npm:3.0.2" + bin: + jsesc: bin/jsesc + checksum: a36d3ca40574a974d9c2063bf68c2b6141c20da8f2a36bd3279fc802563f35f0527a6c828801295bdfb2803952cf2cf387786c2c90ed564f88d5782475abfe3c + languageName: node + linkType: hard + "jsesc@npm:~0.5.0": version: 0.5.0 resolution: "jsesc@npm:0.5.0" @@ -42997,6 +43414,18 @@ __metadata: languageName: node linkType: hard +"metro-babel-transformer@npm:0.80.12": + version: 0.80.12 + resolution: "metro-babel-transformer@npm:0.80.12" + dependencies: + "@babel/core": ^7.20.0 + flow-enums-runtime: ^0.0.6 + hermes-parser: 0.23.1 + nullthrows: ^1.1.1 + checksum: 1ea8bce0c169f3d8bf46f56da126ca52f4c8ba5ca9ffeaca987c34d269b0a3e2a54d0544bd44bfa5d0322e37f0171a52d2a2160defcbcd91ec1fd96f62b0eece + languageName: node + linkType: hard + "metro-babel-transformer@npm:0.80.9": version: 0.80.9 resolution: "metro-babel-transformer@npm:0.80.9" @@ -43008,6 +43437,27 @@ __metadata: languageName: node linkType: hard +"metro-babel-transformer@npm:0.81.0": + version: 0.81.0 + resolution: "metro-babel-transformer@npm:0.81.0" + dependencies: + "@babel/core": ^7.25.2 + flow-enums-runtime: ^0.0.6 + hermes-parser: 0.24.0 + nullthrows: ^1.1.1 + checksum: e67ef5175f574fbf4a3b6c4f5fd209eb04026cdc32a38e2ebaea21a8c1d4ca20d234aba8e3bff95bfcf60353aaaa0e6369544fe15b1d02aa07f77ab2c26cf053 + languageName: node + linkType: hard + +"metro-cache-key@npm:0.80.12": + version: 0.80.12 + resolution: "metro-cache-key@npm:0.80.12" + dependencies: + flow-enums-runtime: ^0.0.6 + checksum: 7a06601180604361339d19eb833d61b79cc188a4e6ebe73188cc10fbf3a33e711d74c81d1d19a14b6581bd9dfeebe1b253684360682d033ab55909c9995b6a18 + languageName: node + linkType: hard + "metro-cache-key@npm:0.80.9": version: 0.80.9 resolution: "metro-cache-key@npm:0.80.9" @@ -43015,6 +43465,26 @@ __metadata: languageName: node linkType: hard +"metro-cache-key@npm:0.81.0": + version: 0.81.0 + resolution: "metro-cache-key@npm:0.81.0" + dependencies: + flow-enums-runtime: ^0.0.6 + checksum: a96e4062ac0f4684f1d80c8b8c3da380c9d7be506c2bc14750d46a6850610c6e05cb1907cc5421393299f25f40575335e899667519d5435c95a09b0438619847 + languageName: node + linkType: hard + +"metro-cache@npm:0.80.12": + version: 0.80.12 + resolution: "metro-cache@npm:0.80.12" + dependencies: + exponential-backoff: ^3.1.1 + flow-enums-runtime: ^0.0.6 + metro-core: 0.80.12 + checksum: 724e33fdda6a3568572c36a3f2d3465ad1b5f3e8ded5ec116b98e0038826187ebdadd05f77e91ddc17fa71ff4dd91281793a940e7b619cac36044ed868abc01d + languageName: node + linkType: hard + "metro-cache@npm:0.80.9": version: 0.80.9 resolution: "metro-cache@npm:0.80.9" @@ -43025,6 +43495,33 @@ __metadata: languageName: node linkType: hard +"metro-cache@npm:0.81.0": + version: 0.81.0 + resolution: "metro-cache@npm:0.81.0" + dependencies: + exponential-backoff: ^3.1.1 + flow-enums-runtime: ^0.0.6 + metro-core: 0.81.0 + checksum: 0498a93b07b8125987268dde7f95b56ea61826be7834b87f03595de905210dc2675855d8dbbbc0aab0a2f50ed8be0086b096a4085f7320247e3fc6added45167 + languageName: node + linkType: hard + +"metro-config@npm:0.80.12, metro-config@npm:~0.80.4": + version: 0.80.12 + resolution: "metro-config@npm:0.80.12" + dependencies: + connect: ^3.6.5 + cosmiconfig: ^5.0.5 + flow-enums-runtime: ^0.0.6 + jest-validate: ^29.6.3 + metro: 0.80.12 + metro-cache: 0.80.12 + metro-core: 0.80.12 + metro-runtime: 0.80.12 + checksum: 49496d2bc875fbb8c89639979753377888f5ce779742a4ef487d812e7c5f3f6c87dd6ae129727f614d2fe3210f7fde08041055d29772b8c86c018e2ef08e7785 + languageName: node + linkType: hard + "metro-config@npm:0.80.9, metro-config@npm:^0.80.3": version: 0.80.9 resolution: "metro-config@npm:0.80.9" @@ -43040,6 +43537,33 @@ __metadata: languageName: node linkType: hard +"metro-config@npm:0.81.0": + version: 0.81.0 + resolution: "metro-config@npm:0.81.0" + dependencies: + connect: ^3.6.5 + cosmiconfig: ^5.0.5 + flow-enums-runtime: ^0.0.6 + jest-validate: ^29.6.3 + metro: 0.81.0 + metro-cache: 0.81.0 + metro-core: 0.81.0 + metro-runtime: 0.81.0 + checksum: 4969423a292b4aec8f604ae0f682bd62f463ee7a84459c1cf069ff0239427a01e287b97516d265a6b1ec9e8a7b3eb09ad5a8b914e469c9aff56f25473325fe29 + languageName: node + linkType: hard + +"metro-core@npm:0.80.12": + version: 0.80.12 + resolution: "metro-core@npm:0.80.12" + dependencies: + flow-enums-runtime: ^0.0.6 + lodash.throttle: ^4.1.1 + metro-resolver: 0.80.12 + checksum: 319f3965fa76fc08987cbd0228024bdbb0eaad7406e384e48929674188f1066cbc7a233053615ebd84b3ce1bbae28f59c114885fd0a0c179a580319ed69f717e + languageName: node + linkType: hard + "metro-core@npm:0.80.9, metro-core@npm:^0.80.3": version: 0.80.9 resolution: "metro-core@npm:0.80.9" @@ -43050,6 +43574,40 @@ __metadata: languageName: node linkType: hard +"metro-core@npm:0.81.0": + version: 0.81.0 + resolution: "metro-core@npm:0.81.0" + dependencies: + flow-enums-runtime: ^0.0.6 + lodash.throttle: ^4.1.1 + metro-resolver: 0.81.0 + checksum: 4e9e63d4c29f7a4f3e13ee8281c2be4458f5482de5f73d6206782cca78dc580b4d3a16516ff278313fcd1a3e4177e521b3aa0f12768fbf5cc335797557846953 + languageName: node + linkType: hard + +"metro-file-map@npm:0.80.12": + version: 0.80.12 + resolution: "metro-file-map@npm:0.80.12" + dependencies: + anymatch: ^3.0.3 + debug: ^2.2.0 + fb-watchman: ^2.0.0 + flow-enums-runtime: ^0.0.6 + fsevents: ^2.3.2 + graceful-fs: ^4.2.4 + invariant: ^2.2.4 + jest-worker: ^29.6.3 + micromatch: ^4.0.4 + node-abort-controller: ^3.1.1 + nullthrows: ^1.1.1 + walker: ^1.0.7 + dependenciesMeta: + fsevents: + optional: true + checksum: 5e6eafcfafe55fd8a9a6e5613394a20ed2a0ad433a394dcb830f017b8fc9d82ddcd715391e36abe5e98c651c074b99a806d3b04d76f2cadb225f9f5b1c92daef + languageName: node + linkType: hard + "metro-file-map@npm:0.80.9": version: 0.80.9 resolution: "metro-file-map@npm:0.80.9" @@ -43072,6 +43630,39 @@ __metadata: languageName: node linkType: hard +"metro-file-map@npm:0.81.0": + version: 0.81.0 + resolution: "metro-file-map@npm:0.81.0" + dependencies: + anymatch: ^3.0.3 + debug: ^2.2.0 + fb-watchman: ^2.0.0 + flow-enums-runtime: ^0.0.6 + fsevents: ^2.3.2 + graceful-fs: ^4.2.4 + invariant: ^2.2.4 + jest-worker: ^29.6.3 + micromatch: ^4.0.4 + node-abort-controller: ^3.1.1 + nullthrows: ^1.1.1 + walker: ^1.0.7 + dependenciesMeta: + fsevents: + optional: true + checksum: fc99466066fc57d506a90b8dbfc85b9aed3b3dfe362f42c35e24a3f0244b5f3e94b833b52b20cdd728842a1ef7e6c2132b9951a2c2d4013fb470e3a65b9971e0 + languageName: node + linkType: hard + +"metro-minify-terser@npm:0.80.12": + version: 0.80.12 + resolution: "metro-minify-terser@npm:0.80.12" + dependencies: + flow-enums-runtime: ^0.0.6 + terser: ^5.15.0 + checksum: ff527b3f04c5814db139e55ceb7689aaaf0af5c7fbb0eb5d4a6f22044932dfb10bd385d388fa7b352acd03a2d078edaf43a6b5cd11cbc87a7c5502a34fc12735 + languageName: node + linkType: hard + "metro-minify-terser@npm:0.80.9": version: 0.80.9 resolution: "metro-minify-terser@npm:0.80.9" @@ -43081,6 +43672,25 @@ __metadata: languageName: node linkType: hard +"metro-minify-terser@npm:0.81.0": + version: 0.81.0 + resolution: "metro-minify-terser@npm:0.81.0" + dependencies: + flow-enums-runtime: ^0.0.6 + terser: ^5.15.0 + checksum: 53472e5d476613c652f0e8bdf68429c80c66b71dd9a559c2185d56f41a8463ba3431353d453d2e20615875d070389ec24247ddbce67c4d7783bfc85113af18e0 + languageName: node + linkType: hard + +"metro-resolver@npm:0.80.12, metro-resolver@npm:~0.80.4": + version: 0.80.12 + resolution: "metro-resolver@npm:0.80.12" + dependencies: + flow-enums-runtime: ^0.0.6 + checksum: a520030a65afab2f3282604ef6dec802051899a356910606b8ffbc5b82a722008d9d416c8ba3d9ef9527912206586b713733b776803a6b76adac72bcb31870cd + languageName: node + linkType: hard + "metro-resolver@npm:0.80.9": version: 0.80.9 resolution: "metro-resolver@npm:0.80.9" @@ -43088,6 +43698,25 @@ __metadata: languageName: node linkType: hard +"metro-resolver@npm:0.81.0": + version: 0.81.0 + resolution: "metro-resolver@npm:0.81.0" + dependencies: + flow-enums-runtime: ^0.0.6 + checksum: 38349c79b5023d993baf30c7feeb9d60287f33e7bf559b75ce6b4177a4acd991353a0fea0a8caeec9a78efa244c8608c0e5bdff4ac64d6fda89ca0b81c9ca3fc + languageName: node + linkType: hard + +"metro-runtime@npm:0.80.12": + version: 0.80.12 + resolution: "metro-runtime@npm:0.80.12" + dependencies: + "@babel/runtime": ^7.25.0 + flow-enums-runtime: ^0.0.6 + checksum: 11a6d36c7dcf9d221f7de6989556f45d4d64cd1cdd225ec96273b584138b4aa77b7afdc9e9a9488d1dc9a3d90f8e94bb68ab149079cc6ebdb8f8f8b03462cb4f + languageName: node + linkType: hard + "metro-runtime@npm:0.80.9, metro-runtime@npm:^0.80.3": version: 0.80.9 resolution: "metro-runtime@npm:0.80.9" @@ -43097,6 +43726,33 @@ __metadata: languageName: node linkType: hard +"metro-runtime@npm:0.81.0": + version: 0.81.0 + resolution: "metro-runtime@npm:0.81.0" + dependencies: + "@babel/runtime": ^7.25.0 + flow-enums-runtime: ^0.0.6 + checksum: 812869ed71d6017d04c3affafa0b1bd4c86075569e0eb98030b8abddb59923903e3dc8eb23d7dd027384496e27010f6aad7839b0e1105e3873c31d0269fb7971 + languageName: node + linkType: hard + +"metro-source-map@npm:0.80.12": + version: 0.80.12 + resolution: "metro-source-map@npm:0.80.12" + dependencies: + "@babel/traverse": ^7.20.0 + "@babel/types": ^7.20.0 + flow-enums-runtime: ^0.0.6 + invariant: ^2.2.4 + metro-symbolicate: 0.80.12 + nullthrows: ^1.1.1 + ob1: 0.80.12 + source-map: ^0.5.6 + vlq: ^1.0.0 + checksum: 39575bff8666abd0944ec71e01a0c0eacbeab48277528608e894ffa6691c4267c389ee51ad86d5cd8e96f13782b66e1f693a3c60786bb201268678232dce6130 + languageName: node + linkType: hard + "metro-source-map@npm:0.80.9, metro-source-map@npm:^0.80.3": version: 0.80.9 resolution: "metro-source-map@npm:0.80.9" @@ -43113,6 +43769,41 @@ __metadata: languageName: node linkType: hard +"metro-source-map@npm:0.81.0": + version: 0.81.0 + resolution: "metro-source-map@npm:0.81.0" + dependencies: + "@babel/traverse": ^7.25.3 + "@babel/traverse--for-generate-function-map": "npm:@babel/traverse@^7.25.3" + "@babel/types": ^7.25.2 + flow-enums-runtime: ^0.0.6 + invariant: ^2.2.4 + metro-symbolicate: 0.81.0 + nullthrows: ^1.1.1 + ob1: 0.81.0 + source-map: ^0.5.6 + vlq: ^1.0.0 + checksum: e83742c187427b009a5e15eeddd0af0ef29c6e0b88e5f0ac0ba13142e8883f45ce9d66dc8439ca080cea242e955c4f4ba0d64f8344777479ad89d97fa393ad29 + languageName: node + linkType: hard + +"metro-symbolicate@npm:0.80.12": + version: 0.80.12 + resolution: "metro-symbolicate@npm:0.80.12" + dependencies: + flow-enums-runtime: ^0.0.6 + invariant: ^2.2.4 + metro-source-map: 0.80.12 + nullthrows: ^1.1.1 + source-map: ^0.5.6 + through2: ^2.0.1 + vlq: ^1.0.0 + bin: + metro-symbolicate: src/index.js + checksum: b775e4613deec421f6287918d0055c50bb2a38fe3f72581eb70b9441e4497c9c7413c2929c579b24fb76893737b6d5af83a5f6cd8c032e2a83957091f82ec5de + languageName: node + linkType: hard + "metro-symbolicate@npm:0.80.9": version: 0.80.9 resolution: "metro-symbolicate@npm:0.80.9" @@ -43129,6 +43820,37 @@ __metadata: languageName: node linkType: hard +"metro-symbolicate@npm:0.81.0": + version: 0.81.0 + resolution: "metro-symbolicate@npm:0.81.0" + dependencies: + flow-enums-runtime: ^0.0.6 + invariant: ^2.2.4 + metro-source-map: 0.81.0 + nullthrows: ^1.1.1 + source-map: ^0.5.6 + through2: ^2.0.1 + vlq: ^1.0.0 + bin: + metro-symbolicate: src/index.js + checksum: 33990dc3722096beb0fabce5d8d2961b8f400e1f2aa6c19ce9760f9d739b63f25c7bd844e37e0de42e7f95c125431f7e42a7ad0b92b9aee8d214fecdfb4018e7 + languageName: node + linkType: hard + +"metro-transform-plugins@npm:0.80.12": + version: 0.80.12 + resolution: "metro-transform-plugins@npm:0.80.12" + dependencies: + "@babel/core": ^7.20.0 + "@babel/generator": ^7.20.0 + "@babel/template": ^7.0.0 + "@babel/traverse": ^7.20.0 + flow-enums-runtime: ^0.0.6 + nullthrows: ^1.1.1 + checksum: 85c99c367d6c0b9721af744fc980372329c6d37711177660e2d5e2dbe5e92e2cd853604eb8a513ad824eafbed84663472fa304cbbe2036957ee8688b72c2324c + languageName: node + linkType: hard + "metro-transform-plugins@npm:0.80.9": version: 0.80.9 resolution: "metro-transform-plugins@npm:0.80.9" @@ -43142,6 +43864,41 @@ __metadata: languageName: node linkType: hard +"metro-transform-plugins@npm:0.81.0": + version: 0.81.0 + resolution: "metro-transform-plugins@npm:0.81.0" + dependencies: + "@babel/core": ^7.25.2 + "@babel/generator": ^7.25.0 + "@babel/template": ^7.25.0 + "@babel/traverse": ^7.25.3 + flow-enums-runtime: ^0.0.6 + nullthrows: ^1.1.1 + checksum: fea77e227c856cd3a41f55ddcde9852d7408cd3ceb4b434f23e02e5122a95f0a29b1950adae0b806d96bfb26581c1160c4bc62942888698394fcc4e85e0b8ee7 + languageName: node + linkType: hard + +"metro-transform-worker@npm:0.80.12": + version: 0.80.12 + resolution: "metro-transform-worker@npm:0.80.12" + dependencies: + "@babel/core": ^7.20.0 + "@babel/generator": ^7.20.0 + "@babel/parser": ^7.20.0 + "@babel/types": ^7.20.0 + flow-enums-runtime: ^0.0.6 + metro: 0.80.12 + metro-babel-transformer: 0.80.12 + metro-cache: 0.80.12 + metro-cache-key: 0.80.12 + metro-minify-terser: 0.80.12 + metro-source-map: 0.80.12 + metro-transform-plugins: 0.80.12 + nullthrows: ^1.1.1 + checksum: 90684b1f1163bfc84b11bfc01082a38de2a5dd9f7bcabc524bc84f1faff32222954f686a60bc0f464d3e46e86c4c01435111e2ed0e9767a5efbfaf205f55245e + languageName: node + linkType: hard + "metro-transform-worker@npm:0.80.9": version: 0.80.9 resolution: "metro-transform-worker@npm:0.80.9" @@ -43162,6 +43919,79 @@ __metadata: languageName: node linkType: hard +"metro-transform-worker@npm:0.81.0": + version: 0.81.0 + resolution: "metro-transform-worker@npm:0.81.0" + dependencies: + "@babel/core": ^7.25.2 + "@babel/generator": ^7.25.0 + "@babel/parser": ^7.25.3 + "@babel/types": ^7.25.2 + flow-enums-runtime: ^0.0.6 + metro: 0.81.0 + metro-babel-transformer: 0.81.0 + metro-cache: 0.81.0 + metro-cache-key: 0.81.0 + metro-minify-terser: 0.81.0 + metro-source-map: 0.81.0 + metro-transform-plugins: 0.81.0 + nullthrows: ^1.1.1 + checksum: 0fa08b09f4e503183af789e39629dd0fdf4209f3453c0642cdef5e683e69644ec925bcccb2bdb3439059c11fc1418b3bcdd7dc38c768183c3deb8e2bc050e604 + languageName: node + linkType: hard + +"metro@npm:0.80.12": + version: 0.80.12 + resolution: "metro@npm:0.80.12" + dependencies: + "@babel/code-frame": ^7.0.0 + "@babel/core": ^7.20.0 + "@babel/generator": ^7.20.0 + "@babel/parser": ^7.20.0 + "@babel/template": ^7.0.0 + "@babel/traverse": ^7.20.0 + "@babel/types": ^7.20.0 + accepts: ^1.3.7 + chalk: ^4.0.0 + ci-info: ^2.0.0 + connect: ^3.6.5 + debug: ^2.2.0 + denodeify: ^1.2.1 + error-stack-parser: ^2.0.6 + flow-enums-runtime: ^0.0.6 + graceful-fs: ^4.2.4 + hermes-parser: 0.23.1 + image-size: ^1.0.2 + invariant: ^2.2.4 + jest-worker: ^29.6.3 + jsc-safe-url: ^0.2.2 + lodash.throttle: ^4.1.1 + metro-babel-transformer: 0.80.12 + metro-cache: 0.80.12 + metro-cache-key: 0.80.12 + metro-config: 0.80.12 + metro-core: 0.80.12 + metro-file-map: 0.80.12 + metro-resolver: 0.80.12 + metro-runtime: 0.80.12 + metro-source-map: 0.80.12 + metro-symbolicate: 0.80.12 + metro-transform-plugins: 0.80.12 + metro-transform-worker: 0.80.12 + mime-types: ^2.1.27 + nullthrows: ^1.1.1 + serialize-error: ^2.1.0 + source-map: ^0.5.6 + strip-ansi: ^6.0.0 + throat: ^5.0.0 + ws: ^7.5.10 + yargs: ^17.6.2 + bin: + metro: src/cli.js + checksum: 8016f7448e6e0947bd38633c01c3daad47b5a29d4a7294ebe922fa3c505430f78861d85965ecfc6f41d9b209e2663cac0f23c99a80a3f941a19de564203fcdb8 + languageName: node + linkType: hard + "metro@npm:0.80.9, metro@npm:^0.80.3": version: 0.80.9 resolution: "metro@npm:0.80.9" @@ -43215,6 +44045,58 @@ __metadata: languageName: node linkType: hard +"metro@npm:0.81.0": + version: 0.81.0 + resolution: "metro@npm:0.81.0" + dependencies: + "@babel/code-frame": ^7.24.7 + "@babel/core": ^7.25.2 + "@babel/generator": ^7.25.0 + "@babel/parser": ^7.25.3 + "@babel/template": ^7.25.0 + "@babel/traverse": ^7.25.3 + "@babel/types": ^7.25.2 + accepts: ^1.3.7 + chalk: ^4.0.0 + ci-info: ^2.0.0 + connect: ^3.6.5 + debug: ^2.2.0 + denodeify: ^1.2.1 + error-stack-parser: ^2.0.6 + flow-enums-runtime: ^0.0.6 + graceful-fs: ^4.2.4 + hermes-parser: 0.24.0 + image-size: ^1.0.2 + invariant: ^2.2.4 + jest-worker: ^29.6.3 + jsc-safe-url: ^0.2.2 + lodash.throttle: ^4.1.1 + metro-babel-transformer: 0.81.0 + metro-cache: 0.81.0 + metro-cache-key: 0.81.0 + metro-config: 0.81.0 + metro-core: 0.81.0 + metro-file-map: 0.81.0 + metro-resolver: 0.81.0 + metro-runtime: 0.81.0 + metro-source-map: 0.81.0 + metro-symbolicate: 0.81.0 + metro-transform-plugins: 0.81.0 + metro-transform-worker: 0.81.0 + mime-types: ^2.1.27 + nullthrows: ^1.1.1 + serialize-error: ^2.1.0 + source-map: ^0.5.6 + strip-ansi: ^6.0.0 + throat: ^5.0.0 + ws: ^7.5.10 + yargs: ^17.6.2 + bin: + metro: src/cli.js + checksum: 326f13e281ba696361c64b1c6bb77ff5b284771a103a78d446f7944ef8baf89e724bd2a76859c5c4e7adc9e94de2c6619755899efdde9bf1e24d3399e7c7cc00 + languageName: node + linkType: hard + "micromark-core-commonmark@npm:^1.0.0, micromark-core-commonmark@npm:^1.0.1": version: 1.1.0 resolution: "micromark-core-commonmark@npm:1.1.0" @@ -43804,15 +44686,6 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^8.0.2": - version: 8.0.4 - resolution: "minimatch@npm:8.0.4" - dependencies: - brace-expansion: ^2.0.1 - checksum: 2e46cffb86bacbc524ad45a6426f338920c529dd13f3a732cc2cf7618988ee1aae88df4ca28983285aca9e0f45222019ac2d14ebd17c1edadd2ee12221ab801a - languageName: node - linkType: hard - "minimatch@npm:^9.0.4": version: 9.0.4 resolution: "minimatch@npm:9.0.4" @@ -43938,13 +44811,6 @@ __metadata: languageName: node linkType: hard -"minipass@npm:^4.2.4": - version: 4.2.8 - resolution: "minipass@npm:4.2.8" - checksum: 7f4914d5295a9a30807cae5227a37a926e6d910c03f315930fde52332cf0575dfbc20295318f91f0baf0e6bb11a6f668e30cde8027dea7a11b9d159867a3c830 - languageName: node - linkType: hard - "minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0": version: 7.0.3 resolution: "minipass@npm:7.0.3" @@ -45422,6 +46288,15 @@ __metadata: languageName: node linkType: hard +"ob1@npm:0.80.12": + version: 0.80.12 + resolution: "ob1@npm:0.80.12" + dependencies: + flow-enums-runtime: ^0.0.6 + checksum: c78af51d6ecf47ba5198bc7eb27d0456a287589533f1445e6d595e2d067f6f8038da02a98e5faa4a6c3d0c04f77c570bc9b29c652fec55518884c40c73212f17 + languageName: node + linkType: hard + "ob1@npm:0.80.9": version: 0.80.9 resolution: "ob1@npm:0.80.9" @@ -45429,6 +46304,15 @@ __metadata: languageName: node linkType: hard +"ob1@npm:0.81.0": + version: 0.81.0 + resolution: "ob1@npm:0.81.0" + dependencies: + flow-enums-runtime: ^0.0.6 + checksum: f3215ccf72604b4db5f9cfc6c83454a136a035ffd26faffec2c100d5810b87599cc95e167888320f3865959a5f9762c03de20a9e40cf66fc13706886820a9523 + languageName: node + linkType: hard + "object-assign@npm:^4, object-assign@npm:^4.0.1, object-assign@npm:^4.1.0, object-assign@npm:^4.1.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" @@ -46437,6 +47321,13 @@ __metadata: languageName: node linkType: hard +"path-dirname@npm:^1.0.2": + version: 1.0.2 + resolution: "path-dirname@npm:1.0.2" + checksum: 0d2f6604ae05a252a0025318685f290e2764ecf9c5436f203cdacfc8c0b17c24cdedaa449d766beb94ab88cc7fc70a09ec21e7933f31abc2b719180883e5e33f + languageName: node + linkType: hard + "path-exists@npm:^2.0.0": version: 2.1.0 resolution: "path-exists@npm:2.1.0" @@ -46521,7 +47412,7 @@ __metadata: languageName: node linkType: hard -"path-scurry@npm:^1.11.1, path-scurry@npm:^1.6.1": +"path-scurry@npm:^1.11.1": version: 1.11.1 resolution: "path-scurry@npm:1.11.1" dependencies: @@ -46937,15 +47828,6 @@ __metadata: languageName: node linkType: hard -"pkg-up@npm:^3.1.0": - version: 3.1.0 - resolution: "pkg-up@npm:3.1.0" - dependencies: - find-up: ^3.0.0 - checksum: 5bac346b7c7c903613c057ae3ab722f320716199d753f4a7d053d38f2b5955460f3e6ab73b4762c62fd3e947f58e04f1343e92089e7bb6091c90877406fcd8c8 - languageName: node - linkType: hard - "playwright-core@npm:1.48.2": version: 1.48.2 resolution: "playwright-core@npm:1.48.2" @@ -47678,7 +48560,7 @@ __metadata: languageName: node linkType: hard -"pretty-format@npm:^29.7.0": +"pretty-format@npm:^29.0.3, pretty-format@npm:^29.7.0": version: 29.7.0 resolution: "pretty-format@npm:29.7.0" dependencies: @@ -49147,6 +50029,21 @@ __metadata: languageName: node linkType: hard +"react-native-svg-transformer@npm:1.3.0": + version: 1.3.0 + resolution: "react-native-svg-transformer@npm:1.3.0" + dependencies: + "@svgr/core": ^8.1.0 + "@svgr/plugin-jsx": ^8.1.0 + "@svgr/plugin-svgo": ^8.1.0 + path-dirname: ^1.0.2 + peerDependencies: + react-native: ">=0.59.0" + react-native-svg: ">=12.0.0" + checksum: 09dc490aad05b8f44289fc6c1b75a2fce92b95b27cacb07f320f29c0d0edcc6979fbd6a46f0b13e6b0681c2ae4652d08af35c018bbbd29324db3579a869a4502 + languageName: node + linkType: hard + "react-native-svg@npm:15.2.0": version: 15.2.0 resolution: "react-native-svg@npm:15.2.0" @@ -49180,6 +50077,25 @@ __metadata: languageName: node linkType: hard +"react-native-web@npm:^0.19.11": + version: 0.19.13 + resolution: "react-native-web@npm:0.19.13" + dependencies: + "@babel/runtime": ^7.18.6 + "@react-native/normalize-colors": ^0.74.1 + fbjs: ^3.0.4 + inline-style-prefixer: ^6.0.1 + memoize-one: ^6.0.0 + nullthrows: ^1.1.1 + postcss-value-parser: ^4.2.0 + styleq: ^0.1.3 + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + checksum: 15077f88204cb980203b8e3784c092c8c25c972bf281db43fd4ccc9696b603380a49f5289fb9a742daddd8e7599baab5798a1f1c857bdd634add827bc39fd8d8 + languageName: node + linkType: hard + "react-native-webview@npm:13.8.6": version: 13.8.6 resolution: "react-native-webview@npm:13.8.6" @@ -49623,15 +50539,6 @@ __metadata: languageName: node linkType: hard -"react@npm:18.2.0": - version: 18.2.0 - resolution: "react@npm:18.2.0" - dependencies: - loose-envify: ^1.1.0 - checksum: 88e38092da8839b830cda6feef2e8505dec8ace60579e46aa5490fc3dc9bba0bd50336507dc166f43e3afc1c42939c09fe33b25fae889d6f402721dcd78fca1b - languageName: node - linkType: hard - "react@npm:18.3.1": version: 18.3.1 resolution: "react@npm:18.3.1" @@ -50580,13 +51487,6 @@ __metadata: languageName: node linkType: hard -"reselect@npm:^4.1.7": - version: 4.1.8 - resolution: "reselect@npm:4.1.8" - checksum: a4ac87cedab198769a29be92bc221c32da76cfdad6911eda67b4d3e7136dca86208c3b210e31632eae31ebd2cded18596f0dd230d3ccc9e978df22f233b5583e - languageName: node - linkType: hard - "resize-observer-polyfill@npm:^1.5.1": version: 1.5.1 resolution: "resize-observer-polyfill@npm:1.5.1" @@ -50698,7 +51598,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.22.2, resolve@npm:^1.22.4, resolve@npm:^1.22.8": +"resolve@npm:^1.22.2, resolve@npm:^1.22.4": version: 1.22.8 resolution: "resolve@npm:1.22.8" dependencies: @@ -50785,7 +51685,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@^1.22.2#~builtin, resolve@patch:resolve@^1.22.4#~builtin, resolve@patch:resolve@^1.22.8#~builtin": +"resolve@patch:resolve@^1.22.2#~builtin, resolve@patch:resolve@^1.22.4#~builtin": version: 1.22.8 resolution: "resolve@patch:resolve@npm%3A1.22.8#~builtin::version=1.22.8&hash=07638b" dependencies: @@ -53642,6 +54542,13 @@ __metadata: languageName: node linkType: hard +"styleq@npm:^0.1.3": + version: 0.1.3 + resolution: "styleq@npm:0.1.3" + checksum: 14a8d23abd914166a9b4bd04ed753bd91363f0e029ee4a94ec2c7dc37d3213fe01fceee22dc655288da3ae89f5dc01cec42d5e2b58478b0dea33bf5bdf509be1 + languageName: node + linkType: hard + "stylis@npm:4.2.0": version: 4.2.0 resolution: "stylis@npm:4.2.0" @@ -55497,6 +56404,15 @@ __metadata: languageName: node linkType: hard +"ua-parser-js@npm:^1.0.35": + version: 1.0.39 + resolution: "ua-parser-js@npm:1.0.39" + bin: + ua-parser-js: script/cli.js + checksum: 19455df8c2348ef53f2e150e7406d3a025a619c2fd69722a1e63363d5ba8d91731ef7585f2dce7d8f14c8782734b4d704c05f246dca5f7565b5ae7d318084f2a + languageName: node + linkType: hard + "uc-first-array@npm:^1.1.10": version: 1.1.10 resolution: "uc-first-array@npm:1.1.10"