diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 00932606d1b3..6caaa1782adf 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -30,7 +30,6 @@ /apps/icelandic-names-registry*/ @island-is/juni /apps/web/screens/PetitionView/ @island-is/juni /libs/cms/ @island-is/juni @island-is/stefna -/libs/clients/cms/ @island-is/juni @island-is/aranja /libs/residence-history @island-is/juni /libs/api/domains/endorsement-system @island-is/juni /libs/api/domains/icelandic-names-registry/ @island-is/juni @@ -131,9 +130,6 @@ codemagic.yaml /apps/portals/my-pages*/ @island-is/hugsmidjan /apps/services/regulations-admin-backend/ @island-is/hugsmidjan /apps/services/user-profile/ @island-is/hugsmidjan @island-is/juni @island-is/aranja -/apps/web/components/Grant/ @island-is/hugsmidjan -/apps/web/components/PlazaCard/ @island-is/hugsmidjan -/apps/web/screens/Grants/ @island-is/hugsmidjan /apps/web/screens/Regulations/ @island-is/hugsmidjan /apps/web/components/Regulations/ @island-is/hugsmidjan /apps/web/screens/OfficialJournalOfIceland/ @island-is/hugsmidjan @@ -198,8 +194,7 @@ codemagic.yaml /libs/portals/admin/regulations-admin/ @island-is/hugsmidjan /libs/portals/admin/document-provider/ @island-is/hugsmidjan @island-is/core /libs/clients/icelandic-health-insurance/rights-portal/ @island-is/hugsmidjan -/libs/clients/health-directorate @island-is/hugsmidjan -/libs/clients/health-directorate/src/lib/clients/occupational-license @island-is/hugsmidjan @island-is/origo +/libs/clients/health-directorate @island-is/hugsmidjan @island-is/origo /libs/clients/mms/grade @island-is/hugsmidjan /libs/portals/admin/air-discount-scheme @island-is/hugsmidjan /libs/application/templates/official-journal-of-iceland/ @island-is/hugsmidjan @@ -393,7 +388,7 @@ codemagic.yaml # QA /apps/system-e2e/ @island-is/qa -/libs/testing/e2e @island-is/qa +/libs/testing/e2e @island-is/qa # Islandis /apps/system-e2e/src/tests/islandis/admin-portal/ @island-is/aranja diff --git a/README.md b/README.md index 209cda2c4c78..f5c26c5c8cde 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ Run on whenever you check out a branch: ```bash yarn install -(cd infra/ && yarn install) +yarn infra install yarn codegen ``` diff --git a/apps/application-system/api/infra/application-system-api.ts b/apps/application-system/api/infra/application-system-api.ts index e4c51330e256..ce7360862365 100644 --- a/apps/application-system/api/infra/application-system-api.ts +++ b/apps/application-system/api/infra/application-system-api.ts @@ -346,11 +346,11 @@ export const serviceSetup = (services: { }) .db() .migrations() - .liveness({ path: '/liveness', initialDelaySeconds: 20 }) - .readiness({ path: '/liveness', initialDelaySeconds: 20 }) + .liveness('/liveness') + .readiness('/liveness') .resources({ - limits: { cpu: '600m', memory: '1024Mi' }, - requests: { cpu: '200m', memory: '512Mi' }, + limits: { cpu: '400m', memory: '1024Mi' }, + requests: { cpu: '75m', memory: '512Mi' }, }) .replicaCount({ default: 2, diff --git a/apps/judicial-system/README.md b/apps/judicial-system/README.md index 2770e62b06f8..f2ee7f505d90 100644 --- a/apps/judicial-system/README.md +++ b/apps/judicial-system/README.md @@ -226,6 +226,18 @@ You can serve this service locally by running: yarn start judicial-system-xrd-api ``` +## Robot API + +This service is for access through xRoad. + +### Running locally + +You can serve this service locally by running: + +```bash +yarn start judicial-system-robot-api +``` + ## Digital Mailbox API This service is for access through xRoad. diff --git a/apps/judicial-system/backend/src/app/formatters/formatters.spec.ts b/apps/judicial-system/backend/src/app/formatters/formatters.spec.ts index 15b4b0fe2d94..3379703684ae 100644 --- a/apps/judicial-system/backend/src/app/formatters/formatters.spec.ts +++ b/apps/judicial-system/backend/src/app/formatters/formatters.spec.ts @@ -15,7 +15,6 @@ import { } from '@island.is/judicial-system/types' import { - filterWhitelistEmails, formatCourtHeadsUpSmsNotification, formatCourtReadyForCourtSmsNotification, formatCourtResubmittedToCourtSmsNotification, @@ -1983,38 +1982,3 @@ describe('formatDefenderResubmittedToCourtEmailNotification', () => { expect(result.subject).toEqual('Krafa í máli R-2022/999') }) }) - -describe('filterWhitelistEmails', () => { - const emails = [ - 'test@rvg.is', - 'test2@rvg.is', - 'test3@rvg.is', - 'test4@example.com', - ] - - it('should return only whitelisted emails', () => { - const whitelist = `${emails[0]}, ${emails[2]}` - const domainWhitelist = 'example.com' - - const result = filterWhitelistEmails(emails, domainWhitelist, whitelist) - - expect(result).toEqual([emails[0], emails[2], emails[3]]) - }) - - it('should return empty array if no emails are whitelisted', () => { - const whitelist = '' - const domainWhitelist = '' - - const result = filterWhitelistEmails(emails, domainWhitelist, whitelist) - - expect(result).toEqual([]) - }) - it('should return domain whitelisted emails', () => { - const whitelist = '' - const domainWhitelist = 'rvg.is' - - const result = filterWhitelistEmails(emails, domainWhitelist, whitelist) - - expect(result).toEqual([emails[0], emails[1], emails[2]]) - }) -}) diff --git a/apps/judicial-system/backend/src/app/formatters/formatters.ts b/apps/judicial-system/backend/src/app/formatters/formatters.ts index 552a328fc515..d48904e27885 100644 --- a/apps/judicial-system/backend/src/app/formatters/formatters.ts +++ b/apps/judicial-system/backend/src/app/formatters/formatters.ts @@ -668,6 +668,49 @@ export const formatCustodyRestrictions = ( }) } +export const formatAdvocateAssignedEmailNotification = ( + formatMessage: FormatMessage, + theCase: Case, + advocateType: AdvocateType, + overviewUrl?: string, +): SubjectAndBody => { + const subject = + advocateType === AdvocateType.DEFENDER + ? formatMessage( + notifications.advocateAssignedEmail.subjectAccessToCaseFiles, + { + court: capitalize(theCase.court?.name ?? ''), + }, + ) + : formatMessage(notifications.advocateAssignedEmail.subjectAccess, { + courtCaseNumber: theCase.courtCaseNumber, + }) + + const body = + advocateType === AdvocateType.DEFENDER + ? formatMessage( + notifications.advocateAssignedEmail.bodyAccessToCaseFiles, + { + defenderHasAccessToRVG: Boolean(overviewUrl), + courtCaseNumber: capitalize(theCase.courtCaseNumber ?? ''), + court: theCase.court?.name ?? '', + courtName: theCase.court?.name.replace('dómur', 'dómi') ?? '', + linkStart: ``, + linkEnd: '', + }, + ) + : formatMessage(notifications.advocateAssignedEmail.bodyAccess, { + defenderHasAccessToRVG: Boolean(overviewUrl), + court: theCase.court?.name, + advocateType, + courtCaseNumber: capitalize(theCase.courtCaseNumber ?? ''), + linkStart: ``, + linkEnd: '', + }) + + return { body, subject } +} + export const formatCourtOfAppealJudgeAssignedEmailNotification = ( formatMessage: FormatMessage, caseNumber: string, @@ -759,31 +802,3 @@ export const formatDefenderRoute = ( export const formatConfirmedIndictmentKey = (key?: string) => key?.replace(/\/([^/]*)$/, '/confirmed/$1') ?? '' - -export const filterWhitelistEmails = ( - emails: string[], - domainWhitelist: string, - emailWhitelist: string, -) => { - if (!emails || emails.length === 0) return [] - - const allowedDomains = new Set( - domainWhitelist - .split(',') - .map((d) => d.trim()) - .filter(Boolean), - ) - const allowedEmails = new Set( - emailWhitelist - .split(',') - .map((e) => e.trim()) - .filter(Boolean), - ) - - return emails.filter((email) => { - const domain = email.split('@')[1] - return ( - domain && (allowedDomains.has(domain) || allowedEmails.has(email.trim())) - ) - }) -} diff --git a/apps/judicial-system/backend/src/app/formatters/index.ts b/apps/judicial-system/backend/src/app/formatters/index.ts index 01d46156bf88..48f8af67b53e 100644 --- a/apps/judicial-system/backend/src/app/formatters/index.ts +++ b/apps/judicial-system/backend/src/app/formatters/index.ts @@ -21,13 +21,13 @@ export { formatProsecutorReceivedByCourtSmsNotification, formatDefenderCourtDateLinkEmailNotification, formatDefenderResubmittedToCourtEmailNotification, + formatAdvocateAssignedEmailNotification, formatCourtIndictmentReadyForCourtEmailNotification, formatDefenderRoute, formatDefenderReadyForCourtEmailNotification, formatCourtOfAppealJudgeAssignedEmailNotification, formatPostponedCourtDateEmailNotification, stripHtmlTags, - filterWhitelistEmails, } from './formatters' export { Confirmation } from './pdfHelpers' export { getRequestPdfAsBuffer, getRequestPdfAsString } from './requestPdf' diff --git a/apps/judicial-system/backend/src/app/messages/notifications.ts b/apps/judicial-system/backend/src/app/messages/notifications.ts index a7bf8f898acf..fe12aea5011d 100644 --- a/apps/judicial-system/backend/src/app/messages/notifications.ts +++ b/apps/judicial-system/backend/src/app/messages/notifications.ts @@ -45,16 +45,6 @@ export const notifications = { 'Hægt er að nálgast yfirlitssíðu málsins á rettarvorslugatt.island.is.', description: 'Notaður sem texti í email til þess að tilgreina slóð á RVG', }), - emailWhitelist: defineMessage({ - id: 'judicial.system.backend:notifications.email_whitelist', - defaultMessage: '', - description: 'Notað til að tilgreina hvort póstfang sé í hvítlista', - }), - emailWhitelistDomains: defineMessage({ - id: 'judicial.system.backend:notifications.email_whitelist_domains', - defaultMessage: 'omnitrix.is,kolibri.is', - description: 'Notað til að tilgreina hvort póstfang sé í hvítlista', - }), readyForCourt: defineMessages({ subject: { id: 'judicial.system.backend:notifications.ready_for_court.subjectV2', @@ -617,6 +607,32 @@ export const notifications = { 'Notaður sem texti í tölvupósti til verjanda vegna breytingar á lengd gæslu/einangrunar/vistunar þar sem úrskurðað var í einangrun.', }, }), + advocateAssignedEmail: defineMessages({ + subjectAccessToCaseFiles: { + id: 'judicial.system.backend:notifications.defender_assigned_email.subject_access_to_case_files', + defaultMessage: '{court} - aðgangur að málsgögnum', + description: + 'Fyrirsögn í pósti til verjanda þegar hann er skráður á mál.', + }, + subjectAccess: { + id: 'judicial.system.backend:notifications.defender_assigned_email.subject_access', + defaultMessage: 'Skráning í máli {courtCaseNumber}', + description: + 'Fyrirsögn í pósti til verjanda þegar hann er skráður á mál.', + }, + bodyAccessToCaseFiles: { + id: 'judicial.system.backend:notifications.defender_assigned_email.body_access_to_case_files', + defaultMessage: + '{court} hefur skráð þig verjanda í máli {courtCaseNumber}.

{defenderHasAccessToRVG, select, true {Gögn málsins eru aðgengileg á {linkStart}yfirlitssíðu málsins í Réttarvörslugátt{linkEnd}} other {Þú getur nálgast gögn málsins hjá {courtName} ef þau hafa ekki þegar verið afhent}}.', + description: 'Texti í pósti til verjanda þegar hann er skráður á mál.', + }, + bodyAccess: { + id: 'judicial.system.backend:notifications.defender_assigned_email.body_access', + defaultMessage: + '{court} hefur skráð þig {advocateType, select, LAWYER {lögmann einkaréttarkröfuhafa} LEGAL_RIGHTS_PROTECTOR {réttargæslumann einkaréttarkröfuhafa} other {verjanda}} í máli {courtCaseNumber}.

{defenderHasAccessToRVG, select, true {Sjá nánar á {linkStart}yfirlitssíðu málsins í Réttarvörslugátt{linkEnd}} other {Þú getur nálgast málið hjá {courtName}.}}.', + description: 'Texti í pósti til verjanda þegar hann er skráður á mál.', + }, + }), defendantsNotUpdatedAtCourt: defineMessages({ subject: { id: 'judicial.system.backend:notifications.defendants_not_updated_at_court.subject', 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..cbab6e505062 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 @@ -39,10 +39,14 @@ import { } from '@island.is/judicial-system/formatters' import type { User } from '@island.is/judicial-system/types' import { + CaseAppealRulingDecision, + CaseDecision, CaseState, + CaseTransition, CaseType, indictmentCases, investigationCases, + isRestrictionCase, restrictionCases, UserRole, } from '@island.is/judicial-system/types' 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 df2e7ee8dc61..6f77a6752c3e 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 @@ -784,9 +784,10 @@ export class CaseService { elementId: caseFile.id, })) ?? [] - const messages: Message[] = deliverCaseFilesRecordToCourtMessages.concat( - deliverCaseFileToCourtMessages, - ) + const messages = this.getDeliverProsecutorToCourtMessages(theCase, user) + .concat(this.getDeliverDefendantToCourtMessages(theCase, user)) + .concat(deliverCaseFilesRecordToCourtMessages) + .concat(deliverCaseFileToCourtMessages) if (isTrafficViolationCase(theCase)) { messages.push({ @@ -2034,23 +2035,11 @@ export class CaseService { } async createCourtCase(theCase: Case, user: TUser): Promise { - let receivalDate: Date - - if (isIndictmentCase(theCase.type)) { - receivalDate = - theCase.eventLogs?.find( - (eventLog) => eventLog.eventType === EventType.INDICTMENT_CONFIRMED, - )?.created ?? nowFactory() - } else { - receivalDate = nowFactory() - } - const courtCaseNumber = await this.courtService.createCourtCase( user, theCase.id, theCase.courtId, theCase.type, - receivalDate, theCase.policeCaseNumbers, Boolean(theCase.parentCaseId), theCase.indictmentSubtypes, diff --git a/apps/judicial-system/backend/src/app/modules/case/dto/createCase.dto.ts b/apps/judicial-system/backend/src/app/modules/case/dto/createCase.dto.ts index a54904d19aab..f3f920723a31 100644 --- a/apps/judicial-system/backend/src/app/modules/case/dto/createCase.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/case/dto/createCase.dto.ts @@ -6,7 +6,6 @@ import { IsObject, IsOptional, IsString, - MaxLength, } from 'class-validator' import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger' @@ -32,7 +31,6 @@ export class CreateCaseDto { readonly indictmentSubtypes?: IndictmentSubtypeMap @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly description?: string @@ -40,31 +38,26 @@ export class CreateCaseDto { @IsNotEmpty() @IsArray() @ArrayMinSize(1) - @MaxLength(255, { each: true }) @IsString({ each: true }) @ApiProperty({ type: String, isArray: true }) readonly policeCaseNumbers!: string[] @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly defenderName?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly defenderNationalId?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly defenderEmail?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly defenderPhoneNumber?: string @@ -75,7 +68,6 @@ export class CreateCaseDto { readonly requestSharedWithDefender?: RequestSharedWithDefender @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly leadInvestigator?: string diff --git a/apps/judicial-system/backend/src/app/modules/case/dto/updateCase.dto.ts b/apps/judicial-system/backend/src/app/modules/case/dto/updateCase.dto.ts index 6eec48f22a86..4930b0b976bb 100644 --- a/apps/judicial-system/backend/src/app/modules/case/dto/updateCase.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/case/dto/updateCase.dto.ts @@ -9,7 +9,6 @@ import { IsOptional, IsString, IsUUID, - MaxLength, ValidateNested, } from 'class-validator' @@ -44,7 +43,6 @@ class UpdateDateLog { readonly date?: Date @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly location?: string @@ -62,7 +60,6 @@ export class UpdateCaseDto { readonly indictmentSubtypes?: IndictmentSubtypeMap @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly description?: string @@ -70,31 +67,26 @@ export class UpdateCaseDto { @IsOptional() @IsArray() @ArrayMinSize(1) - @MaxLength(255, { each: true }) @IsString({ each: true }) @ApiPropertyOptional({ type: String, isArray: true }) readonly policeCaseNumbers?: string[] @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly defenderName?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly defenderNationalId?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly defenderEmail?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly defenderPhoneNumber?: string @@ -115,7 +107,6 @@ export class UpdateCaseDto { readonly courtId?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly leadInvestigator?: string @@ -133,7 +124,6 @@ export class UpdateCaseDto { readonly requestedCourtDate?: Date @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly translator?: string @@ -217,7 +207,6 @@ export class UpdateCaseDto { readonly sharedWithProsecutorsOfficeId?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly courtCaseNumber?: string @@ -240,7 +229,6 @@ export class UpdateCaseDto { readonly courtDate?: UpdateDateLog @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly courtLocation?: string @@ -419,7 +407,6 @@ export class UpdateCaseDto { readonly defendantStatementDate?: Date @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly appealCaseNumber?: string 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..2b03c85cdb96 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 @@ -178,6 +178,31 @@ export class InternalCaseController { ) } + @UseGuards(CaseExistsGuard, new CaseTypeGuard(indictmentCases)) + @Post( + `case/:caseId/${ + messageEndpoint[MessageType.DELIVERY_TO_COURT_INDICTMENT_DEFENDER] + }`, + ) + @ApiOkResponse({ + type: DeliverResponse, + description: 'Delivers indictment case defender info to court', + }) + deliverIndictmentDefenderInfoToCourt( + @Param('caseId') caseId: string, + @CurrentCase() theCase: Case, + @Body() deliverDto: DeliverDto, + ): Promise { + this.logger.debug( + `Delivering the indictment defender info for case ${caseId} to court`, + ) + + return this.internalCaseService.deliverIndictmentDefenderInfoToCourt( + theCase, + deliverDto.user, + ) + } + @UseGuards(CaseExistsGuard, new CaseTypeGuard(indictmentCases)) @Post( `case/:caseId/${ 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..211931752bfc 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 @@ -418,7 +418,7 @@ export class InternalCaseService { collectEncryptionProperties(defendantEncryptionProperties, defendant) defendantsArchive.push(defendantArchive) - await this.defendantService.updateDatabaseDefendant( + await this.defendantService.updateForArcive( theCase.id, defendant.id, clearedDefendantProperties, @@ -580,10 +580,6 @@ export class InternalCaseService { : [] const mappedSubtypes = subtypeList.flatMap((key) => courtSubtypes[key]) - const indictmentIssuedByProsecutorAndReceivedByCourt = - theCase.eventLogs?.find( - (eventLog) => eventLog.eventType === EventType.INDICTMENT_CONFIRMED, - )?.created return this.courtService .updateIndictmentCaseWithIndictmentInfo( @@ -591,8 +587,12 @@ export class InternalCaseService { theCase.id, theCase.court?.name, theCase.courtCaseNumber, - indictmentIssuedByProsecutorAndReceivedByCourt, - indictmentIssuedByProsecutorAndReceivedByCourt, + theCase.eventLogs?.find( + (eventLog) => eventLog.eventType === EventType.CASE_RECEIVED_BY_COURT, + )?.created, + theCase.eventLogs?.find( + (eventLog) => eventLog.eventType === EventType.INDICTMENT_CONFIRMED, + )?.created, theCase.policeCaseNumbers[0], mappedSubtypes, theCase.defendants?.map((defendant) => ({ @@ -617,6 +617,29 @@ export class InternalCaseService { }) } + async deliverIndictmentDefenderInfoToCourt( + theCase: Case, + user: TUser, + ): Promise { + return this.courtService + .updateIndictmentCaseWithDefenderInfo( + user, + theCase.id, + theCase.court?.name, + theCase.courtCaseNumber, + theCase.defendants, + ) + .then(() => ({ delivered: true })) + .catch((reason) => { + this.logger.error( + `Failed to update indictment case ${theCase.id} with defender info`, + { reason }, + ) + + return { delivered: false } + }) + } + async deliverIndictmentAssignedRolesToCourt( theCase: Case, user: TUser, diff --git a/apps/judicial-system/backend/src/app/modules/case/test/caseController/createCourtCase.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/caseController/createCourtCase.spec.ts index b1daaac68f15..318a3420af0d 100644 --- a/apps/judicial-system/backend/src/app/modules/case/test/caseController/createCourtCase.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/test/caseController/createCourtCase.spec.ts @@ -7,23 +7,20 @@ import { CaseFileState, CaseState, CaseType, - EventType, IndictmentSubtype, investigationCases, + isIndictmentCase, restrictionCases, User as TUser, } from '@island.is/judicial-system/types' import { createTestingCaseModule } from '../createTestingCaseModule' -import { nowFactory } from '../../../../factories' -import { randomDate, randomEnum } from '../../../../test' +import { randomEnum } from '../../../../test' import { CourtService } from '../../../court' import { include } from '../../case.service' import { Case } from '../../models/case.model' -jest.mock('../../../../factories') - interface Then { result: Case error: Error @@ -87,71 +84,16 @@ describe('CaseController - Create court case', () => { } }) - describe('request court case created', () => { - const date = randomDate() + describe('court case created', () => { const caseId = uuid() - const type = CaseType.CUSTODY - const policeCaseNumber = uuid() - const policeCaseNumbers = [policeCaseNumber] - const courtId = uuid() - const theCase = { - id: caseId, - type, - policeCaseNumbers, - courtId, - } as Case - const returnedCase = { - id: caseId, - type, - policeCaseNumbers, - courtId, - courtCaseNumber, - } as Case - let then: Then - - beforeEach(async () => { - const mockFindOne = mockCaseModel.findOne as jest.Mock - mockFindOne.mockResolvedValueOnce(returnedCase) - - const mockToday = nowFactory as jest.Mock - mockToday.mockReturnValueOnce(date) - - then = await givenWhenThen(caseId, user, theCase) - }) - - it('should create a court case', () => { - expect(mockCourtService.createCourtCase).toHaveBeenCalledWith( - user, - caseId, - courtId, - type, - date, - policeCaseNumbers, - false, - undefined, - ) - expect(mockCaseModel.update).toHaveBeenCalledWith( - { courtCaseNumber }, - { where: { id: caseId }, transaction }, - ) - expect(mockCaseModel.findOne).toHaveBeenCalledWith({ - include, - where: { - id: caseId, - isArchived: false, - }, - }) - expect(then.result).toBe(returnedCase) - }) - }) - - describe('indictment court case created', () => { - const caseId = uuid() - const type = CaseType.INDICTMENT + const type = randomEnum(CaseType) const policeCaseNumber = uuid() - const indictmentSubtype = randomEnum(IndictmentSubtype) - const indictmentSubtypes = { [policeCaseNumber]: [indictmentSubtype] } - const indictmentConfirmedDate = randomDate() + const indictmentSubtype = isIndictmentCase(type) + ? randomEnum(IndictmentSubtype) + : undefined + const indictmentSubtypes = isIndictmentCase(type) + ? { [policeCaseNumber]: [indictmentSubtype] } + : undefined const policeCaseNumbers = [policeCaseNumber] const courtId = uuid() const theCase = { @@ -160,16 +102,9 @@ describe('CaseController - Create court case', () => { policeCaseNumbers, indictmentSubtypes, courtId, - eventLogs: [ - { - eventType: EventType.INDICTMENT_CONFIRMED, - created: indictmentConfirmedDate, - }, - ], } as Case const returnedCase = { id: caseId, - type, policeCaseNumbers, indictmentSubtypes, courtId, @@ -190,7 +125,6 @@ describe('CaseController - Create court case', () => { caseId, courtId, type, - indictmentConfirmedDate, policeCaseNumbers, false, indictmentSubtypes, @@ -349,6 +283,11 @@ describe('CaseController - Create court case', () => { it('should post to queue', () => { expect(mockMessageService.sendMessagesToQueue).toHaveBeenCalledWith([ + { + type: MessageType.DELIVERY_TO_COURT_PROSECUTOR, + user, + caseId: theCase.id, + }, { type: MessageType.DELIVERY_TO_COURT_CASE_FILES_RECORD, user, diff --git a/apps/judicial-system/backend/src/app/modules/case/test/caseController/update.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/caseController/update.spec.ts index b3b0bb816313..088190ba98dd 100644 --- a/apps/judicial-system/backend/src/app/modules/case/test/caseController/update.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/test/caseController/update.spec.ts @@ -458,6 +458,23 @@ describe('CaseController - Update', () => { it('should post to queue', () => { expect(mockMessageService.sendMessagesToQueue).toHaveBeenCalledWith([ + { + type: MessageType.DELIVERY_TO_COURT_PROSECUTOR, + user, + caseId, + }, + { + type: MessageType.DELIVERY_TO_COURT_DEFENDANT, + user, + caseId, + elementId: defendantId1, + }, + { + type: MessageType.DELIVERY_TO_COURT_DEFENDANT, + user, + caseId, + elementId: defendantId2, + }, { type: MessageType.DELIVERY_TO_COURT_CASE_FILES_RECORD, user, diff --git a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/archive.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/archive.spec.ts index 7f06a822d4aa..d6beb070a348 100644 --- a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/archive.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/archive.spec.ts @@ -287,13 +287,13 @@ describe('InternalCaseController - Archive', () => { ], where: archiveFilter, }) - expect(mockDefendantService.updateDatabaseDefendant).toHaveBeenCalledWith( + expect(mockDefendantService.updateForArcive).toHaveBeenCalledWith( caseId, defendantId1, { nationalId: '', name: '', address: '' }, transaction, ) - expect(mockDefendantService.updateDatabaseDefendant).toHaveBeenCalledWith( + expect(mockDefendantService.updateForArcive).toHaveBeenCalledWith( caseId, defendantId2, { nationalId: '', name: '', address: '' }, diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/internalDefendantController/deliverIndictmentDefenderToCourt.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentDefenderInfoToCourt.spec.ts similarity index 56% rename from apps/judicial-system/backend/src/app/modules/defendant/test/internalDefendantController/deliverIndictmentDefenderToCourt.spec.ts rename to apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentDefenderInfoToCourt.spec.ts index 5aa71689f3cf..f60117749089 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/internalDefendantController/deliverIndictmentDefenderToCourt.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentDefenderInfoToCourt.spec.ts @@ -2,12 +2,11 @@ import { uuid } from 'uuidv4' import { CaseType, User } from '@island.is/judicial-system/types' -import { createTestingDefendantModule } from '../createTestingDefendantModule' +import { createTestingCaseModule } from '../createTestingCaseModule' -import { Case } from '../../../case' import { CourtService } from '../../../court' import { DeliverDto } from '../../dto/deliver.dto' -import { Defendant } from '../../models/defendant.model' +import { Case } from '../../models/case.model' import { DeliverResponse } from '../../models/deliver.response' interface Then { @@ -17,64 +16,56 @@ interface Then { type GivenWhenThen = ( caseId: string, - defendantId: string, theCase: Case, - defendant: Defendant, body: DeliverDto, ) => Promise -describe('InternalDefendantController - Deliver indictment defender to court', () => { +describe('InternalCaseController - Deliver indictment defender info to court', () => { const user = { id: uuid() } as User const caseId = uuid() - const defendantId = uuid() const courtName = uuid() const courtCaseNumber = uuid() - const defendant = { - id: defendantId, - name: 'Test Ákærði', - nationalId: '1234567890', - defenderNationalId: '1234567899', - defenderName: 'Test Verjandi', - defenderEmail: 'defenderEmail', - } as Defendant const theCase = { id: caseId, type: CaseType.INDICTMENT, court: { name: courtName }, courtCaseNumber, - defendants: [defendant], + defendants: [ + { + name: 'Test Ákærði', + nationalId: '1234567890', + defenderNationalId: '1234567899', + defenderName: 'Test Verjandi', + defenderEmail: 'defenderEmail', + }, + { + name: 'Test Ákærði 2', + nationalId: '1234567891', + defenderNationalId: '1234567898', + defenderName: 'Test Verjandi 2', + defenderEmail: 'defenderEmail2', + }, + ], } as Case let mockCourtService: jest.Mocked let givenWhenThen: GivenWhenThen beforeEach(async () => { - const { courtService, internalDefendantController } = - await createTestingDefendantModule() + const { courtService, internalCaseController } = + await createTestingCaseModule() mockCourtService = courtService as jest.Mocked mockCourtService.updateIndictmentCaseWithDefenderInfo.mockResolvedValue( uuid(), ) - givenWhenThen = async ( - caseId: string, - defendantId: string, - theCase: Case, - defendant: Defendant, - body: DeliverDto, - ) => { + givenWhenThen = async (caseId: string, theCase: Case, body: DeliverDto) => { const then = {} as Then - await internalDefendantController - .deliverIndictmentDefenderToCourt( - caseId, - defendantId, - theCase, - defendant, - body, - ) + await internalCaseController + .deliverIndictmentDefenderInfoToCourt(caseId, theCase, body) .then((result) => (then.result = result)) .catch((error) => (then.error = error)) @@ -83,9 +74,7 @@ describe('InternalDefendantController - Deliver indictment defender to court', ( }) it('should deliver the defender information to court', async () => { - const then = await givenWhenThen(caseId, defendantId, theCase, defendant, { - user, - }) + const then = await givenWhenThen(caseId, theCase, { user }) expect( mockCourtService.updateIndictmentCaseWithDefenderInfo, @@ -94,9 +83,7 @@ describe('InternalDefendantController - Deliver indictment defender to court', ( caseId, courtName, courtCaseNumber, - defendant.nationalId, - defendant.defenderName, - defendant.defenderEmail, + theCase.defendants, ) expect(then.result).toEqual({ delivered: true }) @@ -109,9 +96,7 @@ describe('InternalDefendantController - Deliver indictment defender to court', ( error, ) - const then = await givenWhenThen(caseId, defendantId, theCase, defendant, { - user, - }) + const then = await givenWhenThen(caseId, theCase, { user }) expect(then.result).toEqual({ delivered: false }) }) diff --git a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentDefenderInfoToCourtGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentDefenderInfoToCourtGuards.spec.ts new file mode 100644 index 000000000000..4fa794094133 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentDefenderInfoToCourtGuards.spec.ts @@ -0,0 +1,50 @@ +import { CanActivate } from '@nestjs/common' + +import { indictmentCases } from '@island.is/judicial-system/types' + +import { CaseExistsGuard } from '../../guards/caseExists.guard' +import { CaseTypeGuard } from '../../guards/caseType.guard' +import { InternalCaseController } from '../../internalCase.controller' + +describe('InternalCaseController - Deliver indictment defender info to court guards', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let guards: any[] + + beforeEach(() => { + guards = Reflect.getMetadata( + '__guards__', + InternalCaseController.prototype.deliverIndictmentDefenderInfoToCourt, + ) + }) + + it('should have two guards', () => { + expect(guards).toHaveLength(2) + }) + + describe('CaseExistsGuard', () => { + let guard: CanActivate + + beforeEach(() => { + guard = new guards[0]() + }) + + it('should have CaseExistsGuard as guard 1', () => { + expect(guard).toBeInstanceOf(CaseExistsGuard) + }) + }) + + describe('CaseTypeGuard', () => { + let guard: CanActivate + + beforeEach(() => { + guard = guards[1] + }) + + it('should have CaseTypeGuard as guard 2', () => { + expect(guard).toBeInstanceOf(CaseTypeGuard) + expect(guard).toEqual({ + allowedCaseTypes: indictmentCases, + }) + }) + }) +}) diff --git a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentInfoToCourt.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentInfoToCourt.spec.ts index 3236f05eb4de..4c929c759e7a 100644 --- a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentInfoToCourt.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentInfoToCourt.spec.ts @@ -96,7 +96,7 @@ describe('InternalCaseController - Deliver indictment info to court', () => { caseId, courtName, courtCaseNumber, - indictmentDate, + receivedDate, indictmentDate, policeCaseNumber, ['Umferðarlagabrot', 'Hylming', 'Þjófnaður'], 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..3ff75964c054 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 @@ -24,6 +24,8 @@ import { isIndictmentCase, } from '@island.is/judicial-system/types' +import { nowFactory } from '../../factories' +import { Defendant } from '../defendant' import { EventService } from '../event' import { RobotLog } from './models/robotLog.model' import { courtModuleConfig } from './court.config' @@ -322,7 +324,6 @@ export class CourtService { caseId: string, courtId = '', type: CaseType, - receivalDate: Date, policeCaseNumbers: string[], isExtension: boolean, indictmentSubtypes?: IndictmentSubtypeMap, @@ -341,7 +342,7 @@ export class CourtService { caseType: isIndictment ? 'S - Ákærumál' : 'R - Rannsóknarmál', subtype: courtSubtype as string, status: 'Skráð', - receivalDate: formatISO(receivalDate, { representation: 'date' }), + receivalDate: formatISO(nowFactory(), { representation: 'date' }), basedOn: isIndictment ? 'Sakamál' : 'Rannsóknarhagsmunir', // TODO: pass in all policeCaseNumbers when CourtService supports it sourceNumber: policeCaseNumbers[0] ? policeCaseNumbers[0] : '', @@ -614,17 +615,17 @@ export class CourtService { caseId: string, courtName?: string, courtCaseNumber?: string, - defendantNationalId?: string, - defenderName?: string, - defenderEmail?: string, + defendants?: Defendant[], ): Promise { try { - const subject = `${courtName} - ${courtCaseNumber} - verjandi varnaraðila` - const content = JSON.stringify({ - nationalId: defendantNationalId, - defenderName, - defenderEmail, - }) + const defendantInfo = defendants?.map((defendant) => ({ + nationalId: defendant.nationalId, + defenderName: defendant.defenderName, + defenderEmail: defendant.defenderEmail, + })) + + const subject = `${courtName} - ${courtCaseNumber} - verjanda upplýsingar` + const content = JSON.stringify({ defendants: defendantInfo, courtName }) return this.sendToRobot( subject, @@ -634,7 +635,7 @@ export class CourtService { ) } catch (error) { this.eventService.postErrorEvent( - 'Failed to update indictment case with defender info', + 'Failed to update indictment with defender info', { caseId, actor: user.name, diff --git a/apps/judicial-system/backend/src/app/modules/court/test/createCourtCase.spec.ts b/apps/judicial-system/backend/src/app/modules/court/test/createCourtCase.spec.ts index a32798d80b4e..d1be752555d1 100644 --- a/apps/judicial-system/backend/src/app/modules/court/test/createCourtCase.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/court/test/createCourtCase.spec.ts @@ -14,9 +14,12 @@ import { import { createTestingCourtModule } from './createTestingCourtModule' +import { nowFactory } from '../../../factories' import { randomBoolean, randomDate, randomEnum } from '../../../test' import { courtSubtypes, Subtype } from '../court.service' +jest.mock('../../../factories') + interface Then { result: string error: Error @@ -27,14 +30,13 @@ type GivenWhenThen = ( caseId: string, courtId: string, type: CaseType, - receivalDate: Date, policeCaseNumbers: string[], isExtension: boolean, indictmentSubtypes?: IndictmentSubtypeMap, ) => Promise describe('CourtService - Create court case', () => { - const receivalDate = randomDate() + const date = randomDate() let mockCourtClientService: CourtClientService let givenWhenThen: GivenWhenThen @@ -44,12 +46,14 @@ describe('CourtService - Create court case', () => { mockCourtClientService = courtClientService + const mockToday = nowFactory as jest.Mock + mockToday.mockReturnValueOnce(date) + givenWhenThen = async ( user: User, caseId: string, courtId: string, type: CaseType, - receivalDate: Date, policeCaseNumbers: string[], isExtension: boolean, indictmentSubtypes?: IndictmentSubtypeMap, @@ -62,7 +66,6 @@ describe('CourtService - Create court case', () => { caseId, courtId, type, - receivalDate, policeCaseNumbers, isExtension, indictmentSubtypes, @@ -90,7 +93,6 @@ describe('CourtService - Create court case', () => { caseId, courtId, type, - receivalDate, policeCaseNumbers, isExtension, ) @@ -103,7 +105,7 @@ describe('CourtService - Create court case', () => { caseType: 'R - Rannsóknarmál', subtype: courtSubtypes[type as Subtype], status: 'Skráð', - receivalDate: formatISO(receivalDate, { representation: 'date' }), + receivalDate: formatISO(date, { representation: 'date' }), basedOn: 'Rannsóknarhagsmunir', sourceNumber: policeCaseNumbers[0], }, @@ -130,7 +132,6 @@ describe('CourtService - Create court case', () => { caseId, courtId, type, - receivalDate, policeCaseNumbers, isExtension, indictmentSubtypes, @@ -144,7 +145,7 @@ describe('CourtService - Create court case', () => { caseType: 'S - Ákærumál', subtype: courtSubtypes[indictmentSubtype], status: 'Skráð', - receivalDate: formatISO(receivalDate, { representation: 'date' }), + receivalDate: formatISO(date, { representation: 'date' }), basedOn: 'Sakamál', sourceNumber: policeCaseNumbers[0], }, @@ -170,7 +171,6 @@ describe('CourtService - Create court case', () => { caseId, courtId, type, - receivalDate, policeCaseNumbers, isExtension, ) @@ -181,7 +181,7 @@ describe('CourtService - Create court case', () => { caseType: 'R - Rannsóknarmál', subtype: courtSubtypes[type as Subtype][0], status: 'Skráð', - receivalDate: formatISO(receivalDate, { representation: 'date' }), + receivalDate: formatISO(date, { representation: 'date' }), basedOn: 'Rannsóknarhagsmunir', sourceNumber: policeCaseNumbers[0], }) @@ -205,7 +205,6 @@ describe('CourtService - Create court case', () => { caseId, courtId, type, - receivalDate, policeCaseNumbers, isExtension, ) @@ -216,7 +215,7 @@ describe('CourtService - Create court case', () => { caseType: 'R - Rannsóknarmál', subtype: courtSubtypes[type as Subtype][1], status: 'Skráð', - receivalDate: formatISO(receivalDate, { representation: 'date' }), + receivalDate: formatISO(date, { representation: 'date' }), basedOn: 'Rannsóknarhagsmunir', sourceNumber: policeCaseNumbers[0], }) @@ -249,7 +248,6 @@ describe('CourtService - Create court case', () => { caseId, courtId, type, - receivalDate, policeCaseNumbers, isExtension, indictmentSubtypes, @@ -286,7 +284,6 @@ describe('CourtService - Create court case', () => { caseId, courtId, type, - receivalDate, policeCaseNumbers, isExtension, indictmentSubtypes, 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..950e62bf84da 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 @@ -7,11 +7,7 @@ import type { Logger } from '@island.is/logging' import { LOGGER_PROVIDER } from '@island.is/logging' import { normalizeAndFormatNationalId } from '@island.is/judicial-system/formatters' -import { MessageService, MessageType } from '@island.is/judicial-system/message' -import { - CaseState, - CivilClaimantNotificationType, -} from '@island.is/judicial-system/types' +import { CaseState } from '@island.is/judicial-system/types' import { Case } from '../case/models/case.model' import { UpdateCivilClaimantDto } from './dto/updateCivilClaimant.dto' @@ -22,7 +18,6 @@ export class CivilClaimantService { constructor( @InjectModel(CivilClaimant) private readonly civilClaimantModel: typeof CivilClaimant, - private readonly messageService: MessageService, @Inject(LOGGER_PROVIDER) private readonly logger: Logger, ) {} @@ -32,24 +27,6 @@ export class CivilClaimantService { }) } - private async sendUpdateCivilClaimantMessages( - update: UpdateCivilClaimantDto, - updatedCivilClaimant: CivilClaimant, - ): Promise { - if (update.isSpokespersonConfirmed === true) { - return this.messageService.sendMessagesToQueue([ - { - type: MessageType.CIVIL_CLAIMANT_NOTIFICATION, - caseId: updatedCivilClaimant.caseId, - body: { - type: CivilClaimantNotificationType.SPOKESPERSON_ASSIGNED, - }, - elementId: updatedCivilClaimant.id, - }, - ]) - } - } - async update( caseId: string, civilClaimantId: string, @@ -72,8 +49,6 @@ export class CivilClaimantService { throw new Error(`Could not update civil claimant ${civilClaimantId}`) } - await this.sendUpdateCivilClaimantMessages(update, civilClaimants[0]) - return civilClaimants[0] } 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 b266c32f2a76..38374e35a08b 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 @@ -90,85 +90,63 @@ export class DefendantService { return defendants[0] } - private async sendRequestCaseUpdateDefendantMessages( + private async sendUpdateDefendantMessages( theCase: Case, updatedDefendant: Defendant, oldDefendant: Defendant, user: User, - ): Promise { - if (!theCase.courtCaseNumber) { - return - } - - const messages: Message[] = [] - + ) { // Handling of updates sent to the court system - // A defendant is updated after the case has been received by the court. - if (updatedDefendant.noNationalId !== oldDefendant.noNationalId) { - // A defendant nationalId is added or removed. Attempt to add the defendant to the court case. - // In case there is no national id, the court will be notified. - messages.push( - this.getMessageForDeliverDefendantToCourt(updatedDefendant, user), - ) - } else if (updatedDefendant.nationalId !== oldDefendant.nationalId) { - // A defendant is replaced. Attempt to add the defendant to the court case, - // but also ask the court to verify defendants. - messages.push( - this.getMessageForSendDefendantsNotUpdatedAtCourtNotification( - theCase, - user, - ), - this.getMessageForDeliverDefendantToCourt(updatedDefendant, user), - ) - } - - if (messages.length === 0) { - return - } - - return this.messageService.sendMessagesToQueue(messages) - } - - private async sendIndictmentCaseUpdateDefendantMessages( - theCase: Case, - updatedDefendant: Defendant, - oldDefendant: Defendant, - user: User, - ): Promise { - if (!theCase.courtCaseNumber) { - return + if (theCase.courtCaseNumber) { + // A defendant is updated after the case has been received by the court. + if (updatedDefendant.noNationalId !== oldDefendant.noNationalId) { + // This should only happen to non-indictment cases. + // A defendant nationalId is added or removed. Attempt to add the defendant to the court case. + // In case there is no national id, the court will be notified. + await this.messageService.sendMessagesToQueue([ + this.getMessageForDeliverDefendantToCourt(updatedDefendant, user), + ]) + } else if (updatedDefendant.nationalId !== oldDefendant.nationalId) { + // This should only happen to non-indictment cases. + // A defendant is replaced. Attempt to add the defendant to the court case, + // but also ask the court to verify defendants. + await this.messageService.sendMessagesToQueue([ + this.getMessageForSendDefendantsNotUpdatedAtCourtNotification( + theCase, + user, + ), + this.getMessageForDeliverDefendantToCourt(updatedDefendant, user), + ]) + } else if ( + updatedDefendant.defenderEmail !== oldDefendant.defenderEmail + ) { + // This should only happen to indictment cases. + // A defendant's defender email is updated. + // Attempt to update the defendant in the court case. + await this.messageService.sendMessagesToQueue([ + this.getMessageForDeliverDefendantToCourt(updatedDefendant, user), + ]) + } } - if ( - updatedDefendant.isDefenderChoiceConfirmed && - !oldDefendant.isDefenderChoiceConfirmed - ) { - // Defender choice was just confirmed by the court - const messages: Message[] = [ - { - type: MessageType.DELIVERY_TO_COURT_INDICTMENT_DEFENDER, - user, - caseId: theCase.id, - elementId: updatedDefendant.id, - }, - ] - + // Handling of messages sent to participants for indictment cases + if (isIndictmentCase(theCase.type)) { + // Defender was just confirmed by judge if ( - updatedDefendant.defenderChoice === DefenderChoice.CHOOSE || - updatedDefendant.defenderChoice === DefenderChoice.DELEGATE + updatedDefendant.isDefenderChoiceConfirmed && + !oldDefendant.isDefenderChoiceConfirmed && + (updatedDefendant.defenderChoice === DefenderChoice.CHOOSE || + updatedDefendant.defenderChoice === DefenderChoice.DELEGATE) ) { - // Defender was just confirmed by judge - if (!oldDefendant.isDefenderChoiceConfirmed) { - messages.push({ + await this.messageService.sendMessagesToQueue([ + { type: MessageType.DEFENDANT_NOTIFICATION, caseId: theCase.id, body: { type: DefendantNotificationType.DEFENDER_ASSIGNED }, elementId: updatedDefendant.id, - }) - } + }, + ]) } - - return this.messageService.sendMessagesToQueue(messages) } } @@ -205,15 +183,19 @@ export class DefendantService { return defendant } - async updateDatabaseDefendant( + async updateForArcive( caseId: string, defendantId: string, update: UpdateDefendantDto, - transaction?: Transaction, - ) { + transaction: Transaction, + ): Promise { const [numberOfAffectedRows, defendants] = await this.defendantModel.update( update, - { where: { id: defendantId, caseId }, returning: true, transaction }, + { + where: { id: defendantId, caseId }, + returning: true, + transaction, + }, ) return this.getUpdatedDefendant( @@ -224,41 +206,28 @@ export class DefendantService { ) } - async updateRequestCaseDefendant( + async update( theCase: Case, defendant: Defendant, update: UpdateDefendantDto, user: User, ): Promise { - const updatedDefendant = await this.updateDatabaseDefendant( - theCase.id, - defendant.id, + const [numberOfAffectedRows, defendants] = await this.defendantModel.update( update, + { + where: { id: defendant.id, caseId: theCase.id }, + returning: true, + }, ) - await this.sendRequestCaseUpdateDefendantMessages( - theCase, - updatedDefendant, - defendant, - user, - ) - - return updatedDefendant - } - - async updateIndictmentCaseDefendant( - theCase: Case, - defendant: Defendant, - update: UpdateDefendantDto, - user: User, - ): Promise { - const updatedDefendant = await this.updateDatabaseDefendant( - theCase.id, + const updatedDefendant = this.getUpdatedDefendant( + numberOfAffectedRows, + defendants, defendant.id, - update, + theCase.id, ) - await this.sendIndictmentCaseUpdateDefendantMessages( + await this.sendUpdateDefendantMessages( theCase, updatedDefendant, defendant, @@ -268,62 +237,38 @@ export class DefendantService { return updatedDefendant } - async update( - theCase: Case, - defendant: Defendant, - update: UpdateDefendantDto, - user: User, - ): Promise { - if (isIndictmentCase(theCase.type)) { - return this.updateIndictmentCaseDefendant( - theCase, - defendant, - update, - user, - ) - } else { - return this.updateRequestCaseDefendant(theCase, defendant, update, user) - } - } - - async updateRestricted( - theCase: Case, - defendant: Defendant, + async updateByNationalId( + caseId: string, + defendantNationalId: string, update: InternalUpdateDefendantDto, - isDefenderChoiceConfirmed = false, - transaction?: Transaction, ): Promise { // The reason we have a separate dto for this is because requests that end here // are initiated by outside API's which should not be able to edit other fields // Defendant updated originating from the judicial system should use the UpdateDefendantDto // and go through the update method above using the defendantId. - // This is also why we may set the isDefenderChoiceConfirmed to false here - the judge needs to confirm all changes. + // This is also why we set the isDefenderChoiceConfirmed to false here - the judge needs to confirm all changes. + update = { + ...update, + isDefenderChoiceConfirmed: false, + } as UpdateDefendantDto - const updatedDefendant = await this.updateDatabaseDefendant( - theCase.id, - defendant.id, - { ...update, isDefenderChoiceConfirmed }, - transaction, + const [numberOfAffectedRows, defendants] = await this.defendantModel.update( + update, + { + where: { + caseId, + national_id: normalizeAndFormatNationalId(defendantNationalId), + }, + returning: true, + }, ) - // Notify the court if the defendant has changed the defender choice - if ( - !updatedDefendant.isDefenderChoiceConfirmed && - updatedDefendant.defenderChoice === DefenderChoice.CHOOSE && - (updatedDefendant.defenderChoice !== defendant.defenderChoice || - updatedDefendant.defenderNationalId !== defendant.defenderNationalId) - ) { - await this.messageService.sendMessagesToQueue([ - { - type: MessageType.DEFENDANT_NOTIFICATION, - caseId: theCase.id, - elementId: updatedDefendant.id, - body: { - type: DefendantNotificationType.DEFENDANT_SELECTED_DEFENDER, - }, - }, - ]) - } + const updatedDefendant = this.getUpdatedDefendant( + numberOfAffectedRows, + defendants, + defendants[0].id, + caseId, + ) return updatedDefendant } @@ -448,30 +393,4 @@ export class DefendantService { return { delivered: false } }) } - - async deliverIndictmentDefenderToCourt( - theCase: Case, - defendant: Defendant, - user: User, - ): Promise { - return this.courtService - .updateIndictmentCaseWithDefenderInfo( - user, - theCase.id, - theCase.court?.name, - theCase.courtCaseNumber, - defendant.nationalId, - defendant.defenderName, - defendant.defenderEmail, - ) - .then(() => ({ delivered: true })) - .catch((reason) => { - this.logger.error( - `Failed to update defender info for defendant ${defendant.id} of indictment case ${theCase.id}`, - { reason }, - ) - - return { delivered: false } - }) - } } diff --git a/apps/judicial-system/backend/src/app/modules/defendant/dto/createDefendant.dto.ts b/apps/judicial-system/backend/src/app/modules/defendant/dto/createDefendant.dto.ts index 9c32a38660c7..eee5ea58da99 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/dto/createDefendant.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/dto/createDefendant.dto.ts @@ -1,10 +1,4 @@ -import { - IsBoolean, - IsEnum, - IsOptional, - IsString, - MaxLength, -} from 'class-validator' +import { IsBoolean, IsEnum, IsOptional, IsString } from 'class-validator' import { ApiPropertyOptional } from '@nestjs/swagger' @@ -17,13 +11,11 @@ export class CreateDefendantDto { readonly noNationalId?: boolean @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly nationalId?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly name?: string @@ -34,37 +26,31 @@ export class CreateDefendantDto { readonly gender?: Gender @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly address?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly citizenship?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly defenderName?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly defenderNationalId?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly defenderEmail?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly defenderPhoneNumber?: string diff --git a/apps/judicial-system/backend/src/app/modules/defendant/dto/deliver.dto.ts b/apps/judicial-system/backend/src/app/modules/defendant/dto/deliverDefendantToCourt.dto.ts similarity index 85% rename from apps/judicial-system/backend/src/app/modules/defendant/dto/deliver.dto.ts rename to apps/judicial-system/backend/src/app/modules/defendant/dto/deliverDefendantToCourt.dto.ts index 7595952ea24d..7671fe935ed8 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/dto/deliver.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/dto/deliverDefendantToCourt.dto.ts @@ -4,7 +4,7 @@ import { ApiProperty } from '@nestjs/swagger' import type { User } from '@island.is/judicial-system/types' -export class DeliverDto { +export class DeliverDefendantToCourtDto { @IsNotEmpty() @IsObject() @ApiProperty({ type: Object }) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/dto/updateCivilClaimant.dto.ts b/apps/judicial-system/backend/src/app/modules/defendant/dto/updateCivilClaimant.dto.ts index 44bcbed23e49..3f85ec624a7f 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/dto/updateCivilClaimant.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/dto/updateCivilClaimant.dto.ts @@ -1,4 +1,4 @@ -import { IsBoolean, IsOptional, IsString, MaxLength } from 'class-validator' +import { IsBoolean, IsOptional, IsString } from 'class-validator' import { ApiPropertyOptional } from '@nestjs/swagger' @@ -9,13 +9,11 @@ export class UpdateCivilClaimantDto { readonly noNationalId?: boolean @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly name?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly nationalId?: string @@ -31,25 +29,21 @@ export class UpdateCivilClaimantDto { readonly spokespersonIsLawyer?: boolean @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly spokespersonNationalId?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly spokespersonName?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly spokespersonEmail?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly spokespersonPhoneNumber?: string diff --git a/apps/judicial-system/backend/src/app/modules/defendant/dto/updateDefendant.dto.ts b/apps/judicial-system/backend/src/app/modules/defendant/dto/updateDefendant.dto.ts index c59f91dcaa0d..8e9321fbf730 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/dto/updateDefendant.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/dto/updateDefendant.dto.ts @@ -5,7 +5,6 @@ import { IsEnum, IsOptional, IsString, - MaxLength, } from 'class-validator' import { ApiPropertyOptional } from '@nestjs/swagger' @@ -25,13 +24,11 @@ export class UpdateDefendantDto { readonly noNationalId?: boolean @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly nationalId?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly name?: string @@ -42,37 +39,31 @@ export class UpdateDefendantDto { readonly gender?: Gender @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly address?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly citizenship?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly defenderName?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly defenderNationalId?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly defenderEmail?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly defenderPhoneNumber?: string @@ -115,13 +106,11 @@ export class UpdateDefendantDto { readonly requestedDefenderChoice?: DefenderChoice @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly requestedDefenderNationalId?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly requestedDefenderName?: string diff --git a/apps/judicial-system/backend/src/app/modules/defendant/guards/civilClaimaint.decorator.ts b/apps/judicial-system/backend/src/app/modules/defendant/guards/civilClaimaint.decorator.ts deleted file mode 100644 index 9f508c5e60e5..000000000000 --- a/apps/judicial-system/backend/src/app/modules/defendant/guards/civilClaimaint.decorator.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { createParamDecorator } from '@nestjs/common' - -import { CivilClaimant } from '../models/civilClaimant.model' - -export const CurrentCivilClaimant = createParamDecorator( - (data, { args: [_1, { req }] }): CivilClaimant => req.civilClaimant, -) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/guards/civilClaimantExists.guard.ts b/apps/judicial-system/backend/src/app/modules/defendant/guards/civilClaimantExists.guard.ts deleted file mode 100644 index 401886582d41..000000000000 --- a/apps/judicial-system/backend/src/app/modules/defendant/guards/civilClaimantExists.guard.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { - BadRequestException, - CanActivate, - ExecutionContext, - Injectable, - NotFoundException, -} from '@nestjs/common' - -import { Case } from '../../case' - -@Injectable() -export class CivilClaimantExistsGuard implements CanActivate { - canActivate(context: ExecutionContext): boolean { - const request = context.switchToHttp().getRequest() - - const theCase: Case = request.case - - if (!theCase) { - throw new BadRequestException('Missing case') - } - - const civilClaimantId = request.params.civilClaimantId - - if (!civilClaimantId) { - throw new BadRequestException('Missing civil claimant id') - } - - const civilClaimant = theCase.civilClaimants?.find( - (civilClaimants) => civilClaimants.id === civilClaimantId, - ) - - if (!civilClaimant) { - throw new NotFoundException( - `Civil claimant ${civilClaimantId} of case ${theCase.id} does not exist`, - ) - } - - request.civilClaimant = civilClaimant - - return true - } -} diff --git a/apps/judicial-system/backend/src/app/modules/defendant/guards/defendantNationalIdExists.guard.ts b/apps/judicial-system/backend/src/app/modules/defendant/guards/defendantNationalIdExists.guard.ts deleted file mode 100644 index b400bccebd5e..000000000000 --- a/apps/judicial-system/backend/src/app/modules/defendant/guards/defendantNationalIdExists.guard.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { - BadRequestException, - CanActivate, - ExecutionContext, - Injectable, - NotFoundException, -} from '@nestjs/common' - -import { normalizeAndFormatNationalId } from '@island.is/judicial-system/formatters' - -import { Case } from '../../case' - -@Injectable() -export class DefendantNationalIdExistsGuard implements CanActivate { - canActivate(context: ExecutionContext): boolean { - const request = context.switchToHttp().getRequest() - - const theCase: Case = request.case - - if (!theCase) { - throw new BadRequestException('Missing case') - } - - const defendantNationalId = request.params.defendantNationalId - - if (!defendantNationalId) { - throw new BadRequestException('Missing defendant national id') - } - - const normalizedAndFormatedNationalId = - normalizeAndFormatNationalId(defendantNationalId) - - const defendant = theCase.defendants?.find( - (defendant) => - defendant.nationalId && - normalizedAndFormatedNationalId.includes(defendant.nationalId), - ) - - if (!defendant) { - throw new NotFoundException( - `Defendant with given national id of case ${theCase.id} does not exist`, - ) - } - - request.defendant = defendant - - return true - } -} diff --git a/apps/judicial-system/backend/src/app/modules/defendant/guards/test/civilClaimantExistsGuard.spec.ts b/apps/judicial-system/backend/src/app/modules/defendant/guards/test/civilClaimantExistsGuard.spec.ts deleted file mode 100644 index 4e5cd39db640..000000000000 --- a/apps/judicial-system/backend/src/app/modules/defendant/guards/test/civilClaimantExistsGuard.spec.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { uuid } from 'uuidv4' - -import { - BadRequestException, - ExecutionContext, - NotFoundException, -} from '@nestjs/common' - -import { CivilClaimantExistsGuard } from '../civilClaimantExists.guard' - -interface Then { - result: boolean - error: Error -} - -type GivenWhenThen = () => Promise - -describe('Civil Claimant Exists Guard', () => { - const mockRequest = jest.fn() - let givenWhenThen: GivenWhenThen - - beforeEach(async () => { - givenWhenThen = async (): Promise => { - const guard = new CivilClaimantExistsGuard() - const then = {} as Then - - try { - then.result = guard.canActivate({ - switchToHttp: () => ({ getRequest: mockRequest }), - } as unknown as ExecutionContext) - } catch (error) { - then.error = error as Error - } - - return then - } - }) - - describe('civil claimant exists', () => { - const caseId = uuid() - const civilClaimantId = uuid() - const civilClaimant = { id: civilClaimantId, caseId } - const theCase = { id: caseId, civilClaimants: [civilClaimant] } - const request = { - params: { caseId, civilClaimantId }, - case: theCase, - civilClaimant: undefined, - } - let then: Then - - beforeEach(async () => { - mockRequest.mockReturnValueOnce(request) - - then = await givenWhenThen() - }) - - it('should activate', () => { - expect(then.result).toBe(true) - expect(request.civilClaimant).toBe(civilClaimant) - }) - }) - - describe('civil claimant does not exist', () => { - const caseId = uuid() - const civilClaimantId = uuid() - const theCase = { id: caseId, civilClaimants: [] } - let then: Then - - beforeEach(async () => { - mockRequest.mockReturnValueOnce({ - params: { caseId, civilClaimantId }, - case: theCase, - }) - - then = await givenWhenThen() - }) - - it('should throw NotFoundException', () => { - expect(then.error).toBeInstanceOf(NotFoundException) - expect(then.error.message).toBe( - `Civil claimant ${civilClaimantId} of case ${caseId} does not exist`, - ) - }) - }) - - describe('missing case', () => { - 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') - }) - }) - - describe('missing civil claimant id', () => { - const caseId = uuid() - const theCase = { id: caseId, civilClaimants: [] } - let then: Then - - beforeEach(async () => { - mockRequest.mockReturnValueOnce({ params: { caseId }, case: theCase }) - - then = await givenWhenThen() - }) - - it('should throw BadRequestException', () => { - expect(then.error).toBeInstanceOf(BadRequestException) - expect(then.error.message).toBe('Missing civil claimant id') - }) - }) -}) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/guards/test/defendantNationalIdExistsGuard.spec.ts b/apps/judicial-system/backend/src/app/modules/defendant/guards/test/defendantNationalIdExistsGuard.spec.ts deleted file mode 100644 index 07ae3267b24d..000000000000 --- a/apps/judicial-system/backend/src/app/modules/defendant/guards/test/defendantNationalIdExistsGuard.spec.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { uuid } from 'uuidv4' - -import { - BadRequestException, - ExecutionContext, - NotFoundException, -} from '@nestjs/common' - -import { DefendantNationalIdExistsGuard } from '../defendantNationalIdExists.guard' - -interface Then { - result: boolean - error: Error -} - -type GivenWhenThen = () => Promise - -describe('Defendant National Id Exists Guard', () => { - const mockRequest = jest.fn() - let givenWhenThen: GivenWhenThen - - beforeEach(async () => { - givenWhenThen = async (): Promise => { - const guard = new DefendantNationalIdExistsGuard() - const then = {} as Then - - try { - then.result = guard.canActivate({ - switchToHttp: () => ({ getRequest: mockRequest }), - } as unknown as ExecutionContext) - } catch (error) { - then.error = error as Error - } - - return then - } - }) - - describe('defendant exists', () => { - const caseId = uuid() - const defendantId = uuid() - const defendantNationalId = uuid() - const defendant = { - id: defendantId, - nationalId: defendantNationalId, - caseId, - } - const theCase = { id: caseId, defendants: [defendant] } - const request = { - params: { caseId, defendantNationalId }, - case: theCase, - defendant: undefined, - } - let then: Then - - beforeEach(async () => { - mockRequest.mockReturnValueOnce(request) - - then = await givenWhenThen() - }) - - it('should activate', () => { - expect(then.result).toBe(true) - expect(request.defendant).toBe(defendant) - }) - }) - - describe('defendant does not exist', () => { - const caseId = uuid() - const defendantId = uuid() - const theCase = { - id: caseId, - defendants: [{ id: defendantId, nationalId: uuid(), caseId }], - } - let then: Then - - beforeEach(async () => { - mockRequest.mockReturnValueOnce({ - params: { caseId, defendantNationalId: uuid() }, - case: theCase, - }) - - then = await givenWhenThen() - }) - - it('should throw NotFoundException', () => { - expect(then.error).toBeInstanceOf(NotFoundException) - expect(then.error.message).toBe( - `Defendant with given national id of case ${caseId} does not exist`, - ) - }) - }) - - describe('missing case', () => { - 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') - }) - }) - - describe('missing defendant id', () => { - const caseId = uuid() - const theCase = { id: caseId, defendants: [] } - let then: Then - - beforeEach(async () => { - mockRequest.mockReturnValueOnce({ params: { caseId }, case: theCase }) - - 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/defendant/index.ts b/apps/judicial-system/backend/src/app/modules/defendant/index.ts index 5fb8a9c29896..d9914e3dcda4 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/index.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/index.ts @@ -1,9 +1,6 @@ export { Defendant } from './models/defendant.model' export { DefendantService } from './defendant.service' +export { CivilClaimant } from './models/civilClaimant.model' export { DefendantExistsGuard } from './guards/defendantExists.guard' export { CurrentDefendant } from './guards/defendant.decorator' - -export { CivilClaimant } from './models/civilClaimant.model' export { CivilClaimantService } from './civilClaimant.service' -export { CivilClaimantExistsGuard } from './guards/civilClaimantExists.guard' -export { CurrentCivilClaimant } from './guards/civilClaimaint.decorator' diff --git a/apps/judicial-system/backend/src/app/modules/defendant/internalDefendant.controller.ts b/apps/judicial-system/backend/src/app/modules/defendant/internalDefendant.controller.ts index 92511ff786d0..f7dc724065a6 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/internalDefendant.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/internalDefendant.controller.ts @@ -17,18 +17,12 @@ import { messageEndpoint, MessageType, } from '@island.is/judicial-system/message' -import { - indictmentCases, - investigationCases, - restrictionCases, -} from '@island.is/judicial-system/types' -import { Case, CaseExistsGuard, CaseTypeGuard, CurrentCase } from '../case' -import { DeliverDto } from './dto/deliver.dto' +import { Case, CaseExistsGuard, CurrentCase } from '../case' +import { DeliverDefendantToCourtDto } from './dto/deliverDefendantToCourt.dto' import { InternalUpdateDefendantDto } from './dto/internalUpdateDefendant.dto' import { CurrentDefendant } from './guards/defendant.decorator' import { DefendantExistsGuard } from './guards/defendantExists.guard' -import { DefendantNationalIdExistsGuard } from './guards/defendantNationalIdExists.guard' import { Defendant } from './models/defendant.model' import { DeliverResponse } from './models/deliver.response' import { DefendantService } from './defendant.service' @@ -42,10 +36,7 @@ export class InternalDefendantController { @Inject(LOGGER_PROVIDER) private readonly logger: Logger, ) {} - @UseGuards( - new CaseTypeGuard([...restrictionCases, ...investigationCases]), - DefendantExistsGuard, - ) + @UseGuards(DefendantExistsGuard) @Post( `${messageEndpoint[MessageType.DELIVERY_TO_COURT_DEFENDANT]}/:defendantId`, ) @@ -58,7 +49,7 @@ export class InternalDefendantController { @Param('defendantId') defendantId: string, @CurrentCase() theCase: Case, @CurrentDefendant() defendant: Defendant, - @Body() deliverDefendantToCourtDto: DeliverDto, + @Body() deliverDefendantToCourtDto: DeliverDefendantToCourtDto, ): Promise { this.logger.debug( `Delivering defendant ${defendantId} of case ${caseId} to court`, @@ -71,53 +62,25 @@ export class InternalDefendantController { ) } - @UseGuards(new CaseTypeGuard(indictmentCases), DefendantNationalIdExistsGuard) @Patch('defense/:defendantNationalId') @ApiOkResponse({ type: Defendant, description: 'Updates defendant information by case and national id', }) - updateDefendant( + async updateDefendant( @Param('caseId') caseId: string, - @Param('defendantNationalId') _: string, + @Param('defendantNationalId') defendantNationalId: string, @CurrentCase() theCase: Case, - @CurrentDefendant() defendant: Defendant, @Body() updatedDefendantChoice: InternalUpdateDefendantDto, ): Promise { this.logger.debug(`Updating defendant info for ${caseId}`) - return this.defendantService.updateRestricted( - theCase, - defendant, + const updatedDefendant = await this.defendantService.updateByNationalId( + theCase.id, + defendantNationalId, updatedDefendantChoice, ) - } - - @UseGuards(new CaseTypeGuard(indictmentCases), DefendantExistsGuard) - @Post( - `${ - messageEndpoint[MessageType.DELIVERY_TO_COURT_INDICTMENT_DEFENDER] - }/:defendantId`, - ) - @ApiOkResponse({ - type: DeliverResponse, - description: 'Delivers indictment case defender info to court', - }) - deliverIndictmentDefenderToCourt( - @Param('caseId') caseId: string, - @Param('defendantId') defendantId: string, - @CurrentCase() theCase: Case, - @CurrentDefendant() defendant: Defendant, - @Body() deliverDto: DeliverDto, - ): Promise { - this.logger.debug( - `Delivering defender info for defendant ${defendantId} of case ${caseId} to court`, - ) - return this.defendantService.deliverIndictmentDefenderToCourt( - theCase, - defendant, - deliverDto.user, - ) + return updatedDefendant } } diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/create.spec.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/create.spec.ts deleted file mode 100644 index 4684346f3209..000000000000 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/create.spec.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { uuid } from 'uuidv4' - -import { createTestingDefendantModule } from '../createTestingDefendantModule' - -import { Case } from '../../../case' -import { CivilClaimant } from '../../models/civilClaimant.model' - -interface Then { - result: CivilClaimant - error: Error -} - -type GivenWhenThen = (caseId?: string) => Promise - -describe('CivilClaimantController - Create', () => { - const caseId = uuid() - const civilClaimantId = uuid() - const theCase = { id: caseId } as Case - const civilClaimantToCreate = { - caseId, - } - const createdCivilClaimant = { id: civilClaimantId, caseId } - - let mockCivilClaimantModel: typeof CivilClaimant - let givenWhenThen: GivenWhenThen - - beforeEach(async () => { - const { civilClaimantModel, civilClaimantController } = - await createTestingDefendantModule() - - mockCivilClaimantModel = civilClaimantModel - - const mockCreate = mockCivilClaimantModel.create as jest.Mock - mockCreate.mockResolvedValue(createdCivilClaimant) - - givenWhenThen = async () => { - const then = {} as Then - - await civilClaimantController - .create(theCase.id, theCase) - .then((result) => (then.result = result)) - .catch((error) => (then.error = error)) - - return then - } - }) - - describe('civil claimant creation', () => { - let then: Then - - beforeEach(async () => { - then = await givenWhenThen(caseId) - }) - it('should create a civil claimant', () => { - expect(mockCivilClaimantModel.create).toHaveBeenCalledWith( - civilClaimantToCreate, - ) - }) - - it('should return the created civil claimant', () => { - expect(then.result).toEqual(createdCivilClaimant) - }) - }) - - describe('civil claimant creation fails', () => { - let then: Then - - beforeEach(async () => { - const mockCreate = mockCivilClaimantModel.create as jest.Mock - mockCreate.mockRejectedValue(new Error('Test error')) - - then = await givenWhenThen(caseId) - }) - - it('should throw an error', () => { - expect(then.error).toBeInstanceOf(Error) - expect(then.error.message).toEqual('Test error') - }) - }) -}) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/createGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/createGuards.spec.ts deleted file mode 100644 index 738952366261..000000000000 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/createGuards.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { CanActivate } from '@nestjs/common' - -import { CaseExistsGuard, CaseWriteGuard } from '../../../case' -import { CivilClaimantController } from '../../civilClaimant.controller' - -describe('CivilClaimantController - Create guards', () => { - let guards: Array CanActivate> - const expectedGuards = [CaseExistsGuard, CaseWriteGuard] - - beforeEach(() => { - guards = Reflect.getMetadata( - '__guards__', - CivilClaimantController.prototype.create, - ) - }) - - it('should have the correct guards in the correct order', () => { - expect(guards).toHaveLength(expectedGuards.length) - - expectedGuards.forEach((expectedGuard, index) => { - const guardInstance = new guards[index]() - expect(guardInstance).toBeInstanceOf(expectedGuard) - }) - }) -}) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/createRolesRules.spec.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/createRolesRules.spec.ts deleted file mode 100644 index 326946023e4f..000000000000 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/createRolesRules.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { - districtCourtAssistantRule, - districtCourtJudgeRule, - districtCourtRegistrarRule, - prosecutorRepresentativeRule, - prosecutorRule, -} from '../../../../guards' -import { CivilClaimantController } from '../../civilClaimant.controller' - -describe('CivilClaimantController - Create rules', () => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let rules: any[] - - const expectedRules = [ - prosecutorRule, - prosecutorRepresentativeRule, - districtCourtJudgeRule, - districtCourtRegistrarRule, - districtCourtAssistantRule, - ] - - beforeEach(() => { - rules = Reflect.getMetadata( - 'roles-rules', - CivilClaimantController.prototype.create, - ) - }) - - it('should give permission to roles', () => { - expect(rules).toHaveLength(expectedRules.length) - expectedRules.forEach((expectedRule) => - expect(rules).toContain(expectedRule), - ) - }) -}) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/delete.spec.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/delete.spec.ts deleted file mode 100644 index 8a2c17bc5e10..000000000000 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/delete.spec.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { uuid } from 'uuidv4' - -import { createTestingDefendantModule } from '../createTestingDefendantModule' - -import { CivilClaimant } from '../../models/civilClaimant.model' -import { DeleteCivilClaimantResponse } from '../../models/deleteCivilClaimant.response' - -interface Then { - result: DeleteCivilClaimantResponse - error: Error -} - -type GivenWhenThen = ( - caseId?: string, - civilClaimaintId?: string, -) => Promise - -describe('CivilClaimantController - Delete', () => { - const caseId = uuid() - const civilClaimantId = uuid() - - let mockCivilClaimantModel: typeof CivilClaimant - let givenWhenThen: GivenWhenThen - - beforeEach(async () => { - const { civilClaimantController, civilClaimantModel } = - await createTestingDefendantModule() - - mockCivilClaimantModel = civilClaimantModel - - const mockDestroy = mockCivilClaimantModel.destroy as jest.Mock - mockDestroy.mockRejectedValue(new Error('Test error')) - - givenWhenThen = async () => { - const then = {} as Then - - try { - then.result = await civilClaimantController.delete( - caseId, - civilClaimantId, - ) - } catch (error) { - then.error = error as Error - } - - return then - } - }) - - describe('civil claimant deleted', () => { - let then: Then - - beforeEach(async () => { - const mockDestroy = mockCivilClaimantModel.destroy as jest.Mock - mockDestroy.mockResolvedValue(1) - - then = await givenWhenThen(caseId, civilClaimantId) - }) - it('should delete civil claimant', () => { - expect(mockCivilClaimantModel.destroy).toHaveBeenCalledWith({ - where: { caseId, id: civilClaimantId }, - }) - expect(then.result).toEqual({ deleted: true }) - }) - }) - - describe('civil claimant deletion fails', () => { - let then: Then - - beforeEach(async () => { - then = await givenWhenThen() - }) - - it('should throw Error', () => { - expect(then.error).toBeInstanceOf(Error) - expect(then.error.message).toBe('Test error') - }) - }) -}) 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 deleted file mode 100644 index 20e8ef89e2f6..000000000000 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/deleteGuards.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { CanActivate } from '@nestjs/common' - -import { CaseExistsGuard, CaseWriteGuard } from '../../../case' -import { CivilClaimantController } from '../../civilClaimant.controller' - -describe('CivilClaimantController - Delete guards', () => { - let guards: Array CanActivate> - const expectedGuards = [CaseExistsGuard, CaseWriteGuard] - - beforeEach(() => { - guards = Reflect.getMetadata( - '__guards__', - CivilClaimantController.prototype.delete, - ) - }) - - it('should have the correct guards in the correct order', () => { - expect(guards).toHaveLength(expectedGuards.length) - - expectedGuards.forEach((expectedGuard, index) => { - const guardInstance = new guards[index]() - expect(guardInstance).toBeInstanceOf(expectedGuard) - }) - }) -}) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/deleteRolesRules.spec.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/deleteRolesRules.spec.ts deleted file mode 100644 index 46039a55bc8b..000000000000 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/deleteRolesRules.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { - prosecutorRepresentativeRule, - prosecutorRule, -} from '../../../../guards' -import { CivilClaimantController } from '../../civilClaimant.controller' - -describe('CivilClaimantController - Delete rules', () => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let rules: any[] - - const expectedRules = [prosecutorRule, prosecutorRepresentativeRule] - - beforeEach(() => { - rules = Reflect.getMetadata( - 'roles-rules', - CivilClaimantController.prototype.delete, - ) - }) - - it('should give permission to roles', () => { - expect(rules).toHaveLength(expectedRules.length) - expectedRules.forEach((expectedRule) => - expect(rules).toContain(expectedRule), - ) - }) -}) 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 deleted file mode 100644 index d8661906faa4..000000000000 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/update.spec.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { uuid } from 'uuidv4' - -import { MessageService, MessageType } from '@island.is/judicial-system/message' -import { CivilClaimantNotificationType } from '@island.is/judicial-system/types' - -import { createTestingDefendantModule } from '../createTestingDefendantModule' - -import { UpdateCivilClaimantDto } from '../../dto/updateCivilClaimant.dto' -import { CivilClaimant } from '../../models/civilClaimant.model' - -interface Then { - result: CivilClaimant - error: Error -} - -type GivenWhenThen = ( - caseId: string, - civilClaimantId: string, - updateData: UpdateCivilClaimantDto, -) => Promise - -describe('CivilClaimantController - Update', () => { - const caseId = uuid() - const civilClaimantId = uuid() - const civilClaimaint = { - id: civilClaimantId, - caseId, - nationalId: uuid(), - name: 'Original Name', - } as CivilClaimant - - let mockMessageService: MessageService - let mockCivilClaimantModel: typeof CivilClaimant - let givenWhenThen: GivenWhenThen - - beforeEach(async () => { - const { messageService, civilClaimantModel, civilClaimantController } = - await createTestingDefendantModule() - - mockMessageService = messageService - mockCivilClaimantModel = civilClaimantModel - - givenWhenThen = async ( - caseId: string, - civilClaimantId: string, - updateData: UpdateCivilClaimantDto, - ) => { - const then = {} as Then - - await civilClaimantController - .update(caseId, civilClaimantId, updateData) - .then((result) => (then.result = result)) - .catch((error) => (then.error = error)) - - return then - } - }) - - describe('civil claimant updated', () => { - const civilClaimantUpdate = { name: 'Updated Name' } - const updatedCivilClaimant = { - id: civilClaimantId, - caseId, - ...civilClaimantUpdate, - } - let then: Then - - beforeEach(async () => { - const mockUpdate = mockCivilClaimantModel.update as jest.Mock - mockUpdate.mockResolvedValueOnce([1, [updatedCivilClaimant]]) - - then = await givenWhenThen(caseId, civilClaimantId, civilClaimantUpdate) - }) - - it('should update the civil claimant', () => { - expect(mockCivilClaimantModel.update).toHaveBeenCalledWith( - civilClaimantUpdate, - { - where: { id: civilClaimantId, caseId }, - returning: true, - }, - ) - expect(mockMessageService.sendMessagesToQueue).not.toHaveBeenCalled() - }) - - it('should return the updated civil claimant', () => { - expect(then.result).toBe(updatedCivilClaimant) - }) - }) - - describe('civil claimant spokesperson confirmed', () => { - const civilClaimantUpdate = { isSpokespersonConfirmed: true } - const updatedCivilClaimant = { - id: civilClaimantId, - caseId, - ...civilClaimantUpdate, - } - let then: Then - - beforeEach(async () => { - const mockUpdate = mockCivilClaimantModel.update as jest.Mock - mockUpdate.mockResolvedValueOnce([1, [updatedCivilClaimant]]) - - then = await givenWhenThen(caseId, civilClaimantId, civilClaimantUpdate) - }) - - it('should queue spokesperson assigned message', () => { - expect(mockMessageService.sendMessagesToQueue).toHaveBeenCalledWith([ - { - type: MessageType.CIVIL_CLAIMANT_NOTIFICATION, - caseId, - elementId: civilClaimantId, - body: { - type: CivilClaimantNotificationType.SPOKESPERSON_ASSIGNED, - }, - }, - ]) - }) - - it('should return the updated civil claimant', () => { - expect(then.result).toBe(updatedCivilClaimant) - }) - }) - - describe('civil claimant update fails', () => { - let then: Then - - beforeEach(async () => { - const mockUpdate = mockCivilClaimantModel.update as jest.Mock - mockUpdate.mockRejectedValue(new Error('Test error')) - - then = await givenWhenThen(caseId, civilClaimantId, {}) - }) - - it('should throw an error', () => { - expect(then.error).toBeInstanceOf(Error) - expect(then.error.message).toEqual('Test 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 deleted file mode 100644 index d333af01f86f..000000000000 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/updateGuards.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { CanActivate } from '@nestjs/common' - -import { CaseExistsGuard, CaseWriteGuard } from '../../../case' -import { CivilClaimantController } from '../../civilClaimant.controller' - -describe('CivilClaimantController - Update guards', () => { - let guards: Array CanActivate> - const expectedGuards = [CaseExistsGuard, CaseWriteGuard] - - beforeEach(() => { - guards = Reflect.getMetadata( - '__guards__', - CivilClaimantController.prototype.update, - ) - }) - - it('should have the correct guards in the correct order', () => { - expect(guards).toHaveLength(expectedGuards.length) - - expectedGuards.forEach((expectedGuard, index) => { - const guardInstance = new guards[index]() - expect(guardInstance).toBeInstanceOf(expectedGuard) - }) - }) -}) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/updateRolesRules.spec.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/updateRolesRules.spec.ts deleted file mode 100644 index 38a579850480..000000000000 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/updateRolesRules.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { - districtCourtAssistantRule, - districtCourtJudgeRule, - districtCourtRegistrarRule, - prosecutorRepresentativeRule, - prosecutorRule, -} from '../../../../guards' -import { CivilClaimantController } from '../../civilClaimant.controller' - -describe('CivilClaimantController - Update rules', () => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let rules: any[] - - const expectedRules = [ - prosecutorRule, - prosecutorRepresentativeRule, - districtCourtJudgeRule, - districtCourtRegistrarRule, - districtCourtAssistantRule, - ] - - beforeEach(() => { - rules = Reflect.getMetadata( - 'roles-rules', - CivilClaimantController.prototype.update, - ) - }) - - it('should give permission to roles', () => { - expect(rules).toHaveLength(expectedRules.length) - expectedRules.forEach((expectedRule) => - expect(rules).toContain(expectedRule), - ) - }) -}) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/createTestingDefendantModule.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/createTestingDefendantModule.ts index 7ca4abf0ae2d..3b18ac9b8c8c 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/createTestingDefendantModule.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/test/createTestingDefendantModule.ts @@ -13,12 +13,9 @@ import { MessageService } from '@island.is/judicial-system/message' import { CaseService } from '../../case' import { CourtService } from '../../court' import { UserService } from '../../user' -import { CivilClaimantController } from '../civilClaimant.controller' -import { CivilClaimantService } from '../civilClaimant.service' import { DefendantController } from '../defendant.controller' import { DefendantService } from '../defendant.service' import { InternalDefendantController } from '../internalDefendant.controller' -import { CivilClaimant } from '../models/civilClaimant.model' import { Defendant } from '../models/defendant.model' jest.mock('@island.is/judicial-system/message') @@ -29,11 +26,7 @@ jest.mock('../../case/case.service') export const createTestingDefendantModule = async () => { const defendantModule = await Test.createTestingModule({ imports: [ConfigModule.forRoot({ load: [sharedAuthModuleConfig] })], - controllers: [ - DefendantController, - InternalDefendantController, - CivilClaimantController, - ], + controllers: [DefendantController, InternalDefendantController], providers: [ SharedAuthModule, MessageService, @@ -59,19 +52,7 @@ export const createTestingDefendantModule = async () => { findByPk: jest.fn(), }, }, - { - provide: getModelToken(CivilClaimant), - useValue: { - findOne: jest.fn(), - findAll: jest.fn(), - create: jest.fn(), - update: jest.fn(), - destroy: jest.fn(), - findByPk: jest.fn(), - }, - }, DefendantService, - CivilClaimantService, ], }).compile() @@ -96,17 +77,6 @@ export const createTestingDefendantModule = async () => { InternalDefendantController, ) - const civilClaimantModel = await defendantModule.resolve< - typeof CivilClaimant - >(getModelToken(CivilClaimant)) - - const civilClaimantService = - defendantModule.get(CivilClaimantService) - - const civilClaimantController = defendantModule.get( - CivilClaimantController, - ) - defendantModule.close() return { @@ -117,8 +87,5 @@ export const createTestingDefendantModule = async () => { defendantService, defendantController, internalDefendantController, - civilClaimantService, - civilClaimantController, - civilClaimantModel, } } diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/defendantController/update.spec.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/defendantController/update.spec.ts index 9b5d893e2bc1..4ead03bc4b3d 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/defendantController/update.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/test/defendantController/update.spec.ts @@ -22,7 +22,6 @@ interface Then { type GivenWhenThen = ( defendantUpdate: UpdateDefendantDto, - type: CaseType, courtCaseNumber?: string, ) => Promise @@ -53,7 +52,6 @@ describe('DefendantController - Update', () => { givenWhenThen = async ( defendantUpdate: UpdateDefendantDto, - type: CaseType, courtCaseNumber?: string, ) => { const then = {} as Then @@ -63,7 +61,7 @@ describe('DefendantController - Update', () => { caseId, defendantId, user, - { id: caseId, courtCaseNumber, type } as Case, + { id: caseId, courtCaseNumber, type: CaseType.INDICTMENT } as Case, defendant, defendantUpdate, ) @@ -83,7 +81,7 @@ describe('DefendantController - Update', () => { const mockUpdate = mockDefendantModel.update as jest.Mock mockUpdate.mockResolvedValueOnce([1, [updatedDefendant]]) - then = await givenWhenThen(defendantUpdate, CaseType.CUSTODY) + then = await givenWhenThen(defendantUpdate) }) it('should update the defendant without queuing', () => { @@ -104,7 +102,7 @@ describe('DefendantController - Update', () => { const mockUpdate = mockDefendantModel.update as jest.Mock mockUpdate.mockResolvedValueOnce([1, [updatedDefendant]]) - await givenWhenThen(defendantUpdate, CaseType.INDICTMENT, uuid()) + await givenWhenThen(defendantUpdate, uuid()) }) it('should not queue', () => { @@ -120,7 +118,7 @@ describe('DefendantController - Update', () => { const mockUpdate = mockDefendantModel.update as jest.Mock mockUpdate.mockResolvedValueOnce([1, [updatedDefendant]]) - await givenWhenThen(defendantUpdate, CaseType.CUSTODY, uuid()) + await givenWhenThen(defendantUpdate, uuid()) }) it('should queue messages', () => { @@ -143,7 +141,7 @@ describe('DefendantController - Update', () => { const mockUpdate = mockDefendantModel.update as jest.Mock mockUpdate.mockResolvedValueOnce([1, [updatedDefendant]]) - await givenWhenThen(defendantUpdate, CaseType.TELECOMMUNICATIONS, uuid()) + await givenWhenThen(defendantUpdate, uuid()) }) it('should queue messages', () => { @@ -164,6 +162,29 @@ describe('DefendantController - Update', () => { }) }) + describe(`defendant's defender email changed after case is delivered to court`, () => { + const defendantUpdate = { defenderEmail: uuid() } + const updatedDefendant = { ...defendant, ...defendantUpdate } + + beforeEach(async () => { + const mockUpdate = mockDefendantModel.update as jest.Mock + mockUpdate.mockResolvedValueOnce([1, [updatedDefendant]]) + + await givenWhenThen(defendantUpdate, uuid()) + }) + + it('should queue messages', () => { + expect(mockMessageService.sendMessagesToQueue).toHaveBeenCalledWith([ + { + type: MessageType.DELIVERY_TO_COURT_DEFENDANT, + user, + caseId, + elementId: defendantId, + }, + ]) + }) + }) + describe.each([ { isDefenderChoiceConfirmed: true, shouldSendEmail: true }, { isDefenderChoiceConfirmed: false, shouldSendEmail: false }, @@ -181,18 +202,12 @@ describe('DefendantController - Update', () => { const mockUpdate = mockDefendantModel.update as jest.Mock mockUpdate.mockResolvedValueOnce([1, [updatedDefendant]]) - await givenWhenThen(defendantUpdate, CaseType.INDICTMENT, uuid()) + await givenWhenThen(defendantUpdate, uuid()) }) if (shouldSendEmail) { - it('should queue messages if defender has been confirmed', () => { + it('should queue message if defender has been confirmed', () => { expect(mockMessageService.sendMessagesToQueue).toHaveBeenCalledWith([ - { - type: MessageType.DELIVERY_TO_COURT_INDICTMENT_DEFENDER, - user, - caseId, - elementId: defendantId, - }, { type: MessageType.DEFENDANT_NOTIFICATION, caseId, @@ -213,7 +228,7 @@ describe('DefendantController - Update', () => { let then: Then beforeEach(async () => { - then = await givenWhenThen({}, CaseType.CUSTODY) + then = await givenWhenThen({}) }) it('should throw Error', () => { diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/internalDefendantController/deliverDefendantToCourtGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/internalDefendantController/deliverDefendantToCourtGuards.spec.ts deleted file mode 100644 index 7bd257fe4077..000000000000 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/internalDefendantController/deliverDefendantToCourtGuards.spec.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { - investigationCases, - restrictionCases, -} from '@island.is/judicial-system/types' - -import { CaseTypeGuard } from '../../../case' -import { DefendantExistsGuard } from '../../guards/defendantExists.guard' -import { InternalDefendantController } from '../../internalDefendant.controller' - -describe('InternalDefendantController - Deliver defendant to court guards', () => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let guards: any[] - - beforeEach(() => { - guards = Reflect.getMetadata( - '__guards__', - InternalDefendantController.prototype.deliverDefendantToCourt, - ) - }) - - it('should have the right guard configuration', () => { - expect(guards).toHaveLength(2) - expect(guards[0]).toBeInstanceOf(CaseTypeGuard) - expect(guards[0]).toEqual({ - allowedCaseTypes: [...restrictionCases, ...investigationCases], - }) - expect(new guards[1]()).toBeInstanceOf(DefendantExistsGuard) - }) -}) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/internalDefendantController/deliverIndictmentDefenderToCourtGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/internalDefendantController/deliverIndictmentDefenderToCourtGuards.spec.ts deleted file mode 100644 index 8e61e07902e8..000000000000 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/internalDefendantController/deliverIndictmentDefenderToCourtGuards.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { indictmentCases } from '@island.is/judicial-system/types' - -import { CaseTypeGuard } from '../../../case' -import { DefendantExistsGuard } from '../../guards/defendantExists.guard' -import { InternalDefendantController } from '../../internalDefendant.controller' - -describe('InternalDefendantController - Deliver indictment defender to court guards', () => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let guards: any[] - - beforeEach(() => { - guards = Reflect.getMetadata( - '__guards__', - InternalDefendantController.prototype.deliverIndictmentDefenderToCourt, - ) - }) - - it('should have the right guard configuration', () => { - expect(guards).toHaveLength(2) - expect(guards[0]).toBeInstanceOf(CaseTypeGuard) - expect(guards[0]).toEqual({ - allowedCaseTypes: indictmentCases, - }) - expect(new guards[1]()).toBeInstanceOf(DefendantExistsGuard) - }) -}) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/internalDefendantController/internalDefendantControllerGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/internalDefendantController/internalDefendantControllerGuards.spec.ts index ea850b4407f1..ae0ad03605fb 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/internalDefendantController/internalDefendantControllerGuards.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/test/internalDefendantController/internalDefendantControllerGuards.spec.ts @@ -41,4 +41,16 @@ describe('InternalDefendantController - guards', () => { expect(guard).toBeInstanceOf(CaseExistsGuard) }) }) + + describe('Method level guards', () => { + it('should have DefendantExistsGuard on deliverDefendantToCourt method', () => { + const methodGuards = Reflect.getMetadata( + '__guards__', + InternalDefendantController.prototype.deliverDefendantToCourt, + ) + expect(methodGuards).toHaveLength(1) + const guard = new methodGuards[0]() + expect(guard).toBeInstanceOf(DefendantExistsGuard) + }) + }) }) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/internalDefendantController/updateDefendant.spec.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/internalDefendantController/updateDefendant.spec.ts deleted file mode 100644 index 1d6c8e73cf85..000000000000 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/internalDefendantController/updateDefendant.spec.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { uuid } from 'uuidv4' - -import { createTestingDefendantModule } from '../createTestingDefendantModule' - -import { Case } from '../../../case' -import { InternalUpdateDefendantDto } from '../../dto/internalUpdateDefendant.dto' -import { Defendant } from '../../models/defendant.model' - -interface Then { - result: Defendant - error: Error -} - -type GivenWhenThen = () => Promise - -describe('InternalDefendantController - Update defendant', () => { - const caseId = uuid() - const defendantId = uuid() - const defendantNationalId = uuid() - const update = { somefield: 'somevalue' } as InternalUpdateDefendantDto - const updatedDefendant = { - id: defendantId, - nationalId: defendantNationalId, - ...update, - } - let mockDefendantModel: typeof Defendant - let givenWhenThen: GivenWhenThen - - beforeEach(async () => { - const defendant = { - id: defendantId, - nationalId: defendantNationalId, - } as Defendant - const { defendantModel, internalDefendantController } = - await createTestingDefendantModule() - - mockDefendantModel = defendantModel - - const mockUpdate = mockDefendantModel.update as jest.Mock - mockUpdate.mockRejectedValue(new Error('Some error')) - - givenWhenThen = async () => { - const then = {} as Then - - await internalDefendantController - .updateDefendant( - caseId, - defendantNationalId, - { id: caseId, defendants: [defendant] } as Case, - defendant, - update, - ) - .then((result) => (then.result = result)) - .catch((error) => (then.error = error)) - - return then - } - }) - - describe('update defendant', () => { - let then: Then - - beforeEach(async () => { - const mockUpdate = mockDefendantModel.update as jest.Mock - mockUpdate.mockResolvedValue([1, [updatedDefendant]]) - - then = await givenWhenThen() - }) - it('should update the defendant', async () => { - expect(mockDefendantModel.update).toHaveBeenCalledWith( - { ...update, isDefenderChoiceConfirmed: false }, - { where: { id: defendantId, caseId }, returning: true }, - ) - expect(then.result).toEqual(updatedDefendant) - }) - }) -}) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/internalDefendantController/updateDefendantGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/internalDefendantController/updateDefendantGuards.spec.ts deleted file mode 100644 index a11f2f6b280b..000000000000 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/internalDefendantController/updateDefendantGuards.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { indictmentCases } from '@island.is/judicial-system/types' - -import { CaseTypeGuard } from '../../../case' -import { DefendantNationalIdExistsGuard } from '../../guards/defendantNationalIdExists.guard' -import { InternalDefendantController } from '../../internalDefendant.controller' - -describe('InternalDefendantController - Update defendant guards', () => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let guards: any[] - - beforeEach(() => { - guards = Reflect.getMetadata( - '__guards__', - InternalDefendantController.prototype.updateDefendant, - ) - }) - - it('should have the right guard configuration', () => { - expect(guards).toHaveLength(2) - expect(guards[0]).toBeInstanceOf(CaseTypeGuard) - expect(guards[0]).toEqual({ - allowedCaseTypes: indictmentCases, - }) - expect(new guards[1]()).toBeInstanceOf(DefendantNationalIdExistsGuard) - }) -}) diff --git a/apps/judicial-system/backend/src/app/modules/file/dto/createFile.dto.ts b/apps/judicial-system/backend/src/app/modules/file/dto/createFile.dto.ts index b17cfaaacb6c..de31caf5e46b 100644 --- a/apps/judicial-system/backend/src/app/modules/file/dto/createFile.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/file/dto/createFile.dto.ts @@ -6,7 +6,6 @@ import { IsNumber, IsOptional, IsString, - MaxLength, } from 'class-validator' import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger' @@ -15,7 +14,6 @@ import { CaseFileCategory } from '@island.is/judicial-system/types' export class CreateFileDto { @IsNotEmpty() - @MaxLength(255) @IsString() @ApiProperty({ type: String }) readonly type!: string @@ -26,7 +24,6 @@ export class CreateFileDto { readonly category?: CaseFileCategory @IsNotEmpty() - @MaxLength(255) @IsString() @ApiProperty({ type: String }) readonly key!: string @@ -37,7 +34,6 @@ export class CreateFileDto { readonly size!: number @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly policeCaseNumber?: string @@ -59,13 +55,11 @@ export class CreateFileDto { readonly displayDate?: Date @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly policeFileId?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly userGeneratedFilename?: string diff --git a/apps/judicial-system/backend/src/app/modules/file/dto/createPresignedPost.dto.ts b/apps/judicial-system/backend/src/app/modules/file/dto/createPresignedPost.dto.ts index e9bdeb9d4a7a..1dc58cf5f6db 100644 --- a/apps/judicial-system/backend/src/app/modules/file/dto/createPresignedPost.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/file/dto/createPresignedPost.dto.ts @@ -1,16 +1,14 @@ -import { IsNotEmpty, IsString, MaxLength } from 'class-validator' +import { IsNotEmpty, IsString } from 'class-validator' import { ApiProperty } from '@nestjs/swagger' export class CreatePresignedPostDto { @IsNotEmpty() - @MaxLength(255) @IsString() @ApiProperty({ type: String }) readonly fileName!: string @IsNotEmpty() - @MaxLength(255) @IsString() @ApiProperty({ type: String }) readonly type!: string diff --git a/apps/judicial-system/backend/src/app/modules/file/dto/updateFile.dto.ts b/apps/judicial-system/backend/src/app/modules/file/dto/updateFile.dto.ts index a769eeda2ddb..f645b71bf9bb 100644 --- a/apps/judicial-system/backend/src/app/modules/file/dto/updateFile.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/file/dto/updateFile.dto.ts @@ -7,7 +7,6 @@ import { IsOptional, IsString, IsUUID, - MaxLength, Min, ValidateIf, ValidateNested, @@ -22,7 +21,6 @@ export class UpdateFileDto { readonly id!: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly userGeneratedFilename?: string 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 8e0a7940bc95..9982d5c839c3 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 @@ -34,6 +34,7 @@ import { districtCourtAssistantRule, districtCourtJudgeRule, districtCourtRegistrarRule, + prisonSystemStaffRule, prosecutorRepresentativeRule, prosecutorRule, publicProsecutorStaffRule, 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 b1226cdc54e0..103ce2051ea3 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 @@ -4,7 +4,6 @@ import { IsObject, IsOptional, IsString, - MaxLength, } from 'class-validator' import { ApiPropertyOptional } from '@nestjs/swagger' @@ -14,13 +13,11 @@ import { IndictmentCountOffense } from '@island.is/judicial-system/types' export class UpdateIndictmentCountDto { @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly policeCaseNumber?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly vehicleRegistrationNumber?: string diff --git a/apps/judicial-system/backend/src/app/modules/notification/baseNotification.service.ts b/apps/judicial-system/backend/src/app/modules/notification/baseNotification.service.ts index be1034a1dcb1..b06c0242d66f 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/baseNotification.service.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/baseNotification.service.ts @@ -9,7 +9,7 @@ import type { ConfigType } from '@island.is/nest/config' import { NotificationType } from '@island.is/judicial-system/types' -import { filterWhitelistEmails, stripHtmlTags } from '../../formatters' +import { stripHtmlTags } from '../../formatters' import { notifications } from '../../messages' import { EventService } from '../event' import { DeliverResponse } from './models/deliver.response' @@ -53,29 +53,6 @@ export abstract class BaseNotificationService { }) } - private async handleWhitelist(recipients: string[]): Promise { - const whitelist = this.formatMessage(notifications.emailWhitelist) - const whitelistDomains = this.formatMessage( - notifications.emailWhitelistDomains, - ) - - const whitelistedEmails = filterWhitelistEmails( - recipients, - whitelistDomains, - whitelist, - ) - - if (whitelistedEmails.length === 0) { - this.logger.warn('No whitelisted emails found in recipients') - } - - if (whitelistedEmails.length !== recipients?.length) { - this.logger.warn('Some emails missing from whitelist') - } - - return whitelistedEmails - } - protected async sendEmail( subject: string, html: string, @@ -87,11 +64,7 @@ export abstract class BaseNotificationService { try { // This is to handle a comma separated list of emails // We use the first one as the main recipient and the rest as CC - let recipients = recipientEmail ? recipientEmail.split(',') : undefined - - if (!this.config.production && recipients) { - recipients = await this.handleWhitelist(recipients) - } + const recipients = recipientEmail ? recipientEmail.split(',') : undefined html = html.match(/ { + const { subject, body } = formatAdvocateAssignedEmailNotification( + this.formatMessage, + theCase, + advocateType, + advocateNationalId && + formatDefenderRoute(this.config.clientUrl, theCase.type, theCase.id), + ) + + return this.sendEmail( + subject, + body, + advocateName, + advocateEmail, + undefined, + Boolean(advocateNationalId) === false, + ) + } + private async sendAdvocateAssignedNotifications( theCase: Case, ): Promise { const promises: Promise[] = [] - if (DateLog.arraignmentDate(theCase.dateLogs)?.date) { + if (isIndictmentCase(theCase.type)) { + if (theCase.civilClaimants) { + for (const civilClaimant of theCase.civilClaimants) { + const { + spokespersonEmail, + spokespersonIsLawyer, + spokespersonName, + spokespersonNationalId, + hasSpokesperson, + } = civilClaimant + + const shouldSend = + hasSpokesperson && + this.shouldSendAdvocateAssignedNotification( + theCase, + spokespersonEmail, + ) + + if (shouldSend === true) { + promises.push( + this.sendAdvocateAssignedNotification( + theCase, + spokespersonIsLawyer + ? AdvocateType.LAWYER + : AdvocateType.LEGAL_RIGHTS_PROTECTOR, + spokespersonNationalId, + spokespersonName, + spokespersonEmail, + ), + ) + } + } + } + } else if (DateLog.arraignmentDate(theCase.dateLogs)?.date) { const shouldSend = this.shouldSendAdvocateAssignedNotification( theCase, theCase.defenderEmail, diff --git a/apps/judicial-system/backend/src/app/modules/notification/civilClaimantNotification.service.ts b/apps/judicial-system/backend/src/app/modules/notification/civilClaimantNotification.service.ts deleted file mode 100644 index c80250801348..000000000000 --- a/apps/judicial-system/backend/src/app/modules/notification/civilClaimantNotification.service.ts +++ /dev/null @@ -1,171 +0,0 @@ -import { MessageDescriptor } from '@formatjs/intl' - -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 { DEFENDER_INDICTMENT_ROUTE } from '@island.is/judicial-system/consts' -import { capitalize } from '@island.is/judicial-system/formatters' -import { CivilClaimantNotificationType } from '@island.is/judicial-system/types' - -import { Case } from '../case' -import { CivilClaimant } from '../defendant' -import { EventService } from '../event' -import { DeliverResponse } from './models/deliver.response' -import { Notification, Recipient } from './models/notification.model' -import { BaseNotificationService } from './baseNotification.service' -import { strings } from './civilClaimantNotification.strings' -import { notificationModuleConfig } from './notification.config' - -@Injectable() -export class CivilClaimantNotificationService 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( - civilClaimant: CivilClaimant, - theCase: Case, - notificationType: CivilClaimantNotificationType, - subject: MessageDescriptor, - body: MessageDescriptor, - ) { - const courtName = capitalize(theCase.court?.name) - const courtCaseNumber = theCase.courtCaseNumber - const spokespersonHasAccessToRVG = !!civilClaimant.spokespersonNationalId - - const formattedSubject = this.formatMessage(subject, { - courtName, - courtCaseNumber, - }) - - const formattedBody = this.formatMessage(body, { - courtName, - courtCaseNumber, - spokespersonHasAccessToRVG, - spokespersonIsLawyer: civilClaimant.spokespersonIsLawyer, - linkStart: ``, - linkEnd: '', - }) - const promises: Promise[] = [] - - if (civilClaimant.isSpokespersonConfirmed) { - promises.push( - this.sendEmail( - formattedSubject, - formattedBody, - civilClaimant.spokespersonName, - civilClaimant.spokespersonEmail, - undefined, - true, - ), - ) - } - - const recipients = await Promise.all(promises) - - return this.recordNotification(theCase.id, notificationType, recipients) - } - - private shouldSendSpokespersonAssignedNotification( - theCase: Case, - civilClaimant: CivilClaimant, - ): boolean { - if ( - !civilClaimant.spokespersonEmail || - !civilClaimant.isSpokespersonConfirmed - ) { - return false - } - - const hasSentNotificationBefore = this.hasReceivedNotification( - CivilClaimantNotificationType.SPOKESPERSON_ASSIGNED, - civilClaimant.spokespersonEmail, - theCase.notifications, - ) - - if (!hasSentNotificationBefore) { - return true - } - - return false - } - - private async sendSpokespersonAssignedNotification( - civilClaimant: CivilClaimant, - theCase: Case, - ): Promise { - const shouldSend = this.shouldSendSpokespersonAssignedNotification( - theCase, - civilClaimant, - ) - - if (shouldSend) { - return this.sendEmails( - civilClaimant, - theCase, - CivilClaimantNotificationType.SPOKESPERSON_ASSIGNED, - strings.civilClaimantSpokespersonAssignedSubject, - strings.civilClaimantSpokespersonAssignedBody, - ) - } - - // Nothing should be sent so we return a successful response - return { delivered: true } - } - - private sendNotification( - notificationType: CivilClaimantNotificationType, - civilClaimant: CivilClaimant, - theCase: Case, - ): Promise { - switch (notificationType) { - case CivilClaimantNotificationType.SPOKESPERSON_ASSIGNED: - return this.sendSpokespersonAssignedNotification(civilClaimant, theCase) - default: - throw new InternalServerErrorException( - `Invalid notification type: ${notificationType}`, - ) - } - } - - async sendCivilClaimantNotification( - type: CivilClaimantNotificationType, - civilClaimant: CivilClaimant, - theCase: Case, - ): Promise { - await this.refreshFormatMessage() - - try { - return await this.sendNotification(type, civilClaimant, theCase) - } catch (error) { - this.logger.error('Failed to send notification', error) - - return { delivered: false } - } - } -} diff --git a/apps/judicial-system/backend/src/app/modules/notification/civilClaimantNotification.strings.ts b/apps/judicial-system/backend/src/app/modules/notification/civilClaimantNotification.strings.ts deleted file mode 100644 index d9beac015345..000000000000 --- a/apps/judicial-system/backend/src/app/modules/notification/civilClaimantNotification.strings.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { defineMessage } from '@formatjs/intl' - -export const strings = { - civilClaimantSpokespersonAssignedSubject: defineMessage({ - id: 'judicial.system.backend:civil_claimant_notifications.spokesperson_assigned_subject', - defaultMessage: '{courtName} - aðgangur að máli', - description: - 'Subject of the notification when a civil claimant spokesperson is assigned and confirmed', - }), - civilClaimantSpokespersonAssignedBody: defineMessage({ - id: 'judicial.system.backend:civil_claimant_notifications.indictment_assigned_body', - defaultMessage: - '{courtName} hefur skráð þig {spokespersonIsLawyer, select, true {lögmann einkaréttarkröfuhafa} other {réttargæslumann einkaréttarkröfuhafa}} í máli {courtCaseNumber}.

{spokespersonHasAccessToRVG, select, true {Sjá nánar á {linkStart}yfirlitssíðu málsins í Réttarvörslugátt{linkEnd}} other {Þú getur nálgast málið hjá dómstólnum.}}.', - description: - 'Body of the notification when a civil claimant spokesperson is assigned and confirmed', - }), -} diff --git a/apps/judicial-system/backend/src/app/modules/notification/defendantNotification.service.ts b/apps/judicial-system/backend/src/app/modules/notification/defendantNotification.service.ts index d3f6a0f86f2b..5249a63c693d 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/defendantNotification.service.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/defendantNotification.service.ts @@ -1,3 +1,5 @@ +import { MessageDescriptor } from '@formatjs/intl' + import { Inject, Injectable, @@ -10,10 +12,7 @@ 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 { - DEFENDER_INDICTMENT_ROUTE, - ROUTE_HANDLER_ROUTE, -} from '@island.is/judicial-system/consts' +import { DEFENDER_INDICTMENT_ROUTE } from '@island.is/judicial-system/consts' import { DefendantNotificationType, isIndictmentCase, @@ -51,27 +50,39 @@ export class DefendantNotificationService extends BaseNotificationService { } private async sendEmails( + defendant: Defendant, theCase: Case, notificationType: DefendantNotificationType, - subject: string, - body: string, - to: { name?: string; email?: string }[], + subject: MessageDescriptor, + body: MessageDescriptor, ) { + const courtName = theCase.court?.name + const defenderHasAccessToRVG = !!defendant.defenderNationalId + + const formattedSubject = this.formatMessage(subject, { + courtName, + }) + + const formattedBody = this.formatMessage(body, { + courtName, + courtCaseNumber: theCase.courtCaseNumber, + defenderHasAccessToRVG, + linkStart: ``, + linkEnd: '', + }) 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, - ), - ) - } + if (defendant.defenderEmail) { + promises.push( + this.sendEmail( + formattedSubject, + formattedBody, + defendant.defenderName, + defendant.defenderEmail, + undefined, + true, + ), + ) } const recipients = await Promise.all(promises) @@ -79,43 +90,6 @@ export class DefendantNotificationService extends BaseNotificationService { return this.recordNotification(theCase.id, notificationType, recipients) } - private sendDefendantSelectedDefenderNotification( - theCase: Case, - ): Promise { - const formattedSubject = this.formatMessage( - strings.defendantSelectedDefenderSubject, - { - courtCaseNumber: theCase.courtCaseNumber, - }, - ) - - const formattedBody = this.formatMessage( - strings.defendantSelectedDefenderBody, - { - courtCaseNumber: theCase.courtCaseNumber, - linkStart: ``, - linkEnd: '', - }, - ) - - return this.sendEmails( - theCase, - DefendantNotificationType.DEFENDANT_SELECTED_DEFENDER, - formattedSubject, - formattedBody, - [ - { - name: theCase.judge?.name, - email: theCase.judge?.email, - }, - { - name: theCase.registrar?.name, - email: theCase.registrar?.email, - }, - ], - ) - } - private shouldSendDefenderAssignedNotification( theCase: Case, defendant: Defendant, @@ -139,8 +113,8 @@ export class DefendantNotificationService extends BaseNotificationService { } private async sendDefenderAssignedNotification( - theCase: Case, defendant: Defendant, + theCase: Case, ): Promise { const shouldSend = this.shouldSendDefenderAssignedNotification( theCase, @@ -148,30 +122,12 @@ export class DefendantNotificationService extends BaseNotificationService { ) if (shouldSend) { - const courtName = theCase.court?.name - const defenderHasAccessToRVG = !!defendant.defenderNationalId - - const formattedSubject = this.formatMessage( - strings.defenderAssignedSubject, - { - courtName, - }, - ) - - const formattedBody = this.formatMessage(strings.defenderAssignedBody, { - courtName, - courtCaseNumber: theCase.courtCaseNumber, - defenderHasAccessToRVG, - linkStart: ``, - linkEnd: '', - }) - return this.sendEmails( + defendant, theCase, DefendantNotificationType.DEFENDER_ASSIGNED, - formattedSubject, - formattedBody, - [{ name: defendant.defenderName, email: defendant.defenderEmail }], + strings.defenderAssignedSubject, + strings.defenderAssignedBody, ) } @@ -181,14 +137,12 @@ export class DefendantNotificationService extends BaseNotificationService { private sendNotification( notificationType: DefendantNotificationType, - theCase: Case, defendant: Defendant, + theCase: Case, ): Promise { switch (notificationType) { - case DefendantNotificationType.DEFENDANT_SELECTED_DEFENDER: - return this.sendDefendantSelectedDefenderNotification(theCase) case DefendantNotificationType.DEFENDER_ASSIGNED: - return this.sendDefenderAssignedNotification(theCase, defendant) + return this.sendDefenderAssignedNotification(defendant, theCase) default: throw new InternalServerErrorException( `Invalid notification type: ${notificationType}`, @@ -198,12 +152,13 @@ export class DefendantNotificationService extends BaseNotificationService { async sendDefendantNotification( type: DefendantNotificationType, - theCase: Case, defendant: Defendant, + theCase: Case, ): Promise { await this.refreshFormatMessage() + try { - return await this.sendNotification(type, theCase, defendant) + return await this.sendNotification(type, defendant, theCase) } catch (error) { this.logger.error('Failed to send notification', error) diff --git a/apps/judicial-system/backend/src/app/modules/notification/defendantNotification.strings.ts b/apps/judicial-system/backend/src/app/modules/notification/defendantNotification.strings.ts index 0bd021c0dc53..eeeebdae43b3 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/defendantNotification.strings.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/defendantNotification.strings.ts @@ -1,19 +1,6 @@ import { defineMessage } from '@formatjs/intl' export const strings = { - defendantSelectedDefenderSubject: defineMessage({ - id: 'judicial.system.backend:defendant_notifications.defendant_selected_defender_subject', - defaultMessage: 'Val á verjanda í máli {courtCaseNumber}', - description: - 'Subject of the notification sent when the defendant defender choise in an indictment has changed', - }), - defendantSelectedDefenderBody: defineMessage({ - id: 'judicial.system.backend:defendant_notifications.defendant_selected_defender_body', - defaultMessage: - 'Verjandi hefur verið valinn í máli {courtCaseNumber}.

Sjá nánar á {linkStart}yfirlitssíðu málsins í Réttarvörslugátt{linkEnd}.', - description: - 'Body of the notification sent when the defendant defender choise in an indictment has changed', - }), defenderAssignedSubject: defineMessage({ id: 'judicial.system.backend:defendant_notifications.indictment_defender_assigned_subject', defaultMessage: '{courtName} - aðgangur að máli', diff --git a/apps/judicial-system/backend/src/app/modules/notification/dto/civilClaimantNotification.dto.ts b/apps/judicial-system/backend/src/app/modules/notification/dto/civilClaimantNotification.dto.ts deleted file mode 100644 index 5557dbdfe354..000000000000 --- a/apps/judicial-system/backend/src/app/modules/notification/dto/civilClaimantNotification.dto.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { IsEnum, IsNotEmpty } from 'class-validator' - -import { ApiProperty } from '@nestjs/swagger' - -import { CivilClaimantNotificationType } from '@island.is/judicial-system/types' - -export class CivilClaimantNotificationDto { - @IsNotEmpty() - @IsEnum(CivilClaimantNotificationType) - @ApiProperty({ enum: CivilClaimantNotificationType }) - readonly type!: CivilClaimantNotificationType -} 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 c0e2ca1a0003..f5def84647e3 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 @@ -18,24 +18,15 @@ import { } from '@island.is/judicial-system/message' import { Case, CaseHasExistedGuard, CurrentCase } from '../case' -import { - CivilClaimant, - CivilClaimantExistsGuard, - CurrentCivilClaimant, - CurrentDefendant, - Defendant, - DefendantExistsGuard, -} from '../defendant' +import { CurrentDefendant, Defendant, DefendantExistsGuard } from '../defendant' import { SubpoenaExistsGuard } from '../subpoena' import { CaseNotificationDto } from './dto/caseNotification.dto' -import { CivilClaimantNotificationDto } from './dto/civilClaimantNotification.dto' import { DefendantNotificationDto } from './dto/defendantNotification.dto' import { InstitutionNotificationDto } from './dto/institutionNotification.dto' import { NotificationDispatchDto } from './dto/notificationDispatch.dto' import { SubpoenaNotificationDto } from './dto/subpoenaNotification.dto' import { DeliverResponse } from './models/deliver.response' import { CaseNotificationService } from './caseNotification.service' -import { CivilClaimantNotificationService } from './civilClaimantNotification.service' import { DefendantNotificationService } from './defendantNotification.service' import { InstitutionNotificationService } from './institutionNotification.service' import { NotificationDispatchService } from './notificationDispatch.service' @@ -51,7 +42,6 @@ export class InternalNotificationController { private readonly institutionNotificationService: InstitutionNotificationService, private readonly subpoenaNotificationService: SubpoenaNotificationService, private readonly defendantNotificationService: DefendantNotificationService, - private readonly civilClaimantNotificationService: CivilClaimantNotificationService, @Inject(LOGGER_PROVIDER) private readonly logger: Logger, ) {} @@ -128,35 +118,7 @@ export class InternalNotificationController { return this.defendantNotificationService.sendDefendantNotification( notificationDto.type, - theCase, defendant, - ) - } - - @Post( - `case/:caseId/${ - messageEndpoint[MessageType.CIVIL_CLAIMANT_NOTIFICATION] - }/:civilClaimantId`, - ) - @UseGuards(CaseHasExistedGuard, CivilClaimantExistsGuard) - @ApiCreatedResponse({ - type: DeliverResponse, - description: 'Sends civil claimant related notifications', - }) - sendCivilClaimantNotification( - @Param('caseId') caseId: string, - @Param('civilClaimantId') civilClaimantId: string, - @CurrentCase() theCase: Case, - @CurrentCivilClaimant() civilClaimant: CivilClaimant, - @Body() notificationDto: CivilClaimantNotificationDto, - ): Promise { - this.logger.debug( - `Sending ${notificationDto.type} notification for civil claimant ${civilClaimantId} and case ${caseId}`, - ) - - return this.civilClaimantNotificationService.sendCivilClaimantNotification( - notificationDto.type, - civilClaimant, theCase, ) } 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 14fb32c0a565..6dcb837ace05 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 @@ -18,7 +18,6 @@ import { } from '../index' import { Notification } from './models/notification.model' import { CaseNotificationService } from './caseNotification.service' -import { CivilClaimantNotificationService } from './civilClaimantNotification.service' import { DefendantNotificationService } from './defendantNotification.service' import { InstitutionNotificationService } from './institutionNotification.service' import { InternalNotificationController } from './internalNotification.controller' @@ -44,13 +43,12 @@ import { SubpoenaNotificationService } from './subpoenaNotification.service' ], controllers: [NotificationController, InternalNotificationController], providers: [ - CaseNotificationService, - CivilClaimantNotificationService, - DefendantNotificationService, - InstitutionNotificationService, NotificationService, + CaseNotificationService, NotificationDispatchService, + InstitutionNotificationService, SubpoenaNotificationService, + DefendantNotificationService, ], }) export class NotificationModule {} diff --git a/apps/judicial-system/backend/src/app/modules/notification/subpoenaNotification.service.ts b/apps/judicial-system/backend/src/app/modules/notification/subpoenaNotification.service.ts index 41a4630f0c92..13fad8b63ff5 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/subpoenaNotification.service.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/subpoenaNotification.service.ts @@ -56,16 +56,16 @@ export class SubpoenaNotificationService extends BaseNotificationService { courtCaseNumber: theCase.courtCaseNumber, }) - const formattedBody = this.formatMessage(body, { - courtCaseNumber: theCase.courtCaseNumber, - linkStart: ``, - linkEnd: '', - }) - const promises: Promise[] = [] for (const recipient of to) { if (recipient.email && recipient.name) { + const formattedBody = this.formatMessage(body, { + courtCaseNumber: theCase.courtCaseNumber, + linkStart: ``, + linkEnd: '', + }) + promises.push( this.sendEmail( formattedSubject, @@ -134,6 +134,27 @@ export class SubpoenaNotificationService extends BaseNotificationService { ) } + private sendDefendantSelectedDefenderNotification( + theCase: Case, + ): Promise { + return this.sendEmails( + theCase, + SubpoenaNotificationType.DEFENDANT_SELECTED_DEFENDER, + strings.defendantSelectedDefenderSubject, + strings.defendantSelectedDefenderBody, + [ + { + name: theCase.judge?.name, + email: theCase.judge?.email, + }, + { + name: theCase.registrar?.name, + email: theCase.registrar?.email, + }, + ], + ) + } + private sendNotification( type: SubpoenaNotificationType, theCase: Case, @@ -143,6 +164,8 @@ export class SubpoenaNotificationService extends BaseNotificationService { return this.sendServiceSuccessfulNotification(theCase) case SubpoenaNotificationType.SERVICE_FAILED: return this.sendServiceFailedNotification(theCase) + case SubpoenaNotificationType.DEFENDANT_SELECTED_DEFENDER: + return this.sendDefendantSelectedDefenderNotification(theCase) default: throw new InternalServerErrorException( `Invalid notification type: ${type}`, diff --git a/apps/judicial-system/backend/src/app/modules/notification/subpoenaNotification.strings.ts b/apps/judicial-system/backend/src/app/modules/notification/subpoenaNotification.strings.ts index 0b475708668f..8f08b6f475ae 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/subpoenaNotification.strings.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/subpoenaNotification.strings.ts @@ -27,4 +27,17 @@ export const strings = { description: 'Body of the notification sent when the serive status in an indictment has changed', }), + defendantSelectedDefenderSubject: defineMessage({ + id: 'judicial.system.backend:subpoena_notifications.defendant_selected_defender_subject', + defaultMessage: 'Val á verjanda í máli {courtCaseNumber}', + description: + 'Subject of the notification sent when the serive status in an indictment has changed', + }), + defendantSelectedDefenderBody: defineMessage({ + id: 'judicial.system.backend:subpoena_notifications.defendant_selected_defender_body', + defaultMessage: + 'Verjandi hefur verið valinn í máli {courtCaseNumber}.

Sjá nánar á {linkStart}yfirlitssíðu málsins í Réttarvörslugátt{linkEnd}.', + description: + 'Body of the notification sent when the serive status in an indictment has changed', + }), } 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 fe4c450c5873..a269d181001b 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 @@ -4,7 +4,7 @@ import { uuid } from 'uuidv4' import { getModelToken } from '@nestjs/sequelize' import { Test } from '@nestjs/testing' -import { FormatMessage, IntlService } from '@island.is/cms-translations' +import { IntlService } from '@island.is/cms-translations' import { createTestIntl } from '@island.is/cms-translations/test' import { EmailService } from '@island.is/email-service' import { LOGGER_PROVIDER } from '@island.is/logging' @@ -25,7 +25,6 @@ import { eventModuleConfig, EventService } from '../../event' import { InstitutionService } from '../../institution' import { UserService } from '../../user' import { CaseNotificationService } from '../caseNotification.service' -import { CivilClaimantNotificationService } from '../civilClaimantNotification.service' import { DefendantNotificationService } from '../defendantNotification.service' import { InstitutionNotificationService } from '../institutionNotification.service' import { InternalNotificationController } from '../internalNotification.controller' @@ -37,30 +36,6 @@ import { NotificationDispatchService } from '../notificationDispatch.service' jest.mock('@island.is/judicial-system/message') -export const createTestUsers = ( - roles: string[], -): Record< - string, - { - id: string - name: string - email: string - mobile: string - nationalId: string - } -> => - roles.reduce((acc, role) => { - const id = uuid() - acc[role] = { - id: id, - name: `${role}-${id}`, - email: `${role}-${id}@omnitrix.is`, - mobile: id, - nationalId: '1234567890', - } - return acc - }, {} as Record) - const formatMessage = createTestIntl({ onError: jest.fn(), locale: 'is-IS', @@ -129,7 +104,6 @@ export const createTestingNotificationModule = async () => { NotificationDispatchService, InstitutionNotificationService, DefendantNotificationService, - CivilClaimantNotificationService, ], }) .useMocker((token) => { diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/civilClaimantNotification/sendSpokespersonAssignedNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/civilClaimantNotification/sendSpokespersonAssignedNotifications.spec.ts deleted file mode 100644 index 42bf7c2a0ea8..000000000000 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/civilClaimantNotification/sendSpokespersonAssignedNotifications.spec.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { uuid } from 'uuidv4' - -import { EmailService } from '@island.is/email-service' -import { ConfigType } from '@island.is/nest/config' - -import { DEFENDER_INDICTMENT_ROUTE } from '@island.is/judicial-system/consts' -import { - CaseType, - CivilClaimantNotificationType, -} from '@island.is/judicial-system/types' - -import { createTestingNotificationModule } from '../../createTestingNotificationModule' - -import { Case } from '../../../../case' -import { CivilClaimant } from '../../../../defendant' -import { CivilClaimantNotificationDto } from '../../../dto/civilClaimantNotification.dto' -import { DeliverResponse } from '../../../models/deliver.response' -import { Notification } from '../../../models/notification.model' -import { notificationModuleConfig } from '../../../notification.config' - -jest.mock('../../../../../factories') - -interface Then { - result: DeliverResponse - error: Error -} - -type GivenWhenThen = ( - caseId: string, - civilClaimantId: string, - theCase: Case, - civilClaimant: CivilClaimant, - notificationDto: CivilClaimantNotificationDto, -) => Promise - -describe('InternalNotificationController - Send spokesperson assigned notifications', () => { - const caseId = uuid() - const civilClaimantId = uuid() - const court = { name: 'Héraðsdómur Reykjavíkur' } as Case['court'] - - let mockEmailService: EmailService - let mockConfig: ConfigType - let mockNotificationModel: typeof Notification - let givenWhenThen: GivenWhenThen - - let civilClaimantNotificationDTO: CivilClaimantNotificationDto - - beforeEach(async () => { - const { - emailService, - notificationConfig, - internalNotificationController, - notificationModel, - } = await createTestingNotificationModule() - - civilClaimantNotificationDTO = { - type: CivilClaimantNotificationType.SPOKESPERSON_ASSIGNED, - } - - mockEmailService = emailService - mockConfig = notificationConfig - mockNotificationModel = notificationModel - - givenWhenThen = async ( - caseId: string, - civilClaimantId: string, - theCase: Case, - civilClaimant: CivilClaimant, - notificationDto: CivilClaimantNotificationDto, - ) => { - const then = {} as Then - - try { - then.result = - await internalNotificationController.sendCivilClaimantNotification( - caseId, - civilClaimantId, - theCase, - civilClaimant, - notificationDto, - ) - } catch (error) { - then.error = error as Error - } - - return then - } - }) - - describe.each([ - { isSpokespersonConfirmed: true, shouldSendEmail: true }, - { isSpokespersonConfirmed: false, shouldSendEmail: false }, - ])( - 'when sending a spokesperson assigned notification', - ({ isSpokespersonConfirmed, shouldSendEmail }) => { - const civilClaimant = { - id: civilClaimantId, - caseId, - isSpokespersonConfirmed, - spokespersonIsLawyer: true, - spokespersonNationalId: '1234567890', - spokespersonName: 'Ben 10', - spokespersonEmail: 'ben10@omnitrix.is', - } as CivilClaimant - - beforeEach(async () => { - await givenWhenThen( - caseId, - civilClaimantId, - { - id: caseId, - court, - courtCaseNumber: 'R-123-456', - type: CaseType.INDICTMENT, - civilClaimants: [civilClaimant], - hasCivilClaims: true, - } as Case, - civilClaimant, - civilClaimantNotificationDTO, - ) - }) - - test(`should ${ - shouldSendEmail ? '' : 'not ' - }send a spokesperson assigned notification`, async () => { - if (shouldSendEmail) { - expect(mockEmailService.sendEmail).toBeCalledTimes(1) - expect(mockEmailService.sendEmail).toBeCalledWith({ - from: { - name: mockConfig.email.fromName, - address: mockConfig.email.fromEmail, - }, - to: [ - { - name: civilClaimant.spokespersonName, - address: civilClaimant.spokespersonEmail, - }, - ], - replyTo: { - name: mockConfig.email.replyToName, - address: mockConfig.email.replyToEmail, - }, - attachments: undefined, - subject: `Héraðsdómur Reykjavíkur - aðgangur að máli`, - html: expect.stringContaining(DEFENDER_INDICTMENT_ROUTE), - text: expect.stringContaining( - `Héraðsdómur Reykjavíkur hefur skráð þig lögmann einkaréttarkröfuhafa í máli R-123-456`, - ), - }) - expect(mockNotificationModel.create).toHaveBeenCalledTimes(1) - expect(mockNotificationModel.create).toHaveBeenCalledWith({ - caseId, - type: civilClaimantNotificationDTO.type, - recipients: [ - { - address: civilClaimant.spokespersonEmail, - success: shouldSendEmail, - }, - ], - }) - } else { - expect(mockEmailService.sendEmail).not.toBeCalled() - } - }) - }, - ) -}) diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/defendantNotification/sendDefenderAssignedNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/defendantNotification/sendDefenderAssignedNotifications.spec.ts index 5d048acbad6c..41403f0ea3f1 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/defendantNotification/sendDefenderAssignedNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/defendantNotification/sendDefenderAssignedNotifications.spec.ts @@ -9,10 +9,7 @@ import { DefendantNotificationType, } from '@island.is/judicial-system/types' -import { - createTestingNotificationModule, - createTestUsers, -} from '../../createTestingNotificationModule' +import { createTestingNotificationModule } from '../../createTestingNotificationModule' import { Case } from '../../../../case' import { Defendant } from '../../../../defendant' @@ -39,8 +36,6 @@ type GivenWhenThen = ( describe('InternalNotificationController - Send defender assigned notifications', () => { const caseId = uuid() const defendantId = uuid() - - const { defender } = createTestUsers(['defender']) const court = { name: 'Héraðsdómur Reykjavíkur' } as Case['court'] let mockEmailService: EmailService @@ -96,8 +91,8 @@ describe('InternalNotificationController - Send defender assigned notifications' const defendant = { id: defendantId, defenderNationalId: '1234567890', - defenderName: defender.name, - defenderEmail: defender.email, + defenderName: 'Defender Name', + defenderEmail: 'ben10@omnitrix.is', isDefenderChoiceConfirmed: true, } as Defendant @@ -163,7 +158,7 @@ describe('InternalNotificationController - Send defender assigned notifications' describe('when sending defender assigned notification to unconfirmed defender', () => { const defendant = { id: defendantId, - defenderEmail: defender.email, + defenderEmail: 'ben101@omnitrix.is', isDefenderChoiceConfirmed: false, } as Defendant diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAdvocateAssignedNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAdvocateAssignedNotifications.spec.ts index 2ff1ebb316c3..eff36227b407 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAdvocateAssignedNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAdvocateAssignedNotifications.spec.ts @@ -14,14 +14,12 @@ import { User, } from '@island.is/judicial-system/types' -import { - createTestingNotificationModule, - createTestUsers, -} from '../createTestingNotificationModule' +import { createTestingNotificationModule } from '../createTestingNotificationModule' import { Case } from '../../../case' import { CaseNotificationDto } from '../../dto/caseNotification.dto' import { DeliverResponse } from '../../models/deliver.response' +import { Notification } from '../../models/notification.model' import { notificationModuleConfig } from '../../notification.config' jest.mock('../../../../factories') @@ -39,19 +37,21 @@ type GivenWhenThen = ( describe('InternalNotificationController - Send defender assigned notifications', () => { const userId = uuid() - - const { defender } = createTestUsers(['defender']) - const court = { name: 'Héraðsdómur Reykjavíkur' } as Case['court'] let mockEmailService: EmailService let mockConfig: ConfigType + let mockNotificationModel: typeof Notification let givenWhenThen: GivenWhenThen let notificationDTO: CaseNotificationDto beforeEach(async () => { - const { emailService, notificationConfig, internalNotificationController } = - await createTestingNotificationModule() + const { + emailService, + notificationConfig, + notificationModel, + internalNotificationController, + } = await createTestingNotificationModule() notificationDTO = { user: { id: userId } as User, @@ -60,6 +60,7 @@ describe('InternalNotificationController - Send defender assigned notifications' mockEmailService = emailService mockConfig = notificationConfig + mockNotificationModel = notificationModel givenWhenThen = async ( caseId: string, @@ -82,6 +83,120 @@ describe('InternalNotificationController - Send defender assigned notifications' } }) + describe('when the case has civil claims and the advocate is a lawyer', () => { + const caseId = uuid() + const civilClaimant = { + hasSpokesperson: true, + spokespersonNationalId: '1234567890', + spokespersonEmail: 'recipient@gmail.com', + spokespersonName: 'John Doe', + spokespersonIsLawyer: true, + } + const theCase = { + id: caseId, + type: CaseType.INDICTMENT, + court, + courtCaseNumber: 'S-123/2022', + civilClaimants: [civilClaimant], + } as Case + + beforeEach(async () => { + await givenWhenThen(caseId, theCase, notificationDTO) + }) + + it('should send correct email', () => { + expect(mockEmailService.sendEmail).toHaveBeenCalledTimes(1) + expect(mockEmailService.sendEmail).toHaveBeenCalledWith({ + from: { + name: mockConfig.email.fromName, + address: mockConfig.email.fromEmail, + }, + to: [ + { + name: civilClaimant.spokespersonName, + address: civilClaimant.spokespersonEmail, + }, + ], + replyTo: { + name: mockConfig.email.replyToName, + address: mockConfig.email.replyToEmail, + }, + attachments: undefined, + subject: `Skráning í máli ${theCase.courtCaseNumber}`, + text: expect.anything(), // same as html but stripped html tags + html: `Héraðsdómur Reykjavíkur hefur skráð þig lögmann einkaréttarkröfuhafa í máli ${theCase.courtCaseNumber}.

Sjá nánar á yfirlitssíðu málsins í Réttarvörslugátt.`, + }) + }) + }) + + describe('when the case has civil claims and the advocate is a legal rights protector', () => { + const caseId = uuid() + const civilClaimant = { + hasSpokesperson: true, + spokespersonNationalId: '1234567890', + spokespersonEmail: 'recipient@gmail.com', + spokespersonName: 'John Doe', + spokespersonIsLawyer: false, + } + const theCase = { + id: caseId, + type: CaseType.INDICTMENT, + court, + courtCaseNumber: 'S-123/2022', + civilClaimants: [civilClaimant], + } as Case + + beforeEach(async () => { + await givenWhenThen(caseId, theCase, notificationDTO) + }) + + it('should send correct email', () => { + expect(mockEmailService.sendEmail).toHaveBeenCalledTimes(1) + expect(mockEmailService.sendEmail).toHaveBeenCalledWith({ + from: { + name: mockConfig.email.fromName, + address: mockConfig.email.fromEmail, + }, + to: [ + { + name: civilClaimant.spokespersonName, + address: civilClaimant.spokespersonEmail, + }, + ], + replyTo: { + name: mockConfig.email.replyToName, + address: mockConfig.email.replyToEmail, + }, + attachments: undefined, + subject: `Skráning í máli ${theCase.courtCaseNumber}`, + text: expect.anything(), // same as html but stripped html tags + html: `Héraðsdómur Reykjavíkur hefur skráð þig réttargæslumann einkaréttarkröfuhafa í máli ${theCase.courtCaseNumber}.

Sjá nánar á yfirlitssíðu málsins í Réttarvörslugátt.`, + }) + }) + }) + + describe('when the case has civil claims and civil claimant does not have representation', () => { + const caseId = uuid() + const civilClaimant = { + hasSpokesperson: false, + } + const theCase = { + id: caseId, + type: CaseType.INDICTMENT, + court, + courtCaseNumber: 'S-123/2022', + civilClaimants: [civilClaimant], + } as Case + + beforeEach(async () => { + await givenWhenThen(caseId, theCase, notificationDTO) + }) + + it('should send correct email', () => { + expect(mockEmailService.sendEmail).not.toHaveBeenCalled() + }) + }) + describe('when sending assigned defender notifications in a restriction case', () => { const caseId = uuid() const theCase = { @@ -89,8 +204,8 @@ describe('InternalNotificationController - Send defender assigned notifications' type: CaseType.ADMISSION_TO_FACILITY, court, courtCaseNumber: 'R-123/2022', - defenderEmail: defender.email, - defenderName: defender.name, + defenderEmail: 'recipient@gmail.com', + defenderName: 'John Doe', defenderNationalId: '1234567890', dateLogs: [{ date: new Date(), dateType: DateType.ARRAIGNMENT_DATE }], } as Case @@ -131,8 +246,8 @@ describe('InternalNotificationController - Send defender assigned notifications' type: CaseType.ADMISSION_TO_FACILITY, court, courtCaseNumber: 'R-123/2022', - defenderEmail: defender.email, - defenderName: defender.name, + defenderEmail: 'recipient@gmail.com', + defenderName: 'John Doe', dateLogs: [{ date: new Date(), dateType: DateType.ARRAIGNMENT_DATE }], } as Case @@ -172,8 +287,8 @@ describe('InternalNotificationController - Send defender assigned notifications' type: CaseType.PHONE_TAPPING, court, courtCaseNumber: 'R-123/2022', - defenderEmail: defender.email, - defenderName: defender.name, + defenderEmail: 'recipient@gmail.com', + defenderName: 'John Doe', dateLogs: [{ date: new Date(), dateType: DateType.ARRAIGNMENT_DATE }], } as Case diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealCompletedNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealCompletedNotifications.spec.ts index df4807508115..bc9c0a684778 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealCompletedNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealCompletedNotifications.spec.ts @@ -12,10 +12,7 @@ import { User, } from '@island.is/judicial-system/types' -import { - createTestingNotificationModule, - createTestUsers, -} from '../createTestingNotificationModule' +import { createTestingNotificationModule } from '../createTestingNotificationModule' import { Case } from '../../../case' import { DeliverResponse } from '../../models/deliver.response' @@ -32,24 +29,25 @@ type GivenWhenThen = ( ) => Promise describe('InternalNotificationController - Send appeal completed notifications', () => { - const { prosecutor, defender, judge, courtOfAppeals } = createTestUsers([ - 'prosecutor', - 'defender', - 'judge', - 'courtOfAppeals', - ]) const userId = uuid() const caseId = uuid() + const prosecutorName = uuid() + const prosecutorEmail = uuid() + const defenderName = uuid() + const defenderEmail = uuid() + const judgeName = uuid() + const judgeEmail = uuid() const courtCaseNumber = uuid() const appealCaseNumber = uuid() const courtId = uuid() + const courtOfAppealsEmail = uuid() let mockEmailService: EmailService let mockConfig: ConfigType let givenWhenThen: GivenWhenThen beforeEach(async () => { - process.env.COURTS_EMAILS = `{"4676f08b-aab4-4b4f-a366-697540788088":"${courtOfAppeals.email}"}` + process.env.COURTS_EMAILS = `{"4676f08b-aab4-4b4f-a366-697540788088":"${courtOfAppealsEmail}"}` const { emailService, notificationConfig, internalNotificationController } = await createTestingNotificationModule() @@ -73,12 +71,12 @@ describe('InternalNotificationController - Send appeal completed notifications', decision: CaseDecision.ACCEPTING, appealRulingDecision: appealRulingDecision ?? CaseAppealRulingDecision.ACCEPTING, - prosecutor: { name: prosecutor.name, email: prosecutor.email }, - judge: { name: judge.name, email: judge.email }, + prosecutor: { name: prosecutorName, email: prosecutorEmail }, + judge: { name: judgeName, email: judgeEmail }, court: { name: 'Héraðsdómur Reykjavíkur' }, defenderNationalId, - defenderName: defender.name, - defenderEmail: defender.email, + defenderName: defenderName, + defenderEmail: defenderEmail, courtCaseNumber, appealCaseNumber, courtId: courtId, @@ -104,14 +102,14 @@ describe('InternalNotificationController - Send appeal completed notifications', it('should send notifications', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: judge.name, address: judge.email }], + to: [{ name: judgeName, address: judgeEmail }], subject: `Úrskurður í landsréttarmáli ${appealCaseNumber} (${courtCaseNumber})`, html: `Landsréttur hefur úrskurðað í máli ${appealCaseNumber} (héraðsdómsmál nr. ${courtCaseNumber}). Niðurstaða Landsréttar: Staðfest. Hægt er að nálgast gögn málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), ) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: prosecutor.name, address: prosecutor.email }], + to: [{ name: prosecutorName, address: prosecutorEmail }], subject: `Úrskurður í landsréttarmáli ${appealCaseNumber} (${courtCaseNumber})`, html: `Landsréttur hefur úrskurðað í máli ${appealCaseNumber} (héraðsdómsmál nr. ${courtCaseNumber}). Niðurstaða Landsréttar: Staðfest. Hægt er að nálgast gögn málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), @@ -142,7 +140,7 @@ describe('InternalNotificationController - Send appeal completed notifications', ) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: defender.name, address: defender.email }], + to: [{ name: defenderName, address: defenderEmail }], subject: `Úrskurður í landsréttarmáli ${appealCaseNumber} (${courtCaseNumber})`, html: `Landsréttur hefur úrskurðað í máli ${appealCaseNumber} (héraðsdómsmál nr. ${courtCaseNumber}). Niðurstaða Landsréttar: Staðfest. Hægt er að nálgast gögn málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), @@ -161,7 +159,7 @@ describe('InternalNotificationController - Send appeal completed notifications', it('should send notification without a link to defender', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: defender.name, address: defender.email }], + to: [{ name: defenderName, address: defenderEmail }], subject: `Úrskurður í landsréttarmáli ${appealCaseNumber} (${courtCaseNumber})`, html: `Landsréttur hefur úrskurðað í máli ${appealCaseNumber} (héraðsdómsmál nr. ${courtCaseNumber}). Niðurstaða Landsréttar: Staðfest. Hægt er að nálgast gögn málsins hjá Héraðsdómi Reykjavíkur ef þau hafa ekki þegar verið afhent.`, }), @@ -180,14 +178,14 @@ describe('InternalNotificationController - Send appeal completed notifications', it('should send notification about discontinuance', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: prosecutor.name, address: prosecutor.email }], + to: [{ name: prosecutorName, address: prosecutorEmail }], subject: `Niðurfelling máls ${appealCaseNumber} (${courtCaseNumber})`, html: `Landsréttur hefur móttekið afturköllun á kæru í máli ${courtCaseNumber}. Landsréttarmálið ${appealCaseNumber} hefur verið fellt niður. Hægt er að nálgast yfirlitssíðu málsins á rettarvorslugatt.island.is.`, }), ) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: defender.name, address: defender.email }], + to: [{ name: defenderName, address: defenderEmail }], subject: `Niðurfelling máls ${appealCaseNumber} (${courtCaseNumber})`, html: `Landsréttur hefur móttekið afturköllun á kæru í máli ${courtCaseNumber}. Landsréttarmálið ${appealCaseNumber} hefur verið fellt niður.`, }), diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealFilesUpdatedNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealFilesUpdatedNotifications.spec.ts index d50f5d2a5707..1cc6f4f9e31a 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealFilesUpdatedNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealFilesUpdatedNotifications.spec.ts @@ -8,10 +8,7 @@ import { UserRole, } from '@island.is/judicial-system/types' -import { - createTestingNotificationModule, - createTestUsers, -} from '../createTestingNotificationModule' +import { createTestingNotificationModule } from '../createTestingNotificationModule' import { Case } from '../../../case' import { DeliverResponse } from '../../models/deliver.response' @@ -24,17 +21,22 @@ interface Then { type GivenWhenThen = (defenderNationalId?: string) => Promise describe('InternalNotificationController - Send appeal case files updated notifications', () => { - const { assistant, judge1, judge2, judge3 } = createTestUsers([ - 'assistant', - 'judge1', - 'judge2', - 'judge3', - ]) const userId = uuid() const caseId = uuid() const courtCaseNumber = uuid() const appealCaseNumber = uuid() const receivedDate = new Date() + const assistantName = uuid() + const assistantEmail = uuid() + const judgeName1 = uuid() + const judgeEmail1 = uuid() + const judgeId1 = uuid() + const judgeName2 = uuid() + const judgeEmail2 = uuid() + const judgeId2 = uuid() + const judgeName3 = uuid() + const judgeEmail3 = uuid() + const judgeId3 = uuid() let mockEmailService: EmailService let givenWhenThen: GivenWhenThen @@ -57,26 +59,27 @@ describe('InternalNotificationController - Send appeal case files updated notifi appealCaseNumber, appealReceivedByCourtDate: receivedDate, appealAssistant: { - name: assistant.name, - email: assistant.email, + name: assistantName, + email: assistantEmail, role: UserRole.COURT_OF_APPEALS_ASSISTANT, }, appealJudge1: { - name: judge1.name, - email: judge1.email, - id: judge1.id, + name: judgeName1, + email: judgeEmail1, + id: judgeId1, role: UserRole.COURT_OF_APPEALS_JUDGE, }, + appealJudge1Id: judgeId1, appealJudge2: { - name: judge2.name, - email: judge2.email, - id: judge2.id, + name: judgeName2, + email: judgeEmail2, + id: judgeId2, role: UserRole.COURT_OF_APPEALS_JUDGE, }, appealJudge3: { - name: judge3.name, - email: judge3.email, - id: judge3.id, + name: judgeName3, + email: judgeEmail3, + id: judgeId3, role: UserRole.COURT_OF_APPEALS_JUDGE, }, } as Case, @@ -101,7 +104,7 @@ describe('InternalNotificationController - Send appeal case files updated notifi it('should send notification to the assigned court of appeal judges and assistant', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: assistant.name, address: assistant.email }], + to: [{ name: assistantName, address: assistantEmail }], subject: `Ný gögn í máli ${courtCaseNumber} (${appealCaseNumber})`, html: `Ný gögn hafa borist vegna kæru í máli ${courtCaseNumber} (Landsréttarmál nr. ${appealCaseNumber}). Hægt er að nálgast gögn málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), @@ -110,8 +113,8 @@ describe('InternalNotificationController - Send appeal case files updated notifi expect.objectContaining({ to: [ { - name: judge1.name, - address: judge1.email, + name: judgeName1, + address: judgeEmail1, }, ], subject: `Ný gögn í máli ${courtCaseNumber} (${appealCaseNumber})`, @@ -120,14 +123,14 @@ describe('InternalNotificationController - Send appeal case files updated notifi ) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: judge2.name, address: judge2.email }], + to: [{ name: judgeName2, address: judgeEmail2 }], subject: `Ný gögn í máli ${courtCaseNumber} (${appealCaseNumber})`, html: `Ný gögn hafa borist vegna kæru í máli ${courtCaseNumber} (Landsréttarmál nr. ${appealCaseNumber}). Hægt er að nálgast gögn málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), ) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: judge3.name, address: judge3.email }], + to: [{ name: judgeName3, address: judgeEmail3 }], subject: `Ný gögn í máli ${courtCaseNumber} (${appealCaseNumber})`, html: `Ný gögn hafa borist vegna kæru í máli ${courtCaseNumber} (Landsréttarmál nr. ${appealCaseNumber}). Hægt er að nálgast gögn málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealJudgesAssignedNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealJudgesAssignedNotifications.spec.ts index cf73dbe84fcd..b455d775f174 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealJudgesAssignedNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealJudgesAssignedNotifications.spec.ts @@ -8,10 +8,7 @@ import { UserRole, } from '@island.is/judicial-system/types' -import { - createTestingNotificationModule, - createTestUsers, -} from '../createTestingNotificationModule' +import { createTestingNotificationModule } from '../createTestingNotificationModule' import { Case } from '../../../case' import { DeliverResponse } from '../../models/deliver.response' @@ -24,16 +21,21 @@ interface Then { type GivenWhenThen = (defenderNationalId?: string) => Promise describe('InternalNotificationController - Send appeal judges assigned notifications', () => { - const { judge1, judge2, judge3, assistant } = createTestUsers([ - 'judge1', - 'judge2', - 'judge3', - 'assistant', - ]) const userId = uuid() const caseId = uuid() const appealCaseNumber = uuid() const receivedDate = new Date() + const assistantName = uuid() + const assistantEmail = uuid() + const judgeName1 = uuid() + const judgeEmail1 = uuid() + const judgeId1 = uuid() + const judgeName2 = uuid() + const judgeEmail2 = uuid() + const judgeId2 = uuid() + const judgeName3 = uuid() + const judgeEmail3 = uuid() + const judgeId3 = uuid() let mockEmailService: EmailService let givenWhenThen: GivenWhenThen @@ -55,27 +57,27 @@ describe('InternalNotificationController - Send appeal judges assigned notificat appealCaseNumber, appealReceivedByCourtDate: receivedDate, appealAssistant: { - name: assistant.name, - email: assistant.email, + name: assistantName, + email: assistantEmail, role: UserRole.COURT_OF_APPEALS_ASSISTANT, }, appealJudge1: { - name: judge1.name, - email: judge1.email, - id: judge1.id, + name: judgeName1, + email: judgeEmail1, + id: judgeId1, role: UserRole.COURT_OF_APPEALS_JUDGE, }, - appealJudge1Id: judge1.id, + appealJudge1Id: judgeId1, appealJudge2: { - name: judge2.name, - email: judge2.email, - id: judge2.id, + name: judgeName2, + email: judgeEmail2, + id: judgeId2, role: UserRole.COURT_OF_APPEALS_JUDGE, }, appealJudge3: { - name: judge3.name, - email: judge3.email, - id: judge3.id, + name: judgeName3, + email: judgeEmail3, + id: judgeId3, role: UserRole.COURT_OF_APPEALS_JUDGE, }, } as Case, @@ -100,17 +102,17 @@ describe('InternalNotificationController - Send appeal judges assigned notificat it('should send notification to the judge foreperson, the two other judges and the judges assistant', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: assistant.name, address: assistant.email }], + to: [{ name: assistantName, address: assistantEmail }], subject: `Úthlutun máls nr. ${appealCaseNumber}`, - html: `Landsréttur hefur skráð þig sem aðstoðarmann dómara í máli nr. ${appealCaseNumber}. Dómsformaður er ${judge1.name}. Þú getur nálgast yfirlit málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, + html: `Landsréttur hefur skráð þig sem aðstoðarmann dómara í máli nr. ${appealCaseNumber}. Dómsformaður er ${judgeName1}. Þú getur nálgast yfirlit málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), ) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ to: [ { - name: judge1.name, - address: judge1.email, + name: judgeName1, + address: judgeEmail1, }, ], subject: `Úthlutun máls nr. ${appealCaseNumber}`, @@ -119,16 +121,16 @@ describe('InternalNotificationController - Send appeal judges assigned notificat ) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: judge2.name, address: judge2.email }], + to: [{ name: judgeName2, address: judgeEmail2 }], subject: `Úthlutun máls nr. ${appealCaseNumber}`, - html: `Landsréttur hefur skráð þig sem dómara í máli nr. ${appealCaseNumber}. Dómsformaður er ${judge1.name}. Þú getur nálgast yfirlit málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, + html: `Landsréttur hefur skráð þig sem dómara í máli nr. ${appealCaseNumber}. Dómsformaður er ${judgeName1}. Þú getur nálgast yfirlit málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), ) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: judge3.name, address: judge3.email }], + to: [{ name: judgeName3, address: judgeEmail3 }], subject: `Úthlutun máls nr. ${appealCaseNumber}`, - html: `Landsréttur hefur skráð þig sem dómara í máli nr. ${appealCaseNumber}. Dómsformaður er ${judge1.name}. Þú getur nálgast yfirlit málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, + html: `Landsréttur hefur skráð þig sem dómara í máli nr. ${appealCaseNumber}. Dómsformaður er ${judgeName1}. Þú getur nálgast yfirlit málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), ) expect(then.result).toEqual({ delivered: true }) diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealReceivedByCourtNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealReceivedByCourtNotifications.spec.ts index 96a4f7fe0016..27a132aa27a7 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealReceivedByCourtNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealReceivedByCourtNotifications.spec.ts @@ -10,10 +10,7 @@ import { User, } from '@island.is/judicial-system/types' -import { - createTestingNotificationModule, - createTestUsers, -} from '../createTestingNotificationModule' +import { createTestingNotificationModule } from '../createTestingNotificationModule' import { Case } from '../../../case' import { DeliverResponse } from '../../models/deliver.response' @@ -26,14 +23,14 @@ interface Then { type GivenWhenThen = (defenderNationalId?: string) => Promise describe('InternalNotificationController - Send appeal received by court notifications', () => { - const { coa, defender, prosecutor } = createTestUsers([ - 'coa', - 'defender', - 'prosecutor', - ]) - + const courtOfAppealsEmail = uuid() const userId = uuid() const caseId = uuid() + const prosecutorName = uuid() + const prosecutorEmail = uuid() + const prosecutorMobileNumber = uuid() + const defenderName = uuid() + const defenderEmail = uuid() const courtCaseNumber = uuid() const receivedDate = new Date() @@ -43,7 +40,7 @@ describe('InternalNotificationController - Send appeal received by court notific let givenWhenThen: GivenWhenThen beforeEach(async () => { - process.env.COURTS_EMAILS = `{"4676f08b-aab4-4b4f-a366-697540788088":"${coa.email}"}` + process.env.COURTS_EMAILS = `{"4676f08b-aab4-4b4f-a366-697540788088":"${courtOfAppealsEmail}"}` const { emailService, smsService, internalNotificationController } = await createTestingNotificationModule() @@ -60,14 +57,14 @@ describe('InternalNotificationController - Send appeal received by court notific { id: caseId, prosecutor: { - name: prosecutor.name, - email: prosecutor.email, - mobileNumber: prosecutor.mobile, + name: prosecutorName, + email: prosecutorEmail, + mobileNumber: prosecutorMobileNumber, }, court: { name: 'Héraðsdómur Reykjavíkur' }, defenderNationalId, - defenderName: defender.name, - defenderEmail: defender.email, + defenderName: defenderName, + defenderEmail: defenderEmail, courtCaseNumber, appealReceivedByCourtDate: receivedDate, } as Case, @@ -95,7 +92,7 @@ describe('InternalNotificationController - Send appeal received by court notific to: [ { name: 'Landsréttur', - address: coa.email, + address: courtOfAppealsEmail, }, ], subject: `Upplýsingar vegna kæru í máli ${courtCaseNumber}`, @@ -107,7 +104,7 @@ describe('InternalNotificationController - Send appeal received by court notific ) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: prosecutor.name, address: prosecutor.email }], + to: [{ name: prosecutorName, address: prosecutorEmail }], subject: `Upplýsingar vegna kæru í máli ${courtCaseNumber}`, html: `Kæra í máli ${courtCaseNumber} hefur borist Landsrétti. Frestur til að skila greinargerð er til ${formatDate( getStatementDeadline(receivedDate), @@ -117,7 +114,7 @@ describe('InternalNotificationController - Send appeal received by court notific ) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: defender.name, address: defender.email }], + to: [{ name: defenderName, address: defenderEmail }], subject: `Upplýsingar vegna kæru í máli ${courtCaseNumber}`, html: `Kæra í máli ${courtCaseNumber} hefur borist Landsrétti. Frestur til að skila greinargerð er til ${formatDate( getStatementDeadline(receivedDate), @@ -130,7 +127,7 @@ describe('InternalNotificationController - Send appeal received by court notific it('should send sms notification to prosecutor', () => { expect(mockSmsService.sendSms).toHaveBeenCalledWith( - [prosecutor.mobile], + [prosecutorMobileNumber], `Kæra í máli ${courtCaseNumber} hefur borist Landsrétti. Frestur til að skila greinargerð er til ${formatDate( getStatementDeadline(receivedDate), 'PPPp', @@ -149,7 +146,7 @@ describe('InternalNotificationController - Send appeal received by court notific it('should send notification to prosecutor and defender', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: prosecutor.name, address: prosecutor.email }], + to: [{ name: prosecutorName, address: prosecutorEmail }], subject: `Upplýsingar vegna kæru í máli ${courtCaseNumber}`, html: `Kæra í máli ${courtCaseNumber} hefur borist Landsrétti. Frestur til að skila greinargerð er til ${formatDate( getStatementDeadline(receivedDate), @@ -159,7 +156,7 @@ describe('InternalNotificationController - Send appeal received by court notific ) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: defender.name, address: defender.email }], + to: [{ name: defenderName, address: defenderEmail }], subject: `Upplýsingar vegna kæru í máli ${courtCaseNumber}`, html: `Kæra í máli ${courtCaseNumber} hefur borist Landsrétti. Frestur til að skila greinargerð er til ${formatDate( getStatementDeadline(receivedDate), diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealStatementNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealStatementNotifications.spec.ts index a920284e6a9e..0f043505f796 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealStatementNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealStatementNotifications.spec.ts @@ -9,10 +9,7 @@ import { UserRole, } from '@island.is/judicial-system/types' -import { - createTestingNotificationModule, - createTestUsers, -} from '../createTestingNotificationModule' +import { createTestingNotificationModule } from '../createTestingNotificationModule' import { Case } from '../../../case' import { DeliverResponse } from '../../models/deliver.response' @@ -29,22 +26,22 @@ type GivenWhenThen = ( ) => Promise describe('InternalNotificationController - Send appeal statement notifications', () => { - const roles = [ - 'prosecutor', - 'defender', - 'assistant', - 'judge1', - 'judge2', - 'judge3', - ] - - const { prosecutor, defender, assistant, judge1, judge2, judge3 } = - createTestUsers(roles) - const caseId = uuid() + const prosecutorName = uuid() + const prosecutorEmail = uuid() + const defenderName = uuid() + const defenderEmail = uuid() const courtCaseNumber = uuid() const receivedDate = new Date() const appealCaseNumber = uuid() + const assistantName = uuid() + const assistantEmail = uuid() + const judgeName1 = uuid() + const judgeEmail1 = uuid() + const judgeName2 = uuid() + const judgeEmail2 = uuid() + const judgeName3 = uuid() + const judgeEmail3 = uuid() let mockEmailService: EmailService @@ -68,16 +65,16 @@ describe('InternalNotificationController - Send appeal statement notifications', caseId, { id: caseId, - prosecutor: { name: prosecutor.name, email: prosecutor.email }, + prosecutor: { name: prosecutorName, email: prosecutorEmail }, court: { name: 'Héraðsdómur Reykjavíkur' }, defenderNationalId, - defenderName: defender.name, - defenderEmail: defender.email, + defenderName: defenderName, + defenderEmail: defenderEmail, courtCaseNumber, appealReceivedByCourtDate: receivedDate, appealCaseNumber, - appealAssistant: { name: assistant.name, email: assistant.email }, - appealJudge1: { name: judge1.name, email: judge1.email }, + appealAssistant: { name: assistantName, email: assistantEmail }, + appealJudge1: { name: judgeName1, email: judgeEmail1 }, } as Case, { user, @@ -113,13 +110,13 @@ describe('InternalNotificationController - Send appeal statement notifications', }) it('should send notification to appeals court and defender', () => { - expectCourtEmail(assistant.name, assistant.email) - expectCourtEmail(judge1.name, judge1.email) - expectCourtEmail(judge2.name, judge2.email) - expectCourtEmail(judge3.name, judge3.email) + expectCourtEmail(assistantName, assistantEmail) + expectCourtEmail(judgeName1, judgeEmail1) + expectCourtEmail(judgeName2, judgeEmail2) + expectCourtEmail(judgeName3, judgeEmail3) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: defender.name, address: defender.email }], + to: [{ name: defenderName, address: defenderEmail }], subject: `Ný greinargerð í máli ${courtCaseNumber} (${appealCaseNumber})`, html: `Greinargerð hefur borist vegna kæru í máli ${courtCaseNumber} (Landsréttarmál nr. ${appealCaseNumber}). Hægt er að nálgast gögn málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), @@ -143,13 +140,13 @@ describe('InternalNotificationController - Send appeal statement notifications', }) it('should send notification to appeals court and defender', () => { - expectCourtEmail(assistant.name, assistant.email) - expectCourtEmail(judge1.name, judge1.email) - expectCourtEmail(judge2.name, judge2.email) - expectCourtEmail(judge3.name, judge3.email) + expectCourtEmail(assistantName, assistantEmail) + expectCourtEmail(judgeName1, judgeEmail1) + expectCourtEmail(judgeName2, judgeEmail2) + expectCourtEmail(judgeName3, judgeEmail3) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: defender.name, address: defender.email }], + to: [{ name: defenderName, address: defenderEmail }], subject: `Ný greinargerð í máli ${courtCaseNumber} (${appealCaseNumber})`, html: `Greinargerð hefur borist vegna kæru í máli ${courtCaseNumber} (Landsréttarmál nr. ${appealCaseNumber}). Hægt er að nálgast gögn málsins hjá Héraðsdómi Reykjavíkur ef þau hafa ekki þegar verið afhent.`, }), @@ -170,13 +167,13 @@ describe('InternalNotificationController - Send appeal statement notifications', }) it('should send notification to appeals court and prosecutor', () => { - expectCourtEmail(assistant.name, assistant.email) - expectCourtEmail(judge1.name, judge1.email) - expectCourtEmail(judge2.name, judge2.email) - expectCourtEmail(judge3.name, judge3.email) + expectCourtEmail(assistantName, assistantEmail) + expectCourtEmail(judgeName1, judgeEmail1) + expectCourtEmail(judgeName2, judgeEmail2) + expectCourtEmail(judgeName3, judgeEmail3) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: prosecutor.name, address: prosecutor.email }], + to: [{ name: prosecutorName, address: prosecutorEmail }], subject: `Ný greinargerð í máli ${courtCaseNumber} (${appealCaseNumber})`, html: `Greinargerð hefur borist vegna kæru í máli ${courtCaseNumber} (Landsréttarmál nr. ${appealCaseNumber}). Hægt er að nálgast gögn málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), @@ -201,7 +198,7 @@ describe('InternalNotificationController - Send appeal statement notifications', it('should send notification to defender', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: defender.name, address: defender.email }], + to: [{ name: defenderName, address: defenderEmail }], subject: `Ný greinargerð í máli ${courtCaseNumber}`, html: `Greinargerð hefur borist vegna kæru í máli ${courtCaseNumber}. Hægt er að nálgast gögn málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), @@ -223,7 +220,7 @@ describe('InternalNotificationController - Send appeal statement notifications', it('should send notification to defender', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: defender.name, address: defender.email }], + to: [{ name: defenderName, address: defenderEmail }], subject: `Ný greinargerð í máli ${courtCaseNumber}`, html: `Greinargerð hefur borist vegna kæru í máli ${courtCaseNumber}. Hægt er að nálgast gögn málsins hjá Héraðsdómi Reykjavíkur ef þau hafa ekki þegar verið afhent.`, }), @@ -242,7 +239,7 @@ describe('InternalNotificationController - Send appeal statement notifications', it('should send notification to prosecutor', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: prosecutor.name, address: prosecutor.email }], + to: [{ name: prosecutorName, address: prosecutorEmail }], subject: `Ný greinargerð í máli ${courtCaseNumber}`, html: `Greinargerð hefur borist vegna kæru í máli ${courtCaseNumber}. Hægt er að nálgast gögn málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealToCourtOfAppealsNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealToCourtOfAppealsNotifications.spec.ts index ae50c9e3c325..2119328928c1 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealToCourtOfAppealsNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealToCourtOfAppealsNotifications.spec.ts @@ -10,10 +10,7 @@ import { UserRole, } from '@island.is/judicial-system/types' -import { - createTestingNotificationModule, - createTestUsers, -} from '../createTestingNotificationModule' +import { createTestingNotificationModule } from '../createTestingNotificationModule' import { Case } from '../../../case' import { DeliverResponse } from '../../models/deliver.response' @@ -26,17 +23,20 @@ interface Then { type GivenWhenThen = (user: User, defenderNationalId?: string) => Promise describe('InternalNotificationController - Send appeal to court of appeals notifications', () => { - const { prosecutor, judge, registrar, defender, court } = createTestUsers([ - 'prosecutor', - 'judge', - 'registrar', - 'defender', - 'court', - ]) - const caseId = uuid() - + const prosecutorName = uuid() + const prosecutorEmail = uuid() + const prosecutorMobileNumber = uuid() + const judgeName = uuid() + const judgeEmail = uuid() + const registrarName = uuid() + const registrarEmail = uuid() + const defenderName = uuid() + const defenderEmail = uuid() const courtCaseNumber = uuid() + const courtId = uuid() + const courtEmail = uuid() + const courtMobileNumber = uuid() let mockEmailService: EmailService let mockSmsService: SmsService @@ -44,8 +44,8 @@ describe('InternalNotificationController - Send appeal to court of appeals notif let givenWhenThen: GivenWhenThen beforeEach(async () => { - process.env.COURTS_ASSISTANT_MOBILE_NUMBERS = `{"${court.id}": "${court.mobile}"}` - process.env.COURTS_EMAILS = `{"${court.id}": "${court.email}"}` + process.env.COURTS_ASSISTANT_MOBILE_NUMBERS = `{"${courtId}": "${courtMobileNumber}"}` + process.env.COURTS_EMAILS = `{"${courtId}": "${courtEmail}"}` const { emailService, smsService, internalNotificationController } = await createTestingNotificationModule() @@ -62,18 +62,18 @@ describe('InternalNotificationController - Send appeal to court of appeals notif { id: caseId, prosecutor: { - name: prosecutor.name, - email: prosecutor.email, - mobileNumber: prosecutor.mobile, + name: prosecutorName, + email: prosecutorEmail, + mobileNumber: prosecutorMobileNumber, }, - judge: { name: judge.name, email: judge.email }, - registrar: { name: registrar.name, email: registrar.email }, + judge: { name: judgeName, email: judgeEmail }, + registrar: { name: registrarName, email: registrarEmail }, court: { name: 'Héraðsdómur Reykjavíkur' }, defenderNationalId, - defenderName: defender.name, - defenderEmail: defender.email, + defenderName: defenderName, + defenderEmail: defenderEmail, courtCaseNumber, - courtId: court.id, + courtId: courtId, } as Case, { user, @@ -102,35 +102,35 @@ describe('InternalNotificationController - Send appeal to court of appeals notif it('should send notification to judge, registrar, court and defender', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: judge.name, address: judge.email }], + to: [{ name: judgeName, address: judgeEmail }], subject: `Kæra í máli ${courtCaseNumber}`, html: `Úrskurður hefur verið kærður í máli ${courtCaseNumber}. Hægt er að nálgast gögn málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), ) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: registrar.name, address: registrar.email }], + to: [{ name: registrarName, address: registrarEmail }], subject: `Kæra í máli ${courtCaseNumber}`, html: `Úrskurður hefur verið kærður í máli ${courtCaseNumber}. Hægt er að nálgast gögn málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), ) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: 'Héraðsdómur Reykjavíkur', address: court.email }], + to: [{ name: 'Héraðsdómur Reykjavíkur', address: courtEmail }], subject: `Kæra í máli ${courtCaseNumber}`, html: `Úrskurður hefur verið kærður í máli ${courtCaseNumber}. Hægt er að nálgast gögn málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), ) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: defender.name, address: defender.email }], + to: [{ name: defenderName, address: defenderEmail }], subject: `Kæra í máli ${courtCaseNumber}`, html: `Úrskurður hefur verið kærður í máli ${courtCaseNumber}. Hægt er að nálgast gögn málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), ) expect(mockSmsService.sendSms).toHaveBeenCalledWith( - [court.mobile], + [courtMobileNumber], `Úrskurður hefur verið kærður í máli ${courtCaseNumber}. Sjá nánar á rettarvorslugatt.island.is`, ) expect(then.result).toEqual({ delivered: true }) @@ -150,20 +150,20 @@ describe('InternalNotificationController - Send appeal to court of appeals notif it('should send notification to judge and defender', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: judge.name, address: judge.email }], + to: [{ name: judgeName, address: judgeEmail }], subject: `Kæra í máli ${courtCaseNumber}`, html: `Úrskurður hefur verið kærður í máli ${courtCaseNumber}. Hægt er að nálgast gögn málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), ) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: defender.name, address: defender.email }], + to: [{ name: defenderName, address: defenderEmail }], subject: `Kæra í máli ${courtCaseNumber}`, html: `Úrskurður hefur verið kærður í máli ${courtCaseNumber}. Hægt er að nálgast gögn málsins hjá Héraðsdómi Reykjavíkur ef þau hafa ekki þegar verið afhent.`, }), ) expect(mockSmsService.sendSms).toHaveBeenCalledWith( - [court.mobile], + [courtMobileNumber], `Úrskurður hefur verið kærður í máli ${courtCaseNumber}. Sjá nánar á rettarvorslugatt.island.is`, ) expect(then.result).toEqual({ delivered: true }) @@ -180,24 +180,24 @@ describe('InternalNotificationController - Send appeal to court of appeals notif it('should send notifications to judge and prosecutor', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: judge.name, address: judge.email }], + to: [{ name: judgeName, address: judgeEmail }], subject: `Kæra í máli ${courtCaseNumber}`, html: `Úrskurður hefur verið kærður í máli ${courtCaseNumber}. Hægt er að nálgast gögn málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), ) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: prosecutor.name, address: prosecutor.email }], + to: [{ name: prosecutorName, address: prosecutorEmail }], subject: `Kæra í máli ${courtCaseNumber}`, html: `Úrskurður hefur verið kærður í máli ${courtCaseNumber}. Hægt er að nálgast gögn málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), ) expect(mockSmsService.sendSms).toHaveBeenCalledWith( - [court.mobile], + [courtMobileNumber], `Úrskurður hefur verið kærður í máli ${courtCaseNumber}. Sjá nánar á rettarvorslugatt.island.is`, ) expect(mockSmsService.sendSms).toHaveBeenCalledWith( - [prosecutor.mobile], + [prosecutorMobileNumber], `Úrskurður hefur verið kærður í máli ${courtCaseNumber}. Sjá nánar á rettarvorslugatt.island.is`, ) diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealWithdrawnNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealWithdrawnNotifications.spec.ts index bc96d0d487d9..1b2be1516b1e 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealWithdrawnNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAppealWithdrawnNotifications.spec.ts @@ -5,15 +5,11 @@ import { EmailService } from '@island.is/email-service' import { CaseNotificationType, InstitutionType, - NotificationType, User, UserRole, } from '@island.is/judicial-system/types' -import { - createTestingNotificationModule, - createTestUsers, -} from '../createTestingNotificationModule' +import { createTestingNotificationModule } from '../createTestingNotificationModule' import { Case } from '../../../case' import { DeliverResponse } from '../../models/deliver.response' @@ -31,33 +27,26 @@ type GivenWhenThen = ( ) => Promise describe('InternalNotificationController - Send appeal withdrawn notifications', () => { - const { - courtOfAppeals, - court, - judge, - prosecutor, - defender, - registrar, - appealAssistant, - appealJudge1, - } = createTestUsers([ - 'courtOfAppeals', - 'court', - 'judge', - 'prosecutor', - 'defender', - 'registrar', - 'appealAssistant', - 'appealJudge1', - ]) - - const courtOfAppealsEmail = courtOfAppeals.email - const courtEmail = court.email + const courtOfAppealsEmail = uuid() + const courtEmail = uuid() const courtId = uuid() const userId = uuid() const caseId = uuid() + const judgeName = uuid() + const judgeEmail = uuid() + const prosecutorName = uuid() + const prosecutorEmail = uuid() + const prosecutorMobileNumber = uuid() + const defenderName = uuid() + const defenderEmail = uuid() const courtCaseNumber = uuid() const receivedDate = new Date() + const registrarEmail = uuid() + const registrarName = uuid() + const appealAssistantName = uuid() + const appealAssistantEmail = uuid() + const appealJudge1Name = uuid() + const appealJudge1Email = uuid() let mockEmailService: EmailService let mockNotificationModel: typeof Notification @@ -85,25 +74,22 @@ describe('InternalNotificationController - Send appeal withdrawn notifications', { id: caseId, prosecutor: { - name: prosecutor.name, - email: prosecutor.email, - mobileNumber: prosecutor.mobile, + name: prosecutorName, + email: prosecutorEmail, + mobileNumber: prosecutorMobileNumber, }, court: { name: 'Héraðsdómur Reykjavíkur', id: courtId }, - defenderName: defender.name, - defenderEmail: defender.email, + defenderName: defenderName, + defenderEmail: defenderEmail, courtCaseNumber, appealReceivedByCourtDate, appealAssistant: { - name: appealAssistant.name, - email: appealAssistant.email, - }, - judge: { name: judge.name, email: judge.email }, - appealJudge1: { - name: appealJudge1.name, - email: appealJudge1.email, + name: appealAssistantName, + email: appealAssistantEmail, }, - registrar: { name: registrar.name, email: registrar.email }, + judge: { name: judgeName, email: judgeEmail }, + appealJudge1: { name: appealJudge1Name, email: appealJudge1Email }, + registrar: { name: registrarName, email: registrarEmail }, notifications, } as Case, { @@ -130,7 +116,7 @@ describe('InternalNotificationController - Send appeal withdrawn notifications', it('should send notification to defender', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: defender.name, address: defender.email }], + to: [{ name: defenderName, address: defenderEmail }], subject: `Afturköllun kæru í máli ${courtCaseNumber}`, html: `Sækjandi hefur afturkallað kæru í máli ${courtCaseNumber}. Hægt er að nálgast yfirlitssíðu málsins á rettarvorslugatt.island.is.`, }), @@ -154,7 +140,7 @@ describe('InternalNotificationController - Send appeal withdrawn notifications', it('should send notification to appeal assistant ', () => { expect(mockEmailService.sendEmail).not.toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: appealAssistant.name, address: appealAssistant.email }], + to: [{ name: appealAssistantName, address: appealAssistantEmail }], subject: `Afturköllun kæru í máli ${courtCaseNumber}`, html: `Sækjandi hefur afturkallað kæru í máli ${courtCaseNumber}. Hægt er að nálgast yfirlitssíðu málsins á rettarvorslugatt.island.is.`, }), @@ -172,14 +158,14 @@ describe('InternalNotificationController - Send appeal withdrawn notifications', then = await givenWhenThen(UserRole.PROSECUTOR, receivedDate, [ { caseId, - type: NotificationType.APPEAL_JUDGES_ASSIGNED, + type: CaseNotificationType.APPEAL_JUDGES_ASSIGNED, } as Notification, ]) }) it('should send notification to defender', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: defender.name, address: defender.email }], + to: [{ name: defenderName, address: defenderEmail }], subject: `Afturköllun kæru í máli ${courtCaseNumber}`, html: `Sækjandi hefur afturkallað kæru í máli ${courtCaseNumber}. Hægt er að nálgast yfirlitssíðu málsins á rettarvorslugatt.island.is.`, }), @@ -203,7 +189,7 @@ describe('InternalNotificationController - Send appeal withdrawn notifications', it('should send notification to appeal assistant ', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: appealAssistant.name, address: appealAssistant.email }], + to: [{ name: appealAssistantName, address: appealAssistantEmail }], subject: `Afturköllun kæru í máli ${courtCaseNumber}`, html: `Sækjandi hefur afturkallað kæru í máli ${courtCaseNumber}. Hægt er að nálgast yfirlitssíðu málsins á rettarvorslugatt.island.is.`, }), @@ -212,7 +198,7 @@ describe('InternalNotificationController - Send appeal withdrawn notifications', it('should send notification to appeal judges', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: appealJudge1.name, address: appealJudge1.email }], + to: [{ name: appealJudge1Name, address: appealJudge1Email }], }), ) }) @@ -228,7 +214,7 @@ describe('InternalNotificationController - Send appeal withdrawn notifications', it('should send notification to prosecutor', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: prosecutor.name, address: prosecutor.email }], + to: [{ name: prosecutorName, address: prosecutorEmail }], subject: `Afturköllun kæru í máli ${courtCaseNumber}`, html: `Verjandi hefur afturkallað kæru í máli ${courtCaseNumber}. Hægt er að nálgast yfirlitssíðu málsins á rettarvorslugatt.island.is.`, }), @@ -240,7 +226,7 @@ describe('InternalNotificationController - Send appeal withdrawn notifications', it('should send notification to judge', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: judge.name, address: judge.email }], + to: [{ name: judgeName, address: judgeEmail }], subject: `Afturköllun kæru í máli ${courtCaseNumber}`, html: `Verjandi hefur afturkallað kæru í máli ${courtCaseNumber}. Hægt er að nálgast yfirlitssíðu málsins á rettarvorslugatt.island.is.`, }), @@ -250,7 +236,7 @@ describe('InternalNotificationController - Send appeal withdrawn notifications', it('should send notification to registrar', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: registrar.name, address: registrar.email }], + to: [{ name: registrarName, address: registrarEmail }], subject: `Afturköllun kæru í máli ${courtCaseNumber}`, html: `Verjandi hefur afturkallað kæru í máli ${courtCaseNumber}. Hægt er að nálgast yfirlitssíðu málsins á rettarvorslugatt.island.is.`, }), diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendCaseFilesUpdatedNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendCaseFilesUpdatedNotifications.spec.ts index d57f0ea344f2..21df0ee723b4 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendCaseFilesUpdatedNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendCaseFilesUpdatedNotifications.spec.ts @@ -10,10 +10,7 @@ import { UserRole, } from '@island.is/judicial-system/types' -import { - createTestingNotificationModule, - createTestUsers, -} from '../createTestingNotificationModule' +import { createTestingNotificationModule } from '../createTestingNotificationModule' import { Case } from '../../../case' import { DeliverResponse } from '../../models/deliver.response' @@ -26,15 +23,18 @@ interface Then { type GivenWhenThen = (user: User) => Promise describe('InternalNotificationController - Send case files updated notifications', () => { - const { prosecutor, judge, defender, spokesperson } = createTestUsers([ - 'prosecutor', - 'judge', - 'defender', - 'spokesperson', - ]) - const caseId = uuid() const courtCaseNumber = uuid() + const prosecutorName = uuid() + const prosecutorEmail = uuid() + const judgeName = uuid() + const judgeEmail = uuid() + const defenderNationalId = uuid() + const defenderName = uuid() + const defenderEmail = uuid() + const spokespersonNationalId = uuid() + const spokespersonName = uuid() + const spokespersonEmail = uuid() let mockEmailService: EmailService let givenWhenThen: GivenWhenThen @@ -55,21 +55,15 @@ describe('InternalNotificationController - Send case files updated notifications type: CaseType.INDICTMENT, courtCaseNumber, court: { name: 'Héraðsdómur Reykjavíkur' }, - prosecutor: { name: prosecutor.name, email: prosecutor.email }, - judge: { name: judge.name, email: judge.email }, - defendants: [ - { - defenderNationalId: defender.nationalId, - defenderName: defender.name, - defenderEmail: defender.email, - }, - ], + prosecutor: { name: prosecutorName, email: prosecutorEmail }, + judge: { name: judgeName, email: judgeEmail }, + defendants: [{ defenderNationalId, defenderName, defenderEmail }], civilClaimants: [ { hasSpokesperson: true, - spokespersonNationalId: spokesperson.nationalId, - spokespersonName: spokesperson.name, - spokespersonEmail: spokesperson.email, + spokespersonNationalId, + spokespersonName, + spokespersonEmail, }, ], } as Case, @@ -98,21 +92,21 @@ describe('InternalNotificationController - Send case files updated notifications it('should send notifications', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: judge.name, address: judge.email }], + to: [{ name: judgeName, address: judgeEmail }], subject: `Ný gögn í máli ${courtCaseNumber}`, html: `Ný gögn hafa borist vegna máls ${courtCaseNumber}. Hægt er að nálgast gögn málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), ) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: spokesperson.name, address: spokesperson.email }], + to: [{ name: spokespersonName, address: spokespersonEmail }], subject: `Ný gögn í máli ${courtCaseNumber}`, html: `Ný gögn hafa borist vegna máls ${courtCaseNumber}. Hægt er að nálgast gögn málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), ) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: defender.name, address: defender.email }], + to: [{ name: defenderName, address: defenderEmail }], subject: `Ný gögn í máli ${courtCaseNumber}`, html: `Ný gögn hafa borist vegna máls ${courtCaseNumber}. Hægt er að nálgast gögn málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), @@ -133,21 +127,21 @@ describe('InternalNotificationController - Send case files updated notifications it('should send notifications', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: judge.name, address: judge.email }], + to: [{ name: judgeName, address: judgeEmail }], subject: `Ný gögn í máli ${courtCaseNumber}`, html: `Ný gögn hafa borist vegna máls ${courtCaseNumber}. Hægt er að nálgast gögn málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), ) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: spokesperson.name, address: spokesperson.email }], + to: [{ name: spokespersonName, address: spokespersonEmail }], subject: `Ný gögn í máli ${courtCaseNumber}`, html: `Ný gögn hafa borist vegna máls ${courtCaseNumber}. Hægt er að nálgast gögn málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), ) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: prosecutor.name, address: prosecutor.email }], + to: [{ name: prosecutorName, address: prosecutorEmail }], subject: `Ný gögn í máli ${courtCaseNumber}`, html: `Ný gögn hafa borist vegna máls ${courtCaseNumber}. Hægt er að nálgast gögn málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendCourtDateNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendCourtDateNotifications.spec.ts index 6cb820f993dc..18bf5d712b71 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendCourtDateNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendCourtDateNotifications.spec.ts @@ -6,14 +6,10 @@ import { CaseNotificationType, CaseType, DateType, - NotificationType, User, } from '@island.is/judicial-system/types' -import { - createTestingNotificationModule, - createTestUsers, -} from '../createTestingNotificationModule' +import { createTestingNotificationModule } from '../createTestingNotificationModule' import { Case } from '../../../case' import { CaseNotificationDto } from '../../dto/caseNotification.dto' @@ -35,12 +31,13 @@ type GivenWhenThen = ( describe('InternalNotificationController - Send court date notifications', () => { const userId = uuid() const caseId = uuid() - + const prosecutorName = uuid() + const prosecutorEmail = uuid() + const defenderName = uuid() + const defenderEmail = uuid() const courtName = 'Héraðsdómur Reykjavíkur' const courtCaseNumber = uuid() - const { prosecutor, defender } = createTestUsers(['prosecutor', 'defender']) - let mockEmailService: EmailService let mockNotificationModel: typeof Notification let givenWhenThen: GivenWhenThen @@ -78,11 +75,11 @@ describe('InternalNotificationController - Send court date notifications', () => const theCase = { id: caseId, type: CaseType.CUSTODY, - prosecutor: { name: prosecutor.name, email: prosecutor.email }, + prosecutor: { name: prosecutorName, email: prosecutorEmail }, court: { name: courtName }, courtCaseNumber, - defenderName: defender.name, - defenderEmail: defender.email, + defenderName, + defenderEmail, } as Case beforeEach(async () => { @@ -92,23 +89,23 @@ describe('InternalNotificationController - Send court date notifications', () => it('should send notifications to prosecutor and defender', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: prosecutor.name, address: prosecutor.email }], + to: [{ name: prosecutorName, address: prosecutorEmail }], subject: `Fyrirtaka í máli: ${courtCaseNumber}`, - html: `Héraðsdómur Reykjavíkur hefur staðfest fyrirtökutíma fyrir kröfu um gæsluvarðhald.

Fyrirtaka mun fara fram á ótilgreindum tíma.

Dómsalur hefur ekki verið skráður.

Dómari hefur ekki verið skráður.

Verjandi sakbornings: ${defender.name}. Hægt er að nálgast yfirlitssíðu málsins á rettarvorslugatt.island.is.`, + html: `Héraðsdómur Reykjavíkur hefur staðfest fyrirtökutíma fyrir kröfu um gæsluvarðhald.

Fyrirtaka mun fara fram á ótilgreindum tíma.

Dómsalur hefur ekki verið skráður.

Dómari hefur ekki verið skráður.

Verjandi sakbornings: ${defenderName}. Hægt er að nálgast yfirlitssíðu málsins á rettarvorslugatt.island.is.`, }), ) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: defender.name, address: defender.email }], + to: [{ name: defenderName, address: defenderEmail }], subject: `Fyrirtaka í máli ${courtCaseNumber}`, - html: `Héraðsdómur Reykjavíkur hefur boðað þig í fyrirtöku sem verjanda sakbornings.

Fyrirtaka mun fara fram á ótilgreindum tíma.

Málsnúmer: ${courtCaseNumber}.

Dómsalur hefur ekki verið skráður.

Dómari: .

Sækjandi: ${prosecutor.name} ().`, + html: `Héraðsdómur Reykjavíkur hefur boðað þig í fyrirtöku sem verjanda sakbornings.

Fyrirtaka mun fara fram á ótilgreindum tíma.

Málsnúmer: ${courtCaseNumber}.

Dómsalur hefur ekki verið skráður.

Dómari: .

Sækjandi: ${prosecutorName} ().`, }), ) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: defender.name, address: defender.email }], + to: [{ name: defenderName, address: defenderEmail }], subject: `Yfirlit máls ${courtCaseNumber}`, }), ) @@ -128,11 +125,11 @@ describe('InternalNotificationController - Send court date notifications', () => const theCase = { id: caseId, type: CaseType.CUSTODY, - prosecutor: { name: prosecutor.name, email: prosecutor.email }, + prosecutor: { name: prosecutorName, email: prosecutorEmail }, court: { name: courtName }, courtCaseNumber, - defenderName: defender.name, - defenderEmail: defender.email, + defenderName, + defenderEmail, } as Case beforeEach(async () => { @@ -145,8 +142,8 @@ describe('InternalNotificationController - Send court date notifications', () => notifications: [ { caseId, - type: NotificationType.READY_FOR_COURT, - recipients: [{ address: defender.email, success: true }], + type: CaseNotificationType.READY_FOR_COURT, + recipients: [{ address: defenderEmail, success: true }], }, ], } as Case, @@ -157,7 +154,7 @@ describe('InternalNotificationController - Send court date notifications', () => it('should not send link to case to defender', () => { expect(mockEmailService.sendEmail).not.toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: defender.name, address: defender.email }], + to: [{ name: defenderName, address: defenderEmail }], subject: `Yfirlit máls ${courtCaseNumber}`, }), ) @@ -178,11 +175,11 @@ describe('InternalNotificationController - Send court date notifications', () => const theCase = { id: caseId, type: CaseType.INDICTMENT, - prosecutor: { name: prosecutor.name, email: prosecutor.email }, + prosecutor: { name: prosecutorName, email: prosecutorEmail }, defendants: [ { - defenderName: defender.name, - defenderEmail: defender.email, + defenderName, + defenderEmail, }, ], court: { name: courtName }, @@ -197,7 +194,7 @@ describe('InternalNotificationController - Send court date notifications', () => it('should send notifications to prosecutor and defender', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: prosecutor.name, address: prosecutor.email }], + to: [{ name: prosecutorName, address: prosecutorEmail }], subject: `Nýtt þinghald í máli ${courtCaseNumber}`, html: `Héraðsdómur Reykjavíkur boðar til þinghalds í máli ${courtCaseNumber}.
Fyrirtaka mun fara fram 2. maí 2024, kl. 14:32.

Tegund þinghalds: Óþekkt.

Dómsalur hefur ekki verið skráður.

Dómari hefur ekki verið skráður.

Hægt er að nálgast gögn málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), @@ -205,7 +202,7 @@ describe('InternalNotificationController - Send court date notifications', () => expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: defender.name, address: defender.email }], + to: [{ name: defenderName, address: defenderEmail }], subject: `Nýtt þinghald í máli ${courtCaseNumber}`, html: `Héraðsdómur Reykjavíkur boðar til þinghalds í máli ${courtCaseNumber}.
Fyrirtaka mun fara fram 2. maí 2024, kl. 14:32.

Tegund þinghalds: Óþekkt.

Dómsalur hefur ekki verið skráður.

Dómari hefur ekki verið skráður.

Hægt er að nálgast gögn málsins hjá Héraðsdómur Reykjavíkur.`, }), diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendDefendantsNotUpdatedAtCourtNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendDefendantsNotUpdatedAtCourtNotifications.spec.ts index b0448904d07f..b7090ce986dc 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendDefendantsNotUpdatedAtCourtNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendDefendantsNotUpdatedAtCourtNotifications.spec.ts @@ -2,16 +2,9 @@ import { uuid } from 'uuidv4' import { EmailService } from '@island.is/email-service' -import { - CaseNotificationType, - NotificationType, - User, -} from '@island.is/judicial-system/types' +import { CaseNotificationType, User } from '@island.is/judicial-system/types' -import { - createTestingNotificationModule, - createTestUsers, -} from '../createTestingNotificationModule' +import { createTestingNotificationModule } from '../createTestingNotificationModule' import { Case } from '../../../case' import { CaseNotificationDto } from '../../dto/caseNotification.dto' @@ -34,15 +27,14 @@ describe('InternalNotificationController - Send defendants not updated at court user: { id: userId } as User, type: CaseNotificationType.DEFENDANTS_NOT_UPDATED_AT_COURT, } - - const { registrar } = createTestUsers(['registrar', 'defender']) const caseId = uuid() const courtCaseNumber = uuid() - + const registrarName = uuid() + const registrarEmail = uuid() const theCase = { id: caseId, courtCaseNumber, - registrar: { name: registrar.name, email: registrar.email }, + registrar: { name: registrarName, email: registrarEmail }, } as Case let mockEmailService: EmailService @@ -80,7 +72,7 @@ describe('InternalNotificationController - Send defendants not updated at court it('should send email', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: registrar.name, address: registrar.email }], + to: [{ name: registrarName, address: registrarEmail }], subject: `Skráning varnaraðila/verjenda í máli ${courtCaseNumber}`, html: `Ekki tókst að skrá varnaraðila/verjendur í máli ${courtCaseNumber} í Auði. Yfirfara þarf málið í Auði og skrá rétta aðila áður en því er lokað.`, }), @@ -99,8 +91,8 @@ describe('InternalNotificationController - Send defendants not updated at court ...theCase, notifications: [ { - type: NotificationType.DEFENDANTS_NOT_UPDATED_AT_COURT, - recipients: [{ address: registrar.email, success: true }], + type: CaseNotificationType.DEFENDANTS_NOT_UPDATED_AT_COURT, + recipients: [{ address: registrarEmail, success: true }], }, ], } as Case, diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendIndictmentDeniedNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendIndictmentDeniedNotifications.spec.ts index 7efd487f6146..904b873650f9 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendIndictmentDeniedNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendIndictmentDeniedNotifications.spec.ts @@ -8,10 +8,7 @@ import { User, } from '@island.is/judicial-system/types' -import { - createTestingNotificationModule, - createTestUsers, -} from '../createTestingNotificationModule' +import { createTestingNotificationModule } from '../createTestingNotificationModule' import { Case } from '../../../case' import { CaseNotificationDto } from '../../dto/caseNotification.dto' @@ -32,10 +29,8 @@ type GivenWhenThen = ( describe('InternalNotificationController - Send indictment denied notification', () => { const userId = uuid() const caseId = uuid() - - const { prosecutor } = createTestUsers(['prosecutor']) - const prosecutorName = prosecutor.name - const prosecutorEmail = prosecutor.email + const prosecutorName = uuid() + const prosecutorEmail = uuid() const policeCaseNumbers = [uuid(), uuid()] let mockEmailService: EmailService diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendIndictmentReturnedNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendIndictmentReturnedNotifications.spec.ts index ee1f6bd106bb..24a9ddba4558 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendIndictmentReturnedNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendIndictmentReturnedNotifications.spec.ts @@ -8,10 +8,7 @@ import { User, } from '@island.is/judicial-system/types' -import { - createTestingNotificationModule, - createTestUsers, -} from '../createTestingNotificationModule' +import { createTestingNotificationModule } from '../createTestingNotificationModule' import { Case } from '../../../case' import { CaseNotificationDto } from '../../dto/caseNotification.dto' @@ -30,10 +27,10 @@ type GivenWhenThen = ( ) => Promise describe('InternalNotificationController - Send indictment returned notification', () => { - const { prosecutor } = createTestUsers(['prosecutor']) const userId = uuid() const caseId = uuid() - + const prosecutorName = uuid() + const prosecutorEmail = uuid() const policeCaseNumbers = [uuid(), uuid()] const courtName = uuid() @@ -72,7 +69,7 @@ describe('InternalNotificationController - Send indictment returned notification const theCase = { id: caseId, type: CaseType.INDICTMENT, - prosecutor: { name: prosecutor.name, email: prosecutor.email }, + prosecutor: { name: prosecutorName, email: prosecutorEmail }, policeCaseNumbers, court: { name: courtName }, } as Case @@ -84,7 +81,7 @@ describe('InternalNotificationController - Send indictment returned notification it('should send notifications to prosecutor', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: prosecutor.name, address: prosecutor.email }], + to: [{ name: prosecutorName, address: prosecutorEmail }], subject: `Ákæra endursend í máli ${policeCaseNumbers[0]}`, html: `${courtName} hefur endursent ákæru vegna lögreglumáls ${policeCaseNumbers[0]}. Þú getur nálgast samantekt málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, }), 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..d3992015ded4 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 @@ -4,10 +4,7 @@ import { EmailService } from '@island.is/email-service' import { InstitutionNotificationType } from '@island.is/judicial-system/types' -import { - createTestingNotificationModule, - createTestUsers, -} from '../createTestingNotificationModule' +import { createTestingNotificationModule } from '../createTestingNotificationModule' import { InternalCaseService } from '../../../case' import { UserService } from '../../../user' @@ -21,11 +18,10 @@ interface Then { type GivenWhenThen = () => Promise describe('InternalNotificationController - Send indictments waiting for confirmation notifications', () => { - const { prosecutor1, prosecutor2 } = createTestUsers([ - 'prosecutor1', - 'prosecutor2', - ]) - + const prosecutorName1 = uuid() + const prosecutorEmail1 = uuid() + const prosecutorName2 = uuid() + const prosecutorEmail2 = uuid() const prosecutorsOfficeId = uuid() let mockUserService: UserService let mockInternalCaseService: InternalCaseService @@ -120,25 +116,25 @@ describe('InternalNotificationController - Send indictments waiting for confirma const mockGetUsersWhoCanConfirmIndictments = mockUserService.getUsersWhoCanConfirmIndictments as jest.Mock mockGetUsersWhoCanConfirmIndictments.mockResolvedValueOnce([ - { name: prosecutor1.name, email: prosecutor1.email }, - { name: prosecutor2.name, email: prosecutor2.email }, + { name: prosecutorName1, email: prosecutorEmail1 }, + { name: prosecutorName2, email: prosecutorEmail2 }, ]) then = await givenWhenThen() }) - it('should send messages', () => { + it('should not send messages', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledTimes(2) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: prosecutor1.name, address: prosecutor1.email }], + to: [{ name: prosecutorName1, address: prosecutorEmail1 }], subject: 'Ákærur bíða staðfestingar', html: 'Í Réttarvörslugátt bíða 2 ákærur staðfestingar.

Hægt er að nálgast yfirlit og staðfesta ákærur í Réttarvörslugátt.', }), ) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: prosecutor2.name, address: prosecutor2.email }], + to: [{ name: prosecutorName2, address: prosecutorEmail2 }], subject: 'Ákærur bíða staðfestingar', html: 'Í Réttarvörslugátt bíða 2 ákærur staðfestingar.

Hægt er að nálgast yfirlit og staðfesta ákærur í Réttarvörslugátt.', }), diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendReadyForCourtNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendReadyForCourtNotifications.spec.ts index f9757746c4ad..e1b3b2c1a4c6 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendReadyForCourtNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendReadyForCourtNotifications.spec.ts @@ -15,15 +15,11 @@ import { CaseType, DateType, IndictmentSubtype, - NotificationType, RequestSharedWithDefender, User, } from '@island.is/judicial-system/types' -import { - createTestingNotificationModule, - createTestUsers, -} from '../createTestingNotificationModule' +import { createTestingNotificationModule } from '../createTestingNotificationModule' import { randomDate } from '../../../../test' import { Case } from '../../../case' @@ -48,29 +44,23 @@ describe('InternalNotificationController - Send ready for court notifications fo const userId = uuid() const caseId = uuid() const policeCaseNumber = uuid() + const courtId = uuid() const courtCaseNumber = uuid() - - const { prosecutor, defender, testCourt } = createTestUsers([ - 'prosecutor', - 'defender', - 'testCourt', - ]) - const theCase = { id: caseId, type: CaseType.CUSTODY, state: CaseState.RECEIVED, policeCaseNumbers: [policeCaseNumber], prosecutor: { - name: prosecutor.name, - email: prosecutor.email, + name: 'Derrick', + email: 'derrick@dummy.is', }, - courtId: testCourt.id, + courtId, court: { name: 'Héraðsdómur Reykjavíkur' }, courtCaseNumber, - defenderNationalId: defender.nationalId, - defenderName: defender.name, - defenderEmail: defender.email, + defenderNationalId: uuid(), + defenderName: 'Saul Goodman', + defenderEmail: 'saul@dummy.is', requestSharedWithDefender: RequestSharedWithDefender.COURT_DATE, prosecutorsOffice: { name: 'Héraðsdómur Derricks' }, dateLogs: [{ date: randomDate(), dateType: DateType.ARRAIGNMENT_DATE }], @@ -79,6 +69,7 @@ describe('InternalNotificationController - Send ready for court notifications fo user: { id: userId } as User, type: CaseNotificationType.READY_FOR_COURT, } + const courtMobileNumber = uuid() let mockEmailService: EmailService let mockSmsService: SmsService @@ -86,7 +77,7 @@ describe('InternalNotificationController - Send ready for court notifications fo let givenWhenThen: GivenWhenThen beforeEach(async () => { - process.env.COURTS_MOBILE_NUMBERS = `{"${testCourt.id}": "${testCourt.mobile}"}` + process.env.COURTS_MOBILE_NUMBERS = `{"${courtId}": "${courtMobileNumber}"}` const { emailService, @@ -128,7 +119,7 @@ describe('InternalNotificationController - Send ready for court notifications fo name: mockNotificationConfig.email.replyToName, address: mockNotificationConfig.email.replyToEmail, }, - to: [{ name: prosecutor.name, address: prosecutor.email }], + to: [{ name: 'Derrick', address: 'derrick@dummy.is' }], subject: 'Krafa um gæsluvarðhald send', text: `Þú hefur sent kröfu á Héraðsdóm Reykjavíkur vegna LÖKE máls ${policeCaseNumber}. Skjalið er aðgengilegt undir málinu í Réttarvörslugátt.`, html: `Þú hefur sent kröfu á Héraðsdóm Reykjavíkur vegna LÖKE máls ${policeCaseNumber}. Skjalið er aðgengilegt undir málinu í Réttarvörslugátt.`, @@ -138,8 +129,8 @@ describe('InternalNotificationController - Send ready for court notifications fo it('should send ready for court sms notification to court', () => { expect(mockSmsService.sendSms).toHaveBeenCalledWith( - [testCourt.mobile], - `Gæsluvarðhaldskrafa tilbúin til afgreiðslu. Sækjandi: ${prosecutor.name} (Héraðsdómur Derricks). Sjá nánar á rettarvorslugatt.island.is.`, + [courtMobileNumber], + 'Gæsluvarðhaldskrafa tilbúin til afgreiðslu. Sækjandi: Derrick (Héraðsdómur Derricks). Sjá nánar á rettarvorslugatt.island.is.', ) }) @@ -157,13 +148,11 @@ describe('InternalNotificationController - Send ready for court notifications fo notifications: [ { caseId, - type: NotificationType.READY_FOR_COURT, + type: CaseNotificationType.READY_FOR_COURT, recipients: [ { address: - mockNotificationConfig.sms.courtsMobileNumbers[ - testCourt.id - ], + mockNotificationConfig.sms.courtsMobileNumbers[courtId], success: true, }, ], @@ -184,7 +173,7 @@ describe('InternalNotificationController - Send ready for court notifications fo name: mockNotificationConfig.email.replyToName, address: mockNotificationConfig.email.replyToEmail, }, - to: [{ name: prosecutor.name, address: prosecutor.email }], + to: [{ name: 'Derrick', address: 'derrick@dummy.is' }], subject: 'Krafa um gæsluvarðhald send', text: `Þú hefur sent kröfu á Héraðsdóm Reykjavíkur vegna LÖKE máls ${policeCaseNumber}. Skjalið er aðgengilegt undir málinu í Réttarvörslugátt.`, html: `Þú hefur sent kröfu á Héraðsdóm Reykjavíkur vegna LÖKE máls ${policeCaseNumber}. Skjalið er aðgengilegt undir málinu í Réttarvörslugátt.`, @@ -194,7 +183,7 @@ describe('InternalNotificationController - Send ready for court notifications fo it('should send ready for court sms notification to court', () => { expect(mockSmsService.sendSms).toHaveBeenCalledWith( - [testCourt.mobile], + [courtMobileNumber], `Sækjandi í máli ${courtCaseNumber} hefur breytt kröfunni og sent aftur á héraðsdómstól. Nýtt kröfuskjal hefur verið vistað í Auði. Sjá nánar á rettarvorslugatt.island.is.`, ) }) @@ -202,7 +191,7 @@ describe('InternalNotificationController - Send ready for court notifications fo it('should not send ready for court email notification to defender', () => { expect(mockEmailService.sendEmail).not.toHaveBeenCalledWith( expect.objectContaining({ - to: [{ name: defender.name, address: defender.email }], + to: [{ name: 'Saul Goodman', address: 'saul@dummy.is' }], }), ) }) @@ -235,8 +224,8 @@ describe('InternalNotificationController - Send ready for court notifications fo ...theCase, notifications: [ { - type: NotificationType.READY_FOR_COURT, - recipients: [{ address: defender.email, success: true }], + type: CaseNotificationType.READY_FOR_COURT, + recipients: [{ address: 'saul@dummy.is', success: true }], }, ], } as Case, @@ -256,7 +245,7 @@ describe('InternalNotificationController - Send ready for court notifications fo name: mockNotificationConfig.email.replyToName, address: mockNotificationConfig.email.replyToEmail, }, - to: [{ name: defender.name, address: defender.email }], + to: [{ name: 'Saul Goodman', address: 'saul@dummy.is' }], subject: `Krafa í máli ${courtCaseNumber}`, html: `Sækjandi í máli ${courtCaseNumber} hjá Héraðsdómi Reykjavíkur hefur breytt kröfunni og sent hana aftur á dóminn.

Þú getur nálgast gögn málsins á yfirlitssíðu málsins í Réttarvörslugátt.`, attachments: undefined, @@ -268,8 +257,8 @@ describe('InternalNotificationController - Send ready for court notifications fo describe('InternalNotificationController - Send ready for court notifications for indictment cases', () => { const userId = uuid() - const { testCourt } = createTestUsers(['testCourt']) - + const courtId = uuid() + const courtEmail = uuid() const notificationDto = { user: { id: userId } as User, type: CaseNotificationType.READY_FOR_COURT, @@ -281,7 +270,7 @@ describe('InternalNotificationController - Send ready for court notifications fo let givenWhenThen: GivenWhenThen beforeEach(async () => { - process.env.COURTS_EMAILS = `{"${testCourt.id}": "${testCourt.email}"}` + process.env.COURTS_EMAILS = `{"${courtId}": "${courtEmail}"}` const { emailService, @@ -309,9 +298,8 @@ describe('InternalNotificationController - Send ready for court notifications fo describe('indictment notification with single indictment subtype', () => { const caseId = uuid() const policeCaseNumbers = [uuid()] - const court = { - id: testCourt.id, + id: courtId, name: 'Héraðsdómur Reykjavíkur', } as Institution const prosecutorsOffice = { name: 'Lögreglan á höfuðborgarsvæðinu' } @@ -324,7 +312,7 @@ describe('InternalNotificationController - Send ready for court notifications fo indictmentSubtypes: { [policeCaseNumbers[0]]: [IndictmentSubtype.MURDER], }, - courtId: court.id, + courtId, court, prosecutorsOffice, } as unknown as Case @@ -339,7 +327,7 @@ describe('InternalNotificationController - Send ready for court notifications fo to: [ { name: 'Héraðsdómur Reykjavíkur', - address: testCourt.email, + address: courtEmail, }, ], subject: 'Ákæra tilbúin til afgreiðslu', @@ -349,9 +337,7 @@ describe('InternalNotificationController - Send ready for court notifications fo expect(mockNotificationModel.create).toHaveBeenCalledWith({ caseId, type: CaseNotificationType.READY_FOR_COURT, - recipients: [ - { success: true, address: testCourt.email }, - ] as Recipient[], + recipients: [{ success: true, address: courtEmail }] as Recipient[], }) }) }) @@ -360,7 +346,7 @@ describe('InternalNotificationController - Send ready for court notifications fo const caseId = uuid() const policeCaseNumbers = [uuid(), uuid()] const court = { - id: testCourt.id, + id: courtId, name: 'Héraðsdómur Reykjavíkur', } as Institution const prosecutorsOffice = { name: 'Lögreglan á höfuðborgarsvæðinu' } @@ -380,7 +366,7 @@ describe('InternalNotificationController - Send ready for court notifications fo IndictmentSubtype.THEFT, ], }, - courtId: court.id, + courtId, court, prosecutorsOffice, } as unknown as Case @@ -395,7 +381,7 @@ describe('InternalNotificationController - Send ready for court notifications fo to: [ { name: 'Héraðsdómur Reykjavíkur', - address: testCourt.email, + address: courtEmail, }, ], subject: 'Ákæra tilbúin til afgreiðslu', @@ -405,9 +391,7 @@ describe('InternalNotificationController - Send ready for court notifications fo expect(mockNotificationModel.create).toHaveBeenCalledWith({ caseId, type: CaseNotificationType.READY_FOR_COURT, - recipients: [ - { success: true, address: testCourt.email }, - ] as Recipient[], + recipients: [{ success: true, address: courtEmail }] as Recipient[], }) }) }) diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendRevokedNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendRevokedNotifications.spec.ts index 274a52a42c74..c585586d5c1d 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendRevokedNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendRevokedNotifications.spec.ts @@ -2,15 +2,9 @@ import { uuid } from 'uuidv4' import { EmailService } from '@island.is/email-service' -import { - CaseNotificationType, - NotificationType, -} from '@island.is/judicial-system/types' +import { CaseNotificationType } from '@island.is/judicial-system/types' -import { - createTestingNotificationModule, - createTestUsers, -} from '../createTestingNotificationModule' +import { createTestingNotificationModule } from '../createTestingNotificationModule' import { Case } from '../../../case' import { CaseNotificationDto } from '../../dto/caseNotification.dto' @@ -25,27 +19,22 @@ interface Then { type GivenWhenThen = (notifications?: Notification[]) => Promise describe('InternalNotificationController - Send revoked notifications for indictment cases', () => { - const { judge, registrar, defender } = createTestUsers([ - 'judge', - 'registrar', - 'defender', - ]) const caseId = uuid() - + const judgeName = uuid() + const judgeEmail = uuid() + const registrarName = uuid() + const registrarEmail = uuid() + const defenderNationalId = uuid() + const defenderName = uuid() + const defenderEmail = uuid() const prosecutorsOfficeName = uuid() const courtName = uuid() const courtCaseNumber = uuid() const theCase = { id: caseId, - judge: { name: judge.name, email: judge.email }, - registrar: { name: registrar.name, email: registrar.email }, - defendants: [ - { - defenderNationalId: defender.nationalId, - defenderName: defender.name, - defenderEmail: defender.email, - }, - ], + judge: { name: judgeName, email: judgeEmail }, + registrar: { name: registrarName, email: registrarEmail }, + defendants: [{ defenderNationalId, defenderName, defenderEmail }], creatingProsecutor: { institution: { name: prosecutorsOfficeName } }, court: { name: courtName }, courtCaseNumber, @@ -84,8 +73,8 @@ describe('InternalNotificationController - Send revoked notifications for indict beforeEach(async () => { then = await givenWhenThen([ { - type: NotificationType.COURT_DATE, - recipients: [{ address: defender.email, success: true }], + type: CaseNotificationType.COURT_DATE, + recipients: [{ address: defenderEmail, success: true }], } as Notification, ]) }) @@ -93,21 +82,21 @@ describe('InternalNotificationController - Send revoked notifications for indict it('should send a notifications', () => { expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ address: judge.email, name: judge.name }], + to: [{ address: judgeEmail, name: judgeName }], subject: `Ákæra afturkölluð í máli ${courtCaseNumber}`, html: `${prosecutorsOfficeName} hefur afturkallað ákæru í máli ${courtCaseNumber}. Hægt er að nálgast yfirlitssíðu málsins á rettarvorslugatt.island.is.`, }), ) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ address: registrar.email, name: registrar.name }], + to: [{ address: registrarEmail, name: registrarName }], subject: `Ákæra afturkölluð í máli ${courtCaseNumber}`, html: `${prosecutorsOfficeName} hefur afturkallað ákæru í máli ${courtCaseNumber}. Hægt er að nálgast yfirlitssíðu málsins á rettarvorslugatt.island.is.`, }), ) expect(mockEmailService.sendEmail).toHaveBeenCalledWith( expect.objectContaining({ - to: [{ address: defender.email, name: defender.name }], + to: [{ address: defenderEmail, name: defenderName }], subject: `Ákæra afturkölluð í máli ${courtCaseNumber}`, html: `Dómstóllinn hafði skráð þig sem verjanda í málinu.

Sjá nánar á yfirlitssíðu málsins í Réttarvörslugátt.`, }), @@ -116,9 +105,9 @@ describe('InternalNotificationController - Send revoked notifications for indict caseId: caseId, type: CaseNotificationType.REVOKED, recipients: [ - { address: judge.email, success: true }, - { address: registrar.email, success: true }, - { address: defender.email, success: true }, + { address: judgeEmail, success: true }, + { address: registrarEmail, success: true }, + { address: defenderEmail, success: true }, ], }) expect(then.result).toEqual({ delivered: true }) diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendRulingNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendRulingNotifications.spec.ts index 6959fde99979..26ccba06fa36 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendRulingNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendRulingNotifications.spec.ts @@ -16,10 +16,7 @@ import { User, } from '@island.is/judicial-system/types' -import { - createTestingNotificationModule, - createTestUsers, -} from '../createTestingNotificationModule' +import { createTestingNotificationModule } from '../createTestingNotificationModule' import { Case } from '../../../case' import { Defendant, DefendantService } from '../../../defendant' @@ -46,7 +43,6 @@ describe('InternalNotificationController - Send ruling notifications', () => { user: { id: userId } as User, type: CaseNotificationType.RULING, } - const { testProsecutor } = createTestUsers(['testProsecutor']) let mockEmailService: EmailService let mockConfig: ConfigType @@ -54,8 +50,7 @@ describe('InternalNotificationController - Send ruling notifications', () => { let givenWhenThen: GivenWhenThen beforeEach(async () => { - process.env.PRISON_EMAIL = - 'prisonEmail@omnitrix.is,prisonEmail2@omnitrix.is' + process.env.PRISON_EMAIL = 'prisonEmail@email.com,prisonEmail2@email.com' const { emailService, @@ -87,11 +82,7 @@ describe('InternalNotificationController - Send ruling notifications', () => { describe('email to prosecutor for indictment case', () => { const caseId = uuid() - - const prosecutor = { - name: testProsecutor.name, - email: testProsecutor.email, - } + const prosecutor = { name: 'Lögmaður', email: 'logmadur@gmail.com' } const theCase = { id: caseId, type: CaseType.INDICTMENT, @@ -120,10 +111,7 @@ describe('InternalNotificationController - Send ruling notifications', () => { describe('email to prosecutor for restriction case', () => { const caseId = uuid() - const prosecutor = { - name: testProsecutor.name, - email: testProsecutor.email, - } + const prosecutor = { name: 'Lögmaður', email: 'logmadur@gmail.com' } const theCase = { id: caseId, state: CaseState.ACCEPTED, @@ -141,7 +129,7 @@ describe('InternalNotificationController - Send ruling notifications', () => { const expectedLink = `` expect(mockEmailService.sendEmail).toHaveBeenCalledTimes(2) expect(mockEmailService.sendEmail).toHaveBeenNthCalledWith( - 2, + 1, expect.objectContaining({ to: [{ name: prosecutor.name, address: prosecutor.email }], subject: 'Úrskurður í máli 007-2022-07', @@ -153,10 +141,7 @@ describe('InternalNotificationController - Send ruling notifications', () => { describe('email to prosecutor for modified ruling restriction case', () => { const caseId = uuid() - const prosecutor = { - name: testProsecutor.name, - email: testProsecutor.email, - } + const prosecutor = { name: 'Lögmaður', email: 'logmadur@gmail.com' } const theCase = { id: caseId, type: CaseType.CUSTODY, @@ -175,9 +160,9 @@ describe('InternalNotificationController - Send ruling notifications', () => { const expectedLink = `` expect(mockEmailService.sendEmail).toHaveBeenCalledTimes(2) expect(mockEmailService.sendEmail).toHaveBeenNthCalledWith( - 2, + 1, expect.objectContaining({ - to: [{ name: testProsecutor.name, address: testProsecutor.email }], + to: [{ name: prosecutor.name, address: prosecutor.email }], subject: 'Úrskurður í máli 007-2022-07 leiðréttur', html: `Dómari hefur leiðrétt úrskurð í máli 007-2022-07 hjá Héraðsdómi Reykjavíkur.

Skjöl málsins eru aðgengileg á ${expectedLink}yfirlitssíðu málsins í Réttarvörslugátt
.`, }), 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 89e83477f4a2..80603c97865d 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,4 +1,4 @@ -import { IsEnum, IsOptional, IsString, MaxLength } from 'class-validator' +import { IsBoolean, IsEnum, IsOptional, IsString } from 'class-validator' import { ApiPropertyOptional } from '@nestjs/swagger' @@ -11,7 +11,6 @@ export class UpdateSubpoenaDto { readonly serviceStatus?: ServiceStatus @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly servedBy?: string @@ -32,25 +31,21 @@ export class UpdateSubpoenaDto { readonly defenderChoice?: DefenderChoice @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly defenderNationalId?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly defenderName?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly defenderEmail?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly defenderPhoneNumber?: string @@ -61,14 +56,17 @@ export class UpdateSubpoenaDto { readonly requestedDefenderChoice?: DefenderChoice @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly requestedDefenderNationalId?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly requestedDefenderName?: string + + @IsOptional() + @IsBoolean() + @ApiPropertyOptional({ type: Boolean }) + readonly isDefenderChoiceConfirmed?: boolean } 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..411c8f3e4ef3 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 @@ -33,7 +33,7 @@ import { DeliverResponse } from './models/deliver.response' import { Subpoena } from './models/subpoena.model' import { SubpoenaService } from './subpoena.service' -@Controller('api/internal') +@Controller('api/internal/') @ApiTags('internal subpoenas') @UseGuards(TokenGuard) export class InternalSubpoenaController { 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..da84d9c66c59 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 @@ -5,6 +5,7 @@ import { Get, Header, Inject, + InternalServerErrorException, Param, Query, Res, diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.module.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.module.ts index 31fba1e49734..57b9d55af2be 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.module.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.module.ts @@ -4,7 +4,6 @@ import { SequelizeModule } from '@nestjs/sequelize' import { MessageModule } from '@island.is/judicial-system/message' import { CaseModule } from '../case/case.module' -import { DefendantModule } from '../defendant/defendant.module' import { Defendant } from '../defendant/models/defendant.model' import { EventModule } from '../event/event.module' import { FileModule } from '../file/file.module' @@ -22,7 +21,7 @@ import { SubpoenaService } from './subpoena.service' forwardRef(() => FileModule), forwardRef(() => MessageModule), forwardRef(() => EventModule), - forwardRef(() => DefendantModule), + SequelizeModule.forFeature([Subpoena, Defendant]), ], controllers: [ 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 64fc48de8dc9..9f7322f11fe3 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 @@ -20,6 +20,7 @@ import { } from '@island.is/judicial-system/message' import { CaseFileCategory, + DefenderChoice, isFailedServiceStatus, isSuccessfulServiceStatus, isTrafficViolationCase, @@ -30,7 +31,6 @@ import { import { Case } from '../case/models/case.model' import { PdfService } from '../case/pdf.service' -import { DefendantService } from '../defendant/defendant.service' import { Defendant } from '../defendant/models/defendant.model' import { EventService } from '../event' import { FileService } from '../file' @@ -61,7 +61,9 @@ export const include: Includeable[] = [ @Injectable() export class SubpoenaService { constructor( + @InjectConnection() private readonly sequelize: Sequelize, @InjectModel(Subpoena) private readonly subpoenaModel: typeof Subpoena, + @InjectModel(Defendant) private readonly defendantModel: typeof Defendant, private readonly pdfService: PdfService, private readonly messageService: MessageService, @Inject(forwardRef(() => PoliceService)) @@ -69,7 +71,6 @@ export class SubpoenaService { @Inject(forwardRef(() => FileService)) private readonly fileService: FileService, private readonly eventService: EventService, - private readonly defendantService: DefendantService, @Inject(LOGGER_PROVIDER) private readonly logger: Logger, ) {} @@ -110,36 +111,53 @@ export class SubpoenaService { private async addMessagesForSubpoenaUpdateToQueue( subpoena: Subpoena, serviceStatus?: ServiceStatus, + defenderChoice?: DefenderChoice, + defenderNationalId?: string, ): Promise { - let message: Message | undefined = undefined + const messages: Message[] = [] if (serviceStatus && serviceStatus !== subpoena.serviceStatus) { if (isSuccessfulServiceStatus(serviceStatus)) { - message = { + messages.push({ type: MessageType.SUBPOENA_NOTIFICATION, caseId: subpoena.caseId, elementId: [subpoena.defendantId, subpoena.id], body: { type: SubpoenaNotificationType.SERVICE_SUCCESSFUL, }, - } + }) } else if (isFailedServiceStatus(serviceStatus)) { - message = { + messages.push({ type: MessageType.SUBPOENA_NOTIFICATION, caseId: subpoena.caseId, elementId: [subpoena.defendantId, subpoena.id], body: { type: SubpoenaNotificationType.SERVICE_FAILED, }, - } + }) } } - if (!message) { + if ( + defenderChoice === DefenderChoice.CHOOSE && + (defenderChoice !== subpoena.defendant?.defenderChoice || + defenderNationalId !== subpoena.defendant?.defenderNationalId) + ) { + messages.push({ + type: MessageType.SUBPOENA_NOTIFICATION, + caseId: subpoena.caseId, + elementId: [subpoena.defendantId, subpoena.id], + body: { + type: SubpoenaNotificationType.DEFENDANT_SELECTED_DEFENDER, + }, + }) + } + + if (messages.length === 0) { return } - return this.messageService.sendMessagesToQueue([message]) + return this.messageService.sendMessagesToQueue(messages) } async update( @@ -173,53 +191,57 @@ export class SubpoenaService { } if ( - subpoena.case && - subpoena.defendant && - (defenderChoice || - defenderNationalId || - defenderName || - defenderEmail || - defenderPhoneNumber || - requestedDefenderChoice || - requestedDefenderNationalId || - requestedDefenderName) + defenderChoice || + defenderNationalId || + requestedDefenderChoice || + requestedDefenderNationalId ) { // 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 && + const isChangingDefenderChoice = + (defenderChoice && subpoena.defendant?.defenderChoice !== defenderChoice) || - (defenderNationalId && - subpoena.defendant?.defenderNationalId !== defenderNationalId)) + (defenderNationalId && + subpoena.defendant?.defenderNationalId !== defenderNationalId && + subpoena.defendant?.isDefenderChoiceConfirmed) + + const defendantUpdate: Partial = { + defenderChoice, + defenderNationalId, + defenderName, + defenderEmail, + defenderPhoneNumber, + requestedDefenderChoice, + requestedDefenderNationalId, + requestedDefenderName, + isDefenderChoiceConfirmed: isChangingDefenderChoice ? false : undefined, + } - // Færa message handling í defendant service - await this.defendantService.updateRestricted( - subpoena.case, - subpoena.defendant, + const [numberOfAffectedRows] = await this.defendantModel.update( + defendantUpdate, { - defenderChoice, - defenderNationalId, - defenderName, - defenderEmail, - defenderPhoneNumber, - requestedDefenderChoice, - requestedDefenderNationalId, - requestedDefenderName, + where: { id: subpoena.defendantId }, + transaction, }, - resetDefenderChoiceConfirmed ? false : undefined, - transaction, ) + + if (numberOfAffectedRows > 1) { + // Tolerate failure, but log error + this.logger.error( + `Unexpected number of rows ${numberOfAffectedRows} affected when updating defendant`, + ) + } } // No need to wait for this to finish - this.addMessagesForSubpoenaUpdateToQueue(subpoena, serviceStatus) + this.addMessagesForSubpoenaUpdateToQueue( + subpoena, + serviceStatus, + defenderChoice, + defenderNationalId, + ) - if ( - update.serviceStatus && - update.serviceStatus !== subpoena.serviceStatus && - subpoena.case - ) { + if (update.serviceStatus && subpoena.case) { this.eventService.postEvent( 'SUBPOENA_SERVICE_STATUS', subpoena.case, diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/test/createTestingSubpoenaModule.ts b/apps/judicial-system/backend/src/app/modules/subpoena/test/createTestingSubpoenaModule.ts index e397c3e498ac..4a7f93290f45 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/test/createTestingSubpoenaModule.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/test/createTestingSubpoenaModule.ts @@ -13,7 +13,7 @@ import { import { MessageService } from '@island.is/judicial-system/message' import { CaseService, PdfService } from '../../case' -import { Defendant, DefendantService } from '../../defendant' +import { Defendant } from '../../defendant' import { EventService } from '../../event' import { FileService } from '../../file' import { PoliceService } from '../../police' @@ -30,7 +30,6 @@ jest.mock('../../case/pdf.service') jest.mock('../../police/police.service') jest.mock('../../file/file.service') jest.mock('../../event/event.service') -jest.mock('../../defendant/defendant.service') jest.mock('@island.is/judicial-system/message') export const createTestingSubpoenaModule = async () => { @@ -50,7 +49,6 @@ export const createTestingSubpoenaModule = async () => { PoliceService, FileService, EventService, - DefendantService, { provide: LOGGER_PROVIDER, useValue: { diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/deliverSubpoenaToPolice.spec.ts b/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/deliverSubpoenaToPolice.spec.ts index 43255bd30aa1..c2f9ff68a082 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/deliverSubpoenaToPolice.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/deliverSubpoenaToPolice.spec.ts @@ -4,7 +4,6 @@ import { createTestingSubpoenaModule } from '../createTestingSubpoenaModule' import { Case } from '../../../case' import { Defendant } from '../../../defendant' -import { DeliverDto } from '../../dto/deliver.dto' import { DeliverResponse } from '../../models/deliver.response' import { Subpoena } from '../../models/subpoena.model' import { SubpoenaService } from '../../subpoena.service' @@ -23,7 +22,7 @@ describe('InternalSubpoenaController - Deliver subpoena to police', () => { const subpoena = { id: subpoenaId } as Subpoena const defendant = { id: defendantId, subpoenas: [subpoena] } as Defendant const theCase = { id: caseId } as Case - const user = { user: { id: uuid() } } as DeliverDto + const user = { user: { id: uuid() } } as any const delivered = { delivered: true } as DeliverResponse let mockSubpoenaService: SubpoenaService diff --git a/apps/judicial-system/backend/src/app/modules/user/dto/createUser.dto.ts b/apps/judicial-system/backend/src/app/modules/user/dto/createUser.dto.ts index d6b8207fba4d..1460431b37f7 100644 --- a/apps/judicial-system/backend/src/app/modules/user/dto/createUser.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/user/dto/createUser.dto.ts @@ -4,7 +4,6 @@ import { IsNotEmpty, IsString, IsUUID, - MaxLength, } from 'class-validator' import { ApiProperty } from '@nestjs/swagger' @@ -13,31 +12,26 @@ import { UserRole } from '@island.is/judicial-system/types' export class CreateUserDto { @IsNotEmpty() - @MaxLength(255) @IsString() @ApiProperty({ type: String }) readonly nationalId!: string @IsNotEmpty() - @MaxLength(255) @IsString() @ApiProperty({ type: String }) readonly name!: string @IsNotEmpty() - @MaxLength(255) @IsString() @ApiProperty({ type: String }) readonly title!: string @IsNotEmpty() - @MaxLength(255) @IsString() @ApiProperty({ type: String }) readonly mobileNumber!: string @IsNotEmpty() - @MaxLength(255) @IsString() @ApiProperty({ type: String }) readonly email!: string diff --git a/apps/judicial-system/backend/src/app/modules/user/dto/updateUser.dto.ts b/apps/judicial-system/backend/src/app/modules/user/dto/updateUser.dto.ts index 071bd218261b..fcebedbf7405 100644 --- a/apps/judicial-system/backend/src/app/modules/user/dto/updateUser.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/user/dto/updateUser.dto.ts @@ -4,7 +4,6 @@ import { IsOptional, IsString, IsUUID, - MaxLength, } from 'class-validator' import { ApiPropertyOptional } from '@nestjs/swagger' @@ -13,25 +12,21 @@ import { UserRole } from '@island.is/judicial-system/types' export class UpdateUserDto { @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly name?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly title?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly mobileNumber?: string @IsOptional() - @MaxLength(255) @IsString() @ApiPropertyOptional({ type: String }) readonly email?: string diff --git a/apps/judicial-system/robot-api/.eslintrc.json b/apps/judicial-system/robot-api/.eslintrc.json new file mode 100644 index 000000000000..eb74b2b984ef --- /dev/null +++ b/apps/judicial-system/robot-api/.eslintrc.json @@ -0,0 +1,35 @@ +{ + "extends": "../../../.eslintrc.json", + "rules": { + "simple-import-sort/imports": [ + "error", + { + "groups": [ + // External imports come first. + ["^\\w", "@(?!nestjs|island\\.is)"], + // Then nestjs packages. + ["^@nestjs"], + // Then island.is packages. + ["^@island\\.is/(?!judicial-system)"], + // Then judicial-system packages. + ["^@island\\.is/judicial-system"], + // Then all other imports in this order: "../", "./", "./strings", "./css" + [ + "^\\.\\.(?!/?$)", + "^\\.\\./?$", + "^\\./(?=.*/)(?!/?$)", + "^\\.(?!/?$)", + "^\\./?$" + ] + ] + } + ] + }, + "plugins": ["simple-import-sort"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], "rules": {} }, + { "files": ["*.ts", "*.tsx"], "rules": {} }, + { "files": ["*.js", "*.jsx"], "rules": {} } + ] +} diff --git a/apps/judicial-system/robot-api/esbuild.json b/apps/judicial-system/robot-api/esbuild.json new file mode 100644 index 000000000000..ed8694e4a00e --- /dev/null +++ b/apps/judicial-system/robot-api/esbuild.json @@ -0,0 +1,44 @@ +{ + "platform": "node", + "external": [ + "fsevents", + "@nestjs/microservices", + "class-transformer", + "cache-manager", + "@nestjs/websockets/socket-module", + "class-validator", + "class-transformer", + "@nestjs/microservices/microservices-module", + "apollo-server-fastify", + "@elastic/elasticsearch", + "fastify-swagger", + "@nestjs/mongoose", + "@nestjs/typeorm", + "dd-trace", + "express", + "http-errors", + "graphql", + "pg", + "winston", + "util-deprecate", + "source-map-resolve", + "atob", + "logform", + "pg-native", + "form-data", + "swagger-ui-dist", + "@protobufjs/aspromise", + "@protobufjs/base64", + "@protobufjs/codegen", + "@protobufjs/eventemitter", + "@protobufjs/fetch", + "@protobufjs/float", + "@protobufjs/inquire", + "@protobufjs/path", + "@protobufjs/pool", + "@protobufjs/utf8", + "safer-buffer", + "@mikro-orm/core" + ], + "keepNames": true +} diff --git a/apps/judicial-system/robot-api/infra/judicial-system-xrd-robot-api.ts b/apps/judicial-system/robot-api/infra/judicial-system-xrd-robot-api.ts new file mode 100644 index 000000000000..9eed7a919938 --- /dev/null +++ b/apps/judicial-system/robot-api/infra/judicial-system-xrd-robot-api.ts @@ -0,0 +1,33 @@ +import { ref, service, ServiceBuilder } from '../../../../infra/src/dsl/dsl' + +export const serviceSetup = (services: { + backend: ServiceBuilder<'judicial-system-backend'> +}): ServiceBuilder<'judicial-system-robot-api'> => + service('judicial-system-robot-api') + .namespace('judicial-system') + .serviceAccount('judicial-system-robot-api') + .image('judicial-system-robot-api') + .env({ + BACKEND_URL: ref((h) => `http://${h.svc(services.backend)}`), + AUDIT_TRAIL_USE_GENERIC_LOGGER: 'false', + AUDIT_TRAIL_GROUP_NAME: 'k8s/judicial-system/audit-log', + AUDIT_TRAIL_REGION: 'eu-west-1', + }) + .secrets({ + ERROR_EVENT_URL: '/k8s/judicial-system/ERROR_EVENT_URL', + BACKEND_ACCESS_TOKEN: '/k8s/judicial-system/BACKEND_ACCESS_TOKEN', + }) + .liveness('/liveness') + .readiness('/liveness') + .ingress({ + primary: { + host: { + dev: 'judicial-system-robot-api-xrd', + staging: 'judicial-system-robot-api-xrd', + prod: 'judicial-system-robot-api-xrd', + }, + paths: ['/'], + public: false, + }, + }) + .grantNamespaces('nginx-ingress-internal') diff --git a/apps/judicial-system/robot-api/jest.config.ts b/apps/judicial-system/robot-api/jest.config.ts new file mode 100644 index 000000000000..f306ee005a12 --- /dev/null +++ b/apps/judicial-system/robot-api/jest.config.ts @@ -0,0 +1,20 @@ +/* eslint-disable */ +export default { + preset: './jest.preset.js', + rootDir: '../../..', + roots: [__dirname], + globals: {}, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '/coverage/apps/judicial-system/robot-api', + displayName: 'judicial-system-robot-api', + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': [ + 'ts-jest', + { + tsconfig: `${__dirname}/tsconfig.spec.json`, + isolatedModules: true, + }, + ], + }, +} diff --git a/apps/judicial-system/robot-api/project.json b/apps/judicial-system/robot-api/project.json new file mode 100644 index 000000000000..181727e4a47e --- /dev/null +++ b/apps/judicial-system/robot-api/project.json @@ -0,0 +1,53 @@ +{ + "name": "judicial-system-robot-api", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/judicial-system/robot-api/src", + "projectType": "application", + "prefix": "judicial-system-robot-api", + "tags": ["scope:judicial-system-api"], + "generators": {}, + "targets": { + "build": { + "executor": "./tools/executors/node:build", + "options": { + "outputPath": "dist/apps/judicial-system/robot-api", + "main": "apps/judicial-system/robot-api/src/main.ts", + "tsConfig": "apps/judicial-system/robot-api/tsconfig.app.json", + "assets": [], + "maxWorkers": 2 + }, + "configurations": { + "production": { + "optimization": true, + "extractLicenses": true, + "inspect": false + } + } + }, + "serve": { + "executor": "@nx/js:node", + "options": { + "buildTarget": "judicial-system-robot-api:build" + } + }, + "lint": { + "executor": "@nx/eslint:lint" + }, + "test": { + "executor": "@nx/jest:jest", + "options": { + "jestConfig": "apps/judicial-system/robot-api/jest.config.ts" + } + }, + "codegen/backend-schema": { + "executor": "nx:run-commands", + "options": { + "command": "cross-env INIT_SCHEMA=true yarn ts-node -P apps/judicial-system/robot-api/tsconfig.app.json apps/judicial-system/robot-api/src/buildOpenApi.ts" + }, + "outputs": ["{projectRoot}/src/openapi.yaml"] + }, + "docker-express": { + "executor": "Intentionally left blank, only so this target is valid when using `nx show projects --with-target docker-express`" + } + } +} diff --git a/apps/judicial-system/robot-api/src/app/app.config.ts b/apps/judicial-system/robot-api/src/app/app.config.ts new file mode 100644 index 000000000000..5c52fbad0eac --- /dev/null +++ b/apps/judicial-system/robot-api/src/app/app.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from '@island.is/nest/config' + +export default defineConfig({ + name: 'AppModule', + load: (env) => ({ + errorReportUrl: env.required('ERROR_EVENT_URL', ''), + backend: { + accessToken: env.required( + 'BACKEND_ACCESS_TOKEN', + 'secret-backend-api-token', + ), + url: env.required('BACKEND_URL', 'http://localhost:3344'), + }, + }), +}) diff --git a/apps/judicial-system/robot-api/src/app/app.controller.ts b/apps/judicial-system/robot-api/src/app/app.controller.ts new file mode 100644 index 000000000000..5bda74f60457 --- /dev/null +++ b/apps/judicial-system/robot-api/src/app/app.controller.ts @@ -0,0 +1,23 @@ +import { Controller, Get, Inject } from '@nestjs/common' +import { ApiCreatedResponse } from '@nestjs/swagger' + +import type { Logger } from '@island.is/logging' +import { LOGGER_PROVIDER } from '@island.is/logging' + +import { AppService } from './app.service' + +@Controller('api') +export class AppController { + constructor( + private readonly appService: AppService, + @Inject(LOGGER_PROVIDER) private readonly logger: Logger, + ) {} + + @Get('test') + @ApiCreatedResponse({ type: String, description: 'Test connection' }) + async test(): Promise { + this.logger.debug('Testing connection') + + return this.appService.testConnection() + } +} diff --git a/apps/judicial-system/robot-api/src/app/app.module.ts b/apps/judicial-system/robot-api/src/app/app.module.ts new file mode 100644 index 000000000000..9ebdfe08242e --- /dev/null +++ b/apps/judicial-system/robot-api/src/app/app.module.ts @@ -0,0 +1,27 @@ +import { Module } from '@nestjs/common' +import { ConfigModule } from '@nestjs/config' + +import { ProblemModule } from '@island.is/nest/problem' + +import { + AuditTrailModule, + auditTrailModuleConfig, +} from '@island.is/judicial-system/audit-trail' + +import appModuleConfig from './app.config' +import { AppController } from './app.controller' +import { AppService } from './app.service' + +@Module({ + imports: [ + AuditTrailModule, + ProblemModule.forRoot({ logAllErrors: true }), + ConfigModule.forRoot({ + isGlobal: true, + load: [appModuleConfig, auditTrailModuleConfig], + }), + ], + controllers: [AppController], + providers: [AppService], +}) +export class AppModule {} diff --git a/apps/judicial-system/robot-api/src/app/app.service.ts b/apps/judicial-system/robot-api/src/app/app.service.ts new file mode 100644 index 000000000000..4b935505d940 --- /dev/null +++ b/apps/judicial-system/robot-api/src/app/app.service.ts @@ -0,0 +1,28 @@ +import { Inject, Injectable } from '@nestjs/common' + +import type { Logger } from '@island.is/logging' +import { LOGGER_PROVIDER } from '@island.is/logging' +import { type ConfigType } from '@island.is/nest/config' + +import { AuditTrailService } from '@island.is/judicial-system/audit-trail' + +import appModuleConfig from './app.config' + +@Injectable() +export class AppService { + constructor( + @Inject(appModuleConfig.KEY) + private readonly config: ConfigType, + @Inject(LOGGER_PROVIDER) private readonly logger: Logger, + private readonly auditTrailService: AuditTrailService, + ) {} + + private async test(): Promise { + return 'OK' + } + + async testConnection(): Promise { + //TODO: Audit + return this.test() + } +} diff --git a/apps/judicial-system/robot-api/src/app/index.ts b/apps/judicial-system/robot-api/src/app/index.ts new file mode 100644 index 000000000000..a49c6ea5487c --- /dev/null +++ b/apps/judicial-system/robot-api/src/app/index.ts @@ -0,0 +1 @@ +export { AppModule } from './app.module' diff --git a/apps/judicial-system/robot-api/src/buildOpenApi.ts b/apps/judicial-system/robot-api/src/buildOpenApi.ts new file mode 100644 index 000000000000..9a54cdcb918b --- /dev/null +++ b/apps/judicial-system/robot-api/src/buildOpenApi.ts @@ -0,0 +1,10 @@ +import { buildOpenApi } from '@island.is/infra-nest-server' + +import { AppModule } from './app/app.module' +import { openApi } from './openApi' + +buildOpenApi({ + path: 'apps/judicial-system/robot-api/src/openapi.yaml', + appModule: AppModule, + openApi, +}) diff --git a/apps/judicial-system/robot-api/src/main.ts b/apps/judicial-system/robot-api/src/main.ts new file mode 100644 index 000000000000..f41d5432d2f0 --- /dev/null +++ b/apps/judicial-system/robot-api/src/main.ts @@ -0,0 +1,12 @@ +import { bootstrap } from '@island.is/infra-nest-server' + +import { AppModule } from './app' +import { openApi } from './openApi' + +bootstrap({ + appModule: AppModule, + name: 'judicial-system-robot-api', + port: 3356, + swaggerPath: 'api/swagger', + openApi, +}) diff --git a/apps/judicial-system/robot-api/src/openApi.ts b/apps/judicial-system/robot-api/src/openApi.ts new file mode 100644 index 000000000000..3402ffe5a476 --- /dev/null +++ b/apps/judicial-system/robot-api/src/openApi.ts @@ -0,0 +1,8 @@ +import { DocumentBuilder } from '@nestjs/swagger' + +export const openApi = new DocumentBuilder() + .setTitle('Judicial System xRoad robot API') + .setDescription('This is the xRoad robot API for the judicial system.') + .setVersion('1.0') + .addTag('judicial-system') + .build() diff --git a/apps/judicial-system/robot-api/tsconfig.app.json b/apps/judicial-system/robot-api/tsconfig.app.json new file mode 100644 index 000000000000..5479e745834a --- /dev/null +++ b/apps/judicial-system/robot-api/tsconfig.app.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": ["node"] + }, + "exclude": ["**/*.spec.ts", "**/*.test.ts", "**/test/**", "jest.config.ts"], + "include": ["**/*.ts"] +} diff --git a/apps/judicial-system/robot-api/tsconfig.json b/apps/judicial-system/robot-api/tsconfig.json new file mode 100644 index 000000000000..25873177db3f --- /dev/null +++ b/apps/judicial-system/robot-api/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/apps/judicial-system/robot-api/tsconfig.spec.json b/apps/judicial-system/robot-api/tsconfig.spec.json new file mode 100644 index 000000000000..e87789c595d9 --- /dev/null +++ b/apps/judicial-system/robot-api/tsconfig.spec.json @@ -0,0 +1,15 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "**/*.spec.ts", + "**/*.test.ts", + "**/test/*.ts", + "**/*.d.ts", + "jest.config.ts" + ] +} diff --git a/apps/judicial-system/web/src/components/InfoCard/DefendantInfo/DefendantInfo.tsx b/apps/judicial-system/web/src/components/InfoCard/DefendantInfo/DefendantInfo.tsx index eadbf98ea3c6..7d860a8f9f47 100644 --- a/apps/judicial-system/web/src/components/InfoCard/DefendantInfo/DefendantInfo.tsx +++ b/apps/judicial-system/web/src/components/InfoCard/DefendantInfo/DefendantInfo.tsx @@ -54,11 +54,14 @@ export const getAppealExpirationInfo = ( const getVerdictViewDateText = ( formatMessage: IntlShape['formatMessage'], verdictViewDate?: string | null, + serviceNotRequired?: boolean, ): string => { if (verdictViewDate) { return formatMessage(strings.verdictDisplayedDate, { date: formatDate(verdictViewDate, 'PPP'), }) + } else if (serviceNotRequired) { + return formatMessage(strings.serviceNotRequired) } else { return formatMessage(strings.serviceRequired) } @@ -126,19 +129,23 @@ export const DefendantInfo: FC = (props) => { )} {displayAppealExpirationInfo && ( - - {formatMessage(appealExpirationInfo.message, { - appealExpirationDate: appealExpirationInfo.date, - })} + + + {formatMessage(appealExpirationInfo.message, { + appealExpirationDate: appealExpirationInfo.date, + })} + + + )} + {displayVerdictViewDate && defendant.serviceRequirement && ( + + {getVerdictViewDateText( + formatMessage, + defendant.verdictViewDate, + defendant.serviceRequirement === ServiceRequirement.NOT_REQUIRED, + )} )} - {displayVerdictViewDate && - defendant.serviceRequirement && - defendant.serviceRequirement !== ServiceRequirement.NOT_REQUIRED && ( - - {getVerdictViewDateText(formatMessage, defendant.verdictViewDate)} - - )} ) } diff --git a/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Processing/Processing.tsx b/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Processing/Processing.tsx index f7b17f45e9e1..c2a21ccaf8a9 100644 --- a/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Processing/Processing.tsx +++ b/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Processing/Processing.tsx @@ -258,20 +258,17 @@ const Processing: FC = () => { ) useEffect(() => { - if (!civilClaimantNationalIdUpdate) { + if (!personData || !personData.items || personData.items.length === 0) { + setNationalIdNotFound(true) return } - const items = personData?.items || [] - const person = items[0] - - setNationalIdNotFound(items.length === 0) - + setNationalIdNotFound(false) const update = { caseId: workingCase.id, - civilClaimantId: civilClaimantNationalIdUpdate.civilClaimantId || '', - nationalId: civilClaimantNationalIdUpdate.nationalId, - ...(person?.name ? { name: person.name } : {}), + civilClaimantId: civilClaimantNationalIdUpdate?.civilClaimantId || '', + name: personData?.items[0].name, + nationalId: personData.items[0].kennitala, } handleUpdateCivilClaimant(update) 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 2470cb389ec4..58d28cb6146b 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 @@ -336,7 +336,6 @@ const Defendant = () => { setWorkingCase, ) } - maxLength={255} /> diff --git a/apps/native/app/src/graphql/queries/health.graphql b/apps/native/app/src/graphql/queries/health.graphql index ee4446c8f783..d0a5d93185da 100644 --- a/apps/native/app/src/graphql/queries/health.graphql +++ b/apps/native/app/src/graphql/queries/health.graphql @@ -77,19 +77,3 @@ query GetVaccinations($locale: String) { } } } - -query GetOrganDonorStatus($locale: String) { - healthDirectorateOrganDonation(locale: $locale) { - donor { - isDonor - limitations { - hasLimitations - limitedOrgansList { - id - name - } - comment - } - } - } -} diff --git a/apps/native/app/src/graphql/queries/inbox.graphql b/apps/native/app/src/graphql/queries/inbox.graphql index ff129a3e424a..73209b4263fc 100644 --- a/apps/native/app/src/graphql/queries/inbox.graphql +++ b/apps/native/app/src/graphql/queries/inbox.graphql @@ -48,10 +48,3 @@ mutation PostMailActionMutation($input: DocumentsV2MailActionInput!) { success } } - -query DocumentConfirmActions($input: DocumentConfirmActionsInput!) { - documentV2ConfirmActions(input: $input) { - id - confirmed - } -} diff --git a/apps/native/app/src/messages/en.ts b/apps/native/app/src/messages/en.ts index 24a6e3413d63..734168b2c9bb 100644 --- a/apps/native/app/src/messages/en.ts +++ b/apps/native/app/src/messages/en.ts @@ -594,9 +594,6 @@ export const en: TranslatedMessages = { 'problem.offline.title': 'No internet connection', 'problem.offline.message': 'An error occurred while communicating with the service provider', - 'problem.thirdParty.title': 'Service unreachable', - 'problem.thirdParty.message': - 'An error occurred while communicating with the service provider', // passkeys 'passkeys.headingTitle': 'Sign in with Island.is app', @@ -662,18 +659,4 @@ export const en: TranslatedMessages = { 'health.vaccinations.noVaccinationsDescription': 'If you believe you have data that should appear here, please contact service provider.', 'health.vaccinations.directorateOfHealth': 'The directorate of Health', - - // health - organ donation - 'health.organDonation': 'Organ Donation', - 'health.organDonation.change': 'Breyta afstöðu', - 'health.organDonation.isDonor': 'Ég er líffæragjafi', - 'health.organDonation.isDonorWithLimitations': - 'Ég heimila líffæragjöf, með takmörkunum.', - 'health.organDonation.isNotDonor': 'Ég heimila ekki líffæragjöf', - 'health.organDonation.isDonorDescription': - 'Öll líffærin mín má nota til ígræðslu.', - 'health.organDonation.isNotDonorDescription': - 'Engin líffæri má nota til ígræðslu.', - 'health.organDonation.isDonorWithLimitationsDescription': - 'Öll líffærin mín má nota til ígræðslu fyrir utan: {limitations}.', } diff --git a/apps/native/app/src/messages/is.ts b/apps/native/app/src/messages/is.ts index a0ee501f24b1..0135ba4a659e 100644 --- a/apps/native/app/src/messages/is.ts +++ b/apps/native/app/src/messages/is.ts @@ -595,8 +595,6 @@ export const is = { 'Ef þú telur þig eiga gögn sem ættu að birtast hér, vinsamlegast hafðu samband við þjónustuaðila.', 'problem.offline.title': 'Samband næst ekki', 'problem.offline.message': 'Villa kom upp í samskiptum við þjónustuaðila', - 'problem.thirdParty.title': 'Samband næst ekki', - 'problem.thirdParty.message': 'Villa kom upp í samskiptum við þjónustuaðila', // passkeys 'passkeys.headingTitle': 'Innskrá með Ísland.is appinu', @@ -663,18 +661,4 @@ export const is = { 'health.vaccinations.noVaccinationsDescription': 'Ef þú telur þig eiga gögn sem ættu að birtast hér, vinsamlegast hafðu samband við þjónustuaðila.', 'health.vaccinations.directorateOfHealth': 'Embætti landlæknis', - - // health - organ donation - 'health.organDonation': 'Líffæragjöf', - 'health.organDonation.change': 'Breyta afstöðu', - 'health.organDonation.isDonor': 'Ég er líffæragjafi', - 'health.organDonation.isDonorWithLimitations': - 'Ég heimila líffæragjöf, með takmörkunum.', - 'health.organDonation.isNotDonor': 'Ég heimila ekki líffæragjöf', - 'health.organDonation.isDonorDescription': - 'Öll líffærin mín má nota til ígræðslu.', - 'health.organDonation.isNotDonorDescription': - 'Engin líffæri má nota til ígræðslu.', - 'health.organDonation.isDonorWithLimitationsDescription': - 'Öll líffærin mín má nota til ígræðslu fyrir utan: {limitations}.', } 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..e70d6f884bfc 100644 --- a/apps/native/app/src/screens/document-detail/document-detail.tsx +++ b/apps/native/app/src/screens/document-detail/document-detail.tsx @@ -27,7 +27,6 @@ import { DocumentV2Action, ListDocumentFragmentDoc, useGetDocumentQuery, - useDocumentConfirmActionsLazyQuery, } from '../../graphql/types/schema' import { createNavigationOptionHooks } from '../../hooks/create-navigation-option-hooks' import { useConnectivityIndicator } from '../../hooks/use-connectivity-indicator' @@ -249,17 +248,6 @@ export const DocumentDetailScreen: NavigationFunctionComponent<{ const [pdfUrl, setPdfUrl] = useState('') const [refetching, setRefetching] = useState(false) - const [logConfirmedAction] = useDocumentConfirmActionsLazyQuery({ - fetchPolicy: 'no-cache', - }) - - const confirmAction = async (confirmed: boolean) => { - // Adding a suffix '_app' to the id since the backend is currently not distinguishing between the app and the web - await logConfirmedAction({ - variables: { input: { id: `${docId}_app`, confirmed: confirmed } }, - }) - } - const refetchDocumentContent = async () => { setRefetching(true) try { @@ -280,17 +268,11 @@ export const DocumentDetailScreen: NavigationFunctionComponent<{ { text: intl.formatMessage({ id: 'inbox.markAllAsReadPromptCancel' }), style: 'cancel', - onPress: async () => { - await confirmAction(false) - Navigation.pop(componentId) - }, + onPress: () => Navigation.pop(componentId), }, { text: intl.formatMessage({ id: 'inbox.openDocument' }), - onPress: async () => { - await confirmAction(true) - await refetchDocumentContent() - }, + onPress: refetchDocumentContent, }, ]) } diff --git a/apps/native/app/src/screens/health/health-overview.tsx b/apps/native/app/src/screens/health/health-overview.tsx index 740cac1bbb45..25fce0d06d70 100644 --- a/apps/native/app/src/screens/health/health-overview.tsx +++ b/apps/native/app/src/screens/health/health-overview.tsx @@ -1,12 +1,4 @@ -import { - Alert, - Button, - Heading, - Input, - InputRow, - Problem, - Typography, -} from '@ui' +import { Alert, Button, Heading, Input, InputRow, Typography } from '@ui' import React, { useCallback, useMemo, useState } from 'react' import { FormattedMessage, useIntl } from 'react-intl' import { @@ -20,13 +12,11 @@ import { } from 'react-native' import { NavigationFunctionComponent } from 'react-native-navigation' import styled, { useTheme } from 'styled-components/native' -import { ApolloError } from '@apollo/client' import { useGetHealthCenterQuery, useGetHealthInsuranceOverviewQuery, useGetMedicineDataQuery, - useGetOrganDonorStatusQuery, useGetPaymentOverviewQuery, useGetPaymentStatusQuery, } from '../../graphql/types/schema' @@ -37,7 +27,6 @@ import { useBrowser } from '../../lib/use-browser' import { useConnectivityIndicator } from '../../hooks/use-connectivity-indicator' import { navigateTo } from '../../lib/deep-linking' import { useFeatureFlag } from '../../contexts/feature-flag-provider' -import { useLocale } from '../../hooks/use-locale' const Host = styled(SafeAreaView)` padding-horizontal: ${({ theme }) => theme.spacing[2]}px; @@ -54,15 +43,10 @@ const ButtonWrapper = styled.View` interface HeadingSectionProps { title: string - linkTextId?: string onPress: () => void } -const HeadingSection: React.FC = ({ - title, - onPress, - linkTextId, -}) => { +const HeadingSection: React.FC = ({ title, onPress }) => { const theme = useTheme() return ( @@ -81,7 +65,7 @@ const HeadingSection: React.FC = ({ color={theme.color.blue400} style={{ marginRight: 4 }} > - + @@ -93,10 +77,6 @@ const HeadingSection: React.FC = ({ ) } -const showErrorComponent = (error: ApolloError) => { - return -} - const { getNavigationOptions, useNavigationOptions } = createNavigationOptionHooks((theme, intl) => ({ topBar: { @@ -118,17 +98,10 @@ export const HealthOverviewScreen: NavigationFunctionComponent = ({ const { width } = useWindowDimensions() const buttonStyle = { flex: 1, minWidth: width * 0.5 - theme.spacing[3] } const isVaccinationsEnabled = useFeatureFlag('isVaccinationsEnabled', false) - const isOrganDonationEnabled = useFeatureFlag('isOrganDonationEnabled', false) const now = useMemo(() => new Date().toISOString(), []) const medicinePurchaseRes = useGetMedicineDataQuery() - const organDonationRes = useGetOrganDonorStatusQuery({ - variables: { - locale: useLocale(), - }, - skip: !isOrganDonationEnabled, - }) const healthInsuranceRes = useGetHealthInsuranceOverviewQuery() const healthCenterRes = useGetHealthCenterQuery() const paymentStatusRes = useGetPaymentStatusQuery() @@ -151,25 +124,12 @@ export const HealthOverviewScreen: NavigationFunctionComponent = ({ const paymentStatusData = paymentStatusRes.data?.rightsPortalCopaymentStatus const paymentOverviewData = paymentOverviewRes.data?.rightsPortalPaymentOverview?.items?.[0] - const organDonationData = - organDonationRes.data?.healthDirectorateOrganDonation.donor const isMedicinePeriodActive = medicinePurchaseData?.active || (medicinePurchaseData?.dateTo && new Date(medicinePurchaseData.dateTo) > new Date()) - const isOrganDonor = organDonationData?.isDonor ?? false - - const isOrganDonorWithLimitations = - isOrganDonor && (organDonationData?.limitations?.hasLimitations ?? false) - - const organLimitations = isOrganDonorWithLimitations - ? organDonationData?.limitations?.limitedOrgansList?.map( - (organ) => organ.name, - ) ?? [] - : [] - useConnectivityIndicator({ componentId, refetching, @@ -192,8 +152,7 @@ export const HealthOverviewScreen: NavigationFunctionComponent = ({ healthCenterRes.refetch(), paymentStatusRes.refetch(), paymentOverviewRes.refetch(), - isOrganDonationEnabled && organDonationRes.refetch(), - ].filter(Boolean) + ] await Promise.all(promises) } catch (e) { // noop @@ -206,8 +165,6 @@ export const HealthOverviewScreen: NavigationFunctionComponent = ({ healthCenterRes, paymentStatusRes, paymentOverviewRes, - organDonationRes, - isOrganDonationEnabled, ]) return ( @@ -290,57 +247,49 @@ export const HealthOverviewScreen: NavigationFunctionComponent = ({ ) } /> - {(healthCenterRes.data || healthCenterRes.loading) && ( - <> - - - - - - - - )} - {healthCenterRes.error && - !healthCenterRes.data && - showErrorComponent(healthCenterRes.error)} + + + + + + openBrowser(`${origin}/minarsidur/heilsa/yfirlit`, componentId) } /> - {(healthInsuranceRes.data && healthInsuranceData?.isInsured) || - healthInsuranceRes.loading ? ( + {healthInsuranceData?.isInsured || healthInsuranceRes.loading ? ( ) : ( - !healthInsuranceRes.error && - healthInsuranceRes.data && ( - - ) + )} - {healthInsuranceRes.error && - !healthInsuranceRes.data && - showErrorComponent(healthInsuranceRes.error)} - {(paymentOverviewRes.loading || paymentOverviewRes.data) && ( - <> - - - - - - - - - - - - )} - {paymentOverviewRes.error && - !paymentOverviewRes.data && - paymentStatusRes.error && - !paymentStatusRes.data && - showErrorComponent(paymentOverviewRes.error)} + + + + + + + + + + - {(medicinePurchaseRes.loading || medicinePurchaseRes.data) && ( - <> - - - - - - - - )} - {medicinePurchaseRes.error && - !medicinePurchaseRes.data && - showErrorComponent(medicinePurchaseRes.error)} - {isOrganDonationEnabled && ( - + - openBrowser( - `${origin}/minarsidur/heilsa/liffaeragjof/skraning`, - componentId, - ) + value={ + medicinePurchaseData?.dateFrom && medicinePurchaseData?.dateTo + ? `${intl.formatDate( + medicinePurchaseData.dateFrom, + )} - ${intl.formatDate(medicinePurchaseData.dateTo)}` + : '' } + loading={medicinePurchaseRes.loading && !medicinePurchaseRes.data} + error={medicinePurchaseRes.error && !medicinePurchaseRes.data} + darkBorder /> - )} - {isOrganDonationEnabled && ( - - - - )} - {isOrganDonationEnabled && - organDonationRes.error && - !organDonationRes.data && - showErrorComponent(organDonationRes.error)} + + + + diff --git a/apps/native/app/src/stores/organizations-store.ts b/apps/native/app/src/stores/organizations-store.ts index 3b9cb837e73a..2d3580092443 100644 --- a/apps/native/app/src/stores/organizations-store.ts +++ b/apps/native/app/src/stores/organizations-store.ts @@ -30,7 +30,6 @@ interface Organization { interface OrganizationsStore extends State { organizations: Organization[] getOrganizationLogoUrl(forName: string, size?: number): ImageSourcePropType - getOrganizationNameBySlug(slug: string): string actions: any } @@ -73,10 +72,6 @@ export const organizationsStore = create( const uri = `${url}?w=${size}&h=${size}&fit=pad&fm=png` return { uri } }, - getOrganizationNameBySlug(slug: string) { - const org = get().organizations.find((o) => o.slug === slug) - return org?.title ?? '' - }, actions: { updateOriganizations: async () => { const client = await getApolloClientAsync() 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..b15358fbdc05 100644 --- a/apps/native/app/src/ui/lib/problem/problem-template.tsx +++ b/apps/native/app/src/ui/lib/problem/problem-template.tsx @@ -10,7 +10,6 @@ export type ProblemTemplateBaseProps = { title: string message: string | ReactNode withContainer?: boolean - size?: 'small' | 'large' } interface WithIconProps extends ProblemTemplateBaseProps { @@ -69,7 +68,6 @@ const getColorsByVariant = ( const Host = styled.View<{ borderColor: Colors noContainer?: boolean - size: 'small' | 'large' }>` border-color: ${({ borderColor, theme }) => theme.color[borderColor]}; border-width: 1px; @@ -78,12 +76,11 @@ const Host = styled.View<{ justify-content: center; align-items: center; flex: 1; - row-gap: ${({ theme, size }) => - size === 'small' ? theme.spacing[2] : theme.spacing[3]}px; + row-gap: ${({ theme }) => theme.spacing[3]}px; padding: ${({ theme }) => theme.spacing[2]}px; ${({ noContainer, theme }) => noContainer && `margin: ${theme.spacing[2]}px;`} - min-height: ${({ size }) => (size === 'large' ? '280' : '142')}px; + min-height: 280px; ` const Tag = styled(View)<{ @@ -117,13 +114,12 @@ export const ProblemTemplate = ({ showIcon, tag, withContainer, - size = 'large', }: ProblemTemplateProps) => { const { borderColor, tagColor, tagBackgroundColor } = getColorsByVariant(variant) return ( - + {tag && ( @@ -133,18 +129,10 @@ export const ProblemTemplate = ({ )} {showIcon && } - + {title} - - {message} - + {message} ) diff --git a/apps/native/app/src/ui/lib/problem/problem.tsx b/apps/native/app/src/ui/lib/problem/problem.tsx index 4fa3d4e984d1..40963f69836c 100644 --- a/apps/native/app/src/ui/lib/problem/problem.tsx +++ b/apps/native/app/src/ui/lib/problem/problem.tsx @@ -1,10 +1,7 @@ import { useEffect } from 'react' - import { useTranslate } from '../../../hooks/use-translate' import { useOfflineStore } from '../../../stores/offline-store' import { ProblemTemplate, ProblemTemplateBaseProps } from './problem-template' -import { getOrganizationSlugFromError } from '../../../utils/get-organization-slug-from-error' -import { ThirdPartyServiceError } from './third-party-service-error' enum ProblemTypes { error = 'error', @@ -23,7 +20,7 @@ type ProblemBaseProps = { title?: string message?: string logError?: boolean -} & Pick +} & Pick interface ErrorProps extends ProblemBaseProps { type?: 'error' @@ -64,7 +61,6 @@ export const Problem = ({ logError = false, withContainer, showIcon, - size = 'large', }: ProblemProps) => { const t = useTranslate() const { isConnected } = useOfflineStore() @@ -77,7 +73,6 @@ export const Problem = ({ message: message ?? t('problem.error.message'), tag: tag ?? t('problem.error.tag'), variant: 'error', - size: size ?? 'large', } as const useEffect(() => { @@ -95,7 +90,6 @@ export const Problem = ({ variant="warning" title={title ?? t('problem.offline.title')} message={message ?? t('problem.offline.message')} - size={size} /> ) } @@ -105,18 +99,6 @@ export const Problem = ({ switch (type) { case ProblemTypes.error: - if (error) { - const organizationSlug = getOrganizationSlugFromError(error) - - if (organizationSlug) { - return ( - - ) - } - } return case ProblemTypes.noData: @@ -127,7 +109,6 @@ export const Problem = ({ variant="info" title={title ?? t('problem.noData.title')} message={message ?? t('problem.noData.message')} - size={size} /> ) diff --git a/apps/native/app/src/ui/lib/problem/third-party-service-error.tsx b/apps/native/app/src/ui/lib/problem/third-party-service-error.tsx deleted file mode 100644 index d2a714c18694..000000000000 --- a/apps/native/app/src/ui/lib/problem/third-party-service-error.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react' -import { useIntl } from 'react-intl' - -import { ProblemTemplate } from './problem-template' -import { useOrganizationsStore } from '../../../stores/organizations-store' - -type ThirdPartyServiceErrorProps = { - organizationSlug: string - size: 'small' | 'large' -} - -export const ThirdPartyServiceError = ({ - organizationSlug, - size, -}: ThirdPartyServiceErrorProps) => { - const intl = useIntl() - - const { getOrganizationNameBySlug } = useOrganizationsStore() - const organizationName = getOrganizationNameBySlug(organizationSlug) - - return ( - - ) -} diff --git a/apps/native/app/src/utils/get-organization-slug-from-error.ts b/apps/native/app/src/utils/get-organization-slug-from-error.ts deleted file mode 100644 index 1463c87cb8db..000000000000 --- a/apps/native/app/src/utils/get-organization-slug-from-error.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { ApolloError } from '@apollo/client' - -type PartialProblem = { - organizationSlug?: string -} - -type CustomExtension = { - code: string - problem?: PartialProblem - exception?: { - problem?: PartialProblem - } -} - -/** - * Extracts the organization slug from the Apollo error, if it exists. - */ -export const getOrganizationSlugFromError = (error: ApolloError | unknown) => { - const graphQLErrors = (error as ApolloError)?.graphQLErrors - - if (graphQLErrors) { - for (const graphQLError of graphQLErrors) { - const extensions = graphQLError.extensions as CustomExtension - - const organizationSlug = - extensions?.problem?.organizationSlug ?? - extensions?.exception?.problem?.organizationSlug - - if (organizationSlug) { - return organizationSlug - } - } - } - - return undefined -} diff --git a/apps/native/app/src/utils/lifecycle/setup-globals.ts b/apps/native/app/src/utils/lifecycle/setup-globals.ts index ece34480d3c5..a7cc08f279ff 100644 --- a/apps/native/app/src/utils/lifecycle/setup-globals.ts +++ b/apps/native/app/src/utils/lifecycle/setup-globals.ts @@ -98,12 +98,13 @@ if (__DEV__) { getConfig().datadog ?? '', 'production', '2736367a-a841-492d-adef-6f5a509d6ec2', - false, // do not track User interactions (e.g.: Tap on buttons.) + true, // track User interactions (e.g.: Tap on buttons. You can use 'accessibilityLabel' element property to give tap action the name, otherwise element type will be reported) true, // track XHR Resources true, // track Errors ) ddconfig.nativeCrashReportEnabled = true + ddconfig.nativeViewTracking = true ddconfig.site = 'EU' ddconfig.serviceName = 'mobile-app' diff --git a/apps/services/auth/delegation-api/src/app/delegations/test/me-delegations.access-incoming.spec.ts b/apps/services/auth/delegation-api/src/app/delegations/test/me-delegations.access-incoming.spec.ts index 6f5c526d1188..81eea480ae5b 100644 --- a/apps/services/auth/delegation-api/src/app/delegations/test/me-delegations.access-incoming.spec.ts +++ b/apps/services/auth/delegation-api/src/app/delegations/test/me-delegations.access-incoming.spec.ts @@ -9,9 +9,7 @@ import { DelegationScope, DelegationsIndexService, } from '@island.is/auth-api-lib' -import { RskRelationshipsClient } from '@island.is/clients-rsk-relationships' import { NationalRegistryClientService } from '@island.is/clients/national-registry-v2' -import { CompanyRegistryClientService } from '@island.is/clients/rsk/company-registry' import { expectMatchingDelegations, FixtureFactory, @@ -43,16 +41,9 @@ describe('MeDelegationsController', () => { }) server = request(app.getHttpServer()) delegationIndexService = app.get(DelegationsIndexService) - const rskRelationshipsClientService = app.get(RskRelationshipsClient) const nationalRegistryClientService = app.get( NationalRegistryClientService, ) - const companyRegistryClientService = app.get( - CompanyRegistryClientService, - ) - jest - .spyOn(nationalRegistryClientService, 'getCustodyChildren') - .mockImplementation(async () => []) jest .spyOn(nationalRegistryClientService, 'getIndividual') .mockImplementation(async (nationalId: string) => @@ -61,12 +52,6 @@ describe('MeDelegationsController', () => { name: fromName, }), ) - jest - .spyOn(rskRelationshipsClientService, 'getIndividualRelationships') - .mockImplementation(async () => null) - jest - .spyOn(companyRegistryClientService, 'getCompany') - .mockImplementation(async () => null) jest .spyOn(delegationIndexService, 'indexDelegations') .mockImplementation() diff --git a/apps/services/auth/public-api/test/mocks/companyRegistryClientService.mock.ts b/apps/services/auth/public-api/test/mocks/companyRegistryClientService.mock.ts deleted file mode 100644 index fec204a16425..000000000000 --- a/apps/services/auth/public-api/test/mocks/companyRegistryClientService.mock.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { CompanyRegistryClientService } from '@island.is/clients/rsk/company-registry' - -export const CompanyRegistryClientServiceMock: Partial = - { - getCompany() { - return Promise.resolve(null) - }, - } diff --git a/apps/services/auth/public-api/test/mocks/index.ts b/apps/services/auth/public-api/test/mocks/index.ts index b56b101b6fd9..77ff6e030811 100644 --- a/apps/services/auth/public-api/test/mocks/index.ts +++ b/apps/services/auth/public-api/test/mocks/index.ts @@ -1,4 +1,3 @@ export * from './einstaklingurApi.mock' export * from './rskProcuringClient.mock' export * from './featureFlagService.mock' -export * from './companyRegistryClientService.mock' diff --git a/apps/services/auth/public-api/test/setup.ts b/apps/services/auth/public-api/test/setup.ts index e8eef786b36e..eb533a696e10 100644 --- a/apps/services/auth/public-api/test/setup.ts +++ b/apps/services/auth/public-api/test/setup.ts @@ -16,7 +16,6 @@ import { IndividualDto, NationalRegistryClientService, } from '@island.is/clients/national-registry-v2' -import { CompanyRegistryClientService } from '@island.is/clients/rsk/company-registry' import { ConfigType } from '@island.is/nest/config' import { FeatureFlagService } from '@island.is/nest/feature-flags' import { @@ -36,7 +35,6 @@ import { import { AppModule } from '../src/app/app.module' import { - CompanyRegistryClientServiceMock, createMockEinstaklingurApi, FeatureFlagServiceMock, RskProcuringClientMock, @@ -158,8 +156,6 @@ export const setupWithAuth = async ({ .useValue(createMockEinstaklingurApi(nationalRegistryUser)) .overrideProvider(RskRelationshipsClient) .useValue(RskProcuringClientMock) - .overrideProvider(CompanyRegistryClientService) - .useValue(CompanyRegistryClientServiceMock) .overrideProvider(DelegationConfig.KEY) .useValue(delegationConfig) .overrideProvider(FeatureFlagService) diff --git a/apps/services/endorsements/api/project.json b/apps/services/endorsements/api/project.json index 6298812ea242..22c4fe386761 100644 --- a/apps/services/endorsements/api/project.json +++ b/apps/services/endorsements/api/project.json @@ -33,11 +33,6 @@ "glob": "*", "input": "libs/email-service/src/tools/design", "output": "./email-service-assets" - }, - { - "glob": "*", - "input": "apps/services/endorsements/api/src/assets", - "output": "apps/services/endorsements/api/src/assets" } ] }, diff --git a/apps/services/user-notification/infra/user-notification.ts b/apps/services/user-notification/infra/user-notification.ts index d71a994b67c6..162a3a82d3d0 100644 --- a/apps/services/user-notification/infra/user-notification.ts +++ b/apps/services/user-notification/infra/user-notification.ts @@ -111,7 +111,6 @@ export const userNotificationServiceSetup = (services: { 'nginx-ingress-internal', 'islandis', 'identity-server-delegation', - 'application-system', ) export const userNotificationWorkerSetup = (services: { diff --git a/apps/web/components/Grant/HeaderWithImage/HeaderWithImage.css.ts b/apps/web/components/Grant/HeaderWithImage/HeaderWithImage.css.ts deleted file mode 100644 index 459cc3529107..000000000000 --- a/apps/web/components/Grant/HeaderWithImage/HeaderWithImage.css.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { style } from '@vanilla-extract/css' - -import { spacing } from '@island.is/island-ui/theme' - -export const introImage = style({ - marginBottom: -spacing[2], - maxHeight: '17em', - display: 'block', - margin: 'auto', - width: '100%', -}) diff --git a/apps/web/components/Grant/HeaderWithImage/HeaderWithImage.tsx b/apps/web/components/Grant/HeaderWithImage/HeaderWithImage.tsx deleted file mode 100644 index 9cd40930294d..000000000000 --- a/apps/web/components/Grant/HeaderWithImage/HeaderWithImage.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { ReactNode } from 'react' - -import { - Box, - GridColumn, - GridContainer, - GridRow, - Text, -} from '@island.is/island-ui/core' - -import * as styles from './HeaderWithImage.css' - -export type HeaderProps = { - title: string - description?: string - imageLayout?: 'left' | 'right' - breadcrumbs: ReactNode - children?: ReactNode -} - -export type ImageProps = HeaderProps & { - featuredImage: string - featuredImageAlt: string -} - -export type NoImageProps = HeaderProps & { - featuredImage: never - featuredImageAlt: never -} - -export type HeaderWithImageProps = ImageProps | NoImageProps - -export const HeaderWithImage = (props: HeaderWithImageProps) => { - const renderImage = () => { - if (!props.featuredImage) return null - - return ( - - - {props.featuredImageAlt} - - - ) - } - - const renderContent = () => ( - - {props.breadcrumbs} - - {props.title} - - {props.description && {props.description}} - {props.children} - - ) - - return ( - - - {props.imageLayout === 'left' && renderImage()} - {renderContent()} - {props.imageLayout === 'right' && renderImage()} - - - ) -} diff --git a/apps/web/components/Grant/HeaderWithImage/index.ts b/apps/web/components/Grant/HeaderWithImage/index.ts deleted file mode 100644 index a58794eff2d7..000000000000 --- a/apps/web/components/Grant/HeaderWithImage/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { HeaderWithImage } from './HeaderWithImage' - -export const GrantHeaderWithImage = HeaderWithImage diff --git a/apps/web/components/Grant/SearchSection/SearchSection.css.ts b/apps/web/components/Grant/SearchSection/SearchSection.css.ts deleted file mode 100644 index 459cc3529107..000000000000 --- a/apps/web/components/Grant/SearchSection/SearchSection.css.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { style } from '@vanilla-extract/css' - -import { spacing } from '@island.is/island-ui/theme' - -export const introImage = style({ - marginBottom: -spacing[2], - maxHeight: '17em', - display: 'block', - margin: 'auto', - width: '100%', -}) diff --git a/apps/web/components/Grant/SearchSection/SearchSection.tsx b/apps/web/components/Grant/SearchSection/SearchSection.tsx deleted file mode 100644 index 8eed1a10354f..000000000000 --- a/apps/web/components/Grant/SearchSection/SearchSection.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { - Box, - Inline, - Input, - Tag, - TagVariant, - Text, -} from '@island.is/island-ui/core' - -import { - HeaderWithImage, - HeaderWithImageProps, -} from '../HeaderWithImage/HeaderWithImage' - -type QuickLink = { - title: string - href: string - variant?: TagVariant -} - -type SearchSectionProps = HeaderWithImageProps & { - searchPlaceholder: string - quickLinks: Array - searchUrl: string - shortcutsTitle: string -} - -export const SearchSection = (props: SearchSectionProps) => { - return ( - - - - - - - - {props.shortcutsTitle} - - - {props.quickLinks.map((q) => ( - - {q.title} - - ))} - - - - ) -} diff --git a/apps/web/components/Grant/SearchSection/index.tsx b/apps/web/components/Grant/SearchSection/index.tsx deleted file mode 100644 index 88f6803f6f8f..000000000000 --- a/apps/web/components/Grant/SearchSection/index.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import { SearchSection } from './SearchSection' - -export const GrantSearchSection = SearchSection diff --git a/apps/web/components/Grant/Wrapper/GrantWrapper.tsx b/apps/web/components/Grant/Wrapper/GrantWrapper.tsx deleted file mode 100644 index 7e0296669044..000000000000 --- a/apps/web/components/Grant/Wrapper/GrantWrapper.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { ReactNode, useEffect, useState } from 'react' -import { useWindowSize } from 'react-use' - -import { BreadCrumbItem } from '@island.is/island-ui/core' -import { theme } from '@island.is/island-ui/theme' - -import { HeadWithSocialSharing } from '../../HeadWithSocialSharing/HeadWithSocialSharing' - -type WrapperProps = { - pageTitle: string - pageDescription?: string - pageFeaturedImage?: string - children?: ReactNode -} - -export const GrantWrapper = ({ - pageTitle, - pageDescription, - pageFeaturedImage, - children, -}: WrapperProps) => { - return ( - <> - - {children} - - ) -} diff --git a/apps/web/components/Grant/index.ts b/apps/web/components/Grant/index.ts deleted file mode 100644 index fc69b2811bdd..000000000000 --- a/apps/web/components/Grant/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { GrantWrapper } from './Wrapper/GrantWrapper' -export { GrantSearchSection } from './SearchSection' -export { GrantHeaderWithImage } from './HeaderWithImage' diff --git a/apps/web/components/Organization/MarkdownText/MarkdownText.css.ts b/apps/web/components/Organization/MarkdownText/MarkdownText.css.ts index 5f759495007a..e9ea7962a7cd 100644 --- a/apps/web/components/Organization/MarkdownText/MarkdownText.css.ts +++ b/apps/web/components/Organization/MarkdownText/MarkdownText.css.ts @@ -1,18 +1,7 @@ import { globalStyle, style } from '@vanilla-extract/css' -import { theme } from '@island.is/island-ui/theme' - export const markdownText = style({}) globalStyle(`${markdownText} ul`, { paddingTop: 16, }) - -globalStyle(`${markdownText} a`, { - color: theme.color.blue400, - textDecoration: 'underline', -}) - -globalStyle(`${markdownText} a:hover`, { - color: theme.color.blueberry400, -}) diff --git a/apps/web/components/Organization/Wrapper/OrganizationWrapper.tsx b/apps/web/components/Organization/Wrapper/OrganizationWrapper.tsx index 784ebd3e9d98..d73d63528bda 100644 --- a/apps/web/components/Organization/Wrapper/OrganizationWrapper.tsx +++ b/apps/web/components/Organization/Wrapper/OrganizationWrapper.tsx @@ -205,7 +205,13 @@ export const getThemeConfig = ( } } - return { themeConfig: { footerVersion } } + return { + themeConfig: { + headerColorScheme: 'white', + headerButtonColorScheme: 'negative', + footerVersion, + }, + } } export const OrganizationHeader: React.FC< diff --git a/apps/web/components/Organization/Wrapper/Themes/SyslumennTheme/SyslumennFooter.css.ts b/apps/web/components/Organization/Wrapper/Themes/SyslumennTheme/SyslumennFooter.css.ts index eba464a78291..dc3177722fa6 100644 --- a/apps/web/components/Organization/Wrapper/Themes/SyslumennTheme/SyslumennFooter.css.ts +++ b/apps/web/components/Organization/Wrapper/Themes/SyslumennTheme/SyslumennFooter.css.ts @@ -1,6 +1,5 @@ import { style } from '@vanilla-extract/css' - -import { theme, themeUtils } from '@island.is/island-ui/theme' +import { theme } from '@island.is/island-ui/theme' export const footerBg = style({ background: 'linear-gradient(99.09deg, #003D85 23.68%, #4E8ECC 123.07%)', @@ -13,15 +12,3 @@ export const footerItemFirst = style({ }, }, }) - -export const serviceWebFooterBg = style({ - background: theme.color.purple100, -}) - -export const linkMaxWidth = style({ - ...themeUtils.responsiveStyle({ - lg: { - maxWidth: '200px', - }, - }), -}) diff --git a/apps/web/components/Organization/Wrapper/Themes/SyslumennTheme/SyslumennFooter.tsx b/apps/web/components/Organization/Wrapper/Themes/SyslumennTheme/SyslumennFooter.tsx index fe6ecdcd3121..81099f9b6342 100644 --- a/apps/web/components/Organization/Wrapper/Themes/SyslumennTheme/SyslumennFooter.tsx +++ b/apps/web/components/Organization/Wrapper/Themes/SyslumennTheme/SyslumennFooter.tsx @@ -14,7 +14,6 @@ import { LinkProps, Text, } from '@island.is/island-ui/core' -import { theme } from '@island.is/island-ui/theme' import { GlobalContext } from '@island.is/web/context' import { FooterItem } from '@island.is/web/graphql/schema' import { LinkType, useLinkResolver, useNamespace } from '@island.is/web/hooks' @@ -39,15 +38,12 @@ const SyslumennFooter: React.FC> = ({ namespace, }) => { const n = useNamespace(namespace) + const questionsAndAnswersText = n('questionsAndAnswers', 'Spurningar og svör') const canWeHelpText = n('canWeHelp', 'Getum við aðstoðað?') const { activeLocale } = useI18n() const { isServiceWeb } = useContext(GlobalContext) - - const footerTitle = isServiceWeb ? n('serviceWebFooterTitle', title) : title - const footerTextColor: keyof typeof theme.color = isServiceWeb - ? 'dark400' - : 'white' + const { linkResolver } = useLinkResolver() const items = footerItems.map((item, index) => ( > = ({ {item.title} ) : ( - + {item.title} )} @@ -74,7 +70,7 @@ const SyslumennFooter: React.FC> = ({ { renderNode: { [BLOCKS.PARAGRAPH]: (_node: never, children: ReactNode) => ( - + {children} ), @@ -89,7 +85,7 @@ const SyslumennFooter: React.FC> = ({ @@ -109,8 +105,8 @@ const SyslumennFooter: React.FC> = ({ )}
- - {footerTitle} + + {title}
@@ -121,7 +117,6 @@ const SyslumennFooter: React.FC> = ({ <> > = ({ ) as string } > - {canWeHelpText} + {questionsAndAnswersText} - {n('showPrivacyPolicyLink', true) && ( - - + + - - - )} + {canWeHelpText} + + + @@ -180,7 +173,6 @@ interface HeaderLink { linkType?: LinkType underline?: LinkProps['underline'] slug: string - color?: keyof typeof theme.color } const HeaderLink: FC> = ({ @@ -188,7 +180,6 @@ const HeaderLink: FC> = ({ slug, children, underline = 'normal', - color = 'white', }) => { const { linkResolver } = useLinkResolver() @@ -207,7 +198,7 @@ const HeaderLink: FC> = ({ ), }} > - + - tag?: ActionCardProps['tag'] - cta?: ActionCardProps['cta'] -} - -export const PlazaCard = ({ - title, - text, - eyebrow, - subEyebrow, - detailLines, - tag, - logo, - logoAlt, - cta, -}: Props) => { - const renderLogo = () => { - if (!logo) { - return null - } - - return ( - - {logoAlt} - - ) - } - - const renderEyebrow = () => { - if (!eyebrow) { - return null - } - - return ( - - {subEyebrow ? ( - - - {eyebrow} - - - {subEyebrow} - - - ) : ( - - {eyebrow} - - )} - {renderLogo()} - - ) - } - - const renderDetails = () => { - if (!detailLines?.length) { - return null - } - - return ( - - - {detailLines?.map((d, index) => ( - - - - {d.text} - - - ))} - - - ) - } - - const renderTag = () => { - if (!tag) { - return null - } - - return {tag.label} - } - - const renderCta = () => { - if (!cta) { - return null - } - - return ( - - ) - } - - return ( - - - {renderEyebrow()} - - {title} - - {text && ( - - {text} - - )} - {renderDetails()} - - {renderTag()} - {renderCta()} - - - - ) -} diff --git a/apps/web/components/SearchSection/SearchSection.tsx b/apps/web/components/SearchSection/SearchSection.tsx index 2c19f84f4062..905d525b3f5f 100644 --- a/apps/web/components/SearchSection/SearchSection.tsx +++ b/apps/web/components/SearchSection/SearchSection.tsx @@ -1,26 +1,25 @@ import React, { ReactNode, useEffect, useState } from 'react' import { useWindowSize } from 'react-use' import dynamic from 'next/dynamic' - import { - Box, - GridColumn, + Text, GridContainer, GridRow, - Inline, - Link, + GridColumn, + Box, Stack, + Inline, Tag, - Text, + Link, } from '@island.is/island-ui/core' import { theme } from '@island.is/island-ui/theme' -import { TestSupport } from '@island.is/island-ui/utils' import { Locale } from '@island.is/shared/types' import { SearchInput } from '@island.is/web/components' -import { GetFrontpageQuery } from '@island.is/web/graphql/schema' import { LinkType, useLinkResolver } from '@island.is/web/hooks/useLinkResolver' +import { GetFrontpageQuery } from '@island.is/web/graphql/schema' import * as styles from './SearchSection.css' +import { TestSupport } from '@island.is/island-ui/utils' const DefaultIllustration = dynamic(() => import('./Illustration'), { ssr: false, diff --git a/apps/web/components/connected/WHODAS/Calculator.css.ts b/apps/web/components/connected/WHODAS/Calculator.css.ts deleted file mode 100644 index c98463701223..000000000000 --- a/apps/web/components/connected/WHODAS/Calculator.css.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { style } from '@vanilla-extract/css' - -import { theme } from '@island.is/island-ui/theme' - -export const breakdownRowContainer = style({ - display: 'grid', - gridTemplateColumns: '180px 1fr', - gap: theme.spacing[5], -}) diff --git a/apps/web/components/connected/WHODAS/Calculator.strings.ts b/apps/web/components/connected/WHODAS/Calculator.strings.ts deleted file mode 100644 index d1b82b8593f5..000000000000 --- a/apps/web/components/connected/WHODAS/Calculator.strings.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { defineMessages } from 'react-intl' - -export const m = { - form: defineMessages({ - previousStep: { - id: 'web.whodas.calculator:form.previousStep', - defaultMessage: 'Fyrra skref', - description: 'Fyrra skref', - }, - nextStep: { - id: 'web.whodas.calculator:form.nextStep', - defaultMessage: 'Næsta skref', - description: 'Næsta skref', - }, - seeResults: { - id: 'web.whodas.calculator:form.seeResults', - defaultMessage: 'Senda inn svör', - description: 'Senda inn svör', - }, - progress: { - id: 'web.whodas.calculator:form.progress', - defaultMessage: 'Skref {stepIndex} af {stepCount}', - description: 'Skref {stepIndex} af {stepCount}', - }, - }), - answerLabel: defineMessages({ - '0': { - id: 'web.whodas.calculator:form.answerLabel0', - defaultMessage: 'Ekkert erfitt', - description: 'Ekkert erfitt', - }, - '1': { - id: 'web.whodas.calculator:form.answerLabel1', - defaultMessage: 'Svolítið erfitt', - description: 'Svolítið erfitt', - }, - '2': { - id: 'web.whodas.calculator:form.answerLabel2', - defaultMessage: 'Nokkuð erfitt', - description: 'Nokkuð erfitt', - }, - '3': { - id: 'web.whodas.calculator:form.answerLabel3', - defaultMessage: 'Talsvert erfitt', - description: 'Talsvert erfitt', - }, - '4': { - id: 'web.whodas.calculator:form.answerLabel4', - defaultMessage: 'Mjög erfitt eða gekk ekki', - description: 'Mjög erfitt eða gekk ekki', - }, - }), - results: defineMessages({ - mainHeading: { - id: 'web.whodas.calculator:form.mainHeading', - defaultMessage: 'Niðurstaða mats á færni', - description: 'Niðurstaða mats á færni', - }, - print: { - id: 'web.whodas.calculator:form.print', - defaultMessage: 'Prenta', - description: 'Prenta', - }, - scoreHeading: { - id: 'web.whodas.calculator:form.scoreHeading', - 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', - description: 'Lítil skerðing á færni - Túlkun', - }, - secondBracketInterpretationText: { - id: 'web.whodas.calculator:form.secondBracketInterpretationText', - defaultMessage: 'Talsverð færniskerðing', - description: 'Talsverð færniskerðing - Túlkun', - }, - firstBracketAdviceText: { - id: 'web.whodas.calculator:form.firstBracketAdviceText#markdown', - defaultMessage: 'Mælum með að skoða hvað er í boði í þínu nærsamfélagi.', - description: 'Lítil skerðing á færni - Ráð', - }, - secondBracketAdviceText: { - id: 'web.whodas.calculator:form.secondBracketAdviceText#markdown', - defaultMessage: 'Vert er að sækja um heimaþjónustu í þínu sveitarfélagi.', - description: 'Talsverð færniskerðing - Ráð', - }, - interpretationHeading: { - id: 'web.whodas.calculator:form.interpretationHeading', - defaultMessage: 'Túlkun', - description: 'Túlkun', - }, - adviceHeading: { - id: 'web.whodas.calculator:form.adviceHeading', - defaultMessage: 'Ráðleggingar', - description: 'Ráðleggingar', - }, - breakdownHeading: { - id: 'web.whodas.calculator:form.breakdownHeading', - defaultMessage: 'Sundurliðun á niðurstöðum', - description: 'Sundurliðun á niðurstöðum', - }, - }), -} diff --git a/apps/web/components/connected/WHODAS/Calculator.tsx b/apps/web/components/connected/WHODAS/Calculator.tsx deleted file mode 100644 index ee3592b173c1..000000000000 --- a/apps/web/components/connected/WHODAS/Calculator.tsx +++ /dev/null @@ -1,318 +0,0 @@ -import { - type Dispatch, - type SetStateAction, - useEffect, - useRef, - useState, -} from 'react' -import { useIntl } from 'react-intl' - -import { - Box, - Bullet, - BulletList, - Button, - Inline, - ProgressMeter, - RadioButton, - Stack, - Text, -} from '@island.is/island-ui/core' -import { ConnectedComponent } from '@island.is/web/graphql/schema' -import { useDateUtils } from '@island.is/web/i18n/useDateUtils' - -import { MarkdownText } from '../../Organization' -import { m } from './Calculator.strings' -import * as styles from './Calculator.css' - -interface Question { - question: string - answerOptions: { - score: number - }[] -} - -interface Step { - title: string - description: string - questions: Question[] -} - -interface CheckboxState { - steps: { - title: string - questions: { - selectedAnswerIndex: number - answerScore: number - }[] - }[] -} - -interface WHODASFormProps { - step: Step - stepIndex: number - state: CheckboxState - setState: Dispatch> -} - -const WHODASForm = ({ step, stepIndex, state, setState }: WHODASFormProps) => { - const formState = state.steps[stepIndex] - const { formatMessage } = useIntl() - return ( - - - - {step.title} - - {step.description} - - - {step.questions.map(({ question, answerOptions }, questionIndex) => { - const questionState = formState.questions[questionIndex] - return ( - - - {question} - - - {answerOptions.map((option, answerIndex) => { - const id = `${step.title}-${questionIndex}-${answerIndex}` - - if (answerIndex > 3) { - return null - } - - const label = formatMessage( - m.answerLabel[ - String(answerIndex) as keyof typeof m.answerLabel - ], - ) - return ( - { - questionState.selectedAnswerIndex = answerIndex - questionState.answerScore = option.score - setState((prevState) => ({ ...prevState })) - }} - /> - ) - })} - - - ) - })} - - - ) -} - -interface WHODASResultsProps { - results: { - steps: (Pick & { scoreForStep: number })[] - } - bracket: 1 | 2 -} - -const WHODASResults = ({ results, bracket }: 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( - bracket === 1 - ? m.results.firstBracketScoreText - : m.results.secondBracketScoreText, - )} - - - - - - {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.breakdownHeading)} - - - {results.steps.map((step) => ( - - - {step.title} - {step.scoreForStep} - - - ))} - - - )} - - ) -} - -interface WHODASCalculatorProps { - slice: ConnectedComponent -} - -export const WHODASCalculator = ({ slice }: WHODASCalculatorProps) => { - const [stepIndex, setStepIndex] = useState(0) - const formRef = useRef(null) - const steps = (slice.json?.steps ?? []) as Step[] - const initialRender = useRef(true) - - const step = steps[stepIndex] - const showResults = stepIndex >= steps.length - - const { formatMessage } = useIntl() - - const [state, setState] = useState({ - steps: steps.map(({ title, description, questions }) => ({ - title, - description, - questions: questions.map(() => ({ - selectedAnswerIndex: 0, - answerScore: 0, - })), - })), - }) - - useEffect(() => { - if (initialRender.current) { - initialRender.current = false - return - } - window.scrollTo({ - behavior: 'smooth', - top: formRef.current?.offsetTop ?? 0, - }) - }, [stepIndex]) - - if (showResults) { - let totalScore = 0 - const results: WHODASResultsProps['results'] = { - steps: [], - } - for (const stateStep of state.steps) { - let score = 0 - for (const question of stateStep.questions) { - score += question.answerScore - } - results.steps.push({ ...stateStep, scoreForStep: score }) - totalScore += score - } - return ( - - ) - } - - return ( - - - - - - {formatMessage(m.form.progress, { - stepIndex: stepIndex + 1, - stepCount: steps.length, - })} - - - - - - - - {stepIndex > 0 && ( - - )} - - - - ) -} diff --git a/apps/web/components/real.ts b/apps/web/components/real.ts index d6e81e23def1..e6f84f68ca80 100644 --- a/apps/web/components/real.ts +++ b/apps/web/components/real.ts @@ -12,7 +12,6 @@ */ export * from './Card/Card' -export * from './PlazaCard/PlazaCard' export * from './Header/Header' export * from './SearchInput/SearchInput' export * from './LanguageToggler/LanguageToggler' @@ -22,7 +21,6 @@ export * from './SideMenu/SideMenu' export * from './StoryList/StoryList' export * from './ContentLink/ContentLink' export * from './NewsCard/NewsCard' -export * from './Grant' export * from './LogoList/LogoList' export * from './List/List' export * from './Bullet/Bullet' diff --git a/apps/web/constants/index.ts b/apps/web/constants/index.ts index 09b99769622a..c52757d262e3 100644 --- a/apps/web/constants/index.ts +++ b/apps/web/constants/index.ts @@ -7,7 +7,6 @@ export const PROJECT_STORIES_TAG_ID = '9yqOTwQYzgyej5kItFTtd' export const ADGERDIR_INDIVIDUALS_TAG_ID = '4kLt3eRht5yJoakIHWsusb' export const ADGERDIR_COMPANIES_TAG_ID = '4ZWcwoW2IiB2AhtzQpzdIW' export const FRONTPAGE_NEWS_TAG_ID = 'forsidufrettir' -export const PLAUSIBLE_SCRIPT_SRC = - 'https://plausible.io/js/script.outbound-links.js' +export const PLAUSIBLE_SCRIPT_SRC = 'https://plausible.io/js/plausible.js' export const DIGITAL_ICELAND_PLAUSIBLE_TRACKING_DOMAIN = 'island.is/s/stafraent-island' diff --git a/apps/web/hooks/useLinkResolver/useLinkResolver.ts b/apps/web/hooks/useLinkResolver/useLinkResolver.ts index b295b3f6c28b..2e8796315fc5 100644 --- a/apps/web/hooks/useLinkResolver/useLinkResolver.ts +++ b/apps/web/hooks/useLinkResolver/useLinkResolver.ts @@ -307,18 +307,6 @@ export const routesTemplate = { is: '/undirskriftalistar', en: '/en/petitions', }, - styrkjatorg: { - is: '/styrkjatorg', - en: '', - }, - styrkjatorgsearch: { - is: '/styrkjatorg/leit', - en: '', - }, - styrkjatorggrant: { - is: '/styrkjatorg/styrkur/[id]', - en: '', - }, } // This considers one block ("[someVar]") to be one variable and ignores the path variables name diff --git a/apps/web/pages/en/o/[...slugs]/index.ts b/apps/web/pages/en/o/[slug]/[subSlug]/[genericListItemSlug]/index.ts similarity index 82% rename from apps/web/pages/en/o/[...slugs]/index.ts rename to apps/web/pages/en/o/[slug]/[subSlug]/[genericListItemSlug]/index.ts index 627c95b4f2c8..64f34948d7ef 100644 --- a/apps/web/pages/en/o/[...slugs]/index.ts +++ b/apps/web/pages/en/o/[slug]/[subSlug]/[genericListItemSlug]/index.ts @@ -1,11 +1,12 @@ import withApollo from '@island.is/web/graphql/withApollo' import { withLocale } from '@island.is/web/i18n' -import { Component } from '@island.is/web/pages/s/[...slugs]' +import { Component } from '@island.is/web/pages/s/[slug]/[subSlug]/[genericListItemSlug]' import { getServerSidePropsWrapper } from '@island.is/web/utils/getServerSidePropsWrapper' + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore make web strict const Screen = withApollo(withLocale('en')(Component)) -export const getServerSideProps = getServerSidePropsWrapper(Screen) - export default Screen + +export const getServerSideProps = getServerSidePropsWrapper(Screen) diff --git a/apps/web/pages/styrkjatorg/leit/index.ts b/apps/web/pages/en/o/[slug]/[subSlug]/index.ts similarity index 70% rename from apps/web/pages/styrkjatorg/leit/index.ts rename to apps/web/pages/en/o/[slug]/[subSlug]/index.ts index 6360f482e3ff..c5541b519da9 100644 --- a/apps/web/pages/styrkjatorg/leit/index.ts +++ b/apps/web/pages/en/o/[slug]/[subSlug]/index.ts @@ -1,11 +1,12 @@ +import '@island.is/api/mocks' + import withApollo from '@island.is/web/graphql/withApollo' import { withLocale } from '@island.is/web/i18n' -import GrantsSearchResults from '@island.is/web/screens/Grants/SearchResults/SearchResults' +import subPage from '@island.is/web/screens/Organization/SubPage' import { getServerSidePropsWrapper } from '@island.is/web/utils/getServerSidePropsWrapper' // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore make web strict -// -const Screen = withApollo(withLocale('is')(GrantsSearchResults)) +const Screen = withApollo(withLocale('en')(subPage)) export default Screen diff --git a/apps/web/pages/en/o/[slug]/events/[eventSlug].ts b/apps/web/pages/en/o/[slug]/events/[eventSlug].ts new file mode 100644 index 000000000000..a863c57b6b53 --- /dev/null +++ b/apps/web/pages/en/o/[slug]/events/[eventSlug].ts @@ -0,0 +1,12 @@ +import withApollo from '@island.is/web/graphql/withApollo' +import { withLocale } from '@island.is/web/i18n' +import OrganizationEventArticle from '@island.is/web/screens/Organization/OrganizationEvents/OrganizationEventArticle' +import { getServerSidePropsWrapper } from '@island.is/web/utils/getServerSidePropsWrapper' + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore make web strict +const Screen = withApollo(withLocale('en')(OrganizationEventArticle)) + +export default Screen + +export const getServerSideProps = getServerSidePropsWrapper(Screen) diff --git a/apps/web/pages/en/o/[slug]/events/index.ts b/apps/web/pages/en/o/[slug]/events/index.ts new file mode 100644 index 000000000000..72fac8ab4d2d --- /dev/null +++ b/apps/web/pages/en/o/[slug]/events/index.ts @@ -0,0 +1,12 @@ +import withApollo from '@island.is/web/graphql/withApollo' +import { withLocale } from '@island.is/web/i18n' +import OrganizationEventList from '@island.is/web/screens/Organization/OrganizationEvents/OrganizationEventList' +import { getServerSidePropsWrapper } from '@island.is/web/utils/getServerSidePropsWrapper' + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore make web strict +const Screen = withApollo(withLocale('en')(OrganizationEventList)) + +export default Screen + +export const getServerSideProps = getServerSidePropsWrapper(Screen) diff --git a/apps/web/pages/styrkjatorg/index.ts b/apps/web/pages/en/o/[slug]/index.ts similarity index 75% rename from apps/web/pages/styrkjatorg/index.ts rename to apps/web/pages/en/o/[slug]/index.ts index 407e2ceaa326..ee4fa0ec953a 100644 --- a/apps/web/pages/styrkjatorg/index.ts +++ b/apps/web/pages/en/o/[slug]/index.ts @@ -1,10 +1,10 @@ import withApollo from '@island.is/web/graphql/withApollo' import { withLocale } from '@island.is/web/i18n' -import GrantsHome from '@island.is/web/screens/Grants/Home/GrantsHome' +import syslumenn from '@island.is/web/screens/Organization/Home' import { getServerSidePropsWrapper } from '@island.is/web/utils/getServerSidePropsWrapper' // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore make web strict -const Screen = withApollo(withLocale('is')(GrantsHome)) +const Screen = withApollo(withLocale('en')(syslumenn)) export default Screen diff --git a/apps/web/pages/en/o/[slug]/news/[newsSlug]/index.tsx b/apps/web/pages/en/o/[slug]/news/[newsSlug]/index.tsx new file mode 100644 index 000000000000..91a2edb65454 --- /dev/null +++ b/apps/web/pages/en/o/[slug]/news/[newsSlug]/index.tsx @@ -0,0 +1,11 @@ +import withApollo from '@island.is/web/graphql/withApollo' +import { withLocale } from '@island.is/web/i18n' +import OrganizationNewsArticle from '@island.is/web/screens/Organization/OrganizationNews/OrganizationNewsArticle' +import { getServerSidePropsWrapper } from '@island.is/web/utils/getServerSidePropsWrapper' +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore make web strict +const Screen = withApollo(withLocale('en')(OrganizationNewsArticle)) + +export default Screen + +export const getServerSideProps = getServerSidePropsWrapper(Screen) diff --git a/apps/web/pages/en/o/[slug]/news/index.tsx b/apps/web/pages/en/o/[slug]/news/index.tsx new file mode 100644 index 000000000000..e8042761807b --- /dev/null +++ b/apps/web/pages/en/o/[slug]/news/index.tsx @@ -0,0 +1,11 @@ +import withApollo from '@island.is/web/graphql/withApollo' +import { withLocale } from '@island.is/web/i18n' +import OrganizationNewsList from '@island.is/web/screens/Organization/OrganizationNews/OrganizationNewsList' +import { getServerSidePropsWrapper } from '@island.is/web/utils/getServerSidePropsWrapper' +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore make web strict +const Screen = withApollo(withLocale('en')(OrganizationNewsList)) + +export default Screen + +export const getServerSideProps = getServerSidePropsWrapper(Screen) diff --git a/apps/web/pages/en/o/[slug]/published-material.ts b/apps/web/pages/en/o/[slug]/published-material.ts new file mode 100644 index 000000000000..0a7382896313 --- /dev/null +++ b/apps/web/pages/en/o/[slug]/published-material.ts @@ -0,0 +1,11 @@ +import withApollo from '@island.is/web/graphql/withApollo' +import { withLocale } from '@island.is/web/i18n' +import PublishedMaterial from '@island.is/web/screens/Organization/PublishedMaterial' +import { getServerSidePropsWrapper } from '@island.is/web/utils/getServerSidePropsWrapper' +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore make web strict +const Screen = withApollo(withLocale('en')(PublishedMaterial)) + +export default Screen + +export const getServerSideProps = getServerSidePropsWrapper(Screen) diff --git a/apps/web/pages/s/[...slugs]/index.tsx b/apps/web/pages/s/[...slugs]/index.tsx deleted file mode 100644 index 554ac08cc02d..000000000000 --- a/apps/web/pages/s/[...slugs]/index.tsx +++ /dev/null @@ -1,251 +0,0 @@ -import { type FC } from 'react' - -import withApollo from '@island.is/web/graphql/withApollo' -import { withLocale } from '@island.is/web/i18n' -import type { LayoutProps } from '@island.is/web/layouts/main' -import OrganizationSubPageGenericListItem, { - OrganizationSubPageGenericListItemProps, -} from '@island.is/web/screens/GenericList/OrganizationSubPageGenericListItem' -import Home, { - type HomeProps, -} from '@island.is/web/screens/Organization/Home/Home' -import OrganizationEventArticle, { - type OrganizationEventArticleProps, -} from '@island.is/web/screens/Organization/OrganizationEvents/OrganizationEventArticle' -import OrganizationEventList, { - type OrganizationEventListProps, -} from '@island.is/web/screens/Organization/OrganizationEvents/OrganizationEventList' -import OrganizationNewsArticle, { - type OrganizationNewsArticleProps, -} from '@island.is/web/screens/Organization/OrganizationNews/OrganizationNewsArticle' -import OrganizationNewsList, { - type OrganizationNewsListProps, -} from '@island.is/web/screens/Organization/OrganizationNews/OrganizationNewsList' -import PublishedMaterial, { - type PublishedMaterialProps, -} from '@island.is/web/screens/Organization/PublishedMaterial/PublishedMaterial' -import SubPage, { - type SubPageProps, -} from '@island.is/web/screens/Organization/SubPage' -import { Screen as ScreenType } from '@island.is/web/types' -import { CustomNextError } from '@island.is/web/units/errors' -import { getServerSidePropsWrapper } from '@island.is/web/utils/getServerSidePropsWrapper' - -enum PageType { - FRONTPAGE = 'frontpage', - SUBPAGE = 'subpage', - ALL_NEWS = 'news', - PUBLISHED_MATERIAL = 'published-material', - ALL_EVENTS = 'events', - NEWS_DETAILS = 'news-details', - EVENT_DETAILS = 'event-details', - GENERIC_LIST_ITEM = 'generic-list-item', -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const pageMap: Record> = { - [PageType.FRONTPAGE]: (props) => , - [PageType.SUBPAGE]: (props) => , - [PageType.ALL_NEWS]: (props) => , - [PageType.PUBLISHED_MATERIAL]: (props) => , - [PageType.ALL_EVENTS]: (props) => , - [PageType.NEWS_DETAILS]: (props) => , - [PageType.EVENT_DETAILS]: (props) => , - [PageType.GENERIC_LIST_ITEM]: (props) => ( - - ), -} - -interface Props { - page: - | { - type: PageType.FRONTPAGE - props: { - layoutProps: LayoutProps - componentProps: HomeProps - } - } - | { - type: PageType.SUBPAGE - props: { - layoutProps: LayoutProps - componentProps: SubPageProps - } - } - | { - type: PageType.ALL_NEWS - props: { - layoutProps: LayoutProps - componentProps: OrganizationNewsListProps - } - } - | { - type: PageType.PUBLISHED_MATERIAL - props: { - layoutProps: LayoutProps - componentProps: PublishedMaterialProps - } - } - | { - type: PageType.ALL_EVENTS - props: { - layoutProps: LayoutProps - componentProps: OrganizationEventListProps - } - } - | { - type: PageType.NEWS_DETAILS - props: { - layoutProps: LayoutProps - componentProps: OrganizationNewsArticleProps - } - } - | { - type: PageType.EVENT_DETAILS - props: { - layoutProps: LayoutProps - componentProps: OrganizationEventArticleProps - } - } - | { - type: PageType.GENERIC_LIST_ITEM - props: OrganizationSubPageGenericListItemProps - } -} - -export const Component: ScreenType = ({ page }: Props) => { - return pageMap[page.type](page.props) -} - -Component.getProps = async (context) => { - const slugs = context.query.slugs as string[] - const locale = context.locale || 'is' - - // Frontpage - if (slugs.length === 1) { - return { - page: { - type: PageType.FRONTPAGE, - props: await Home.getProps(context), - }, - } - } - - if (slugs.length === 2) { - if (locale !== 'is') { - if (slugs[1] === 'news') { - return { - page: { - type: PageType.ALL_NEWS, - props: await OrganizationNewsList.getProps(context), - }, - } - } - if (slugs[1] === 'events') { - return { - page: { - type: PageType.ALL_EVENTS, - props: await OrganizationEventList.getProps(context), - }, - } - } - if (slugs[1] === 'published-material') { - return { - page: { - type: PageType.PUBLISHED_MATERIAL, - props: await PublishedMaterial.getProps(context), - }, - } - } - } else { - if (slugs[1] === 'frett') { - return { - page: { - type: PageType.ALL_NEWS, - props: await OrganizationNewsList.getProps(context), - }, - } - } - if (slugs[1] === 'vidburdir') { - return { - page: { - type: PageType.ALL_EVENTS, - props: await OrganizationEventList.getProps(context), - }, - } - } - if (slugs[1] === 'utgefid-efni') { - return { - page: { - type: PageType.PUBLISHED_MATERIAL, - props: await PublishedMaterial.getProps(context), - }, - } - } - } - - // Subpage - const props = await SubPage.getProps(context) - return { - page: { - type: PageType.SUBPAGE, - props, - }, - } - } - - if (slugs.length === 3) { - if (locale !== 'is') { - if (slugs[1] === 'news') { - return { - page: { - type: PageType.NEWS_DETAILS, - props: await OrganizationNewsArticle.getProps(context), - }, - } - } - if (slugs[1] === 'events') { - return { - page: { - type: PageType.EVENT_DETAILS, - props: await OrganizationEventArticle.getProps(context), - }, - } - } - } else { - if (slugs[1] === 'frett') { - return { - page: { - type: PageType.NEWS_DETAILS, - props: await OrganizationNewsArticle.getProps(context), - }, - } - } - if (slugs[1] === 'vidburdir') { - return { - page: { - type: PageType.EVENT_DETAILS, - props: await OrganizationEventArticle.getProps(context), - }, - } - } - } - - return { - page: { - type: PageType.GENERIC_LIST_ITEM, - props: await OrganizationSubPageGenericListItem.getProps(context), - }, - } - } - - throw new CustomNextError(404) -} - -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore make web strict -const Screen = withApollo(withLocale('is')(Component)) - -export const getServerSideProps = getServerSidePropsWrapper(Screen) - -export default Screen diff --git a/apps/web/pages/s/[slug]/[subSlug]/[genericListItemSlug]/index.tsx b/apps/web/pages/s/[slug]/[subSlug]/[genericListItemSlug]/index.tsx new file mode 100644 index 000000000000..26366989d023 --- /dev/null +++ b/apps/web/pages/s/[slug]/[subSlug]/[genericListItemSlug]/index.tsx @@ -0,0 +1,115 @@ +import { useMemo } from 'react' +import { useRouter } from 'next/router' + +import withApollo from '@island.is/web/graphql/withApollo' +import { useLinkResolver } from '@island.is/web/hooks' +import { useI18n, withLocale } from '@island.is/web/i18n' +import type { LayoutProps } from '@island.is/web/layouts/main' +import GenericListItemPage, { + type GenericListItemPageProps, +} from '@island.is/web/screens/GenericList/GenericListItem' +import SubPageLayout, { + type SubPageProps, +} from '@island.is/web/screens/Organization/SubPage' +import type { Screen as ScreenType } from '@island.is/web/types' +import { CustomNextError } from '@island.is/web/units/errors' +import { getServerSidePropsWrapper } from '@island.is/web/utils/getServerSidePropsWrapper' + +interface ComponentProps { + parentProps: { + layoutProps: LayoutProps + componentProps: SubPageProps + } + genericListItemProps: GenericListItemPageProps +} + +export const Component: ScreenType = ({ + parentProps, + genericListItemProps, +}) => { + const { activeLocale } = useI18n() + const router = useRouter() + const { linkResolver } = useLinkResolver() + const backLinkUrl = useMemo(() => { + const pathname = new URL(router.asPath, 'https://island.is').pathname + return pathname.slice(0, pathname.lastIndexOf('/')) + }, [router.asPath]) + + const { organizationPage, subpage } = parentProps.componentProps + + return ( + + ), + customBreadcrumbItems: [ + { + title: 'Ísland.is', + href: linkResolver('homepage').href, + }, + { + title: organizationPage?.title ?? '', + href: linkResolver('organizationpage', [ + organizationPage?.slug ?? '', + ]).href, + }, + { + title: subpage?.title ?? '', + href: backLinkUrl, + isTag: true, + }, + ], + backLink: { + text: activeLocale === 'is' ? 'Til baka' : 'Go back', + url: backLinkUrl, + }, + customContentfulIds: [ + organizationPage?.id, + subpage?.id, + genericListItemProps.item.id, + ], + }} + /> + ) +} + +Component.getProps = async (ctx) => { + const [parentProps, genericListItemProps] = await Promise.all([ + SubPageLayout.getProps?.(ctx), + GenericListItemPage.getProps?.(ctx), + ]) + + if (!parentProps) { + throw new CustomNextError( + 404, + 'Could not fetch subpage layout for generic list item', + ) + } + if (!genericListItemProps) { + throw new CustomNextError(404, 'Could not fetch generic list item props') + } + + return { + parentProps, + genericListItemProps, + } +} + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore make web strict +const Screen = withApollo(withLocale('is')(Component)) + +export default Screen + +export const getServerSideProps = getServerSidePropsWrapper(Screen) diff --git a/apps/web/pages/s/[slug]/[subSlug]/index.ts b/apps/web/pages/s/[slug]/[subSlug]/index.ts new file mode 100644 index 000000000000..c1c68debcf44 --- /dev/null +++ b/apps/web/pages/s/[slug]/[subSlug]/index.ts @@ -0,0 +1,13 @@ +import '@island.is/api/mocks' + +import withApollo from '@island.is/web/graphql/withApollo' +import { withLocale } from '@island.is/web/i18n' +import subPage from '@island.is/web/screens/Organization/SubPage' +import { getServerSidePropsWrapper } from '@island.is/web/utils/getServerSidePropsWrapper' +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore make web strict +const Screen = withApollo(withLocale('is')(subPage)) + +export default Screen + +export const getServerSideProps = getServerSidePropsWrapper(Screen) diff --git a/apps/web/pages/s/[slug]/frett/[newsSlug]/index.tsx b/apps/web/pages/s/[slug]/frett/[newsSlug]/index.tsx new file mode 100644 index 000000000000..6698e1496af8 --- /dev/null +++ b/apps/web/pages/s/[slug]/frett/[newsSlug]/index.tsx @@ -0,0 +1,11 @@ +import withApollo from '@island.is/web/graphql/withApollo' +import { withLocale } from '@island.is/web/i18n' +import OrganizationNewsArticle from '@island.is/web/screens/Organization/OrganizationNews/OrganizationNewsArticle' +import { getServerSidePropsWrapper } from '@island.is/web/utils/getServerSidePropsWrapper' +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore make web strict +const Screen = withApollo(withLocale('is')(OrganizationNewsArticle)) + +export default Screen + +export const getServerSideProps = getServerSidePropsWrapper(Screen) diff --git a/apps/web/pages/s/[slug]/frett/index.tsx b/apps/web/pages/s/[slug]/frett/index.tsx new file mode 100644 index 000000000000..a15bef2828aa --- /dev/null +++ b/apps/web/pages/s/[slug]/frett/index.tsx @@ -0,0 +1,11 @@ +import withApollo from '@island.is/web/graphql/withApollo' +import { withLocale } from '@island.is/web/i18n' +import OrganizationNewsList from '@island.is/web/screens/Organization/OrganizationNews/OrganizationNewsList' +import { getServerSidePropsWrapper } from '@island.is/web/utils/getServerSidePropsWrapper' +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore make web strict +const Screen = withApollo(withLocale('is')(OrganizationNewsList)) + +export default Screen + +export const getServerSideProps = getServerSidePropsWrapper(Screen) diff --git a/apps/web/pages/styrkjatorg/styrkur/[id].ts b/apps/web/pages/s/[slug]/index.ts similarity index 73% rename from apps/web/pages/styrkjatorg/styrkur/[id].ts rename to apps/web/pages/s/[slug]/index.ts index e59a8c014b13..e70c074e89f7 100644 --- a/apps/web/pages/styrkjatorg/styrkur/[id].ts +++ b/apps/web/pages/s/[slug]/index.ts @@ -1,11 +1,10 @@ import withApollo from '@island.is/web/graphql/withApollo' import { withLocale } from '@island.is/web/i18n' -import GrantSinglePage from '@island.is/web/screens/Grants/Grant/Grant' +import organizationPage from '@island.is/web/screens/Organization/Home' import { getServerSidePropsWrapper } from '@island.is/web/utils/getServerSidePropsWrapper' // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore make web strict -// -const Screen = withApollo(withLocale('is')(GrantSinglePage)) +const Screen = withApollo(withLocale('is')(organizationPage)) export default Screen diff --git a/apps/web/pages/s/[slug]/utgefid-efni/index.ts b/apps/web/pages/s/[slug]/utgefid-efni/index.ts new file mode 100644 index 000000000000..7962af216a5c --- /dev/null +++ b/apps/web/pages/s/[slug]/utgefid-efni/index.ts @@ -0,0 +1,11 @@ +import withApollo from '@island.is/web/graphql/withApollo' +import { withLocale } from '@island.is/web/i18n' +import PublishedMaterial from '@island.is/web/screens/Organization/PublishedMaterial' +import { getServerSidePropsWrapper } from '@island.is/web/utils/getServerSidePropsWrapper' +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore make web strict +const Screen = withApollo(withLocale('is')(PublishedMaterial)) + +export default Screen + +export const getServerSideProps = getServerSidePropsWrapper(Screen) diff --git a/apps/web/pages/s/[slug]/vidburdir/[eventSlug].ts b/apps/web/pages/s/[slug]/vidburdir/[eventSlug].ts new file mode 100644 index 000000000000..bab2c662551c --- /dev/null +++ b/apps/web/pages/s/[slug]/vidburdir/[eventSlug].ts @@ -0,0 +1,12 @@ +import withApollo from '@island.is/web/graphql/withApollo' +import { withLocale } from '@island.is/web/i18n' +import OrganizationEventArticle from '@island.is/web/screens/Organization/OrganizationEvents/OrganizationEventArticle' +import { getServerSidePropsWrapper } from '@island.is/web/utils/getServerSidePropsWrapper' + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore make web strict +const Screen = withApollo(withLocale('is')(OrganizationEventArticle)) + +export default Screen + +export const getServerSideProps = getServerSidePropsWrapper(Screen) diff --git a/apps/web/pages/s/[slug]/vidburdir/index.ts b/apps/web/pages/s/[slug]/vidburdir/index.ts new file mode 100644 index 000000000000..52084401fa70 --- /dev/null +++ b/apps/web/pages/s/[slug]/vidburdir/index.ts @@ -0,0 +1,12 @@ +import withApollo from '@island.is/web/graphql/withApollo' +import { withLocale } from '@island.is/web/i18n' +import OrganizationEventList from '@island.is/web/screens/Organization/OrganizationEvents/OrganizationEventList' +import { getServerSidePropsWrapper } from '@island.is/web/utils/getServerSidePropsWrapper' + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore make web strict +const Screen = withApollo(withLocale('is')(OrganizationEventList)) + +export default Screen + +export const getServerSideProps = getServerSidePropsWrapper(Screen) diff --git a/apps/web/pages/v/[slug]/[subSlug]/[genericListItemSlug]/index.tsx b/apps/web/pages/v/[slug]/[subSlug]/[genericListItemSlug]/index.tsx index bdb7678b50cf..94a01d0664e6 100644 --- a/apps/web/pages/v/[slug]/[subSlug]/[genericListItemSlug]/index.tsx +++ b/apps/web/pages/v/[slug]/[subSlug]/[genericListItemSlug]/index.tsx @@ -92,8 +92,8 @@ export const Component: ScreenType = ({ Component.getProps = async (ctx) => { const [parentProps, genericListItemProps] = await Promise.all([ - SubPageLayout.getProps(ctx), - GenericListItemPage.getProps(ctx), + SubPageLayout.getProps?.(ctx), + GenericListItemPage.getProps?.(ctx), ]) if (!parentProps) { diff --git a/apps/web/public/assets/sofa.svg b/apps/web/public/assets/sofa.svg deleted file mode 100644 index c0c403237583..000000000000 --- a/apps/web/public/assets/sofa.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/web/screens/GenericList/GenericListItem.tsx b/apps/web/screens/GenericList/GenericListItem.tsx index c264ad93b4ad..2cea5ee87c74 100644 --- a/apps/web/screens/GenericList/GenericListItem.tsx +++ b/apps/web/screens/GenericList/GenericListItem.tsx @@ -49,8 +49,7 @@ const GenericListItemPage: Screen = ({ } GenericListItemPage.getProps = async ({ apolloClient, query, locale }) => { - const slug = - (query.slugs as string[])?.[2] ?? (query.genericListItemSlug as string) + const slug = query.genericListItemSlug if (!slug) { throw new CustomNextError( diff --git a/apps/web/screens/GenericList/OrganizationSubPageGenericListItem.tsx b/apps/web/screens/GenericList/OrganizationSubPageGenericListItem.tsx deleted file mode 100644 index 7bba1474eef6..000000000000 --- a/apps/web/screens/GenericList/OrganizationSubPageGenericListItem.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { useMemo } from 'react' -import { useRouter } from 'next/router' - -import { useLinkResolver } from '@island.is/web/hooks' -import { useI18n } from '@island.is/web/i18n' -import { type LayoutProps, withMainLayout } from '@island.is/web/layouts/main' -import { Screen } from '@island.is/web/types' - -import SubPage, { type SubPageProps } from '../Organization/SubPage' -import GenericListItemPage, { - type GenericListItemPageProps, -} from './GenericListItem' - -export interface OrganizationSubPageGenericListItemProps { - parentProps: { - layoutProps: LayoutProps - componentProps: SubPageProps - } - genericListItemProps: GenericListItemPageProps -} - -const OrganizationSubPageGenericListItem: Screen< - OrganizationSubPageGenericListItemProps -> = (props) => { - const { organizationPage, subpage } = props.parentProps.componentProps - const router = useRouter() - const { linkResolver } = useLinkResolver() - const backLinkUrl = useMemo(() => { - const pathname = new URL(router.asPath, 'https://island.is').pathname - return pathname.slice(0, pathname.lastIndexOf('/')) - }, [router.asPath]) - const { activeLocale } = useI18n() - return ( - - ), - customBreadcrumbItems: [ - { - title: 'Ísland.is', - href: linkResolver('homepage').href, - }, - { - title: organizationPage?.title ?? '', - href: linkResolver('organizationpage', [ - organizationPage?.slug ?? '', - ]).href, - }, - { - title: subpage?.title ?? '', - href: backLinkUrl, - isTag: true, - }, - ], - backLink: { - text: activeLocale === 'is' ? 'Til baka' : 'Go back', - url: backLinkUrl, - }, - customContentfulIds: [ - organizationPage?.id, - subpage?.id, - props.genericListItemProps.item.id, - ], - }} - /> - ) -} - -OrganizationSubPageGenericListItem.getProps = async (context) => { - const [subPageProps, genericListItemProps] = await Promise.all([ - SubPage.getProps(context), - GenericListItemPage.getProps(context), - ]) - return { - parentProps: subPageProps, - genericListItemProps, - } as OrganizationSubPageGenericListItemProps -} - -export default OrganizationSubPageGenericListItem diff --git a/apps/web/screens/Grants/Grant/Grant.tsx b/apps/web/screens/Grants/Grant/Grant.tsx deleted file mode 100644 index d3f4caf7aa06..000000000000 --- a/apps/web/screens/Grants/Grant/Grant.tsx +++ /dev/null @@ -1,220 +0,0 @@ -import { useIntl } from 'react-intl' -import NextLink from 'next/link' -import { useRouter } from 'next/router' - -import { SliceType } from '@island.is/island-ui/contentful' -import { - ActionCard, - Box, - Breadcrumbs, - Divider, - Stack, - Text, -} from '@island.is/island-ui/core' -import { Locale } from '@island.is/shared/types' -import { GrantWrapper } from '@island.is/web/components' -import { - ContentLanguage, - CustomPageUniqueIdentifier, - Grant, - Query, - QueryGetSingleGrantArgs, -} from '@island.is/web/graphql/schema' -import { useLinkResolver } from '@island.is/web/hooks' -import { withMainLayout } from '@island.is/web/layouts/main' -import { webRichText } from '@island.is/web/utils/richText' - -import { - CustomScreen, - withCustomPageWrapper, -} from '../../CustomPage/CustomPageWrapper' -import SidebarLayout from '../../Layouts/SidebarLayout' -import { GET_GRANT_QUERY } from '../../queries' -import { m } from '../messages' -import { GrantSidebar } from './GrantSidebar' -import { CustomNextError } from '@island.is/web/units/errors' - -const GrantSinglePage: CustomScreen = ({ grant, locale }) => { - const { formatMessage } = useIntl() - const { linkResolver } = useLinkResolver() - const router = useRouter() - - const baseUrl = linkResolver('styrkjatorg', [], locale).href - const currentUrl = linkResolver( - 'styrkjatorggrant', - [grant?.applicationId ?? ''], - locale, - ).href - - const breadcrumbItems = [ - { - title: 'Ísland.is', - href: linkResolver('homepage', [], locale).href, - }, - { - title: formatMessage(m.home.title), - href: baseUrl, - }, - { - title: grant?.name ?? formatMessage(m.home.grant), - href: currentUrl, - isTag: true, - }, - ] - - if (!grant) { - return null - } - - return ( - - } - > - - - { - return item?.href ? ( - - {link} - - ) : ( - link - ) - }} - /> - - - {grant.name} - - {grant.description} - - router.push(grant.applicationUrl?.slug ?? ''), - icon: 'open', - iconType: 'outline', - }} - /> - {grant.specialEmphasis?.length ? ( - <> - - {webRichText( - grant.specialEmphasis as SliceType[], - undefined, - locale, - )} - - - - ) : undefined} - {grant.whoCanApply?.length ? ( - <> - - {formatMessage(m.single.whoCanApply)} - - {webRichText( - grant.whoCanApply as SliceType[], - undefined, - locale, - )} - - - - - ) : undefined} - {grant.howToApply?.length ? ( - - {formatMessage(m.single.howToApply)} - - {webRichText( - grant.howToApply as SliceType[], - undefined, - locale, - )} - - - ) : undefined} - {grant.applicationDeadline?.length ? ( - - {webRichText( - grant.applicationDeadline as SliceType[], - undefined, - locale, - )} - - ) : undefined} - {grant.applicationHints?.length ? ( - - {webRichText( - grant.applicationHints as SliceType[], - undefined, - locale, - )} - - ) : undefined} - - - - ) -} - -interface GrantSingleProps { - grant?: Grant - locale: Locale -} - -const GrantSingle: CustomScreen = ({ - grant, - customPageData, - locale, -}) => { - return ( - - ) -} - -GrantSingle.getProps = async ({ apolloClient, locale, query }) => { - const { - data: { getSingleGrant: grant }, - } = await apolloClient.query({ - query: GET_GRANT_QUERY, - variables: { - input: { - lang: locale as ContentLanguage, - id: String(query.id), - }, - }, - }) - - if (!grant) { - throw new CustomNextError(404, 'Grant not found') - } - - return { - grant: grant ?? undefined, - locale: locale as Locale, - showSearchInHeader: false, - themeConfig: { - footerVersion: 'organization', - }, - } -} - -export default withMainLayout( - withCustomPageWrapper(CustomPageUniqueIdentifier.Grants, GrantSingle), -) diff --git a/apps/web/screens/Grants/Grant/GrantSidebar.tsx b/apps/web/screens/Grants/Grant/GrantSidebar.tsx deleted file mode 100644 index 1d97d89b388c..000000000000 --- a/apps/web/screens/Grants/Grant/GrantSidebar.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import { useMemo } from 'react' -import { useIntl } from 'react-intl' - -import { Box, Button, LinkV2, Stack, Text } from '@island.is/island-ui/core' -import { Locale } from '@island.is/shared/types' -import { isDefined } from '@island.is/shared/utils' -import { InstitutionPanel } from '@island.is/web/components' -import { Grant } from '@island.is/web/graphql/schema' -import { LinkType, useLinkResolver } from '@island.is/web/hooks' - -import { m } from '../messages' - -interface Props { - grant: Grant - locale: Locale -} - -const generateLine = (heading: string, content?: React.ReactNode) => { - if (!content) { - return null - } - return ( - - - {heading} - - {content} - - ) -} - -export const GrantSidebar = ({ grant, locale }: Props) => { - const { formatMessage } = useIntl() - const { linkResolver } = useLinkResolver() - - const detailPanelData = useMemo( - () => - [ - generateLine( - formatMessage(m.single.fund), - grant?.fund?.link?.slug ? ( - - - {grant.fund.title} - - - ) : undefined, - ), - generateLine( - formatMessage(m.single.category), - grant?.categoryTags - ? grant.categoryTags - .map((ct) => ct.title) - .filter(isDefined) - .join(', ') - : undefined, - ), - generateLine( - formatMessage(m.single.type), - grant?.typeTag?.title ? ( - {grant.typeTag?.title} - ) : undefined, - ), - generateLine( - formatMessage(m.single.deadline), - grant?.applicationDeadlineText ? ( - {grant.applicationDeadlineText} - ) : undefined, - ), - generateLine( - formatMessage(m.single.status), - grant?.statusText ? ( - {grant.statusText} - ) : undefined, - ), - ].filter(isDefined) ?? [], - [grant, formatMessage, linkResolver], - ) - - const filesPanelData = useMemo( - () => - grant.files - ?.map((f, index) => { - if (!f.url) { - return null - } - return ( - - - - ) - }) - .filter(isDefined) ?? [], - [grant.files], - ) - - return ( - - - {detailPanelData.length ? ( - - {detailPanelData} - - ) : undefined} - {filesPanelData.length ? ( - - {filesPanelData} - - ) : undefined} - - ) -} diff --git a/apps/web/screens/Grants/Home/GrantsHome.tsx b/apps/web/screens/Grants/Home/GrantsHome.tsx deleted file mode 100644 index d106bee459fe..000000000000 --- a/apps/web/screens/Grants/Home/GrantsHome.tsx +++ /dev/null @@ -1,203 +0,0 @@ -import { useIntl } from 'react-intl' -import NextLink from 'next/link' - -import { - ArrowLink, - Box, - Breadcrumbs, - CategoryCard, - GridColumn, - GridContainer, - GridRow, - Stack, - Text, -} from '@island.is/island-ui/core' -import { Locale } from '@island.is/shared/types' -import { GrantSearchSection } from '@island.is/web/components' -import { SLICE_SPACING } from '@island.is/web/constants' -import { - ContentLanguage, - CustomPageUniqueIdentifier, - GenericTag, - Query, - QueryGetGenericTagsInTagGroupsArgs, -} from '@island.is/web/graphql/schema' -import { useLinkResolver } from '@island.is/web/hooks' -import { withMainLayout } from '@island.is/web/layouts/main' - -import { - CustomScreen, - withCustomPageWrapper, -} from '../../CustomPage/CustomPageWrapper' -import { GET_GENERIC_TAGS_IN_TAG_GROUPS_QUERY } from '../../queries/GenericTag' -import { m } from '../messages' - -const GrantsHomePage: CustomScreen = ({ - categories, - locale, - customPageData, -}) => { - const { formatMessage } = useIntl() - const { linkResolver } = useLinkResolver() - - const baseUrl = linkResolver('styrkjatorg', [], locale).href - const searchUrl = linkResolver('styrkjatorgsearch', [], locale).href - - const breadcrumbItems = [ - { - title: 'Ísland.is', - href: linkResolver('homepage', [], locale).href, - }, - { - title: formatMessage(m.home.title), - href: baseUrl, - }, - ] - - return ( - - - { - return item?.href ? ( - - {link} - - ) : ( - link - ) - }} - /> - ) - } - /> - - - - - - {formatMessage(m.home.popularCategories)} - - - {formatMessage(m.home.allGrants)} - - - - - {categories?.map((c) => ( - - - - ))} - - - - - - ) -} - -interface GrantsHomeProps { - organization?: Query['getOrganization'] - categories?: Array - locale: Locale -} - -const GrantsHome: CustomScreen = ({ - categories, - organization, - customPageData, - locale, -}) => { - return ( - - ) -} - -GrantsHome.getProps = async ({ apolloClient, locale }) => { - const tagGroupCategory = 'grant-category' - //Todo: add more organizations ?? - - const { - data: { getGenericTagsInTagGroups: tags }, - } = await apolloClient.query({ - query: GET_GENERIC_TAGS_IN_TAG_GROUPS_QUERY, - variables: { - input: { - lang: locale as ContentLanguage, - tagGroupSlugs: [tagGroupCategory], - }, - }, - }) - - return { - categories: tags ?? [], - locale: locale as Locale, - showSearchInHeader: false, - themeConfig: { - footerVersion: 'organization', - }, - } -} - -export default withMainLayout( - withCustomPageWrapper(CustomPageUniqueIdentifier.Grants, GrantsHome), -) diff --git a/apps/web/screens/Grants/SearchResults/SearchResults.tsx b/apps/web/screens/Grants/SearchResults/SearchResults.tsx deleted file mode 100644 index b0ce8ffde03d..000000000000 --- a/apps/web/screens/Grants/SearchResults/SearchResults.tsx +++ /dev/null @@ -1,413 +0,0 @@ -import { useCallback, useEffect, useMemo, useState } from 'react' -import { useIntl } from 'react-intl' -import { useWindowSize } from 'react-use' -import debounce from 'lodash/debounce' -import NextLink from 'next/link' -import { useRouter } from 'next/router' -import { - parseAsArrayOf, - parseAsInteger, - parseAsString, -} from 'next-usequerystate' -import { useLazyQuery } from '@apollo/client' - -import { - Box, - BreadCrumbItem, - Breadcrumbs, - FilterInput, - Text, -} from '@island.is/island-ui/core' -import { theme } from '@island.is/island-ui/theme' -import { debounceTime } from '@island.is/shared/constants' -import { Locale } from '@island.is/shared/types' -import { GrantHeaderWithImage, GrantWrapper } from '@island.is/web/components' -import { - ContentLanguage, - CustomPageUniqueIdentifier, - GenericTag, - Grant, - GrantList, - Query, - QueryGetGenericTagsInTagGroupsArgs, - QueryGetGrantsArgs, -} from '@island.is/web/graphql/schema' -import { useLinkResolver } from '@island.is/web/hooks' -import { withMainLayout } from '@island.is/web/layouts/main' - -import { - CustomScreen, - withCustomPageWrapper, -} from '../../CustomPage/CustomPageWrapper' -import SidebarLayout from '../../Layouts/SidebarLayout' -import { GET_GENERIC_TAGS_IN_TAG_GROUPS_QUERY } from '../../queries/GenericTag' -import { GET_GRANTS_QUERY } from '../../queries/Grants' -import { m } from '../messages' -import { SearchResultsContent } from './SearchResultsContent' -import { GrantsSearchResultsFilter } from './SearchResultsFilter' - -export interface SearchState { - page?: number - query?: string - status?: Array - category?: Array - type?: Array - organization?: Array -} - -const GrantsSearchResultsPage: CustomScreen = ({ - locale, - initialGrants, - tags, -}) => { - const { formatMessage } = useIntl() - const router = useRouter() - const { linkResolver } = useLinkResolver() - - const parentUrl = linkResolver('styrkjatorg', [], locale).href - const currentUrl = linkResolver('styrkjatorgsearch', [], locale).href - - const [grants, setGrants] = useState>(initialGrants ?? []) - const [searchState, setSearchState] = useState() - const [initialRender, setInitialRender] = useState(true) - - const { width } = useWindowSize() - const isMobile = width <= theme.breakpoints.md - - const [getGrants, { error }] = useLazyQuery< - { getGrants: GrantList }, - QueryGetGrantsArgs - >(GET_GRANTS_QUERY) - - //load params into search state on first render - useEffect(() => { - const searchParams = new URLSearchParams(document.location.search) - const page = searchParams.get('page') - const statuses = searchParams.getAll('status') - const categories = searchParams.getAll('category') - const types = searchParams.getAll('type') - const organizations = searchParams.getAll('organization') - - setSearchState({ - page: page ? Number.parseInt(page) : undefined, - query: searchParams.get('query') ?? undefined, - status: statuses.length ? statuses : undefined, - category: categories.length ? categories : undefined, - type: types.length ? types : undefined, - organization: organizations.length ? organizations : undefined, - }) - }, []) - - const updateUrl = useCallback(() => { - if (!searchState) { - return - } - router.replace( - { - pathname: currentUrl, - query: Object.entries(searchState) - .filter(([_, value]) => !!value) - .reduce( - (accumulator, [searchStateKey, searchStateValue]) => ({ - ...accumulator, - [searchStateKey]: searchStateValue, - }), - {}, - ), - }, - undefined, - { shallow: true }, - ) - }, [searchState, router, currentUrl]) - - const fetchGrants = useCallback(() => { - if (initialRender) { - setInitialRender(false) - return - } - getGrants({ - variables: { - input: { - categories: searchState?.category, - lang: locale, - organizations: searchState?.organization, - page: searchState?.page, - search: searchState?.query, - size: 8, - statuses: searchState?.status, - types: searchState?.type, - }, - }, - }) - .then((res) => { - if (res.data) { - setGrants(res.data.getGrants.items) - } else if (res.error) { - setGrants([]) - console.error('Error fetching grants', res.error) - } - }) - .catch((err) => { - setGrants([]) - console.error('Error fetching grants', err) - }) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [searchState, initialRender]) - - //SEARCH STATE UPDATES - const debouncedSearchUpdate = useMemo(() => { - return debounce(() => { - updateUrl() - fetchGrants() - }, debounceTime.search) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [searchState]) - - useEffect(() => { - debouncedSearchUpdate() - return () => { - debouncedSearchUpdate.cancel() - } - }, [debouncedSearchUpdate]) - - const updateSearchStateValue = ( - categoryId: keyof SearchState, - values: unknown, - ) => { - setSearchState({ - ...searchState, - [categoryId]: values, - }) - } - - const breadcrumbItems: Array = [ - { - title: 'Ísland.is', - href: linkResolver('homepage', [], locale).href, - }, - { - title: formatMessage(m.home.title), - href: parentUrl, - }, - { - title: formatMessage(m.search.results), - href: currentUrl, - isTag: true, - }, - ] - - const onResetFilter = () => { - setSearchState({ - page: undefined, - query: undefined, - status: undefined, - category: undefined, - type: undefined, - organization: undefined, - }) - router.replace(currentUrl, {}, { shallow: true }) - } - - const hitsMessage = useMemo(() => { - if (!grants) { - return - } - if (grants.length === 1) { - return formatMessage(m.search.resultFound, { - arg: {grants.length}, - }) - } - return formatMessage(m.search.resultsFound, { - arg: {grants.length}, - }) - }, [formatMessage, grants]) - - return ( - - { - return item?.href ? ( - - {link} - - ) : ( - link - ) - }} - /> - ) - } - /> - - {!isMobile && ( - - - - {formatMessage(m.search.search)} - - - - - updateSearchStateValue('query', option) - } - /> - - - - } - > - - - - - )} - {isMobile && ( - - - - {formatMessage(m.search.search)} - - - - updateSearchStateValue('query', option)} - backgroundColor={'white'} - /> - - - {hitsMessage} - - - - - )} - - - ) -} - -interface GrantsHomeProps { - locale: Locale - initialGrants?: Array - tags?: Array -} - -const GrantsSearchResults: CustomScreen = ({ - initialGrants, - tags, - customPageData, - locale, -}) => { - return ( - - ) -} - -GrantsSearchResults.getProps = async ({ apolloClient, locale, query }) => { - const arrayParser = parseAsArrayOf(parseAsString) - - const parseArray = (arg: string | string[] | undefined) => { - const array = arrayParser.parseServerSide(arg) - - if (array && array.length > 0) { - return array - } - - return undefined - } - const [ - { - data: { getGrants }, - }, - { - data: { getGenericTagsInTagGroups }, - }, - ] = await Promise.all([ - apolloClient.query({ - query: GET_GRANTS_QUERY, - variables: { - input: { - lang: locale as ContentLanguage, - page: parseAsInteger.withDefault(1).parseServerSide(query?.page), - search: parseAsString.parseServerSide(query?.query) ?? undefined, - categories: parseArray(query?.category), - statuses: parseArray(query?.status), - types: parseArray(query?.type), - organizations: parseArray(query?.organization), - }, - }, - }), - apolloClient.query({ - query: GET_GENERIC_TAGS_IN_TAG_GROUPS_QUERY, - variables: { - input: { - lang: locale as ContentLanguage, - tagGroupSlugs: ['grant-category', 'grant-type'], - }, - }, - }), - ]) - return { - initialGrants: getGrants.items, - tags: getGenericTagsInTagGroups ?? undefined, - locale: locale as Locale, - themeConfig: { - footerVersion: 'organization', - }, - } -} - -export default withMainLayout( - withCustomPageWrapper(CustomPageUniqueIdentifier.Grants, GrantsSearchResults), -) diff --git a/apps/web/screens/Grants/SearchResults/SearchResultsContent.tsx b/apps/web/screens/Grants/SearchResults/SearchResultsContent.tsx deleted file mode 100644 index 163c5f3b08c4..000000000000 --- a/apps/web/screens/Grants/SearchResults/SearchResultsContent.tsx +++ /dev/null @@ -1,151 +0,0 @@ -import { useIntl } from 'react-intl' -import { useWindowSize } from 'react-use' -import format from 'date-fns/format' -import { useRouter } from 'next/router' - -import { Box, Inline, TagVariant, Text } from '@island.is/island-ui/core' -import { theme } from '@island.is/island-ui/theme' -import { Locale } from '@island.is/shared/types' -import { isDefined } from '@island.is/shared/utils' -import { PlazaCard } from '@island.is/web/components' -import { Grant, GrantStatus } from '@island.is/web/graphql/schema' -import { useLinkResolver } from '@island.is/web/hooks' - -import { m } from '../messages' - -interface Props { - grants?: Array - subheader?: React.ReactNode - locale: Locale -} - -export const SearchResultsContent = ({ grants, subheader, locale }: Props) => { - const { formatMessage } = useIntl() - const router = useRouter() - const { linkResolver } = useLinkResolver() - - const { width } = useWindowSize() - const isMobile = width <= theme.breakpoints.md - const isTablet = width <= theme.breakpoints.lg && width > theme.breakpoints.md - - return ( - <> - {!isMobile && ( - - {subheader} - - )} - {grants?.length ? ( - - {grants?.map((grant) => { - if (!grant) { - return null - } - - let tagVariant: TagVariant | undefined - switch (grant.status) { - case GrantStatus.Open: - tagVariant = 'mint' - break - case GrantStatus.Closed: - tagVariant = 'rose' - break - case GrantStatus.OpensSoon: - tagVariant = 'purple' - break - default: - break - } - - return ( - - {grant.applicationId && ( - { - router.push( - linkResolver( - 'styrkjatorggrant', - [grant?.applicationId ?? ''], - locale, - ).href, - ) - }, - icon: 'arrowForward', - }} - detailLines={[ - grant.dateFrom && grant.dateTo - ? { - icon: 'calendar' as const, - text: `${format( - new Date(grant.dateFrom), - 'dd.MM.', - )}-${format(new Date(grant.dateTo), 'dd.MM.yyyy')}`, - } - : null, - { - icon: 'time' as const, - //todo: fix when the text is ready - text: 'Frestur til 16.08.2024, kl. 15.00', - }, - grant.categoryTags - ? { - icon: 'informationCircle' as const, - text: grant.categoryTags - .map((ct) => ct.title) - .filter(isDefined) - .join(', '), - } - : undefined, - ].filter(isDefined)} - /> - )} - - ) - })} - - ) : undefined} - {!grants?.length && ( - - - - {formatMessage(m.search.noResultsFound)} - - - {!(isTablet || isMobile) && ( - {formatMessage(m.search.noResultsFound)} - )} - - )} - - ) -} diff --git a/apps/web/screens/Grants/SearchResults/SearchResultsFilter.tsx b/apps/web/screens/Grants/SearchResults/SearchResultsFilter.tsx deleted file mode 100644 index df716f5c3d7e..000000000000 --- a/apps/web/screens/Grants/SearchResults/SearchResultsFilter.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import { useIntl } from 'react-intl' - -import { - Box, - Filter, - FilterMultiChoice, - FilterProps, -} from '@island.is/island-ui/core' -import { isDefined } from '@island.is/shared/utils' -import { GenericTag } from '@island.is/web/graphql/schema' - -import { m } from '../messages' -import { SearchState } from './SearchResults' - -interface Props { - onSearchUpdate: (categoryId: keyof SearchState, value: unknown) => void - onReset: () => void - searchState?: SearchState - tags: Array - url: string - variant?: FilterProps['variant'] -} - -export const GrantsSearchResultsFilter = ({ - onSearchUpdate, - onReset, - searchState, - tags, - url, - 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', - ) - - return ( - { - e.preventDefault() - }} - > - - - { - onSearchUpdate( - categoryId as keyof SearchState, - selected.length ? selected : undefined, - ) - }} - onClear={(categoryId) => { - onSearchUpdate(categoryId as keyof SearchState, undefined) - }} - categories={[ - { - id: 'status', - label: formatMessage(m.search.applicationStatus), - selected: searchState?.['status'] ?? [], - filters: [ - { - value: 'open', - label: formatMessage(m.search.applicationOpen), - }, - { - value: 'open-soon', - label: formatMessage(m.search.applicationOpensSoon), - }, - { - value: 'closed', - label: formatMessage(m.search.applicationClosed), - }, - ], - }, - categoryFilters - ? { - id: 'category', - label: formatMessage(m.search.category), - selected: searchState?.['category'] ?? [], - filters: categoryFilters.map((t) => ({ - value: t.slug, - label: t.title, - })), - } - : undefined, - typeFilters - ? { - id: 'type', - label: formatMessage(m.search.type), - selected: searchState?.['type'] ?? [], - filters: typeFilters.map((t) => ({ - value: t.slug, - label: t.title, - })), - } - : undefined, - { - id: 'organization', - label: formatMessage(m.search.organization), - - selected: searchState?.['organization'] ?? [], - filters: [ - { - value: 'rannis', - label: 'Rannís', - }, - { - value: 'tonlistarmidstod', - label: 'Tónlistarmiðstöð', - }, - { - value: 'kvikmyndastod', - label: 'Kvikmyndastöð', - }, - ], - }, - ].filter(isDefined)} - /> - - - - ) -} diff --git a/apps/web/screens/Grants/messages.ts b/apps/web/screens/Grants/messages.ts deleted file mode 100644 index 15a1cf7d595a..000000000000 --- a/apps/web/screens/Grants/messages.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { defineMessages } from 'react-intl' - -export const m = { - general: defineMessages({ - seeMore: { - id: 'web.grants:general.seeMore', - defaultMessage: 'Skoða nánar', - }, - }), - search: defineMessages({ - search: { - id: 'web.grants:search.search', - defaultMessage: 'Leit', - }, - description: { - id: 'web.grants:search.description', - defaultMessage: 'Lýsing', - }, - results: { - id: 'web.grants:search.results', - defaultMessage: 'Leitarniðurstöður', - }, - inputPlaceholder: { - id: 'web.grants:search.inputPlaceholder', - defaultMessage: 'Sía eftir leitarorði', - }, - clearFilters: { - id: 'web.grants:search.clearFilters', - defaultMessage: 'Hreinsa allar síur', - }, - clearFilterCategory: { - id: 'web.grants:search.clearFilterCategory', - defaultMessage: 'Hreinsa flokk', - }, - applicationStatus: { - id: 'web.grants:search.applicationStatus', - defaultMessage: 'Staða umsóknar', - }, - applicationOpen: { - id: 'web.grants:search.applicationOpen', - defaultMessage: 'Opið fyrir umsóknir', - }, - applicationClosed: { - id: 'web.grants:search.applicationClosed', - defaultMessage: 'Lokað fyrir umsóknir', - }, - applicationOpensSoon: { - id: 'web.grants:search.applicationOpensSoon', - defaultMessage: 'Opnar fljótlega', - }, - category: { - id: 'web.grants:search.category', - defaultMessage: 'Flokkun', - }, - type: { - id: 'web.grants:search.type', - defaultMessage: 'Tegund', - }, - resultFound: { - id: 'web.grants:search.resultFound', - defaultMessage: '{arg} styrkur fannst', - }, - resultsFound: { - id: 'web.grants:search.resultsFound', - defaultMessage: '{arg} styrkir fundust', - }, - noResultsFound: { - id: 'web.grants:search.noResultsFound', - defaultMessage: 'Engir styrkir fundust', - }, - organization: { - id: 'web.grants:search.organization', - defaultMessage: 'Stofnun', - }, - }), - single: defineMessages({ - fund: { - id: 'web.grants:single.fund', - defaultMessage: 'Sjóður', - }, - category: { - id: 'web.grants:single.category', - defaultMessage: 'Styrkjaflokkun', - }, - provider: { - id: 'web.grants:single.provider', - defaultMessage: 'Þjónustuaðili', - }, - unknownInstitution: { - id: 'web.grants:single.unknownInstitution', - defaultMessage: 'Óþekkt stofnun', - }, - type: { - id: 'web.grants:single.type', - defaultMessage: 'Tegund', - }, - deadline: { - id: 'web.grants:single.deadline', - defaultMessage: 'Umsóknarfrestur', - }, - status: { - id: 'web.grants:single.status', - defaultMessage: 'Staða', - }, - whatIsGranted: { - id: 'web.grants:single.whatIsGranted', - defaultMessage: 'Hvað er styrkt?', - }, - specialEmphasis: { - id: 'web.grants:single.specialEmphasis', - defaultMessage: 'Sérstakar áherslur', - }, - whoCanApply: { - id: 'web.grants:single.whoCanApply', - defaultMessage: 'Hverjir geta sótt um?', - }, - apply: { - id: 'web.grants:single.apply', - defaultMessage: 'Sækja um', - }, - howToApply: { - id: 'web.grants:single.howToApply', - defaultMessage: 'Hvernig er sótt um?', - }, - }), - home: defineMessages({ - title: { - id: 'web.grants:home.title', - defaultMessage: 'Styrkjatorg', - }, - grant: { - id: 'web.grants:home.gramt', - defaultMessage: 'Styrkur', - }, - description: { - id: 'web.grants:home.description', - defaultMessage: 'Styrkjatorg lýsing .....', - }, - featuredImage: { - id: 'web.grants:home.featuredImage', - defaultMessage: - 'https://images.ctfassets.net/8k0h54kbe6bj/5LqU9yD9nzO5oOijpZF0K0/b595e1cf3e72bc97b2f9d869a53f5da9/LE_-_Jobs_-_S3.png', - }, - featuredImageAlt: { - id: 'web.grants:home.featuredImageAlt', - defaultMessage: 'Mynd af klemmuspjaldi ásamt blýanti', - }, - inputPlaceholder: { - id: 'web.grants:home.inputPlaceholder', - defaultMessage: 'Leitaðu á styrkjatorgi', - }, - mostVisited: { - id: 'web.grants:home.mostVisited', - defaultMessage: 'Mest sótt', - }, - popularCategories: { - id: 'web.grants:home.popularCategories', - defaultMessage: 'Vinsælir flokkar', - }, - allGrants: { - id: 'web.grants:home.allGrants', - defaultMessage: 'Allir styrkir', - }, - }), -} diff --git a/apps/web/screens/Organization/Home/Home.tsx b/apps/web/screens/Organization/Home/Home.tsx index 7cea582b6055..e1f10a1ace18 100644 --- a/apps/web/screens/Organization/Home/Home.tsx +++ b/apps/web/screens/Organization/Home/Home.tsx @@ -50,11 +50,11 @@ const parseOrganizationLinkHref = (organization: Query['getOrganization']) => { return link } -const OrganizationHomePage = ({ +const OrganizationHomePage: Screen = ({ organizationPage, organization, namespace, -}: HomeProps) => { +}) => { const n = useNamespace(namespace) useContentfulId(organizationPage?.id) const { linkResolver } = useLinkResolver() @@ -228,7 +228,7 @@ const OrganizationHomePage = ({ ) } -export interface HomeProps { +interface HomeProps { organizationPage?: Query['getOrganizationPage'] organization?: Query['getOrganization'] namespace: Record @@ -260,7 +260,6 @@ const Home: Screen = ({ } Home.getProps = async ({ apolloClient, locale, query }) => { - const slug = (query.slugs as string[])[0] const [ { data: { getOrganizationPage }, @@ -274,7 +273,7 @@ Home.getProps = async ({ apolloClient, locale, query }) => { query: GET_ORGANIZATION_PAGE_QUERY, variables: { input: { - slug, + slug: query.slug as string, lang: locale as ContentLanguage, }, }, @@ -283,7 +282,7 @@ Home.getProps = async ({ apolloClient, locale, query }) => { query: GET_ORGANIZATION_QUERY, variables: { input: { - slug, + slug: query.slug as string, lang: locale as ContentLanguage, }, }, diff --git a/apps/web/screens/Organization/OrganizationEvents/OrganizationEventArticle.tsx b/apps/web/screens/Organization/OrganizationEvents/OrganizationEventArticle.tsx index ba876f34926b..84b1073b9929 100644 --- a/apps/web/screens/Organization/OrganizationEvents/OrganizationEventArticle.tsx +++ b/apps/web/screens/Organization/OrganizationEvents/OrganizationEventArticle.tsx @@ -1,5 +1,6 @@ import { useEffect, useState } from 'react' import cn from 'classnames' +import type { Locale } from '@island.is/shared/types' import { useRouter } from 'next/router' import { EmbeddedVideo, Image } from '@island.is/island-ui/contentful' @@ -13,7 +14,6 @@ import { Stack, Text, } from '@island.is/island-ui/core' -import type { Locale } from '@island.is/shared/types' import { EventLocation, EventTime, @@ -133,7 +133,7 @@ const EventInformationBox = ({ ) } -export interface OrganizationEventArticleProps { +interface OrganizationEventArticleProps { organizationPage: OrganizationPage event: EventModel namespace: Record @@ -285,14 +285,13 @@ const OrganizationEventArticle: Screen = ({ ) } OrganizationEventArticle.getProps = async ({ apolloClient, query, locale }) => { - const [organizationPageSlug, _, eventSlug] = query.slugs as string[] const [organizationPageResponse, eventResponse, namespace] = await Promise.all([ apolloClient.query({ query: GET_ORGANIZATION_PAGE_QUERY, variables: { input: { - slug: organizationPageSlug, + slug: query.slug as string, lang: locale as Locale, }, }, @@ -301,7 +300,7 @@ OrganizationEventArticle.getProps = async ({ apolloClient, query, locale }) => { query: GET_SINGLE_EVENT_QUERY, variables: { input: { - slug: eventSlug, + slug: query.eventSlug as string, lang: locale as Locale, }, }, diff --git a/apps/web/screens/Organization/OrganizationEvents/OrganizationEventList.tsx b/apps/web/screens/Organization/OrganizationEvents/OrganizationEventList.tsx index 2442d9a02c1b..8c8addf1eaa1 100644 --- a/apps/web/screens/Organization/OrganizationEvents/OrganizationEventList.tsx +++ b/apps/web/screens/Organization/OrganizationEvents/OrganizationEventList.tsx @@ -1,3 +1,4 @@ +import type { Locale } from '@island.is/shared/types' import { useRouter } from 'next/router' import { @@ -8,7 +9,6 @@ import { Stack, Text, } from '@island.is/island-ui/core' -import type { Locale } from '@island.is/shared/types' import { EventList, getThemeConfig, @@ -39,7 +39,7 @@ import { GET_EVENTS_QUERY } from '../../queries/Events' const PAGE_SIZE = 10 -export interface OrganizationEventListProps { +interface OrganizationEventListProps { organizationPage: OrganizationPage eventList: EventListSchema namespace: Record @@ -153,13 +153,12 @@ const extractPageNumberQueryParameter = (query: ScreenContext['query']) => { } OrganizationEventList.getProps = async ({ apolloClient, query, locale }) => { - const slug = (query.slugs as string[])[0] const [organizationPageResponse] = await Promise.all([ apolloClient.query({ query: GET_ORGANIZATION_PAGE_QUERY, variables: { input: { - slug, + slug: query.slug as string, lang: locale as Locale, }, }, @@ -171,7 +170,7 @@ OrganizationEventList.getProps = async ({ apolloClient, query, locale }) => { if (!organizationPage) { throw new CustomNextError( 404, - `Could not find organization page with slug: ${slug}`, + `Could not find organization page with slug: ${query.slug}`, ) } @@ -182,7 +181,8 @@ OrganizationEventList.getProps = async ({ apolloClient, query, locale }) => { query: GET_EVENTS_QUERY, variables: { input: { - organization: organizationPage?.organization?.slug ?? slug, + organization: + organizationPage?.organization?.slug ?? (query.slug as string), lang: locale as Locale, page: selectedPage, size: PAGE_SIZE, diff --git a/apps/web/screens/Organization/OrganizationNews/OrganizationNewsArticle.tsx b/apps/web/screens/Organization/OrganizationNews/OrganizationNewsArticle.tsx index a55e8800dc6a..61472a70d0f5 100644 --- a/apps/web/screens/Organization/OrganizationNews/OrganizationNewsArticle.tsx +++ b/apps/web/screens/Organization/OrganizationNews/OrganizationNewsArticle.tsx @@ -1,37 +1,36 @@ import { useRouter } from 'next/router' - import { BreadCrumbItem, NavigationItem } from '@island.is/island-ui/core' -import { Locale } from '@island.is/shared/types' +import { Screen } from '@island.is/web/types' import { - getThemeConfig, - HeadWithSocialSharing, - NewsArticle, - OrganizationWrapper, -} from '@island.is/web/components' + GET_NAMESPACE_QUERY, + GET_ORGANIZATION_PAGE_QUERY, + GET_SINGLE_NEWS_ITEM_QUERY, +} from '@island.is/web/screens/queries' +import { withMainLayout } from '@island.is/web/layouts/main' +import useContentfulId from '@island.is/web/hooks/useContentfulId' import { ContentLanguage, - GetNamespaceQuery, GetSingleNewsItemQuery, - OrganizationPage, - Query, + QueryGetSingleNewsArgs, QueryGetNamespaceArgs, + GetNamespaceQuery, + Query, QueryGetOrganizationPageArgs, - QueryGetSingleNewsArgs, + OrganizationPage, } from '@island.is/web/graphql/schema' +import { + getThemeConfig, + HeadWithSocialSharing, + NewsArticle, + OrganizationWrapper, +} from '@island.is/web/components' import { useNamespace } from '@island.is/web/hooks' import { useLinkResolver } from '@island.is/web/hooks' -import useContentfulId from '@island.is/web/hooks/useContentfulId' -import { useLocalLinkTypeResolver } from '@island.is/web/hooks/useLocalLinkTypeResolver' -import { withMainLayout } from '@island.is/web/layouts/main' -import { - GET_NAMESPACE_QUERY, - GET_ORGANIZATION_PAGE_QUERY, - GET_SINGLE_NEWS_ITEM_QUERY, -} from '@island.is/web/screens/queries' -import { Screen } from '@island.is/web/types' import { CustomNextError } from '@island.is/web/units/errors' +import { useLocalLinkTypeResolver } from '@island.is/web/hooks/useLocalLinkTypeResolver' +import { Locale } from '@island.is/shared/types' -export interface OrganizationNewsArticleProps { +interface OrganizationNewsArticleProps { newsItem: GetSingleNewsItemQuery['getSingleNews'] namespace: GetNamespaceQuery['getNamespace'] organizationPage: OrganizationPage @@ -163,15 +162,13 @@ const OrganizationNewsArticle: Screen = ({ } OrganizationNewsArticle.getProps = async ({ apolloClient, locale, query }) => { - const [organizationPageSlug, _, newsSlug] = query.slugs as string[] - const organizationPage = ( await Promise.resolve( apolloClient.query({ query: GET_ORGANIZATION_PAGE_QUERY, variables: { input: { - slug: organizationPageSlug, + slug: query.slug as string, lang: locale as Locale, }, }, @@ -182,7 +179,7 @@ OrganizationNewsArticle.getProps = async ({ apolloClient, locale, query }) => { if (!organizationPage) { throw new CustomNextError( 404, - `Could not find organization page with slug: ${organizationPageSlug}`, + `Could not find organization page with slug: ${query.slug}`, ) } @@ -196,7 +193,7 @@ OrganizationNewsArticle.getProps = async ({ apolloClient, locale, query }) => { query: GET_SINGLE_NEWS_ITEM_QUERY, variables: { input: { - slug: newsSlug, + slug: query.newsSlug as string, lang: locale as ContentLanguage, }, }, diff --git a/apps/web/screens/Organization/OrganizationNews/OrganizationNewsList.tsx b/apps/web/screens/Organization/OrganizationNews/OrganizationNewsList.tsx index cc21540d2977..e868b65c663a 100644 --- a/apps/web/screens/Organization/OrganizationNews/OrganizationNewsList.tsx +++ b/apps/web/screens/Organization/OrganizationNews/OrganizationNewsList.tsx @@ -44,7 +44,7 @@ import { GET_GENERIC_TAG_BY_SLUG_QUERY } from '../../queries/GenericTag' const PERPAGE = 10 -export interface OrganizationNewsListProps { +interface OrganizationNewsListProps { organizationPage: OrganizationPage newsList: GetNewsQuery['getNews']['items'] total: number @@ -236,15 +236,13 @@ OrganizationNewsList.getProps = async ({ apolloClient, query, locale }) => { const month = year && getIntParam(query.m, { minValue: 1, maxValue: 12 }) const selectedPage = getIntParam(query.page, { minValue: 1 }) ?? 1 - const organizationPageSlug = (query.slugs as string[])[0] - const organizationPage = ( await Promise.resolve( apolloClient.query({ query: GET_ORGANIZATION_PAGE_QUERY, variables: { input: { - slug: organizationPageSlug, + slug: query.slug as string, lang: locale as Locale, }, }, @@ -255,7 +253,7 @@ OrganizationNewsList.getProps = async ({ apolloClient, query, locale }) => { if (!organizationPage) { throw new CustomNextError( 404, - `Could not find organization page with slug: ${organizationPageSlug}`, + `Could not find organization page with slug: ${query.slug}`, ) } diff --git a/apps/web/screens/Organization/PublishedMaterial/PublishedMaterial.tsx b/apps/web/screens/Organization/PublishedMaterial/PublishedMaterial.tsx index ad6cb1300f7a..715fa5a10709 100644 --- a/apps/web/screens/Organization/PublishedMaterial/PublishedMaterial.tsx +++ b/apps/web/screens/Organization/PublishedMaterial/PublishedMaterial.tsx @@ -64,7 +64,7 @@ import * as styles from './PublishedMaterial.css' const ASSETS_PER_PAGE = 20 const DEBOUNCE_TIME_IN_MS = 300 -export interface PublishedMaterialProps { +interface PublishedMaterialProps { organizationPage: Query['getOrganizationPage'] genericTagFilters: GenericTag[] namespace: Record @@ -500,7 +500,6 @@ const PublishedMaterial: Screen = ({ } PublishedMaterial.getProps = async ({ apolloClient, locale, query }) => { - const organizationPageSlug = (query.slugs as string[])[0] const [ { data: { getOrganizationPage }, @@ -511,7 +510,7 @@ PublishedMaterial.getProps = async ({ apolloClient, locale, query }) => { query: GET_ORGANIZATION_PAGE_QUERY, variables: { input: { - slug: organizationPageSlug, + slug: query.slug as string, lang: locale as ContentLanguage, }, }, @@ -542,7 +541,7 @@ PublishedMaterial.getProps = async ({ apolloClient, locale, query }) => { query: GET_ORGANIZATION_QUERY, variables: { input: { - slug: getOrganizationPage.organization?.slug ?? organizationPageSlug, + slug: getOrganizationPage.organization?.slug ?? (query.slug as string), lang: locale as ContentLanguage, }, }, diff --git a/apps/web/screens/Organization/SubPage.tsx b/apps/web/screens/Organization/SubPage.tsx index 2f3b938d0cf9..0db2c57fee70 100644 --- a/apps/web/screens/Organization/SubPage.tsx +++ b/apps/web/screens/Organization/SubPage.tsx @@ -1,3 +1,4 @@ +import { Locale } from '@island.is/shared/types' import { useRouter } from 'next/router' import { ParsedUrlQuery } from 'querystring' @@ -13,7 +14,6 @@ import { Stack, Text, } from '@island.is/island-ui/core' -import { Locale } from '@island.is/shared/types' import { getThemeConfig, OrganizationWrapper, @@ -60,13 +60,46 @@ export interface SubPageProps { customContentfulIds?: (string | undefined)[] } -const SubPageContent = ({ +const SubPage: Screen = ({ + organizationPage, subpage, namespace, - organizationPage, -}: Pick) => { - const n = useNamespace(namespace) + locale, + customContent, + customBreadcrumbItems, + customContentfulIds, + backLink, +}) => { + const router = useRouter() const { activeLocale } = useI18n() + + const n = useNamespace(namespace) + const { linkResolver } = useLinkResolver() + + const contentfulIds = customContentfulIds + ? customContentfulIds + : [organizationPage?.id, subpage?.id] + + 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, + })), + }), + ) + const content = ( <> {subpage?.showTableOfContents && ( @@ -114,137 +147,6 @@ const SubPageContent = ({ ) - return ( - <> - - - - - - - - <> - - - {subpage?.title} - - - - - {subpage?.signLanguageVideo?.url && ( - - - {subpage.title} - - {content} - {renderSlices( - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore make web strict - subpage.slices, - subpage.sliceCustomRenderer, - subpage.sliceExtraText, - namespace, - organizationPage?.slug, - )} - - } - /> - )} - - - - - {content} - - - - - - - {renderSlices( - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore make web strict - subpage.slices, - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore make web strict - subpage.sliceCustomRenderer, - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore make web strict - subpage.sliceExtraText, - namespace, - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore make web strict - organizationPage.slug, - )} - - - ) -} - -const SubPage: Screen = ({ - organizationPage, - subpage, - namespace, - locale, - customContent, - customBreadcrumbItems, - customContentfulIds, - backLink, -}) => { - const router = useRouter() - - const n = useNamespace(namespace) - const { linkResolver } = useLinkResolver() - - const contentfulIds = customContentfulIds - ? customContentfulIds - : [organizationPage?.id, subpage?.id] - - 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 ( = ({ items: navList, }} > - {customContent ? ( - - - - - {customContent} - - - - - ) : ( - - )} + + + + + + + + {customContent ? ( + customContent + ) : ( + <> + + + {subpage?.title} + + + + + {subpage?.signLanguageVideo?.url && ( + + + {subpage.title} + + {content} + {renderSlices( + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore make web strict + subpage.slices, + subpage.sliceCustomRenderer, + subpage.sliceExtraText, + namespace, + organizationPage?.slug, + )} + + } + /> + )} + + + )} + + + {!customContent && content} + + + + + + + {renderSlices( + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore make web strict + subpage.slices, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore make web strict + subpage.sliceCustomRenderer, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore make web strict + subpage.sliceExtraText, + namespace, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore make web strict + organizationPage.slug, + )} + ) } @@ -431,7 +404,7 @@ SubPage.getProps = async ({ apolloClient, locale, query, req }) => { const getSlugAndSubSlug = (query: ParsedUrlQuery, pathname: string) => { const path = pathname?.split('/') ?? [] - let [slug, subSlug] = (query.slugs as string[]) ?? [] + let { slug, subSlug } = query if (!slug && path.length >= 2) { // The slug is the next-last index in the path, i.e. "syslumenn" in the case of "/s/syslumenn/utgefid-efni" @@ -439,7 +412,7 @@ const getSlugAndSubSlug = (query: ParsedUrlQuery, pathname: string) => { } if (!subSlug && path.length > 0) { // The subslug is the last index in the path, i.e. "utgefid-efni" in the case of "/s/syslumenn/utgefid-efni" - subSlug = path.pop() as string + subSlug = path.pop() } return { slug, subSlug } diff --git a/apps/web/screens/queries/GenericTag.ts b/apps/web/screens/queries/GenericTag.ts index 7ed6c061300c..91b0797138c8 100644 --- a/apps/web/screens/queries/GenericTag.ts +++ b/apps/web/screens/queries/GenericTag.ts @@ -8,18 +8,3 @@ export const GET_GENERIC_TAG_BY_SLUG_QUERY = gql` } } ` - -export const GET_GENERIC_TAGS_IN_TAG_GROUPS_QUERY = gql` - query GetGenericTagInTagGroups($input: GetGenericTagsInTagGroupsInput!) { - getGenericTagsInTagGroups(input: $input) { - id - title - slug - genericTagGroup { - id - title - slug - } - } - } -` diff --git a/apps/web/screens/queries/Grants.ts b/apps/web/screens/queries/Grants.ts deleted file mode 100644 index b042c9e46aa4..000000000000 --- a/apps/web/screens/queries/Grants.ts +++ /dev/null @@ -1,126 +0,0 @@ -import gql from 'graphql-tag' -import { nestedFields, slices } from './fragments' - -export const GET_GRANTS_QUERY = gql` - query GetGrants($input: GetGrantsInput!) { - getGrants(input: $input) { - items { - id - name - description - applicationId - applicationDeadlineText - applicationUrl { - slug - type - } - dateFrom - dateTo - isOpen - status - statusText - categoryTags { - id - title - genericTagGroup { - title - } - } - typeTag { - id - title - genericTagGroup { - title - } - } - fund { - id - title - link { - slug - type - } - featuredImage { - id - url - } - parentOrganization { - id - title - logo { - url - } - } - } - } - } - } -` - -export const GET_GRANT_QUERY = gql` - query GetGrant($input: GetSingleGrantInput!) { - getSingleGrant(input: $input) { - id - name - description - applicationId - applicationUrl { - slug - type - } - applicationDeadlineText - statusText - categoryTags { - id - title - } - typeTag { - id - title - } - files { - ...AssetFields - } - fund { - id - title - link { - slug - type - } - featuredImage { - id - url - } - parentOrganization { - id - title - logo { - url - } - } - } - specialEmphasis { - ...AllSlices - ${nestedFields} - } - whoCanApply { - ...AllSlices - ${nestedFields} - } - howToApply { - ...AllSlices - ${nestedFields} - } - applicationDeadline { - ...AllSlices - ${nestedFields} - } - applicationHints { - ...AllSlices - ${nestedFields} - } - } - } - ${slices} -` diff --git a/apps/web/screens/queries/index.tsx b/apps/web/screens/queries/index.tsx index 9b1c24ac57bc..a1e0347e1114 100644 --- a/apps/web/screens/queries/index.tsx +++ b/apps/web/screens/queries/index.tsx @@ -13,7 +13,6 @@ export * from './Adgerdir' export * from './Frontpage' export * from './TellUsAStory' export * from './ServiceWebForms' -export * from './Grants' export * from './ApiCatalogue' export * from './SubpageHeader' export * from './ErrorPage' diff --git a/apps/web/types.tsx b/apps/web/types.tsx index 245763e30d29..a20d98059be8 100644 --- a/apps/web/types.tsx +++ b/apps/web/types.tsx @@ -1,7 +1,7 @@ import { ComponentType } from 'react' -import { GetServerSidePropsContext } from 'next' -import { NormalizedCacheObject } from '@apollo/client/cache' import { ApolloClient } from '@apollo/client/core' +import { NormalizedCacheObject } from '@apollo/client/cache' +import { GetServerSidePropsContext } from 'next' export type ScreenContext = { query: GetServerSidePropsContext['query'] @@ -12,5 +12,5 @@ export type ScreenContext = { } export type Screen = ComponentType & { - getProps: (ctx: ScreenContext) => Promise + getProps?: (ctx: ScreenContext) => Promise } diff --git a/apps/web/utils/richText.tsx b/apps/web/utils/richText.tsx index e0456a2f12ab..41c1b857d440 100644 --- a/apps/web/utils/richText.tsx +++ b/apps/web/utils/richText.tsx @@ -88,7 +88,6 @@ import HousingBenefitCalculator from '../components/connected/HousingBenefitCalc import JourneymanList from '../components/connected/syslumenn/TableLists/JourneymanList/JourneymanList' import ProfessionRights from '../components/connected/syslumenn/TableLists/ProfessionRights/ProfessionRights' import { UmsCostOfLivingCalculator } from '../components/connected/UmbodsmadurSkuldara' -import { WHODASCalculator } from '../components/connected/WHODAS/Calculator' import FeaturedEvents from '../components/FeaturedEvents/FeaturedEvents' import FeaturedSupportQNAs from '../components/FeaturedSupportQNAs/FeaturedSupportQNAs' import { EmbedSlice } from '../components/Organization/Slice/EmbedSlice/EmbedSlice' @@ -200,9 +199,6 @@ export const webRenderConnectedComponent = ( ) break - case 'WHODAS/Calculator': - connectedComponent = - break default: connectedComponent = renderConnectedComponent(slice) } diff --git a/charts/islandis/values.dev.yaml b/charts/islandis/values.dev.yaml index cf7b2fabaa49..be98e9b1790d 100644 --- a/charts/islandis/values.dev.yaml +++ b/charts/islandis/values.dev.yaml @@ -700,11 +700,11 @@ application-system-api: grantNamespacesEnabled: true healthCheck: liveness: - initialDelaySeconds: 20 + initialDelaySeconds: 3 path: '/liveness' timeoutSeconds: 3 readiness: - initialDelaySeconds: 20 + initialDelaySeconds: 3 path: '/liveness' timeoutSeconds: 3 hpa: @@ -768,10 +768,10 @@ application-system-api: min: 2 resources: limits: - cpu: '600m' + cpu: '400m' memory: '1024Mi' requests: - cpu: '200m' + cpu: '75m' memory: '512Mi' secrets: ALTHINGI_OMBUDSMAN_XROAD_PASSWORD: '/k8s/api/ALTHINGI_OMBUDSMAN_XROAD_PASSWORD' @@ -3092,7 +3092,6 @@ user-notification: - 'nginx-ingress-internal' - 'islandis' - 'identity-server-delegation' - - 'application-system' grantNamespacesEnabled: true healthCheck: liveness: @@ -3177,7 +3176,6 @@ user-notification-cleanup-worker: - 'nginx-ingress-internal' - 'islandis' - 'identity-server-delegation' - - 'application-system' grantNamespacesEnabled: true healthCheck: liveness: @@ -3293,7 +3291,6 @@ user-notification-worker: - 'nginx-ingress-internal' - 'islandis' - 'identity-server-delegation' - - 'application-system' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/islandis/values.prod.yaml b/charts/islandis/values.prod.yaml index 66ded2ed398d..82318dd89853 100644 --- a/charts/islandis/values.prod.yaml +++ b/charts/islandis/values.prod.yaml @@ -687,11 +687,11 @@ application-system-api: grantNamespacesEnabled: true healthCheck: liveness: - initialDelaySeconds: 20 + initialDelaySeconds: 3 path: '/liveness' timeoutSeconds: 3 readiness: - initialDelaySeconds: 20 + initialDelaySeconds: 3 path: '/liveness' timeoutSeconds: 3 hpa: @@ -755,10 +755,10 @@ application-system-api: min: 2 resources: limits: - cpu: '600m' + cpu: '400m' memory: '1024Mi' requests: - cpu: '200m' + cpu: '75m' memory: '512Mi' secrets: ALTHINGI_OMBUDSMAN_XROAD_PASSWORD: '/k8s/api/ALTHINGI_OMBUDSMAN_XROAD_PASSWORD' @@ -2968,7 +2968,6 @@ user-notification: - 'nginx-ingress-internal' - 'islandis' - 'identity-server-delegation' - - 'application-system' grantNamespacesEnabled: true healthCheck: liveness: @@ -3053,7 +3052,6 @@ user-notification-cleanup-worker: - 'nginx-ingress-internal' - 'islandis' - 'identity-server-delegation' - - 'application-system' grantNamespacesEnabled: true healthCheck: liveness: @@ -3169,7 +3167,6 @@ user-notification-worker: - 'nginx-ingress-internal' - 'islandis' - 'identity-server-delegation' - - 'application-system' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/islandis/values.staging.yaml b/charts/islandis/values.staging.yaml index f0836bbb7380..6d70c4ca2bc1 100644 --- a/charts/islandis/values.staging.yaml +++ b/charts/islandis/values.staging.yaml @@ -696,11 +696,11 @@ application-system-api: grantNamespacesEnabled: true healthCheck: liveness: - initialDelaySeconds: 20 + initialDelaySeconds: 3 path: '/liveness' timeoutSeconds: 3 readiness: - initialDelaySeconds: 20 + initialDelaySeconds: 3 path: '/liveness' timeoutSeconds: 3 hpa: @@ -764,10 +764,10 @@ application-system-api: min: 2 resources: limits: - cpu: '600m' + cpu: '400m' memory: '1024Mi' requests: - cpu: '200m' + cpu: '75m' memory: '512Mi' secrets: ALTHINGI_OMBUDSMAN_XROAD_PASSWORD: '/k8s/api/ALTHINGI_OMBUDSMAN_XROAD_PASSWORD' @@ -2829,7 +2829,6 @@ user-notification: - 'nginx-ingress-internal' - 'islandis' - 'identity-server-delegation' - - 'application-system' grantNamespacesEnabled: true healthCheck: liveness: @@ -2914,7 +2913,6 @@ user-notification-cleanup-worker: - 'nginx-ingress-internal' - 'islandis' - 'identity-server-delegation' - - 'application-system' grantNamespacesEnabled: true healthCheck: liveness: @@ -3030,7 +3028,6 @@ user-notification-worker: - 'nginx-ingress-internal' - 'islandis' - 'identity-server-delegation' - - 'application-system' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/judicial-system/values.dev.yaml b/charts/judicial-system/values.dev.yaml index 161909e0781c..88200d412a54 100644 --- a/charts/judicial-system/values.dev.yaml +++ b/charts/judicial-system/values.dev.yaml @@ -375,6 +375,76 @@ judicial-system-message-handler: eks.amazonaws.com/role-arn: 'arn:aws:iam::013313053092:role/judicial-system-message-handler' create: true name: 'judicial-system-message-handler' +judicial-system-robot-api: + enabled: true + env: + AUDIT_TRAIL_GROUP_NAME: 'k8s/judicial-system/audit-log' + AUDIT_TRAIL_REGION: 'eu-west-1' + AUDIT_TRAIL_USE_GENERIC_LOGGER: 'false' + BACKEND_URL: 'http://web-judicial-system-backend' + LOG_LEVEL: 'info' + NODE_OPTIONS: '--max-old-space-size=230 -r dd-trace/init' + SERVERSIDE_FEATURES_ON: '' + grantNamespaces: + - 'nginx-ingress-internal' + grantNamespacesEnabled: true + healthCheck: + liveness: + initialDelaySeconds: 3 + path: '/liveness' + timeoutSeconds: 3 + readiness: + initialDelaySeconds: 3 + path: '/liveness' + timeoutSeconds: 3 + hpa: + scaling: + metric: + cpuAverageUtilization: 90 + nginxRequestsIrate: 5 + replicas: + max: 3 + min: 1 + image: + repository: '821090935708.dkr.ecr.eu-west-1.amazonaws.com/judicial-system-robot-api' + ingress: + primary-alb: + annotations: + kubernetes.io/ingress.class: 'nginx-internal-alb' + nginx.ingress.kubernetes.io/service-upstream: 'true' + hosts: + - host: 'judicial-system-robot-api-xrd.internal.dev01.devland.is' + paths: + - '/' + namespace: 'judicial-system' + podDisruptionBudget: + maxUnavailable: 1 + podSecurityContext: + fsGroup: 65534 + pvcs: [] + replicaCount: + default: 1 + max: 3 + min: 1 + resources: + limits: + cpu: '200m' + memory: '256Mi' + requests: + cpu: '100m' + memory: '128Mi' + secrets: + BACKEND_ACCESS_TOKEN: '/k8s/judicial-system/BACKEND_ACCESS_TOKEN' + CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' + ERROR_EVENT_URL: '/k8s/judicial-system/ERROR_EVENT_URL' + securityContext: + allowPrivilegeEscalation: false + privileged: false + serviceAccount: + annotations: + eks.amazonaws.com/role-arn: 'arn:aws:iam::013313053092:role/judicial-system-robot-api' + create: true + name: 'judicial-system-robot-api' judicial-system-scheduler: args: - '--no-experimental-fetch' diff --git a/charts/judicial-system/values.prod.yaml b/charts/judicial-system/values.prod.yaml index 511ed797ff10..a963d0ca05f9 100644 --- a/charts/judicial-system/values.prod.yaml +++ b/charts/judicial-system/values.prod.yaml @@ -375,6 +375,76 @@ judicial-system-message-handler: eks.amazonaws.com/role-arn: 'arn:aws:iam::251502586493:role/judicial-system-message-handler' create: true name: 'judicial-system-message-handler' +judicial-system-robot-api: + enabled: true + env: + AUDIT_TRAIL_GROUP_NAME: 'k8s/judicial-system/audit-log' + AUDIT_TRAIL_REGION: 'eu-west-1' + AUDIT_TRAIL_USE_GENERIC_LOGGER: 'false' + BACKEND_URL: 'http://web-judicial-system-backend' + LOG_LEVEL: 'info' + NODE_OPTIONS: '--max-old-space-size=230 -r dd-trace/init' + SERVERSIDE_FEATURES_ON: 'driving-license-use-v1-endpoint-for-v2-comms' + grantNamespaces: + - 'nginx-ingress-internal' + grantNamespacesEnabled: true + healthCheck: + liveness: + initialDelaySeconds: 3 + path: '/liveness' + timeoutSeconds: 3 + readiness: + initialDelaySeconds: 3 + path: '/liveness' + timeoutSeconds: 3 + hpa: + scaling: + metric: + cpuAverageUtilization: 90 + nginxRequestsIrate: 5 + replicas: + max: 10 + min: 3 + image: + repository: '821090935708.dkr.ecr.eu-west-1.amazonaws.com/judicial-system-robot-api' + ingress: + primary-alb: + annotations: + kubernetes.io/ingress.class: 'nginx-internal-alb' + nginx.ingress.kubernetes.io/service-upstream: 'true' + hosts: + - host: 'judicial-system-robot-api-xrd.internal.island.is' + paths: + - '/' + namespace: 'judicial-system' + podDisruptionBudget: + maxUnavailable: 1 + podSecurityContext: + fsGroup: 65534 + pvcs: [] + replicaCount: + default: 3 + max: 10 + min: 3 + resources: + limits: + cpu: '200m' + memory: '256Mi' + requests: + cpu: '100m' + memory: '128Mi' + secrets: + BACKEND_ACCESS_TOKEN: '/k8s/judicial-system/BACKEND_ACCESS_TOKEN' + CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' + ERROR_EVENT_URL: '/k8s/judicial-system/ERROR_EVENT_URL' + securityContext: + allowPrivilegeEscalation: false + privileged: false + serviceAccount: + annotations: + eks.amazonaws.com/role-arn: 'arn:aws:iam::251502586493:role/judicial-system-robot-api' + create: true + name: 'judicial-system-robot-api' judicial-system-scheduler: args: - '--no-experimental-fetch' diff --git a/charts/judicial-system/values.staging.yaml b/charts/judicial-system/values.staging.yaml index 18be3b03e360..6cb69f4a67b9 100644 --- a/charts/judicial-system/values.staging.yaml +++ b/charts/judicial-system/values.staging.yaml @@ -376,6 +376,76 @@ judicial-system-message-handler: eks.amazonaws.com/role-arn: 'arn:aws:iam::261174024191:role/judicial-system-message-handler' create: true name: 'judicial-system-message-handler' +judicial-system-robot-api: + enabled: true + env: + AUDIT_TRAIL_GROUP_NAME: 'k8s/judicial-system/audit-log' + AUDIT_TRAIL_REGION: 'eu-west-1' + AUDIT_TRAIL_USE_GENERIC_LOGGER: 'false' + BACKEND_URL: 'http://web-judicial-system-backend' + LOG_LEVEL: 'info' + NODE_OPTIONS: '--max-old-space-size=230 -r dd-trace/init' + SERVERSIDE_FEATURES_ON: '' + grantNamespaces: + - 'nginx-ingress-internal' + grantNamespacesEnabled: true + healthCheck: + liveness: + initialDelaySeconds: 3 + path: '/liveness' + timeoutSeconds: 3 + readiness: + initialDelaySeconds: 3 + path: '/liveness' + timeoutSeconds: 3 + hpa: + scaling: + metric: + cpuAverageUtilization: 90 + nginxRequestsIrate: 5 + replicas: + max: 3 + min: 1 + image: + repository: '821090935708.dkr.ecr.eu-west-1.amazonaws.com/judicial-system-robot-api' + ingress: + primary-alb: + annotations: + kubernetes.io/ingress.class: 'nginx-internal-alb' + nginx.ingress.kubernetes.io/service-upstream: 'true' + hosts: + - host: 'judicial-system-robot-api-xrd.internal.staging01.devland.is' + paths: + - '/' + namespace: 'judicial-system' + podDisruptionBudget: + maxUnavailable: 1 + podSecurityContext: + fsGroup: 65534 + pvcs: [] + replicaCount: + default: 1 + max: 3 + min: 1 + resources: + limits: + cpu: '200m' + memory: '256Mi' + requests: + cpu: '100m' + memory: '128Mi' + secrets: + BACKEND_ACCESS_TOKEN: '/k8s/judicial-system/BACKEND_ACCESS_TOKEN' + CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' + ERROR_EVENT_URL: '/k8s/judicial-system/ERROR_EVENT_URL' + securityContext: + allowPrivilegeEscalation: false + privileged: false + serviceAccount: + annotations: + eks.amazonaws.com/role-arn: 'arn:aws:iam::261174024191:role/judicial-system-robot-api' + create: true + name: 'judicial-system-robot-api' judicial-system-scheduler: args: - '--no-experimental-fetch' diff --git a/charts/services/application-system-api/values.dev.yaml b/charts/services/application-system-api/values.dev.yaml index 9cabf0c60fd2..1afae5cc210e 100644 --- a/charts/services/application-system-api/values.dev.yaml +++ b/charts/services/application-system-api/values.dev.yaml @@ -142,11 +142,11 @@ grantNamespaces: grantNamespacesEnabled: true healthCheck: liveness: - initialDelaySeconds: 20 + initialDelaySeconds: 3 path: '/liveness' timeoutSeconds: 3 readiness: - initialDelaySeconds: 20 + initialDelaySeconds: 3 path: '/liveness' timeoutSeconds: 3 hpa: @@ -210,10 +210,10 @@ replicaCount: min: 2 resources: limits: - cpu: '600m' + cpu: '400m' memory: '1024Mi' requests: - cpu: '200m' + cpu: '75m' memory: '512Mi' secrets: ALTHINGI_OMBUDSMAN_XROAD_PASSWORD: '/k8s/api/ALTHINGI_OMBUDSMAN_XROAD_PASSWORD' diff --git a/charts/services/application-system-api/values.prod.yaml b/charts/services/application-system-api/values.prod.yaml index bb8e6388c85d..d38991ed8318 100644 --- a/charts/services/application-system-api/values.prod.yaml +++ b/charts/services/application-system-api/values.prod.yaml @@ -142,11 +142,11 @@ grantNamespaces: grantNamespacesEnabled: true healthCheck: liveness: - initialDelaySeconds: 20 + initialDelaySeconds: 3 path: '/liveness' timeoutSeconds: 3 readiness: - initialDelaySeconds: 20 + initialDelaySeconds: 3 path: '/liveness' timeoutSeconds: 3 hpa: @@ -210,10 +210,10 @@ replicaCount: min: 2 resources: limits: - cpu: '600m' + cpu: '400m' memory: '1024Mi' requests: - cpu: '200m' + cpu: '75m' memory: '512Mi' secrets: ALTHINGI_OMBUDSMAN_XROAD_PASSWORD: '/k8s/api/ALTHINGI_OMBUDSMAN_XROAD_PASSWORD' diff --git a/charts/services/application-system-api/values.staging.yaml b/charts/services/application-system-api/values.staging.yaml index 4be6d8efdc8e..c1b6c19b7099 100644 --- a/charts/services/application-system-api/values.staging.yaml +++ b/charts/services/application-system-api/values.staging.yaml @@ -142,11 +142,11 @@ grantNamespaces: grantNamespacesEnabled: true healthCheck: liveness: - initialDelaySeconds: 20 + initialDelaySeconds: 3 path: '/liveness' timeoutSeconds: 3 readiness: - initialDelaySeconds: 20 + initialDelaySeconds: 3 path: '/liveness' timeoutSeconds: 3 hpa: @@ -210,10 +210,10 @@ replicaCount: min: 2 resources: limits: - cpu: '600m' + cpu: '400m' memory: '1024Mi' requests: - cpu: '200m' + cpu: '75m' memory: '512Mi' secrets: ALTHINGI_OMBUDSMAN_XROAD_PASSWORD: '/k8s/api/ALTHINGI_OMBUDSMAN_XROAD_PASSWORD' diff --git a/charts/services/user-notification-cleanup-worker/values.dev.yaml b/charts/services/user-notification-cleanup-worker/values.dev.yaml index 9e558ec19c87..0a120dea2a1a 100644 --- a/charts/services/user-notification-cleanup-worker/values.dev.yaml +++ b/charts/services/user-notification-cleanup-worker/values.dev.yaml @@ -36,7 +36,6 @@ grantNamespaces: - 'nginx-ingress-internal' - 'islandis' - 'identity-server-delegation' - - 'application-system' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/services/user-notification-cleanup-worker/values.prod.yaml b/charts/services/user-notification-cleanup-worker/values.prod.yaml index 43ba4e7bf3c7..d8fce489b1a7 100644 --- a/charts/services/user-notification-cleanup-worker/values.prod.yaml +++ b/charts/services/user-notification-cleanup-worker/values.prod.yaml @@ -36,7 +36,6 @@ grantNamespaces: - 'nginx-ingress-internal' - 'islandis' - 'identity-server-delegation' - - 'application-system' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/services/user-notification-cleanup-worker/values.staging.yaml b/charts/services/user-notification-cleanup-worker/values.staging.yaml index 946da9daabf0..f2e7f59cd8c1 100644 --- a/charts/services/user-notification-cleanup-worker/values.staging.yaml +++ b/charts/services/user-notification-cleanup-worker/values.staging.yaml @@ -36,7 +36,6 @@ grantNamespaces: - 'nginx-ingress-internal' - 'islandis' - 'identity-server-delegation' - - 'application-system' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/services/user-notification-worker/values.dev.yaml b/charts/services/user-notification-worker/values.dev.yaml index 9b31936004f0..1bf7c01440c1 100644 --- a/charts/services/user-notification-worker/values.dev.yaml +++ b/charts/services/user-notification-worker/values.dev.yaml @@ -59,7 +59,6 @@ grantNamespaces: - 'nginx-ingress-internal' - 'islandis' - 'identity-server-delegation' - - 'application-system' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/services/user-notification-worker/values.prod.yaml b/charts/services/user-notification-worker/values.prod.yaml index 505fd0fc1186..2ce3c5f1e8f6 100644 --- a/charts/services/user-notification-worker/values.prod.yaml +++ b/charts/services/user-notification-worker/values.prod.yaml @@ -59,7 +59,6 @@ grantNamespaces: - 'nginx-ingress-internal' - 'islandis' - 'identity-server-delegation' - - 'application-system' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/services/user-notification-worker/values.staging.yaml b/charts/services/user-notification-worker/values.staging.yaml index 20c9089348c6..6016cd6dfdea 100644 --- a/charts/services/user-notification-worker/values.staging.yaml +++ b/charts/services/user-notification-worker/values.staging.yaml @@ -59,7 +59,6 @@ grantNamespaces: - 'nginx-ingress-internal' - 'islandis' - 'identity-server-delegation' - - 'application-system' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/services/user-notification/values.dev.yaml b/charts/services/user-notification/values.dev.yaml index 31c759a45806..de82ce62f772 100644 --- a/charts/services/user-notification/values.dev.yaml +++ b/charts/services/user-notification/values.dev.yaml @@ -56,7 +56,6 @@ grantNamespaces: - 'nginx-ingress-internal' - 'islandis' - 'identity-server-delegation' - - 'application-system' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/services/user-notification/values.prod.yaml b/charts/services/user-notification/values.prod.yaml index b6d09de39826..5431ce0f5d1c 100644 --- a/charts/services/user-notification/values.prod.yaml +++ b/charts/services/user-notification/values.prod.yaml @@ -56,7 +56,6 @@ grantNamespaces: - 'nginx-ingress-internal' - 'islandis' - 'identity-server-delegation' - - 'application-system' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/charts/services/user-notification/values.staging.yaml b/charts/services/user-notification/values.staging.yaml index e6dc9bc23264..f4c312c8b9fa 100644 --- a/charts/services/user-notification/values.staging.yaml +++ b/charts/services/user-notification/values.staging.yaml @@ -56,7 +56,6 @@ grantNamespaces: - 'nginx-ingress-internal' - 'islandis' - 'identity-server-delegation' - - 'application-system' grantNamespacesEnabled: true healthCheck: liveness: diff --git a/infra/README.md b/infra/README.md index 8bdf8d606924..4456476bd408 100644 --- a/infra/README.md +++ b/infra/README.md @@ -1,4 +1,4 @@ -# Infra Tool Documentation +# CLI Tool Documentation This CLI tool simplifies the management and setup of local environments for development. It provides commands to render environment variables, secrets, and URLs that are essential for configuring services during development. @@ -8,12 +8,35 @@ This CLI tool simplifies the management and setup of local environments for deve - **Ingress URL Management**: Generate and render ingress URLs for your services. - **Service Configuration**: Ensure all necessary secrets and environment variables are loaded to correctly configure services. -## Available Commands - -For a full list of available commands and options, run: - -```bash -yarn infra --help +### Available Commands + +While the CLI offers various commands, one of the most powerful and useful for developers is the `run-local-env` command. This command allows developers to render the required environment and start a local development environment with the necessary configuration. + +Here is a summary of all available commands in the CLI: + +```text +yarn cli + +Commands: + yarn cli render-env Render a chart for environment + yarn cli render-urls Render urls from ingress for environment + yarn cli render-secrets Render secrets secrets needed by service + yarn cli render-env-vars Render environment variables needed by service. + This is to be used when developing locally and + loading of the environment variables for "dev" + environment is needed. + yarn cli render-local-env Render environment variables needed by service. + This is to be used when developing locally and + loading of the environment variables for "dev" + environment is needed. + yarn cli run-local-env Render environment and run the local environment. + This is to be used when developing locally and + loading of the environment variables for "dev" + environment is needed. + +Options: + --help Show help [boolean] + --version Show version number [boolean] ``` ### `run-local-env` Command @@ -23,7 +46,7 @@ The `run-local-env` command is a key tool for developers working in local enviro #### Example Usage ```bash -yarn infra run-local-env [options] +yarn cli run-local-env [options] ``` This command automates the configuration setup, ensuring that the correct environment variables and dependencies are loaded for your local development environment. diff --git a/infra/helm/Chart.lock b/infra/helm/Chart.lock index 18eb45050564..477c293f5ef4 100644 --- a/infra/helm/Chart.lock +++ b/infra/helm/Chart.lock @@ -56,6 +56,9 @@ dependencies: - name: api-template repository: file://libs/api-template version: 0.0.1 +- name: api-template + repository: file://libs/api-template + version: 0.0.1 - name: cronjob-template repository: file://libs/cronjob-template version: 0.0.1 @@ -107,17 +110,11 @@ dependencies: - name: api-template repository: file://libs/api-template version: 0.0.1 -- name: api-template - repository: file://libs/api-template - version: 0.0.1 -- name: api-template - repository: file://libs/api-template - version: 0.0.1 -- name: api-template - repository: file://libs/api-template +- name: cronjob-template + repository: file://libs/cronjob-template version: 0.0.1 - name: api-template repository: file://libs/api-template version: 0.0.1 -digest: sha256:4a1d366fb8ddc48a860688bd2137eacb9693056372824c634eb3d40d32797247 -generated: "2024-11-21T11:44:06.23465238Z" +digest: sha256:d282af5c4270985ea9ca1ecb9f8bfb5c96f72ac30aade68f34b7e7f898da180b +generated: "2023-09-18T13:08:28.226991116Z" diff --git a/infra/helm/libs/api-template/templates/_helpers.tpl b/infra/helm/libs/api-template/templates/_helpers.tpl index de39c681ba35..7e2591422b25 100644 --- a/infra/helm/libs/api-template/templates/_helpers.tpl +++ b/infra/helm/libs/api-template/templates/_helpers.tpl @@ -3,7 +3,11 @@ Expand the name of the chart. */}} {{- define "api-template.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- if .Values.name -}} + {{- .Values.name | trunc 63 | trimSuffix "-" -}} +{{- else -}} + {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} {{- end -}} {{/* @@ -13,14 +17,14 @@ If release name contains chart name it will be used as a full name. */}} {{- define "api-template.fullname" -}} {{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} + {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} {{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} + {{- $name := .Values.name | default .Chart.Name .Values.nameOverride -}} + {{- if contains $name .Release.Name -}} + {{- .Release.Name | trunc 63 | trimSuffix "-" -}} + {{- else -}} + {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} + {{- end -}} {{- end -}} {{- end -}} diff --git a/infra/helm/libs/api-template/templates/deployment.yaml b/infra/helm/libs/api-template/templates/deployment.yaml index 81812e13abdd..e1ff2f6676a3 100644 --- a/infra/helm/libs/api-template/templates/deployment.yaml +++ b/infra/helm/libs/api-template/templates/deployment.yaml @@ -55,6 +55,15 @@ spec: {{- toYaml .Values.annotations | nindent 8 }} {{- end }} spec: + {{- if .Values.armBetaEnrolled }} + tolerations: + - key: "arm" + operator: "Equal" + value: "true" + effect: "NoSchedule" + nodeSelector: + nodetype: "arm" + {{- end }} {{- with .Values.imagePullSecrets }} imagePullSecrets: {{- toYaml . | nindent 8 }} @@ -175,6 +184,8 @@ spec: fieldPath: metadata.labels['tags.datadoghq.com/version'] - name: DD_LOGS_INJECTION value: "true" + - name: DD_DBM_PROPAGATION_MODE + value: "full" - name: DD_AGENT_HOST valueFrom: fieldRef: @@ -183,6 +194,8 @@ spec: value: "true" - name: DD_DOGSTATSD_NON_LOCAL_TRAFFIC value: "true" + - name: DD_PROFILING_ENABLED + value: "false" - name: DD_APM_REPLACE_TAGS value: '[ { @@ -232,4 +245,4 @@ spec: claimName: {{.name}} {{- end }} {{- end }} - {{- end }} \ No newline at end of file + {{- end }} diff --git a/infra/helm/libs/api-template/templates/pdb.yaml b/infra/helm/libs/api-template/templates/pdb.yaml new file mode 100644 index 000000000000..3b3534fd8eb4 --- /dev/null +++ b/infra/helm/libs/api-template/templates/pdb.yaml @@ -0,0 +1,22 @@ +{{- $labels := include "api-template.labels" . -}} +{{- $namespace := $.Values.namespace -}} +{{- $serviceName := include "api-template.name" . -}} +{{- if .Values.enabled }} +{{- if .Values.podDisruptionBudget }} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ $serviceName }}-pdb + namespace: {{ $namespace }} +spec: +{{- if hasKey .Values.podDisruptionBudget "minAvailable" }} + minAvailable: {{ .Values.podDisruptionBudget.minAvailable }} +{{- end }} +{{- if hasKey .Values.podDisruptionBudget "maxUnavailable" }} + maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }} +{{- end }} + selector: + matchLabels: + app.kubernetes.io/name: {{ $serviceName }} +{{- end }} +{{- end }} diff --git a/infra/helm/libs/api-template/values.global.yaml b/infra/helm/libs/api-template/values.global.yaml new file mode 100644 index 000000000000..416197556582 --- /dev/null +++ b/infra/helm/libs/api-template/values.global.yaml @@ -0,0 +1,3 @@ +global: + image: + tag: debug diff --git a/infra/helm/libs/cronjob-template/templates/_helpers.tpl b/infra/helm/libs/cronjob-template/templates/_helpers.tpl index 924d9da8880e..00e92e0d4f65 100644 --- a/infra/helm/libs/cronjob-template/templates/_helpers.tpl +++ b/infra/helm/libs/cronjob-template/templates/_helpers.tpl @@ -3,8 +3,12 @@ Expand the name of the chart. */}} {{- define "cronjob-template.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} -{{- end }} +{{- if .Values.name -}} + {{- .Values.name | trunc 63 | trimSuffix "-" -}} +{{- else -}} + {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} {{/* Create a default fully qualified app name. @@ -12,23 +16,23 @@ We truncate at 63 chars because some Kubernetes name fields are limited to this If release name contains chart name it will be used as a full name. */}} {{- define "cronjob-template.fullname" -}} -{{- if .Values.fullnameOverride }} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- $name := default .Chart.Name .Values.nameOverride }} -{{- if contains $name .Release.Name }} -{{- .Release.Name | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} -{{- end }} -{{- end }} -{{- end }} +{{- if .Values.fullnameOverride -}} + {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} + {{- $name := .Values.name | default .Chart.Name .Values.nameOverride -}} + {{- if contains $name .Release.Name -}} + {{- .Release.Name | trunc 63 | trimSuffix "-" -}} + {{- else -}} + {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} + {{- end -}} +{{- end -}} +{{- end -}} {{/* Create chart name and version as used by the chart label. */}} {{- define "cronjob-template.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} {{- end }} {{/* diff --git a/infra/helm/libs/cronjob-template/templates/cronjob.yaml b/infra/helm/libs/cronjob-template/templates/cronjob.yaml index ef17498097e4..a3892dce80ce 100644 --- a/infra/helm/libs/cronjob-template/templates/cronjob.yaml +++ b/infra/helm/libs/cronjob-template/templates/cronjob.yaml @@ -18,8 +18,21 @@ spec: failedJobsHistoryLimit: {{ .Values.failedJobsHistoryLimit | default 1 }} successfulJobsHistoryLimit: {{ .Values.successfulJobsHistoryLimit | default 3}} schedule: {{ .Values.schedule | quote }} + {{- if .Values.startingDeadlineSeconds }} + startingDeadlineSeconds: {{ .Values.startingDeadlineSeconds }} + {{- end }} jobTemplate: spec: + {{- if .Values.armBetaEnrolled }} + tolerations: + - key: "arm" + operator: "Equal" + value: "true" + effect: "NoSchedule" + nodeSelector: + nodetype: "arm" + {{- end }} + ttlSecondsAfterFinished: {{ .Values.ttlSecondsAfterFinished | default 600 }} template: metadata: annotations: diff --git a/infra/helm/libs/cronjob-template/values.global.yaml b/infra/helm/libs/cronjob-template/values.global.yaml new file mode 100644 index 000000000000..416197556582 --- /dev/null +++ b/infra/helm/libs/cronjob-template/values.global.yaml @@ -0,0 +1,3 @@ +global: + image: + tag: debug diff --git a/infra/helm/libs/cronjob-template/values.yaml b/infra/helm/libs/cronjob-template/values.yaml index 4727071401ae..19c15349ef2f 100644 --- a/infra/helm/libs/cronjob-template/values.yaml +++ b/infra/helm/libs/cronjob-template/values.yaml @@ -9,6 +9,7 @@ serviceAccount: image: repository: defaultmissing schedule: '0 8 * * 1' +startingDeadlineSeconds: null resources: # We usually recommend not to specify default resources and to leave this as a conscious # choice for the user. This also increases chances charts run on environments with little diff --git a/infra/helm/libs/job-template/.helmignore b/infra/helm/libs/job-template/.helmignore new file mode 100644 index 000000000000..0e8a0eb36f4c --- /dev/null +++ b/infra/helm/libs/job-template/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/infra/helm/libs/job-template/Chart.yaml b/infra/helm/libs/job-template/Chart.yaml new file mode 100644 index 000000000000..bcd9e96aec26 --- /dev/null +++ b/infra/helm/libs/job-template/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: job-template +description: A Helm chart template for Kubernetes jobs +type: application +version: 0.0.1 +appVersion: 1.16.0 diff --git a/infra/helm/libs/job-template/templates/_helpers.tpl b/infra/helm/libs/job-template/templates/_helpers.tpl new file mode 100644 index 000000000000..d9b22069a578 --- /dev/null +++ b/infra/helm/libs/job-template/templates/_helpers.tpl @@ -0,0 +1,67 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "job-template.name" -}} +{{- if .Values.name -}} + {{- .Values.name | trunc 63 | trimSuffix "-" -}} +{{- else -}} + {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "job-template.fullname" -}} +{{- if .Values.fullnameOverride -}} + {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} + {{- $name := .Values.name | default .Chart.Name .Values.nameOverride -}} + {{- if contains $name .Release.Name -}} + {{- .Release.Name | trunc 63 | trimSuffix "-" -}} + {{- else -}} + {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "job-template.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "job-template.labels" -}} +helm.sh/chart: {{ include "job-template.chart" . }} +{{ include "job-template.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "job-template.selectorLabels" -}} +app.kubernetes.io/name: {{ include "job-template.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "job-template.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "job-template.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/infra/helm/libs/job-template/templates/job.yaml b/infra/helm/libs/job-template/templates/job.yaml new file mode 100644 index 000000000000..303f3e362798 --- /dev/null +++ b/infra/helm/libs/job-template/templates/job.yaml @@ -0,0 +1,133 @@ +{{- if .Values.enabled }} +{{- $fullName := include "job-template.fullname" . -}} +apiVersion: batch/v1 +kind: Job +metadata: + name: "{{ .Chart.Name }}-{{ now | unixEpoch }}" + {{- if $.Values.namespace }} + namespace: {{ $.Values.namespace }} + {{- end }} + labels: + {{- include "job-template.labels" . | nindent 4 }} + chart: "{{ $.Chart.Name }}-{{ $.Chart.Version | replace "+" "_" }}" + tags.datadoghq.com/env: {{ .Values.global.env.name }} + tags.datadoghq.com/service: {{ include "job-template.name" . }} + tags.datadoghq.com/version: {{ .Values.image.tag | default .Values.global.image.tag }} +spec: + ttlSecondsAfterFinished: {{ .Values.ttlSecondsAfterFinished | default 600 }} + template: + metadata: + annotations: + linkerd.io/inject: disabled + ad.datadoghq.com/{{ .Chart.Name }}.logs: >- + [{ + "log_processing_rules": [{ + "type": "mask_sequences", + "name": "mask_national_ids", + "replace_placeholder": "--MASKED--", + "pattern" : "\\b(?:[89]\\d{3}|(?:[012]\\d|3[01])(?:0\\d|1[012]))\\d\\d-?\\d{4}\\b" + }] + }] + labels: + {{- include "job-template.labels" . | nindent 8 }} + chart: "{{ $.Chart.Name }}-{{ $.Chart.Version | replace "+" "_" }}" + tags.datadoghq.com/env: {{ .Values.global.env.name }} + tags.datadoghq.com/service: {{ include "job-template.name" . }} + tags.datadoghq.com/version: {{ .Values.image.tag | default .Values.global.image.tag }} + spec: + {{- if .Values.armBetaEnrolled }} + tolerations: + - key: "arm" + operator: "Equal" + value: "true" + effect: "NoSchedule" + nodeSelector: + nodetype: "arm" + {{- end }} + serviceAccountName: {{ include "job-template.serviceAccountName" $ }} + securityContext: + {{- toYaml $.Values.podSecurityContext | nindent 12 }} + containers: + - image: "{{ $.Values.image.repository }}:{{ $.Values.image.tag | default .Values.global.image.tag }}" + name: {{ .Chart.Name }} + securityContext: + {{- toYaml $.Values.securityContext | nindent 14 }} + volumeMounts: + {{- if not (empty .Values.pvcs) }} + {{- range .Values.pvcs}} + - mountPath: {{ .mountPath }} + name: {{ .name }} + {{- end}} + {{- end }} + {{- if not (empty .Values.files) }} + - name: config-volume + mountPath: /etc/config + {{- end }} + env: + {{- range $key, $value := .Values.global.env }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + {{- range $key, $value := .Values.env }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + {{- range $key, $value := .Values.secrets }} + - name: {{ $key }} + valueFrom: + secretKeyRef: + name: {{ $fullName }} + key: {{ $key }} + {{- end }} + - name: APP_VERSION + value: {{ .Values.image.tag | default .Values.global.image.tag }} + - name: DD_ENV + valueFrom: + fieldRef: + fieldPath: metadata.labels['tags.datadoghq.com/env'] + - name: DD_SERVICE + valueFrom: + fieldRef: + fieldPath: metadata.labels['tags.datadoghq.com/service'] + - name: DD_VERSION + valueFrom: + fieldRef: + fieldPath: metadata.labels['tags.datadoghq.com/version'] + - name: DD_LOGS_INJECTION + value: "true" + - name: DD_AGENT_HOST + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: DD_APM_REPLACE_TAGS + value: '[ + { + "name": "*", + "pattern": "\\b(?:[89]\\d{3}|(?:[012]\\d|3[01])(?:0\\d|1[012]))\\d\\d-?\\d{4}\\b", + "repl": "--MASKED--" + }]' + {{- with .Values.command }} + command: {{ toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.args }} + args: {{ toYaml . | nindent 12 }} + {{- end }} + resources: + {{ toYaml $.Values.resources | nindent 14 }} + {{- if or (not (empty .Values.files)) (not (empty .Values.pvcs)) }} + volumes: + {{- end }} + {{- if not (empty .Values.files) }} + - name: config-volume + configMap: + name: {{ include "job-template.name" . }} + {{- end }} + {{- if not (empty .Values.pvcs) }} + {{- range .Values.pvcs }} + - name: {{ .name }} + persistentVolumeClaim: + claimName: {{ .name }} + {{- end }} + {{- end }} + restartPolicy: {{ .Values.restartPolicy | default "Never" }} +{{- end }} diff --git a/infra/helm/libs/job-template/templates/pvc.yaml b/infra/helm/libs/job-template/templates/pvc.yaml new file mode 100644 index 000000000000..0bf55327ec12 --- /dev/null +++ b/infra/helm/libs/job-template/templates/pvc.yaml @@ -0,0 +1,22 @@ +{{- $labels := include "job-template.labels" . -}} +{{- $namespace := $.Values.namespace -}} +{{- if .Values.enabled }} +{{- range .Values.pvcs }} +{{- if not .useExisting }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + labels: + {{- $labels | nindent 4 }} + name: {{ .name }} + namespace: {{ $namespace }} +spec: + accessModes: + - {{ .accessModes }} + resources: + requests: + storage: {{ .size }} + storageClassName: {{ .storageClass }} +{{- end }} +{{- end }} +{{- end }} diff --git a/infra/helm/libs/job-template/templates/secrets.yaml b/infra/helm/libs/job-template/templates/secrets.yaml new file mode 100644 index 000000000000..7436eaa53a19 --- /dev/null +++ b/infra/helm/libs/job-template/templates/secrets.yaml @@ -0,0 +1,18 @@ +{{- if .Values.enabled }} +{{- if not (empty .Values.secrets) }} +apiVersion: kubernetes-client.io/v1 +kind: ExternalSecret +metadata: + {{- if .Values.namespace }} + namespace: {{ .Values.namespace }} + {{- end }} + name: {{ include "cronjob-template.fullname" . }} +spec: + backendType: systemManager + data: + {{- range $key, $value := .Values.secrets }} + - name: {{ $key }} + key: {{ $value | quote }} + {{- end }} +{{- end }} +{{- end }} diff --git a/infra/helm/libs/job-template/templates/serviceaccount.yaml b/infra/helm/libs/job-template/templates/serviceaccount.yaml new file mode 100644 index 000000000000..80bb679cd044 --- /dev/null +++ b/infra/helm/libs/job-template/templates/serviceaccount.yaml @@ -0,0 +1,17 @@ +{{- if .Values.enabled }} +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "job-template.serviceAccountName" . }} + {{- if .Values.namespace }} + namespace: {{ .Values.namespace }} + {{- end }} + labels: + {{- include "cronjob-template.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end -}} +{{- end }} diff --git a/infra/helm/libs/job-template/values.global.yaml b/infra/helm/libs/job-template/values.global.yaml new file mode 100644 index 000000000000..416197556582 --- /dev/null +++ b/infra/helm/libs/job-template/values.global.yaml @@ -0,0 +1,3 @@ +global: + image: + tag: debug diff --git a/infra/helm/libs/job-template/values.yaml b/infra/helm/libs/job-template/values.yaml new file mode 100644 index 000000000000..4fbfb611fb19 --- /dev/null +++ b/infra/helm/libs/job-template/values.yaml @@ -0,0 +1,33 @@ +global: + env: + name: dev + image: + tag: latest +enabled: false +serviceAccount: + create: true +image: + repository: defaultmissing +resources: + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + limits: + cpu: 400m ## Research + memory: 256Mi + requests: + cpu: 100m + memory: 128Mi +podSecurityContext: + {} + # fsGroup: 2000 + +securityContext: + {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: runner diff --git a/infra/package.json b/infra/package.json index 9dccecbd85d4..37b440fdd44c 100644 --- a/infra/package.json +++ b/infra/package.json @@ -7,8 +7,7 @@ "charts": "node -r esbuild-register src/cli/generate-chart-values.ts", "update": "yarn update:packagejson", "update:packagejson": "node -r esbuild-register scripts/update-package-json.ts", - "cli": "echo 'Please use `yarn infra` instead of `yarn cli`' && exit 1", - "infra": "node -r esbuild-register src/cli/cli.ts" + "cli": "node -r esbuild-register src/cli/cli.ts" }, "license": "MIT", "devDependencies": { diff --git a/infra/src/cli/cli.ts b/infra/src/cli/cli.ts index 5b4d08560f0e..b806dcecdd97 100644 --- a/infra/src/cli/cli.ts +++ b/infra/src/cli/cli.ts @@ -8,7 +8,7 @@ import { renderServiceEnvVars } from './render-env-vars' import { renderLocalServices, runLocalServices } from './render-local-mocks' const cli = yargs(process.argv.slice(2)) - .scriptName('yarn infra') + .scriptName('yarn cli') .command( 'render-env', 'Render a chart for environment', diff --git a/infra/src/uber-charts/judicial-system.ts b/infra/src/uber-charts/judicial-system.ts index 10891616030e..7a65b51c4eeb 100644 --- a/infra/src/uber-charts/judicial-system.ts +++ b/infra/src/uber-charts/judicial-system.ts @@ -4,6 +4,7 @@ import { serviceSetup as jsWebSetup } from '../../../apps/judicial-system/web/in import { serviceSetup as jsXrdApiSetup } from '../../../apps/judicial-system/xrd-api/infra/judicial-system-xrd-api' import { serviceSetup as jsSchedulerSetup } from '../../../apps/judicial-system/scheduler/infra/judicial-system-scheduler' import { serviceSetup as jsMessageHandlerSetup } from '../../../apps/judicial-system/message-handler/infra/judicial-system-message-handler' +import { serviceSetup as jsRobotApiSetup } from '../../../apps/judicial-system/robot-api/infra/judicial-system-xrd-robot-api' import { serviceSetup as jsDigitalInboxRobotApiSetup } from '../../../apps/judicial-system/digital-mailbox-api/infra/judicial-system-xrd-robot-api' import { EnvironmentServices } from '.././dsl/types/charts' @@ -14,6 +15,7 @@ const jsWeb = jsWebSetup({ api: jsApi }) const jsXrdApi = jsXrdApiSetup({ backend: jsBack }) const jsScheduler = jsSchedulerSetup({ backend: jsBack }) const jsMessageHandler = jsMessageHandlerSetup({ backend: jsBack }) +const jsRobotApi = jsRobotApiSetup({ backend: jsBack }) const jsDigitalInboxRobotApi = jsDigitalInboxRobotApiSetup({ backend: jsBack }) export const Services: EnvironmentServices = { @@ -24,6 +26,7 @@ export const Services: EnvironmentServices = { jsXrdApi, jsScheduler, jsMessageHandler, + jsRobotApi, jsDigitalInboxRobotApi, ], staging: [ @@ -33,6 +36,7 @@ export const Services: EnvironmentServices = { jsXrdApi, jsScheduler, jsMessageHandler, + jsRobotApi, jsDigitalInboxRobotApi, ], dev: [ @@ -42,6 +46,7 @@ export const Services: EnvironmentServices = { jsXrdApi, jsScheduler, jsMessageHandler, + jsRobotApi, jsDigitalInboxRobotApi, ], } diff --git a/libs/api/domains/email-signup/src/lib/services/campaignMonitor/campaignMonitor.service.ts b/libs/api/domains/email-signup/src/lib/services/campaignMonitor/campaignMonitor.service.ts index 092c58a389e7..2a7ef5e87f42 100644 --- a/libs/api/domains/email-signup/src/lib/services/campaignMonitor/campaignMonitor.service.ts +++ b/libs/api/domains/email-signup/src/lib/services/campaignMonitor/campaignMonitor.service.ts @@ -35,12 +35,7 @@ export class CampaignMonitorSignupService { const obj = Object.fromEntries(map) return axios - .post(url, obj, { - headers: { - Authorization: authHeader, - 'Content-Type': 'application/json', - }, - }) + .post(url, obj, { headers: { Authorization: authHeader } }) .then((response) => { return { subscribed: response?.data?.result === 'error' ? false : true, diff --git a/libs/api/domains/health-directorate/src/lib/health-directorate.module.ts b/libs/api/domains/health-directorate/src/lib/health-directorate.module.ts index a4386cc36008..23a06c73e96c 100644 --- a/libs/api/domains/health-directorate/src/lib/health-directorate.module.ts +++ b/libs/api/domains/health-directorate/src/lib/health-directorate.module.ts @@ -2,10 +2,9 @@ import { HealthDirectorateClientModule } from '@island.is/clients/health-directo import { Module } from '@nestjs/common' import { HealthDirectorateResolver } from './health-directorate.resolver' import { HealthDirectorateService } from './health-directorate.service' -import { FeatureFlagModule } from '@island.is/nest/feature-flags' @Module({ - imports: [HealthDirectorateClientModule, FeatureFlagModule], + imports: [HealthDirectorateClientModule], providers: [HealthDirectorateResolver, HealthDirectorateService], }) export class HealthDirectorateModule {} diff --git a/libs/api/domains/health-directorate/src/lib/health-directorate.resolver.ts b/libs/api/domains/health-directorate/src/lib/health-directorate.resolver.ts index 234bbb70ca56..9961e0cd8328 100644 --- a/libs/api/domains/health-directorate/src/lib/health-directorate.resolver.ts +++ b/libs/api/domains/health-directorate/src/lib/health-directorate.resolver.ts @@ -22,12 +22,9 @@ import { DonorInput, Organ, OrganDonation } from './models/organ-donation.model' import type { Locale } from '@island.is/shared/types' import { Vaccinations } from './models/vaccinations.model' import { HealthDirectorateService } from './health-directorate.service' -import { - FeatureFlag, - FeatureFlagGuard, - Features, -} from '@island.is/nest/feature-flags' -@UseGuards(IdsUserGuard, ScopesGuard, FeatureFlagGuard) + +@UseGuards(IdsUserGuard, ScopesGuard) +@Scopes(ApiScope.internal) @Audit({ namespace: '@island.is/api/health-directorate' }) @Resolver(() => OrganDonation) export class HealthDirectorateResolver { @@ -38,8 +35,6 @@ export class HealthDirectorateResolver { name: 'healthDirectorateOrganDonation', }) @Audit() - @Scopes(ApiScope.healthOrganDonation) - @FeatureFlag(Features.servicePortalHealthOrganDonationPageEnabled) async getDonorStatus( @Args('locale', { type: () => String, nullable: true }) locale: Locale = 'is', @@ -48,7 +43,6 @@ export class HealthDirectorateResolver { const data = await this.api.getDonorStatus(user, locale) return { donor: data, locale: locale } } - @ResolveField('organList', () => [Organ], { nullable: true, }) @@ -64,8 +58,6 @@ export class HealthDirectorateResolver { name: 'healthDirectorateOrganDonationUpdateDonorStatus', }) @Audit() - @Scopes(ApiScope.healthOrganDonation) - @FeatureFlag(Features.servicePortalHealthOrganDonationPageEnabled) async updateDonorStatus( @Args('input') input: DonorInput, @Args('locale', { type: () => String, nullable: true }) @@ -80,8 +72,6 @@ export class HealthDirectorateResolver { name: 'healthDirectorateVaccinations', }) @Audit() - @Scopes(ApiScope.healthVaccinations) - @FeatureFlag(Features.servicePortalHealthVaccinationsPageEnabled) getVaccinations( @Args('locale', { type: () => String, nullable: true }) locale: Locale = 'is', diff --git a/libs/api/domains/law-and-order/src/lib/law-and-order.service.ts b/libs/api/domains/law-and-order/src/lib/law-and-order.service.ts index d773090945c3..ca8f4038a1e0 100644 --- a/libs/api/domains/law-and-order/src/lib/law-and-order.service.ts +++ b/libs/api/domains/law-and-order/src/lib/law-and-order.service.ts @@ -162,8 +162,6 @@ export class LawAndOrderService { (alert) => alert.type === AlertMessageTypeEnum.Success, )?.message, description: subpoenaData.subtitle, - information: subpoenaData.subpoenaInfoText, - deadline: subpoenaData.subpoenaNotificationDeadline, }, } return data diff --git a/libs/api/domains/law-and-order/src/models/subpoena.model.ts b/libs/api/domains/law-and-order/src/models/subpoena.model.ts index d54a1af82517..c71f0f365388 100644 --- a/libs/api/domains/law-and-order/src/models/subpoena.model.ts +++ b/libs/api/domains/law-and-order/src/models/subpoena.model.ts @@ -6,16 +6,16 @@ import { Group } from './group.model' @ObjectType('LawAndOrderSubpoenaTexts') export class Text { @Field({ nullable: true }) - confirmation?: string + intro?: string @Field({ nullable: true }) - description?: string + confirmation?: string @Field({ nullable: true }) - information?: string + description?: string @Field({ nullable: true }) - deadline?: string + claim?: string } @ObjectType('LawAndOrderSubpoenaData') diff --git a/libs/api/domains/transport-authority/src/lib/graphql/dto/index.ts b/libs/api/domains/transport-authority/src/lib/graphql/dto/index.ts index 88d82dba0190..4ae849d54bcc 100644 --- a/libs/api/domains/transport-authority/src/lib/graphql/dto/index.ts +++ b/libs/api/domains/transport-authority/src/lib/graphql/dto/index.ts @@ -2,4 +2,3 @@ export * from './ownerChangeAnswers.input' export * from './operatorChangeAnswers.input' export * from './checkTachoNet.input' export * from './plateAvailability.input' -export * from './plateOrderAnswers.input' diff --git a/libs/api/domains/transport-authority/src/lib/graphql/dto/plateOrderAnswers.input.ts b/libs/api/domains/transport-authority/src/lib/graphql/dto/plateOrderAnswers.input.ts deleted file mode 100644 index b79fcc3117d0..000000000000 --- a/libs/api/domains/transport-authority/src/lib/graphql/dto/plateOrderAnswers.input.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Field, InputType } from '@nestjs/graphql' - -@InputType() -export class PlateOrderAnswersPickVehicle { - @Field(() => String, { nullable: false }) - plate!: string -} - -@InputType() -export class PlateOrderAnswersPlateSize { - @Field(() => [String], { nullable: true }) - frontPlateSize?: string[] - - @Field(() => [String], { nullable: true }) - rearPlateSize?: string[] -} - -@InputType() -export class OperatorChangeAnswersPlateDelivery { - @Field(() => String, { nullable: true }) - deliveryMethodIsDeliveryStation?: string - - @Field(() => String, { nullable: true }) - deliveryStationTypeCode?: string - - @Field(() => [String], { nullable: true }) - includeRushFee?: string[] -} - -@InputType() -export class PlateOrderAnswers { - @Field(() => PlateOrderAnswersPickVehicle, { nullable: false }) - pickVehicle!: PlateOrderAnswersPickVehicle - - @Field(() => PlateOrderAnswersPlateSize, { nullable: false }) - plateSize!: PlateOrderAnswersPlateSize - - @Field(() => OperatorChangeAnswersPlateDelivery, { nullable: false }) - plateDelivery!: OperatorChangeAnswersPlateDelivery -} diff --git a/libs/api/domains/transport-authority/src/lib/graphql/main.resolver.ts b/libs/api/domains/transport-authority/src/lib/graphql/main.resolver.ts index f1a4945921b7..e06ff583d9da 100644 --- a/libs/api/domains/transport-authority/src/lib/graphql/main.resolver.ts +++ b/libs/api/domains/transport-authority/src/lib/graphql/main.resolver.ts @@ -15,7 +15,6 @@ import { CheckTachoNetInput, OperatorChangeAnswers, PlateAvailabilityInput, - PlateOrderAnswers, } from './dto' import { CheckTachoNetExists, @@ -26,7 +25,6 @@ import { VehiclePlateOrderChecksByPermno, MyPlateOwnershipChecksByRegno, PlateAvailability, - PlateOrderValidation, } from './models' import { CoOwnerChangeAnswers } from './dto/coOwnerChangeAnswers.input' @@ -125,18 +123,6 @@ export class MainResolver { ) } - @Scopes(ApiScope.samgongustofaVehicles) - @Query(() => PlateOrderValidation, { nullable: true }) - vehiclePlateOrderValidation( - @CurrentUser() user: User, - @Args('answers') answers: PlateOrderAnswers, - ) { - return this.transportAuthorityApi.validateApplicationForPlateOrder( - user, - answers, - ) - } - @Scopes(ApiScope.internal) @Query(() => MyPlateOwnershipChecksByRegno, { name: 'myPlateOwnershipChecksByRegno', diff --git a/libs/api/domains/transport-authority/src/lib/graphql/models/index.ts b/libs/api/domains/transport-authority/src/lib/graphql/models/index.ts index e9cbbe331e7a..89277ee12a7f 100644 --- a/libs/api/domains/transport-authority/src/lib/graphql/models/index.ts +++ b/libs/api/domains/transport-authority/src/lib/graphql/models/index.ts @@ -3,4 +3,3 @@ export * from './operatorChangeValidation.model' export * from './checkTachoNetExists.model' export * from './getCurrentVehicles.model' export * from './plateAvailability.model' -export * from './plateOrderValidation.model' diff --git a/libs/api/domains/transport-authority/src/lib/graphql/models/plateOrderValidation.model.ts b/libs/api/domains/transport-authority/src/lib/graphql/models/plateOrderValidation.model.ts deleted file mode 100644 index 4bf0b7cf3f0b..000000000000 --- a/libs/api/domains/transport-authority/src/lib/graphql/models/plateOrderValidation.model.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Field, ObjectType } from '@nestjs/graphql' - -@ObjectType() -export class PlateOrderValidationMessage { - @Field(() => String, { nullable: true }) - errorNo?: string | null - - @Field(() => String, { nullable: true }) - defaultMessage?: string | null -} - -@ObjectType() -export class PlateOrderValidation { - @Field(() => Boolean) - hasError!: boolean - - @Field(() => [PlateOrderValidationMessage], { nullable: true }) - errorMessages?: PlateOrderValidationMessage[] | null -} diff --git a/libs/api/domains/transport-authority/src/lib/transportAuthority.service.ts b/libs/api/domains/transport-authority/src/lib/transportAuthority.service.ts index 19c9e5fe9de0..f2fee3b09a5f 100644 --- a/libs/api/domains/transport-authority/src/lib/transportAuthority.service.ts +++ b/libs/api/domains/transport-authority/src/lib/transportAuthority.service.ts @@ -3,11 +3,7 @@ import { Auth, AuthMiddleware, User } from '@island.is/auth-nest-tools' import { VehicleOwnerChangeClient } from '@island.is/clients/transport-authority/vehicle-owner-change' import { DigitalTachographDriversCardClient } from '@island.is/clients/transport-authority/digital-tachograph-drivers-card' import { VehicleOperatorsClient } from '@island.is/clients/transport-authority/vehicle-operators' -import { - SGS_DELIVERY_STATION_CODE, - SGS_DELIVERY_STATION_TYPE, - VehiclePlateOrderingClient, -} from '@island.is/clients/transport-authority/vehicle-plate-ordering' +import { VehiclePlateOrderingClient } from '@island.is/clients/transport-authority/vehicle-plate-ordering' import { VehiclePlateRenewalClient } from '@island.is/clients/transport-authority/vehicle-plate-renewal' import { VehicleServiceFjsV1Client } from '@island.is/clients/vehicle-service-fjs-v1' import { @@ -18,7 +14,6 @@ import { OwnerChangeAnswers, OperatorChangeAnswers, CheckTachoNetInput, - PlateOrderAnswers, } from './graphql/dto' import { OwnerChangeValidation, @@ -27,7 +22,6 @@ import { VehicleOwnerchangeChecksByPermno, VehicleOperatorChangeChecksByPermno, VehiclePlateOrderChecksByPermno, - PlateOrderValidation, } from './graphql/models' import { ApolloError } from 'apollo-server-express' import { CoOwnerChangeAnswers } from './graphql/dto/coOwnerChangeAnswers.input' @@ -115,7 +109,6 @@ export class TransportAuthorityApi { return { basicVehicleInformation: { permno: vehicle.permno, - // Note: subModel (vehcom+speccom) has already been added to this field make: vehicle.make, color: vehicle.colorName, requireMileage: vehicle.requiresMileageRegistration, @@ -141,6 +134,12 @@ export class TransportAuthorityApi { // the current timestamp const todayStr = new Date().toISOString() + // No need to continue with this validation in user is neither seller nor buyer + // (only time application data changes is on state change from these roles) + if (user.nationalId !== sellerSsn && user.nationalId !== buyerSsn) { + return null + } + const filteredBuyerCoOwnerAndOperator = answers?.buyerCoOwnerAndOperator?.filter( ({ wasRemoved }) => wasRemoved !== 'true', @@ -280,7 +279,6 @@ export class TransportAuthorityApi { : null, basicVehicleInformation: { color: vehicle.colorName, - // Note: subModel (vehcom+speccom) has already been added to this field make: vehicle.make, permno: vehicle.permno, requireMileage: vehicle.requiresMileageRegistration, @@ -293,6 +291,13 @@ export class TransportAuthorityApi { user: User, answers: OperatorChangeAnswers, ): Promise { + // No need to continue with this validation in user is not owner + // (only time application data changes is on state change from that role) + const ownerSsn = answers?.owner?.nationalId + if (user.nationalId !== ownerSsn) { + return null + } + const permno = answers?.pickVehicle?.plate const filteredOldOperators = answers?.oldOperators?.filter( @@ -342,13 +347,12 @@ export class TransportAuthorityApi { }) // Get validation - const validation = - await this.vehiclePlateOrderingClient.validateVehicleForPlateOrder( - auth, - permno, - vehicleInfo?.platetypefront || '', - vehicleInfo?.platetyperear || '', - ) + const validation = await this.vehiclePlateOrderingClient.validatePlateOrder( + auth, + permno, + vehicleInfo?.platetypefront || '', + vehicleInfo?.platetyperear || '', + ) return { validationErrorMessages: validation?.hasError @@ -356,57 +360,12 @@ export class TransportAuthorityApi { : null, basicVehicleInformation: { color: vehicleInfo.color, - make: `${vehicleInfo.make} ${this.getVehicleSubModel(vehicleInfo)}`, + make: `${vehicleInfo.make} ${vehicleInfo.vehcom}`, permno: vehicleInfo.permno, }, } } - private getVehicleSubModel(vehicle: BasicVehicleInformationDto) { - return [vehicle.vehcom, vehicle.speccom].filter(Boolean).join(' ') - } - - async validateApplicationForPlateOrder( - user: User, - answers: PlateOrderAnswers, - ): Promise { - const YES = 'yes' - - const includeRushFee = - answers?.plateDelivery?.includeRushFee?.includes(YES) || false - - // Check if used selected delivery method: Pick up at delivery station - const deliveryStationTypeCode = - answers?.plateDelivery?.deliveryStationTypeCode - let deliveryStationType: string - let deliveryStationCode: string - if ( - answers.plateDelivery?.deliveryMethodIsDeliveryStation === YES && - deliveryStationTypeCode - ) { - // Split up code+type (was merged when we fetched that data) - deliveryStationType = deliveryStationTypeCode.split('_')[0] - deliveryStationCode = deliveryStationTypeCode.split('_')[1] - } else { - // Otherwise we will default to option "Pick up at Samgöngustofa" - deliveryStationType = SGS_DELIVERY_STATION_TYPE - deliveryStationCode = SGS_DELIVERY_STATION_CODE - } - - const result = - await this.vehiclePlateOrderingClient.validateAllForPlateOrder( - user, - answers?.pickVehicle?.plate, - answers?.plateSize?.frontPlateSize?.[0] || '', - answers?.plateSize?.rearPlateSize?.[0] || '', - deliveryStationType, - deliveryStationCode, - includeRushFee, - ) - - return result - } - async getMyPlateOwnershipChecksByRegno( auth: User, regno: string, diff --git a/libs/api/domains/vehicles/src/lib/models/usersVehicles.model.ts b/libs/api/domains/vehicles/src/lib/models/usersVehicles.model.ts index 594e021951e8..04476ff794ae 100644 --- a/libs/api/domains/vehicles/src/lib/models/usersVehicles.model.ts +++ b/libs/api/domains/vehicles/src/lib/models/usersVehicles.model.ts @@ -232,7 +232,7 @@ export class VehicleListed { requiresMileageRegistration?: boolean | null @Field(() => Boolean, { nullable: true }) - canRegisterMileage?: boolean | null + canRegisterMilage?: boolean | null @Field({ nullable: true }) regTypeCode?: string diff --git a/libs/api/domains/vehicles/src/lib/resolvers/vehicles.resolver.ts b/libs/api/domains/vehicles/src/lib/resolvers/vehicles.resolver.ts index b77a647a8988..2a92af484ca2 100644 --- a/libs/api/domains/vehicles/src/lib/resolvers/vehicles.resolver.ts +++ b/libs/api/domains/vehicles/src/lib/resolvers/vehicles.resolver.ts @@ -82,12 +82,7 @@ export class VehiclesResolver { const res = await this.vehiclesService.getVehiclesListV2(user, input) const downloadServiceURL = `${this.downloadServiceConfig.baseUrl}/download/v1/vehicles/ownership/${user.nationalId}` return { - vehicleList: res.data?.map((vehicle) => { - return { - ...vehicle, - canRegisterMileage: vehicle.canRegisterMilage, - } - }), + vehicleList: res.data, downloadServiceURL, paging: { pageNumber: res.pageNumber, diff --git a/libs/application/core/src/lib/fieldBuilders.ts b/libs/application/core/src/lib/fieldBuilders.ts index 2f1a86f6a03c..af82c47fc7f8 100644 --- a/libs/application/core/src/lib/fieldBuilders.ts +++ b/libs/application/core/src/lib/fieldBuilders.ts @@ -118,8 +118,6 @@ export const buildDateField = ( const { maxDate, minDate, - minYear, - maxYear, excludeDates, placeholder, backgroundColor = 'blue', @@ -132,8 +130,6 @@ export const buildDateField = ( placeholder, maxDate, minDate, - minYear, - maxYear, excludeDates, type: FieldTypes.DATE, component: FieldComponents.DATE, diff --git a/libs/application/core/src/lib/messages.ts b/libs/application/core/src/lib/messages.ts index 3190a3542b1a..d39632da5fcf 100644 --- a/libs/application/core/src/lib/messages.ts +++ b/libs/application/core/src/lib/messages.ts @@ -41,11 +41,6 @@ export const coreMessages = defineMessages({ defaultMessage: 'Bæta við', description: 'Add button', }, - buttonCancel: { - id: 'application.system:button.cancel', - defaultMessage: 'Hætta við', - description: 'Cancel button', - }, cardButtonInProgress: { id: 'application.system:card.button.inProgress', defaultMessage: 'Opna umsókn', @@ -141,16 +136,6 @@ export const coreMessages = defineMessages({ defaultMessage: 'Ekki tókst að búa til umsókn af gerðinni: {type}', description: 'Failed to create application of type: {type}', }, - nationalId: { - id: 'application.system:nationalId', - defaultMessage: 'Kennitala', - description: 'National ID', - }, - name: { - id: 'application.system:name', - defaultMessage: 'Nafn', - description: 'Name', - }, applications: { id: 'application.system:applications', defaultMessage: 'Þínar umsóknir', @@ -754,11 +739,6 @@ export const coreErrorMessages = defineMessages({ defaultMessage: 'Þú uppfyllir ekki skilyrði fyrir umsókn um nafnskírteini', description: 'Requirements for id card application not met', }, - drivingLicenseBookActiveBookNotFound: { - id: 'application.system:core.fetch.data.drivingLicenseBookActiveBookNotFound', - defaultMessage: 'Ekki fannst virk ökunámsbók', - description: 'Did not find active student book', - }, }) export const coreDelegationsMessages = defineMessages({ diff --git a/libs/application/template-api-modules/src/lib/modules/shared/shared.service.spec.ts b/libs/application/template-api-modules/src/lib/modules/shared/shared.service.spec.ts deleted file mode 100644 index ddc63927d4d0..000000000000 --- a/libs/application/template-api-modules/src/lib/modules/shared/shared.service.spec.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { Test } from '@nestjs/testing' -import { SharedTemplateApiService } from './shared.service' -import { LOGGER_PROVIDER } from '@island.is/logging' -import type { Logger } from '@island.is/logging' -import { EmailService } from '@island.is/email-service' -import { SmsService } from '@island.is/nova-sms' -import { ApplicationService } from '@island.is/application/api/core' -import { PaymentService } from '@island.is/application/api/payment' -import { sharedModuleConfig } from './shared.config' -import { FormValue } from '@island.is/application/types' -import { - Application, - ApplicationStatus, - ActionCardMetaData, -} from '@island.is/application/types' -import { ApplicationTypes } from '@island.is/application/types' - -describe('SharedTemplateApiService', () => { - let service: SharedTemplateApiService - let smsService: jest.Mocked - let logger: jest.Mocked - - const mockActionCard: ActionCardMetaData = { - title: 'Sample Application', - description: 'This is a sample application description', - tag: { - label: 'In Review', - variant: 'blue', - }, - history: [ - { - date: new Date('2024-03-20'), - log: 'Application submitted', - }, - ], - } - - const mockApplication: Application = { - id: '12345-abcde', - state: 'submitted', - actionCard: mockActionCard, - applicant: 'user123', - assignees: ['reviewer1', 'reviewer2'], - applicantActors: ['user123'], - typeId: ApplicationTypes.EXAMPLE, - modified: new Date('2024-03-20T10:30:00'), - created: new Date('2024-03-19T15:45:00'), - answers: { - firstName: 'John', - lastName: 'Doe', - email: 'john.doe@example.com', - }, - externalData: {}, - name: 'John Doe Application', - institution: 'Example University', - progress: 75, - status: ApplicationStatus.IN_PROGRESS, - draftTotalSteps: 4, - draftFinishedSteps: 3, - } - - const mockConfig = { - templateApi: { - clientLocationOrigin: 'http://example.com', - jwtSecret: 'secret', - email: 'test@example.com', - }, - } - - beforeEach(async () => { - const module = await Test.createTestingModule({ - providers: [ - SharedTemplateApiService, - { - provide: LOGGER_PROVIDER, - useValue: { - warn: jest.fn(), - }, - }, - { - provide: EmailService, - useValue: {}, - }, - { - provide: SmsService, - useValue: { - sendSms: jest.fn(), - }, - }, - { - provide: sharedModuleConfig.KEY, - useValue: mockConfig, - }, - { - provide: ApplicationService, - useValue: {}, - }, - { - provide: PaymentService, - useValue: {}, - }, - ], - }).compile() - - service = module.get(SharedTemplateApiService) - smsService = module.get(SmsService) - logger = module.get(LOGGER_PROVIDER) - }) - - describe('sendSms', () => { - it('should successfully send an SMS with normalized phone number', async () => { - // Arrange - const mockSmsTemplateGenerator = jest.fn().mockReturnValue({ - phoneNumber: '+354 1234567', - message: 'Test message', - }) - - // Act - await service.sendSms(mockSmsTemplateGenerator, mockApplication) - - // Assert - expect(mockSmsTemplateGenerator).toHaveBeenCalledWith(mockApplication, { - clientLocationOrigin: mockConfig.templateApi.clientLocationOrigin, - }) - expect(smsService.sendSms).toHaveBeenCalledWith('1234567', 'Test message') - expect(logger.warn).toHaveBeenCalledTimes(2) - }) - - it('should normalize phone numbers with special characters', async () => { - // Arrange - const mockSmsTemplateGenerator = jest.fn().mockReturnValue({ - phoneNumber: '+354-123-4567', - message: 'Test message', - }) - - // Act - await service.sendSms(mockSmsTemplateGenerator, mockApplication) - - // Assert - expect(smsService.sendSms).toHaveBeenCalledWith('1234567', 'Test message') - expect(logger.warn).toHaveBeenCalledTimes(2) - }) - - it('should handle phone numbers longer than 7 digits', async () => { - // Arrange - const mockSmsTemplateGenerator = jest.fn().mockReturnValue({ - phoneNumber: '3541234567', - message: 'Test message', - }) - - // Act - await service.sendSms(mockSmsTemplateGenerator, mockApplication) - - // Assert - expect(smsService.sendSms).toHaveBeenCalledWith('1234567', 'Test message') - expect(logger.warn).toHaveBeenCalledTimes(1) - }) - }) -}) diff --git a/libs/application/template-api-modules/src/lib/modules/shared/shared.service.ts b/libs/application/template-api-modules/src/lib/modules/shared/shared.service.ts index 3f48ce61e62b..64fdabb796a1 100644 --- a/libs/application/template-api-modules/src/lib/modules/shared/shared.service.ts +++ b/libs/application/template-api-modules/src/lib/modules/shared/shared.service.ts @@ -58,12 +58,7 @@ export class SharedTemplateApiService { clientLocationOrigin, }) - const normalizedPhoneNumber = this.normalizePhoneNumber( - phoneNumber, - application.id, - ) - - return this.smsService.sendSms(normalizedPhoneNumber, message) + return this.smsService.sendSms(phoneNumber, message) } async assignApplicationThroughSms( @@ -80,26 +75,7 @@ export class SharedTemplateApiService { assignLink, ) - const normalizedPhoneNumber = this.normalizePhoneNumber( - phoneNumber, - application.id, - ) - - return this.smsService.sendSms(normalizedPhoneNumber, message) - } - - normalizePhoneNumber(phoneNumber: string, applicationId: string) { - if (phoneNumber.trim().length > 7) { - this.logger.warn( - `Recipient number for application ${applicationId} is longer than 7 characters, attempting to recover`, - ) - } - if (phoneNumber.match(/\D/g)) { - this.logger.warn( - `Recipient number for application ${applicationId} contains non-numeric characters, attempting to recover`, - ) - } - return phoneNumber.trim().replace(/\D/g, '').slice(-7) + return this.smsService.sendSms(phoneNumber, message) } async sendEmail( diff --git a/libs/application/template-api-modules/src/lib/modules/templates/accident-notification/accident-notification-v2.utils.ts b/libs/application/template-api-modules/src/lib/modules/templates/accident-notification/accident-notification-v2.utils.ts index 2b8228aa9375..6c5b3db62187 100644 --- a/libs/application/template-api-modules/src/lib/modules/templates/accident-notification/accident-notification-v2.utils.ts +++ b/libs/application/template-api-modules/src/lib/modules/templates/accident-notification/accident-notification-v2.utils.ts @@ -49,9 +49,9 @@ export const applicationToAccidentReport = ( const reportingForMap = { [WhoIsTheNotificationForEnum.ME]: MinarsidurAPIModelsAccidentReportsReporterDTOReportingForEnum.NUMBER_1, - [WhoIsTheNotificationForEnum.POWEROFATTORNEY]: - MinarsidurAPIModelsAccidentReportsReporterDTOReportingForEnum.NUMBER_2, [WhoIsTheNotificationForEnum.JURIDICALPERSON]: + MinarsidurAPIModelsAccidentReportsReporterDTOReportingForEnum.NUMBER_2, + [WhoIsTheNotificationForEnum.POWEROFATTORNEY]: MinarsidurAPIModelsAccidentReportsReporterDTOReportingForEnum.NUMBER_3, [WhoIsTheNotificationForEnum.CHILDINCUSTODY]: MinarsidurAPIModelsAccidentReportsReporterDTOReportingForEnum.NUMBER_4, @@ -371,14 +371,12 @@ const getEmployer = ( answers.applicant && utils.isRepresentativeOfCompanyOrInstitute(answers) ) { - const { companyName, companyNationalId } = answers.juridicalPerson - const { name, email, phoneNumber } = answers.applicant return { - companyName, - companyNationalId, - representativeName: name ?? '', - representativeEmail: email ?? '', - representativePhone: phoneNumber ?? '', + companyName: answers.juridicalPerson.companyNationalId, + companyNationalId: answers.juridicalPerson.companyName, + representativeName: answers.applicant.name ?? '', + representativeEmail: answers.applicant.email ?? '', + representativePhone: answers.applicant.phoneNumber ?? '', } } @@ -390,18 +388,12 @@ const getEmployer = ( return undefined } - const { - name: companyName = '', - nationalRegistrationId: companyNationalId = '', - } = companyInfo - const { name, email, phoneNumber } = representative - return { - companyName, - companyNationalId, - representativeName: name ?? '', - representativeEmail: email ?? '', - representativePhone: phoneNumber ?? '', + companyName: companyInfo.name ?? '', + companyNationalId: companyInfo.nationalRegistrationId ?? '', + representativeName: representative.name ?? '', + representativeEmail: representative.email ?? '', + representativePhone: representative.phoneNumber ?? '', } } diff --git a/libs/application/template-api-modules/src/lib/modules/templates/aosh/work-accident-notification/work-accident-notification.service.ts b/libs/application/template-api-modules/src/lib/modules/templates/aosh/work-accident-notification/work-accident-notification.service.ts index b078f12def46..54839ce19a07 100644 --- a/libs/application/template-api-modules/src/lib/modules/templates/aosh/work-accident-notification/work-accident-notification.service.ts +++ b/libs/application/template-api-modules/src/lib/modules/templates/aosh/work-accident-notification/work-accident-notification.service.ts @@ -12,8 +12,10 @@ import { } from '@island.is/clients/work-accident-ver' import { getDateAndTime, + getValueList, mapVictimData, } from './work-accident-notification.utils' +import { getValueViaPath } from '@island.is/application/core' import { TemplateApiError } from '@island.is/nest/problem' @Injectable() @@ -55,20 +57,20 @@ export class WorkAccidentNotificationTemplateService extends BaseTemplateApiServ const answers = application.answers as unknown as WorkAccidentNotification const payload = { accidentForCreationDto: { - companySSN: answers.basicInformation.nationalId, + companySSN: answers.companyInformation.nationalId, sizeOfEnterprise: parseInt( - answers.basicInformation.numberOfEmployees, + answers.companyInformation.numberOfEmployees, 10, ), nameOfBranchOrDepartment: answers.companyInformation.nameOfBranch ?? - answers.basicInformation.name, + answers.companyInformation.name, address: answers.companyInformation.addressOfBranch ?? - answers.basicInformation.address, + answers.companyInformation.address, postcode: answers.companyInformation.postnumberOfBranch?.slice(0, 3) ?? - answers.basicInformation.postnumber.slice(0, 3), + answers.companyInformation.postnumber.slice(0, 3), workplaceHealthAndSafety: answers.companyLaborProtection.workhealthAndSafetyOccupation?.map( (code: string) => { diff --git a/libs/application/template-api-modules/src/lib/modules/templates/driving-license-book-update-instructor/driving-license-book-update-instructor.service.ts b/libs/application/template-api-modules/src/lib/modules/templates/driving-license-book-update-instructor/driving-license-book-update-instructor.service.ts index b24b7fadd825..39075c44bea1 100644 --- a/libs/application/template-api-modules/src/lib/modules/templates/driving-license-book-update-instructor/driving-license-book-update-instructor.service.ts +++ b/libs/application/template-api-modules/src/lib/modules/templates/driving-license-book-update-instructor/driving-license-book-update-instructor.service.ts @@ -4,8 +4,6 @@ import { DrivingLicenseBookUpdateInstructorAnswers } from '@island.is/applicatio import { BaseTemplateApiService } from '../../base-template-api.service' import { ApplicationTypes } from '@island.is/application/types' import { DrivingLicenseBookClientApiFactory } from '@island.is/clients/driving-license-book' -import { TemplateApiError } from '@island.is/nest/problem' -import { coreErrorMessages } from '@island.is/application/core' @Injectable() export class DrivingLicenseBookUpdateInstructorService extends BaseTemplateApiService { @@ -23,13 +21,7 @@ export class DrivingLicenseBookUpdateInstructorService extends BaseTemplateApiSe ) if (!overview?.active) { - throw new TemplateApiError( - { - title: coreErrorMessages.drivingLicenseBookActiveBookNotFound, - summary: coreErrorMessages.drivingLicenseBookActiveBookNotFound, - }, - 400, - ) + throw new Error('Did not find active student book') } const teacherNationalId = overview.book?.teacherNationalId diff --git a/libs/application/template-api-modules/src/lib/modules/templates/transport-authority/change-co-owner-of-vehicle/change-co-owner-of-vehicle.service.ts b/libs/application/template-api-modules/src/lib/modules/templates/transport-authority/change-co-owner-of-vehicle/change-co-owner-of-vehicle.service.ts index fb0c2b9ec91e..9fc9f0c56b2d 100644 --- a/libs/application/template-api-modules/src/lib/modules/templates/transport-authority/change-co-owner-of-vehicle/change-co-owner-of-vehicle.service.ts +++ b/libs/application/template-api-modules/src/lib/modules/templates/transport-authority/change-co-owner-of-vehicle/change-co-owner-of-vehicle.service.ts @@ -101,7 +101,7 @@ export class ChangeCoOwnerOfVehicleService extends BaseTemplateApiService { ) } - // Case: count > 20 + // A. vehicleCount > 20 // Display search box, validate vehicle when permno is entered if (totalRecords > 20) { return { @@ -114,13 +114,13 @@ export class ChangeCoOwnerOfVehicleService extends BaseTemplateApiService { const vehicles = await Promise.all( resultData.map(async (vehicle) => { - // Case: 20 >= count > 5 + // B. 20 >= vehicleCount > 5 // Display dropdown, validate vehicle when selected in dropdown if (totalRecords > 5) { return this.mapVehicle(auth, vehicle, false) } - // Case: count <= 5 + // C. vehicleCount <= 5 // Display radio buttons, validate all vehicles now return this.mapVehicle(auth, vehicle, true) }), @@ -468,51 +468,34 @@ export class ChangeCoOwnerOfVehicleService extends BaseTemplateApiService { const mileage = answers?.vehicleMileage?.value - const submitResult = await this.vehicleOwnerChangeClient.saveOwnerChange( - auth, - { - permno: permno, - seller: { - ssn: ownerSsn, - email: ownerEmail, - }, - buyer: { - ssn: ownerSsn, - email: ownerEmail, - }, - dateOfPurchase: new Date(application.created), - dateOfPurchaseTimestamp: createdStr.substring(11, createdStr.length), - saleAmount: currentOwnerChange?.saleAmount, - mileage: mileage ? Number(mileage) || 0 : null, - insuranceCompanyCode: currentOwnerChange?.insuranceCompanyCode, - operators: currentOperators?.map((operator) => ({ - ssn: operator.ssn || '', - // Note: It should be ok that the email we send in is empty, since we dont get - // the email when fetching current operators, and according to them (SGS), they - // are not using the operator email in their API (not being saved in their DB) - email: null, - isMainOperator: operator.isMainOperator || false, - })), - coOwners: filteredCoOwners.map((x) => ({ - ssn: x.nationalId || '', - email: x.email || '', - })), + await this.vehicleOwnerChangeClient.saveOwnerChange(auth, { + permno: permno, + seller: { + ssn: ownerSsn, + email: ownerEmail, }, - ) - - if ( - submitResult.hasError && - submitResult.errorMessages && - submitResult.errorMessages.length > 0 - ) { - throw new TemplateApiError( - { - title: applicationCheck.validation.alertTitle, - summary: submitResult.errorMessages, - }, - 400, - ) - } + buyer: { + ssn: ownerSsn, + email: ownerEmail, + }, + dateOfPurchase: new Date(application.created), + dateOfPurchaseTimestamp: createdStr.substring(11, createdStr.length), + saleAmount: currentOwnerChange?.saleAmount, + mileage: mileage ? Number(mileage) || 0 : null, + insuranceCompanyCode: currentOwnerChange?.insuranceCompanyCode, + operators: currentOperators?.map((operator) => ({ + ssn: operator.ssn || '', + // Note: It should be ok that the email we send in is empty, since we dont get + // the email when fetching current operators, and according to them (SGS), they + // are not using the operator email in their API (not being saved in their DB) + email: null, + isMainOperator: operator.isMainOperator || false, + })), + coOwners: filteredCoOwners.map((x) => ({ + ssn: x.nationalId || '', + email: x.email || '', + })), + }) // 3. Notify everyone in the process that the application has successfully been submitted diff --git a/libs/application/template-api-modules/src/lib/modules/templates/transport-authority/change-operator-of-vehicle/change-operator-of-vehicle.service.ts b/libs/application/template-api-modules/src/lib/modules/templates/transport-authority/change-operator-of-vehicle/change-operator-of-vehicle.service.ts index 62e407eb1094..fd9d7a62ffb9 100644 --- a/libs/application/template-api-modules/src/lib/modules/templates/transport-authority/change-operator-of-vehicle/change-operator-of-vehicle.service.ts +++ b/libs/application/template-api-modules/src/lib/modules/templates/transport-authority/change-operator-of-vehicle/change-operator-of-vehicle.service.ts @@ -99,7 +99,7 @@ export class ChangeOperatorOfVehicleService extends BaseTemplateApiService { ) } - // Case: count > 20 + // A. vehicleCount > 20 // Display search box, validate vehicle when permno is entered if (totalRecords > 20) { return { @@ -112,13 +112,13 @@ export class ChangeOperatorOfVehicleService extends BaseTemplateApiService { const vehicles = await Promise.all( resultData.map(async (vehicle) => { - // Case: 20 >= count > 5 + // B. 20 >= vehicleCount > 5 // Display dropdown, validate vehicle when selected in dropdown if (totalRecords > 5) { return this.mapVehicle(auth, vehicle, false) } - // Case: count <= 5 + // C. vehicleCount <= 5 // Display radio buttons, validate all vehicles now return this.mapVehicle(auth, vehicle, true) }), @@ -439,27 +439,13 @@ export class ChangeOperatorOfVehicleService extends BaseTemplateApiService { const mileage = answers?.vehicleMileage?.value - const submitResult = await this.vehicleOperatorsClient.saveOperators( + await this.vehicleOperatorsClient.saveOperators( auth, permno, operators, mileage ? Number(mileage) || 0 : null, ) - if ( - submitResult.hasError && - submitResult.errorMessages && - submitResult.errorMessages.length > 0 - ) { - throw new TemplateApiError( - { - title: applicationCheck.validation.alertTitle, - summary: submitResult.errorMessages, - }, - 400, - ) - } - // 3. Notify everyone in the process that the application has successfully been submitted // 3a. Get list of users that need to be notified diff --git a/libs/application/template-api-modules/src/lib/modules/templates/transport-authority/license-plate-renewal/license-plate-renewal.service.ts b/libs/application/template-api-modules/src/lib/modules/templates/transport-authority/license-plate-renewal/license-plate-renewal.service.ts index 7b2781e29cf8..a53ab7eae0c5 100644 --- a/libs/application/template-api-modules/src/lib/modules/templates/transport-authority/license-plate-renewal/license-plate-renewal.service.ts +++ b/libs/application/template-api-modules/src/lib/modules/templates/transport-authority/license-plate-renewal/license-plate-renewal.service.ts @@ -3,10 +3,7 @@ import { SharedTemplateApiService } from '../../../shared' import { TemplateApiModuleActionProps } from '../../../../types' import { BaseTemplateApiService } from '../../../base-template-api.service' import { ApplicationTypes } from '@island.is/application/types' -import { - applicationCheck, - LicensePlateRenewalAnswers, -} from '@island.is/application/templates/transport-authority/license-plate-renewal' +import { LicensePlateRenewalAnswers } from '@island.is/application/templates/transport-authority/license-plate-renewal' import { PlateOwnership, PlateOwnershipValidation, @@ -15,7 +12,6 @@ import { import { TemplateApiError } from '@island.is/nest/problem' import { info } from 'kennitala' import { error } from '@island.is/application/templates/transport-authority/license-plate-renewal' -import { User } from '@island.is/auth-nest-tools' @Injectable() export class LicensePlateRenewalService extends BaseTemplateApiService { @@ -30,10 +26,8 @@ export class LicensePlateRenewalService extends BaseTemplateApiService { const result = await this.vehiclePlateRenewalClient.getMyPlateOwnerships( auth, ) - const totalRecords = (result && result.length) || 0 - // Validate that user has at least 1 plate ownership - if (!totalRecords) { + if (!result || !result.length) { throw new TemplateApiError( { title: error.plateOwnershipEmptyList, @@ -43,45 +37,54 @@ export class LicensePlateRenewalService extends BaseTemplateApiService { ) } - const plateOwnerships = await Promise.all( - result.map(async (plateOwnership) => { - // Case: count > 5 - // Display dropdown, validate plate ownership when selected in dropdown - if (totalRecords > 5) { - return this.mapPlateOwnership(auth, plateOwnership, false) + return await Promise.all( + result.map(async (item: PlateOwnership) => { + let validation: PlateOwnershipValidation | undefined + + // Only validate if fewer than 5 items + if (result.length <= 5) { + validation = + await this.vehiclePlateRenewalClient.validatePlateOwnership( + auth, + item.regno, + ) } - // Case: count <= 5 - // Display radio buttons, validate all plate ownerships now - return this.mapPlateOwnership(auth, plateOwnership, true) + return { + regno: item.regno, + startDate: item.startDate, + endDate: item.endDate, + validationErrorMessages: validation?.hasError + ? validation.errorMessages + : null, + } }), ) - - return plateOwnerships } - private async mapPlateOwnership( - auth: User, - plateOwnership: PlateOwnership, - fetchExtraData: boolean, - ) { - let validation: PlateOwnershipValidation | undefined + async validateApplication({ + application, + auth, + }: TemplateApiModuleActionProps) { + const answers = application.answers as LicensePlateRenewalAnswers + const regno = answers?.pickPlate?.regno - if (fetchExtraData) { - // Get plate renewal validation - validation = await this.vehiclePlateRenewalClient.validatePlateOwnership( - auth, - plateOwnership.regno, - ) - } + const result = await this.vehiclePlateRenewalClient.validatePlateOwnership( + auth, + regno, + ) - return { - regno: plateOwnership.regno, - startDate: plateOwnership.startDate, - endDate: plateOwnership.endDate, - validationErrorMessages: validation?.hasError - ? validation.errorMessages - : null, + // If we get any error messages, we will just throw an error with a default title + // We will fetch these error messages again through graphql in the template, to be able + // to translate the error message + if (result.hasError && result.errorMessages?.length) { + throw new TemplateApiError( + { + title: error.validationAlertTitle, + summary: error.validationAlertTitle, + }, + 400, + ) } } @@ -105,22 +108,7 @@ export class LicensePlateRenewalService extends BaseTemplateApiService { const regno = answers?.pickPlate?.regno // Submit the application - const submitResult = - await this.vehiclePlateRenewalClient.renewPlateOwnership(auth, regno) - - if ( - submitResult.hasError && - submitResult.errorMessages && - submitResult.errorMessages.length > 0 - ) { - throw new TemplateApiError( - { - title: applicationCheck.validation.alertTitle, - summary: submitResult.errorMessages, - }, - 400, - ) - } + await this.vehiclePlateRenewalClient.renewPlateOwnership(auth, regno) } private async handlePayment({ diff --git a/libs/application/template-api-modules/src/lib/modules/templates/transport-authority/order-vehicle-license-plate/order-vehicle-license-plate.service.ts b/libs/application/template-api-modules/src/lib/modules/templates/transport-authority/order-vehicle-license-plate/order-vehicle-license-plate.service.ts index 261883a4385c..c7c1b851bfd0 100644 --- a/libs/application/template-api-modules/src/lib/modules/templates/transport-authority/order-vehicle-license-plate/order-vehicle-license-plate.service.ts +++ b/libs/application/template-api-modules/src/lib/modules/templates/transport-authority/order-vehicle-license-plate/order-vehicle-license-plate.service.ts @@ -3,10 +3,7 @@ import { SharedTemplateApiService } from '../../../shared' import { TemplateApiModuleActionProps } from '../../../../types' import { BaseTemplateApiService } from '../../../base-template-api.service' import { ApplicationTypes } from '@island.is/application/types' -import { - applicationCheck, - OrderVehicleLicensePlateAnswers, -} from '@island.is/application/templates/transport-authority/order-vehicle-license-plate' +import { OrderVehicleLicensePlateAnswers } from '@island.is/application/templates/transport-authority/order-vehicle-license-plate' import { PlateOrderValidation, SGS_DELIVERY_STATION_CODE, @@ -14,12 +11,9 @@ import { VehiclePlateOrderingClient, } from '@island.is/clients/transport-authority/vehicle-plate-ordering' import { VehicleCodetablesClient } from '@island.is/clients/transport-authority/vehicle-codetables' -import { - CurrentVehiclesWithMilageAndNextInspDto, - VehicleSearchApi, -} from '@island.is/clients/vehicles' +import { VehicleSearchApi } from '@island.is/clients/vehicles' import { YES, coreErrorMessages } from '@island.is/application/core' -import { Auth, AuthMiddleware, User } from '@island.is/auth-nest-tools' +import { Auth, AuthMiddleware } from '@island.is/auth-nest-tools' import { TemplateApiError } from '@island.is/nest/problem' @Injectable() @@ -61,22 +55,33 @@ export class OrderVehicleLicensePlateService extends BaseTemplateApiService { async getCurrentVehiclesWithPlateOrderChecks({ auth, }: TemplateApiModuleActionProps) { - // Get max 20 vehicles and total count of vehicles - // Note: Should be enough to only get 20, because if totalRecords - // is higher than 20, then we won't return any vehicles - const result = await this.vehiclesApiWithAuth( - auth, - ).currentvehicleswithmileageandinspGet({ + const countResult = + ( + await this.vehiclesApiWithAuth( + auth, + ).currentvehicleswithmileageandinspGet({ + showOwned: true, + showCoowned: false, + showOperated: false, + page: 1, + pageSize: 1, + }) + ).totalRecords || 0 + if (countResult && countResult > 20) { + return { + totalRecords: countResult, + vehicles: [], + } + } + const result = await this.vehiclesApiWithAuth(auth).currentVehiclesGet({ + persidNo: auth.nationalId, showOwned: true, showCoowned: false, showOperated: false, - page: 1, - pageSize: 20, }) - const totalRecords = result.totalRecords || 0 // Validate that user has at least 1 vehicle - if (!totalRecords) { + if (!result || !result.length) { throw new TemplateApiError( { title: coreErrorMessages.vehiclesEmptyListOwner, @@ -86,73 +91,44 @@ export class OrderVehicleLicensePlateService extends BaseTemplateApiService { ) } - // Case: count > 20 - // Display search box, validate vehicle when permno is entered - if (totalRecords > 20) { - return { - totalRecords: totalRecords, - vehicles: [], - } - } - - const resultData = result.data || [] - - const vehicles = await Promise.all( - resultData.map(async (vehicle) => { - // Case: 20 >= count > 5 - // Display dropdown, validate vehicle when selected in dropdown - if (totalRecords > 5) { - return this.mapVehicle(auth, vehicle, false) - } - - // Case: count <= 5 - // Display radio buttons, validate all vehicles now - return this.mapVehicle(auth, vehicle, true) - }), - ) - - return { - totalRecords: totalRecords, - vehicles: vehicles, - } - } - - private async mapVehicle( - auth: User, - vehicle: CurrentVehiclesWithMilageAndNextInspDto, - fetchExtraData: boolean, - ) { - let validation: PlateOrderValidation | undefined - - if (fetchExtraData) { - // Get basic information about vehicle - const vehicleInfo = await this.vehiclesApiWithAuth( - auth, - ).basicVehicleInformationGet({ - clientPersidno: auth.nationalId, - permno: vehicle.permno || '', - regno: undefined, - vin: undefined, - }) - - // Get plate order validation - validation = - await this.vehiclePlateOrderingClient.validateVehicleForPlateOrder( - auth, - vehicle.permno || '', - vehicleInfo?.platetypefront || '', - vehicleInfo?.platetyperear || '', - ) - } - return { - permno: vehicle.permno || undefined, - make: vehicle.make || undefined, - color: vehicle.colorName || undefined, - role: vehicle.role || undefined, - validationErrorMessages: validation?.hasError - ? validation.errorMessages - : null, + totalRecords: countResult, + vehicles: await Promise.all( + result?.map(async (vehicle) => { + let validation: PlateOrderValidation | undefined + + // Only validate if fewer than 5 items + if (result.length <= 5) { + // Get basic information about vehicle + const vehicleInfo = await this.vehiclesApiWithAuth( + auth, + ).basicVehicleInformationGet({ + clientPersidno: auth.nationalId, + permno: vehicle.permno || '', + regno: undefined, + vin: undefined, + }) + // Get validation + validation = + await this.vehiclePlateOrderingClient.validatePlateOrder( + auth, + vehicle.permno || '', + vehicleInfo?.platetypefront || '', + vehicleInfo?.platetyperear || '', + ) + } + + return { + permno: vehicle.permno || undefined, + make: vehicle.make || undefined, + color: vehicle.color || undefined, + role: vehicle.role || undefined, + validationErrorMessages: validation?.hasError + ? validation.errorMessages + : null, + } + }), + ), } } @@ -160,58 +136,6 @@ export class OrderVehicleLicensePlateService extends BaseTemplateApiService { return await this.vehicleCodetablesClient.getPlateTypes() } - async validateApplication({ - application, - auth, - }: TemplateApiModuleActionProps) { - const answers = application.answers as OrderVehicleLicensePlateAnswers - - const includeRushFee = - answers?.plateDelivery?.includeRushFee?.includes(YES) || false - - // Check if used selected delivery method: Pick up at delivery station - const deliveryStationTypeCode = - answers?.plateDelivery?.deliveryStationTypeCode - let deliveryStationType: string - let deliveryStationCode: string - if ( - answers.plateDelivery?.deliveryMethodIsDeliveryStation === YES && - deliveryStationTypeCode - ) { - // Split up code+type (was merged when we fetched that data) - deliveryStationType = deliveryStationTypeCode.split('_')[0] - deliveryStationCode = deliveryStationTypeCode.split('_')[1] - } else { - // Otherwise we will default to option "Pick up at Samgöngustofa" - deliveryStationType = SGS_DELIVERY_STATION_TYPE - deliveryStationCode = SGS_DELIVERY_STATION_CODE - } - - const result = - await this.vehiclePlateOrderingClient.validateAllForPlateOrder( - auth, - answers?.pickVehicle?.plate, - answers?.plateSize?.frontPlateSize?.[0], - answers?.plateSize?.rearPlateSize?.[0], - deliveryStationType, - deliveryStationCode, - includeRushFee, - ) - - // If we get any error messages, we will just throw an error with a default title - // We will fetch these error messages again through graphql in the template, to be able - // to translate the error message - if (result.hasError && result.errorMessages?.length) { - throw new TemplateApiError( - { - title: applicationCheck.validation.alertTitle, - summary: applicationCheck.validation.alertTitle, - }, - 400, - ) - } - } - async submitApplication({ application, auth, @@ -257,30 +181,13 @@ export class OrderVehicleLicensePlateService extends BaseTemplateApiService { deliveryStationCode = SGS_DELIVERY_STATION_CODE } - const submitResult = await this.vehiclePlateOrderingClient.savePlateOrders( - auth, - { - permno: answers?.pickVehicle?.plate, - frontType: answers?.plateSize?.frontPlateSize?.[0], - rearType: answers?.plateSize?.rearPlateSize?.[0], - deliveryStationType: deliveryStationType, - deliveryStationCode: deliveryStationCode, - expressOrder: includeRushFee, - }, - ) - - if ( - submitResult.hasError && - submitResult.errorMessages && - submitResult.errorMessages.length > 0 - ) { - throw new TemplateApiError( - { - title: applicationCheck.validation.alertTitle, - summary: submitResult.errorMessages, - }, - 400, - ) - } + await this.vehiclePlateOrderingClient.savePlateOrders(auth, { + permno: answers?.pickVehicle?.plate, + frontType: answers?.plateSize?.frontPlateSize?.[0], + rearType: answers?.plateSize?.rearPlateSize?.[0], + deliveryStationType: deliveryStationType, + deliveryStationCode: deliveryStationCode, + expressOrder: includeRushFee, + }) } } diff --git a/libs/application/template-api-modules/src/lib/modules/templates/transport-authority/transfer-of-vehicle-ownership/transfer-of-vehicle-ownership.service.ts b/libs/application/template-api-modules/src/lib/modules/templates/transport-authority/transfer-of-vehicle-ownership/transfer-of-vehicle-ownership.service.ts index 2974f7dbdffd..7f31c0891d47 100644 --- a/libs/application/template-api-modules/src/lib/modules/templates/transport-authority/transfer-of-vehicle-ownership/transfer-of-vehicle-ownership.service.ts +++ b/libs/application/template-api-modules/src/lib/modules/templates/transport-authority/transfer-of-vehicle-ownership/transfer-of-vehicle-ownership.service.ts @@ -103,7 +103,7 @@ export class TransferOfVehicleOwnershipService extends BaseTemplateApiService { ) } - // Case: count > 20 + // A. vehicleCount > 20 // Display search box, validate vehicle when permno is entered if (totalRecords > 20) { return { @@ -116,13 +116,13 @@ export class TransferOfVehicleOwnershipService extends BaseTemplateApiService { const vehicles = await Promise.all( resultData.map(async (vehicle) => { - // Case: 20 >= count > 5 + // B. 20 >= vehicleCount > 5 // Display dropdown, validate vehicle when selected in dropdown if (totalRecords > 5) { return this.mapVehicle(auth, vehicle, false) } - // Case: count <= 5 + // C. vehicleCount <= 5 // Display radio buttons, validate all vehicles now return this.mapVehicle(auth, vehicle, true) }), @@ -333,7 +333,7 @@ export class TransferOfVehicleOwnershipService extends BaseTemplateApiService { async addReview({ application, auth, - }: TemplateApiModuleActionProps): Promise> { + }: TemplateApiModuleActionProps): Promise { const answers = application.answers as TransferOfVehicleOwnershipAnswers // 1. Make sure review comes from buyer, he is the only one that can add more reviewers @@ -342,7 +342,7 @@ export class TransferOfVehicleOwnershipService extends BaseTemplateApiService { !answers.buyer.nationalId || auth.nationalId !== answers.buyer.nationalId ) { - return [] + return } // 2. Notify users that were added that need to review @@ -365,11 +365,10 @@ export class TransferOfVehicleOwnershipService extends BaseTemplateApiService { ) if (buyerCoOwners) { for (let i = 0; i < buyerCoOwners.length; i++) { - const oldEntry = oldRecipientList.find( - (x) => - x.role === EmailRole.buyerCoOwner && - x.ssn === buyerCoOwners[i].nationalId, - ) + const oldEntry = oldRecipientList.find((x) => { + x.role === EmailRole.buyerCoOwner && + x.ssn === buyerCoOwners[i].nationalId + }) const emailChanged = oldEntry ? oldEntry.email !== buyerCoOwners[i].email : true @@ -394,11 +393,10 @@ export class TransferOfVehicleOwnershipService extends BaseTemplateApiService { ) if (buyerOperators) { for (let i = 0; i < buyerOperators.length; i++) { - const oldEntry = oldRecipientList.find( - (x) => - x.role === EmailRole.buyerOperator && - x.ssn === buyerOperators[i].nationalId, - ) + const oldEntry = oldRecipientList.find((x) => { + x.role === EmailRole.buyerOperator && + x.ssn === buyerOperators[i].nationalId + }) const emailChanged = oldEntry ? oldEntry.email !== buyerOperators[i].email : true @@ -455,8 +453,6 @@ export class TransferOfVehicleOwnershipService extends BaseTemplateApiService { }) } } - - return newlyAddedRecipientList } async rejectApplication(props: TemplateApiModuleActionProps): Promise { @@ -588,51 +584,34 @@ export class TransferOfVehicleOwnershipService extends BaseTemplateApiService { const mileage = answers?.vehicleMileage?.value - const submitResult = await this.vehicleOwnerChangeClient.saveOwnerChange( - auth, - { - permno: answers?.pickVehicle?.plate, - seller: { - ssn: answers?.seller?.nationalId, - email: answers?.seller?.email, - }, - buyer: { - ssn: answers?.buyer?.nationalId, - email: answers?.buyer?.email, - }, - dateOfPurchase: new Date(answers?.vehicle?.date), - dateOfPurchaseTimestamp: createdStr.substring(11, createdStr.length), - saleAmount: Number(answers?.vehicle?.salePrice || '0') || 0, - mileage: mileage ? Number(mileage) || 0 : null, - insuranceCompanyCode: answers?.insurance?.value, - coOwners: buyerCoOwners?.map((coOwner) => ({ - ssn: coOwner.nationalId || '', - email: coOwner.email || '', - })), - operators: buyerOperators?.map((operator) => ({ - ssn: operator.nationalId || '', - email: operator.email || '', - isMainOperator: - buyerOperators.length > 1 - ? operator.nationalId === answers.buyerMainOperator?.nationalId - : true, - })), + await this.vehicleOwnerChangeClient.saveOwnerChange(auth, { + permno: answers?.pickVehicle?.plate, + seller: { + ssn: answers?.seller?.nationalId, + email: answers?.seller?.email, }, - ) - - if ( - submitResult.hasError && - submitResult.errorMessages && - submitResult.errorMessages.length > 0 - ) { - throw new TemplateApiError( - { - title: applicationCheck.validation.alertTitle, - summary: submitResult.errorMessages, - }, - 400, - ) - } + buyer: { + ssn: answers?.buyer?.nationalId, + email: answers?.buyer?.email, + }, + dateOfPurchase: new Date(answers?.vehicle?.date), + dateOfPurchaseTimestamp: createdStr.substring(11, createdStr.length), + saleAmount: Number(answers?.vehicle?.salePrice || '0') || 0, + mileage: mileage ? Number(mileage) || 0 : null, + insuranceCompanyCode: answers?.insurance?.value, + coOwners: buyerCoOwners?.map((coOwner) => ({ + ssn: coOwner.nationalId || '', + email: coOwner.email || '', + })), + operators: buyerOperators?.map((operator) => ({ + ssn: operator.nationalId || '', + email: operator.email || '', + isMainOperator: + buyerOperators.length > 1 + ? operator.nationalId === answers.buyerMainOperator?.nationalId + : true, + })), + }) // 3. Notify everyone in the process that the application has successfully been submitted diff --git a/libs/application/templates/aosh/transfer-of-machine-ownership/src/lib/messages/payment.ts b/libs/application/templates/aosh/transfer-of-machine-ownership/src/lib/messages/payment.ts index 939de1252627..3573842f697a 100644 --- a/libs/application/templates/aosh/transfer-of-machine-ownership/src/lib/messages/payment.ts +++ b/libs/application/templates/aosh/transfer-of-machine-ownership/src/lib/messages/payment.ts @@ -14,6 +14,16 @@ export const payment = { }, }), paymentChargeOverview: defineMessages({ + forPayment: { + id: 'aosh.tmo.application:payment.paymentChargeOverview.forPayment', + defaultMessage: 'Til greiðslu', + description: 'For payment label', + }, + total: { + id: 'aosh.tmo.application:payment.paymentChargeOverview.total', + defaultMessage: 'Samtals', + description: 'Total amount label', + }, alertTitle: { id: 'aosh.tmo.application:payment.paymentChargeOverview.alertTitle', defaultMessage: 'Til athugunar!', diff --git a/libs/application/templates/aosh/work-accident-notification/project.json b/libs/application/templates/aosh/work-accident-notification/project.json index a2309411789d..ab889cfaf462 100644 --- a/libs/application/templates/aosh/work-accident-notification/project.json +++ b/libs/application/templates/aosh/work-accident-notification/project.json @@ -7,12 +7,6 @@ "targets": { "lint": { "executor": "@nx/eslint:lint" - }, - "extract-strings": { - "executor": "nx:run-commands", - "options": { - "command": "yarn ts-node -P libs/localization/tsconfig.lib.json libs/localization/scripts/extract 'libs/application/templates/aosh/work-accident-notification/src/**/*.{js,ts,tsx}'" - } } } } diff --git a/libs/application/templates/aosh/work-accident-notification/src/fields/AccidentLocation/index.tsx b/libs/application/templates/aosh/work-accident-notification/src/fields/AccidentLocation/index.tsx index 69426791673e..df31e4cfa436 100644 --- a/libs/application/templates/aosh/work-accident-notification/src/fields/AccidentLocation/index.tsx +++ b/libs/application/templates/aosh/work-accident-notification/src/fields/AccidentLocation/index.tsx @@ -42,15 +42,7 @@ export const AccidentLocation: FC> = ( ) const [minorGroupOptions, setMinorGroupOptions] = useState< WorkingEnvironmentDto[] - >( - selectedMajorGroup - ? minorGroups.filter( - (group) => - group.code?.substring(0, 2) === - selectedMajorGroup?.value?.substring(0, 2), - ) - : [], - ) + >([]) return ( @@ -64,7 +56,7 @@ export const AccidentLocation: FC> = ( label={formatMessage( accident.about.locationOfAccidentMajorGroup, )} - name="accident.accidentLocationParentGroup" + name="subMajorGroupSelect" options={majorGroups.map((option) => ({ label: option.name || '', value: option.code, @@ -88,7 +80,7 @@ export const AccidentLocation: FC> = ( /> ) }} - name={'accident.accidentLocationParentGroup'} + name={'subMajorGroup'} /> @@ -100,12 +92,11 @@ export const AccidentLocation: FC> = ( label={formatMessage( accident.about.locationOfAccidentMinorGroup, )} - name="accident.accidentLocation" + name="subMajorGroupSelect" options={minorGroupOptions.map((group) => ({ label: group.name || '', value: group.code, }))} - isDisabled={!selectedMajorGroup} value={selectedMinorGroup} backgroundColor="blue" onChange={(v) => { @@ -122,7 +113,7 @@ export const AccidentLocation: FC> = ( /> ) }} - name={'accident.accidentLocation'} + name={'subMajorGroup'} /> diff --git a/libs/application/templates/aosh/work-accident-notification/src/fields/EmployeeStartTimeError/index.tsx b/libs/application/templates/aosh/work-accident-notification/src/fields/EmployeeStartTimeError/index.tsx index 2d9bfe513c0e..03bcd17321be 100644 --- a/libs/application/templates/aosh/work-accident-notification/src/fields/EmployeeStartTimeError/index.tsx +++ b/libs/application/templates/aosh/work-accident-notification/src/fields/EmployeeStartTimeError/index.tsx @@ -5,8 +5,7 @@ import { useFormContext } from 'react-hook-form' import { employee } from '../../lib/messages' import { useLocale } from '@island.is/localization' import { WorkAccidentNotification } from '../../lib/dataSchema' -import { dateIsWithin36Hours, formatDate } from '../../utils' -import { getValueViaPath } from '@island.is/application/core' +import { dateIsWithin36Hours } from '../../utils' interface EmployeeStartTimeErrorProps { field: { @@ -24,10 +23,6 @@ export const EmployeeStartTimeError: FC< const { getValues } = useFormContext() const { formatMessage } = useLocale() const [inputError, setInputError] = useState(false) - const startDate = - getValueViaPath(application.answers, 'accident.date') ?? '' - const startTime = - getValueViaPath(application.answers, 'accident.time') ?? '' setBeforeSubmitCallback?.(async () => { const values = getValues('employee') @@ -53,9 +48,7 @@ export const EmployeeStartTimeError: FC< {inputError && ( - {`${formatMessage(employee.employee.errorMessage)}, ${formatDate( - startDate, - )} ${startTime.slice(0, 2)}:${startTime.slice(2, 4)}`} + {formatMessage(employee.employee.errorMessage)} )} diff --git a/libs/application/templates/aosh/work-accident-notification/src/forms/WorkAccidentNotificationForm/AccidentSection/about.ts b/libs/application/templates/aosh/work-accident-notification/src/forms/WorkAccidentNotificationForm/AccidentSection/about.ts index 7c583e68c242..96e389a19054 100644 --- a/libs/application/templates/aosh/work-accident-notification/src/forms/WorkAccidentNotificationForm/AccidentSection/about.ts +++ b/libs/application/templates/aosh/work-accident-notification/src/forms/WorkAccidentNotificationForm/AccidentSection/about.ts @@ -3,6 +3,7 @@ import { buildCustomField, buildDateField, buildDescriptionField, + buildHiddenInput, buildMultiField, buildSelectField, buildSubSection, @@ -33,7 +34,6 @@ export const aboutSection = buildSubSection({ width: 'half', required: true, minDate: new Date('1.1.2020'), - maxDate: new Date(), }), buildTextField({ id: 'accident.time', @@ -155,8 +155,11 @@ export const aboutSection = buildSubSection({ title: accident.about.locationOfAccidentHeading, marginTop: 3, }), + buildHiddenInput({ + id: 'accident.accidentLocationParentGroup', + }), buildCustomField({ - id: 'accident', + id: 'accident.accidentLocation', title: '', component: 'AccidentLocation', }), diff --git a/libs/application/templates/aosh/work-accident-notification/src/forms/WorkAccidentNotificationForm/EmployeeSection/employee.ts b/libs/application/templates/aosh/work-accident-notification/src/forms/WorkAccidentNotificationForm/EmployeeSection/employee.ts index 5ac5d07f705b..bb0fa88d47a3 100644 --- a/libs/application/templates/aosh/work-accident-notification/src/forms/WorkAccidentNotificationForm/EmployeeSection/employee.ts +++ b/libs/application/templates/aosh/work-accident-notification/src/forms/WorkAccidentNotificationForm/EmployeeSection/employee.ts @@ -3,6 +3,7 @@ import { buildCustomField, buildDateField, buildDescriptionField, + buildHiddenInput, buildMultiField, buildNationalIdWithNameField, buildSelectField, @@ -117,9 +118,6 @@ export const employeeSubSection = (index: number) => width: 'half', required: true, title: employee.employee.startDate, - maxDate: new Date(), - minYear: 1940, - maxYear: new Date().getFullYear(), }), buildSelectField({ id: `employee[${index}].employmentTime`, @@ -166,13 +164,6 @@ export const employeeSubSection = (index: number) => })) }, }), - buildAlertMessageField({ - id: 'employee.startTimeAlert', - title: '', - message: employee.employee.startTimeAlert, - alertType: 'info', - marginBottom: 0, - }), buildDateField({ id: `employee[${index}].startOfWorkdayDate`, width: 'half', @@ -244,6 +235,18 @@ export const employeeSubSection = (index: number) => titleVariant: 'h5', marginTop: 3, }), + buildHiddenInput({ + id: `employee[${index}].victimsOccupationMajor`, + }), + buildHiddenInput({ + id: `employee[${index}].victimsOccupationSubMajor`, + }), + buildHiddenInput({ + id: `employee[${index}].victimsOccupationMinor`, + }), + buildHiddenInput({ + id: `employee[${index}].victimsOccupationUnit`, + }), buildCustomField( { id: `employee[${index}].victimsOccupation`, diff --git a/libs/application/templates/aosh/work-accident-notification/src/forms/WorkAccidentNotificationForm/InformationSection/companySection.ts b/libs/application/templates/aosh/work-accident-notification/src/forms/WorkAccidentNotificationForm/InformationSection/companySection.ts index 99818937fb19..2575705bba0f 100644 --- a/libs/application/templates/aosh/work-accident-notification/src/forms/WorkAccidentNotificationForm/InformationSection/companySection.ts +++ b/libs/application/templates/aosh/work-accident-notification/src/forms/WorkAccidentNotificationForm/InformationSection/companySection.ts @@ -38,12 +38,12 @@ export const companySection = buildSubSection({ }, }), buildDescriptionField({ - id: 'basicInformation.description', + id: 'companyInformation.description', title: information.labels.company.descriptionField, titleVariant: 'h5', }), buildTextField({ - id: 'basicInformation.nationalId', + id: 'companyInformation.nationalId', title: information.labels.company.nationalId, backgroundColor: 'white', width: 'half', @@ -59,11 +59,10 @@ export const companySection = buildSubSection({ }, }), buildTextField({ - id: 'basicInformation.name', + id: 'companyInformation.name', title: information.labels.company.name, backgroundColor: 'white', width: 'half', - readOnly: true, defaultValue: (application: Application) => { const name = getValueViaPath( application.externalData, @@ -74,11 +73,10 @@ export const companySection = buildSubSection({ }, }), buildTextField({ - id: 'basicInformation.address', + id: 'companyInformation.address', title: information.labels.company.address, backgroundColor: 'white', width: 'half', - readOnly: true, defaultValue: (application: Application) => { const streetAddress = getValueViaPath( application.externalData, @@ -89,11 +87,10 @@ export const companySection = buildSubSection({ }, }), buildTextField({ - id: 'basicInformation.postnumber', + id: 'companyInformation.postnumber', title: information.labels.company.postNumberAndTown, backgroundColor: 'white', width: 'half', - readOnly: true, defaultValue: (application: Application) => { const postalCode = getValueViaPath( application.externalData, @@ -108,7 +105,7 @@ export const companySection = buildSubSection({ }, }), buildSelectField({ - id: 'basicInformation.numberOfEmployees', + id: 'companyInformation.numberOfEmployees', title: information.labels.company.numberOfEmployees, width: 'half', required: true, diff --git a/libs/application/templates/aosh/work-accident-notification/src/forms/WorkAccidentNotificationForm/prerequisitesSection.ts b/libs/application/templates/aosh/work-accident-notification/src/forms/WorkAccidentNotificationForm/prerequisitesSection.ts index f7ef164ff598..11a5028f4ece 100644 --- a/libs/application/templates/aosh/work-accident-notification/src/forms/WorkAccidentNotificationForm/prerequisitesSection.ts +++ b/libs/application/templates/aosh/work-accident-notification/src/forms/WorkAccidentNotificationForm/prerequisitesSection.ts @@ -4,6 +4,7 @@ import { buildDataProviderItem, buildSubmitField, coreMessages, + buildHiddenInput, } from '@island.is/application/core' import { externalData } from '../../lib/messages' import { diff --git a/libs/application/templates/aosh/work-accident-notification/src/lib/dataSchema.ts b/libs/application/templates/aosh/work-accident-notification/src/lib/dataSchema.ts index 34fd239f7894..f2c032533d83 100644 --- a/libs/application/templates/aosh/work-accident-notification/src/lib/dataSchema.ts +++ b/libs/application/templates/aosh/work-accident-notification/src/lib/dataSchema.ts @@ -27,7 +27,7 @@ const accidentSchema = z.object({ wentWrong: z.string().min(1).max(499), }) -const basicCompanySchema = z.object({ +const companySchema = z.object({ nationalId: z .string() .refine( @@ -35,13 +35,10 @@ const basicCompanySchema = z.object({ nationalId && nationalId.length !== 0 && kennitala.isValid(nationalId), ), address: z.string(), + addressOfBranch: z.string().optional(), name: z.string(), numberOfEmployees: z.string(), postnumber: z.string(), -}) - -const companySchema = z.object({ - addressOfBranch: z.string().optional(), nameOfBranch: z.string().optional(), // VER needs to confirm requirement here for individuals vs company postnumberOfBranch: z.string().optional(), industryClassification: z.string().optional(), @@ -194,7 +191,6 @@ const projectPurchaseSchema = z export const WorkAccidentNotificationAnswersSchema = z.object({ approveExternalData: z.boolean().refine((v) => v), - basicInformation: basicCompanySchema, companyInformation: companySchema, companyLaborProtection: companyLaborProtectionSchema, accident: accidentSchema, @@ -212,7 +208,6 @@ export const WorkAccidentNotificationAnswersSchema = z.object({ export type WorkAccidentNotification = z.TypeOf< typeof WorkAccidentNotificationAnswersSchema > -export type BasicCompanyType = z.TypeOf export type CompanyType = z.TypeOf export type CompanyLaborProtectionType = z.TypeOf< typeof companyLaborProtectionSchema diff --git a/libs/application/templates/aosh/work-accident-notification/src/lib/messages/accident.ts b/libs/application/templates/aosh/work-accident-notification/src/lib/messages/accident.ts index 185bde25b75e..2b54f6e6b49b 100644 --- a/libs/application/templates/aosh/work-accident-notification/src/lib/messages/accident.ts +++ b/libs/application/templates/aosh/work-accident-notification/src/lib/messages/accident.ts @@ -84,7 +84,7 @@ export const accident = { wasDoingPlaceholder: { id: 'aosh.wan.application:accident.about.wasDoingPlaceholder#markdown', defaultMessage: - 'Tilgreinið hvaða verkfæri eða vélar voru notaðar.\nDæmi:\n - Vann með handborvél\n - Var að styðja sjúkling á leið til baðherbergis', + 'Tilgreinið hvaða verkfæri eða vélar voru notaðar.\nDæmi:\n - Vann með handborvél\n - Vann með handborvél', description: 'Placeholder of wasDoing text area', }, wentWrongTitle: { @@ -95,7 +95,7 @@ export const accident = { wenWrongPlaceholder: { id: 'aosh.wan.application:accident.about.wenWrongPlaceholder#markdown', defaultMessage: - 'Tilgreinið hvaða verkfæri, vélar eða byrði áttu þátt í því.\nDæmi:\n - Borinn brotnaði í efninu\n - Sjúklingurinn gat skyndilega ekki staðið á fótunum', + 'Tilgreinið hvaða verkfæri eða vélar voru notaðar.\nDæmi:\n - Vann með handborvél\n - Vann með handborvél', description: 'Placeholder of wentWrong text area', }, howTitle: { @@ -106,7 +106,7 @@ export const accident = { howPlaceholder: { id: 'aosh.wan.application:accident.about.howPlaceholder#markdown', defaultMessage: - 'Tilgreinið hvaða verkfæri, vélar eða byrði var orsök slyssins.\nDæmi:\n - Borinn lenti í hendinni\n - Við að reyna að halda sjúklingnum uppi hrasaði slasaði og fékk hnykk á bakið', + 'Tilgreinið hvaða verkfæri eða vélar voru notaðar.\nDæmi:\n - Vann með handborvél\n - Vann með handborvél', description: 'Placeholder of how text area', }, locationOfAccidentHeading: { diff --git a/libs/application/templates/aosh/work-accident-notification/src/lib/messages/employee.ts b/libs/application/templates/aosh/work-accident-notification/src/lib/messages/employee.ts index f8d0d5e135a4..dad48b068ec9 100644 --- a/libs/application/templates/aosh/work-accident-notification/src/lib/messages/employee.ts +++ b/libs/application/templates/aosh/work-accident-notification/src/lib/messages/employee.ts @@ -69,7 +69,7 @@ export const employee = { description: 'Employment time in the same job', }, employmentRate: { - id: 'aosh.wan.application:employee.employmentRate', + id: 'aosh.wan.application:employee.employmentTime', defaultMessage: 'Starfshlutfall', description: 'Employment rate %', }, @@ -79,13 +79,13 @@ export const employee = { description: 'Employees work hour arrangement', }, startOfWorkdayDate: { - id: 'aosh.wan.application:employee.startOfWorkdayDate', - defaultMessage: 'Dagsetning', + id: 'aosh.wan.application:employee.time', + defaultMessage: 'Dagsetning byrjun starfsdags', description: 'Start of employees workday date', }, time: { id: 'aosh.wan.application:employee.time', - defaultMessage: 'Tími', + defaultMessage: 'Tími byrjun starfsdags', description: 'Start of employees workday time', }, timePlaceholder: { @@ -155,11 +155,5 @@ export const employee = { 'Starfsdagur má byrja allt að 36 tímum fyrir slys og að tímasetningu slyss', description: 'Error message for employee start time', }, - startTimeAlert: { - id: 'aosh.wan.application:employee.startTimeAlert', - defaultMessage: - 'Dagsetning og tími þegar starfsmaður mætti til vinnu á slysadegi', - description: 'Error message for employee start time', - }, }), } diff --git a/libs/application/templates/aosh/work-accident-notification/src/lib/messages/information.ts b/libs/application/templates/aosh/work-accident-notification/src/lib/messages/information.ts index f8e92b4b6986..ae458fb96080 100644 --- a/libs/application/templates/aosh/work-accident-notification/src/lib/messages/information.ts +++ b/libs/application/templates/aosh/work-accident-notification/src/lib/messages/information.ts @@ -14,7 +14,7 @@ export const information = { description: 'Description of information page', }, pageTitle: { - id: 'aosh.wan.application:information.general.pageTitle', + id: 'aosh.wan.application:information.company.pageTitle', defaultMessage: 'Tilkynningaraðili', description: 'Title of company information section', }, diff --git a/libs/application/templates/aosh/work-accident-notification/src/lib/messages/overview.ts b/libs/application/templates/aosh/work-accident-notification/src/lib/messages/overview.ts index 715d57d0654b..e364a4957dc8 100644 --- a/libs/application/templates/aosh/work-accident-notification/src/lib/messages/overview.ts +++ b/libs/application/templates/aosh/work-accident-notification/src/lib/messages/overview.ts @@ -47,37 +47,37 @@ export const overview = { description: 'Date and time label', }, didAoshCome: { - id: 'aosh.wan.application:overview.labels.didAoshCome', + id: 'aosh.wan.application:overview.labels.dateAndTime', defaultMessage: 'Kom Vinnueftirlitið?', description: 'Date and time label', }, didPoliceCome: { - id: 'aosh.wan.application:overview.labels.didPoliceCome', + id: 'aosh.wan.application:overview.labels.dateAndTime', defaultMessage: 'Kom Lögreglan?', description: 'Date and time label', }, injuredAmount: { - id: 'aosh.wan.application:overview.labels.injuredAmount', + id: 'aosh.wan.application:overview.labels.dateAndTime', defaultMessage: 'Fjöldi slasaðra', description: 'Date and time label', }, municipality: { - id: 'aosh.wan.application:overview.labels.municipality', + id: 'aosh.wan.application:overview.labels.dateAndTime', defaultMessage: 'Sveitarfélag þar sem slysið átti sér stað', description: 'Date and time label', }, exactLocation: { - id: 'aosh.wan.application:overview.labels.exactLocation', + id: 'aosh.wan.application:overview.labels.dateAndTime', defaultMessage: 'Nákvæm staðsetning slyssins', description: 'Date and time label', }, accidentDescription: { - id: 'aosh.wan.application:overview.labels.accidentDescription', + id: 'aosh.wan.application:overview.labels.dateAndTime', defaultMessage: 'Tildrög slyssins', description: 'Date and time label', }, locationOfAccident: { - id: 'aosh.wan.application:overview.labels.locationOfAccident', + id: 'aosh.wan.application:overview.labels.dateAndTime', defaultMessage: 'Vettvangur slyssins', description: 'Date and time label', }, diff --git a/libs/application/templates/aosh/work-accident-notification/src/utils/getCompanyInformationForOverview.ts b/libs/application/templates/aosh/work-accident-notification/src/utils/getCompanyInformationForOverview.ts index bdc845fa82e8..81a8637ffe61 100644 --- a/libs/application/templates/aosh/work-accident-notification/src/utils/getCompanyInformationForOverview.ts +++ b/libs/application/templates/aosh/work-accident-notification/src/utils/getCompanyInformationForOverview.ts @@ -6,11 +6,7 @@ import { } from '@island.is/application/types' import { information, overview } from '../lib/messages' import { format as formatKennitala } from 'kennitala' -import { - BasicCompanyType, - CompanyLaborProtectionType, - CompanyType, -} from '../lib/dataSchema' +import { CompanyLaborProtectionType, CompanyType } from '../lib/dataSchema' import { SizeOfTheEnterpriseDto, WorkplaceHealthAndSafetyDto, @@ -22,10 +18,6 @@ export const getCompanyInformationForOverview = ( externalData: ExternalData, formatMessage: FormatMessage, ) => { - const basicCompany = getValueViaPath( - answers, - 'basicInformation', - ) const company = getValueViaPath(answers, 'companyInformation') const companyLaborProtection = getValueViaPath( answers, @@ -42,15 +34,13 @@ export const getCompanyInformationForOverview = ( 'aoshData.data.workplaceHealthAndSafety', ) ?? [] const chosenSizeOfEnterprise = sizeOfEnterprises.find( - (size) => basicCompany?.numberOfEmployees === size?.code, + (size) => company?.numberOfEmployees === size?.code, ) return [ - basicCompany?.name ?? undefined, - basicCompany?.nationalId - ? formatKennitala(basicCompany.nationalId) - : undefined, - `${basicCompany?.address ?? ''}, ${basicCompany?.postnumber ?? ''}`, + company?.name ?? undefined, + company?.nationalId ? formatKennitala(company.nationalId) : undefined, + `${company?.address ?? ''}, ${company?.postnumber ?? ''}`, company?.industryClassification ?? undefined, chosenSizeOfEnterprise?.name ?? undefined, `${formatMessage(information.labels.workhealth.sectionTitle)}: ${ diff --git a/libs/application/templates/aosh/work-accident-notification/src/utils/index.ts b/libs/application/templates/aosh/work-accident-notification/src/utils/index.ts index 8dd880060ee4..e3e0d34b45e6 100644 --- a/libs/application/templates/aosh/work-accident-notification/src/utils/index.ts +++ b/libs/application/templates/aosh/work-accident-notification/src/utils/index.ts @@ -9,4 +9,3 @@ export { isValidPhoneNumber, } from './dateManipulation' export { isCompany } from './isCompany' -export { formatDate } from './formatDate' diff --git a/libs/application/templates/car-recycling/src/graphql/queries.ts b/libs/application/templates/car-recycling/src/graphql/queries.ts index 1d0ec4b420a4..472254beae03 100644 --- a/libs/application/templates/car-recycling/src/graphql/queries.ts +++ b/libs/application/templates/car-recycling/src/graphql/queries.ts @@ -9,7 +9,7 @@ export const GetUsersVehiclesV2Query = gql` colorName modelYear requiresMileageRegistration - canRegisterMileage + canRegisterMilage role } paging { diff --git a/libs/application/templates/directorate-of-immigration/citizenship/src/lib/messages/payment.ts b/libs/application/templates/directorate-of-immigration/citizenship/src/lib/messages/payment.ts index 6c504b1c2bea..0f207aa213e0 100644 --- a/libs/application/templates/directorate-of-immigration/citizenship/src/lib/messages/payment.ts +++ b/libs/application/templates/directorate-of-immigration/citizenship/src/lib/messages/payment.ts @@ -13,6 +13,18 @@ export const payment = { description: 'Payment page title', }, }), + paymentChargeOverview: defineMessages({ + forPayment: { + id: 'doi.cs.application:payment.paymentChargeOverview.forPayment', + defaultMessage: 'Til greiðslu', + description: 'For payment label', + }, + total: { + id: 'doi.cs.application:payment.paymentChargeOverview.total', + defaultMessage: 'Samtals', + description: 'Total amount label', + }, + }), confirmation: defineMessages({ confirm: { id: 'doi.cs.application:payment.confirmation.confirm', diff --git a/libs/application/templates/driving-license-duplicate/src/fields/Alert.tsx b/libs/application/templates/driving-license-duplicate/src/fields/Alert.tsx index da86370f043b..97547f2646d6 100644 --- a/libs/application/templates/driving-license-duplicate/src/fields/Alert.tsx +++ b/libs/application/templates/driving-license-duplicate/src/fields/Alert.tsx @@ -27,7 +27,7 @@ export const Alert: FC> = ({ }) => { const { formatMessage } = useLocale() const { title, type, message, heading } = field.props as Field - + console.log('message', formatText(message, application, formatMessage)) return ( {heading && ( diff --git a/libs/application/templates/driving-license/src/fields/AdvancedLicenseSelection/AdvancedLicenseSelection.tsx b/libs/application/templates/driving-license/src/fields/AdvancedLicenseSelection/AdvancedLicenseSelection.tsx deleted file mode 100644 index 7976512917fc..000000000000 --- a/libs/application/templates/driving-license/src/fields/AdvancedLicenseSelection/AdvancedLicenseSelection.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import React, { FC, useEffect, useState } from 'react' - -import { Box, Checkbox, ErrorMessage, Text } from '@island.is/island-ui/core' -import { FieldBaseProps } from '@island.is/application/types' -import { useFormContext } from 'react-hook-form' -import { - organizedAdvancedLicenseMap, - AdvancedLicense as AdvancedLicenseEnum, -} from '../../lib/constants' -import { useLocale } from '@island.is/localization' -import { m } from '../../lib/messages' - -const AdvancedLicenseSelection: FC> = ({ - errors, -}) => { - const { formatMessage } = useLocale() - const { setValue, watch } = useFormContext() - - const requiredMessage = (errors as { advancedLicense?: string }) - ?.advancedLicense - ? formatMessage(m.applicationForAdvancedRequiredError) - : '' - - const advancedLicenseValue = watch('advancedLicense') ?? [] - - const [selectedLicenses, setSelectedLicenses] = - useState>(advancedLicenseValue) - - useEffect(() => { - setValue('advancedLicense', selectedLicenses) - }, [selectedLicenses, setValue]) - - return ( - - {Object.entries(organizedAdvancedLicenseMap).map(([, options], index) => { - const group = options.find((x) => x.group)?.group - const groupAge = options.find((x) => x.minAge)?.minAge - - return ( - - - - {group ? formatMessage(m[`groupTitle${group}`]) : ''} - - - {formatMessage(m[`applicationForAdvancedAgeRequired`], { - age: String(groupAge), - })} - - - {options.map((option) => { - const name = `field-${option.code}` - - return ( - - { - setSelectedLicenses((prev) => { - return prev.includes(option.code) - ? prev - .filter((item) => item !== option.code) - .filter( - (item) => item !== option.professional?.code, - ) - : [...prev, option.code] - }) - }} - /> - {option?.professional?.code && ( - - { - setSelectedLicenses((prev) => { - if (e.target.checked && option.professional?.code) { - return [...prev, option.professional.code] - } - - return prev.filter( - (item) => item !== option.professional?.code, - ) - }) - }} - /> - - )} - - ) - })} - - ) - })} - {!selectedLicenses?.length && requiredMessage && ( - -
{requiredMessage}
-
- )} -
- ) -} - -export { AdvancedLicenseSelection } diff --git a/libs/application/templates/driving-license/src/fields/AdvancedLicenseSelection/index.tsx b/libs/application/templates/driving-license/src/fields/AdvancedLicenseSelection/index.tsx deleted file mode 100644 index 051ad8e7ef5f..000000000000 --- a/libs/application/templates/driving-license/src/fields/AdvancedLicenseSelection/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { AdvancedLicenseSelection } from './AdvancedLicenseSelection' diff --git a/libs/application/templates/driving-license/src/fields/index.ts b/libs/application/templates/driving-license/src/fields/index.ts index 6e5d3bd15a6b..6f42ecaa4bb4 100644 --- a/libs/application/templates/driving-license/src/fields/index.ts +++ b/libs/application/templates/driving-license/src/fields/index.ts @@ -4,6 +4,5 @@ export { EligibilitySummary } from './EligibilitySummary' export { SubmitAndDecline } from './SubmitAndDecline' export { LinkExistingApplication } from './LinkExistingApplication' export { PaymentPending } from './PaymentPending' -export { AdvancedLicenseSelection } from './AdvancedLicenseSelection' export { QualityPhoto } from './QualityPhoto' export { default as HealthRemarks } from './HealthRemarks' diff --git a/libs/application/templates/driving-license/src/forms/prerequisites/getForm.ts b/libs/application/templates/driving-license/src/forms/prerequisites/getForm.ts index 28d94b416070..9bf9a5b197a7 100644 --- a/libs/application/templates/driving-license/src/forms/prerequisites/getForm.ts +++ b/libs/application/templates/driving-license/src/forms/prerequisites/getForm.ts @@ -8,23 +8,13 @@ import { sectionApplicationFor } from './sectionApplicationFor' import { sectionRequirements } from './sectionRequirements' import { sectionExistingApplication } from './sectionExistingApplication' import { sectionDigitalLicenseInfo } from './sectionDigitalLicenseInfo' -import { sectionAdvancedLicenseSelection } from './sectionAdvancedLicenseSelection' - -interface DrivingLicenseFormConfig { - allowFakeData?: boolean - allowPickLicense?: boolean - allowBELicense?: boolean - allow65Renewal?: boolean - allowAdvanced?: boolean -} export const getForm = ({ allowFakeData = false, allowPickLicense = false, allowBELicense = false, allow65Renewal = false, - allowAdvanced = false, -}: DrivingLicenseFormConfig): Form => +}): Form => buildForm({ id: 'DrivingLicenseApplicationPrerequisitesForm', title: '', @@ -41,15 +31,8 @@ export const getForm = ({ sectionExternalData, sectionExistingApplication, ...(allowPickLicense - ? [ - sectionApplicationFor( - allowBELicense, - allow65Renewal, - allowAdvanced, - ), - ] + ? [sectionApplicationFor(allowBELicense, allow65Renewal)] : []), - ...(allowAdvanced ? [sectionAdvancedLicenseSelection] : []), sectionDigitalLicenseInfo, sectionRequirements, ], diff --git a/libs/application/templates/driving-license/src/forms/prerequisites/sectionAdvancedLicenseSelection.ts b/libs/application/templates/driving-license/src/forms/prerequisites/sectionAdvancedLicenseSelection.ts deleted file mode 100644 index e58582a6577b..000000000000 --- a/libs/application/templates/driving-license/src/forms/prerequisites/sectionAdvancedLicenseSelection.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { - buildCustomField, - buildMultiField, - buildSubSection, - getValueViaPath, -} from '@island.is/application/core' -import { m } from '../../lib/messages' -import { LicenseTypes } from '../../lib/constants' - -export const sectionAdvancedLicenseSelection = buildSubSection({ - id: 'sectionAdvancedLicenseSelection', - title: m.applicationForAdvancedLicenseTitle, - condition: (answers) => { - const applicationFor = getValueViaPath( - answers, - 'applicationFor', - ) - - return applicationFor != null && applicationFor === LicenseTypes.B_ADVANCED - }, - children: [ - buildMultiField({ - id: 'advancedLicenseSelectionFields', - title: m.applicationForAdvancedLicenseSectionTitle, - description: m.applicationForAdvancedLicenseSectionDescription, - children: [ - buildCustomField({ - id: 'advancedLicense', - title: '', - component: 'AdvancedLicenseSelection', - }), - ], - }), - ], -}) diff --git a/libs/application/templates/driving-license/src/forms/prerequisites/sectionApplicationFor.ts b/libs/application/templates/driving-license/src/forms/prerequisites/sectionApplicationFor.ts index 571231ab2c73..cf6ba528b6bb 100644 --- a/libs/application/templates/driving-license/src/forms/prerequisites/sectionApplicationFor.ts +++ b/libs/application/templates/driving-license/src/forms/prerequisites/sectionApplicationFor.ts @@ -7,7 +7,6 @@ import { import { m } from '../../lib/messages' import { DrivingLicense } from '../../lib/types' import { - B_ADVANCED, B_FULL, B_FULL_RENEWAL_65, B_TEMP, @@ -18,7 +17,6 @@ import { export const sectionApplicationFor = ( allowBELicense = false, allow65Renewal = false, - allowAdvanced = false, ) => buildSubSection({ id: 'applicationFor', @@ -114,17 +112,6 @@ export const sectionApplicationFor = ( }) } - if (allowAdvanced) { - options = options.concat({ - label: m.applicationForAdvancedLicenseTitle, - subLabel: m.applicationForAdvancedLicenseDescription, - value: B_ADVANCED, - disabled: !categories?.some( - (c) => c.nr.toUpperCase() === 'B' && c.validToCode !== 8, - ), - }) - } - return options }, }), diff --git a/libs/application/templates/driving-license/src/lib/constants.ts b/libs/application/templates/driving-license/src/lib/constants.ts index c10fc573f53b..0e4e00652e02 100644 --- a/libs/application/templates/driving-license/src/lib/constants.ts +++ b/libs/application/templates/driving-license/src/lib/constants.ts @@ -8,141 +8,9 @@ export enum ApiActions { export const B_FULL = 'B-full' export const B_TEMP = 'B-temp' export const B_FULL_RENEWAL_65 = 'B-full-renewal-65' -export const B_ADVANCED = 'B-advanced' export const BE = 'BE' export const DELIVERY_FEE = 'deliveryFee' -export enum LicenseTypes { - 'B_FULL' = 'B-full', - 'B_TEMP' = 'B-temp', - 'B_FULL_RENEWAL_65' = 'B-full-renewal-65', - 'BE' = 'BE', - 'B_ADVANCED' = 'B-advanced', -} - -export enum Pickup { - 'POST' = 'post', - 'DISTRICT' = 'district', -} - -export enum AdvancedLicenseGroupCodes { - 'C1' = 'C1', - 'C' = 'C', - 'D1' = 'D1', - 'D' = 'D', -} - -export enum MainAdvancedLicense { - 'C1' = 'C1', - 'D1' = 'D1', - 'C' = 'C', - 'D' = 'D', - 'C1E' = 'C1E', - 'D1E' = 'D1E', - 'CE' = 'CE', - 'DE' = 'DE', -} - -export enum ProfessionalAdvancedLicense { - 'C1A' = 'C1A', - 'D1A' = 'D1A', - 'CA' = 'CA', - 'DA' = 'DA', -} - -export const AdvancedLicense = { - ...MainAdvancedLicense, - ...ProfessionalAdvancedLicense, -} as const - -type AdvancedLicenseMapItem = { - minAge: number - group: keyof typeof AdvancedLicenseGroupCodes - code: keyof typeof MainAdvancedLicense - professional?: { - minAge: number - code: keyof typeof ProfessionalAdvancedLicense - } -} - -export const advancedLicenseMap: AdvancedLicenseMapItem[] = [ - // C1 - { - code: 'C1', - group: 'C1', - minAge: 18, - professional: { - code: 'C1A', - minAge: 18, - }, - }, - { - code: 'C1E', - group: 'C1', - minAge: 18, - }, - - // C - { - code: 'C', - group: 'C', - minAge: 21, - professional: { - code: 'CA', - minAge: 21, - }, - }, - { - code: 'CE', - group: 'C', - minAge: 21, - }, - - // D1 - { - code: 'D1', - group: 'D1', - minAge: 21, - professional: { - code: 'D1A', - minAge: 21, - }, - }, - { - code: 'D1E', - group: 'D1', - minAge: 21, - }, - - // D - { - code: 'D', - group: 'D', - minAge: 23, - professional: { - code: 'DA', - minAge: 23, - }, - }, - { - code: 'DE', - group: 'D', - minAge: 23, - }, -] - -export const organizedAdvancedLicenseMap = advancedLicenseMap.reduce< - Record ->((acc, item) => { - if (!acc[item.group]) { - acc[item.group] = [] - } - - acc[item.group].push(item) - - return acc -}, {}) - export const CHARGE_ITEM_CODES: Record = { [B_TEMP]: 'AY114', [B_FULL]: 'AY110', diff --git a/libs/application/templates/driving-license/src/lib/dataSchema.ts b/libs/application/templates/driving-license/src/lib/dataSchema.ts index 981719fbf325..82d68194a468 100644 --- a/libs/application/templates/driving-license/src/lib/dataSchema.ts +++ b/libs/application/templates/driving-license/src/lib/dataSchema.ts @@ -1,14 +1,5 @@ import { z } from 'zod' -import { - YES, - NO, - B_FULL_RENEWAL_65, - BE, - B_TEMP, - B_FULL, - B_ADVANCED, - AdvancedLicense, -} from './constants' +import { YES, NO, B_FULL_RENEWAL_65, BE, B_TEMP, B_FULL } from './constants' import { parsePhoneNumberFromString } from 'libphonenumber-js' import { Pickup } from './types' @@ -45,13 +36,7 @@ export const dataSchema = z.object({ ]), requirementsMet: z.boolean().refine((v) => v), certificate: z.array(z.enum([YES, NO])).nonempty(), - applicationFor: z.enum([B_FULL, B_TEMP, BE, B_FULL_RENEWAL_65, B_ADVANCED]), - advancedLicense: z - .array(z.enum(Object.values(AdvancedLicense) as [string, ...string[]])) - .nonempty() - .refine((value) => { - return value.length > 0 - }), + applicationFor: z.enum([B_FULL, B_TEMP, BE, B_FULL_RENEWAL_65]), email: z.string().email(), phone: z.string().refine((v) => isValidPhoneNumber(v)), drivingInstructor: z.string().min(1), diff --git a/libs/application/templates/driving-license/src/lib/drivingLicenseTemplate.ts b/libs/application/templates/driving-license/src/lib/drivingLicenseTemplate.ts index 56ea5c57742f..d7c7a9ea2d27 100644 --- a/libs/application/templates/driving-license/src/lib/drivingLicenseTemplate.ts +++ b/libs/application/templates/driving-license/src/lib/drivingLicenseTemplate.ts @@ -53,7 +53,7 @@ import { Pickup } from './types' const getCodes = (application: Application) => { const applicationFor = getValueViaPath< - 'B-full' | 'B-temp' | 'BE' | 'B-full-renewal-65' | 'B-advanced' + 'B-full' | 'B-temp' | 'BE' | 'B-full-renewal-65' >(application.answers, 'applicationFor', 'B-full') const pickup = getValueViaPath(application.answers, 'pickup') @@ -142,8 +142,6 @@ const template: ApplicationTemplate< featureFlags[DrivingLicenseFeatureFlags.ALLOW_BE_LICENSE], allow65Renewal: featureFlags[DrivingLicenseFeatureFlags.ALLOW_65_RENEWAL], - allowAdvanced: - featureFlags[DrivingLicenseFeatureFlags.ALLOW_ADVANCED], }) }, write: 'all', diff --git a/libs/application/templates/driving-license/src/lib/getApplicationFeatureFlags.ts b/libs/application/templates/driving-license/src/lib/getApplicationFeatureFlags.ts index 2905a9b30d89..9a7186289db8 100644 --- a/libs/application/templates/driving-license/src/lib/getApplicationFeatureFlags.ts +++ b/libs/application/templates/driving-license/src/lib/getApplicationFeatureFlags.ts @@ -5,7 +5,6 @@ export enum DrivingLicenseFeatureFlags { ALLOW_LICENSE_SELECTION = 'applicationTemplateDrivingLicenseAllowLicenseSelection', ALLOW_BE_LICENSE = 'isBEApplicationEnabled', ALLOW_65_RENEWAL = 'is65RenewalApplicationEnabled', - ALLOW_ADVANCED = 'isDrivingLicenseAdvancedEnabled', } export const getApplicationFeatureFlags = async ( @@ -16,7 +15,6 @@ export const getApplicationFeatureFlags = async ( DrivingLicenseFeatureFlags.ALLOW_LICENSE_SELECTION, DrivingLicenseFeatureFlags.ALLOW_BE_LICENSE, DrivingLicenseFeatureFlags.ALLOW_65_RENEWAL, - DrivingLicenseFeatureFlags.ALLOW_ADVANCED, ] return ( diff --git a/libs/application/templates/driving-license/src/lib/messages.ts b/libs/application/templates/driving-license/src/lib/messages.ts index 778c8fa73979..07431e977201 100644 --- a/libs/application/templates/driving-license/src/lib/messages.ts +++ b/libs/application/templates/driving-license/src/lib/messages.ts @@ -878,164 +878,6 @@ export const m = defineMessages({ description: 'Health declaration answers indicate that health certificate is required and BE application does not support health certificate requirement', }, - applicationForAdvancedLicenseTitle: { - id: 'dl.application:applicationForAdvancedLicenseTitle', - defaultMessage: 'Aukin ökuréttindi / meirapróf', - description: 'Option title for selecting advanced driving license', - }, - applicationForAdvancedLicenseDescription: { - id: 'dl.application:applicationForAdvancedLicenseDescription', - defaultMessage: 'Texti kemur hér', - description: 'Option description for selecting advanced driving license', - }, - applicationForAdvancedLicenseSectionTitle: { - id: 'dl.application:applicationForAdvancedLicenseSectionTitle', - defaultMessage: 'Veldu réttindi', - description: 'Option title for selecting advanced driving license', - }, - applicationForAdvancedLicenseSectionDescription: { - id: 'dl.application:applicationForAdvancedLicenseSectionDescription', - defaultMessage: 'Í þessari umsókn er verið að sækja um:', - description: 'Option description for selecting advanced driving license', - }, - applicationForAdvancedAgeRequired: { - id: 'dl.application:applicationForAdvancedAgeFor', - defaultMessage: 'Réttindaaldur er {age} ára.', - description: 'Required age for {licenses} is {age} years', - }, - groupTitleC1: { - id: 'dl.application:groupTitleC1', - defaultMessage: 'Minni vörubíll og eftirvagn (C1 og C1E)', - description: 'C1 group title', - }, - groupTitleC: { - id: 'dl.application:groupTitleC1', - defaultMessage: 'Vörubíll og eftirvagn (C og CE)', - description: 'C1 group title', - }, - groupTitleD1: { - id: 'dl.application:groupTitleC1', - defaultMessage: 'Lítil rúta og eftirvagn (D1 og D1E)', - description: 'C1 group title', - }, - groupTitleD: { - id: 'dl.application:groupTitleC1', - defaultMessage: 'Stór rúta og eftirvagn (D og DE)', - description: 'C1 group title', - }, - applicationForAdvancedLicenseTitleC1: { - id: 'dl.application:applicationForAdvancedLicenseTitleC1', - defaultMessage: 'Minni vörubíll (C1)', - description: 'C1 title', - }, - applicationForAdvancedLicenseLabelC1: { - id: 'dl.application:applicationForAdvancedLicenseLabelC1', - defaultMessage: - 'Gefur réttindi til að aka bifreið fyrir 8 farþega eða færri, sem er þyngri en 3.500 kg en þó ekki þyngri en 7.500 kg. Sá sem hefur C1 réttindi má tengja eftirvagn/tengitæki sem er 750 kg eða minna af leyfðri heildarþyngd. Til þess að mega draga þyngri eftirvagna/tengitæki þarf að taka C1E réttindi.', - description: 'C1 description', - }, - applicationForAdvancedLicenseLabelC1A: { - id: 'dl.application:applicationForAdvancedLicenseLabelC1A', - defaultMessage: 'Sækja um leyfi í atvinnuskyni', - description: 'C1A description', - }, - applicationForAdvancedLicenseTitleD1: { - id: 'dl.application:applicationForAdvancedLicenseTitleD1', - defaultMessage: 'Lítil rúta (D1)', - description: 'D1 title', - }, - applicationForAdvancedLicenseLabelD1: { - id: 'dl.application:applicationForAdvancedLicenseLabelD1', - defaultMessage: - 'Gefur réttindi til að aka hópbifreið sem er gerð fyrir að hámarki 16 farþega. Sá sem hefur D1 réttindi má tengja eftirvagn/tengitæki sem er 750 kg eða minna að leyfðri heildarþyngd.', - description: 'D1 description', - }, - applicationForAdvancedLicenseLabelD1A: { - id: 'dl.application:applicationForAdvancedLicenseLabelD1A', - defaultMessage: 'Sækja um leyfi í atvinnuskyni', - description: 'D1A description', - }, - applicationForAdvancedLicenseTitleC: { - id: 'dl.application:applicationForAdvancedLicenseTitleC', - defaultMessage: 'Vörubíll (C)', - description: 'C title', - }, - applicationForAdvancedLicenseLabelC: { - id: 'dl.application:applicationForAdvancedLicenseLabelC', - defaultMessage: - 'Gefur réttindi til að aka vörubifreið fyrir 8 farþega eða færri, sem er þyngri en 7.500 kg. C flokkur gefur einnig réttindi til að aka bifreiðinni með eftirvagni sem er 750 kg eða minna af leyfðri heildarþyngd.', - description: 'C description', - }, - applicationForAdvancedLicenseLabelCA: { - id: 'dl.application:applicationForAdvancedLicenseLabelCA', - defaultMessage: 'Sækja um leyfi í atvinnuskyni', - description: 'CA description', - }, - applicationForAdvancedLicenseTitleD: { - id: 'dl.application:applicationForAdvancedLicenseTitleD', - defaultMessage: 'Stór rúta (D)', - description: 'D title', - }, - applicationForAdvancedLicenseLabelD: { - id: 'dl.application:applicationForAdvancedLicenseLabelD', - defaultMessage: - 'Gefur réttindi til að aka bifreið sem gerð er fyrir fleiri en 8 farþega auk ökumanns. Sá sem hefur D réttindi má tengja eftirvagn/tengitæki sem er 750 kg eða minna af leyfðri heildarþyngd.', - description: 'D description', - }, - applicationForAdvancedLicenseLabelDA: { - id: 'dl.application:applicationForAdvancedLicenseLabelDA', - defaultMessage: 'Sækja um leyfi í atvinnuskyni', - description: 'DA description', - }, - applicationForAdvancedLicenseTitleC1E: { - id: 'dl.application:applicationForAdvancedLicenseTitleC1E', - defaultMessage: 'Minni vörubíll og eftirvagn (C1E)', - description: 'C1E title', - }, - applicationForAdvancedLicenseLabelC1E: { - id: 'dl.application:applicationForAdvancedLicenseLabelC1E', - defaultMessage: - 'Gefur réttindi til að aka vörubifreið/stórum pallbíl í flokki C1 með eftirvagni sem er þyngri en 750 kg að heildarþunga. Þó má sameiginlegur heildarþungi beggja ökutækja ekki fara yfir 12.000 kg. ', - description: 'C1E description', - }, - applicationForAdvancedLicenseTitleD1E: { - id: 'dl.application:applicationForAdvancedLicenseTitleD1E', - defaultMessage: 'Lítil rúta og eftirvagn (D1)', - description: 'D1E title', - }, - applicationForAdvancedLicenseLabelD1E: { - id: 'dl.application:applicationForAdvancedLicenseLabelD1E', - defaultMessage: - 'Gefur réttindi til að aka bifreið í B-flokki með eftirvagn í BE-flokki og hópbifreið í D1 flokki með eftirvagn sem er þyngri en 750 kg að heildarþunga. Þó má sameiginlegur heildarþungi beggja ökutækja ekki fara yfir 12.000 kg.', - description: 'D1E description', - }, - applicationForAdvancedLicenseTitleCE: { - id: 'dl.application:applicationForAdvancedLicenseTitleCE', - defaultMessage: 'Vörubíll og eftirvagn (CE)', - description: 'CE title', - }, - applicationForAdvancedLicenseLabelCE: { - id: 'dl.application:applicationForAdvancedLicenseLabelCE', - defaultMessage: - 'Gefur réttindi til að aka vörubifreið í flokki C með eftirvagni sem er þyngri en 750 kg að heildarþunga.', - description: 'CE description', - }, - applicationForAdvancedLicenseTitleDE: { - id: 'dl.application:applicationForAdvancedLicenseTitleDE', - defaultMessage: 'Stór rúta og eftirvagn (DE)', - description: 'DE title', - }, - applicationForAdvancedLicenseLabelDE: { - id: 'dl.application:applicationForAdvancedLicenseLabelDE', - defaultMessage: - 'Að loknum D réttindum, er hægt að taka að auki DE, sem gefur réttindi til að aka hópbifreið í flokki D með eftirvagni sem er þyngri en 750 kg að heildarþunga. Þeir nemendur sem taka eftirvagnaréttindi í flokki DE og gilda þau réttindi einnig fyrir CE.', - description: 'DE description', - }, - applicationForAdvancedRequiredError: { - id: 'dl.application:applicationForAdvancedRequiredError', - defaultMessage: 'Þú verður að velja að minnsta kosti einn valmöguleika', - description: 'You must select at least one option', - }, }) export const requirementsMessages = defineMessages({ diff --git a/libs/application/templates/financial-aid/src/fields/Summary/FileList.tsx b/libs/application/templates/financial-aid/src/fields/Summary/FileList.tsx index 5a637f8a87d6..8706ccce4976 100644 --- a/libs/application/templates/financial-aid/src/fields/Summary/FileList.tsx +++ b/libs/application/templates/financial-aid/src/fields/Summary/FileList.tsx @@ -1,24 +1,17 @@ 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 { getFileType } from '@island.is/financial-aid/shared/lib' import { useFileUpload } from '../../lib/hooks/useFileUpload' -import { missingFiles } from '../../lib/messages' +import cn from 'classnames' + import * as styles from './FileList.css' -interface Props { +type Props = { applicationSystemId: string files?: UploadFile[] } -const FileList = ({ files, applicationSystemId }: Props) => { - const { formatMessage } = useIntl() - const { openFileById } = useFileUpload(files ?? [], applicationSystemId) - +const FileList = ({ files }: Props) => { if (files === undefined || files.length === 0) { return null } @@ -26,17 +19,13 @@ const FileList = ({ files, applicationSystemId }: Props) => { return ( {files.map((file, i) => { - return file.id ? ( - + ) : null })} ) } + export default FileList diff --git a/libs/application/templates/financial-aid/src/fields/childrenFilesForm/ChildrenFilesForm.tsx b/libs/application/templates/financial-aid/src/fields/childrenFilesForm/ChildrenFilesForm.tsx deleted file mode 100644 index 4e8c901d9749..000000000000 --- a/libs/application/templates/financial-aid/src/fields/childrenFilesForm/ChildrenFilesForm.tsx +++ /dev/null @@ -1,25 +0,0 @@ -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' - -export const ChildrenFilesForm = ({ field, application }: FieldBaseProps) => { - const { formatMessage } = useIntl() - const { id, answers } = application - - return ( - <> - - {formatMessage(childrenFilesForm.general.description)} - - - - ) -} diff --git a/libs/application/templates/financial-aid/src/fields/fileUploadController/FileUploadControler.tsx b/libs/application/templates/financial-aid/src/fields/fileUploadController/FileUploadControler.tsx deleted file mode 100644 index 297bbefe6d7a..000000000000 --- a/libs/application/templates/financial-aid/src/fields/fileUploadController/FileUploadControler.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React, { ReactNode } from 'react' -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' -interface Props { - children: ReactNode - hasError?: boolean -} - -const FileUploadContainer = ({ children, hasError = false }: Props) => { - const { formatMessage } = useIntl() - - return ( - - - {children} -
- - {formatMessage(filesText.errorMessage)} - -
-
-
- ) -} - -export default FileUploadContainer 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/files/Files.tsx b/libs/application/templates/financial-aid/src/fields/files/Files.tsx deleted file mode 100644 index e1077c1412d5..000000000000 --- a/libs/application/templates/financial-aid/src/fields/files/Files.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import React, { useEffect } from 'react' -import { InputFileUpload, UploadFile } from '@island.is/island-ui/core' - -import { useIntl } from 'react-intl' -import { filesText } from '../../lib/messages' -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[] - fileKey: UploadFileType - folderId: string - hasError?: boolean -} - -const Files = ({ uploadFiles, fileKey, folderId, hasError = false }: Props) => { - const { formatMessage } = useIntl() - const { setValue } = useFormContext() - - const { - files, - uploadErrorMessage, - onChange, - onRemove, - onRetry, - onUploadRejection, - } = useFileUpload(uploadFiles, folderId) - - const fileToObject = (file: UploadFile) => { - return { - key: file.key, - name: file.name, - size: file.size, - status: file.status, - percent: file?.percent, - } - } - - useEffect(() => { - const formFiles = files - .filter((f) => f.status === 'done') - .map((f) => { - return fileToObject(f) - }) - setValue(fileKey, formFiles) - }, [files]) - - return ( - - - - ) -} - -export default Files diff --git a/libs/application/templates/financial-aid/src/fields/incomeFilesForm/IncomeFilesForm.tsx b/libs/application/templates/financial-aid/src/fields/incomeFilesForm/IncomeFilesForm.tsx deleted file mode 100644 index c69fabaa0f5b..000000000000 --- a/libs/application/templates/financial-aid/src/fields/incomeFilesForm/IncomeFilesForm.tsx +++ /dev/null @@ -1,33 +0,0 @@ -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' - -export const IncomeFilesForm = ({ field, application }: FieldBaseProps) => { - const { formatMessage } = useIntl() - const { id, answers, externalData } = application - const success = getValueViaPath( - externalData, - 'taxData.data.municipalitiesDirectTaxPayments.success', - ) - return ( - <> - - {formatMessage( - success - ? incomeFilesForm.general.descriptionTaxSuccess - : incomeFilesForm.general.description, - )} - - - - ) -} diff --git a/libs/application/templates/financial-aid/src/fields/index.ts b/libs/application/templates/financial-aid/src/fields/index.ts index de41cb87c6bf..39e0c44bdc48 100644 --- a/libs/application/templates/financial-aid/src/fields/index.ts +++ b/libs/application/templates/financial-aid/src/fields/index.ts @@ -5,7 +5,3 @@ 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' diff --git a/libs/application/templates/financial-aid/src/fields/missingFiles/MissingFiles.tsx b/libs/application/templates/financial-aid/src/fields/missingFiles/MissingFiles.tsx deleted file mode 100644 index 4b0edab8d80d..000000000000 --- a/libs/application/templates/financial-aid/src/fields/missingFiles/MissingFiles.tsx +++ /dev/null @@ -1,181 +0,0 @@ -import React, { useEffect, useMemo, useState } from 'react' -import { useIntl } from 'react-intl' -import { - Text, - Box, - AlertMessage, - Input, - LoadingDots, -} from '@island.is/island-ui/core' -import { - ApplicationEventType, - ApplicationState, - FileType, - getCommentFromLatestEvent, -} from '@island.is/financial-aid/shared/lib' -import { getValueViaPath } from '@island.is/application/core' -import { FieldBaseProps, RecordObject } from '@island.is/application/types' -import { filesText, missingFiles } from '../../lib/messages' -import { 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' - -export const MissingFiles = ({ - application, - setBeforeSubmitCallback, - field, -}: FieldBaseProps) => { - const currentApplicationId = getValueViaPath( - application.externalData, - 'currentApplication.data.currentApplicationId', - ) - const email = getValueViaPath( - application.externalData, - 'municipality.data.email', - ) - 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) - - const { uploadFiles } = useFileUpload(files, application.id) - - const [error, setError] = useState(false) - const [filesError, setFilesError] = useState(false) - - const fileComment = useMemo(() => { - if (currentApplication?.applicationEvents) { - return getCommentFromLatestEvent( - currentApplication?.applicationEvents, - ApplicationEventType.DATANEEDED, - ) - } - }, [currentApplication]) - - useEffect(() => { - if (filesError) { - setFilesError(false) - } - }, [files]) - - setBeforeSubmitCallback && - setBeforeSubmitCallback(async () => { - setError(false) - if (files.length <= 0) { - setFilesError(true) - return [false, formatMessage(filesText.errorMessage)] - } - - try { - if (!currentApplicationId) { - throw new Error() - } - - const uploadedFiles = await uploadFiles( - currentApplicationId, - FileType.OTHER, - files, - ) - setValue(fileType, uploadedFiles) - - await updateApplication( - ApplicationState.INPROGRESS, - isSpouse - ? ApplicationEventType.SPOUSEFILEUPLOAD - : ApplicationEventType.FILEUPLOAD, - getValues(commentType), - ) - } catch (e) { - setError(true) - return [false, formatMessage(missingFiles.error.title)] - } - return [true, null] - }) - - if (loading) { - return - } - - return ( - <> - - {formatMessage(missingFiles.general.description)} - - - {fileComment?.comment && ( - - - } - /> - - )} - - - - - - - - {formatMessage(missingFiles.comment.title)} - - - { - return ( - { - onChange(e.target.value) - setValue(commentType, e.target.value) - }} - /> - ) - }} - /> - - - - {error && ( - <> - - {formatMessage(missingFiles.error.title)} - - - - )} - - ) -} diff --git a/libs/application/templates/financial-aid/src/fields/taxReturnFilesForm/TaxFormContent.tsx b/libs/application/templates/financial-aid/src/fields/taxReturnFilesForm/TaxFormContent.tsx deleted file mode 100644 index b24399cae3e2..000000000000 --- a/libs/application/templates/financial-aid/src/fields/taxReturnFilesForm/TaxFormContent.tsx +++ /dev/null @@ -1,77 +0,0 @@ -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' - -const DirectTaxPaymentsInfo = () => { - const { formatMessage } = useIntl() - return ( - <> - - {formatMessage(taxReturnForm.instructions.findDirectTaxPaymentsTitle)} - - - - {formatMessage(taxReturnForm.instructions.findDirectTaxPayments)} - - - ) -} - -const TaxReturnInfo = () => { - const { formatMessage } = useIntl() - return ( - <> - - {formatMessage(taxReturnForm.instructions.findTaxReturnTitle)} - - - - ) -} - -export const getTaxFormContent = ( - taxReturnFailed: boolean, - directTaxPaymentsFailed: boolean, -) => { - switch (true) { - case taxReturnFailed && !directTaxPaymentsFailed: - return { - data: ( - - - - ), - info: , - } - case directTaxPaymentsFailed && !taxReturnFailed: - return { - data: ( - - - - ), - info: , - } - - default: - return { - data: ( - <> - - - - - ), - info: ( - <> - - - - ), - } - } -} diff --git a/libs/application/templates/financial-aid/src/fields/taxReturnFilesForm/TaxReturnFilesForm.tsx b/libs/application/templates/financial-aid/src/fields/taxReturnFilesForm/TaxReturnFilesForm.tsx deleted file mode 100644 index 71480d5c5aa0..000000000000 --- a/libs/application/templates/financial-aid/src/fields/taxReturnFilesForm/TaxReturnFilesForm.tsx +++ /dev/null @@ -1,72 +0,0 @@ -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', - ) - - const taxDataToUse = - assignees.includes(nationalId ?? '') && spouseTaxData - ? spouseTaxData - : taxData - - if (!taxDataToUse) { - return null - } - - const { municipalitiesDirectTaxPayments, municipalitiesPersonalTaxReturn } = - taxDataToUse - - const taxReturnFetchFailed = - municipalitiesPersonalTaxReturn?.personalTaxReturn === null - const directTaxPaymentsFetchedFailed = - municipalitiesDirectTaxPayments.directTaxPayments.length === 0 && - !municipalitiesDirectTaxPayments.success - const taxDataGatheringFailed = - taxReturnFetchFailed && directTaxPaymentsFetchedFailed - - const content = getTaxFormContent( - taxReturnFetchFailed, - directTaxPaymentsFetchedFailed, - ) - - return ( - <> - {taxDataGatheringFailed && ( - - - - )} - - {content.data} - - - - {content.info} - - ) -} diff --git a/libs/application/templates/financial-aid/src/forms/ApplicantSubmittedForm/MissingFiles.ts b/libs/application/templates/financial-aid/src/forms/ApplicantSubmittedForm/MissingFiles.ts index cfe796a5429b..2d829cec7d3a 100644 --- a/libs/application/templates/financial-aid/src/forms/ApplicantSubmittedForm/MissingFiles.ts +++ b/libs/application/templates/financial-aid/src/forms/ApplicantSubmittedForm/MissingFiles.ts @@ -1,10 +1,12 @@ import { - buildCustomField, + buildDescriptionField, + buildFileUploadField, buildMultiField, buildSection, buildSubmitField, + buildTextField, } from '@island.is/application/core' -import { Routes } from '../../lib/constants' +import { FILE_SIZE_LIMIT, Routes, UPLOAD_ACCEPT } from '../../lib/constants' import { DefaultEvents } from '@island.is/application/types' import * as m from '../../lib/messages' @@ -17,14 +19,26 @@ export const MissingFiles = buildSection({ title: m.missingFiles.general.pageTitle, description: m.missingFiles.general.description, children: [ - buildCustomField( - { - id: Routes.MISSINGFILES, - title: m.missingFiles.general.pageTitle, - component: 'MissingFiles', - }, - { isSpouse: false }, - ), + buildFileUploadField({ + id: `${Routes.MISSINGFILES}`, + title: m.missingFiles.general.pageTitle, + uploadMultiple: true, + maxSize: FILE_SIZE_LIMIT, + uploadAccept: UPLOAD_ACCEPT, + }), + buildDescriptionField({ + id: `${Routes.MISSINGFILES}.description`, + title: m.missingFiles.comment.title, + marginTop: 4, + titleVariant: 'h3', + }), + buildTextField({ + id: 'fileUploadComment', + title: m.missingFiles.comment.inputTitle, + placeholder: m.missingFiles.comment.inputPlaceholder, + variant: 'textarea', + rows: 6, + }), buildSubmitField({ id: 'missingFilesSubmit', title: '', 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 index 4648c58268e2..8ad68a5fda79 100644 --- a/libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/incomeFileSubSection.ts +++ b/libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/incomeFileSubSection.ts @@ -1,5 +1,4 @@ import { - buildCustomField, buildFileUploadField, buildMultiField, buildSubSection, @@ -23,10 +22,12 @@ export const incomeFilesSubSection = buildSubSection({ title: m.incomeFilesForm.general.pageTitle, description: m.incomeFilesForm.general.descriptionTaxSuccess, children: [ - buildCustomField({ + buildFileUploadField({ id: Routes.INCOMEFILES, - title: m.incomeFilesForm.general.pageTitle, - component: 'IncomeFilesForm', + title: '', + uploadMultiple: true, + maxSize: FILE_SIZE_LIMIT, + uploadAccept: UPLOAD_ACCEPT, }), ], }), 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 index b5e179eacb52..8bca3236f2cf 100644 --- a/libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/taxReturnFilesSubSection.ts +++ b/libs/application/templates/financial-aid/src/forms/ApplicationForm/financesSection/taxReturnFilesSubSection.ts @@ -1,9 +1,11 @@ import { - buildCustomField, + buildDescriptionField, + buildFileUploadField, + buildMultiField, buildSubSection, getValueViaPath, } from '@island.is/application/core' -import { Routes } from '../../../lib/constants' +import { FILE_SIZE_LIMIT, Routes, UPLOAD_ACCEPT } from '../../../lib/constants' import * as m from '../../../lib/messages' export const taxReturnFilesSubSection = buildSubSection({ @@ -21,10 +23,33 @@ export const taxReturnFilesSubSection = buildSubSection({ return personalTaxSuccess === false || personalTaxReturn == null }, children: [ - buildCustomField({ + buildMultiField({ id: Routes.TAXRETURNFILES, title: m.taxReturnForm.general.pageTitle, - component: 'TaxReturnFilesForm', + description: m.taxReturnForm.general.description, + children: [ + buildFileUploadField({ + id: Routes.TAXRETURNFILES, + title: '', + uploadMultiple: true, + maxSize: FILE_SIZE_LIMIT, + uploadAccept: UPLOAD_ACCEPT, + }), + buildDescriptionField({ + id: `${Routes.TAXRETURNFILES}.findTaxReturn`, + title: m.taxReturnForm.instructions.findTaxReturnTitle, + titleVariant: 'h3', + marginTop: 3, + description: m.taxReturnForm.instructions.findTaxReturn, + }), + buildDescriptionField({ + id: `${Routes.TAXRETURNFILES}.findTaxReturn2`, + title: m.taxReturnForm.instructions.findDirectTaxPaymentsTitle, + titleVariant: 'h3', + marginTop: 3, + description: m.taxReturnForm.instructions.findDirectTaxPayments, + }), + ], }), ], }) 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 index 709c9f211acd..a932f53022fb 100644 --- a/libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/childrenFilesSubSection.ts +++ b/libs/application/templates/financial-aid/src/forms/ApplicationForm/personalInterestSection/childrenFilesSubSection.ts @@ -1,10 +1,10 @@ import { - buildCustomField, + buildFileUploadField, buildMultiField, buildSubSection, getValueViaPath, } from '@island.is/application/core' -import { Routes } from '../../../lib/constants' +import { FILE_SIZE_LIMIT, Routes, UPLOAD_ACCEPT } from '../../../lib/constants' import * as m from '../../../lib/messages' import { ApplicantChildCustodyInformation } from '@island.is/application/types' @@ -23,10 +23,12 @@ export const childrenFilesSubSection = buildSubSection({ title: m.childrenFilesForm.general.pageTitle, description: m.childrenFilesForm.general.description, children: [ - buildCustomField({ + buildFileUploadField({ id: Routes.CHILDRENFILES, - title: m.childrenFilesForm.general.pageTitle, - component: 'ChildrenFilesForm', + uploadMultiple: true, + maxSize: FILE_SIZE_LIMIT, + uploadAccept: UPLOAD_ACCEPT, + title: '', }), ], }), diff --git a/libs/application/templates/financial-aid/src/forms/SpouseForm/spouseIncomeFilesSection.ts b/libs/application/templates/financial-aid/src/forms/SpouseForm/spouseIncomeFilesSection.ts index c595cce82ef0..981e0dcef73d 100644 --- a/libs/application/templates/financial-aid/src/forms/SpouseForm/spouseIncomeFilesSection.ts +++ b/libs/application/templates/financial-aid/src/forms/SpouseForm/spouseIncomeFilesSection.ts @@ -1,10 +1,11 @@ import { - buildCustomField, + buildFileUploadField, + buildMultiField, buildSection, getValueViaPath, } from '@island.is/application/core' import { ApproveOptions } from '../../lib/types' -import { Routes } from '../../lib/constants' +import { FILE_SIZE_LIMIT, Routes, UPLOAD_ACCEPT } from '../../lib/constants' import * as m from '../../lib/messages' export const spouseIncomeFilesSection = buildSection({ @@ -15,10 +16,18 @@ export const spouseIncomeFilesSection = buildSection({ id: Routes.SPOUSEINCOMEFILES, title: m.incomeFilesForm.general.sectionTitle, children: [ - buildCustomField({ + buildMultiField({ id: Routes.SPOUSEINCOMEFILES, title: m.incomeFilesForm.general.pageTitle, - component: 'IncomeFilesForm', + description: m.incomeFilesForm.general.description, + children: [ + buildFileUploadField({ + id: Routes.SPOUSEINCOMEFILES, + title: '', + maxSize: FILE_SIZE_LIMIT, + uploadAccept: UPLOAD_ACCEPT, + }), + ], }), ], }) diff --git a/libs/application/templates/financial-aid/src/forms/SpouseForm/spouseTaxReturnFilesSection.ts b/libs/application/templates/financial-aid/src/forms/SpouseForm/spouseTaxReturnFilesSection.ts index 20a6b88a1273..8ee2894ae18d 100644 --- a/libs/application/templates/financial-aid/src/forms/SpouseForm/spouseTaxReturnFilesSection.ts +++ b/libs/application/templates/financial-aid/src/forms/SpouseForm/spouseTaxReturnFilesSection.ts @@ -1,5 +1,4 @@ import { - buildCustomField, buildDescriptionField, buildFileUploadField, buildMultiField, @@ -8,7 +7,6 @@ import { } 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) => { @@ -25,10 +23,38 @@ export const spouseTaxReturnFilesSection = buildSection({ id: Routes.SPOUSETAXRETURNFILES, title: m.taxReturnForm.general.sectionTitle, children: [ - buildCustomField({ + buildMultiField({ id: Routes.SPOUSETAXRETURNFILES, title: m.taxReturnForm.general.pageTitle, - component: 'TaxReturnFilesForm', + children: [ + buildDescriptionField({ + id: `${Routes.SPOUSETAXRETURNFILES}-description`, + title: '', + description: m.taxReturnForm.general.description, + marginBottom: 3, + }), + buildFileUploadField({ + id: Routes.SPOUSETAXRETURNFILES, + title: '', + uploadMultiple: true, + maxSize: FILE_SIZE_LIMIT, + uploadAccept: UPLOAD_ACCEPT, + }), + buildDescriptionField({ + id: `${Routes.SPOUSETAXRETURNFILES}.findTaxReturn`, + title: m.taxReturnForm.instructions.findTaxReturnTitle, + titleVariant: 'h3', + marginTop: 3, + description: m.taxReturnForm.instructions.findTaxReturn, + }), + buildDescriptionField({ + id: `${Routes.SPOUSETAXRETURNFILES}.findTaxReturn2`, + title: m.taxReturnForm.instructions.findDirectTaxPaymentsTitle, + titleVariant: 'h3', + marginTop: 3, + description: m.taxReturnForm.instructions.findDirectTaxPayments, + }), + ], }), ], }) diff --git a/libs/application/templates/financial-aid/src/forms/SpouseSubmittedForm/MissingFiles.ts b/libs/application/templates/financial-aid/src/forms/SpouseSubmittedForm/MissingFiles.ts index 7fc1b6835e58..5aaf6f46699c 100644 --- a/libs/application/templates/financial-aid/src/forms/SpouseSubmittedForm/MissingFiles.ts +++ b/libs/application/templates/financial-aid/src/forms/SpouseSubmittedForm/MissingFiles.ts @@ -1,5 +1,4 @@ import { - buildCustomField, buildDescriptionField, buildFileUploadField, buildMultiField, @@ -20,14 +19,26 @@ export const MissingFiles = buildSection({ title: m.missingFiles.general.pageTitle, description: m.missingFiles.general.description, children: [ - buildCustomField( - { - id: Routes.MISSINGFILES, - title: m.missingFiles.general.pageTitle, - component: 'MissingFiles', - }, - { isSpouse: true }, - ), + buildFileUploadField({ + id: Routes.MISSINGFILESSPOUSE, + title: m.missingFiles.general.pageTitle, + uploadMultiple: true, + maxSize: FILE_SIZE_LIMIT, + uploadAccept: UPLOAD_ACCEPT, + }), + buildDescriptionField({ + id: `${Routes.MISSINGFILESSPOUSE}.description`, + title: m.missingFiles.comment.title, + marginTop: 4, + titleVariant: 'h3', + }), + buildTextField({ + id: 'fileUploadComment', + title: m.missingFiles.comment.inputTitle, + placeholder: m.missingFiles.comment.inputPlaceholder, + variant: 'textarea', + rows: 6, + }), buildSubmitField({ id: 'missingFilesSubmit', title: '', diff --git a/libs/application/templates/financial-aid/src/lib/FinancialAidTemplate.ts b/libs/application/templates/financial-aid/src/lib/FinancialAidTemplate.ts index 0c19192d553d..24cfc382cd6d 100644 --- a/libs/application/templates/financial-aid/src/lib/FinancialAidTemplate.ts +++ b/libs/application/templates/financial-aid/src/lib/FinancialAidTemplate.ts @@ -163,8 +163,8 @@ const FinancialAidTemplate: ApplicationTemplate< target: ApplicationStates.SUBMITTED, cond: hasActiveCurrentApplication, }, + { target: ApplicationStates.SPOUSE }, ], - EDIT: { target: ApplicationStates.SPOUSE }, }, }, [ApplicationStates.SPOUSE]: { diff --git a/libs/application/templates/financial-aid/src/lib/types.ts b/libs/application/templates/financial-aid/src/lib/types.ts index 48f62d65f3ee..b344216b94ff 100644 --- a/libs/application/templates/financial-aid/src/lib/types.ts +++ b/libs/application/templates/financial-aid/src/lib/types.ts @@ -1,6 +1,5 @@ import { ApplicantChildCustodyInformation, - ApplicationAnswerFile, NationalRegistryIndividual, NationalRegistrySpouse, } from '@island.is/application/types' @@ -119,13 +118,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..87d6eaf3d853 100644 --- a/libs/application/templates/financial-aid/src/lib/utils.ts +++ b/libs/application/templates/financial-aid/src/lib/utils.ts @@ -19,7 +19,6 @@ 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 @@ -80,13 +79,6 @@ export function findFamilyStatus( } 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( externalData, diff --git a/libs/application/templates/healthcare-license-certificate/src/lib/messages/payment.ts b/libs/application/templates/healthcare-license-certificate/src/lib/messages/payment.ts index 37e82a8cf3ea..ebfa02b10376 100644 --- a/libs/application/templates/healthcare-license-certificate/src/lib/messages/payment.ts +++ b/libs/application/templates/healthcare-license-certificate/src/lib/messages/payment.ts @@ -13,6 +13,18 @@ export const payment = { description: 'Payment page title', }, }), + paymentChargeOverview: defineMessages({ + forPayment: { + id: 'hlc.application:payment.paymentChargeOverview.forPayment', + defaultMessage: 'Til greiðslu', + description: 'For payment label', + }, + total: { + id: 'hlc.application:payment.paymentChargeOverview.total', + defaultMessage: 'Samtals', + description: 'Total amount label', + }, + }), confirmation: defineMessages({ confirm: { id: 'hlc.application:payment.confirmation.confirm', diff --git a/libs/application/templates/healthcare-work-permit/src/lib/messages/payment.ts b/libs/application/templates/healthcare-work-permit/src/lib/messages/payment.ts index 21fa21aa3ac7..9b6d48dd2766 100644 --- a/libs/application/templates/healthcare-work-permit/src/lib/messages/payment.ts +++ b/libs/application/templates/healthcare-work-permit/src/lib/messages/payment.ts @@ -13,7 +13,18 @@ export const payment = { description: 'Payment page title', }, }), - + paymentChargeOverview: defineMessages({ + forPayment: { + id: 'hwp.application:payment.paymentChargeOverview.forPayment', + defaultMessage: 'Til greiðslu', + description: 'For payment label', + }, + total: { + id: 'hwp.application:payment.paymentChargeOverview.total', + defaultMessage: 'Samtals', + description: 'Total amount label', + }, + }), confirmation: defineMessages({ confirm: { id: 'hwp.application:payment.confirmation.confirm', diff --git a/libs/application/templates/id-card/src/lib/messages/payment.ts b/libs/application/templates/id-card/src/lib/messages/payment.ts index ee649c63d4f6..4af3ecb4bd03 100644 --- a/libs/application/templates/id-card/src/lib/messages/payment.ts +++ b/libs/application/templates/id-card/src/lib/messages/payment.ts @@ -13,6 +13,18 @@ export const payment = { description: 'Payment page title', }, }), + paymentChargeOverview: defineMessages({ + forPayment: { + id: 'id.application:payment.paymentChargeOverview.forPayment', + defaultMessage: 'Til greiðslu', + description: 'For payment label', + }, + total: { + id: 'id.application:payment.paymentChargeOverview.total', + defaultMessage: 'Samtals', + description: 'Total amount label', + }, + }), confirmation: defineMessages({ confirm: { id: 'id.application:payment.confirmation.confirm', diff --git a/libs/application/templates/inheritance-report/src/components/ShareInput/index.tsx b/libs/application/templates/inheritance-report/src/components/ShareInput/index.tsx index ec84cdf5994c..41c1a6c38e95 100644 --- a/libs/application/templates/inheritance-report/src/components/ShareInput/index.tsx +++ b/libs/application/templates/inheritance-report/src/components/ShareInput/index.tsx @@ -156,13 +156,6 @@ export const ShareInput = ({ const validInput = percentageRegex.test(val) const numberValue = valueToNumber(val, ',') - - // allow percentage with up to 4 digits after a decimal point - const regex4dec = /^(\d+(\.\d{0,4})?)?$/ - if (!regex4dec.test(val.replace(',', '.'))) { - return - } - const isRemoving = len < prevLen.current prevLen.current = len diff --git a/libs/application/templates/inheritance-report/src/fields/Overview/OverviewAssets/index.tsx b/libs/application/templates/inheritance-report/src/fields/Overview/OverviewAssets/index.tsx index e00c9255592a..021c0afc6024 100644 --- a/libs/application/templates/inheritance-report/src/fields/Overview/OverviewAssets/index.tsx +++ b/libs/application/templates/inheritance-report/src/fields/Overview/OverviewAssets/index.tsx @@ -11,6 +11,7 @@ import { FC, Fragment } from 'react' import { m } from '../../../lib/messages' import { getBankAccountsDataRow, + getClaimsDataRow, getGunsDataRow, getInventoryDataRow, getMoneyDataRow, @@ -108,12 +109,14 @@ export const OverviewAssets: FC> = ({ }) // Claims + const claimsDataRow = getClaimsDataRow(answers) const claimsDataTotal = formatCurrency( String(getValueViaPath(answers, 'assets.claims.total')) ?? '', ) sections.push({ title: m.claimsTitle, + data: claimsDataRow, total: claimsDataTotal, totalTitle: m.totalValue, }) @@ -172,7 +175,7 @@ export const OverviewAssets: FC> = ({ {formatMessage(title)}
{showTotalFirst && totalRow} - {data?.map((row, index) => ( + {data.map((row, index) => ( ))} {!showTotalFirst && totalRow} diff --git a/libs/application/templates/inheritance-report/src/fields/Overview/OverviewAssets/rows.ts b/libs/application/templates/inheritance-report/src/fields/Overview/OverviewAssets/rows.ts index 7be77ebc83c2..9e52fa24bc07 100644 --- a/libs/application/templates/inheritance-report/src/fields/Overview/OverviewAssets/rows.ts +++ b/libs/application/templates/inheritance-report/src/fields/Overview/OverviewAssets/rows.ts @@ -146,6 +146,38 @@ export const getInventoryDataRow = (answers: FormValue): RowType[] => { ] } +export const getClaimsDataRow = (answers: FormValue): RowType[] => { + const values = (answers.assets as unknown as EstateAssets)?.claims?.data + + const data = (values ?? []).map((item) => { + const propertyValuation = roundedValueToNumber(item.propertyValuation) + + const items: RowItemsType = [ + { + title: m.claimsAmount, + value: formatCurrency(String(valueToNumber(item.value))), + }, + ] + + const deceasedShare = valueToNumber(item.deceasedShare ?? '0') + + if (hasYes(item.deceasedShareEnabled)) { + items.push({ + title: m.deceasedShare, + value: `${String(deceasedShare)}%`, + }) + } + + return { + title: item.description, + value: formatCurrency(String(propertyValuation)), + items, + } + }) + + return data +} + export const getStocksDataRow = (answers: FormValue): RowType[] => { const values = (answers.assets as unknown as EstateAssets)?.stocks?.data @@ -257,7 +289,26 @@ export const getOtherAssetsDataRow = (answers: FormValue): RowType[] => { export const getMoneyDataRow = (answers: FormValue): RowType[] => { const values = (answers.assets as unknown as EstateAssets)?.money + const items: RowItemsType = [] + + const deceasedShare = valueToNumber(values?.deceasedShare ?? '0') + + if (values?.info) { + items.push({ + title: m.moneyText, + value: values?.info, + type: 'info', + }) + } + + if (hasYes(values?.deceasedShareEnabled)) { + items.push({ + title: m.deceasedShare, + value: `${String(deceasedShare)}%`, + }) + } + return [ { title: diff --git a/libs/application/templates/inheritance-report/src/fields/Overview/OverviewAssets/types.ts b/libs/application/templates/inheritance-report/src/fields/Overview/OverviewAssets/types.ts index 2579df688f18..74c2beb377d2 100644 --- a/libs/application/templates/inheritance-report/src/fields/Overview/OverviewAssets/types.ts +++ b/libs/application/templates/inheritance-report/src/fields/Overview/OverviewAssets/types.ts @@ -2,7 +2,7 @@ import { MessageDescriptor } from 'react-intl' export type SectionType = { title: MessageDescriptor | string - data?: RowType[] + data: RowType[] total?: string totalTitle?: MessageDescriptor | string showTotalFirst?: boolean diff --git a/libs/application/templates/inheritance-report/src/fields/OverviewOtherDocuments/index.tsx b/libs/application/templates/inheritance-report/src/fields/OverviewOtherDocuments/index.tsx deleted file mode 100644 index 341e1a8dafcc..000000000000 --- a/libs/application/templates/inheritance-report/src/fields/OverviewOtherDocuments/index.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { FieldBaseProps } from '@island.is/application/types' -import { Text } from '@island.is/island-ui/core' -import { FC } from 'react' - -export const OverviewOtherDocuments: FC< - React.PropsWithChildren -> = ({ application }) => { - const { answers } = application - const files = getValueViaPath<{ key: string; name: string }[]>( - answers, - 'heirsAdditionalInfoFilesOtherDocuments', - ) - return files?.map((file) => {file.name}) -} - -export default OverviewOtherDocuments 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..88ba53971318 100644 --- a/libs/application/templates/inheritance-report/src/fields/ReportFieldsRepeater/index.tsx +++ b/libs/application/templates/inheritance-report/src/fields/ReportFieldsRepeater/index.tsx @@ -153,8 +153,8 @@ export const ReportFieldsRepeater: FC< const faceValue = stockValues?.amount const rateOfExchange = stockValues?.exchangeRateOrInterest - const a = faceValue?.replace(',', '.') || explicitAVal - const b = rateOfExchange?.replace(',', '.') || explicitBVal + const a = faceValue?.replace(/[^\d.]/g, '') || explicitAVal + const b = rateOfExchange?.replace(/[^\d.]/g, '') || explicitBVal const aVal = parseFloat(a) const bVal = parseFloat(b) diff --git a/libs/application/templates/inheritance-report/src/fields/index.ts b/libs/application/templates/inheritance-report/src/fields/index.ts index a96297d0e039..61b6498cd211 100644 --- a/libs/application/templates/inheritance-report/src/fields/index.ts +++ b/libs/application/templates/inheritance-report/src/fields/index.ts @@ -15,4 +15,3 @@ export { CalculateShare } from './CalculateShare' export { OverviewAssets } from './Overview/OverviewAssets' export { OverviewDebts } from './Overview/OverviewDebts' export { ShareInput } from '../components/ShareInput' -export { OverviewOtherDocuments } from './OverviewOtherDocuments' diff --git a/libs/application/templates/inheritance-report/src/forms/sections/assets.ts b/libs/application/templates/inheritance-report/src/forms/sections/assets.ts index 0bebecb2ae92..7946cf6e67aa 100644 --- a/libs/application/templates/inheritance-report/src/forms/sections/assets.ts +++ b/libs/application/templates/inheritance-report/src/forms/sections/assets.ts @@ -502,7 +502,7 @@ export const assets = buildSection({ { title: m.stocksRateOfChange, id: 'exchangeRateOrInterest', - type: 'currency', + type: 'number', required: true, }, { diff --git a/libs/application/templates/inheritance-report/src/forms/sections/heirs.ts b/libs/application/templates/inheritance-report/src/forms/sections/heirs.ts index fe96962a63e4..ae58219582d3 100644 --- a/libs/application/templates/inheritance-report/src/forms/sections/heirs.ts +++ b/libs/application/templates/inheritance-report/src/forms/sections/heirs.ts @@ -174,9 +174,9 @@ export const heirs = buildSection({ id: 'heirsAdditionalInfoPrivateTransferFiles', uploadAccept: '.pdf, .doc, .docx, .jpg, .jpeg, .png, .xls, .xlsx', uploadDescription: m.uploadPrivateTransferDescription, + uploadMultiple: false, title: '', uploadHeader: '', - uploadMultiple: false, }), buildDescriptionField({ id: 'heirsAdditionalInfoFilesOtherDocumentsTitle', @@ -192,7 +192,6 @@ export const heirs = buildSection({ uploadDescription: m.uploadOtherDocumentsDescription, title: '', uploadHeader: '', - uploadMultiple: true, }), ], }), @@ -384,14 +383,18 @@ export const heirs = buildSection({ }), buildDescriptionField({ id: 'heirs_space6', - title: m.fileUploadOtherDocuments, - titleVariant: 'h5', + title: '', space: 'gutter', }), - buildCustomField({ - title: '', - id: 'otherDocs', - component: 'OverviewOtherDocuments', + buildKeyValueField({ + label: m.fileUploadOtherDocuments, + value: ({ answers }) => { + const files = getValueViaPath( + answers, + 'heirsAdditionalInfoFilesOtherDocuments', + ) + return files.map((file: any) => file.name).join(', ') + }, }), buildDescriptionField({ id: 'heirs_space7', diff --git a/libs/application/templates/inheritance-report/src/forms/sections/prepaidInheritance/applicant.ts b/libs/application/templates/inheritance-report/src/forms/sections/prepaidInheritance/applicant.ts index dc53eb07648d..2de640649a88 100644 --- a/libs/application/templates/inheritance-report/src/forms/sections/prepaidInheritance/applicant.ts +++ b/libs/application/templates/inheritance-report/src/forms/sections/prepaidInheritance/applicant.ts @@ -16,7 +16,7 @@ export const prePaidApplicant = buildSection({ buildMultiField({ id: 'prePaidApplicant', title: m.applicantsInfo, - description: m.applicantsPrePaidInfoSubtitle, + description: m.applicantsInfoSubtitle, children: [ buildNationalIdWithNameField({ id: 'prePaidApplicant', 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..af810a90beb5 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 @@ -112,7 +112,6 @@ export const prePaidHeirs = buildSection({ uploadDescription: m.fileUploadOtherDocumentsPrePaidDescription, title: '', uploadHeader: '', - uploadMultiple: true, }), ], }), diff --git a/libs/application/templates/inheritance-report/src/forms/sections/prepaidInheritance/overview.ts b/libs/application/templates/inheritance-report/src/forms/sections/prepaidInheritance/overview.ts index e24ac9a5c7fc..7ef3122938c0 100644 --- a/libs/application/templates/inheritance-report/src/forms/sections/prepaidInheritance/overview.ts +++ b/libs/application/templates/inheritance-report/src/forms/sections/prepaidInheritance/overview.ts @@ -153,14 +153,18 @@ export const prepaidOverview = buildSubSection({ }), buildDescriptionField({ id: 'heirs_space5', - title: m.fileUploadOtherDocumentsPrePaid, - titleVariant: 'h3', + title: '', space: 'gutter', }), - buildCustomField({ - title: '', - id: 'otherDocs', - component: 'OverviewOtherDocuments', + buildKeyValueField({ + label: m.fileUploadOtherDocumentsPrePaid, + value: ({ answers }) => { + const files = getValueViaPath( + answers, + 'heirsAdditionalInfoFilesOtherDocuments', + ) + return files.map((file: any) => file.name).join(', ') + }, }), buildCustomField({ title: '', diff --git a/libs/application/templates/inheritance-report/src/lib/messages.ts b/libs/application/templates/inheritance-report/src/lib/messages.ts index 8880f3a158e1..fc3969ea9920 100644 --- a/libs/application/templates/inheritance-report/src/lib/messages.ts +++ b/libs/application/templates/inheritance-report/src/lib/messages.ts @@ -227,12 +227,6 @@ export const m = defineMessages({ 'Vinsamlegast farðu yfir upplýsingarnar og athugaðu hvort þær séu réttar.', description: '', }, - applicantsPrePaidInfoSubtitle: { - id: 'ir.application:applicantsPrePaidInfoSubtitle', - defaultMessage: - 'Vinsamlegast farðu yfir upplýsingarnar og athugaðu hvort þær séu réttar.', - description: 'Subtitle text shown above prepaid applicant information form', - }, name: { id: 'ir.application:name', defaultMessage: 'Nafn', diff --git a/libs/application/templates/mortgage-certificate/src/dataProviders/index.ts b/libs/application/templates/mortgage-certificate/src/dataProviders/index.ts index e0ccb3b4a9f3..37f69272d6b1 100644 --- a/libs/application/templates/mortgage-certificate/src/dataProviders/index.ts +++ b/libs/application/templates/mortgage-certificate/src/dataProviders/index.ts @@ -1,6 +1,5 @@ import { InstitutionNationalIds, - MockablePaymentCatalogApi, PaymentCatalogApi, } from '@island.is/application/types' @@ -12,11 +11,3 @@ export const SyslumadurPaymentCatalogApi = PaymentCatalogApi.configure({ }, externalDataId: 'payment', }) - -export const MockableSyslumadurPaymentCatalogApi = - MockablePaymentCatalogApi.configure({ - params: { - organizationId: InstitutionNationalIds.SYSLUMENN, - }, - externalDataId: 'payment', - }) diff --git a/libs/application/templates/mortgage-certificate/src/forms/MortgageCertificateForm.ts b/libs/application/templates/mortgage-certificate/src/forms/MortgageCertificateForm.ts index aee592b0f510..6b025791e05a 100644 --- a/libs/application/templates/mortgage-certificate/src/forms/MortgageCertificateForm.ts +++ b/libs/application/templates/mortgage-certificate/src/forms/MortgageCertificateForm.ts @@ -10,6 +10,7 @@ import { confirmation, externalData, overview, + payment, property, propertySearch, } from '../lib/messages' @@ -82,6 +83,8 @@ export const MortgageCertificateForm = ( ], }), buildFormPaymentChargeOverviewSection({ + sectionTitle: payment.general.sectionTitle, + forPaymentLabel: payment.labels.forPayment, getSelectedChargeItems: (application) => getChargeItemCodesAndExtraLabel(application), }), diff --git a/libs/application/templates/mortgage-certificate/src/forms/Prerequisites.ts b/libs/application/templates/mortgage-certificate/src/forms/Prerequisites.ts index 773e7be9b419..b92435ef2f30 100644 --- a/libs/application/templates/mortgage-certificate/src/forms/Prerequisites.ts +++ b/libs/application/templates/mortgage-certificate/src/forms/Prerequisites.ts @@ -11,7 +11,6 @@ import { IdentityApi, UserProfileApi, SyslumadurPaymentCatalogApi, - MockableSyslumadurPaymentCatalogApi, } from '../dataProviders' import { confirmation, externalData, payment, property } from '../lib/messages' import Logo from '../assets/Logo' @@ -61,10 +60,6 @@ export const PrerequisitesForm: Form = buildForm({ provider: SyslumadurPaymentCatalogApi, title: '', }), - buildDataProviderItem({ - provider: MockableSyslumadurPaymentCatalogApi, - title: '', - }), ], }), ], diff --git a/libs/application/templates/mortgage-certificate/src/lib/messages/payment.ts b/libs/application/templates/mortgage-certificate/src/lib/messages/payment.ts index 85141ec5e0a7..3bd2a7300ecc 100644 --- a/libs/application/templates/mortgage-certificate/src/lib/messages/payment.ts +++ b/libs/application/templates/mortgage-certificate/src/lib/messages/payment.ts @@ -8,4 +8,11 @@ export const payment = { description: 'Payment section title', }, }), + labels: defineMessages({ + forPayment: { + id: 'mc.application:payment.labels.forPayment', + defaultMessage: 'Til greiðslu', + description: 'Payment for payment label', + }, + }), } diff --git a/libs/application/templates/mortgage-certificate/src/lib/mortgageCertificateTemplate.ts b/libs/application/templates/mortgage-certificate/src/lib/mortgageCertificateTemplate.ts index 34578e7e3b5e..12fcde99929c 100644 --- a/libs/application/templates/mortgage-certificate/src/lib/mortgageCertificateTemplate.ts +++ b/libs/application/templates/mortgage-certificate/src/lib/mortgageCertificateTemplate.ts @@ -21,7 +21,6 @@ import { IdentityApi, UserProfileApi, SyslumadurPaymentCatalogApi, - MockableSyslumadurPaymentCatalogApi, } from '../dataProviders' import { AuthDelegationType } from '@island.is/shared/types' import { buildPaymentState } from '@island.is/application/utils' @@ -85,12 +84,7 @@ const template: ApplicationTemplate< write: 'all', read: 'all', delete: true, - api: [ - IdentityApi, - UserProfileApi, - SyslumadurPaymentCatalogApi, - MockableSyslumadurPaymentCatalogApi, - ], + api: [IdentityApi, UserProfileApi, SyslumadurPaymentCatalogApi], }, ], }, diff --git a/libs/application/templates/parental-leave/src/lib/parentalLeaveUtils.ts b/libs/application/templates/parental-leave/src/lib/parentalLeaveUtils.ts index 277c1b1cfbd6..c929e62bd2ec 100644 --- a/libs/application/templates/parental-leave/src/lib/parentalLeaveUtils.ts +++ b/libs/application/templates/parental-leave/src/lib/parentalLeaveUtils.ts @@ -1429,17 +1429,11 @@ export const calculateEndDateForPeriodWithStartAndLength = ( let endDate = addDays(lastMonthBeforeEndDate, daysToAdd - 1) const daysInMonth = getDaysInMonth(lastMonthBeforeEndDate) + // If startDay is first day of the month and daysToAdd = 0 if (daysToAdd === 0) { if (start.getDate() === 31) { endDate = addDays(endDate, 1) } - if ( - start.getDate() > 28 && - daysInMonth === 28 && - endDate.getDate() !== 28 - ) { - endDate = addDays(endDate, 1) - } return endDate } diff --git a/libs/application/templates/social-insurance-administration/income-plan/src/forms/IncomePlanForm.ts b/libs/application/templates/social-insurance-administration/income-plan/src/forms/IncomePlanForm.ts index d7d70518d272..1ca22ef46eba 100644 --- a/libs/application/templates/social-insurance-administration/income-plan/src/forms/IncomePlanForm.ts +++ b/libs/application/templates/social-insurance-administration/income-plan/src/forms/IncomePlanForm.ts @@ -69,9 +69,8 @@ export const IncomePlanForm: Form = buildForm({ id: 'incomePlanTable', title: incomePlanFormMessage.info.section, description: (application: Application) => { - const { latestIncomePlan } = getApplicationExternalData( - application.externalData, - ) + const { incomePlanConditions, latestIncomePlan } = + getApplicationExternalData(application.externalData) const hasLatestIncomePlan = !isEmpty(latestIncomePlan) const baseMessage = hasLatestIncomePlan ? incomePlanFormMessage.incomePlan @@ -81,7 +80,7 @@ export const IncomePlanForm: Form = buildForm({ return { ...baseMessage, values: { - incomePlanYear: latestIncomePlan.year, + incomePlanYear: incomePlanConditions.incomePlanYear, }, } }, diff --git a/libs/application/templates/social-insurance-administration/income-plan/src/lib/constants.ts b/libs/application/templates/social-insurance-administration/income-plan/src/lib/constants.ts index 83ac9bea41be..a87cc7cbb9f7 100644 --- a/libs/application/templates/social-insurance-administration/income-plan/src/lib/constants.ts +++ b/libs/application/templates/social-insurance-administration/income-plan/src/lib/constants.ts @@ -10,7 +10,6 @@ export const DIVIDENDS_IN_FOREIGN_BANKS = export const ISK = 'IKR' export const INCOME = 'Atvinnutekjur' export const INCOME_PLANS_CLOSED = 'INCOME_PLANS_CLOSED' -export const NO_ACTIVE_APPLICATIONS = 'NO_ACTIVE_APPLICATIONS' export enum RatioType { YEARLY = 'yearly', diff --git a/libs/application/templates/social-insurance-administration/income-plan/src/lib/dataSchema.ts b/libs/application/templates/social-insurance-administration/income-plan/src/lib/dataSchema.ts index bf20588b6047..b2a8d5890a4d 100644 --- a/libs/application/templates/social-insurance-administration/income-plan/src/lib/dataSchema.ts +++ b/libs/application/templates/social-insurance-administration/income-plan/src/lib/dataSchema.ts @@ -1,3 +1,4 @@ +import { coreErrorMessages } from '@island.is/application/core' import { z } from 'zod' import { INCOME, ISK, RatioType, YES } from './constants' import { errorMessages } from './messages' @@ -31,7 +32,9 @@ export const dataSchema = z.object({ }) .refine( ({ income, incomePerYear }) => - income === RatioType.YEARLY ? !!incomePerYear : true, + income === RatioType.YEARLY + ? !!incomePerYear && Number(incomePerYear) > 0 + : true, { path: ['incomePerYear'], }, @@ -51,7 +54,7 @@ export const dataSchema = z.object({ return income === RatioType.MONTHLY && currency === ISK && unevenAndEmploymentIncome - ? !!equalIncomePerMonth + ? !!equalIncomePerMonth && Number(equalIncomePerMonth) > 0 : true }, { @@ -73,7 +76,8 @@ export const dataSchema = z.object({ return income === RatioType.MONTHLY && currency !== ISK && unevenAndEmploymentIncome - ? !!equalForeignIncomePerMonth + ? !!equalForeignIncomePerMonth && + Number(equalForeignIncomePerMonth) > 0 : true }, { @@ -104,7 +108,39 @@ export const dataSchema = z.object({ path: ['incomePerYear'], params: errorMessages.monthsRequired, }, - ), + ) + .superRefine((incomePlanTable, ctx) => { + if ( + incomePlanTable.income === RatioType.MONTHLY && + incomePlanTable?.incomeCategory === INCOME && + incomePlanTable.unevenIncomePerYear?.[0] === YES + ) { + const months = { + january: incomePlanTable.january, + february: incomePlanTable.february, + march: incomePlanTable.march, + april: incomePlanTable.april, + may: incomePlanTable.may, + june: incomePlanTable.june, + july: incomePlanTable.july, + august: incomePlanTable.august, + september: incomePlanTable.september, + october: incomePlanTable.october, + november: incomePlanTable.november, + december: incomePlanTable.december, + } + + Object.entries(months).forEach(([key, value]) => { + if (value && !(Number(value) > 0)) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: [key], + params: coreErrorMessages.defaultError, + }) + } + }) + } + }), ) .refine((i) => i === undefined || i.length > 0, { params: errorMessages.incomePlanRequired, diff --git a/libs/application/templates/social-insurance-administration/income-plan/src/lib/incomePlanUtils.ts b/libs/application/templates/social-insurance-administration/income-plan/src/lib/incomePlanUtils.ts index a01e51b430bb..a9a021aea627 100644 --- a/libs/application/templates/social-insurance-administration/income-plan/src/lib/incomePlanUtils.ts +++ b/libs/application/templates/social-insurance-administration/income-plan/src/lib/incomePlanUtils.ts @@ -8,7 +8,7 @@ import { LatestIncomePlan, WithholdingTax, } from '../types' -import { NO_ACTIVE_APPLICATIONS, INCOME_PLANS_CLOSED } from './constants' +import { INCOME_PLANS_CLOSED } from './constants' import { incomePlanFormMessage } from './messages' export const getApplicationExternalData = ( @@ -133,9 +133,8 @@ export const isEligible = (externalData: ExternalData): boolean => { export const eligibleText = (externalData: ExternalData) => { const { isEligible } = getApplicationExternalData(externalData) + return isEligible.reasonCode === INCOME_PLANS_CLOSED ? incomePlanFormMessage.pre.isNotEligibleClosedDescription - : isEligible.reasonCode === NO_ACTIVE_APPLICATIONS - ? incomePlanFormMessage.pre.isNotEligibleNoActiveApplicationDescription : incomePlanFormMessage.pre.isNotEligibleDescription } 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 cc32fe4202c6..1fd0c4c269c2 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 @@ -24,17 +24,10 @@ export const incomePlanFormMessage: MessageDir = { isNotEligibleDescription: { id: 'ip.application:is.not.eligible.description#markdown', defaultMessage: - 'Ástæðan fyrir því er eftirfarandi:\n* Það eru innan við 10 dagar síðan síðasta tekjuáætlun þín var tekin í vinnslu hjá Tryggingastofnun.\n\nEf þú telur það ekki eiga við um þig, vinsamlegast hafið samband við [tr@tr.is](mailto:tr@tr.is)', + '* Það eru innan við 10 dagar síðan síðasta tekjuáætlun þín var tekin í vinnslu hjá Tryggingastofnun.\n\nEf þú telur það ekki eiga við um þig, vinsamlegast hafið samband við [tr@tr.is](mailto:tr@tr.is)', description: '* It has been less than 10 days since your last income plan was processed by the Social Insurance Administration.\n\nIf you do not think that apply to you, please contact [tr@tr.is](mailto:tr @tr.is)', }, - isNotEligibleNoActiveApplicationDescription: { - id: 'ip.application:is.not.eligible.no.active.application.description#markdown', - defaultMessage: - 'Ástæðan fyrir því er eftirfarandi:\n* Þú ert ekki með virka umsókn hjá Tryggingastofnun.\n\nEf þú telur það ekki eiga við um þig, vinsamlegast hafið samband við [tr@tr.is](mailto:tr@tr.is)', - description: - 'The reason for this is the following:\n* You do not have any active applications at the Social Insurance Administration.\n\nIf you do not think that apply to you, please contact [tr@tr.is](mailto:tr @tr.is)', - }, isNotEligibleClosedDescription: { id: 'ip.application:is.not.eligible.closed.description#markdown', defaultMessage: diff --git a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/dataProviders/index.ts b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/dataProviders/index.ts index b59c159b6b5b..b48b425c53ce 100644 --- a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/dataProviders/index.ts +++ b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/dataProviders/index.ts @@ -1,7 +1,6 @@ import { defineTemplateApi, InstitutionNationalIds, - MockablePaymentCatalogApi, PaymentCatalogApi, } from '@island.is/application/types' @@ -14,14 +13,6 @@ export const SamgongustofaPaymentCatalogApi = PaymentCatalogApi.configure({ externalDataId: 'payment', }) -export const MockableSamgongustofaPaymentCatalogApi = - MockablePaymentCatalogApi.configure({ - params: { - organizationId: InstitutionNationalIds.SAMGONGUSTOFA, - }, - externalDataId: 'payment', - }) - export const CurrentVehiclesApi = defineTemplateApi({ action: 'getCurrentVehiclesWithOwnerchangeChecks', externalDataId: 'currentVehicleList', diff --git a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/ApplicationStatus/index.tsx b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/ApplicationStatus/index.tsx index 0d3b7aa5e325..299592820227 100644 --- a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/ApplicationStatus/index.tsx +++ b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/ApplicationStatus/index.tsx @@ -8,7 +8,7 @@ import { useLocale } from '@island.is/localization' import { FC } from 'react' import { review } from '../../lib/messages' import { ReviewScreenProps } from '../../shared' -import { getReviewSteps, canReviewerApprove } from '../../utils' +import { getReviewSteps, hasReviewerApproved } from '../../utils' import { MessageWithLinkButtonFormField } from '@island.is/application/ui-fields' import { StatusStep } from './StatusStep' import { coreMessages } from '@island.is/application/core' @@ -21,7 +21,7 @@ export const ApplicationStatus: FC< const steps = getReviewSteps(application) - const showReviewButton = canReviewerApprove( + const showReviewButton = !hasReviewerApproved( reviewerNationalId, application.answers, ) diff --git a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/Overview/index.tsx b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/Overview/index.tsx index 8be2109222a1..67abcbd743e2 100644 --- a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/Overview/index.tsx +++ b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/Overview/index.tsx @@ -13,9 +13,8 @@ import { overview, review, error as errorMsg } from '../../lib/messages' import { VehicleSection, OwnerSection, CoOwnersSection } from './sections' import { getApproveAnswers, - canReviewerApprove, + hasReviewerApproved, isLastReviewer, - canReviewerReApprove, } from '../../utils' import { RejectConfirmationModal } from './RejectConfirmationModal' import { @@ -23,8 +22,6 @@ import { SUBMIT_APPLICATION, } from '@island.is/application/graphql' import { useLazyQuery, useMutation } from '@apollo/client' -import { States } from '../../lib/constants' -import { ValidationErrorMessages } from '../ValidationErrorMessages' export const Overview: FC< React.PropsWithChildren @@ -45,14 +42,11 @@ export const Overview: FC< const [submitApplication, { error }] = useMutation(SUBMIT_APPLICATION, { onError: (e) => { console.error(e, e.message) - setButtonLoading(false) return }, }) - const [buttonLoading, setButtonLoading] = useState(false) - const [shouldLoadValidation, setShouldLoadValidation] = useState(false) - const [validationErrorFound, setValidationErrorFound] = useState(false) + const [loading, setLoading] = useState(false) const doApproveAndSubmit = async () => { // Need to get updated application answers, in case any other reviewer has approved @@ -113,11 +107,11 @@ export const Overview: FC< }) if (resSubmit?.data) { - setButtonLoading(false) + setLoading(false) setStep && setStep('conclusion') } } else { - setButtonLoading(false) + setLoading(false) setStep && setStep('conclusion') } } @@ -133,8 +127,7 @@ export const Overview: FC< } const onApproveButtonClick = async () => { - setButtonLoading(true) - setShouldLoadValidation(true) + setLoading(true) await doApproveAndSubmit() } @@ -151,14 +144,7 @@ export const Overview: FC< - {!buttonLoading && shouldLoadValidation && ( - - )} - - {!validationErrorFound && error && ( + {error && ( @@ -170,7 +156,7 @@ export const Overview: FC< - {canReviewerApprove(reviewerNationalId, application.answers) && ( + {!hasReviewerApproved(reviewerNationalId, application.answers) && ( )} - {canReviewerReApprove(reviewerNationalId, application.answers) && - application.state !== States.COMPLETED && ( - - - - )} diff --git a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/ValidationErrorMessages/index.tsx b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/ValidationErrorMessages/index.tsx index bb20ad4ad494..245df4cdeeee 100644 --- a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/ValidationErrorMessages/index.tsx +++ b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/ValidationErrorMessages/index.tsx @@ -2,25 +2,16 @@ import { gql, useQuery } from '@apollo/client' import { OwnerChangeValidationMessage } from '@island.is/api/schema' import { getValueViaPath } from '@island.is/application/core' import { FieldBaseProps } from '@island.is/application/types' -import { - AlertMessage, - Box, - Bullet, - BulletList, -} from '@island.is/island-ui/core' +import { AlertMessage, Box, Text } from '@island.is/island-ui/core' import { useLocale } from '@island.is/localization' -import { Dispatch, FC, SetStateAction, useEffect } from 'react' +import { FC, useEffect } from 'react' import { ChangeCoOwnerOfVehicleAnswers } from '../..' import { VALIDATE_VEHICLE_CO_OWNER_CHANGE } from '../../graphql/queries' import { applicationCheck } from '../../lib/messages' -interface Props { - setValidationErrorFound?: Dispatch> -} - export const ValidationErrorMessages: FC< - React.PropsWithChildren -> = ({ setValidationErrorFound, ...props }) => { + React.PropsWithChildren +> = (props) => { const { application, setFieldLoadingState } = props const { formatMessage } = useLocale() @@ -56,18 +47,12 @@ export const ValidationErrorMessages: FC< })), }, }, - onCompleted: (data) => { - if (data?.vehicleCoOwnerChangeValidation?.hasError) { - setValidationErrorFound?.(true) - } - }, - fetchPolicy: 'no-cache', }, ) useEffect(() => { setFieldLoadingState?.(loading) - }, [loading, setFieldLoadingState]) + }, [loading]) return data?.vehicleCoOwnerChangeValidation?.hasError && data.vehicleCoOwnerChangeValidation.errorMessages.length > 0 ? ( @@ -77,7 +62,7 @@ export const ValidationErrorMessages: FC< title={formatMessage(applicationCheck.validation.alertTitle)} message={ - +
    {data.vehicleCoOwnerChangeValidation.errorMessages.map( (error: OwnerChangeValidationMessage) => { const message = formatMessage( @@ -95,13 +80,15 @@ export const ValidationErrorMessages: FC< error?.errorNo return ( - - {message || defaultMessage || fallbackMessage} - +
  • + + {message || defaultMessage || fallbackMessage} + +
  • ) }, )} - +
} /> diff --git a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/VehiclesField/VehicleRadioField.tsx b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/VehiclesField/VehicleRadioField.tsx new file mode 100644 index 000000000000..72ac2c0a3258 --- /dev/null +++ b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/VehiclesField/VehicleRadioField.tsx @@ -0,0 +1,142 @@ +import { + Box, + Text, + AlertMessage, + BulletList, + Bullet, + InputError, +} from '@island.is/island-ui/core' +import { FC, useState } from 'react' +import { VehiclesCurrentVehicleWithOwnerchangeChecks } from '../../shared' +import { RadioController } from '@island.is/shared/form-fields' +import { useFormContext } from 'react-hook-form' +import { getValueViaPath } from '@island.is/application/core' +import { FieldBaseProps } from '@island.is/application/types' +import { useLocale } from '@island.is/localization' +import { applicationCheck, information, error } from '../../lib/messages' + +interface Option { + value: string + label: React.ReactNode + disabled?: boolean +} + +interface VehicleSearchFieldProps { + currentVehicleList: VehiclesCurrentVehicleWithOwnerchangeChecks[] +} + +export const VehicleRadioField: FC< + React.PropsWithChildren +> = ({ currentVehicleList, application, errors }) => { + const { formatMessage } = useLocale() + const { setValue } = useFormContext() + + const [plate, setPlate] = useState( + getValueViaPath(application.answers, 'pickVehicle.plate', '') as string, + ) + + const onRadioControllerSelect = (s: string) => { + const currentVehicle = currentVehicleList[parseInt(s, 10)] + const permno = currentVehicle.permno || '' + + setPlate(permno) + setValue('pickVehicle.plate', permno) + setValue('pickVehicle.color', currentVehicle.color || undefined) + setValue('pickVehicle.type', currentVehicle.make || undefined) + setValue('vehicleMileage.requireMileage', currentVehicle?.requireMileage) + setValue('vehicleMileage.mileageReading', currentVehicle?.mileageReading) + if (permno) setValue('vehicleInfo.plate', permno) + if (permno) setValue('vehicleInfo.type', currentVehicle.make) + } + + const vehicleOptions = ( + vehicles: VehiclesCurrentVehicleWithOwnerchangeChecks[], + ) => { + const options = [] as Option[] + + for (const [index, vehicle] of vehicles.entries()) { + const disabled = + !vehicle.isDebtLess || !!vehicle.validationErrorMessages?.length + options.push({ + value: `${index}`, + label: ( + + + + {vehicle.make} + + + {vehicle.color} - {vehicle.permno} + + + {disabled && ( + + + + {!vehicle.isDebtLess && ( + + {formatMessage( + information.labels.pickVehicle.isNotDebtLessTag, + )} + + )} + {!!vehicle.validationErrorMessages?.length && + vehicle.validationErrorMessages?.map((error) => { + const message = formatMessage( + getValueViaPath( + applicationCheck.validation, + error.errorNo || '', + ), + ) + const defaultMessage = error.defaultMessage + const fallbackMessage = + formatMessage( + applicationCheck.validation + .fallbackErrorMessage, + ) + + ' - ' + + error.errorNo + + return ( + + {message || defaultMessage || fallbackMessage} + + ) + })} + + + } + /> + + )} + + ), + disabled: disabled, + }) + } + return options + } + + return ( +
+ + {plate.length === 0 && (errors as any)?.pickVehicle && ( + + )} +
+ ) +} diff --git a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/VehiclesField/index.tsx b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/VehiclesField/index.tsx index 11c7b5ca4819..3adad308746b 100644 --- a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/VehiclesField/index.tsx +++ b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/fields/VehiclesField/index.tsx @@ -6,14 +6,12 @@ import { import { Box } from '@island.is/island-ui/core' import { FC, useCallback, useEffect } from 'react' import { CurrentVehiclesAndRecords } from '../../shared' +import { VehicleRadioField } from './VehicleRadioField' import { useFormContext } from 'react-hook-form' import { ApolloQueryResult, useMutation } from '@apollo/client' import { UPDATE_APPLICATION } from '@island.is/application/graphql' import { useLocale } from '@island.is/localization' -import { - FindVehicleFormField, - VehicleRadioFormField, -} from '@island.is/application/ui-fields' +import { FindVehicleFormField } from '@island.is/application/ui-fields' import { applicationCheck, error, information } from '../../lib/messages' import { useLazyVehicleDetails } from '../../hooks/useLazyVehicleDetails' import { VehicleSelectField } from './VehicleSelectField' @@ -94,27 +92,9 @@ export const VehiclesField: FC> = ( {...props} /> ) : ( - )} diff --git a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/forms/ChangeCoOwnerOfVehicleForm/InformationSection/vehicleSubSection.ts b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/forms/ChangeCoOwnerOfVehicleForm/InformationSection/vehicleSubSection.ts index 32932c24bd91..efac7e83c97d 100644 --- a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/forms/ChangeCoOwnerOfVehicleForm/InformationSection/vehicleSubSection.ts +++ b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/forms/ChangeCoOwnerOfVehicleForm/InformationSection/vehicleSubSection.ts @@ -3,9 +3,11 @@ import { buildMultiField, buildTextField, buildSubSection, + buildCustomField, buildHiddenInput, } from '@island.is/application/core' import { information } from '../../../lib/messages' +import { VehiclesCurrentVehicle } from '../../../shared' import { getSelectedVehicle } from '../../../utils' export const vehicleSubSection = buildSubSection({ @@ -27,8 +29,8 @@ export const vehicleSubSection = buildSubSection({ const vehicle = getSelectedVehicle( application.externalData, application.answers, - ) - return vehicle?.permno + ) as VehiclesCurrentVehicle + return vehicle.permno }, }), buildTextField({ @@ -41,8 +43,8 @@ export const vehicleSubSection = buildSubSection({ const vehicle = getSelectedVehicle( application.externalData, application.answers, - ) - return vehicle?.make + ) as VehiclesCurrentVehicle + return vehicle.make }, }), buildHiddenInput({ @@ -51,8 +53,8 @@ export const vehicleSubSection = buildSubSection({ const vehicle = getSelectedVehicle( application.externalData, application.answers, - ) - return vehicle?.requireMileage || false + ) as VehiclesCurrentVehicle + return vehicle.requireMileage || false }, }), buildHiddenInput({ @@ -61,8 +63,8 @@ export const vehicleSubSection = buildSubSection({ const vehicle = getSelectedVehicle( application.externalData, application.answers, - ) - return vehicle?.mileageReading || '' + ) as VehiclesCurrentVehicle + return vehicle.mileageReading || '' }, }), buildTextField({ @@ -71,15 +73,18 @@ export const vehicleSubSection = buildSubSection({ width: 'full', variant: 'number', condition: (answers, externalData) => { - const vehicle = getSelectedVehicle(externalData, answers) + const vehicle = getSelectedVehicle( + externalData, + answers, + ) as VehiclesCurrentVehicle return vehicle?.requireMileage || false }, placeholder(application) { const vehicle = getSelectedVehicle( application.externalData, application.answers, - ) - return vehicle?.mileageReading + ) as VehiclesCurrentVehicle + return vehicle.mileageReading ? `Síðasta skráning ${vehicle.mileageReading} Km` : '' }, diff --git a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/forms/ChangeCoOwnerOfVehicleForm/externalDataSection.ts b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/forms/ChangeCoOwnerOfVehicleForm/externalDataSection.ts index ccd5f7be745b..2883e932a9f9 100644 --- a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/forms/ChangeCoOwnerOfVehicleForm/externalDataSection.ts +++ b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/forms/ChangeCoOwnerOfVehicleForm/externalDataSection.ts @@ -9,7 +9,6 @@ import { UserProfileApi, SamgongustofaPaymentCatalogApi, CurrentVehiclesApi, - MockableSamgongustofaPaymentCatalogApi, } from '../../dataProviders' export const externalDataSection = buildSection({ @@ -42,10 +41,6 @@ export const externalDataSection = buildSection({ title: externalData.payment.title, subTitle: externalData.payment.subTitle, }), - buildDataProviderItem({ - provider: MockableSamgongustofaPaymentCatalogApi, - title: '', - }), ], }), ], diff --git a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/lib/ChangeCoOwnerOfVehicleTemplate.ts b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/lib/ChangeCoOwnerOfVehicleTemplate.ts index 714000aa6b9a..2837bd7bb640 100644 --- a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/lib/ChangeCoOwnerOfVehicleTemplate.ts +++ b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/lib/ChangeCoOwnerOfVehicleTemplate.ts @@ -30,13 +30,12 @@ import { UserProfileApi, SamgongustofaPaymentCatalogApi, CurrentVehiclesApi, - MockableSamgongustofaPaymentCatalogApi, } from '../dataProviders' import { application as applicationMessage } from './messages' import { assign } from 'xstate' import set from 'lodash/set' import { AuthDelegationType } from '@island.is/shared/types' -import { getChargeItemCodes, canReviewerApprove } from '../utils' +import { getChargeItemCodes, hasReviewerApproved } from '../utils' import { ApiScope } from '@island.is/auth/scopes' import { buildPaymentState } from '@island.is/application/utils' import { getExtraData } from '../utils/getChargeItemCodes' @@ -66,7 +65,7 @@ const reviewStatePendingAction = ( role: string, nationalId: string, ): PendingAction => { - if (nationalId && canReviewerApprove(nationalId, application.answers)) { + if (nationalId && !hasReviewerApproved(nationalId, application.answers)) { return { title: corePendingActionMessages.waitingForReviewTitle, content: corePendingActionMessages.youNeedToReviewDescription, @@ -146,7 +145,6 @@ const template: ApplicationTemplate< IdentityApi, UserProfileApi, SamgongustofaPaymentCatalogApi, - MockableSamgongustofaPaymentCatalogApi, CurrentVehiclesApi, ], }, diff --git a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/lib/messages/review.ts b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/lib/messages/review.ts index 464483f7f07d..c777091d9f69 100644 --- a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/lib/messages/review.ts +++ b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/lib/messages/review.ts @@ -110,13 +110,8 @@ export const review = { }, approve: { id: 'ta.ccov.application:review.buttons.approve', - defaultMessage: 'Samþykkja', + defaultMessage: `Samþykkja`, description: 'Approve button in review process', }, - tryAgain: { - id: 'ta.ccov.application:review.buttons.tryAgain', - defaultMessage: 'Reyna aftur', - description: 'Try again button in review process', - }, }), } diff --git a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/utils/canReviewerApprove.ts b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/utils/canReviewerApprove.ts deleted file mode 100644 index 4e925416b81f..000000000000 --- a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/utils/canReviewerApprove.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { getValueViaPath } from '@island.is/application/core' -import { CoOwnersInformation, OwnerCoOwnersInformation } from '../shared' -import { applicationHasPendingApproval } from './isLastReviewer' - -// Function to check if the reviewer is authorized to approve and hasn't done that yet -export const canReviewerApprove = ( - reviewerNationalId: string, - answers: FormValue, -): boolean => { - // Check if reviewer is old co-owner and has not approved - const oldCoOwners = getValueViaPath( - answers, - 'ownerCoOwners', - [], - ) as OwnerCoOwnersInformation[] - if ( - oldCoOwners.some( - ({ nationalId, approved }) => - nationalId === reviewerNationalId && !approved, - ) - ) { - return true - } - - // Check if reviewer is new co-owner and has not approved - const newCoOwners = ( - getValueViaPath(answers, 'coOwners', []) as CoOwnersInformation[] - ).filter(({ wasRemoved }) => wasRemoved !== 'true') - if ( - newCoOwners.some( - ({ nationalId, approved }) => - nationalId === reviewerNationalId && !approved, - ) - ) { - return true - } - - return false -} - -// Special case to allow any reviewer to trigger an external API call to complete co-owner change -// Necessary when approve is updated in answers, but application is still stuck in REVIEW state -// then any user can try to 'push' the application to the next state -export const canReviewerReApprove = ( - reviewerNationalId: string, - answers: FormValue, -): boolean => { - const oldCoOwners = getValueViaPath( - answers, - 'ownerCoOwners', - [], - ) as OwnerCoOwnersInformation[] - const newCoOwners = ( - getValueViaPath(answers, 'coOwners', []) as CoOwnersInformation[] - ).filter(({ wasRemoved }) => wasRemoved !== 'true') - - const isReviewerAuthorized = [ - oldCoOwners.some(({ nationalId }) => nationalId === reviewerNationalId), - newCoOwners.some(({ nationalId }) => nationalId === reviewerNationalId), - ].some(Boolean) - - // Check if the reviewer is authorized and if all required approvals have been completed - return isReviewerAuthorized && !applicationHasPendingApproval(answers) -} diff --git a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/utils/getSelectedVehicle.ts b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/utils/getSelectedVehicle.ts index 38e162f6a245..a13ea829dc3c 100644 --- a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/utils/getSelectedVehicle.ts +++ b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/utils/getSelectedVehicle.ts @@ -5,7 +5,7 @@ import { CurrentVehiclesAndRecords, VehiclesCurrentVehicle } from '../shared' export const getSelectedVehicle = ( externalData: ExternalData, answers: FormValue, -): VehiclesCurrentVehicle | undefined => { +): VehiclesCurrentVehicle => { if (answers.findVehicle) { const vehicle = getValueViaPath( answers, diff --git a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/utils/hasReviewerApproved.ts b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/utils/hasReviewerApproved.ts new file mode 100644 index 000000000000..164158ecdfbb --- /dev/null +++ b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/utils/hasReviewerApproved.ts @@ -0,0 +1,38 @@ +import { FormValue } from '@island.is/application/types' +import { getValueViaPath } from '@island.is/application/core' +import { CoOwnersInformation, OwnerCoOwnersInformation } from '../shared' + +export const hasReviewerApproved = ( + reviewerNationalId: string, + answers: FormValue, +) => { + // Check if reviewer is old co-owner and has not approved + const ownerCoOwners = getValueViaPath( + answers, + 'ownerCoOwners', + [], + ) as OwnerCoOwnersInformation[] + const ownerCoOwner = ownerCoOwners.find( + (x) => x.nationalId === reviewerNationalId, + ) + if (ownerCoOwner) { + const hasApproved = ownerCoOwner?.approved || false + if (!hasApproved) return false + } + + // Check if reviewer is new co-owner and has not approved + const coOwners = getValueViaPath( + answers, + 'coOwners', + [], + ) as CoOwnersInformation[] + const coOwner = coOwners + .filter(({ wasRemoved }) => wasRemoved !== 'true') + .find((x) => x.nationalId === reviewerNationalId) + if (coOwner) { + const hasApproved = coOwner?.approved || false + if (!hasApproved) return false + } + + return true +} diff --git a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/utils/index.ts b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/utils/index.ts index b446cdce56ca..6aa4812ab24e 100644 --- a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/utils/index.ts +++ b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/utils/index.ts @@ -1,6 +1,6 @@ export { getChargeItemCodes } from './getChargeItemCodes' export { getSelectedVehicle } from './getSelectedVehicle' -export { canReviewerApprove, canReviewerReApprove } from './canReviewerApprove' +export { hasReviewerApproved } from './hasReviewerApproved' export { getReviewSteps } from './getReviewSteps' export { isLastReviewer } from './isLastReviewer' export { getApproveAnswers } from './getApproveAnswers' diff --git a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/utils/isLastReviewer.ts b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/utils/isLastReviewer.ts index 9ab1916cd600..cb0199e9c966 100644 --- a/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/utils/isLastReviewer.ts +++ b/libs/application/templates/transport-authority/change-co-owner-of-vehicle/src/utils/isLastReviewer.ts @@ -2,50 +2,39 @@ import { getValueViaPath } from '@island.is/application/core' import { FormValue } from '@island.is/application/types' import { OwnerCoOwnersInformation, CoOwnersInformation } from '../shared' -// Function to check if an application has pending approval -export const applicationHasPendingApproval = ( +export const isLastReviewer = ( + reviewerNationalId: string, answers: FormValue, - excludeNationalId?: string, -): boolean => { - // Check if any old co-owners have not approved - const oldCoOwners = getValueViaPath( +) => { + // First check if any reviewer that is not the current user has not approved + const ownerCoOwners = getValueViaPath( answers, 'ownerCoOwners', [], ) as OwnerCoOwnersInformation[] - if ( - oldCoOwners.some( - ({ nationalId, approved }) => - (!excludeNationalId || nationalId !== excludeNationalId) && !approved, + const approvedOwnerCoOwner = ownerCoOwners.find((ownerCoOwner) => { + return ( + ownerCoOwner.nationalId !== reviewerNationalId && !ownerCoOwner.approved ) - ) { - return true + }) + if (approvedOwnerCoOwner) { + return false } - // Check if any new co-owners have not approved - const newCoOwners = ( - getValueViaPath(answers, 'coOwners', []) as CoOwnersInformation[] - ).filter(({ wasRemoved }) => wasRemoved !== 'true') - if ( - newCoOwners.some( - ({ nationalId, approved }) => - (!excludeNationalId || nationalId !== excludeNationalId) && !approved, + const coOwners = getValueViaPath( + answers, + 'coOwners', + [], + ) as CoOwnersInformation[] + const approvedCoOwner = coOwners + .filter(({ wasRemoved }) => wasRemoved !== 'true') + .find( + (coOwner) => + coOwner.nationalId !== reviewerNationalId && !coOwner.approved, ) - ) { - return true + if (approvedCoOwner) { + return false } - return false -} - -// Function to check if the current reviewer is the last one who needs to approve -export const isLastReviewer = ( - reviewerNationalId: string, - answers: FormValue, -): boolean => { - // If there are pending approvals (excluding current reviewer), then he is not the last reviewer - if (applicationHasPendingApproval(answers, reviewerNationalId)) return false - - // Otherwise, the only review missing is from the current reviewer return true } diff --git a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/dataProviders/index.ts b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/dataProviders/index.ts index 924a4a2c5375..85a5a4b28121 100644 --- a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/dataProviders/index.ts +++ b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/dataProviders/index.ts @@ -1,7 +1,6 @@ import { defineTemplateApi, InstitutionNationalIds, - MockablePaymentCatalogApi, PaymentCatalogApi, } from '@island.is/application/types' @@ -14,14 +13,6 @@ export const SamgongustofaPaymentCatalogApi = PaymentCatalogApi.configure({ externalDataId: 'payment', }) -export const MockableSamgongustofaPaymentCatalogApi = - MockablePaymentCatalogApi.configure({ - params: { - organizationId: InstitutionNationalIds.SAMGONGUSTOFA, - }, - externalDataId: 'payment', - }) - export const CurrentVehiclesApi = defineTemplateApi({ action: 'getCurrentVehiclesWithOperatorChangeChecks', externalDataId: 'currentVehicleList', diff --git a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/ApplicationStatus/index.tsx b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/ApplicationStatus/index.tsx index 3781f051aa49..27dea9781631 100644 --- a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/ApplicationStatus/index.tsx +++ b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/ApplicationStatus/index.tsx @@ -8,7 +8,7 @@ import { useLocale } from '@island.is/localization' import { FC } from 'react' import { review } from '../../lib/messages' import { ReviewScreenProps } from '../../shared' -import { getReviewSteps, canReviewerApprove } from '../../utils' +import { getReviewSteps, hasReviewerApproved } from '../../utils' import { MessageWithLinkButtonFormField } from '@island.is/application/ui-fields' import { StatusStep } from './StatusStep' import { coreMessages } from '@island.is/application/core' @@ -21,7 +21,7 @@ export const ApplicationStatus: FC< const steps = getReviewSteps(application) - const showReviewButton = canReviewerApprove( + const showReviewButton = !hasReviewerApproved( reviewerNationalId, application.answers, ) diff --git a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/Overview/index.tsx b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/Overview/index.tsx index e238eead5248..809b77c0d538 100644 --- a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/Overview/index.tsx +++ b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/Overview/index.tsx @@ -18,9 +18,8 @@ import { } from './sections' import { getApproveAnswers, - canReviewerApprove, + hasReviewerApproved, isLastReviewer, - canReviewerReApprove, } from '../../utils' import { RejectConfirmationModal } from './RejectConfirmationModal' import { @@ -28,8 +27,6 @@ import { SUBMIT_APPLICATION, } from '@island.is/application/graphql' import { useLazyQuery, useMutation } from '@apollo/client' -import { States } from '../../lib/constants' -import { ValidationErrorMessages } from '../ValidationErrorMessages' export const Overview: FC< React.PropsWithChildren @@ -50,14 +47,11 @@ export const Overview: FC< const [submitApplication, { error }] = useMutation(SUBMIT_APPLICATION, { onError: (e) => { console.error(e, e.message) - setButtonLoading(false) return }, }) - const [buttonLoading, setButtonLoading] = useState(false) - const [shouldLoadValidation, setShouldLoadValidation] = useState(false) - const [validationErrorFound, setValidationErrorFound] = useState(false) + const [loading, setLoading] = useState(false) const doApproveAndSubmit = async () => { // Need to get updated application answers, in case any other reviewer has approved @@ -118,11 +112,11 @@ export const Overview: FC< }) if (resSubmit?.data) { - setButtonLoading(false) + setLoading(false) setStep && setStep('conclusion') } } else { - setButtonLoading(false) + setLoading(false) setStep && setStep('conclusion') } } @@ -138,8 +132,7 @@ export const Overview: FC< } const onApproveButtonClick = async () => { - setButtonLoading(true) - setShouldLoadValidation(true) + setLoading(true) await doApproveAndSubmit() } @@ -157,14 +150,7 @@ export const Overview: FC< - {!buttonLoading && shouldLoadValidation && ( - - )} - - {!validationErrorFound && error && ( + {error && ( @@ -176,7 +162,7 @@ export const Overview: FC< - {canReviewerApprove(reviewerNationalId, application.answers) && ( + {!hasReviewerApproved(reviewerNationalId, application.answers) && ( )} - {canReviewerReApprove(reviewerNationalId, application.answers) && - application.state !== States.COMPLETED && ( - - - - )} diff --git a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/ValidationErrorMessages/index.tsx b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/ValidationErrorMessages/index.tsx index 27aee1ba9b8c..093b87cd3f4f 100644 --- a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/ValidationErrorMessages/index.tsx +++ b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/ValidationErrorMessages/index.tsx @@ -2,25 +2,16 @@ import { gql, useQuery } from '@apollo/client' import { OperatorChangeValidationMessage } from '@island.is/api/schema' import { getValueViaPath } from '@island.is/application/core' import { FieldBaseProps } from '@island.is/application/types' -import { - AlertMessage, - Box, - Bullet, - BulletList, -} from '@island.is/island-ui/core' +import { AlertMessage, Box, Text } from '@island.is/island-ui/core' import { useLocale } from '@island.is/localization' -import { Dispatch, FC, SetStateAction, useEffect } from 'react' +import { FC, useEffect } from 'react' import { ChangeOperatorOfVehicleAnswers } from '../..' import { VALIDATE_VEHICLE_OPERATOR_CHANGE } from '../../graphql/queries' import { applicationCheck } from '../../lib/messages' -interface Props { - setValidationErrorFound?: Dispatch> -} - export const ValidationErrorMessages: FC< - React.PropsWithChildren -> = ({ setValidationErrorFound, ...props }) => { + React.PropsWithChildren +> = (props) => { const { application, setFieldLoadingState } = props const { formatMessage } = useLocale() @@ -58,18 +49,12 @@ export const ValidationErrorMessages: FC< : null, }, }, - onCompleted: (data) => { - if (data?.vehicleOperatorChangeValidation?.hasError) { - setValidationErrorFound?.(true) - } - }, - fetchPolicy: 'no-cache', }, ) useEffect(() => { setFieldLoadingState?.(loading) - }, [loading, setFieldLoadingState]) + }, [loading]) return data?.vehicleOperatorChangeValidation?.hasError && data.vehicleOperatorChangeValidation.errorMessages.length > 0 ? ( @@ -79,7 +64,7 @@ export const ValidationErrorMessages: FC< title={formatMessage(applicationCheck.validation.alertTitle)} message={ - +
    {data.vehicleOperatorChangeValidation.errorMessages.map( (error: OperatorChangeValidationMessage) => { const message = formatMessage( @@ -97,13 +82,15 @@ export const ValidationErrorMessages: FC< error?.errorNo return ( - - {message || defaultMessage || fallbackMessage} - +
  • + + {message || defaultMessage || fallbackMessage} + +
  • ) }, )} - +
} /> diff --git a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/VehiclesField/VehicleRadioField.tsx b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/VehiclesField/VehicleRadioField.tsx new file mode 100644 index 000000000000..8f72e958cdba --- /dev/null +++ b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/VehiclesField/VehicleRadioField.tsx @@ -0,0 +1,142 @@ +import { + Box, + Text, + AlertMessage, + BulletList, + Bullet, + InputError, +} from '@island.is/island-ui/core' +import { FC, useState } from 'react' +import { VehiclesCurrentVehicleWithOperatorChangeChecks } from '../../shared' +import { RadioController } from '@island.is/shared/form-fields' +import { useFormContext } from 'react-hook-form' +import { getValueViaPath } from '@island.is/application/core' +import { FieldBaseProps } from '@island.is/application/types' +import { useLocale } from '@island.is/localization' +import { applicationCheck, information, error } from '../../lib/messages' + +interface Option { + value: string + label: React.ReactNode + disabled?: boolean +} + +interface VehicleSearchFieldProps { + currentVehicleList: VehiclesCurrentVehicleWithOperatorChangeChecks[] +} + +export const VehicleRadioField: FC< + React.PropsWithChildren +> = ({ currentVehicleList, application, errors }) => { + const { formatMessage } = useLocale() + const { setValue } = useFormContext() + + const [plate, setPlate] = useState( + getValueViaPath(application.answers, 'pickVehicle.plate', '') as string, + ) + + const onRadioControllerSelect = (s: string) => { + const currentVehicle = currentVehicleList[parseInt(s, 10)] + const permno = currentVehicle.permno || '' + + setPlate(permno) + setValue('pickVehicle.plate', permno) + setValue('pickVehicle.color', currentVehicle.color || undefined) + setValue('pickVehicle.type', currentVehicle.make || undefined) + setValue('vehicleMileage.requireMileage', currentVehicle?.requireMileage) + setValue('vehicleMileage.mileageReading', currentVehicle?.mileageReading) + if (permno) setValue('vehicleInfo.plate', permno) + if (permno) setValue('vehicleInfo.type', currentVehicle.make) + } + + const vehicleOptions = ( + vehicles: VehiclesCurrentVehicleWithOperatorChangeChecks[], + ) => { + const options = [] as Option[] + + for (const [index, vehicle] of vehicles.entries()) { + const disabled = + !vehicle.isDebtLess || !!vehicle.validationErrorMessages?.length + options.push({ + value: `${index}`, + label: ( + + + + {vehicle.make} + + + {vehicle.color} - {vehicle.permno} + + + {disabled && ( + + + + {!vehicle.isDebtLess && ( + + {formatMessage( + information.labels.pickVehicle.isNotDebtLessTag, + )} + + )} + {!!vehicle.validationErrorMessages?.length && + vehicle.validationErrorMessages?.map((error) => { + const message = formatMessage( + getValueViaPath( + applicationCheck.validation, + error.errorNo || '', + ), + ) + const defaultMessage = error.defaultMessage + const fallbackMessage = + formatMessage( + applicationCheck.validation + .fallbackErrorMessage, + ) + + ' - ' + + error.errorNo + + return ( + + {message || defaultMessage || fallbackMessage} + + ) + })} + + + } + /> + + )} + + ), + disabled: disabled, + }) + } + return options + } + + return ( +
+ + {plate.length === 0 && (errors as any)?.pickVehicle && ( + + )} +
+ ) +} diff --git a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/VehiclesField/index.tsx b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/VehiclesField/index.tsx index 3e0019887e76..7ab26655af07 100644 --- a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/VehiclesField/index.tsx +++ b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/fields/VehiclesField/index.tsx @@ -6,14 +6,12 @@ import { import { Box } from '@island.is/island-ui/core' import { FC, useCallback, useEffect } from 'react' import { CurrentVehiclesAndRecords } from '../../shared' +import { VehicleRadioField } from './VehicleRadioField' import { useFormContext } from 'react-hook-form' import { ApolloQueryResult, useMutation } from '@apollo/client' import { UPDATE_APPLICATION } from '@island.is/application/graphql' import { useLocale } from '@island.is/localization' -import { - FindVehicleFormField, - VehicleRadioFormField, -} from '@island.is/application/ui-fields' +import { FindVehicleFormField } from '@island.is/application/ui-fields' import { useLazyVehicleDetails } from '../../hooks/useLazyVehicleDetails' import { applicationCheck, error, information } from '../../lib/messages' import { VehicleSelectField } from './VehicleSelectField' @@ -99,27 +97,9 @@ export const VehiclesField: FC> = ( {...props} /> ) : ( - )} diff --git a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/forms/ChangeOperatorOfVehicleForm/InformationSection/vehicleSubSection.ts b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/forms/ChangeOperatorOfVehicleForm/InformationSection/vehicleSubSection.ts index 91dcd0eaba17..70cd26729e83 100644 --- a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/forms/ChangeOperatorOfVehicleForm/InformationSection/vehicleSubSection.ts +++ b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/forms/ChangeOperatorOfVehicleForm/InformationSection/vehicleSubSection.ts @@ -6,6 +6,7 @@ import { buildHiddenInput, } from '@island.is/application/core' import { information } from '../../../lib/messages' +import { VehiclesCurrentVehicle } from '../../../shared' import { getSelectedVehicle } from '../../../utils' export const vehicleSubSection = buildSubSection({ @@ -26,7 +27,7 @@ export const vehicleSubSection = buildSubSection({ const vehicle = getSelectedVehicle( application.externalData, application.answers, - ) + ) as VehiclesCurrentVehicle return vehicle?.permno }, }), @@ -40,7 +41,7 @@ export const vehicleSubSection = buildSubSection({ const vehicle = getSelectedVehicle( application.externalData, application.answers, - ) + ) as VehiclesCurrentVehicle return vehicle?.make }, }), @@ -50,7 +51,7 @@ export const vehicleSubSection = buildSubSection({ const vehicle = getSelectedVehicle( application.externalData, application.answers, - ) + ) as VehiclesCurrentVehicle return vehicle?.requireMileage || false }, }), @@ -60,8 +61,8 @@ export const vehicleSubSection = buildSubSection({ const vehicle = getSelectedVehicle( application.externalData, application.answers, - ) - return vehicle?.mileageReading || '' + ) as VehiclesCurrentVehicle + return vehicle.mileageReading || '' }, }), buildTextField({ @@ -70,14 +71,17 @@ export const vehicleSubSection = buildSubSection({ width: 'full', variant: 'number', condition: (answers, externalData) => { - const vehicle = getSelectedVehicle(externalData, answers) + const vehicle = getSelectedVehicle( + externalData, + answers, + ) as VehiclesCurrentVehicle return vehicle?.requireMileage || false }, placeholder(application) { const vehicle = getSelectedVehicle( application.externalData, application.answers, - ) + ) as VehiclesCurrentVehicle return vehicle?.mileageReading ? `Síðasta skráning ${vehicle.mileageReading} Km` : '' diff --git a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/forms/ChangeOperatorOfVehicleForm/externalDataSection.ts b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/forms/ChangeOperatorOfVehicleForm/externalDataSection.ts index 051a18a09f69..9132f1d53cb7 100644 --- a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/forms/ChangeOperatorOfVehicleForm/externalDataSection.ts +++ b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/forms/ChangeOperatorOfVehicleForm/externalDataSection.ts @@ -9,7 +9,6 @@ import { UserProfileApi, SamgongustofaPaymentCatalogApi, CurrentVehiclesApi, - MockableSamgongustofaPaymentCatalogApi, } from '../../dataProviders' export const externalDataSection = buildSection({ @@ -41,10 +40,6 @@ export const externalDataSection = buildSection({ provider: SamgongustofaPaymentCatalogApi, title: '', }), - buildDataProviderItem({ - provider: MockableSamgongustofaPaymentCatalogApi, - title: '', - }), ], }), ], diff --git a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/lib/ChangeOperatorOfVehicleTemplate.ts b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/lib/ChangeOperatorOfVehicleTemplate.ts index 68f932caa4a9..1d5144b1d48b 100644 --- a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/lib/ChangeOperatorOfVehicleTemplate.ts +++ b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/lib/ChangeOperatorOfVehicleTemplate.ts @@ -26,12 +26,11 @@ import { UserProfileApi, SamgongustofaPaymentCatalogApi, CurrentVehiclesApi, - MockableSamgongustofaPaymentCatalogApi, } from '../dataProviders' import { application as applicationMessage } from './messages' import { assign } from 'xstate' import set from 'lodash/set' -import { canReviewerApprove, isRemovingOperatorOnly } from '../utils' +import { hasReviewerApproved, isRemovingOperatorOnly } from '../utils' import { AuthDelegationType } from '@island.is/shared/types' import { ApiScope } from '@island.is/auth/scopes' import { buildPaymentState } from '@island.is/application/utils' @@ -62,7 +61,7 @@ const reviewStatePendingAction = ( role: string, nationalId: string, ): PendingAction => { - if (nationalId && canReviewerApprove(nationalId, application.answers)) { + if (nationalId && !hasReviewerApproved(nationalId, application.answers)) { return { title: corePendingActionMessages.waitingForReviewTitle, content: corePendingActionMessages.youNeedToReviewDescription, @@ -142,7 +141,6 @@ const template: ApplicationTemplate< IdentityApi, UserProfileApi, SamgongustofaPaymentCatalogApi, - MockableSamgongustofaPaymentCatalogApi, CurrentVehiclesApi, ], }, diff --git a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/lib/messages/review.ts b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/lib/messages/review.ts index 9811e33b6e07..90130992c5a0 100644 --- a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/lib/messages/review.ts +++ b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/lib/messages/review.ts @@ -123,13 +123,8 @@ export const review = { }, approve: { id: 'ta.cov.application:review.buttons.approve', - defaultMessage: 'Samþykkja', + defaultMessage: `Samþykkja`, description: 'Approve button in review process', }, - tryAgain: { - id: 'ta.cov.application:review.buttons.tryAgain', - defaultMessage: 'Reyna aftur', - description: 'Try again button in review process', - }, }), } diff --git a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/utils/canReviewerApprove.ts b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/utils/canReviewerApprove.ts deleted file mode 100644 index 98830e4526b6..000000000000 --- a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/utils/canReviewerApprove.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { getValueViaPath } from '@island.is/application/core' -import { OperatorInformation, UserInformation } from '../shared' -import { applicationHasPendingApproval } from './isLastReviewer' - -// Function to check if the reviewer is authorized to approve and hasn't done that yet -export const canReviewerApprove = ( - reviewerNationalId: string, - answers: FormValue, -): boolean => { - // Check if reviewer is co-owner and has not approved - const coOwners = getValueViaPath( - answers, - 'ownerCoOwner', - [], - ) as UserInformation[] - if ( - coOwners.some( - ({ nationalId, approved }) => - nationalId === reviewerNationalId && !approved, - ) - ) { - return true - } - - // Check if reviewer is new operator and has not approved - const newOperators = ( - getValueViaPath(answers, 'operators', []) as OperatorInformation[] - ).filter(({ wasRemoved }) => wasRemoved !== 'true') - if ( - newOperators.some( - ({ nationalId, approved }) => - nationalId === reviewerNationalId && !approved, - ) - ) { - return true - } - - return false -} - -// Special case to allow any reviewer to trigger an external API call to complete owner change -// Necessary when approve is updated in answers, but application is still stuck in REVIEW state -// then any user can try to 'push' the application to the next state -export const canReviewerReApprove = ( - reviewerNationalId: string, - answers: FormValue, -): boolean => { - const coOwners = getValueViaPath( - answers, - 'ownerCoOwner', - [], - ) as UserInformation[] - const newOperators = ( - getValueViaPath(answers, 'operators', []) as OperatorInformation[] - ).filter(({ wasRemoved }) => wasRemoved !== 'true') - - const isReviewerAuthorized = [ - coOwners.some(({ nationalId }) => nationalId === reviewerNationalId), - newOperators.some(({ nationalId }) => nationalId === reviewerNationalId), - ].some(Boolean) - - // Check if the reviewer is authorized and if all required approvals have been completed - return isReviewerAuthorized && !applicationHasPendingApproval(answers) -} diff --git a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/utils/getSelectedVehicle.ts b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/utils/getSelectedVehicle.ts index 38e162f6a245..a13ea829dc3c 100644 --- a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/utils/getSelectedVehicle.ts +++ b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/utils/getSelectedVehicle.ts @@ -5,7 +5,7 @@ import { CurrentVehiclesAndRecords, VehiclesCurrentVehicle } from '../shared' export const getSelectedVehicle = ( externalData: ExternalData, answers: FormValue, -): VehiclesCurrentVehicle | undefined => { +): VehiclesCurrentVehicle => { if (answers.findVehicle) { const vehicle = getValueViaPath( answers, diff --git a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/utils/hasReviewerApproved.ts b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/utils/hasReviewerApproved.ts new file mode 100644 index 000000000000..c53aad9fc6e8 --- /dev/null +++ b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/utils/hasReviewerApproved.ts @@ -0,0 +1,38 @@ +import { FormValue } from '@island.is/application/types' +import { getValueViaPath } from '@island.is/application/core' +import { OperatorInformation, UserInformation } from '../shared' + +export const hasReviewerApproved = ( + reviewerNationalId: string, + answers: FormValue, +) => { + // Check if reviewer is owner coowner and has not approved + const ownerCoOwners = getValueViaPath( + answers, + 'ownerCoOwner', + [], + ) as UserInformation[] + const ownerCoOwner = ownerCoOwners.find( + (ownerCoOwner) => ownerCoOwner.nationalId === reviewerNationalId, + ) + if (ownerCoOwner) { + const hasApproved = ownerCoOwner?.approved || false + if (!hasApproved) return false + } + + // Check if reviewer is operator and has not approved + const operators = getValueViaPath( + answers, + 'operators', + [], + ) as OperatorInformation[] + const operator = operators + .filter(({ wasRemoved }) => wasRemoved !== 'true') + .find((operator) => operator.nationalId === reviewerNationalId) + if (operator) { + const hasApproved = operator?.approved || false + if (!hasApproved) return false + } + + return true +} diff --git a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/utils/index.ts b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/utils/index.ts index b2b0a8ee552c..290b6e7851b3 100644 --- a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/utils/index.ts +++ b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/utils/index.ts @@ -1,6 +1,6 @@ export { getChargeItemCodes } from './getChargeItemCodes' export { getReviewSteps } from './getReviewSteps' -export { canReviewerApprove, canReviewerReApprove } from './canReviewerApprove' +export { hasReviewerApproved } from './hasReviewerApproved' export { getApproveAnswers } from './getApproveAnswers' export { isLastReviewer } from './isLastReviewer' export { getRejecter } from './getRejecter' diff --git a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/utils/isLastReviewer.ts b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/utils/isLastReviewer.ts index 223849b33eea..87a99d12d73c 100644 --- a/libs/application/templates/transport-authority/change-operator-of-vehicle/src/utils/isLastReviewer.ts +++ b/libs/application/templates/transport-authority/change-operator-of-vehicle/src/utils/isLastReviewer.ts @@ -2,50 +2,39 @@ import { getValueViaPath } from '@island.is/application/core' import { FormValue } from '@island.is/application/types' import { OperatorInformation, UserInformation } from '../shared' -// Function to check if an application has pending approval -export const applicationHasPendingApproval = ( +export const isLastReviewer = ( + reviewerNationalId: string, answers: FormValue, - excludeNationalId?: string, -): boolean => { - // Check if any co-owners have not approved - const coOwners = getValueViaPath( +) => { + // First check if any reviewer that is not the current user has not approved + const ownerCoOwners = getValueViaPath( answers, 'ownerCoOwner', [], ) as UserInformation[] - if ( - coOwners.some( - ({ nationalId, approved }) => - (!excludeNationalId || nationalId !== excludeNationalId) && !approved, + const approvedOwnerCoOwner = ownerCoOwners.find((ownerCoOwner) => { + return ( + ownerCoOwner.nationalId !== reviewerNationalId && !ownerCoOwner.approved ) - ) { - return true + }) + if (approvedOwnerCoOwner) { + return false } - // Check if any new operators have not approved - const newOperators = ( - getValueViaPath(answers, 'operators', []) as OperatorInformation[] - ).filter(({ wasRemoved }) => wasRemoved !== 'true') - if ( - newOperators.some( - ({ nationalId, approved }) => - (!excludeNationalId || nationalId !== excludeNationalId) && !approved, + const operators = getValueViaPath( + answers, + 'operators', + [], + ) as OperatorInformation[] + const approvedOperator = operators + .filter(({ wasRemoved }) => wasRemoved !== 'true') + .find( + (operator) => + operator.nationalId !== reviewerNationalId && !operator.approved, ) - ) { - return true + if (approvedOperator) { + return false } - return false -} - -// Function to check if the current reviewer is the last one who needs to approve -export const isLastReviewer = ( - reviewerNationalId: string, - answers: FormValue, -): boolean => { - // If there are pending approvals (excluding current reviewer), then he is not the last reviewer - if (applicationHasPendingApproval(answers, reviewerNationalId)) return false - - // Otherwise, the only review missing is from the current reviewer return true } diff --git a/libs/application/templates/transport-authority/license-plate-renewal/src/dataProviders/index.ts b/libs/application/templates/transport-authority/license-plate-renewal/src/dataProviders/index.ts index b16e607530df..712988da9d9f 100644 --- a/libs/application/templates/transport-authority/license-plate-renewal/src/dataProviders/index.ts +++ b/libs/application/templates/transport-authority/license-plate-renewal/src/dataProviders/index.ts @@ -1,7 +1,6 @@ import { defineTemplateApi, InstitutionNationalIds, - MockablePaymentCatalogApi, PaymentCatalogApi, } from '@island.is/application/types' @@ -14,14 +13,6 @@ export const SamgongustofaPaymentCatalogApi = PaymentCatalogApi.configure({ externalDataId: 'payment', }) -export const MockableSamgongustofaPaymentCatalogApi = - MockablePaymentCatalogApi.configure({ - params: { - organizationId: InstitutionNationalIds.SAMGONGUSTOFA, - }, - externalDataId: 'payment', - }) - export const MyPlateOwnershipsApi = defineTemplateApi({ action: 'getMyPlateOwnershipList', externalDataId: 'myPlateOwnershipList', diff --git a/libs/application/templates/transport-authority/license-plate-renewal/src/fields/PlateField/PlateRadioField.tsx b/libs/application/templates/transport-authority/license-plate-renewal/src/fields/PlateField/PlateRadioField.tsx new file mode 100644 index 000000000000..7a89fef1f4cf --- /dev/null +++ b/libs/application/templates/transport-authority/license-plate-renewal/src/fields/PlateField/PlateRadioField.tsx @@ -0,0 +1,109 @@ +import { + Box, + Text, + AlertMessage, + BulletList, + Bullet, + Tag, +} from '@island.is/island-ui/core' +import { FC, useEffect, useState } from 'react' +import { PlateOwnership } from '../../shared' +import { RadioController } from '@island.is/shared/form-fields' +import { FieldBaseProps } from '@island.is/application/types' +import { useLocale } from '@island.is/localization' +import { information } from '../../lib/messages' +import { useFormContext } from 'react-hook-form' +import { getValueViaPath } from '@island.is/application/core' + +interface Option { + value: string + label: React.ReactNode + disabled?: boolean +} + +interface PlateSearchFieldProps { + myPlateOwnershipList: PlateOwnership[] +} + +export const PlateRadioField: FC< + React.PropsWithChildren +> = ({ myPlateOwnershipList, application }) => { + const { formatMessage, formatDateFns } = useLocale() + const { setValue } = useFormContext() + const [regno, setRegno] = useState( + getValueViaPath(application.answers, 'pickPlate.regno', '') as string, + ) + + const plateOptions = (plates: PlateOwnership[]) => { + const options = [] as Option[] + + for (const [index, plate] of plates.entries()) { + const inThreeMonths = new Date().setMonth(new Date().getMonth() + 3) + const canRenew = +new Date(plate.endDate) <= +inThreeMonths + const disabled = !!plate?.validationErrorMessages?.length || !canRenew + options.push({ + value: `${index}`, + label: ( + + + + + {plate.regno} + + + + {formatMessage(information.labels.pickPlate.expiresTag, { + date: formatDateFns(new Date(plate.endDate), 'do MMM yyyy'), + })} + + + {disabled && ( + + + + {!!plate.validationErrorMessages?.length && + plate.validationErrorMessages?.map((error) => { + return {error.defaultMessage} + })} + + + } + /> + + )} + + ), + disabled: disabled, + }) + } + return options + } + + useEffect(() => { + setValue('pickPlate.regno', regno) + }, [setValue, regno]) + + return ( +
+ + setRegno(myPlateOwnershipList[parseInt(s, 10)].regno) + } + /> +
+ ) +} diff --git a/libs/application/templates/transport-authority/license-plate-renewal/src/fields/PlateField/PlateSelectField.tsx b/libs/application/templates/transport-authority/license-plate-renewal/src/fields/PlateField/PlateSelectField.tsx index 9ba3b36c4d53..127b4e2b4f66 100644 --- a/libs/application/templates/transport-authority/license-plate-renewal/src/fields/PlateField/PlateSelectField.tsx +++ b/libs/application/templates/transport-authority/license-plate-renewal/src/fields/PlateField/PlateSelectField.tsx @@ -1,6 +1,6 @@ import { FieldBaseProps, Option } from '@island.is/application/types' import { useLocale } from '@island.is/localization' -import { FC, useCallback, useEffect, useState } from 'react' +import { FC, useState } from 'react' import { Box, SkeletonLoader, @@ -8,15 +8,12 @@ import { Bullet, BulletList, ActionCard, - InputError, } from '@island.is/island-ui/core' import { PlateOwnership } from '../../shared' -import { error, information } from '../../lib/messages' +import { information } from '../../lib/messages' import { SelectController } from '@island.is/shared/form-fields' import { getValueViaPath } from '@island.is/application/core' import { useFormContext } from 'react-hook-form' -import { useLazyPlateDetails } from '../../hooks/useLazyPlateDetails' -import { checkCanRenew } from '../../utils' interface PlateSearchFieldProps { myPlateOwnershipList: PlateOwnership[] @@ -24,7 +21,7 @@ interface PlateSearchFieldProps { export const PlateSelectField: FC< React.PropsWithChildren -> = ({ myPlateOwnershipList, application, errors, setFieldLoadingState }) => { +> = ({ myPlateOwnershipList, application }) => { const { formatMessage, formatDateFns } = useLocale() const { setValue } = useFormContext() @@ -46,59 +43,27 @@ export const PlateSelectField: FC< } : null, ) - const [plate, setPlate] = useState( - getValueViaPath(application.answers, 'pickPlate.regno', '') as string, - ) - - const getPlateDetails = useLazyPlateDetails() - const getPlateDetailsCallback = useCallback( - async ({ regno }: { regno: string }) => { - const { data } = await getPlateDetails({ - regno, - }) - return data - }, - [getPlateDetails], - ) const onChange = (option: Option) => { const currentPlate = myPlateOwnershipList[parseInt(option.value, 10)] setIsLoading(true) if (currentPlate.regno) { - getPlateDetailsCallback({ + setSelectedPlate({ regno: currentPlate.regno, + startDate: currentPlate.startDate, + endDate: currentPlate.endDate, + validationErrorMessages: currentPlate.validationErrorMessages, }) - .then((response) => { - setSelectedPlate({ - regno: currentPlate.regno, - startDate: currentPlate.startDate, - endDate: currentPlate.endDate, - validationErrorMessages: - response?.myPlateOwnershipChecksByRegno?.validationErrorMessages, - }) - - const resHasError = - !!response?.myPlateOwnershipChecksByRegno?.validationErrorMessages - ?.length - const resCanRenew = checkCanRenew(currentPlate) - const resDisabled = resHasError || !resCanRenew - - setPlate(resDisabled ? '' : currentPlate.regno || '') - setValue('pickPlate.regno', resDisabled ? '' : currentPlate.regno) - setIsLoading(false) - }) - .catch((error) => console.error(error)) + setValue('pickPlate.regno', disabled ? '' : selectedPlate.regno) + setIsLoading(false) } } - - const hasError = !!selectedPlate?.validationErrorMessages?.length - const canRenew = checkCanRenew(selectedPlate) - const disabled = hasError || !canRenew - - useEffect(() => { - setFieldLoadingState?.(isLoading) - }, [isLoading]) - + const inThreeMonths = new Date().setMonth(new Date().getMonth() + 3) + const canRenew = + selectedPlate && +new Date(selectedPlate.endDate) <= +inThreeMonths + const disabled = + (selectedPlate && !!selectedPlate.validationErrorMessages?.length) || + !canRenew return ( )} - {selectedPlate && hasError && ( + {selectedPlate && disabled && ( - {selectedPlate.validationErrorMessages?.map((error) => { - return {error.defaultMessage} - })} + {!!selectedPlate.validationErrorMessages?.length && + selectedPlate.validationErrorMessages?.map( + (error) => { + return {error.defaultMessage} + }, + )} } @@ -159,9 +126,6 @@ export const PlateSelectField: FC< )} - {!isLoading && plate.length === 0 && (errors as any)?.pickPlate && ( - - )} ) } diff --git a/libs/application/templates/transport-authority/license-plate-renewal/src/fields/PlateField/index.tsx b/libs/application/templates/transport-authority/license-plate-renewal/src/fields/PlateField/index.tsx index e2548291c5c8..e18e46412b8e 100644 --- a/libs/application/templates/transport-authority/license-plate-renewal/src/fields/PlateField/index.tsx +++ b/libs/application/templates/transport-authority/license-plate-renewal/src/fields/PlateField/index.tsx @@ -1,19 +1,13 @@ -import { - FieldBaseProps, - FieldComponents, - FieldTypes, -} from '@island.is/application/types' +import { FieldBaseProps } from '@island.is/application/types' import { Box } from '@island.is/island-ui/core' import { FC, useCallback, useEffect } from 'react' import { PlateOwnership } from '../../shared' import { PlateSelectField } from './PlateSelectField' +import { PlateRadioField } from './PlateRadioField' import { useMutation } from '@apollo/client' import { UPDATE_APPLICATION } from '@island.is/application/graphql' import { useLocale } from '@island.is/localization' import { useFormContext } from 'react-hook-form' -import { VehicleRadioFormField } from '@island.is/application/ui-fields' -import { applicationCheck, error, information } from '../../lib/messages' -import { checkCanRenew } from '../../utils' export const PlateField: FC> = ( props, @@ -53,25 +47,9 @@ export const PlateField: FC> = ( {...props} /> ) : ( - checkCanRenew(item as PlateOwnership), - }} /> )} diff --git a/libs/application/templates/transport-authority/license-plate-renewal/src/forms/LicensePlateRenewalForm/InformationSection/informationSubSection.ts b/libs/application/templates/transport-authority/license-plate-renewal/src/forms/LicensePlateRenewalForm/InformationSection/informationSubSection.ts index 155cdf5b8e34..6394cd67542d 100644 --- a/libs/application/templates/transport-authority/license-plate-renewal/src/forms/LicensePlateRenewalForm/InformationSection/informationSubSection.ts +++ b/libs/application/templates/transport-authority/license-plate-renewal/src/forms/LicensePlateRenewalForm/InformationSection/informationSubSection.ts @@ -30,11 +30,10 @@ export const informationSubSection = buildSubSection({ width: 'half', readOnly: true, defaultValue: (application: Application) => { - const vehicle = getSelectedVehicle( + return getSelectedVehicle( application.externalData, application.answers, - ) - return vehicle?.regno + ).regno }, }), buildDescriptionField({ @@ -50,12 +49,15 @@ export const informationSubSection = buildSubSection({ width: 'half', readOnly: true, defaultValue: (application: Application) => { - const vehicle = getSelectedVehicle( - application.externalData, - application.answers, + return format( + new Date( + getSelectedVehicle( + application.externalData, + application.answers, + ).startDate, + ), + 'dd.MM.yyyy', ) - const dateFrom = vehicle ? new Date(vehicle.startDate) : new Date() - return format(dateFrom, 'dd.MM.yyyy') }, }), buildTextField({ @@ -65,11 +67,12 @@ export const informationSubSection = buildSubSection({ width: 'half', readOnly: true, defaultValue: (application: Application) => { - const vehicle = getSelectedVehicle( - application.externalData, - application.answers, + const dateTo = new Date( + getSelectedVehicle( + application.externalData, + application.answers, + ).endDate, ) - const dateTo = vehicle ? new Date(vehicle?.endDate) : new Date() return format( dateTo.setFullYear(dateTo.getFullYear() + 8), 'dd.MM.yyyy', diff --git a/libs/application/templates/transport-authority/license-plate-renewal/src/forms/LicensePlateRenewalForm/prerequisitesSection.ts b/libs/application/templates/transport-authority/license-plate-renewal/src/forms/LicensePlateRenewalForm/prerequisitesSection.ts index 532ee1267e0e..aa3cba6698d4 100644 --- a/libs/application/templates/transport-authority/license-plate-renewal/src/forms/LicensePlateRenewalForm/prerequisitesSection.ts +++ b/libs/application/templates/transport-authority/license-plate-renewal/src/forms/LicensePlateRenewalForm/prerequisitesSection.ts @@ -8,7 +8,6 @@ import { IdentityApi, SamgongustofaPaymentCatalogApi, MyPlateOwnershipsApi, - MockableSamgongustofaPaymentCatalogApi, } from '../../dataProviders' export const prerequisitesSection = buildSection({ @@ -35,10 +34,6 @@ export const prerequisitesSection = buildSection({ provider: SamgongustofaPaymentCatalogApi, title: '', }), - buildDataProviderItem({ - provider: MockableSamgongustofaPaymentCatalogApi, - title: '', - }), ], }), ], diff --git a/libs/application/templates/transport-authority/license-plate-renewal/src/hooks/useLazyPlateDetails.ts b/libs/application/templates/transport-authority/license-plate-renewal/src/hooks/useLazyPlateDetails.ts deleted file mode 100644 index 6ed497a6d88c..000000000000 --- a/libs/application/templates/transport-authority/license-plate-renewal/src/hooks/useLazyPlateDetails.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { gql } from '@apollo/client' -import { MyPlateOwnershipChecksByRegno } from '@island.is/api/schema' -import { GET_MY_PLATE_OWNERSHIP_CHECKS_BY_REGNO } from '../graphql/queries' -import { useLazyQuery } from './useLazyQuery' - -export const useLazyPlateDetails = () => { - return useLazyQuery< - { - myPlateOwnershipChecksByRegno: MyPlateOwnershipChecksByRegno - }, - { - regno: string - } - >( - gql` - ${GET_MY_PLATE_OWNERSHIP_CHECKS_BY_REGNO} - `, - ) -} diff --git a/libs/application/templates/transport-authority/license-plate-renewal/src/hooks/useLazyQuery.ts b/libs/application/templates/transport-authority/license-plate-renewal/src/hooks/useLazyQuery.ts deleted file mode 100644 index f8964864e9f3..000000000000 --- a/libs/application/templates/transport-authority/license-plate-renewal/src/hooks/useLazyQuery.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { - DocumentNode, - OperationVariables, - useApolloClient, -} from '@apollo/client' -import { useCallback } from 'react' - -export const useLazyQuery = < - TData = any, - TVariables extends OperationVariables = OperationVariables, ->( - query: DocumentNode, -) => { - const client = useApolloClient() - - return useCallback( - (variables: TVariables) => - client.query({ - query, - variables, - }), - [client], - ) -} diff --git a/libs/application/templates/transport-authority/license-plate-renewal/src/index.ts b/libs/application/templates/transport-authority/license-plate-renewal/src/index.ts index ed5617b1cf2e..b758ce9adb96 100644 --- a/libs/application/templates/transport-authority/license-plate-renewal/src/index.ts +++ b/libs/application/templates/transport-authority/license-plate-renewal/src/index.ts @@ -8,6 +8,5 @@ export type LicensePlateRenewalAnswers = LicensePlateRenewal export * from './utils' export * from './lib/messages/error' -export * from './lib/messages/applicationCheck' export default template diff --git a/libs/application/templates/transport-authority/license-plate-renewal/src/lib/LicensePlateRenewalTemplate.ts b/libs/application/templates/transport-authority/license-plate-renewal/src/lib/LicensePlateRenewalTemplate.ts index 36fdce40e474..52194f92cf09 100644 --- a/libs/application/templates/transport-authority/license-plate-renewal/src/lib/LicensePlateRenewalTemplate.ts +++ b/libs/application/templates/transport-authority/license-plate-renewal/src/lib/LicensePlateRenewalTemplate.ts @@ -25,7 +25,6 @@ import { LicensePlateRenewalSchema } from './dataSchema' import { SamgongustofaPaymentCatalogApi, MyPlateOwnershipsApi, - MockableSamgongustofaPaymentCatalogApi, } from '../dataProviders' import { AuthDelegationType } from '@island.is/shared/types' import { ApiScope } from '@island.is/auth/scopes' @@ -87,6 +86,9 @@ const template: ApplicationTemplate< }, lifecycle: EphemeralStateLifeCycle, onExit: [ + defineTemplateApi({ + action: ApiActions.validateApplication, + }), defineTemplateApi({ action: ApiActions.submitApplication, }), @@ -109,7 +111,6 @@ const template: ApplicationTemplate< delete: true, api: [ SamgongustofaPaymentCatalogApi, - MockableSamgongustofaPaymentCatalogApi, MyPlateOwnershipsApi, IdentityApi, ], diff --git a/libs/application/templates/transport-authority/license-plate-renewal/src/lib/dataSchema.ts b/libs/application/templates/transport-authority/license-plate-renewal/src/lib/dataSchema.ts index 0eb01c1e204d..0b45b7882b92 100644 --- a/libs/application/templates/transport-authority/license-plate-renewal/src/lib/dataSchema.ts +++ b/libs/application/templates/transport-authority/license-plate-renewal/src/lib/dataSchema.ts @@ -4,7 +4,7 @@ export const LicensePlateRenewalSchema = z.object({ approveExternalData: z.boolean().refine((v) => v), pickPlate: z.object({ regno: z.string().min(1), - value: z.string().optional(), + value: z.string().min(1), }), }) diff --git a/libs/application/templates/transport-authority/license-plate-renewal/src/lib/messages/applicationCheck.ts b/libs/application/templates/transport-authority/license-plate-renewal/src/lib/messages/applicationCheck.ts deleted file mode 100644 index 1697e26d3606..000000000000 --- a/libs/application/templates/transport-authority/license-plate-renewal/src/lib/messages/applicationCheck.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { defineMessages } from 'react-intl' - -export const applicationCheck = { - validation: defineMessages({ - alertTitle: { - id: 'ta.lpr.application:applicationCheck.validation.alertTitle', - defaultMessage: 'Það kom upp villa', - description: 'Application check validation alert title', - }, - fallbackErrorMessage: { - id: 'ta.lpr.application:applicationCheck.validation.fallbackErrorMessage', - defaultMessage: 'Það kom upp villa við að sannreyna gögn', - description: 'Fallback error message for validation', - }, - }), -} diff --git a/libs/application/templates/transport-authority/license-plate-renewal/src/lib/messages/error.ts b/libs/application/templates/transport-authority/license-plate-renewal/src/lib/messages/error.ts index 756a92386086..35b5ba7b0355 100644 --- a/libs/application/templates/transport-authority/license-plate-renewal/src/lib/messages/error.ts +++ b/libs/application/templates/transport-authority/license-plate-renewal/src/lib/messages/error.ts @@ -1,11 +1,6 @@ import { defineMessages } from 'react-intl' export const error = defineMessages({ - requiredValidPlate: { - id: 'ta.lpr.application:error.requiredValidPlate', - defaultMessage: 'Einkamerki þarf að vera gilt', - description: 'Error message if the plate chosen is invalid or not chosen', - }, errorDataProvider: { id: 'ta.lpr.application:error.dataProvider', defaultMessage: 'Reyndu aftur síðar', diff --git a/libs/application/templates/transport-authority/license-plate-renewal/src/lib/messages/index.ts b/libs/application/templates/transport-authority/license-plate-renewal/src/lib/messages/index.ts index cde6d64fc218..b1dfa8e15ab3 100644 --- a/libs/application/templates/transport-authority/license-plate-renewal/src/lib/messages/index.ts +++ b/libs/application/templates/transport-authority/license-plate-renewal/src/lib/messages/index.ts @@ -4,4 +4,3 @@ export * from './information' export * from './payment' export * from './conclusion' export * from './error' -export * from './applicationCheck' diff --git a/libs/application/templates/transport-authority/license-plate-renewal/src/lib/messages/information.ts b/libs/application/templates/transport-authority/license-plate-renewal/src/lib/messages/information.ts index e3db808d044e..d14ae07c9f27 100644 --- a/libs/application/templates/transport-authority/license-plate-renewal/src/lib/messages/information.ts +++ b/libs/application/templates/transport-authority/license-plate-renewal/src/lib/messages/information.ts @@ -52,6 +52,11 @@ export const information = { defaultMessage: 'Ekki er hægt að endurnýja einkamerki vegna:', description: 'Pick plate has an error title', }, + isNotDebtLessTag: { + id: 'ta.lpr.application:information.labels.pickVehicle.isNotDebtLessTag', + defaultMessage: 'Ógreidd bifreiðagjöld', + description: 'Pick plate is not debt less tag', + }, expiresTag: { id: 'ta.lpr.application:information.labels.pickVehicle.expiresTag', defaultMessage: 'Rennur út {date}', diff --git a/libs/application/templates/transport-authority/license-plate-renewal/src/shared/constants.ts b/libs/application/templates/transport-authority/license-plate-renewal/src/shared/constants.ts index b67eb7433299..f62f9cd86c1f 100644 --- a/libs/application/templates/transport-authority/license-plate-renewal/src/shared/constants.ts +++ b/libs/application/templates/transport-authority/license-plate-renewal/src/shared/constants.ts @@ -1,3 +1,4 @@ export enum ApiActions { + validateApplication = 'validateApplication', submitApplication = 'submitApplication', } diff --git a/libs/application/templates/transport-authority/license-plate-renewal/src/utils/checkCanRenew.ts b/libs/application/templates/transport-authority/license-plate-renewal/src/utils/checkCanRenew.ts deleted file mode 100644 index 1d2ac5fe148f..000000000000 --- a/libs/application/templates/transport-authority/license-plate-renewal/src/utils/checkCanRenew.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { PlateOwnership } from '../shared' - -export const checkCanRenew = (plate?: PlateOwnership | null): boolean => { - const inThreeMonths = new Date().setMonth(new Date().getMonth() + 3) - return plate ? +new Date(plate.endDate) <= +inThreeMonths : false -} diff --git a/libs/application/templates/transport-authority/license-plate-renewal/src/utils/getSelectedVehicle.ts b/libs/application/templates/transport-authority/license-plate-renewal/src/utils/getSelectedVehicle.ts index 324c3ceb1d91..49782eda7261 100644 --- a/libs/application/templates/transport-authority/license-plate-renewal/src/utils/getSelectedVehicle.ts +++ b/libs/application/templates/transport-authority/license-plate-renewal/src/utils/getSelectedVehicle.ts @@ -5,7 +5,7 @@ import { PlateOwnership } from '../shared' export const getSelectedVehicle = ( externalData: ExternalData, answers: FormValue, -): PlateOwnership | undefined => { +) => { const currentVehicleList = (externalData?.['myPlateOwnershipList']?.data as PlateOwnership[]) || [] const vehicleValue = getValueViaPath(answers, 'pickPlate.value', '') as string diff --git a/libs/application/templates/transport-authority/license-plate-renewal/src/utils/index.ts b/libs/application/templates/transport-authority/license-plate-renewal/src/utils/index.ts index 2ad52b10bbab..69aa4fbeac9d 100644 --- a/libs/application/templates/transport-authority/license-plate-renewal/src/utils/index.ts +++ b/libs/application/templates/transport-authority/license-plate-renewal/src/utils/index.ts @@ -16,4 +16,3 @@ export const getExtraData = (application: Application): ExtraData[] => { } export { getSelectedVehicle } from './getSelectedVehicle' -export { checkCanRenew } from './checkCanRenew' diff --git a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/dataProviders/index.ts b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/dataProviders/index.ts index e90307fa5ae4..962d63233cb0 100644 --- a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/dataProviders/index.ts +++ b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/dataProviders/index.ts @@ -1,7 +1,6 @@ import { defineTemplateApi, InstitutionNationalIds, - MockablePaymentCatalogApi, PaymentCatalogApi, } from '@island.is/application/types' @@ -12,14 +11,6 @@ export const SamgongustofaPaymentCatalogApi = PaymentCatalogApi.configure({ externalDataId: 'payment', }) -export const MockableSamgongustofaPaymentCatalogApi = - MockablePaymentCatalogApi.configure({ - params: { - organizationId: InstitutionNationalIds.SAMGONGUSTOFA, - }, - externalDataId: 'payment', - }) - export const CurrentVehiclesApi = defineTemplateApi({ action: 'getCurrentVehiclesWithPlateOrderChecks', externalDataId: 'currentVehicleList', diff --git a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/fields/PickPlateSize.tsx b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/fields/PickPlateSize.tsx index 64c8732fc832..d29a97fcb2e6 100644 --- a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/fields/PickPlateSize.tsx +++ b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/fields/PickPlateSize.tsx @@ -11,7 +11,7 @@ import { CheckboxController } from '@island.is/shared/form-fields' import { gql, useQuery } from '@apollo/client' import { GET_VEHICLE_INFORMATION } from '../graphql/queries' import { getErrorViaPath } from '@island.is/application/core' -import { PlateType } from '../shared' +import { PlateType, VehiclesCurrentVehicle } from '../shared' import { information } from '../lib/messages' import { getSelectedVehicle } from '../utils' import { useFormContext } from 'react-hook-form' @@ -26,7 +26,7 @@ export const PickPlateSize: FC> = ( const vehicle = getSelectedVehicle( application.externalData, application.answers, - ) + ) as VehiclesCurrentVehicle const { data, loading, error } = useQuery( gql` @@ -35,7 +35,7 @@ export const PickPlateSize: FC> = ( { variables: { input: { - permno: vehicle?.permno, + permno: vehicle.permno, regno: '', vin: '', }, @@ -54,9 +54,9 @@ export const PickPlateSize: FC> = ( // Plate type front should always be defined (rear type can be empty in some cases) const plateTypeFrontError = !currentPlateTypeFront - const plateTypeFrontList = - plateTypeList?.filter((x) => x.code === currentPlateTypeFront) || [] - const plateTypeFrontListEmptyError = plateTypeFrontList.length === 0 + const noPlateMatchError = + plateTypeList?.filter((x) => x.code === currentPlateTypeFront)?.length === + 0 ?? false useEffect(() => { if (!loading && currentPlateTypeRear === null) { @@ -66,7 +66,7 @@ export const PickPlateSize: FC> = ( useEffect(() => { setFieldLoadingState?.(loading || !!error) - }, [loading, error, setFieldLoadingState]) + }, [loading, error]) return ( @@ -77,7 +77,7 @@ export const PickPlateSize: FC> = ( repeat={2} borderRadius="large" /> - ) : !error && !plateTypeFrontError && !plateTypeFrontListEmptyError ? ( + ) : !error && !plateTypeFrontError && !noPlateMatchError ? ( <> {formatMessage(information.labels.plateSize.frontPlateSubtitle)} @@ -137,7 +137,7 @@ export const PickPlateSize: FC> = ( > -} - -export const ValidationErrorMessages: FC< - React.PropsWithChildren -> = ({ setValidationErrorFound, ...props }) => { - const { application, setFieldLoadingState } = props - - const { formatMessage } = useLocale() - - const answers = application.answers as OrderVehicleLicensePlateAnswers - - const { data, loading } = useQuery( - gql` - ${VALIDATE_VEHICLE_PLATE_ORDER} - `, - { - variables: { - answers: { - pickVehicle: { - plate: answers?.pickVehicle?.plate, - }, - plateSize: { - frontPlateSize: answers?.plateSize?.frontPlateSize, - rearPlateSize: answers?.plateSize?.rearPlateSize, - }, - plateDelivery: { - deliveryMethodIsDeliveryStation: - answers?.plateDelivery?.deliveryMethodIsDeliveryStation, - deliveryStationTypeCode: - answers?.plateDelivery?.deliveryStationTypeCode, - includeRushFee: answers?.plateDelivery?.includeRushFee, - }, - }, - }, - onCompleted: (data) => { - if (data?.vehiclePlateOrderValidation?.hasError) { - setValidationErrorFound?.(true) - } - }, - fetchPolicy: 'no-cache', - }, - ) - - useEffect(() => { - setFieldLoadingState?.(loading) - }, [loading, setFieldLoadingState]) - - return data?.vehiclePlateOrderValidation?.hasError && - data.vehiclePlateOrderValidation.errorMessages.length > 0 ? ( - - - - {data.vehiclePlateOrderValidation.errorMessages.map( - (error: PlateOrderValidationMessage) => { - const message = formatMessage( - getValueViaPath( - applicationCheck.validation, - error?.errorNo || '', - ), - ) - const defaultMessage = error.defaultMessage - const fallbackMessage = - formatMessage( - applicationCheck.validation.fallbackErrorMessage, - ) + - ' - ' + - error?.errorNo - - return ( - - {message || defaultMessage || fallbackMessage} - - ) - }, - )} - - - } - /> - - ) : null -} diff --git a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/fields/VehiclesField/VehicleRadioField.tsx b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/fields/VehiclesField/VehicleRadioField.tsx new file mode 100644 index 000000000000..7acc956fcba9 --- /dev/null +++ b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/fields/VehiclesField/VehicleRadioField.tsx @@ -0,0 +1,119 @@ +import { + AlertMessage, + Box, + Bullet, + BulletList, + InputError, + Text, +} from '@island.is/island-ui/core' +import { FC, useState } from 'react' +import { RadioController } from '@island.is/shared/form-fields' +import { useFormContext } from 'react-hook-form' +import { getValueViaPath } from '@island.is/application/core' +import { FieldBaseProps } from '@island.is/application/types' +import { useLocale } from '@island.is/localization' +import { error, information } from '../../lib/messages' +import { VehiclesCurrentVehicleWithPlateOrderChecks } from '../../shared' + +interface Option { + value: string + label: React.ReactNode + disabled?: boolean +} + +interface VehicleSearchFieldProps { + currentVehicleList: VehiclesCurrentVehicleWithPlateOrderChecks[] +} + +export const VehicleRadioField: FC< + React.PropsWithChildren +> = ({ currentVehicleList, application, errors }) => { + const { formatMessage } = useLocale() + const { setValue } = useFormContext() + + const [plate, setPlate] = useState( + getValueViaPath(application.answers, 'pickVehicle.plate', '') as string, + ) + + const onRadioControllerSelect = (s: string) => { + const currentVehicle = currentVehicleList[parseInt(s, 10)] + setPlate(currentVehicle.permno || '') + setValue('pickVehicle.plate', currentVehicle.permno || '') + } + + const vehicleOptions = ( + vehicles: VehiclesCurrentVehicleWithPlateOrderChecks[], + ) => { + const options = [] as Option[] + + for (const [index, vehicle] of vehicles.entries()) { + const disabled = !!vehicle.validationErrorMessages?.length + options.push({ + value: `${index}`, + label: ( + + + + {vehicle.make} + + + {vehicle.color} - {vehicle.permno} + + + {disabled && ( + + + + {!!vehicle.validationErrorMessages?.length && + vehicle.validationErrorMessages?.map((err) => { + const defaultMessage = err.defaultMessage + const fallbackMessage = + formatMessage( + error.validationFallbackErrorMessage, + ) + + ' - ' + + err.errorNo + + return ( + + {defaultMessage || fallbackMessage} + + ) + })} + + + } + /> + + )} + + ), + disabled: disabled, + }) + } + return options + } + + return ( +
+ + {plate.length === 0 && (errors as any)?.pickVehicle && ( + + )} +
+ ) +} diff --git a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/fields/VehiclesField/VehicleSelectField.tsx b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/fields/VehiclesField/VehicleSelectField.tsx index 8a75865a36ba..833cbfac19cf 100644 --- a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/fields/VehiclesField/VehicleSelectField.tsx +++ b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/fields/VehiclesField/VehicleSelectField.tsx @@ -1,6 +1,6 @@ import { FieldBaseProps, Option } from '@island.is/application/types' import { useLocale } from '@island.is/localization' -import { FC, useCallback, useEffect, useState } from 'react' +import { FC, useCallback, useState } from 'react' import { ActionCard, AlertMessage, @@ -27,7 +27,7 @@ interface VehicleSearchFieldProps { export const VehicleSelectField: FC< React.PropsWithChildren -> = ({ currentVehicleList, application, errors, setFieldLoadingState }) => { +> = ({ currentVehicleList, application, errors }) => { const { formatMessage } = useLocale() const { setValue } = useFormContext() @@ -87,16 +87,11 @@ export const VehicleSelectField: FC< const disabled = !!response?.vehiclePlateOrderChecksByPermno?.validationErrorMessages ?.length - - const permno = disabled ? '' : currentVehicle.permno || '' - - setPlate(permno) - - setValue('pickVehicle.plate', permno) - - setValue('plateSize.frontPlateSize', []) - setValue('plateSize.rearPlateSize', []) - + setPlate(disabled ? '' : currentVehicle.permno || '') + setValue( + 'pickVehicle.plate', + disabled ? '' : currentVehicle.permno || '', + ) setIsLoading(false) }) .catch((error) => console.error(error)) @@ -106,10 +101,6 @@ export const VehicleSelectField: FC< const disabled = selectedVehicle && !!selectedVehicle.validationErrorMessages?.length - useEffect(() => { - setFieldLoadingState?.(isLoading) - }, [isLoading]) - return ( > = ( {...props} /> ) : ( - )} diff --git a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/fields/index.ts b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/fields/index.ts index 26eaa94eb12a..c9510178fdfe 100644 --- a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/fields/index.ts +++ b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/fields/index.ts @@ -1,3 +1,2 @@ export { VehiclesField } from './VehiclesField' export { PickPlateSize } from './PickPlateSize' -export { ValidationErrorMessages } from './ValidationErrorMessages' diff --git a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/forms/OrderVehicleLicensePlateForm/InformationSection/plateSizeSubSection.ts b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/forms/OrderVehicleLicensePlateForm/InformationSection/plateSizeSubSection.ts index ae1772f2d5aa..a6f75bc424a5 100644 --- a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/forms/OrderVehicleLicensePlateForm/InformationSection/plateSizeSubSection.ts +++ b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/forms/OrderVehicleLicensePlateForm/InformationSection/plateSizeSubSection.ts @@ -1,4 +1,5 @@ import { Application } from '@island.is/api/schema' +import { VehiclesCurrentVehicle } from '../../../shared' import { buildMultiField, buildTextField, @@ -24,7 +25,7 @@ export const plateSizeSubSection = buildSubSection({ titleVariant: 'h5', }), buildTextField({ - id: 'vehicleInfo.plate', + id: 'plateSize.vehiclePlate', title: information.labels.plateSize.vehiclePlate, backgroundColor: 'white', width: 'half', @@ -33,12 +34,12 @@ export const plateSizeSubSection = buildSubSection({ const vehicle = getSelectedVehicle( application.externalData, application.answers, - ) + ) as VehiclesCurrentVehicle return vehicle?.permno }, }), buildTextField({ - id: 'vehicleInfo.type', + id: 'plateSize.vehicleType', title: information.labels.plateSize.vehicleType, backgroundColor: 'white', width: 'half', @@ -47,7 +48,7 @@ export const plateSizeSubSection = buildSubSection({ const vehicle = getSelectedVehicle( application.externalData, application.answers, - ) + ) as VehiclesCurrentVehicle return vehicle?.make }, }), diff --git a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/forms/OrderVehicleLicensePlateForm/index.ts b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/forms/OrderVehicleLicensePlateForm/index.ts index 36e802e646e4..d3af3ddc9e64 100644 --- a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/forms/OrderVehicleLicensePlateForm/index.ts +++ b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/forms/OrderVehicleLicensePlateForm/index.ts @@ -1,10 +1,11 @@ import { buildForm, buildSection } from '@island.is/application/core' import { Form, FormModes } from '@island.is/application/types' -import { confirmation } from '../../lib/messages' +import { confirmation, payment } from '../../lib/messages' import { informationSection } from './InformationSection' import { prerequisitesSection } from './prerequisitesSection' -import { paymentSection } from './paymentSection' import { Logo } from '../../assets/Logo' +import { buildFormPaymentChargeOverviewSection } from '@island.is/application/ui-forms' +import { getChargeItemCodesAndExtraLabel } from '../../utils' export const OrderVehicleLicensePlateForm: Form = buildForm({ id: 'OrderVehicleLicensePlateFormDraft', @@ -16,7 +17,10 @@ export const OrderVehicleLicensePlateForm: Form = buildForm({ children: [ prerequisitesSection, informationSection, - paymentSection, + buildFormPaymentChargeOverviewSection({ + sectionTitle: payment.general.sectionTitle, + getSelectedChargeItems: getChargeItemCodesAndExtraLabel, + }), buildSection({ id: 'confirmation', title: confirmation.general.sectionTitle, diff --git a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/forms/OrderVehicleLicensePlateForm/paymentSection.ts b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/forms/OrderVehicleLicensePlateForm/paymentSection.ts deleted file mode 100644 index a3613fff227f..000000000000 --- a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/forms/OrderVehicleLicensePlateForm/paymentSection.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { - buildSection, - buildCustomField, - buildMultiField, - buildSubmitField, - buildPaymentChargeOverviewField, -} from '@island.is/application/core' -import { DefaultEvents } from '@island.is/application/types' -import { payment } from '../../lib/messages' -import { - getChargeItemCodesAndExtraLabel, - getChargeItemCodesWithAnswers, -} from '../../utils' -import { OrderVehicleLicensePlate } from '../../lib/dataSchema' - -export const paymentSection = buildSection({ - id: 'payment', - title: payment.general.sectionTitle, - children: [ - buildMultiField({ - id: 'paymentMultiField', - title: payment.general.pageTitle, - space: 1, - children: [ - buildPaymentChargeOverviewField({ - id: 'uiForms.paymentChargeOverviewMultifield', - title: '', - forPaymentLabel: payment.paymentChargeOverview.forPayment, - totalLabel: payment.paymentChargeOverview.total, - getSelectedChargeItems: getChargeItemCodesAndExtraLabel, - }), - buildCustomField({ - id: 'ValidationErrorMessages', - title: '', - component: 'ValidationErrorMessages', - }), - buildSubmitField({ - id: 'submit', - placement: 'footer', - title: payment.general.confirm, - refetchApplicationAfterSubmit: true, - actions: [ - { - event: DefaultEvents.SUBMIT, - name: payment.general.confirm, - type: 'primary', - condition: (formValue, externalData) => { - const chargeItemCodes = getChargeItemCodesWithAnswers( - formValue as OrderVehicleLicensePlate, - ) - const allItems = externalData?.payment?.data as [ - { - priceAmount: number - chargeItemName: string - chargeItemCode: string - }, - ] - const items = chargeItemCodes.map((chargeItemCode) => { - return allItems.find( - (item) => item.chargeItemCode === chargeItemCode, - ) - }) - return items.length > 0 - }, - }, - ], - }), - ], - }), - ], -}) diff --git a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/forms/OrderVehicleLicensePlateForm/prerequisitesSection.ts b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/forms/OrderVehicleLicensePlateForm/prerequisitesSection.ts index 1538341271a0..b2954cf408f8 100644 --- a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/forms/OrderVehicleLicensePlateForm/prerequisitesSection.ts +++ b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/forms/OrderVehicleLicensePlateForm/prerequisitesSection.ts @@ -9,7 +9,6 @@ import { CurrentVehiclesApi, DeliveryStationsApi, PlateTypesApi, - MockableSamgongustofaPaymentCatalogApi, } from '../../dataProviders' export const prerequisitesSection = buildSection({ @@ -31,10 +30,6 @@ export const prerequisitesSection = buildSection({ provider: SamgongustofaPaymentCatalogApi, title: '', }), - buildDataProviderItem({ - provider: MockableSamgongustofaPaymentCatalogApi, - title: '', - }), buildDataProviderItem({ provider: DeliveryStationsApi, title: '', diff --git a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/graphql/queries.ts b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/graphql/queries.ts index 5d5bd05d0ecf..526d8a5f041d 100644 --- a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/graphql/queries.ts +++ b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/graphql/queries.ts @@ -32,15 +32,3 @@ export const GET_VEHICLE_INFORMATION = ` } } ` - -export const VALIDATE_VEHICLE_PLATE_ORDER = ` - query GetVehiclePlateOrderValidation($answers: PlateOrderAnswers!) { - vehiclePlateOrderValidation(answers: $answers) { - hasError - errorMessages { - errorNo - defaultMessage - } - } - } -` diff --git a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/index.ts b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/index.ts index b91f9a24ef2c..59a1addbc8fb 100644 --- a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/index.ts +++ b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/index.ts @@ -8,6 +8,5 @@ export type OrderVehicleLicensePlateAnswers = OrderVehicleLicensePlate export * from './utils' export * from './lib/messages/externalData' -export * from './lib/messages/applicationCheck' export default template diff --git a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/lib/OrderVehicleLicensePlateTemplate.ts b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/lib/OrderVehicleLicensePlateTemplate.ts index 07b5ce555e52..40cf0138972b 100644 --- a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/lib/OrderVehicleLicensePlateTemplate.ts +++ b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/lib/OrderVehicleLicensePlateTemplate.ts @@ -25,7 +25,6 @@ import { CurrentVehiclesApi, DeliveryStationsApi, PlateTypesApi, - MockableSamgongustofaPaymentCatalogApi, } from '../dataProviders' import { AuthDelegationType } from '@island.is/shared/types' import { ApiScope } from '@island.is/auth/scopes' @@ -85,9 +84,6 @@ const template: ApplicationTemplate< ], }, lifecycle: EphemeralStateLifeCycle, - onExit: defineTemplateApi({ - action: ApiActions.validateApplication, - }), roles: [ { id: Roles.APPLICANT, @@ -107,7 +103,6 @@ const template: ApplicationTemplate< delete: true, api: [ SamgongustofaPaymentCatalogApi, - MockableSamgongustofaPaymentCatalogApi, CurrentVehiclesApi, DeliveryStationsApi, PlateTypesApi, diff --git a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/lib/dataSchema.ts b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/lib/dataSchema.ts index f8ac10213e78..985eb0b9fae9 100644 --- a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/lib/dataSchema.ts +++ b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/lib/dataSchema.ts @@ -11,14 +11,10 @@ export const OrderVehicleLicensePlateSchema = z.object({ plate: z.string(), type: z.string(), }), - plateSize: z - .object({ - frontPlateSize: z.array(z.string()), - rearPlateSize: z.array(z.string()), - }) - .refine(({ frontPlateSize, rearPlateSize }) => { - return frontPlateSize.length !== 0 || rearPlateSize.length !== 0 - }), + plateSize: z.object({ + frontPlateSize: z.array(z.string()), + rearPlateSize: z.array(z.string()), + }), plateDelivery: z.object({ deliveryMethodIsDeliveryStation: z.enum([YES, NO]), deliveryStationTypeCode: z.string().optional(), diff --git a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/lib/messages/applicationCheck.ts b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/lib/messages/applicationCheck.ts deleted file mode 100644 index 364b2bcbd13a..000000000000 --- a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/lib/messages/applicationCheck.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { defineMessages } from 'react-intl' - -export const applicationCheck = { - validation: defineMessages({ - alertTitle: { - id: 'ta.ovlp.application:applicationCheck.validation.alertTitle', - defaultMessage: 'Það kom upp villa', - description: 'Application check validation alert title', - }, - fallbackErrorMessage: { - id: 'ta.ovlp.application:applicationCheck.validation.fallbackErrorMessage', - defaultMessage: 'Það kom upp villa við að sannreyna gögn', - description: 'Fallback error message for validation', - }, - }), -} diff --git a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/lib/messages/index.ts b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/lib/messages/index.ts index e6de91a1f1cb..dc41f673cb74 100644 --- a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/lib/messages/index.ts +++ b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/lib/messages/index.ts @@ -4,4 +4,3 @@ export * from './information' export * from './payment' export * from './confirmation' export * from './error' -export * from './applicationCheck' diff --git a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/lib/messages/payment.ts b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/lib/messages/payment.ts index 59016c396c95..ba47ab991e24 100644 --- a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/lib/messages/payment.ts +++ b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/lib/messages/payment.ts @@ -7,28 +7,8 @@ export const payment = { defaultMessage: 'Greiðsla', description: 'Title of for payment section', }, - pageTitle: { - id: 'ta.ovlp.application:payment.general.pageTitle', - defaultMessage: 'Greiðsla', - description: 'Title of for payment page', - }, - confirm: { - id: 'ta.ovlp.application:payment.general.confirm', - defaultMessage: 'Staðfesta', - description: 'confirm', - }, }), paymentChargeOverview: defineMessages({ - forPayment: { - id: 'ta.ovlp.application:payment.paymentChargeOverview.forPayment', - defaultMessage: 'Til greiðslu', - description: 'For payment label', - }, - total: { - id: 'ta.ovlp.application:payment.paymentChargeOverview.total', - defaultMessage: 'Samtals', - description: 'Total amount label', - }, frontLabel: { id: 'ta.ovlp.application:payment.paymentChargeOverview.frontLabel', defaultMessage: 'merki að framan', diff --git a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/shared/constants.ts b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/shared/constants.ts index f62f9cd86c1f..b67eb7433299 100644 --- a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/shared/constants.ts +++ b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/shared/constants.ts @@ -1,4 +1,3 @@ export enum ApiActions { - validateApplication = 'validateApplication', submitApplication = 'submitApplication', } diff --git a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/utils/getSelectedVehicle.ts b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/utils/getSelectedVehicle.ts index ce19939d3633..fbba4d58c387 100644 --- a/libs/application/templates/transport-authority/order-vehicle-license-plate/src/utils/getSelectedVehicle.ts +++ b/libs/application/templates/transport-authority/order-vehicle-license-plate/src/utils/getSelectedVehicle.ts @@ -5,7 +5,7 @@ import { VehiclesCurrentVehicle, CurrentVehiclesAndRecords } from '../shared' export const getSelectedVehicle = ( externalData: ExternalData, answers: FormValue, -): VehiclesCurrentVehicle | undefined => { +): VehiclesCurrentVehicle => { if (answers.findVehicle) { const vehicle = getValueViaPath( answers, @@ -23,5 +23,5 @@ export const getSelectedVehicle = ( '', ) as string - return currentVehicleList?.vehicles?.[parseInt(vehicleIndex, 10)] + return currentVehicleList?.vehicles[parseInt(vehicleIndex, 10)] } diff --git a/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/dataProviders/index.ts b/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/dataProviders/index.ts index 146dc78e37f9..f0bb5f857cfd 100644 --- a/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/dataProviders/index.ts +++ b/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/dataProviders/index.ts @@ -1,7 +1,6 @@ import { defineTemplateApi, InstitutionNationalIds, - MockablePaymentCatalogApi, PaymentCatalogApi, } from '@island.is/application/types' @@ -14,14 +13,6 @@ export const SamgongustofaPaymentCatalogApi = PaymentCatalogApi.configure({ externalDataId: 'payment', }) -export const MockableSamgongustofaPaymentCatalogApi = - MockablePaymentCatalogApi.configure({ - params: { - organizationId: InstitutionNationalIds.SAMGONGUSTOFA, - }, - externalDataId: 'payment', - }) - interface CurrentVehiclesParameters { showOwned?: boolean showCoOwned?: boolean diff --git a/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/fields/MainOwner.tsx b/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/fields/MainOwner.tsx index c49b7f28397f..e4f0e995464d 100644 --- a/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/fields/MainOwner.tsx +++ b/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/fields/MainOwner.tsx @@ -1,4 +1,5 @@ import { gql, useQuery } from '@apollo/client' +import { VehiclesCurrentVehicle } from '../shared' import { FieldBaseProps } from '@island.is/application/types' import { AlertMessage, @@ -25,7 +26,7 @@ export const MainOwner: FC> = ( const vehicle = getSelectedVehicle( application.externalData, application.answers, - ) + ) as VehiclesCurrentVehicle const { data, loading, error } = useQuery( gql` @@ -34,7 +35,7 @@ export const MainOwner: FC> = ( { variables: { input: { - permno: vehicle?.permno, + permno: vehicle.permno, regno: '', vin: '', }, @@ -46,7 +47,7 @@ export const MainOwner: FC> = ( useEffect(() => { setFieldLoadingState?.(loading || !!error) - }, [loading, error, setFieldLoadingState]) + }, [loading, error]) return loading ? ( diff --git a/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/fields/VehiclesField/VehicleRadioField.tsx b/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/fields/VehiclesField/VehicleRadioField.tsx new file mode 100644 index 000000000000..af789b491604 --- /dev/null +++ b/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/fields/VehiclesField/VehicleRadioField.tsx @@ -0,0 +1,62 @@ +import { Box, Text } from '@island.is/island-ui/core' +import { FC } from 'react' +import { VehiclesCurrentVehicle } from '../../shared' +import { RadioController } from '@island.is/shared/form-fields' +import { useFormContext } from 'react-hook-form' +import { FieldBaseProps } from '@island.is/application/types' + +interface Option { + value: string + label: React.ReactNode + disabled?: boolean +} + +interface VehicleSearchFieldProps { + currentVehicleList: VehiclesCurrentVehicle[] +} + +export const VehicleRadioField: FC< + React.PropsWithChildren +> = ({ currentVehicleList }) => { + const { setValue } = useFormContext() + + const onRadioControllerSelect = (s: string) => { + const currentVehicle = currentVehicleList[parseInt(s, 10)] + setValue('pickVehicle.plate', currentVehicle.permno || '') + } + + const vehicleOptions = (vehicles: VehiclesCurrentVehicle[]) => { + const options = [] as Option[] + + for (const [index, vehicle] of vehicles.entries()) { + options.push({ + value: `${index}`, + label: ( + + + + {vehicle.make} + + + {vehicle.color} - {vehicle.permno} + + + + ), + }) + } + return options + } + + return ( +
+ +
+ ) +} diff --git a/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/fields/VehiclesField/VehicleSelectField.tsx b/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/fields/VehiclesField/VehicleSelectField.tsx index 33ca041004fc..d6b832c0a84e 100644 --- a/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/fields/VehiclesField/VehicleSelectField.tsx +++ b/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/fields/VehiclesField/VehicleSelectField.tsx @@ -1,14 +1,9 @@ import { FieldBaseProps, Option } from '@island.is/application/types' import { useLocale } from '@island.is/localization' import { FC, useState } from 'react' -import { - ActionCard, - Box, - InputError, - SkeletonLoader, -} from '@island.is/island-ui/core' +import { ActionCard, Box, SkeletonLoader } from '@island.is/island-ui/core' import { VehiclesCurrentVehicle } from '../../shared' -import { error, information } from '../../lib/messages' +import { information } from '../../lib/messages' import { SelectController } from '@island.is/shared/form-fields' import { useFormContext } from 'react-hook-form' import { getValueViaPath } from '@island.is/application/core' @@ -19,7 +14,7 @@ interface VehicleSearchFieldProps { export const VehicleSelectField: FC< React.PropsWithChildren -> = ({ currentVehicleList, application, errors }) => { +> = ({ currentVehicleList, application }) => { const { formatMessage } = useLocale() const { setValue } = useFormContext() @@ -43,10 +38,6 @@ export const VehicleSelectField: FC< : null, ) - const [plate, setPlate] = useState( - getValueViaPath(application.answers, 'pickVehicle.plate', '') as string, - ) - const onChange = (option: Option) => { const currentVehicle = currentVehicleList[parseInt(option.value, 10)] setIsLoading(true) @@ -58,7 +49,6 @@ export const VehicleSelectField: FC< role: currentVehicle?.role, }) setValue('pickVehicle.plate', currentVehicle.permno || '') - setPlate(currentVehicle.permno || '') setIsLoading(false) } } @@ -95,9 +85,6 @@ export const VehicleSelectField: FC<
)} - {!isLoading && plate.length === 0 && (errors as any)?.pickVehicle && ( - - )} ) } diff --git a/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/fields/VehiclesField/index.tsx b/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/fields/VehiclesField/index.tsx index 09f95f9eaba6..1c99f5cf28e4 100644 --- a/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/fields/VehiclesField/index.tsx +++ b/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/fields/VehiclesField/index.tsx @@ -3,22 +3,22 @@ import { FieldComponents, FieldTypes, } from '@island.is/application/types' -import { Box } from '@island.is/island-ui/core' +import { Box, InputError } from '@island.is/island-ui/core' import { FC } from 'react' import { CurrentVehiclesAndRecords } from '../../shared' +import { VehicleRadioField } from './VehicleRadioField' +import { useLocale } from '@island.is/localization' import { error, information } from '../../lib/messages' import { useLazyVehicleDetails } from '../../hooks/useLazyVehicleDetails' -import { - FindVehicleFormField, - VehicleRadioFormField, -} from '@island.is/application/ui-fields' +import { FindVehicleFormField } from '@island.is/application/ui-fields' import { ApolloQueryResult } from '@apollo/client' import { VehicleSelectField } from './VehicleSelectField' export const VehiclesField: FC> = ( props, ) => { - const { application } = props + const { formatMessage } = useLocale() + const { application, errors } = props const getVehicleDetails = useLazyVehicleDetails() const createGetVehicleDetailsWrapper = ( @@ -65,20 +65,14 @@ export const VehiclesField: FC> = ( {...props} /> ) : ( - )} + {(errors as any)?.pickVehicle && ( + + )} ) } diff --git a/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/forms/OrderVehicleRegistrationCertificateForm/InformationSection/vehicleSubSection.ts b/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/forms/OrderVehicleRegistrationCertificateForm/InformationSection/vehicleSubSection.ts index bbac58c544d2..d3b8c8e4a1b2 100644 --- a/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/forms/OrderVehicleRegistrationCertificateForm/InformationSection/vehicleSubSection.ts +++ b/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/forms/OrderVehicleRegistrationCertificateForm/InformationSection/vehicleSubSection.ts @@ -6,6 +6,7 @@ import { buildDescriptionField, buildCustomField, } from '@island.is/application/core' +import { VehiclesCurrentVehicle } from '../../../shared' import { information } from '../../../lib/messages' import { getSelectedVehicle } from '../../../utils' @@ -24,7 +25,7 @@ export const vehicleSubSection = buildSubSection({ titleVariant: 'h5', }), buildTextField({ - id: 'vehicleInfo.plate', + id: 'pickVehicle.plate', title: information.labels.vehicle.plate, backgroundColor: 'white', width: 'half', @@ -33,12 +34,12 @@ export const vehicleSubSection = buildSubSection({ const vehicle = getSelectedVehicle( application.externalData, application.answers, - ) + ) as VehiclesCurrentVehicle return vehicle?.permno }, }), buildTextField({ - id: 'vehicleInfo.type', + id: 'pickVehicle.type', title: information.labels.vehicle.type, backgroundColor: 'white', width: 'half', @@ -47,7 +48,7 @@ export const vehicleSubSection = buildSubSection({ const vehicle = getSelectedVehicle( application.externalData, application.answers, - ) + ) as VehiclesCurrentVehicle return vehicle?.make }, }), @@ -56,7 +57,10 @@ export const vehicleSubSection = buildSubSection({ title: '', component: 'MainOwner', condition: (formValue, externalData) => { - const vehicle = getSelectedVehicle(externalData, formValue) + const vehicle = getSelectedVehicle( + externalData, + formValue, + ) as VehiclesCurrentVehicle return vehicle?.role !== 'Eigandi' }, }), @@ -66,7 +70,10 @@ export const vehicleSubSection = buildSubSection({ titleVariant: 'h5', space: 3, condition: (formValue, externalData) => { - const vehicle = getSelectedVehicle(externalData, formValue) + const vehicle = getSelectedVehicle( + externalData, + formValue, + ) as VehiclesCurrentVehicle return vehicle?.role === 'Eigandi' }, }), @@ -76,7 +83,10 @@ export const vehicleSubSection = buildSubSection({ titleVariant: 'h5', space: 3, condition: (formValue, externalData) => { - const vehicle = getSelectedVehicle(externalData, formValue) + const vehicle = getSelectedVehicle( + externalData, + formValue, + ) as VehiclesCurrentVehicle return vehicle?.role !== 'Eigandi' }, }), @@ -106,7 +116,10 @@ export const vehicleSubSection = buildSubSection({ width: 'half', readOnly: true, condition: (formValue, externalData) => { - const vehicle = getSelectedVehicle(externalData, formValue) + const vehicle = getSelectedVehicle( + externalData, + formValue, + ) as VehiclesCurrentVehicle return vehicle?.role === 'Eigandi' }, defaultValue: (application: Application) => @@ -119,7 +132,10 @@ export const vehicleSubSection = buildSubSection({ width: 'half', readOnly: true, condition: (formValue, externalData) => { - const vehicle = getSelectedVehicle(externalData, formValue) + const vehicle = getSelectedVehicle( + externalData, + formValue, + ) as VehiclesCurrentVehicle return vehicle?.role === 'Eigandi' }, defaultValue: (application: Application) => diff --git a/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/forms/OrderVehicleRegistrationCertificateForm/prerequisitesSection.ts b/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/forms/OrderVehicleRegistrationCertificateForm/prerequisitesSection.ts index 9cc00d156b20..4c1ab06ac0ff 100644 --- a/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/forms/OrderVehicleRegistrationCertificateForm/prerequisitesSection.ts +++ b/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/forms/OrderVehicleRegistrationCertificateForm/prerequisitesSection.ts @@ -8,7 +8,6 @@ import { IdentityApi, SamgongustofaPaymentCatalogApi, CurrentVehiclesApi, - MockableSamgongustofaPaymentCatalogApi, } from '../../dataProviders' export const prerequisitesSection = buildSection({ @@ -35,10 +34,6 @@ export const prerequisitesSection = buildSection({ provider: SamgongustofaPaymentCatalogApi, title: '', }), - buildDataProviderItem({ - provider: MockableSamgongustofaPaymentCatalogApi, - title: '', - }), ], }), ], diff --git a/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/lib/OrderVehicleRegistrationCertificateTemplate.ts b/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/lib/OrderVehicleRegistrationCertificateTemplate.ts index cbe4113c072e..ad71d254401d 100644 --- a/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/lib/OrderVehicleRegistrationCertificateTemplate.ts +++ b/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/lib/OrderVehicleRegistrationCertificateTemplate.ts @@ -24,7 +24,6 @@ import { IdentityApi, SamgongustofaPaymentCatalogApi, CurrentVehiclesApi, - MockableSamgongustofaPaymentCatalogApi, } from '../dataProviders' import { AuthDelegationType } from '@island.is/shared/types' import { ApiScope } from '@island.is/auth/scopes' @@ -108,7 +107,6 @@ const template: ApplicationTemplate< api: [ IdentityApi, SamgongustofaPaymentCatalogApi, - MockableSamgongustofaPaymentCatalogApi, CurrentVehiclesApi, ], }, diff --git a/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/utils/getSelectedVehicle.ts b/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/utils/getSelectedVehicle.ts index deb5579e7385..a849d4c77742 100644 --- a/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/utils/getSelectedVehicle.ts +++ b/libs/application/templates/transport-authority/order-vehicle-registration-certificate/src/utils/getSelectedVehicle.ts @@ -5,7 +5,7 @@ import { CurrentVehiclesAndRecords, VehiclesCurrentVehicle } from '../shared' export const getSelectedVehicle = ( externalData: ExternalData, answers: FormValue, -): VehiclesCurrentVehicle | undefined => { +): VehiclesCurrentVehicle => { if (answers.findVehicle) { const vehicle = getValueViaPath( answers, diff --git a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/ApplicationStatus/index.tsx b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/ApplicationStatus/index.tsx index 8a20a0ee11b9..14a13ea40c7a 100644 --- a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/ApplicationStatus/index.tsx +++ b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/ApplicationStatus/index.tsx @@ -9,7 +9,7 @@ import { FC } from 'react' import { review } from '../../lib/messages' import { States } from '../../lib/constants' import { ReviewScreenProps } from '../../shared' -import { getReviewSteps, canReviewerApprove } from '../../utils' +import { getReviewSteps, hasReviewerApproved } from '../../utils' import { StatusStep } from './StatusStep' import { MessageWithLinkButtonFormField } from '@island.is/application/ui-fields' import { coreMessages } from '@island.is/application/core' @@ -27,7 +27,7 @@ export const ApplicationStatus: FC< const steps = getReviewSteps(application, coOwnersAndOperators || []) - const showReviewButton = canReviewerApprove( + const showReviewButton = !hasReviewerApproved( reviewerNationalId, application.answers, ) diff --git a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/CoOwner/index.tsx b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/CoOwner/index.tsx index 50e921cb8561..b31da67bb076 100644 --- a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/CoOwner/index.tsx +++ b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/CoOwner/index.tsx @@ -14,6 +14,7 @@ import { InputController } from '@island.is/shared/form-fields' import { FC, useEffect } from 'react' import { GET_VEHICLE_INFORMATION } from '../../graphql/queries' import { information } from '../../lib/messages' +import { VehiclesCurrentVehicle } from '../../shared' import { getSelectedVehicle } from '../../utils' export const CoOwner: FC> = (props) => { @@ -25,7 +26,7 @@ export const CoOwner: FC> = (props) => { const vehicle = getSelectedVehicle( application.externalData, application.answers, - ) + ) as VehiclesCurrentVehicle const { data, loading, error } = useQuery( gql` @@ -34,7 +35,7 @@ export const CoOwner: FC> = (props) => { { variables: { input: { - permno: vehicle?.permno, + permno: vehicle.permno, regno: '', vin: '', }, @@ -44,7 +45,7 @@ export const CoOwner: FC> = (props) => { useEffect(() => { setFieldLoadingState?.(loading || !!error) - }, [loading, error, setFieldLoadingState]) + }, [loading, error]) return !loading && !error ? ( data?.vehiclesDetail?.coOwners && diff --git a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/Overview/index.tsx b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/Overview/index.tsx index ce401d658ae1..b0c19cfc84ef 100644 --- a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/Overview/index.tsx +++ b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/Overview/index.tsx @@ -5,11 +5,17 @@ import { Text, Divider, Button, + AlertMessage, InputError, } from '@island.is/island-ui/core' import { ReviewScreenProps } from '../../shared' import { useLocale } from '@island.is/localization' -import { overview, review, error as errorMsg } from '../../lib/messages' +import { + applicationCheck, + overview, + review, + error as errorMsg, +} from '../../lib/messages' import { States } from '../../lib/constants' import { VehicleSection, @@ -21,8 +27,7 @@ import { } from './sections' import { getApproveAnswers, - canReviewerApprove, - canReviewerReApprove, + hasReviewerApproved, isLastReviewer, } from '../../utils' import { RejectConfirmationModal } from './RejectConfirmationModal' @@ -30,10 +35,14 @@ import { APPLICATION_APPLICATION, SUBMIT_APPLICATION, } from '@island.is/application/graphql' -import { useLazyQuery, useMutation } from '@apollo/client' +import { gql, useLazyQuery, useMutation } from '@apollo/client' import { getValueViaPath } from '@island.is/application/core' +import { + OwnerChangeAnswers, + OwnerChangeValidationMessage, +} from '@island.is/api/schema' +import { VALIDATE_VEHICLE_OWNER_CHANGE } from '../../graphql/queries' import { TransferOfVehicleOwnershipAnswers } from '../..' -import { ValidationErrorMessages } from '../ValidationErrorMessages' export const Overview: FC< React.PropsWithChildren @@ -62,14 +71,11 @@ export const Overview: FC< const [submitApplication, { error }] = useMutation(SUBMIT_APPLICATION, { onError: (e) => { console.error(e, e.message) - setButtonLoading(false) return }, }) - const [buttonLoading, setButtonLoading] = useState(false) - const [shouldLoadValidation, setShouldLoadValidation] = useState(false) - const [validationErrorFound, setValidationErrorFound] = useState(false) + const [loading, setLoading] = useState(false) const isBuyer = (getValueViaPath(answers, 'buyer.nationalId', '') as string) === @@ -136,17 +142,33 @@ export const Overview: FC< }) if (resSubmit?.data) { - setButtonLoading(false) + setLoading(false) setStep && setStep('conclusion') } } else { - setButtonLoading(false) + setLoading(false) setStep && setStep('conclusion') } } } } + const [validateVehicleThenApproveAndSubmit, { data }] = useLazyQuery< + any, + { answers: OwnerChangeAnswers } + >( + gql` + ${VALIDATE_VEHICLE_OWNER_CHANGE} + `, + { + onCompleted: async (data) => { + if (!data?.vehicleOwnerChangeValidation?.hasError) { + await doApproveAndSubmit() + } + }, + }, + ) + const onBackButtonClick = () => { setStep && setStep('states') } @@ -160,10 +182,65 @@ export const Overview: FC< setNoInsuranceError(true) } else { setNoInsuranceError(false) + setLoading(true) - setButtonLoading(true) - setShouldLoadValidation(true) - await doApproveAndSubmit() + if (isBuyer) { + // Need to get updated application, in case buyer has changed co-owner + const applicationInfo = await getApplicationInfo({ + variables: { + input: { + id: application.id, + }, + locale: 'is', + }, + fetchPolicy: 'no-cache', + }) + const updatedApplication = applicationInfo?.data?.applicationApplication + + if (updatedApplication) { + const currentAnswers: OwnerChangeAnswers = updatedApplication.answers + + validateVehicleThenApproveAndSubmit({ + variables: { + answers: { + pickVehicle: { + plate: currentAnswers?.pickVehicle?.plate, + }, + vehicle: { + date: currentAnswers?.vehicle?.date, + salePrice: currentAnswers?.vehicle?.salePrice, + }, + vehicleMileage: { + value: answers?.vehicleMileage?.value, + }, + seller: { + email: currentAnswers?.seller?.email, + nationalId: currentAnswers?.seller?.nationalId, + }, + buyer: { + email: currentAnswers?.buyer?.email, + nationalId: currentAnswers?.buyer?.nationalId, + }, + buyerCoOwnerAndOperator: + currentAnswers?.buyerCoOwnerAndOperator?.map((x) => ({ + nationalId: x.nationalId || '', + email: x.email || '', + type: x.type, + wasRemoved: x.wasRemoved, + })), + buyerMainOperator: currentAnswers?.buyerMainOperator + ? { + nationalId: currentAnswers.buyerMainOperator.nationalId, + } + : null, + insurance: insurance ? { value: insurance } : null, + }, + }, + }) + } + } else { + await doApproveAndSubmit() + } } } @@ -201,27 +278,60 @@ export const Overview: FC< noInsuranceError={noInsuranceError} /> - {!buttonLoading && shouldLoadValidation && ( - - )} - - {!validationErrorFound && error && ( + {error && ( )} + {data?.vehicleOwnerChangeValidation?.hasError && + data.vehicleOwnerChangeValidation.errorMessages.length > 0 ? ( + + +
    + {data.vehicleOwnerChangeValidation.errorMessages.map( + (error: OwnerChangeValidationMessage) => { + const message = formatMessage( + getValueViaPath( + applicationCheck.validation, + error?.errorNo || '', + ), + ) + const defaultMessage = error.defaultMessage + const fallbackMessage = + formatMessage( + applicationCheck.validation.fallbackErrorMessage, + ) + + ' - ' + + error?.errorNo + + return ( +
  • + + {message || defaultMessage || fallbackMessage} + +
  • + ) + }, + )} +
+
+ } + /> + + ) : null} + - {canReviewerApprove(reviewerNationalId, application.answers) && + {!hasReviewerApproved(reviewerNationalId, application.answers) && application.state !== States.COMPLETED && ( @@ -236,7 +346,7 @@ export const Overview: FC< - - )} diff --git a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/Overview/sections/BuyerSection.tsx b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/Overview/sections/BuyerSection.tsx index dcb08a7493fa..3aefa52bbfb4 100644 --- a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/Overview/sections/BuyerSection.tsx +++ b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/Overview/sections/BuyerSection.tsx @@ -8,7 +8,7 @@ import { information, overview, review } from '../../../lib/messages' import { States } from '../../../lib/constants' import { ReviewGroup } from '../../ReviewGroup' import { ReviewScreenProps } from '../../../shared' -import { formatPhoneNumber, canReviewerApprove } from '../../../utils' +import { formatPhoneNumber, hasReviewerApproved } from '../../../utils' import kennitala from 'kennitala' export const BuyerSection: FC< @@ -30,7 +30,7 @@ export const BuyerSection: FC< > -} - export const ValidationErrorMessages: FC< - React.PropsWithChildren -> = ({ showErrorOnly, setValidationErrorFound, ...props }) => { + React.PropsWithChildren +> = (props) => { const { application, setFieldLoadingState } = props const { formatMessage } = useLocale() @@ -72,18 +61,12 @@ export const ValidationErrorMessages: FC< : null, }, }, - onCompleted: (data) => { - if (data?.vehicleOwnerChangeValidation?.hasError) { - setValidationErrorFound?.(true) - } - }, - fetchPolicy: 'no-cache', }, ) useEffect(() => { setFieldLoadingState?.(loading) - }, [loading, setFieldLoadingState]) + }, [loading]) return data?.vehicleOwnerChangeValidation?.hasError && data.vehicleOwnerChangeValidation.errorMessages.length > 0 ? ( @@ -93,7 +76,7 @@ export const ValidationErrorMessages: FC< title={formatMessage(applicationCheck.validation.alertTitle)} message={ - +
    {data.vehicleOwnerChangeValidation.errorMessages.map( (error: OwnerChangeValidationMessage) => { const message = formatMessage( @@ -111,18 +94,20 @@ export const ValidationErrorMessages: FC< error?.errorNo return ( - - {message || defaultMessage || fallbackMessage} - +
  • + + {message || defaultMessage || fallbackMessage} + +
  • ) }, )} - +
} />
- ) : !showErrorOnly ? ( + ) : ( - ) : null + ) } diff --git a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/VehiclesField/VehicleRadioField.tsx b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/VehiclesField/VehicleRadioField.tsx new file mode 100644 index 000000000000..18da50ff8a31 --- /dev/null +++ b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/VehiclesField/VehicleRadioField.tsx @@ -0,0 +1,142 @@ +import { + AlertMessage, + Box, + Bullet, + BulletList, + Text, + InputError, +} from '@island.is/island-ui/core' +import { useLocale } from '@island.is/localization' +import { FC, useState } from 'react' +import { VehiclesCurrentVehicleWithOwnerchangeChecks } from '../../shared' +import { information, applicationCheck, error } from '../../lib/messages' +import { RadioController } from '@island.is/shared/form-fields' +import { useFormContext } from 'react-hook-form' +import { getValueViaPath } from '@island.is/application/core' +import { FieldBaseProps } from '@island.is/application/types' + +interface Option { + value: string + label: React.ReactNode + disabled?: boolean +} + +interface VehicleSearchFieldProps { + currentVehicleList: VehiclesCurrentVehicleWithOwnerchangeChecks[] +} + +export const VehicleRadioField: FC< + React.PropsWithChildren +> = ({ currentVehicleList, application, errors }) => { + const { formatMessage } = useLocale() + const { setValue } = useFormContext() + + const [plate, setPlate] = useState( + getValueViaPath(application.answers, 'pickVehicle.plate', '') as string, + ) + + const onRadioControllerSelect = (s: string) => { + const currentVehicle = currentVehicleList[parseInt(s, 10)] + const permno = currentVehicle.permno || '' + + setPlate(permno) + setValue('pickVehicle.plate', permno) + setValue('pickVehicle.type', currentVehicle.make) + setValue('pickVehicle.color', currentVehicle.color || undefined) + setValue('vehicleMileage.requireMileage', currentVehicle?.requireMileage) + setValue('vehicleMileage.mileageReading', currentVehicle?.mileageReading) + if (permno) setValue('vehicleInfo.plate', permno) + if (permno) setValue('vehicleInfo.type', currentVehicle.make) + } + + const vehicleOptions = ( + vehicles: VehiclesCurrentVehicleWithOwnerchangeChecks[], + ) => { + const options = [] as Option[] + + for (const [index, vehicle] of vehicles.entries()) { + const disabled = + !vehicle.isDebtLess || !!vehicle.validationErrorMessages?.length + options.push({ + value: `${index}`, + label: ( + + + + {vehicle.make} + + + {vehicle.color} - {vehicle.permno} + + + {disabled && ( + + + + {!vehicle.isDebtLess && ( + + {formatMessage( + information.labels.pickVehicle.isNotDebtLessTag, + )} + + )} + {!!vehicle.validationErrorMessages?.length && + vehicle.validationErrorMessages?.map((error) => { + const message = formatMessage( + getValueViaPath( + applicationCheck.validation, + error.errorNo || '', + ), + ) + const defaultMessage = error.defaultMessage + const fallbackMessage = + formatMessage( + applicationCheck.validation + .fallbackErrorMessage, + ) + + ' - ' + + error.errorNo + + return ( + + {message || defaultMessage || fallbackMessage} + + ) + })} + + + } + /> + + )} + + ), + disabled: disabled, + }) + } + return options + } + + return ( +
+ + {plate.length === 0 && (errors as any)?.pickVehicle && ( + + )} +
+ ) +} diff --git a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/VehiclesField/index.tsx b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/VehiclesField/index.tsx index fb33b27bda13..a3b37a8d45be 100644 --- a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/VehiclesField/index.tsx +++ b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/fields/VehiclesField/index.tsx @@ -5,16 +5,14 @@ import { } from '@island.is/application/types' import { Box } from '@island.is/island-ui/core' import { FC, useCallback, useEffect } from 'react' +import { VehicleRadioField } from './VehicleRadioField' import { useFormContext } from 'react-hook-form' import { CurrentVehiclesAndRecords } from '../../shared' import { ApolloQueryResult, useMutation } from '@apollo/client' import { UPDATE_APPLICATION } from '@island.is/application/graphql' import { useLocale } from '@island.is/localization' import { VehicleSelectField } from './VehicleSelectField' -import { - FindVehicleFormField, - VehicleRadioFormField, -} from '@island.is/application/ui-fields' +import { FindVehicleFormField } from '@island.is/application/ui-fields' import { information, applicationCheck, error } from '../../lib/messages' import { useLazyVehicleDetails } from '../../hooks/useLazyVehicleDetails' @@ -94,27 +92,9 @@ export const VehiclesField: FC> = ( {...props} /> ) : ( - )} diff --git a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/forms/TransferOfVehicleOwnershipForm/InformationSection/vehicleSubSection.ts b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/forms/TransferOfVehicleOwnershipForm/InformationSection/vehicleSubSection.ts index 7aba9479e20f..24869654cb25 100644 --- a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/forms/TransferOfVehicleOwnershipForm/InformationSection/vehicleSubSection.ts +++ b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/forms/TransferOfVehicleOwnershipForm/InformationSection/vehicleSubSection.ts @@ -7,6 +7,7 @@ import { buildHiddenInput, } from '@island.is/application/core' import { information } from '../../../lib/messages' +import { VehiclesCurrentVehicle } from '../../../shared' import { getSelectedVehicle } from '../../../utils' export const vehicleSubSection = buildSubSection({ @@ -28,8 +29,8 @@ export const vehicleSubSection = buildSubSection({ const vehicle = getSelectedVehicle( application.externalData, application.answers, - ) - return vehicle?.permno + ) as VehiclesCurrentVehicle + return vehicle.permno }, }), buildTextField({ @@ -42,8 +43,8 @@ export const vehicleSubSection = buildSubSection({ const vehicle = getSelectedVehicle( application.externalData, application.answers, - ) - return vehicle?.make + ) as VehiclesCurrentVehicle + return vehicle.make }, }), buildTextField({ @@ -71,8 +72,8 @@ export const vehicleSubSection = buildSubSection({ const vehicle = getSelectedVehicle( application.externalData, application.answers, - ) - return vehicle?.requireMileage || false + ) as VehiclesCurrentVehicle + return vehicle.requireMileage || false }, }), buildHiddenInput({ @@ -81,8 +82,8 @@ export const vehicleSubSection = buildSubSection({ const vehicle = getSelectedVehicle( application.externalData, application.answers, - ) - return vehicle?.mileageReading || '' + ) as VehiclesCurrentVehicle + return vehicle.mileageReading || '' }, }), buildTextField({ @@ -91,15 +92,18 @@ export const vehicleSubSection = buildSubSection({ width: 'full', variant: 'number', condition: (answers, externalData) => { - const vehicle = getSelectedVehicle(externalData, answers) + const vehicle = getSelectedVehicle( + externalData, + answers, + ) as VehiclesCurrentVehicle return vehicle?.requireMileage || false }, placeholder(application) { const vehicle = getSelectedVehicle( application.externalData, application.answers, - ) - return vehicle?.mileageReading + ) as VehiclesCurrentVehicle + return vehicle.mileageReading ? `Síðasta skráning ${vehicle.mileageReading} Km` : '' }, diff --git a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/lib/TransferOfVehicleOwnershipTemplate.ts b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/lib/TransferOfVehicleOwnershipTemplate.ts index a1a53cc58794..c77287a619be 100644 --- a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/lib/TransferOfVehicleOwnershipTemplate.ts +++ b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/lib/TransferOfVehicleOwnershipTemplate.ts @@ -34,7 +34,7 @@ import { CurrentVehiclesApi, InsuranceCompaniesApi, } from '../dataProviders' -import { getChargeItemCodes, getExtraData, canReviewerApprove } from '../utils' +import { getChargeItemCodes, getExtraData, hasReviewerApproved } from '../utils' import { ApiScope } from '@island.is/auth/scopes' import { buildPaymentState } from '@island.is/application/utils' @@ -63,7 +63,7 @@ const reviewStatePendingAction = ( role: string, nationalId: string, ): PendingAction => { - if (nationalId && canReviewerApprove(nationalId, application.answers)) { + if (nationalId && !hasReviewerApproved(nationalId, application.answers)) { return { title: corePendingActionMessages.waitingForReviewTitle, content: corePendingActionMessages.youNeedToReviewDescription, diff --git a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/lib/messages/review.ts b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/lib/messages/review.ts index fef98cc77fff..dbd3a26196d1 100644 --- a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/lib/messages/review.ts +++ b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/lib/messages/review.ts @@ -157,13 +157,8 @@ export const review = { }, approve: { id: 'ta.tvo.application:review.buttons.approve', - defaultMessage: 'Samþykkja', + defaultMessage: `Samþykkja`, description: 'Approve button in review process', }, - tryAgain: { - id: 'ta.tvo.application:review.buttons.tryAgain', - defaultMessage: 'Reyna aftur', - description: 'Try again button in review process', - }, }), } diff --git a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/lib/messages/steps.ts b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/lib/messages/steps.ts index 3f62e174505f..07d8a78ec0f5 100644 --- a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/lib/messages/steps.ts +++ b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/lib/messages/steps.ts @@ -27,7 +27,7 @@ export const review = { }, approve: { id: 'ta.tvo.application:review.buttons.approve', - defaultMessage: 'Samþykkja', + defaultMessage: `Samþykkja`, description: 'Approve button in review process', }, }), diff --git a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/utils/canReviewerApprove.ts b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/utils/canReviewerApprove.ts deleted file mode 100644 index 0c4bfe337640..000000000000 --- a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/utils/canReviewerApprove.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { getValueViaPath } from '@island.is/application/core' -import { CoOwnerAndOperator, UserInformation } from '../shared' -import { applicationHasPendingApproval } from './isLastReviewer' - -// Function to check if the reviewer is authorized to approve and hasn't done that yet -export const canReviewerApprove = ( - reviewerNationalId: string, - answers: FormValue, -): boolean => { - // Check if reviewer is buyer and has not approved - const buyer = getValueViaPath(answers, 'buyer') as UserInformation - if (buyer?.nationalId === reviewerNationalId && !buyer.approved) { - return true - } - - // Check if reviewer is buyer's co-owner or operator and has not approved - const buyerCoOwnersAndOperators = ( - getValueViaPath( - answers, - 'buyerCoOwnerAndOperator', - [], - ) as CoOwnerAndOperator[] - ).filter(({ wasRemoved }) => wasRemoved !== 'true') - if ( - buyerCoOwnersAndOperators.some( - ({ nationalId, approved }) => - nationalId === reviewerNationalId && !approved, - ) - ) { - return true - } - - // Check if reviewer is seller's co-owner and has not approved - const sellerCoOwners = getValueViaPath( - answers, - 'sellerCoOwner', - [], - ) as CoOwnerAndOperator[] - if ( - sellerCoOwners.some( - ({ nationalId, approved }) => - nationalId === reviewerNationalId && !approved, - ) - ) { - return true - } - - return false -} - -// Special case to allow seller (or any reviewer) to trigger an external API call to complete owner change -// Necessary when approve is updated in answers, but application is still stuck in REVIEW state -// then any user can try to 'push' the application to the next state -export const canReviewerReApprove = ( - reviewerNationalId: string, - answers: FormValue, -): boolean => { - const sellerNationalId = getValueViaPath( - answers, - 'seller.nationalId', - '', - ) as string - const buyerNationalId = getValueViaPath( - answers, - 'buyer.nationalId', - '', - ) as string - const buyerCoOwnersAndOperators = ( - getValueViaPath( - answers, - 'buyerCoOwnerAndOperator', - [], - ) as CoOwnerAndOperator[] - ).filter(({ wasRemoved }) => wasRemoved !== 'true') - const sellerCoOwners = getValueViaPath( - answers, - 'sellerCoOwner', - [], - ) as CoOwnerAndOperator[] - - const isReviewerAuthorized = [ - sellerNationalId === reviewerNationalId, - buyerNationalId === reviewerNationalId, - buyerCoOwnersAndOperators.some( - ({ nationalId }) => nationalId === reviewerNationalId, - ), - sellerCoOwners.some(({ nationalId }) => nationalId === reviewerNationalId), - ].some(Boolean) - - // Check if the reviewer is authorized and if all required approvals have been completed - return isReviewerAuthorized && !applicationHasPendingApproval(answers) -} diff --git a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/utils/getSelectedVehicle.ts b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/utils/getSelectedVehicle.ts index 38e162f6a245..a13ea829dc3c 100644 --- a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/utils/getSelectedVehicle.ts +++ b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/utils/getSelectedVehicle.ts @@ -5,7 +5,7 @@ import { CurrentVehiclesAndRecords, VehiclesCurrentVehicle } from '../shared' export const getSelectedVehicle = ( externalData: ExternalData, answers: FormValue, -): VehiclesCurrentVehicle | undefined => { +): VehiclesCurrentVehicle => { if (answers.findVehicle) { const vehicle = getValueViaPath( answers, diff --git a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/utils/hasReviewerApproved.ts b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/utils/hasReviewerApproved.ts new file mode 100644 index 000000000000..02d342d8c7f7 --- /dev/null +++ b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/utils/hasReviewerApproved.ts @@ -0,0 +1,64 @@ +import { FormValue } from '@island.is/application/types' +import { getValueViaPath } from '@island.is/application/core' +import { CoOwnerAndOperator, UserInformation } from '../shared' +import { isLastReviewer } from './isLastReviewer' + +export const hasReviewerApproved = ( + reviewerNationalId: string, + answers: FormValue, +) => { + // Check if reviewer is buyer and has not approved + if ( + (getValueViaPath(answers, 'buyer.nationalId', '') as string) === + reviewerNationalId + ) { + const buyer = getValueViaPath(answers, 'buyer') as UserInformation + const hasApproved = buyer?.approved || false + if (!hasApproved) return false + } + + // Check if reviewer is buyers coowner or operator and has not approved + const filteredBuyerCoOwnersAndOperators = ( + getValueViaPath( + answers, + 'buyerCoOwnerAndOperator', + [], + ) as CoOwnerAndOperator[] + ).filter(({ wasRemoved }) => wasRemoved !== 'true') + const buyerCoOwnerAndOperator = filteredBuyerCoOwnersAndOperators.find( + (coOwnerOrOperator) => coOwnerOrOperator.nationalId === reviewerNationalId, + ) + if (buyerCoOwnerAndOperator) { + const hasApproved = buyerCoOwnerAndOperator?.approved || false + if (!hasApproved) return false + } + + // Check if reviewer is sellers coowner and has not approved + const sellerCoOwners = getValueViaPath( + answers, + 'sellerCoOwner', + [], + ) as CoOwnerAndOperator[] + const sellerCoOwner = sellerCoOwners.find( + (coOwner) => coOwner.nationalId === reviewerNationalId, + ) + if (sellerCoOwner) { + const hasApproved = sellerCoOwner?.approved || false + if (!hasApproved) return false + } + + // Check if reviewer is seller and everyone else has approved + if ( + (getValueViaPath(answers, 'seller.nationalId', '') as string) === + reviewerNationalId && + isLastReviewer( + reviewerNationalId, + answers, + filteredBuyerCoOwnersAndOperators, + ) + ) { + return false + } + + return true +} diff --git a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/utils/index.ts b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/utils/index.ts index 51bafc528202..ac668869ba8b 100644 --- a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/utils/index.ts +++ b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/utils/index.ts @@ -13,7 +13,7 @@ export const formatPhoneNumber = (value: string): string => export { getSelectedVehicle } from './getSelectedVehicle' export { getReviewSteps } from './getReviewSteps' -export { canReviewerApprove, canReviewerReApprove } from './canReviewerApprove' +export { hasReviewerApproved } from './hasReviewerApproved' export { getApproveAnswers } from './getApproveAnswers' export { isLastReviewer } from './isLastReviewer' export { getRejecter } from './getRejecter' diff --git a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/utils/isLastReviewer.ts b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/utils/isLastReviewer.ts index f57645404e72..8c3dacd58168 100644 --- a/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/utils/isLastReviewer.ts +++ b/libs/application/templates/transport-authority/transfer-of-vehicle-ownership/src/utils/isLastReviewer.ts @@ -2,97 +2,77 @@ import { getValueViaPath } from '@island.is/application/core' import { FormValue } from '@island.is/application/types' import { CoOwnerAndOperator, UserInformation } from '../shared' -// Function to check if an application has pending approval -export const applicationHasPendingApproval = ( +export const isLastReviewer = ( + reviewerNationalId: string, answers: FormValue, - excludeNationalId?: string, -): boolean => { - // Check if buyer has not approved + newBuyerCoOwnerAndOperator: CoOwnerAndOperator[], +) => { + // 1. First check if any reviewer that is not the current user has not approved + + // Buyer const buyer = getValueViaPath(answers, 'buyer', {}) as UserInformation - if ( - (!excludeNationalId || buyer.nationalId !== excludeNationalId) && - !buyer.approved - ) { - return true + if (buyer.nationalId !== reviewerNationalId && !buyer.approved) { + return false } - // Check if any buyer's co-owners/operators have not approved - const buyerCoOwnersAndOperators = ( + // Buyer's co-owner / Buyer's operator + const oldBuyerCoOwnersAndOperators = ( getValueViaPath( answers, 'buyerCoOwnerAndOperator', [], ) as CoOwnerAndOperator[] ).filter(({ wasRemoved }) => wasRemoved !== 'true') - if ( - buyerCoOwnersAndOperators.some( - ({ nationalId, approved }) => - (!excludeNationalId || nationalId !== excludeNationalId) && !approved, - ) - ) { - return true + const buyerCoOwnerAndOperatorHasNotApproved = + oldBuyerCoOwnersAndOperators.find((coOwnerOrOperator) => { + return ( + coOwnerOrOperator.nationalId !== reviewerNationalId && + !coOwnerOrOperator.approved + ) + }) + if (buyerCoOwnerAndOperatorHasNotApproved) { + return false } - // Check if any seller's co-owners have not approved + // Seller's co-owner const sellerCoOwners = getValueViaPath( answers, 'sellerCoOwner', [], ) as CoOwnerAndOperator[] - if ( - sellerCoOwners.some( - ({ nationalId, approved }) => - (!excludeNationalId || nationalId !== excludeNationalId) && !approved, - ) - ) { - return true + const sellerCoOwnerNotApproved = sellerCoOwners.find( + (coOwner) => coOwner.nationalId !== reviewerNationalId && !coOwner.approved, + ) + if (sellerCoOwnerNotApproved) { + return false } - return false -} - -// Function to check if the current reviewer is the last one who needs to approve -export const isLastReviewer = ( - reviewerNationalId: string, - answers: FormValue, - newBuyerCoOwnerAndOperator: CoOwnerAndOperator[], -): boolean => { - // If there are pending approvals (excluding current reviewer), then he is not the last reviewer - if (applicationHasPendingApproval(answers, reviewerNationalId)) return false + // 2. Then check if user which is the last reviewer is a buyer and is adding more reviewers - // If the current reviewer is the buyer, check for changes in buyer's co-owners/operators list - const buyer = getValueViaPath(answers, 'buyer', {}) as UserInformation if (buyer.nationalId === reviewerNationalId) { - const oldBuyerCoOwnersAndOperators = ( - getValueViaPath( - answers, - 'buyerCoOwnerAndOperator', - [], - ) as CoOwnerAndOperator[] - ).filter(({ wasRemoved }) => wasRemoved !== 'true') - - // If no changes in buyer co-owner/operator list, the buyer is the last reviewer + // Check if buyerCoOwnerAndOperator did not change, then buyer is last reviewer if (newBuyerCoOwnerAndOperator === oldBuyerCoOwnersAndOperators) { return true } - // If new buyer co-owners/operators have been added, buyer is not the last reviewer + // Check if buyer added to buyerCoOwnerAndOperator, then buyer is not the last reviewer if ( newBuyerCoOwnerAndOperator.length > oldBuyerCoOwnersAndOperators.length ) { return false } - // If new reviewers were added (and others removed), the buyer is not the last reviewer - const newReviewerAdded = newBuyerCoOwnerAndOperator.some( - ({ nationalId }) => - !oldBuyerCoOwnersAndOperators.some( - (oldReviewer) => oldReviewer.nationalId === nationalId, - ), - ) - return !newReviewerAdded + //Check if buyer added (and removed) in buyerCoOwnerAndOperator, then buyer is not the last reviewer + const newReviewer = newBuyerCoOwnerAndOperator.find((newReviewer) => { + const sameReviewer = oldBuyerCoOwnersAndOperators.find( + (oldReviewer) => oldReviewer.nationalId === newReviewer.nationalId, + ) + return !sameReviewer + }) + if (newReviewer) { + return false + } } - // Otherwise, the only review missing is from the current reviewer return true } diff --git a/libs/application/templates/university/src/lib/messages/payment.ts b/libs/application/templates/university/src/lib/messages/payment.ts index 2224458691c5..8995d3536af5 100644 --- a/libs/application/templates/university/src/lib/messages/payment.ts +++ b/libs/application/templates/university/src/lib/messages/payment.ts @@ -13,6 +13,18 @@ export const payment = { description: 'Payment page title', }, }), + paymentChargeOverview: defineMessages({ + forPayment: { + id: 'uni.application:payment.paymentChargeOverview.forPayment', + defaultMessage: 'Til greiðslu', + description: 'For payment label', + }, + total: { + id: 'uni.application:payment.paymentChargeOverview.total', + defaultMessage: 'Samtals', + description: 'Total amount label', + }, + }), confirmation: defineMessages({ confirm: { id: 'uni.application:payment.confirmation.confirm', diff --git a/libs/application/types/src/lib/Fields.ts b/libs/application/types/src/lib/Fields.ts index 67e1c8223cc4..e4bab487a79d 100644 --- a/libs/application/types/src/lib/Fields.ts +++ b/libs/application/types/src/lib/Fields.ts @@ -256,7 +256,6 @@ export enum FieldTypes { HIDDEN_INPUT = 'HIDDEN_INPUT', HIDDEN_INPUT_WITH_WATCHED_VALUE = 'HIDDEN_INPUT_WITH_WATCHED_VALUE', FIND_VEHICLE = 'FIND_VEHICLE', - VEHICLE_RADIO = 'VEHICLE_RADIO', STATIC_TABLE = 'STATIC_TABLE', ACCORDION = 'ACCORDION', BANK_ACCOUNT = 'BANK_ACCOUNT', @@ -291,7 +290,6 @@ export enum FieldComponents { TABLE_REPEATER = 'TableRepeaterFormField', HIDDEN_INPUT = 'HiddenInputFormField', FIND_VEHICLE = 'FindVehicleFormField', - VEHICLE_RADIO = 'VehicleRadioFormField', STATIC_TABLE = 'StaticTableFormField', ACCORDION = 'AccordionFormField', BANK_ACCOUNT = 'BankAccountFormField', @@ -315,8 +313,6 @@ export interface DateField extends InputField { component: FieldComponents.DATE maxDate?: MaybeWithApplicationAndField minDate?: MaybeWithApplicationAndField - minYear?: number - maxYear?: number excludeDates?: MaybeWithApplicationAndField backgroundColor?: DatePickerBackgroundColor onChange?(date: string): void @@ -628,7 +624,6 @@ export type TableRepeaterField = BaseField & { component: FieldComponents.TABLE_REPEATER formTitle?: StaticText addItemButtonText?: StaticText - cancelButtonText?: StaticText saveItemButtonText?: StaticText getStaticTableData?: (application: Application) => Record[] removeButtonTooltipText?: StaticText @@ -677,22 +672,6 @@ export interface FindVehicleField extends InputField { energyFundsMessages?: Record } -export interface VehicleRadioField extends InputField { - readonly type: FieldTypes.VEHICLE_RADIO - component: FieldComponents.VEHICLE_RADIO - itemType: 'VEHICLE' | 'PLATE' - itemList: unknown[] - shouldValidateDebtStatus?: boolean - shouldValidateRenewal?: boolean - alertMessageErrorTitle?: FormText - validationErrorMessages?: Record - validationErrorFallbackMessage?: FormText - inputErrorMessage: FormText - debtStatusErrorMessage?: FormText - renewalExpiresAtTag?: StaticText - validateRenewal?: (item: unknown) => boolean -} - export interface HiddenInputWithWatchedValueField extends BaseField { watchValue: string type: FieldTypes.HIDDEN_INPUT_WITH_WATCHED_VALUE @@ -787,7 +766,6 @@ export type Field = | HiddenInputWithWatchedValueField | HiddenInputField | FindVehicleField - | VehicleRadioField | StaticTableField | AccordionField | BankAccountField diff --git a/libs/application/ui-fields/src/lib/DateFormField/DateFormField.tsx b/libs/application/ui-fields/src/lib/DateFormField/DateFormField.tsx index 3b90993c051d..faeb76a86d30 100644 --- a/libs/application/ui-fields/src/lib/DateFormField/DateFormField.tsx +++ b/libs/application/ui-fields/src/lib/DateFormField/DateFormField.tsx @@ -41,8 +41,6 @@ export const DateFormField: FC> = ({ excludeDates, minDate, maxDate, - minYear, - maxYear, onChange, readOnly, } = field @@ -141,8 +139,6 @@ export const DateFormField: FC> = ({ excludeDates={finalExcludeDates} minDate={finalMinDate} maxDate={finalMaxDate} - minYear={minYear} - maxYear={maxYear} backgroundColor={backgroundColor} readOnly={readOnly} label={formatTextWithLocale(title, application, lang, formatMessage)} diff --git a/libs/application/ui-fields/src/lib/FindVehicleFormField/FindVehicleFormField.tsx b/libs/application/ui-fields/src/lib/FindVehicleFormField/FindVehicleFormField.tsx index ca57d4b6e732..5b8d1a2cd4e5 100644 --- a/libs/application/ui-fields/src/lib/FindVehicleFormField/FindVehicleFormField.tsx +++ b/libs/application/ui-fields/src/lib/FindVehicleFormField/FindVehicleFormField.tsx @@ -250,9 +250,6 @@ export const FindVehicleFormField: FC> = ({ setValue('vehicleMileage.requireMileage', vehicleDetails?.requireMileage) setValue('vehicleMileage.mileageReading', vehicleDetails?.mileageReading) - setValue('plateSize.frontPlateSize', []) - setValue('plateSize.rearPlateSize', []) - if (permno) setValue('vehicleInfo.plate', permno) if (permno) setValue('vehicleInfo.type', vehicleDetails.make) diff --git a/libs/application/ui-fields/src/lib/TableRepeaterFormField/TableRepeaterFormField.tsx b/libs/application/ui-fields/src/lib/TableRepeaterFormField/TableRepeaterFormField.tsx index ec1e39bf08f0..5d3b8f3f6bc0 100644 --- a/libs/application/ui-fields/src/lib/TableRepeaterFormField/TableRepeaterFormField.tsx +++ b/libs/application/ui-fields/src/lib/TableRepeaterFormField/TableRepeaterFormField.tsx @@ -22,11 +22,7 @@ import { useLocale } from '@island.is/localization' import { FieldDescription } from '@island.is/shared/form-fields' import { FC, useState } from 'react' import { useFieldArray, useFormContext, useWatch } from 'react-hook-form' -import { - buildDefaultTableHeader, - buildDefaultTableRows, - handleCustomMappedValues, -} from './utils' +import { handleCustomMappedValues } from './utils' import { Item } from './TableRepeaterItem' import { Locale } from '@island.is/shared/types' @@ -51,7 +47,6 @@ export const TableRepeaterFormField: FC = ({ title, titleVariant = 'h4', addItemButtonText = coreMessages.buttonAdd, - cancelButtonText = coreMessages.buttonCancel, saveItemButtonText = coreMessages.reviewButtonSubmit, removeButtonTooltipText = coreMessages.deleteFieldText, editButtonTooltipText = coreMessages.editFieldText, @@ -76,8 +71,8 @@ export const TableRepeaterFormField: FC = ({ const activeField = activeIndex >= 0 ? fields[activeIndex] : null const savedFields = fields.filter((_, index) => index !== activeIndex) const tableItems = items.filter((x) => x.displayInTable !== false) - const tableHeader = table?.header ?? buildDefaultTableHeader(tableItems) - const tableRows = table?.rows ?? buildDefaultTableRows(tableItems) + const tableHeader = table?.header ?? tableItems.map((item) => item.label) + const tableRows = table?.rows ?? tableItems.map((item) => item.id) const staticData = getStaticTableData?.(application) const canAddItem = maxRows ? savedFields.length < maxRows : true @@ -94,11 +89,6 @@ export const TableRepeaterFormField: FC = ({ } } - const handleCancelItem = (index: number) => { - setActiveIndex(-1) - remove(index) - } - const handleNewItem = () => { append({}) setActiveIndex(fields.length) @@ -249,24 +239,14 @@ export const TableRepeaterFormField: FC = ({ /> ))} - - - - - - - + + ) : ( diff --git a/libs/application/ui-fields/src/lib/TableRepeaterFormField/utils.ts b/libs/application/ui-fields/src/lib/TableRepeaterFormField/utils.ts index f1b0c79ee077..fd1962a41f14 100644 --- a/libs/application/ui-fields/src/lib/TableRepeaterFormField/utils.ts +++ b/libs/application/ui-fields/src/lib/TableRepeaterFormField/utils.ts @@ -1,4 +1,3 @@ -import { coreMessages } from '@island.is/application/core' import { TableRepeaterItem } from '@island.is/application/types' type Item = { @@ -32,7 +31,7 @@ const handleNationalIdWithNameItem = ( // with a nested object inside it. This function will extract the nested // object and merge it with the rest of the values. const newValues = values.map((value) => { - if (!!value[item.id] && typeof value[item.id] === 'object') { + if (typeof value[item.id] === 'object' && value[item.id] !== null) { const { [item.id]: nestedObject, ...rest } = value return { ...nestedObject, ...rest } } @@ -41,25 +40,3 @@ const handleNationalIdWithNameItem = ( return newValues } - -export const buildDefaultTableHeader = (items: Array) => - items - .map((item) => - // nationalIdWithName is a special case where the value is an object of name and nationalId - item.component === 'nationalIdWithName' - ? [coreMessages.name, coreMessages.nationalId] - : item.label, - ) - .flat(2) - -export const buildDefaultTableRows = ( - items: Array, -) => - items - .map((item) => - // nationalIdWithName is a special case where the value is an object of name and nationalId - item.component === 'nationalIdWithName' - ? ['name', 'nationalId'] - : item.id, - ) - .flat(2) diff --git a/libs/application/ui-fields/src/lib/VehicleRadioFormField/VehicleDetails.ts b/libs/application/ui-fields/src/lib/VehicleRadioFormField/VehicleDetails.ts deleted file mode 100644 index b6d04706b4cd..000000000000 --- a/libs/application/ui-fields/src/lib/VehicleRadioFormField/VehicleDetails.ts +++ /dev/null @@ -1,22 +0,0 @@ -interface ValidationErrorMessage { - errorNo?: string | null - defaultMessage?: string | null -} - -export interface VehicleDetails { - permno?: string - make?: string - color?: string - role?: string - requireMileage?: boolean | null - mileageReading?: string | null - isDebtLess?: boolean | null - validationErrorMessages?: ValidationErrorMessage[] -} - -export interface PlateOwnership { - regno: string - startDate: string - endDate: string - validationErrorMessages?: ValidationErrorMessage[] -} diff --git a/libs/application/ui-fields/src/lib/VehicleRadioFormField/VehicleRadioFormField.tsx b/libs/application/ui-fields/src/lib/VehicleRadioFormField/VehicleRadioFormField.tsx deleted file mode 100644 index 69a2cb6fc86d..000000000000 --- a/libs/application/ui-fields/src/lib/VehicleRadioFormField/VehicleRadioFormField.tsx +++ /dev/null @@ -1,284 +0,0 @@ -import { formatText, getValueViaPath } from '@island.is/application/core' -import { FieldBaseProps, VehicleRadioField } from '@island.is/application/types' -import { - AlertMessage, - Box, - Bullet, - BulletList, - Text, - InputError, - Tag, -} from '@island.is/island-ui/core' -import { useLocale } from '@island.is/localization' -import { FC, useState } from 'react' -import { useFormContext } from 'react-hook-form' -import { RadioController } from '@island.is/shared/form-fields' -import { PlateOwnership, VehicleDetails } from './VehicleDetails' -import { MessageDescriptor } from 'react-intl' - -interface Option { - value: string - label: React.ReactNode - disabled?: boolean -} - -interface Props extends FieldBaseProps { - field: VehicleRadioField -} - -export const VehicleRadioFormField: FC> = ({ - application, - field, - errors, -}) => { - const { formatMessage, formatDateFns } = useLocale() - const { setValue } = useFormContext() - - let answersSelectedValueKey: string | undefined - let radioControllerId = field.id - if (field.itemType === 'VEHICLE') { - answersSelectedValueKey = `${field.id}.plate` - radioControllerId = `${field.id}.vehicle` - } else if (field.itemType === 'PLATE') { - answersSelectedValueKey = `${field.id}.regno` - radioControllerId = `${field.id}.value` - } - - const [selectedValue, setSelectedValue] = useState( - answersSelectedValueKey && - getValueViaPath(application.answers, answersSelectedValueKey, ''), - ) - - const onRadioControllerSelect = (s: string) => { - if (field.itemType === 'VEHICLE') { - const currentVehicleList = field.itemList as VehicleDetails[] - const currentVehicle = currentVehicleList?.[parseInt(s, 10)] - const permno = currentVehicle?.permno || '' - - setSelectedValue(permno) - - setValue(`${field.id}.plate`, permno) - setValue(`${field.id}.type`, currentVehicle?.make) - setValue(`${field.id}.color`, currentVehicle?.color || undefined) - - setValue('vehicleMileage.requireMileage', currentVehicle?.requireMileage) - setValue('vehicleMileage.mileageReading', currentVehicle?.mileageReading) - - setValue('plateSize.frontPlateSize', []) - setValue('plateSize.rearPlateSize', []) - - if (permno) setValue('vehicleInfo.plate', permno) - if (permno) setValue('vehicleInfo.type', currentVehicle?.make) - } else if (field.itemType === 'PLATE') { - const currentPlateList = field.itemList as PlateOwnership[] - const currentPlate = currentPlateList?.[parseInt(s, 10)] - const regno = currentPlate?.regno - - setSelectedValue(regno) - - setValue(`${field.id}.regno`, regno) - } - } - - const vehicleOptions = (vehicles: VehicleDetails[]) => { - const options: Option[] = [] - - for (const [index, vehicle] of vehicles.entries()) { - const hasError = !!vehicle.validationErrorMessages?.length - const hasDebtError = field.shouldValidateDebtStatus && !vehicle.isDebtLess - const disabled = hasError || hasDebtError - - options.push({ - value: `${index}`, - label: ( - - - - {vehicle.make} - - - {vehicle.color} - {vehicle.permno} - - - {disabled && ( - - - - {field.shouldValidateDebtStatus && - !vehicle.isDebtLess && ( - - {field.debtStatusErrorMessage && - formatText( - field.debtStatusErrorMessage, - application, - formatMessage, - )} - - )} - {!!vehicle.validationErrorMessages?.length && - vehicle.validationErrorMessages?.map((error) => { - const message = - field.validationErrorMessages && - formatMessage( - getValueViaPath( - field.validationErrorMessages, - error.errorNo || '', - ) || '', - ) - const defaultMessage = error.defaultMessage - const fallbackMessage = - (field.validationErrorFallbackMessage && - formatText( - field.validationErrorFallbackMessage, - application, - formatMessage, - )) + - ' - ' + - error.errorNo - - return ( - - {message || defaultMessage || fallbackMessage} - - ) - })} - - - } - /> - - )} - - ), - disabled: disabled, - }) - } - return options - } - - const plateOptions = (plates: PlateOwnership[]) => { - const options: Option[] = [] - - for (const [index, plate] of plates.entries()) { - const hasError = !!plate.validationErrorMessages?.length - const canRenew = - !field.shouldValidateRenewal || field.validateRenewal?.(plate) - const disabled = hasError || !canRenew - - options.push({ - value: `${index}`, - label: ( - - - - - {plate.regno} - - - - {field.renewalExpiresAtTag && - formatMessage(field.renewalExpiresAtTag, { - date: formatDateFns(new Date(plate.endDate), 'do MMM yyyy'), - })} - - - {hasError && ( - - - - {plate.validationErrorMessages?.map((error) => { - const message = - field.validationErrorMessages && - formatMessage( - getValueViaPath( - field.validationErrorMessages, - error.errorNo || '', - ) || '', - ) - - const defaultMessage = error.defaultMessage - - const fallbackMessage = - (field.validationErrorFallbackMessage && - formatText( - field.validationErrorFallbackMessage, - application, - formatMessage, - )) + - ' - ' + - error.errorNo - - return ( - - {message || defaultMessage || fallbackMessage} - - ) - })} - - - } - /> - - )} - - ), - disabled: disabled, - }) - } - return options - } - - let options: Option[] = [] - if (field.itemType === 'VEHICLE') { - options = vehicleOptions(field.itemList as VehicleDetails[]) - } else if (field.itemType === 'PLATE') { - options = plateOptions(field.itemList as PlateOwnership[]) - } - - return ( -
- - - {!selectedValue?.length && !!errors?.[field.id] && ( - - )} -
- ) -} diff --git a/libs/application/ui-fields/src/lib/index.ts b/libs/application/ui-fields/src/lib/index.ts index c3e3f9724309..fa43ff03e370 100644 --- a/libs/application/ui-fields/src/lib/index.ts +++ b/libs/application/ui-fields/src/lib/index.ts @@ -25,7 +25,6 @@ export { HiddenInputFormField } from './HiddenInputFormField/HiddenInputFormFiel export { ActionCardListFormField } from './ActionCardListFormField/ActionCardListFormField' export { TableRepeaterFormField } from './TableRepeaterFormField/TableRepeaterFormField' 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' diff --git a/libs/clients/judicial-system-sp/src/clientConfig.json b/libs/clients/judicial-system-sp/src/clientConfig.json index 2e4d54e84c54..48c81122e582 100644 --- a/libs/clients/judicial-system-sp/src/clientConfig.json +++ b/libs/clients/judicial-system-sp/src/clientConfig.json @@ -450,8 +450,6 @@ "type": "object", "properties": { "title": { "type": "string" }, - "subpoenaInfoText": { "type": "string" }, - "subpoenaNotificationDeadline": { "type": "string" }, "subtitle": { "type": "string" }, "groups": { "type": "array", @@ -470,8 +468,6 @@ }, "required": [ "title", - "subpoenaInfoText", - "subpoenaNotificationDeadline", "subtitle", "groups", "alerts", diff --git a/libs/clients/transport-authority/vehicle-operators/src/lib/vehicleOperatorsClient.module.ts b/libs/clients/transport-authority/vehicle-operators/src/lib/vehicleOperatorsClient.module.ts index e382021fb5d5..aaf3401c166d 100644 --- a/libs/clients/transport-authority/vehicle-operators/src/lib/vehicleOperatorsClient.module.ts +++ b/libs/clients/transport-authority/vehicle-operators/src/lib/vehicleOperatorsClient.module.ts @@ -1,10 +1,8 @@ import { Module } from '@nestjs/common' import { VehicleOperatorsClient } from './vehicleOperatorsClient.service' import { exportedApis } from './apiConfiguration' -import { VehiclesMileageClientModule } from '@island.is/clients/vehicles-mileage' @Module({ - imports: [VehiclesMileageClientModule], providers: [...exportedApis, VehicleOperatorsClient], exports: [VehicleOperatorsClient], }) diff --git a/libs/clients/transport-authority/vehicle-operators/src/lib/vehicleOperatorsClient.service.ts b/libs/clients/transport-authority/vehicle-operators/src/lib/vehicleOperatorsClient.service.ts index 4d2b7d23558d..cb5989c587a5 100644 --- a/libs/clients/transport-authority/vehicle-operators/src/lib/vehicleOperatorsClient.service.ts +++ b/libs/clients/transport-authority/vehicle-operators/src/lib/vehicleOperatorsClient.service.ts @@ -1,32 +1,20 @@ import { Auth, AuthMiddleware, User } from '@island.is/auth-nest-tools' import { Injectable } from '@nestjs/common' +import { ReturnTypeMessage } from '../../gen/fetch' import { OperatorApi } from '../../gen/fetch/apis' import { Operator, OperatorChangeValidation, } from './vehicleOperatorsClient.types' -import { - ErrorMessage, - getCleanErrorMessagesFromTryCatch, -} from '@island.is/clients/transport-authority/vehicle-owner-change' -import { MileageReadingApi } from '@island.is/clients/vehicles-mileage' -import { logger } from '@island.is/logging' @Injectable() export class VehicleOperatorsClient { - constructor( - private readonly operatorsApi: OperatorApi, - private readonly mileageReadingApi: MileageReadingApi, - ) {} + constructor(private readonly operatorsApi: OperatorApi) {} private operatorsApiWithAuth(auth: Auth) { return this.operatorsApi.withMiddleware(new AuthMiddleware(auth)) } - private mileageReadingApiWithAuth(auth: Auth) { - return this.mileageReadingApi.withMiddleware(new AuthMiddleware(auth)) - } - public async getOperators(auth: User, permno: string): Promise { const result = await this.operatorsApiWithAuth(auth).permnoGet({ apiVersion: '3.0', @@ -48,36 +36,16 @@ export class VehicleOperatorsClient { auth: User, permno: string, ): Promise { - // Get current mileage reading - let currentMileage = 0 - try { - const mileageReadings = await this.mileageReadingApiWithAuth( - auth, - ).getMileageReading({ permno }) - currentMileage = mileageReadings?.[0]?.mileage || 0 - } catch (e) { - logger.error(e) - return { - hasError: true, - errorMessages: [{ defaultMessage: e.message }], - } - } - - return await this.validateAllForOperatorChange( - auth, - permno, - null, - currentMileage + 1, - ) + return await this.validateAllForOperatorChange(auth, permno, null) } public async validateAllForOperatorChange( auth: User, permno: string, operators: Operator[] | null, - mileage: number | null, + mileage?: number | null, ): Promise { - let errorMessages: ErrorMessage[] | undefined + let errorList: ReturnTypeMessage[] | undefined // In case we dont have the operators selected yet, // then we will send in the owner as operator @@ -104,14 +72,35 @@ export class VehicleOperatorsClient { }, }) } catch (e) { - // Note: We had to wrap in try-catch to get the error messages, because if this action results in error, - // we get 4xx error (instead of 200 with error messages) with the error messages in the body - errorMessages = getCleanErrorMessagesFromTryCatch(e) + // Note: We need to wrap in try-catch to get the error messages, because if this action results in error, + // we get 4xx error (instead of 200 with error messages) with the errorList in this field + // ("body.Errors" for input validation, and "body" for data validation (in database)), + // that is of the same class as 200 result schema + if (e?.body?.Errors && Array.isArray(e.body.Errors)) { + errorList = e.body.Errors as ReturnTypeMessage[] + } else if (e?.body && Array.isArray(e.body)) { + errorList = e.body as ReturnTypeMessage[] + } else { + throw e + } } + const warnSeverityError = 'E' + const warnSeverityLock = 'L' + errorList = errorList?.filter( + (x) => + x.errorMess && + (x.warnSever === warnSeverityError || x.warnSever === warnSeverityLock), + ) + return { - hasError: !!errorMessages?.length, - errorMessages: errorMessages, + hasError: !!errorList?.length, + errorMessages: errorList?.map((item) => { + return { + errorNo: (item.warnSever || '_') + item.warningSerialNumber, + defaultMessage: item.errorMess, + } + }), } } @@ -123,48 +112,33 @@ export class VehicleOperatorsClient { permno: string, operators: Operator[], mileage?: number | null, - ): Promise { - let errorMessages: ErrorMessage[] | undefined - + ): Promise { if (operators.length === 0) { - try { - await this.operatorsApiWithAuth(auth).closeWithoutcontractPost({ - apiVersion: '3.0', - apiVersion2: '3.0', - postCloseOperatorsWithoutContractModel: { - permno: permno, - endDate: new Date(), - reportingPersonIdNumber: auth.nationalId, - }, - }) - } catch (e) { - errorMessages = getCleanErrorMessagesFromTryCatch(e) - } + await this.operatorsApiWithAuth(auth).closeWithoutcontractPost({ + apiVersion: '3.0', + apiVersion2: '3.0', + postCloseOperatorsWithoutContractModel: { + permno: permno, + endDate: new Date(), + reportingPersonIdNumber: auth.nationalId, + }, + }) } else { - try { - await this.operatorsApiWithAuth(auth).withoutcontractPost({ - apiVersion: '3.0', - apiVersion2: '3.0', - postOperatorsWithoutContractModel: { - permno: permno, - startDate: new Date(), - reportingPersonIdNumber: auth.nationalId, - onlyRunFlexibleWarning: false, - mileage: mileage, - operators: operators.map((operator) => ({ - personIdNumber: operator.ssn || '', - mainOperator: operator.isMainOperator ? 1 : 0, - })), - }, - }) - } catch (e) { - errorMessages = getCleanErrorMessagesFromTryCatch(e) - } - } - - return { - hasError: !!errorMessages?.length, - errorMessages: errorMessages, + await this.operatorsApiWithAuth(auth).withoutcontractPost({ + apiVersion: '3.0', + apiVersion2: '3.0', + postOperatorsWithoutContractModel: { + permno: permno, + startDate: new Date(), + reportingPersonIdNumber: auth.nationalId, + onlyRunFlexibleWarning: false, + mileage: mileage, + operators: operators.map((operator) => ({ + personIdNumber: operator.ssn || '', + mainOperator: operator.isMainOperator ? 1 : 0, + })), + }, + }) } } } diff --git a/libs/clients/transport-authority/vehicle-owner-change/src/index.ts b/libs/clients/transport-authority/vehicle-owner-change/src/index.ts index 2e9322e2605b..7cea2073a52a 100644 --- a/libs/clients/transport-authority/vehicle-owner-change/src/index.ts +++ b/libs/clients/transport-authority/vehicle-owner-change/src/index.ts @@ -3,8 +3,3 @@ export * from './lib/vehicleOwnerChangeClient.module' export * from './lib/vehicleOwnerChangeClient.types' export { VehicleOwnerChangeClientConfig } from './lib/vehicleOwnerChangeClient.config' - -export { - ErrorMessage, - getCleanErrorMessagesFromTryCatch, -} from './lib/vehicleOwnerChangeClient.utils' diff --git a/libs/clients/transport-authority/vehicle-owner-change/src/lib/vehicleOwnerChangeClient.module.ts b/libs/clients/transport-authority/vehicle-owner-change/src/lib/vehicleOwnerChangeClient.module.ts index 0b7b6d2ed27a..bbf856b6ebb3 100644 --- a/libs/clients/transport-authority/vehicle-owner-change/src/lib/vehicleOwnerChangeClient.module.ts +++ b/libs/clients/transport-authority/vehicle-owner-change/src/lib/vehicleOwnerChangeClient.module.ts @@ -1,10 +1,8 @@ import { Module } from '@nestjs/common' import { VehicleOwnerChangeClient } from './vehicleOwnerChangeClient.service' import { exportedApis } from './apiConfiguration' -import { VehiclesMileageClientModule } from '@island.is/clients/vehicles-mileage' @Module({ - imports: [VehiclesMileageClientModule], providers: [...exportedApis, VehicleOwnerChangeClient], exports: [VehicleOwnerChangeClient], }) diff --git a/libs/clients/transport-authority/vehicle-owner-change/src/lib/vehicleOwnerChangeClient.service.ts b/libs/clients/transport-authority/vehicle-owner-change/src/lib/vehicleOwnerChangeClient.service.ts index 24a062649dcc..b8c27504d343 100644 --- a/libs/clients/transport-authority/vehicle-owner-change/src/lib/vehicleOwnerChangeClient.service.ts +++ b/libs/clients/transport-authority/vehicle-owner-change/src/lib/vehicleOwnerChangeClient.service.ts @@ -1,58 +1,29 @@ import { Auth, AuthMiddleware, User } from '@island.is/auth-nest-tools' import { Injectable } from '@nestjs/common' +import { ReturnTypeMessage } from '../../gen/fetch' import { OwnerChangeApi } from '../../gen/fetch/apis' import { NewestOwnerChange, OwnerChange, OwnerChangeValidation, } from './vehicleOwnerChangeClient.types' -import { - ErrorMessage, - getCleanErrorMessagesFromTryCatch, - getDateAtTimestamp, -} from './vehicleOwnerChangeClient.utils' -import { MileageReadingApi } from '@island.is/clients/vehicles-mileage' -import { logger } from '@island.is/logging' +import { getDateAtTimestamp } from './vehicleOwnerChangeClient.utils' @Injectable() export class VehicleOwnerChangeClient { - constructor( - private readonly ownerchangeApi: OwnerChangeApi, - private readonly mileageReadingApi: MileageReadingApi, - ) {} + constructor(private readonly ownerchangeApi: OwnerChangeApi) {} private ownerchangeApiWithAuth(auth: Auth) { return this.ownerchangeApi.withMiddleware(new AuthMiddleware(auth)) } - private mileageReadingApiWithAuth(auth: Auth) { - return this.mileageReadingApi.withMiddleware(new AuthMiddleware(auth)) - } - public async validateVehicleForOwnerChange( auth: User, permno: string, ): Promise { // Note: since the vehiclecheck endpoint is funky, we will instead just use the personcheck endpoint // and send in dummy data where needed - const todayStr = new Date().toISOString() - - // Get current mileage reading - let currentMileage = 0 - try { - const mileageReadings = await this.mileageReadingApiWithAuth( - auth, - ).getMileageReading({ permno }) - currentMileage = mileageReadings?.[0]?.mileage || 0 - } catch (e) { - logger.error(e) - return { - hasError: true, - errorMessages: [{ defaultMessage: e.message }], - } - } - return await this.validateAllForOwnerChange(auth, { permno: permno, seller: { @@ -69,7 +40,6 @@ export class VehicleOwnerChangeClient { insuranceCompanyCode: null, operators: null, coOwners: null, - mileage: currentMileage + 1, }) } @@ -79,7 +49,7 @@ export class VehicleOwnerChangeClient { ): Promise { const useGroup = '000' - let errorMessages: ErrorMessage[] | undefined + let errorList: ReturnTypeMessage[] | undefined try { // Note: If insurance company has not been supplied (we have not required the user to fill in at this point), @@ -97,7 +67,7 @@ export class VehicleOwnerChangeClient { ownerChange.dateOfPurchaseTimestamp, ) - // Note: we have manually changed this endpoint to void (in clientConfig), since the messages we want only + // Note: we have manually changed this endpoint to void, since the messages we want only // come with error code 400. If this function returns an array of ReturnTypeMessage, then // we will get an error with code 204, since the openapi generator tries to convert empty result // into an array of ReturnTypeMessage @@ -122,14 +92,44 @@ export class VehicleOwnerChangeClient { }, }) } catch (e) { - // Note: We had to wrap in try-catch to get the error messages, because if this action results in error, - // we get 4xx error (instead of 200 with error messages) with the error messages in the body - errorMessages = getCleanErrorMessagesFromTryCatch(e) + // Note: We need to wrap in try-catch to get the error messages, because if this action results in error, + // we get 4xx error (instead of 200 with error messages) with the errorList in this field + // ("body.Errors" for input validation, and "body" for data validation (in database)), + // that is of the same class as 200 result schema + if (e?.body?.Errors && Array.isArray(e.body.Errors)) { + errorList = e.body.Errors as ReturnTypeMessage[] + } else if (e?.body && Array.isArray(e.body)) { + errorList = e.body as ReturnTypeMessage[] + } else { + throw e + } } + const warnSeverityError = 'E' + const warnSeverityLock = 'L' + errorList = errorList?.filter( + (x) => + x.errorMess && + (x.warnSever === warnSeverityError || x.warnSever === warnSeverityLock), + ) + return { - hasError: !!errorMessages?.length, - errorMessages: errorMessages, + hasError: !!errorList?.length, + errorMessages: errorList?.map((item) => { + let errorNo = item.warningSerialNumber?.toString() + + // Note: For vehicle locks, we need to do some special parsing since + // the error number (warningSerialNumber) is always -1 for locks, + // but the number is included in the errorMess field (value before the first space) + if (item.warnSever === warnSeverityLock) { + errorNo = item.errorMess?.split(' ')[0] + } + + return { + errorNo: (item.warnSever || '_') + errorNo, + defaultMessage: item.errorMess, + } + }), } } @@ -160,7 +160,7 @@ export class VehicleOwnerChangeClient { public async saveOwnerChange( auth: User, ownerChange: OwnerChange, - ): Promise { + ): Promise { const useGroup = '000' // Note: API throws error if timestamp is 00:00:00, so we will use @@ -170,42 +170,31 @@ export class VehicleOwnerChangeClient { ownerChange.dateOfPurchaseTimestamp, ) - let errorMessages: ErrorMessage[] | undefined - - try { - await this.ownerchangeApiWithAuth(auth).rootPost({ - apiVersion: '2.0', - apiVersion2: '2.0', - postOwnerChange: { - permno: ownerChange.permno, - sellerPersonIdNumber: ownerChange.seller.ssn, - sellerEmail: ownerChange.seller.email, - buyerPersonIdNumber: ownerChange.buyer.ssn, - buyerEmail: ownerChange.buyer.email, - dateOfPurchase: purchaseDate, - saleAmount: ownerChange.saleAmount, - insuranceCompanyCode: ownerChange.insuranceCompanyCode || '', - useGroup: useGroup, - operatorEmail: ownerChange.operators?.find((x) => x.isMainOperator) - ?.email, - operators: ownerChange.operators?.map((operator) => ({ - personIdNumber: operator.ssn, - mainOperator: operator.isMainOperator ? 1 : 0, - })), - coOwners: ownerChange.coOwners?.map((coOwner) => ({ - personIdNumber: coOwner.ssn, - })), - reportingPersonIdNumber: auth.nationalId, - mileage: ownerChange.mileage, - }, - }) - } catch (e) { - errorMessages = getCleanErrorMessagesFromTryCatch(e) - } - - return { - hasError: !!errorMessages?.length, - errorMessages: errorMessages, - } + await this.ownerchangeApiWithAuth(auth).rootPost({ + apiVersion: '2.0', + apiVersion2: '2.0', + postOwnerChange: { + permno: ownerChange.permno, + sellerPersonIdNumber: ownerChange.seller.ssn, + sellerEmail: ownerChange.seller.email, + buyerPersonIdNumber: ownerChange.buyer.ssn, + buyerEmail: ownerChange.buyer.email, + dateOfPurchase: purchaseDate, + saleAmount: ownerChange.saleAmount, + insuranceCompanyCode: ownerChange.insuranceCompanyCode || '', + useGroup: useGroup, + operatorEmail: ownerChange.operators?.find((x) => x.isMainOperator) + ?.email, + operators: ownerChange.operators?.map((operator) => ({ + personIdNumber: operator.ssn, + mainOperator: operator.isMainOperator ? 1 : 0, + })), + coOwners: ownerChange.coOwners?.map((coOwner) => ({ + personIdNumber: coOwner.ssn, + })), + reportingPersonIdNumber: auth.nationalId, + mileage: ownerChange.mileage, + }, + }) } } diff --git a/libs/clients/transport-authority/vehicle-owner-change/src/lib/vehicleOwnerChangeClient.utils.ts b/libs/clients/transport-authority/vehicle-owner-change/src/lib/vehicleOwnerChangeClient.utils.ts index 78b75aed8566..530e731c2a64 100644 --- a/libs/clients/transport-authority/vehicle-owner-change/src/lib/vehicleOwnerChangeClient.utils.ts +++ b/libs/clients/transport-authority/vehicle-owner-change/src/lib/vehicleOwnerChangeClient.utils.ts @@ -1,69 +1,6 @@ -import { ReturnTypeMessage } from '../../gen/fetch' - // Returns date object with at the selected timestamp export const getDateAtTimestamp = (oldDate: Date, timestamp: string): Date => { const newDate = oldDate instanceof Date && !isNaN(oldDate.getDate()) ? oldDate : new Date() return new Date(newDate.toISOString().substring(0, 10) + 'T' + timestamp) } - -export interface ErrorMessage { - errorNo: string | undefined - defaultMessage: string | null | undefined -} - -export const getCleanErrorMessagesFromTryCatch = (e: any): ErrorMessage[] => { - let errorList: ReturnTypeMessage[] | undefined - let filteredErrorList: ReturnTypeMessage[] | undefined - - // Note: Error message will be in the field "body.Errors" for input validation, - // and "body" for data validation (in database) - // Note: The schema for the errors is the same as the schema for the 200 response - // result schema (ReturnTypeMessage[]) - if (e?.body?.Errors && Array.isArray(e.body.Errors)) { - errorList = e.body.Errors as ReturnTypeMessage[] - } else if (e?.body && Array.isArray(e.body)) { - errorList = e.body as ReturnTypeMessage[] - } else { - throw e - } - - const warnSeverityError = 'E' - const warnSeverityLock = 'L' - const warnSeverityWarning = 'W' - - // Note: All three types of warnSever (E, L, W) will cause the api to throw error - // (and not continue execute on SGS side). But some messages are duplicated as both - // E and W. To prevent us from displaying the duplicated W error, we will first - // check for E and L, if none, then display W errors - - // First check if there are any E or L errors - filteredErrorList = errorList?.filter( - (x) => - x.errorMess && - (x.warnSever === warnSeverityError || x.warnSever === warnSeverityLock), - ) - - // If not, then check for W error - if (!filteredErrorList?.length) { - filteredErrorList = errorList?.filter( - (x) => x.errorMess && x.warnSever === warnSeverityWarning, - ) - } - - return filteredErrorList?.map((item) => { - let errorNo = item.warningSerialNumber?.toString() - - // Note: For vehicle locks, we need to do some special parsing since - // the error number (warningSerialNumber) is always -1 for locks, - // but the number is included in the errorMess field (value before the first space) - if (item.warnSever === warnSeverityLock) { - errorNo = item.errorMess?.split(' ')[0] - } - - return { - errorNo: errorNo ? (item.warnSever || '_') + errorNo : undefined, - defaultMessage: item.errorMess, - } - }) -} diff --git a/libs/clients/transport-authority/vehicle-plate-ordering/src/lib/vehiclePlateOrderingClient.service.ts b/libs/clients/transport-authority/vehicle-plate-ordering/src/lib/vehiclePlateOrderingClient.service.ts index 069bb05e5bf9..6120232c061b 100644 --- a/libs/clients/transport-authority/vehicle-plate-ordering/src/lib/vehiclePlateOrderingClient.service.ts +++ b/libs/clients/transport-authority/vehicle-plate-ordering/src/lib/vehiclePlateOrderingClient.service.ts @@ -1,5 +1,6 @@ import { Auth, AuthMiddleware, User } from '@island.is/auth-nest-tools' import { Injectable } from '@nestjs/common' +import { ReturnTypeMessage } from '../../gen/fetch' import { PlateOrderingApi } from '../../gen/fetch/apis' import { DeliveryStation, @@ -8,10 +9,6 @@ import { PlateOrder, PlateOrderValidation, } from './vehiclePlateOrderingClient.types' -import { - ErrorMessage, - getCleanErrorMessagesFromTryCatch, -} from '@island.is/clients/transport-authority/vehicle-owner-change' @Injectable() export class VehiclePlateOrderingClient { @@ -38,93 +35,84 @@ export class VehiclePlateOrderingClient { })) } - public async validateVehicleForPlateOrder( - auth: User, - permno: string, - frontType: string, - rearType: string, - ): Promise { - // Dummy values - // Note: option "Pick up at Samgöngustofa" which is always valid - const deliveryStationType = SGS_DELIVERY_STATION_TYPE - const deliveryStationCode = SGS_DELIVERY_STATION_CODE - const expressOrder = false - - return await this.validateAllForPlateOrder( - auth, - permno, - frontType, - rearType, - deliveryStationType, - deliveryStationCode, - expressOrder, - ) - } - - public async validateAllForPlateOrder( + public async validatePlateOrder( auth: User, permno: string, frontType: string, rearType: string, - deliveryStationType: string, - deliveryStationCode: string, - expressOrder: boolean, ): Promise { - let errorMessages: ErrorMessage[] | undefined + let errorList: ReturnTypeMessage[] | undefined try { + // Dummy values + // Note: option "Pick up at Samgöngustofa" which is always valid + const deliveryStationType = SGS_DELIVERY_STATION_TYPE + const deliveryStationCode = SGS_DELIVERY_STATION_CODE + const expressOrder = false + await this.plateOrderingApiWithAuth(auth).orderplatesPost({ apiVersion: '1.0', apiVersion2: '1.0', postOrderPlatesModel: { permno: permno, - frontType: frontType || null, - rearType: rearType || null, - stationToDeliverTo: deliveryStationCode || '', - stationType: deliveryStationType || '', + frontType: frontType, + rearType: rearType, + stationToDeliverTo: deliveryStationCode, + stationType: deliveryStationType, expressOrder: expressOrder, checkOnly: true, // to make sure we are only validating }, }) } catch (e) { - // Note: We had to wrap in try-catch to get the error messages, because if this action results in error, - // we get 4xx error (instead of 200 with error messages) with the error messages in the body - errorMessages = getCleanErrorMessagesFromTryCatch(e) + // Note: We need to wrap in try-catch to get the error messages, because if this action results in error, + // we get 4xx error (instead of 200 with error messages) with the errorList in this field + // ("body.Errors" for input validation, and "body" for data validation (in database)), + // that is of the same class as 200 result schema + if (e?.body?.Errors && Array.isArray(e.body.Errors)) { + errorList = e.body.Errors as ReturnTypeMessage[] + } else if (e?.body && Array.isArray(e.body)) { + errorList = e.body as ReturnTypeMessage[] + } else { + throw e + } } + const warnSeverityError = 'E' + const warnSeverityWarning = 'W' + errorList = errorList?.filter( + (x) => + x.errorMess && + (x.warnSever === warnSeverityError || + x.warnSever === warnSeverityWarning), + ) + return { - hasError: !!errorMessages?.length, - errorMessages: errorMessages, + hasError: !!errorList?.length, + errorMessages: errorList?.map((item) => { + return { + errorNo: (item.warnSever || '_') + item.warningSerialNumber, + defaultMessage: item.errorMess, + } + }), } } public async savePlateOrders( auth: User, plateOrder: PlateOrder, - ): Promise { - let errorMessages: ErrorMessage[] | undefined - - try { - await this.plateOrderingApiWithAuth(auth).orderplatesPost({ - apiVersion: '1.0', - apiVersion2: '1.0', - postOrderPlatesModel: { - permno: plateOrder.permno, - frontType: plateOrder.frontType || null, - rearType: plateOrder.rearType || null, - stationToDeliverTo: plateOrder.deliveryStationCode || '', - stationType: plateOrder.deliveryStationType || '', - expressOrder: plateOrder.expressOrder, - checkOnly: false, - }, - }) - } catch (e) { - errorMessages = getCleanErrorMessagesFromTryCatch(e) - } - - return { - hasError: !!errorMessages?.length, - errorMessages: errorMessages, - } + ): Promise { + await this.plateOrderingApiWithAuth(auth).orderplatesPost({ + apiVersion: '1.0', + apiVersion2: '1.0', + postOrderPlatesModel: { + permno: plateOrder.permno, + frontType: plateOrder.frontType, + rearType: plateOrder.rearType || null, + stationToDeliverTo: plateOrder.deliveryStationCode || '', + stationType: plateOrder.deliveryStationType || '', + expressOrder: plateOrder.expressOrder, + checkOnly: false, + }, + }) } } diff --git a/libs/clients/transport-authority/vehicle-plate-renewal/src/lib/vehiclePlateRenewalClient.service.ts b/libs/clients/transport-authority/vehicle-plate-renewal/src/lib/vehiclePlateRenewalClient.service.ts index d96b4fc29371..09502fddece5 100644 --- a/libs/clients/transport-authority/vehicle-plate-renewal/src/lib/vehiclePlateRenewalClient.service.ts +++ b/libs/clients/transport-authority/vehicle-plate-renewal/src/lib/vehiclePlateRenewalClient.service.ts @@ -6,10 +6,12 @@ import { PlateOwnershipValidation, } from './vehiclePlateRenewalClient.types' import { PlateOwnershipApiWithoutIdsAuth } from './apiConfiguration' -import { - ErrorMessage, - getCleanErrorMessagesFromTryCatch, -} from '@island.is/clients/transport-authority/vehicle-owner-change' + +interface ReturnTypeMessage { + warnSever?: string | null + errorMess?: string | null + warningSerialNumber?: number | null +} @Injectable() export class VehiclePlateRenewalClient { @@ -47,7 +49,7 @@ export class VehiclePlateRenewalClient { auth: User, regno: string, ): Promise { - let errorMessages: ErrorMessage[] | undefined + let errorList: ReturnTypeMessage[] | undefined try { await this.plateOwnershipApiWithAuth(auth).renewplateownershipPost({ @@ -60,41 +62,45 @@ export class VehiclePlateRenewalClient { }, }) } catch (e) { - // Note: We had to wrap in try-catch to get the error messages, because if this action results in error, - // we get 4xx error (instead of 200 with error messages) with the error messages in the body - errorMessages = getCleanErrorMessagesFromTryCatch(e) + // Note: We need to wrap in try-catch to get the error messages, because if this action results in error, + // we get 4xx error (instead of 200 with error messages) with the errorList in this field + // ("body.Errors" for input validation, and "body" for data validation (in database)), + // that is of the same class as 200 result schema + if (e?.body?.Errors) { + errorList = e.body.Errors as ReturnTypeMessage[] + } else if (e?.body) { + errorList = e.body as ReturnTypeMessage[] + } else { + throw e + } } + const warnSeverityError = 'E' + errorList = errorList?.filter( + (x) => x.errorMess && x.warnSever === warnSeverityError, + ) + return { - hasError: !!errorMessages?.length, - errorMessages: errorMessages, + hasError: !!errorList?.length, + errorMessages: errorList?.map((item) => { + return { + errorNo: (item.warnSever || '_') + item.warningSerialNumber, + defaultMessage: item.errorMess, + } + }), } } - public async renewPlateOwnership( - auth: User, - regno: string, - ): Promise { - let errorMessages: ErrorMessage[] | undefined - - try { - await this.plateOwnershipApiWithAuth(auth).renewplateownershipPost({ - apiVersion: '1.0', - apiVersion2: '1.0', - postRenewPlateOwnershipModel: { - regno: regno, - persidno: auth.nationalId, - check: false, - }, - }) - } catch (e) { - errorMessages = getCleanErrorMessagesFromTryCatch(e) - } - - return { - hasError: !!errorMessages?.length, - errorMessages: errorMessages, - } + public async renewPlateOwnership(auth: User, regno: string): Promise { + await this.plateOwnershipApiWithAuth(auth).renewplateownershipPost({ + apiVersion: '1.0', + apiVersion2: '1.0', + postRenewPlateOwnershipModel: { + regno: regno, + persidno: auth.nationalId, + check: false, + }, + }) } public async getPlateAvailability(regno: string) { diff --git a/libs/cms/src/lib/cms.contentful.service.ts b/libs/cms/src/lib/cms.contentful.service.ts index f54e8a4794c9..71bcfabb44cc 100644 --- a/libs/cms/src/lib/cms.contentful.service.ts +++ b/libs/cms/src/lib/cms.contentful.service.ts @@ -77,13 +77,10 @@ import { mapImage } from './models/image.model' import { EmailSignup, mapEmailSignup } from './models/emailSignup.model' import { GetTabSectionInput } from './dto/getTabSection.input' import { mapTabSection, TabSection } from './models/tabSection.model' +import { GetGenericTagBySlugInput } from './dto/getGenericTagBySlug.input' import { GenericTag, mapGenericTag } from './models/genericTag.model' import { GetEmailSignupInput } from './dto/getEmailSignup.input' import { LifeEventPage, mapLifeEventPage } from './models/lifeEventPage.model' -import { GetGenericTagBySlugInput } from './dto/getGenericTagBySlug.input' -import { GetGenericTagsInTagGroupsInput } from './dto/getGenericTagsInTagGroups.input' -import { Grant, mapGrant } from './models/grant.model' -import { GrantList } from './models/grantList.model' import { mapManual } from './models/manual.model' import { mapServiceWebPage } from './models/serviceWebPage.model' import { mapEvent } from './models/event.model' @@ -602,19 +599,6 @@ export class CmsContentfulService { return (result.items as types.INews[]).map(mapNews)[0] ?? null } - async getGrant(lang: string, id: string): Promise { - const params = { - ['content_type']: 'grant', - 'fields.grantApplicationId': id, - } - - const result = await this.contentfulRepository - .getLocalizedEntries(lang, params) - .catch(errorHandler('getGrant')) - - return (result.items as types.IGrant[]).map(mapGrant)[0] ?? null - } - async getSingleEvent(lang: string, slug: string) { const params = { ['content_type']: 'event', @@ -1126,29 +1110,4 @@ export class CmsContentfulService { return (result.items as types.IGenericTag[]).map(mapGenericTag)[0] ?? null } - - async getGenericTagsInTagGroups({ - lang = 'is', - tagGroupSlugs, - }: GetGenericTagsInTagGroupsInput): Promise | null> { - let params - if (tagGroupSlugs) { - params = { - ['content_type']: 'genericTag', - 'fields.genericTagGroup.fields.slug[in]': tagGroupSlugs.join(','), - 'fields.genericTagGroup.sys.contentType.sys.id': 'genericTagGroup', - } - } else { - params = { - ['content_type']: 'genericTag', - 'fields.genericTagGroup.sys.contentType.sys.id': 'genericTagGroup', - } - } - - const result = await this.contentfulRepository - .getLocalizedEntries(lang, params) - .catch(errorHandler('getGenericTag')) - - return (result.items as types.IGenericTag[]).map(mapGenericTag) - } } diff --git a/libs/cms/src/lib/cms.elasticsearch.service.ts b/libs/cms/src/lib/cms.elasticsearch.service.ts index 6f83b2649b4e..1a248a16e320 100644 --- a/libs/cms/src/lib/cms.elasticsearch.service.ts +++ b/libs/cms/src/lib/cms.elasticsearch.service.ts @@ -46,10 +46,6 @@ import { GetGenericListItemBySlugInput } from './dto/getGenericListItemBySlug.in import { GenericListItem } from './models/genericListItem.model' import { GetTeamMembersInput } from './dto/getTeamMembers.input' import { TeamMemberResponse } from './models/teamMemberResponse.model' -import { GetGrantsInput } from './dto/getGrants.input' -import { Grant } from './models/grant.model' -import { GrantList } from './models/grantList.model' -import { logger } from '@island.is/logging' @Injectable() export class CmsElasticsearchService { @@ -609,120 +605,6 @@ export class CmsElasticsearchService { .filter(Boolean) } - async getGrants( - index: string, - { - lang, - search, - page = 1, - size = 8, - statuses, - categories, - types, - organizations, - }: GetGrantsInput, - ): Promise { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const must: Record[] = [ - { - term: { - type: { - value: 'webGrant', - }, - }, - }, - ] - - let queryString = search ? search.toLowerCase() : '' - - if (lang === 'is') { - queryString = queryString.replace('`', '') - } - - const sort: ('_score' | sortRule)[] = [ - { - [SortField.RELEASE_DATE]: { - 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 (queryString) { - must.push({ - simple_query_string: { - query: queryString + '*', - fields: ['title^100', 'content'], - analyze_wildcard: true, - }, - }) - } - - const tagFilters: Array> = [] - - if (categories) { - tagFilters.push(categories) - } - if (types) { - tagFilters.push(types) - } - if (statuses) { - tagFilters.push(statuses) - } - if (organizations) { - tagFilters.push(organizations) - } - - tagFilters.forEach((filter) => { - must.push({ - nested: { - path: 'tags', - query: { - bool: { - must: [ - { - terms: { - 'tags.key': filter, - }, - }, - { - term: { - 'tags.type': 'genericTag', - }, - }, - ], - }, - }, - }, - }) - }) - - const grantListResponse: ApiResponse> = - await this.elasticService.findByQuery(index, { - query: { - bool: { - must, - }, - }, - sort, - size, - from: (page - 1) * size, - }) - - return { - total: grantListResponse.body.hits.total.value, - items: grantListResponse.body.hits.hits.map((response) => - JSON.parse(response._source.response ?? '[]'), - ), - } - } - async getPublishedMaterial( index: string, { diff --git a/libs/cms/src/lib/cms.resolver.ts b/libs/cms/src/lib/cms.resolver.ts index 462b5e4531ec..63ad79020440 100644 --- a/libs/cms/src/lib/cms.resolver.ts +++ b/libs/cms/src/lib/cms.resolver.ts @@ -121,11 +121,6 @@ import { TeamMemberResponse } from './models/teamMemberResponse.model' import { TeamList } from './models/teamList.model' import { TeamMember } from './models/teamMember.model' import { LatestGenericListItems } from './models/latestGenericListItems.model' -import { GetGenericTagsInTagGroupsInput } from './dto/getGenericTagsInTagGroups.input' -import { Grant } from './models/grant.model' -import { GetGrantsInput } from './dto/getGrants.input' -import { GetSingleGrantInput } from './dto/getSingleGrant.input' -import { GrantList } from './models/grantList.model' const defaultCache: CacheControlOptions = { maxAge: CACHE_CONTROL_MAX_AGE } @@ -449,23 +444,6 @@ export class CmsResolver { ) } - @CacheControl(defaultCache) - @Query(() => GrantList) - async getGrants(@Args('input') input: GetGrantsInput): Promise { - return this.cmsElasticsearchService.getGrants( - getElasticsearchIndex(input.lang), - input, - ) - } - - @CacheControl(defaultCache) - @Query(() => Grant, { nullable: true }) - async getSingleGrant( - @Args('input') { lang, id }: GetSingleGrantInput, - ): Promise { - return this.cmsContentfulService.getGrant(lang, id) - } - @CacheControl(defaultCache) @Query(() => News, { nullable: true }) getSingleNews( @@ -628,14 +606,6 @@ export class CmsResolver { return this.cmsContentfulService.getGenericTagBySlug(input) } - @CacheControl(defaultCache) - @Query(() => [GenericTag], { nullable: true }) - getGenericTagsInTagGroups( - @Args('input') input: GetGenericTagsInTagGroupsInput, - ): Promise | null> { - return this.cmsContentfulService.getGenericTagsInTagGroups(input) - } - @CacheControl(defaultCache) @Query(() => Manual, { nullable: true }) getSingleManual( diff --git a/libs/cms/src/lib/dto/getGenericTagsInTagGroups.input.ts b/libs/cms/src/lib/dto/getGenericTagsInTagGroups.input.ts deleted file mode 100644 index b91d2891d97d..000000000000 --- a/libs/cms/src/lib/dto/getGenericTagsInTagGroups.input.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Field, InputType } from '@nestjs/graphql' -import { IsArray, IsOptional, IsString } from 'class-validator' - -@InputType() -export class GetGenericTagsInTagGroupsInput { - @Field(() => String) - @IsString() - lang = 'is-IS' - - @Field(() => [String], { nullable: true }) - @IsArray() - @IsOptional() - tagGroupSlugs?: Array -} diff --git a/libs/cms/src/lib/dto/getGrants.input.ts b/libs/cms/src/lib/dto/getGrants.input.ts deleted file mode 100644 index bfe512bad196..000000000000 --- a/libs/cms/src/lib/dto/getGrants.input.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { IsArray, IsInt, IsOptional, IsString } from 'class-validator' -import { Field, InputType, Int } from '@nestjs/graphql' -import { ElasticsearchIndexLocale } from '@island.is/content-search-index-manager' - -@InputType() -export class GetGrantsInput { - @Field({ nullable: true }) - @IsString() - @IsOptional() - search?: string - - @Field(() => String) - @IsString() - lang: ElasticsearchIndexLocale = 'is' - - @Field(() => Int, { nullable: true }) - @IsInt() - @IsOptional() - page?: number = 1 - - @Field(() => Int, { nullable: true }) - @IsInt() - @IsOptional() - size?: number = 8 - - @Field(() => [String], { nullable: true }) - @IsArray() - @IsOptional() - statuses?: string[] - - @Field(() => [String], { nullable: true }) - @IsArray() - @IsOptional() - categories?: string[] - - @Field(() => [String], { nullable: true }) - @IsArray() - @IsOptional() - types?: string[] - - @Field(() => [String], { nullable: true }) - @IsArray() - @IsOptional() - organizations?: string[] -} diff --git a/libs/cms/src/lib/dto/getSingleGrant.input.ts b/libs/cms/src/lib/dto/getSingleGrant.input.ts deleted file mode 100644 index 95e668413368..000000000000 --- a/libs/cms/src/lib/dto/getSingleGrant.input.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Field, InputType } from '@nestjs/graphql' -import { IsString } from 'class-validator' -import { ElasticsearchIndexLocale } from '@island.is/content-search-index-manager' - -@InputType() -export class GetSingleGrantInput { - @Field() - @IsString() - id!: string - - @Field(() => String) - @IsString() - lang: ElasticsearchIndexLocale = 'is' -} diff --git a/libs/cms/src/lib/environments/environment.ts b/libs/cms/src/lib/environments/environment.ts index 6b4bb5935d35..0e973855b528 100644 --- a/libs/cms/src/lib/environments/environment.ts +++ b/libs/cms/src/lib/environments/environment.ts @@ -8,7 +8,6 @@ export default { 'articleCategory', 'news', 'page', - 'grant', 'vidspyrnaPage', 'menu', 'groupedMenu', diff --git a/libs/cms/src/lib/generated/contentfulTypes.d.ts b/libs/cms/src/lib/generated/contentfulTypes.d.ts index 5107c1cfa6ef..83dd0e9ffc84 100644 --- a/libs/cms/src/lib/generated/contentfulTypes.d.ts +++ b/libs/cms/src/lib/generated/contentfulTypes.d.ts @@ -8,7 +8,7 @@ export interface IAccordionSliceFields { title?: string | undefined /** Type */ - type: 'accordion' | 'accordion_minimal' | 'category_card' + type: 'accordion' | 'accordion_minimal' | 'CTA' | 'category_card' /** Accordion Items */ accordionItems?: IOneColumnText[] | undefined @@ -770,7 +770,6 @@ export interface ICustomPageFields { | 'PensionCalculator' | 'OfficialJournalOfIceland' | 'Vacancies' - | 'Grants' | undefined /** Alert Banner */ @@ -1550,39 +1549,6 @@ export interface IFrontpageSlider extends Entry { } } -export interface IFundFields { - /** Title */ - fundTitle: string - - /** Link */ - fundLink?: ILinkUrl | undefined - - /** Featured Image */ - fundFeaturedImage: Asset - - /** Parent Organization */ - fundParentOrganization: IOrganization -} - -/** Fund is a part of "Styrkjatorg". */ - -export interface IFund extends Entry { - sys: { - id: string - type: string - createdAt: string - updatedAt: string - locale: string - contentType: { - sys: { - id: 'fund' - linkType: 'ContentType' - type: 'Link' - } - } - } -} - export interface IGenericListFields { /** Internal Title */ internalTitle: string @@ -1630,7 +1596,7 @@ export interface IGenericListItemFields { title: string /** Date */ - date?: string | undefined + date: string /** Card Intro */ cardIntro?: Document | undefined @@ -1783,9 +1749,6 @@ export interface IGenericTagGroupFields { /** Slug */ slug: string - - /** Items */ - items?: Record | undefined } /** A way to group together generic tags */ @@ -1807,85 +1770,6 @@ export interface IGenericTagGroup extends Entry { } } -export interface IGrantFields { - /** Title */ - grantName: string - - /** Description */ - grantDescription?: string | undefined - - /** Grant Application ID */ - grantApplicationId?: string | undefined - - /** Application deadline text */ - grantApplicationDeadlineText: string[] - - /** Application Url */ - granApplicationUrl?: ILinkUrl | undefined - - /** Special emphasis */ - grantSpecialEmphasis?: Document | undefined - - /** Who can apply? */ - grantWhoCanApply?: Document | undefined - - /** How to apply? */ - grantHowToApply?: Document | undefined - - /** Application deadline */ - grantApplicationDeadline?: Document | undefined - - /** Application hints */ - grantApplicationHints?: Document | undefined - - /** Date from */ - grantDateFrom?: string | undefined - - /** Date to */ - grantDateTo?: string | undefined - - /** Is open? */ - grantIsOpen?: boolean | undefined - - /** Status */ - grantStatus?: - | 'Opið fyrir umsóknir' - | 'Lokað fyrir umsóknir' - | 'Opnar fljótlega' - | undefined - - /** Files */ - grantFiles?: Asset[] | undefined - - /** Category tags */ - grantCategoryTags?: IGenericTag[] | undefined - - /** Type tag */ - grantTypeTag?: IGenericTag | undefined - - /** Fund */ - grantFund: IFund -} - -/** Grant is a part of "Styrkjatorg". */ - -export interface IGrant extends Entry { - sys: { - id: string - type: string - createdAt: string - updatedAt: string - locale: string - contentType: { - sys: { - id: 'grant' - linkType: 'ContentType' - type: 'Link' - } - } - } -} - export interface IGraphCardFields { /** Graph Title */ graphTitle: string @@ -2051,7 +1935,7 @@ export interface IIntroLinkImageFields { /** Image */ image: Asset - /** Image Position */ + /** Left Image */ leftImage?: boolean | undefined /** Link Title */ @@ -2127,7 +2011,7 @@ export interface ILatestGenericListItemsFields { genericList?: IGenericList | undefined /** See more page */ - seeMorePage: IOrganizationSubpage + seeMorePage?: IOrganizationSubpage | undefined /** See more link text */ seeMoreLinkText?: string | undefined @@ -2251,34 +2135,6 @@ export interface ILifeEventPage extends Entry { } } -export interface ILifeEventPageListSliceFields { - /** Title */ - title?: string | undefined - - /** List */ - lifeEventPageList?: (ILifeEventPage | IAnchorPage)[] | undefined -} - -/** !!DO NOT USE!! - This content type has been deprecated. Use Anchor Page List */ - -export interface ILifeEventPageListSlice - extends Entry { - sys: { - id: string - type: string - createdAt: string - updatedAt: string - locale: string - contentType: { - sys: { - id: 'lifeEventPageListSlice' - linkType: 'ContentType' - type: 'Link' - } - } - } -} - export interface ILinkFields { /** Text */ text: string @@ -2881,9 +2737,6 @@ export interface IOneColumnTextFields { /** Show Title */ showTitle?: boolean | undefined - - /** Filter tags */ - filterTags?: IGenericTag[] | undefined } export interface IOneColumnText extends Entry { @@ -3071,9 +2924,6 @@ export interface IOrganizationFields { /** Kennitala */ kennitala?: string | undefined - - /** Alert Banner */ - alertBanner?: IAlertBanner | undefined } export interface IOrganization extends Entry { @@ -3132,7 +2982,6 @@ export interface IOrganizationPageFields { | IAnchorPageList | ISectionWithImage | IChartNumberBox - | ILatestGenericListItems )[] | undefined @@ -3327,7 +3176,6 @@ export interface IOrganizationSubpageFields { | ISectionWithVideo | ISectionHeading | ILatestEventsSlice - | IGenericList )[] | undefined @@ -4030,10 +3878,6 @@ export interface ISliceConnectedComponentFields { | 'SpecificHousingBenefitSupportCalculator' | 'GrindavikResidentialPropertyPurchaseCalculator' | 'Ums/CostOfLivingCalculator' - | 'Sveinslisti/JourneymanList' - | 'Starfsrettindi/ProfessionRights' - | 'VMST/ParentalLeaveCalculator' - | 'DigitalIceland/BenefitsOfDigitalProcesses' | undefined /** Localized JSON */ @@ -5289,14 +5133,12 @@ export type CONTENT_TYPE = | 'formField' | 'frontpage' | 'frontpageSlider' - | 'fund' | 'genericList' | 'genericListItem' | 'genericOverviewPage' | 'genericPage' | 'genericTag' | 'genericTagGroup' - | 'grant' | 'graphCard' | 'groupedMenu' | 'hnippTemplate' @@ -5306,7 +5148,6 @@ export type CONTENT_TYPE = | 'latestGenericListItems' | 'latestNewsSlice' | 'lifeEventPage' - | 'lifeEventPageListSlice' | 'link' | 'linkedPage' | 'linkGroup' diff --git a/libs/cms/src/lib/models/fund.model.ts b/libs/cms/src/lib/models/fund.model.ts deleted file mode 100644 index 447e103695d7..000000000000 --- a/libs/cms/src/lib/models/fund.model.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Field, ObjectType, ID } from '@nestjs/graphql' - -import { IFund } from '../generated/contentfulTypes' -import { CacheField } from '@island.is/nest/graphql' -import { ReferenceLink, mapReferenceLink } from './referenceLink.model' -import { Image, mapImage } from './image.model' -import { Organization, mapOrganization } from './organization.model' - -@ObjectType('OrganizationFund') -export class Fund { - @Field(() => ID) - id!: string - - @Field() - title!: string - - @CacheField(() => ReferenceLink, { nullable: true }) - link?: ReferenceLink - - @CacheField(() => Image, { nullable: true }) - featuredImage?: Image | null - - @CacheField(() => Organization) - parentOrganization!: Organization -} - -export const mapFund = ({ fields, sys }: IFund): Fund => ({ - id: sys.id, - title: fields.fundTitle, - link: fields.fundLink ? mapReferenceLink(fields.fundLink) : undefined, - featuredImage: fields.fundFeaturedImage - ? mapImage(fields.fundFeaturedImage) - : undefined, - parentOrganization: mapOrganization(fields.fundParentOrganization), -}) diff --git a/libs/cms/src/lib/models/grant.model.ts b/libs/cms/src/lib/models/grant.model.ts deleted file mode 100644 index 9a1d0e182a27..000000000000 --- a/libs/cms/src/lib/models/grant.model.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { Field, ObjectType, ID, registerEnumType } from '@nestjs/graphql' - -import { IGrant } from '../generated/contentfulTypes' -import { GenericTag, mapGenericTag } from './genericTag.model' -import { CacheField } from '@island.is/nest/graphql' -import { mapDocument, SliceUnion } from '../unions/slice.union' -import { Asset, mapAsset } from './asset.model' -import { ReferenceLink, mapReferenceLink } from './referenceLink.model' -import { Fund, mapFund } from './fund.model' - -enum GrantStatus { - CLOSED, - OPEN, - OPENS_SOON, - INACTIVE, -} - -registerEnumType(GrantStatus, { name: 'GrantStatus' }) - -@ObjectType() -export class Grant { - @Field(() => ID) - id!: string - - @Field() - name!: string - - @Field({ nullable: true }) - description?: string - - @Field({ nullable: true }) - applicationId?: string - - @Field(() => [String], { nullable: true }) - applicationDeadlineText?: Array - - @CacheField(() => ReferenceLink, { nullable: true }) - applicationUrl?: ReferenceLink - - @CacheField(() => [SliceUnion]) - specialEmphasis?: Array - - @CacheField(() => [SliceUnion]) - whoCanApply?: Array - - @CacheField(() => [SliceUnion]) - howToApply?: Array - - @CacheField(() => [SliceUnion]) - applicationDeadline?: Array - - @CacheField(() => [SliceUnion]) - applicationHints?: Array - - @Field({ nullable: true }) - dateFrom?: string - - @Field({ nullable: true }) - dateTo?: string - - @Field({ nullable: true }) - isOpen?: boolean - - @Field({ nullable: true }) - statusText?: string - - @CacheField(() => GrantStatus, { nullable: true }) - status?: GrantStatus - - @CacheField(() => [Asset], { nullable: true }) - files?: Array - - @CacheField(() => [GenericTag], { nullable: true }) - categoryTags?: Array - - @CacheField(() => GenericTag, { nullable: true }) - typeTag?: GenericTag - - @CacheField(() => Fund, { nullable: true }) - fund?: Fund -} - -export const mapGrant = ({ fields, sys }: IGrant): Grant => ({ - id: sys.id, - name: fields.grantName, - description: fields.grantDescription, - applicationId: fields.grantApplicationId, - applicationDeadlineText: fields.grantApplicationDeadlineText, - applicationUrl: fields.granApplicationUrl?.fields - ? mapReferenceLink(fields.granApplicationUrl) - : undefined, - - specialEmphasis: fields.grantSpecialEmphasis - ? mapDocument(fields.grantSpecialEmphasis, sys.id + ':special-emphasis') - : [], - whoCanApply: fields.grantWhoCanApply - ? mapDocument(fields.grantWhoCanApply, sys.id + ':who-can-apply') - : [], - howToApply: fields.grantHowToApply - ? mapDocument(fields.grantHowToApply, sys.id + ':how-to-apply') - : [], - applicationDeadline: fields.grantApplicationDeadline - ? mapDocument( - fields.grantApplicationDeadline, - sys.id + ':application-deadline', - ) - : [], - applicationHints: fields.grantApplicationHints - ? mapDocument(fields.grantApplicationHints, sys.id + ':application-hints') - : [], - dateFrom: fields.grantDateFrom, - dateTo: fields.grantDateTo, - isOpen: fields.grantIsOpen ?? undefined, - statusText: fields.grantStatus ?? 'Óvirkur sjóður', - status: - fields.grantStatus === 'Opið fyrir umsóknir' - ? GrantStatus.OPEN - : fields.grantStatus === 'Lokað fyrir umsóknir' - ? GrantStatus.CLOSED - : fields.grantStatus === 'Opnar fljótlega' - ? GrantStatus.OPENS_SOON - : undefined, - fund: fields.grantFund ? mapFund(fields.grantFund) : undefined, - files: (fields.grantFiles ?? []).map((file) => mapAsset(file)) ?? [], - categoryTags: fields.grantCategoryTags - ? fields.grantCategoryTags.map((tag) => mapGenericTag(tag)) - : undefined, - typeTag: fields.grantTypeTag ? mapGenericTag(fields.grantTypeTag) : undefined, -}) diff --git a/libs/cms/src/lib/models/grantList.model.ts b/libs/cms/src/lib/models/grantList.model.ts deleted file mode 100644 index 43263d4ae912..000000000000 --- a/libs/cms/src/lib/models/grantList.model.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Field, Int, ObjectType } from '@nestjs/graphql' -import { CacheField } from '@island.is/nest/graphql' -import { Grant } from './grant.model' - -@ObjectType() -export class GrantList { - @Field(() => Int) - total!: number - - @CacheField(() => [Grant]) - items!: Grant[] -} diff --git a/libs/cms/src/lib/search/cmsSync.module.ts b/libs/cms/src/lib/search/cmsSync.module.ts index 671094405728..d6edead06251 100644 --- a/libs/cms/src/lib/search/cmsSync.module.ts +++ b/libs/cms/src/lib/search/cmsSync.module.ts @@ -33,7 +33,6 @@ import { CustomPageSyncService } from './importers/customPage.service' import { GenericListItemSyncService } from './importers/genericListItem.service' import { TeamListSyncService } from './importers/teamList.service' import { MappingService } from './mapping.service' -import { GrantsSyncService } from './importers/grants.service' @Module({ imports: [ @@ -55,7 +54,6 @@ import { GrantsSyncService } from './importers/grants.service' NewsSyncService, AdgerdirPageSyncService, MenuSyncService, - GrantsSyncService, GroupedMenuSyncService, OrganizationPageSyncService, OrganizationSubpageSyncService, diff --git a/libs/cms/src/lib/search/importers/grants.service.ts b/libs/cms/src/lib/search/importers/grants.service.ts deleted file mode 100644 index a1c35e33bb20..000000000000 --- a/libs/cms/src/lib/search/importers/grants.service.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { MappedData } from '@island.is/content-search-indexer/types' -import { logger } from '@island.is/logging' -import { Injectable } from '@nestjs/common' -import { Entry } from 'contentful' -import isCircular from 'is-circular' -import { IGrant } from '../../generated/contentfulTypes' -import { CmsSyncProvider, processSyncDataInput } from '../cmsSync.service' -import { - createTerms, - extractChildEntryIds, - extractStringsFromObject, - pruneNonSearchableSliceUnionFields, -} from './utils' -import { mapGrant } from '../../models/grant.model' -import { isDefined } from '@island.is/shared/utils' - -@Injectable() -export class GrantsSyncService implements CmsSyncProvider { - processSyncData(entries: processSyncDataInput) { - // only process grants that we consider not to be empty and dont have circular structures - return entries.filter( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (entry: Entry): entry is IGrant => - entry.sys.contentType.sys.id === 'grant' && - entry.fields.grantName && - !isCircular(entry), - ) - } - - doMapping(entries: IGrant[]) { - return entries - .map((entry) => { - try { - const mapped = mapGrant(entry) - if (isCircular(mapped)) { - logger.warn('Circular reference found in grants', { - id: entry?.sys?.id, - }) - return false - } - - const content = [ - mapped.specialEmphasis - ? extractStringsFromObject( - mapped?.specialEmphasis?.map( - pruneNonSearchableSliceUnionFields, - ), - ) - : undefined, - mapped.whoCanApply - ? extractStringsFromObject( - mapped?.whoCanApply?.map(pruneNonSearchableSliceUnionFields), - ) - : undefined, - mapped.howToApply - ? extractStringsFromObject( - mapped?.howToApply?.map(pruneNonSearchableSliceUnionFields), - ) - : undefined, - mapped.applicationDeadline - ? extractStringsFromObject( - mapped?.applicationDeadline?.map( - pruneNonSearchableSliceUnionFields, - ), - ) - : undefined, - mapped?.applicationHints - ? extractStringsFromObject( - mapped?.applicationHints?.map( - pruneNonSearchableSliceUnionFields, - ), - ) - : undefined, - ] - .filter(isDefined) - .join() - - const tags: Array<{ - key: string - type: string - value?: string - }> = [ - mapped.typeTag - ? { - key: mapped.typeTag.slug, - type: 'genericTag', - value: mapped.typeTag.title, - } - : null, - ].filter(isDefined) - - mapped.categoryTags?.forEach((tag) => { - if (tag) { - tags.push({ - key: tag.slug, - type: 'genericTag', - value: tag.title, - }) - } - }) - - // Tag the document with the ids of its children so we can later look up what document a child belongs to - const childEntryIds = extractChildEntryIds(entry) - for (const id of childEntryIds) { - tags.push({ - key: id, - type: 'hasChildEntryWithId', - }) - } - - return { - _id: mapped.id, - title: mapped.name, - content, - contentWordCount: content.split(/\s+/).length, - type: 'webGrant', - termPool: createTerms([mapped.name]), - response: JSON.stringify({ ...mapped, typename: 'Grant' }), - tags, - dateCreated: entry.sys.createdAt, - dateUpdated: new Date().getTime().toString(), - } - } catch (error) { - logger.warn('Failed to import grants', { - error: error.message, - id: entry?.sys?.id, - }) - return false - } - }) - .filter((value): value is MappedData => Boolean(value)) - } -} diff --git a/libs/cms/src/lib/search/mapping.service.ts b/libs/cms/src/lib/search/mapping.service.ts index 51630b3c3e9b..981675af8792 100644 --- a/libs/cms/src/lib/search/mapping.service.ts +++ b/libs/cms/src/lib/search/mapping.service.ts @@ -25,7 +25,6 @@ import { CustomPageSyncService } from './importers/customPage.service' import { GenericListItemSyncService } from './importers/genericListItem.service' import { TeamListSyncService } from './importers/teamList.service' import type { CmsSyncProvider, processSyncDataInput } from './cmsSync.service' -import { GrantsSyncService } from './importers/grants.service' @Injectable() export class MappingService { @@ -54,7 +53,6 @@ export class MappingService { private readonly manualSyncService: ManualSyncService, private readonly manualChapterItemSyncService: ManualChapterItemSyncService, private readonly customPageSyncService: CustomPageSyncService, - private readonly grantSyncService: GrantsSyncService, private readonly genericListItemSyncService: GenericListItemSyncService, private readonly teamListSyncService: TeamListSyncService, ) { @@ -75,7 +73,6 @@ export class MappingService { this.supportQNASyncService, this.linkSyncService, this.enhancedAssetService, - this.grantSyncService, this.vacancyService, this.serviceWebPageSyncService, this.eventSyncService, diff --git a/libs/feature-flags/src/lib/features.ts b/libs/feature-flags/src/lib/features.ts index ae839cdd1690..71759a803ae5 100644 --- a/libs/feature-flags/src/lib/features.ts +++ b/libs/feature-flags/src/lib/features.ts @@ -45,8 +45,7 @@ export enum Features { servicePortalVehicleBulkMileagePageEnabled = 'isServicePortalVehicleBulkMileagePageEnabled', servicePortalSocialInsurancePageEnabled = 'isServicePortalSocialInsurancePageEnabled', servicePortalSocialInsuranceIncomePlanPageEnabled = 'isServicePortalSocialInsuranceIncomePlanPageEnabled', - servicePortalHealthVaccinationsPageEnabled = 'isServicePortalHealthVaccinationsPageEnabled', - servicePortalHealthOrganDonationPageEnabled = 'isServicePortalHealthOrganDonationPageEnabled', + ServicePortalNotificationsEnabled = 'isServicePortalNotificationsPageEnabled', servicePortalLawAndOrderModuleEnabled = 'isServicePortalLawAndOrderModuleEnabled', servicePortalDocumentsActionsEnabled = 'isServicePortalDocumentsActionsEnabled', diff --git a/libs/island-ui/core/src/lib/Toast/Toast.css.ts b/libs/island-ui/core/src/lib/Toast/Toast.css.ts index 1a6e0062a834..4bfc19872fe2 100644 --- a/libs/island-ui/core/src/lib/Toast/Toast.css.ts +++ b/libs/island-ui/core/src/lib/Toast/Toast.css.ts @@ -14,7 +14,7 @@ globalStyle(`${root} .Toastify__toast-container`, { transform: 'translate3d(0, 0, 9999px)', position: 'fixed', padding: '4px', - width: '432px', + width: '320px', boxSizing: 'border-box', color: theme.color.dark400, '@media': { @@ -97,19 +97,18 @@ globalStyle(`${root} .Toastify__toast`, { minHeight: '64px', boxSizing: 'border-box', marginBottom: theme.spacing[2], - padding: theme.spacing[2], + padding: theme.spacing[1], borderWidth: 1, borderStyle: 'solid', borderRadius: theme.border.radius.large, display: 'flex', + justifyContent: 'space-between', overflow: 'hidden', cursor: 'pointer', direction: 'ltr', fontWeight: theme.typography.medium, '@media': { [`(max-width: ${theme.breakpoints.sm}px)`]: { - padding: theme.spacing[1], - minHeight: '48px', marginBottom: 0, }, }, @@ -148,6 +147,7 @@ globalStyle(`${root} .Toastify__toast--error`, { }) globalStyle(`${root} .Toastify__toast-body`, { flex: '1 1 auto', + margin: 'auto 0', }) /** Close button **/ @@ -160,7 +160,7 @@ globalStyle(`${root} .Toastify__close-button`, { cursor: 'pointer', opacity: 0.7, transition: '0.3s ease', - alignSelf: 'center', + alignSelf: 'flex-start', }) globalStyle(`${root} .Toastify__close-button--default`, { color: '#000', diff --git a/libs/island-ui/core/src/lib/Toast/Toast.tsx b/libs/island-ui/core/src/lib/Toast/Toast.tsx index 9d8de91f883e..4e221e38c91b 100644 --- a/libs/island-ui/core/src/lib/Toast/Toast.tsx +++ b/libs/island-ui/core/src/lib/Toast/Toast.tsx @@ -1,4 +1,4 @@ -import React, { FC } from 'react' +import * as React from 'react' import { ToastContainer as ToastifyContainer, toast as toastify, @@ -6,11 +6,10 @@ import { ToastOptions, } from 'react-toastify' import cn from 'classnames' - import { Box } from '../Box/Box' +import { Icon } from '../Icon/Icon' import { Text } from '../Text/Text' import * as toastStyles from './Toast.css' -import { Icon } from '../IconRC/Icon' interface ToastProps { hideProgressBar?: boolean @@ -33,16 +32,16 @@ const RenderMessage = ({ info: 'blue400', } as const const icons = { - error: 'warning', - success: 'checkmarkCircle', - warning: 'warning', - info: 'informationCircle', + error: 'toasterError', + success: 'toasterSuccess', + warning: 'toasterWarning', + info: 'toasterInfo', } as const return ( - - - + + + {message} @@ -51,7 +50,7 @@ const RenderMessage = ({ ) } -export const ToastContainer: FC = ({ +export const ToastContainer: React.FC> = ({ hideProgressBar = false, timeout = 5000, closeButton = false, diff --git a/libs/judicial-system/message/src/lib/message.ts b/libs/judicial-system/message/src/lib/message.ts index c8650bc09256..4cccc3b48615 100644 --- a/libs/judicial-system/message/src/lib/message.ts +++ b/libs/judicial-system/message/src/lib/message.ts @@ -30,7 +30,6 @@ export enum MessageType { INSTITUTION_NOTIFICATION = 'INSTITUTION_NOTIFICATION', NOTIFICATION_DISPATCH = 'NOTIFICATION_DISPATCH', DEFENDANT_NOTIFICATION = 'DEFENDANT_NOTIFICATION', - CIVIL_CLAIMANT_NOTIFICATION = 'CIVIL_CLAIMANT_NOTIFICATION', } export const messageEndpoint: { [key in MessageType]: string } = { @@ -67,7 +66,6 @@ export const messageEndpoint: { [key in MessageType]: string } = { INSTITUTION_NOTIFICATION: 'institutionNotification', NOTIFICATION_DISPATCH: 'notification/dispatch', DEFENDANT_NOTIFICATION: 'defendantNotification', - CIVIL_CLAIMANT_NOTIFICATION: 'civilClaimantNotification', } export type Message = { diff --git a/libs/judicial-system/types/src/index.ts b/libs/judicial-system/types/src/index.ts index 4357ea6ad607..494e5067a5e2 100644 --- a/libs/judicial-system/types/src/index.ts +++ b/libs/judicial-system/types/src/index.ts @@ -18,7 +18,6 @@ export { InstitutionNotificationType, NotificationDispatchType, DefendantNotificationType, - CivilClaimantNotificationType, 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 6c449ffc196e..89d0dcdc5f71 100644 --- a/libs/judicial-system/types/src/lib/notification.ts +++ b/libs/judicial-system/types/src/lib/notification.ts @@ -21,17 +21,13 @@ export enum CaseNotificationType { } export enum DefendantNotificationType { - DEFENDANT_SELECTED_DEFENDER = 'DEFENDANT_SELECTED_DEFENDER', DEFENDER_ASSIGNED = 'DEFENDER_ASSIGNED', } export enum SubpoenaNotificationType { SERVICE_SUCCESSFUL = 'SERVICE_SUCCESSFUL', SERVICE_FAILED = 'SERVICE_FAILED', -} - -export enum CivilClaimantNotificationType { - SPOKESPERSON_ASSIGNED = 'SPOKESPERSON_ASSIGNED', + DEFENDANT_SELECTED_DEFENDER = 'DEFENDANT_SELECTED_DEFENDER', } export enum InstitutionNotificationType { @@ -58,11 +54,10 @@ export enum NotificationType { INDICTMENT_DENIED = CaseNotificationType.INDICTMENT_DENIED, INDICTMENT_RETURNED = CaseNotificationType.INDICTMENT_RETURNED, CASE_FILES_UPDATED = CaseNotificationType.CASE_FILES_UPDATED, - DEFENDANT_SELECTED_DEFENDER = DefendantNotificationType.DEFENDANT_SELECTED_DEFENDER, DEFENDER_ASSIGNED = DefendantNotificationType.DEFENDER_ASSIGNED, SERVICE_SUCCESSFUL = SubpoenaNotificationType.SERVICE_SUCCESSFUL, SERVICE_FAILED = SubpoenaNotificationType.SERVICE_FAILED, - SPOKESPERSON_ASSIGNED = CivilClaimantNotificationType.SPOKESPERSON_ASSIGNED, + DEFENDANT_SELECTED_DEFENDER = SubpoenaNotificationType.DEFENDANT_SELECTED_DEFENDER, INDICTMENTS_WAITING_FOR_CONFIRMATION = InstitutionNotificationType.INDICTMENTS_WAITING_FOR_CONFIRMATION, } diff --git a/libs/localization/scripts/extract.ts b/libs/localization/scripts/extract.ts index a261bef373cb..0b61d2c7da01 100644 --- a/libs/localization/scripts/extract.ts +++ b/libs/localization/scripts/extract.ts @@ -30,7 +30,7 @@ if (!CONTENTFUL_MANAGEMENT_ACCESS_TOKEN) { const client = createClient({ accessToken: CONTENTFUL_MANAGEMENT_ACCESS_TOKEN }) -const format = spawn.sync('yarn', [ +const format = spawn.sync('npx', [ 'formatjs', 'extract', '--out-file', diff --git a/libs/nest/aws/src/lib/s3.service.ts b/libs/nest/aws/src/lib/s3.service.ts index 0e534dcd33c1..caa898cd2218 100644 --- a/libs/nest/aws/src/lib/s3.service.ts +++ b/libs/nest/aws/src/lib/s3.service.ts @@ -141,12 +141,7 @@ export class S3Service { params: PresignedPostOptions, ): Promise { try { - // The S3 Aws sdk v3 returns a trailing forward slash - const post = await createPresignedPost(this.s3Client, params) - if (post.url.endsWith('/')) { - post.url = post.url.slice(0, -1) - } - return post + return await createPresignedPost(this.s3Client, params) } catch (error) { this.logger.error( `An error occurred while trying to create a presigned post for file: ${params.Key} in bucket: ${params.Bucket}`, diff --git a/libs/portals/admin/ids-admin/src/lib/messages.ts b/libs/portals/admin/ids-admin/src/lib/messages.ts index 4fa97ac192d2..37ca1fe0afaa 100644 --- a/libs/portals/admin/ids-admin/src/lib/messages.ts +++ b/libs/portals/admin/ids-admin/src/lib/messages.ts @@ -836,11 +836,11 @@ export const m = defineMessages({ }, successDeletingClient: { id: 'ap.ids-admin:success-deleting-client', - defaultMessage: 'Successfully archived application', + defaultMessage: 'Successfully deleted application', }, delete: { id: 'ap.ids-admin:delete', - defaultMessage: 'Archive', + defaultMessage: 'Delete', }, closeDeleteModal: { id: 'ap.ids-admin:close-delete-modal', @@ -852,12 +852,12 @@ export const m = defineMessages({ }, deleteClient: { id: 'ap.ids-admin:delete-client-all-env', - defaultMessage: 'Archive application', + defaultMessage: 'Delete application', }, deleteClientAlertMessage: { id: 'ap.ids-admin:delete-client-alert-message', defaultMessage: - 'The client ID will be archived from all available environments and cannot be reused. Authentications will stop working immediately for your application.', + 'The client ID will be deleted from all available environments and cannot be reused. Authentications will stop working immediately for your application.', }, partiallyCreatedClient: { id: 'ap.ids-admin:partially-created-client', diff --git a/libs/portals/admin/petition/src/components/ExportList/MyPdfDocument/index.tsx b/libs/portals/admin/petition/src/components/ExportList/MyPdfDocument/index.tsx new file mode 100644 index 000000000000..b7885ba5aa48 --- /dev/null +++ b/libs/portals/admin/petition/src/components/ExportList/MyPdfDocument/index.tsx @@ -0,0 +1,162 @@ +import { + Page, + Text, + Document, + Image, + View, + Font, + StyleSheet, +} from '@react-pdf/renderer' +import { + Endorsement, + EndorsementList, + PaginatedEndorsementResponse, +} from '@island.is/api/schema' +import { dark200 } from '@island.is/island-ui/theme' +import { formatDate } from '../../../lib/utils/utils' +import { format as formatNationalId } from 'kennitala' +import island from './island.png' +import thjodskra from './thjodskra.png' + +const MyPdfDocument = (data: { + petition?: EndorsementList + petitionSigners: PaginatedEndorsementResponse +}) => { + const { petition, petitionSigners } = data + return ( + + + {/* Header */} + + + {/* Body */} + + Upplýsingar um undirskriftalista + Heiti undirskriftalista + {petition?.title} + Um undirskriftalista + {petition?.description} + + + Opinn til: + {formatDate(petition?.closedDate)} + + + Fjöldi undirskrifta: + {petitionSigners?.totalCount} + + + + + Ábyrgðarmaður: + {petition?.ownerName} + + + Kennitala ábyrgðarmanns: + {formatNationalId(petition?.owner as string)} + + + + + + Dags. skráð + Nafn + Sveitarfélag + + + {petitionSigners?.data?.map((sign: Endorsement) => { + return ( + + + {formatDate(sign.created)} + + + {sign.meta.fullName ? sign.meta.fullName : ''} + + + {sign.meta.locality ? sign.meta.locality : ''} + + + ) + })} + + + + {/* Footer */} + + + + ) +} + +export const pdfStyles = StyleSheet.create({ + body: { + paddingTop: 40, + paddingBottom: 70, + paddingHorizontal: 60, + fontFamily: 'IBM Plex Sans', + fontSize: 10, + }, + title: { + fontSize: 24, + }, + header: { + fontSize: 14, + fontWeight: 600, + marginTop: 20, + marginBottom: 5, + }, + tableHeaderDate: { + fontWeight: 600, + marginBottom: 5, + width: '17%', + }, + tableHeader: { + fontWeight: 600, + marginBottom: 5, + width: '33%', + }, + row: { + flexDirection: 'row', + }, + widthHalf: { + width: '50%', + }, + listInfo: { + paddingBottom: 30, + }, + tableView: { + paddingTop: 25, + borderTop: `1px solid ${dark200}`, + }, + tableRow: { + flexDirection: 'row', + marginVertical: 5, + }, + image: { + width: 120, + marginBottom: 20, + marginLeft: -9, + }, + footerImage: { + position: 'absolute', + left: 60, + bottom: 40, + width: 120, + }, +}) + +Font.register({ + family: 'IBM Plex Sans', + fonts: [ + { + src: './assets/fonts/ibm-plex-sans-v7-latin-regular.ttf', + }, + { + src: './assets/fonts/ibm-plex-sans-v7-latin-600.ttf', + fontWeight: 600, + }, + ], +}) + +export default MyPdfDocument diff --git a/libs/portals/admin/petition/src/components/ExportList/MyPdfDocument/island.png b/libs/portals/admin/petition/src/components/ExportList/MyPdfDocument/island.png new file mode 100644 index 000000000000..c67725fe2eb9 Binary files /dev/null and b/libs/portals/admin/petition/src/components/ExportList/MyPdfDocument/island.png differ diff --git a/libs/portals/admin/petition/src/components/ExportList/MyPdfDocument/thjodskra.png b/libs/portals/admin/petition/src/components/ExportList/MyPdfDocument/thjodskra.png new file mode 100644 index 000000000000..96058703b270 Binary files /dev/null and b/libs/portals/admin/petition/src/components/ExportList/MyPdfDocument/thjodskra.png differ diff --git a/libs/portals/admin/petition/src/components/ExportList/downloadCSV.ts b/libs/portals/admin/petition/src/components/ExportList/downloadCSV.ts new file mode 100644 index 000000000000..13f4ee0c5a82 --- /dev/null +++ b/libs/portals/admin/petition/src/components/ExportList/downloadCSV.ts @@ -0,0 +1,37 @@ +import CSVStringify from 'csv-stringify' +import { formatDate } from '../../lib/utils/utils' +import { + Endorsement, + PaginatedEndorsementResponse, +} from '@island.is/api/schema' + +export const getCSV = async ( + signers: PaginatedEndorsementResponse, + fileName: string, +) => { + const name = `${fileName}` + const dataArray = signers.data.map((item: Endorsement) => [ + formatDate(item.created) ?? '', + item.meta.fullName ?? '', + item.meta.locality ?? '', + ]) + + await downloadCSV(name, ['Dagsetning', 'Nafn', 'Sveitarfélag'], dataArray) +} + +export const downloadCSV = async ( + name: string, + header: string[], + data: Array>, +) => { + const csvData = [header, ...data] + CSVStringify(csvData, (err, output) => { + const encodedUri = encodeURI(`data:text/csv;charset=utf-8,${output}`) + const link = document.createElement('a') + link.setAttribute('href', encodedUri) + link.setAttribute('download', name) + document.body.appendChild(link) + + link.click() + }) +} diff --git a/libs/portals/admin/petition/src/components/ExportList/exportList.graphql b/libs/portals/admin/petition/src/components/ExportList/exportList.graphql deleted file mode 100644 index 9a431559801b..000000000000 --- a/libs/portals/admin/petition/src/components/ExportList/exportList.graphql +++ /dev/null @@ -1,5 +0,0 @@ -mutation ExportList($input: ExportEndorsementListInput!) { - endorsementSystemExportList(input: $input) { - url - } -} diff --git a/libs/portals/admin/petition/src/components/ExportList/index.tsx b/libs/portals/admin/petition/src/components/ExportList/index.tsx index 738f58baf43b..0bc7c06b4b8a 100644 --- a/libs/portals/admin/petition/src/components/ExportList/index.tsx +++ b/libs/portals/admin/petition/src/components/ExportList/index.tsx @@ -1,11 +1,20 @@ import { FC, ReactElement } from 'react' -import { Box, DropdownMenu, LoadingDots } from '@island.is/island-ui/core' +import { Box, DropdownMenu } from '@island.is/island-ui/core' import { useLocale } from '@island.is/localization' import * as styles from './styles.css' import { m } from '../../lib/messages' -import { useExportListMutation } from './exportList.generated' +import { + EndorsementList, + PaginatedEndorsementResponse, +} from '@island.is/api/schema' +import MyPdfDocument from './MyPdfDocument' +import { usePDF } from '@react-pdf/renderer' + interface Props { + petition: EndorsementList + petitionSigners: PaginatedEndorsementResponse petitionId: string + onGetCSV: () => void dropdownItems?: { href?: string onClick?: () => void @@ -19,75 +28,43 @@ interface Props { } export const ExportList: FC> = ({ + petition, + petitionSigners, petitionId, + onGetCSV, dropdownItems = [], }) => { const { formatMessage } = useLocale() - const [exportListPdf, { loading: loadingPdf }] = useExportListMutation({ - variables: { - input: { - listId: petitionId, - fileType: 'pdf', - }, - }, - onCompleted: (data) => { - window.open(data.endorsementSystemExportList.url, '_blank') - }, - }) - - const [exportListCsv, { loading: loadingCsv }] = useExportListMutation({ - variables: { - input: { - listId: petitionId, - fileType: 'csv', - }, - }, - onCompleted: (data) => { - window.open(data.endorsementSystemExportList.url, '_blank') - }, + const [document] = usePDF({ + document: ( + + ), }) return ( - loadingPdf ? ( - - - - ) : ( - exportListPdf()} - > - {formatMessage(m.asPdf)} - - ), + render: () => ( +
+ {formatMessage(m.asPdf)} + + ), }, { + onClick: () => onGetCSV(), title: formatMessage(m.asCsv), - render: () => - loadingCsv ? ( - - - - ) : ( - exportListCsv()} - > - {formatMessage(m.asCsv)} - - ), }, ...dropdownItems, ]} diff --git a/libs/portals/admin/petition/src/components/ExportList/styles.css.ts b/libs/portals/admin/petition/src/components/ExportList/styles.css.ts index 9f659ad2b7dc..f0998466e1f4 100644 --- a/libs/portals/admin/petition/src/components/ExportList/styles.css.ts +++ b/libs/portals/admin/petition/src/components/ExportList/styles.css.ts @@ -7,7 +7,6 @@ export const buttonWrapper = style({ export const menuItem = style({ paddingTop: '16px', - paddingBottom: '16px', display: 'flex', justifyContent: 'center', transition: 'color .2s', @@ -19,9 +18,4 @@ export const menuItem = style({ color: theme.color.blue400, }, }, - '@media': { - [`screen and (max-width: ${theme.breakpoints.sm}px)`]: { - fontSize: 12, - }, - }, }) diff --git a/libs/portals/admin/petition/src/components/ListSignersTable/index.tsx b/libs/portals/admin/petition/src/components/ListSignersTable/index.tsx index 984b4d91ace1..7b9fce1629bc 100644 --- a/libs/portals/admin/petition/src/components/ListSignersTable/index.tsx +++ b/libs/portals/admin/petition/src/components/ListSignersTable/index.tsx @@ -15,6 +15,7 @@ import { EndorsementList, PaginatedEndorsementResponse, } from '@island.is/api/schema' +import { getCSV } from '../ExportList/downloadCSV' const ListSignersTable = (data: { listId: string @@ -43,7 +44,12 @@ const ListSignersTable = (data: { {formatMessage(m.petitionsOverview)} - + getCSV(data.petitionSigners, 'Undirskriftalisti')} + /> diff --git a/libs/portals/admin/regulations-admin/src/utils/formatAmendingRegulation.ts b/libs/portals/admin/regulations-admin/src/utils/formatAmendingRegulation.ts index d42974eb109f..8d1be305a33c 100644 --- a/libs/portals/admin/regulations-admin/src/utils/formatAmendingRegulation.ts +++ b/libs/portals/admin/regulations-admin/src/utils/formatAmendingRegulation.ts @@ -23,7 +23,7 @@ import { getDeletionOrAddition } from './getDeletionOrAddition' // ---------------------------------------------------------------------- const PREFIX = 'Reglugerð um ' const PREFIX_AMENDING = 'breytingu á reglugerð nr. ' -const PREFIX_REPEALING = 'brottfellingu reglugerðar nr. ' +const PREFIX_REPEALING = 'brottfellingu á reglugerð nr. ' const formatAffectedAndPlaceAffectedAtEnd = ( groups: { diff --git a/libs/portals/my-pages/air-discount/tsconfig.json b/libs/portals/my-pages/air-discount/tsconfig.json index 3512bf7afeea..4b421814593c 100644 --- a/libs/portals/my-pages/air-discount/tsconfig.json +++ b/libs/portals/my-pages/air-discount/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../tsconfig.base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "jsx": "react-jsx", "allowJs": true, diff --git a/libs/portals/my-pages/air-discount/tsconfig.lib.json b/libs/portals/my-pages/air-discount/tsconfig.lib.json index 9668c0996130..b1fdab0f8413 100644 --- a/libs/portals/my-pages/air-discount/tsconfig.lib.json +++ b/libs/portals/my-pages/air-discount/tsconfig.lib.json @@ -5,8 +5,8 @@ "types": ["node"] }, "files": [ - "../../../../node_modules/@nx/react/typings/cssmodule.d.ts", - "../../../../node_modules/@nx/react/typings/image.d.ts" + "../../../node_modules/@nx/react/typings/cssmodule.d.ts", + "../../../node_modules/@nx/react/typings/image.d.ts" ], "exclude": [ "**/*.spec.ts", diff --git a/libs/portals/my-pages/applications/tsconfig.json b/libs/portals/my-pages/applications/tsconfig.json index 4aee5eb425be..45d3d172fde4 100644 --- a/libs/portals/my-pages/applications/tsconfig.json +++ b/libs/portals/my-pages/applications/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../tsconfig.base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "jsx": "react-jsx", "allowJs": true diff --git a/libs/portals/my-pages/applications/tsconfig.lib.json b/libs/portals/my-pages/applications/tsconfig.lib.json index 9668c0996130..1d50996ab513 100644 --- a/libs/portals/my-pages/applications/tsconfig.lib.json +++ b/libs/portals/my-pages/applications/tsconfig.lib.json @@ -4,20 +4,16 @@ "outDir": "../../../../dist/out-tsc", "types": ["node"] }, - "files": [ - "../../../../node_modules/@nx/react/typings/cssmodule.d.ts", - "../../../../node_modules/@nx/react/typings/image.d.ts" - ], "exclude": [ "**/*.spec.ts", "**/*.test.ts", "**/*.spec.tsx", "**/*.test.tsx", - "**/*.spec.js", - "**/*.test.js", - "**/*.spec.jsx", - "**/*.test.jsx", "jest.config.ts" ], - "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] + "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"], + "files": [ + "../../../node_modules/@nx/react/typings/cssmodule.d.ts", + "../../../node_modules/@nx/react/typings/image.d.ts" + ] } diff --git a/libs/portals/my-pages/assets/src/screens/Overview/Overview.graphql b/libs/portals/my-pages/assets/src/screens/Overview/Overview.graphql index d220225f3a2e..4541f2e46dae 100644 --- a/libs/portals/my-pages/assets/src/screens/Overview/Overview.graphql +++ b/libs/portals/my-pages/assets/src/screens/Overview/Overview.graphql @@ -7,7 +7,7 @@ query GetUsersVehiclesV2($input: GetVehiclesListV2Input!) { colorName modelYear requiresMileageRegistration - canRegisterMileage + canRegisterMilage nextMainInspection } paging { diff --git a/libs/portals/my-pages/assets/tsconfig.json b/libs/portals/my-pages/assets/tsconfig.json index 4aee5eb425be..45d3d172fde4 100644 --- a/libs/portals/my-pages/assets/tsconfig.json +++ b/libs/portals/my-pages/assets/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../tsconfig.base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "jsx": "react-jsx", "allowJs": true diff --git a/libs/portals/my-pages/consent/tsconfig.json b/libs/portals/my-pages/consent/tsconfig.json index 3512bf7afeea..4b421814593c 100644 --- a/libs/portals/my-pages/consent/tsconfig.json +++ b/libs/portals/my-pages/consent/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../tsconfig.base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "jsx": "react-jsx", "allowJs": true, diff --git a/libs/portals/my-pages/consent/tsconfig.lib.json b/libs/portals/my-pages/consent/tsconfig.lib.json index 9668c0996130..b1fdab0f8413 100644 --- a/libs/portals/my-pages/consent/tsconfig.lib.json +++ b/libs/portals/my-pages/consent/tsconfig.lib.json @@ -5,8 +5,8 @@ "types": ["node"] }, "files": [ - "../../../../node_modules/@nx/react/typings/cssmodule.d.ts", - "../../../../node_modules/@nx/react/typings/image.d.ts" + "../../../node_modules/@nx/react/typings/cssmodule.d.ts", + "../../../node_modules/@nx/react/typings/image.d.ts" ], "exclude": [ "**/*.spec.ts", diff --git a/libs/portals/my-pages/constants/tsconfig.json b/libs/portals/my-pages/constants/tsconfig.json index 4aee5eb425be..45d3d172fde4 100644 --- a/libs/portals/my-pages/constants/tsconfig.json +++ b/libs/portals/my-pages/constants/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../tsconfig.base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "jsx": "react-jsx", "allowJs": true diff --git a/libs/portals/my-pages/constants/tsconfig.lib.json b/libs/portals/my-pages/constants/tsconfig.lib.json index df044b77b9a5..1d50996ab513 100644 --- a/libs/portals/my-pages/constants/tsconfig.lib.json +++ b/libs/portals/my-pages/constants/tsconfig.lib.json @@ -13,7 +13,7 @@ ], "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"], "files": [ - "../../../../node_modules/@nx/react/typings/cssmodule.d.ts", - "../../../../node_modules/@nx/react/typings/image.d.ts" + "../../../node_modules/@nx/react/typings/cssmodule.d.ts", + "../../../node_modules/@nx/react/typings/image.d.ts" ] } diff --git a/libs/portals/my-pages/core/tsconfig.json b/libs/portals/my-pages/core/tsconfig.json index 4aee5eb425be..45d3d172fde4 100644 --- a/libs/portals/my-pages/core/tsconfig.json +++ b/libs/portals/my-pages/core/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../tsconfig.base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "jsx": "react-jsx", "allowJs": true diff --git a/libs/portals/my-pages/documents/tsconfig.json b/libs/portals/my-pages/documents/tsconfig.json index 4aee5eb425be..45d3d172fde4 100644 --- a/libs/portals/my-pages/documents/tsconfig.json +++ b/libs/portals/my-pages/documents/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../tsconfig.base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "jsx": "react-jsx", "allowJs": true diff --git a/libs/portals/my-pages/documents/tsconfig.lib.json b/libs/portals/my-pages/documents/tsconfig.lib.json index 9668c0996130..1d50996ab513 100644 --- a/libs/portals/my-pages/documents/tsconfig.lib.json +++ b/libs/portals/my-pages/documents/tsconfig.lib.json @@ -4,20 +4,16 @@ "outDir": "../../../../dist/out-tsc", "types": ["node"] }, - "files": [ - "../../../../node_modules/@nx/react/typings/cssmodule.d.ts", - "../../../../node_modules/@nx/react/typings/image.d.ts" - ], "exclude": [ "**/*.spec.ts", "**/*.test.ts", "**/*.spec.tsx", "**/*.test.tsx", - "**/*.spec.js", - "**/*.test.js", - "**/*.spec.jsx", - "**/*.test.jsx", "jest.config.ts" ], - "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] + "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"], + "files": [ + "../../../node_modules/@nx/react/typings/cssmodule.d.ts", + "../../../node_modules/@nx/react/typings/image.d.ts" + ] } diff --git a/libs/portals/my-pages/education-career/tsconfig.json b/libs/portals/my-pages/education-career/tsconfig.json index 4aee5eb425be..45d3d172fde4 100644 --- a/libs/portals/my-pages/education-career/tsconfig.json +++ b/libs/portals/my-pages/education-career/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../tsconfig.base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "jsx": "react-jsx", "allowJs": true diff --git a/libs/portals/my-pages/education-career/tsconfig.lib.json b/libs/portals/my-pages/education-career/tsconfig.lib.json index d547efefcf0b..bc322cb27aa7 100644 --- a/libs/portals/my-pages/education-career/tsconfig.lib.json +++ b/libs/portals/my-pages/education-career/tsconfig.lib.json @@ -5,8 +5,8 @@ "types": ["node"] }, "files": [ - "../../../../node_modules/@nx/react/typings/cssmodule.d.ts", - "../../../../node_modules/@nx/react/typings/image.d.ts" + "../../../node_modules/@nx/react/typings/cssmodule.d.ts", + "../../../node_modules/@nx/react/typings/image.d.ts" ], "exclude": [ "**/*.spec.ts", diff --git a/libs/portals/my-pages/education-degree/tsconfig.json b/libs/portals/my-pages/education-degree/tsconfig.json index 4aee5eb425be..45d3d172fde4 100644 --- a/libs/portals/my-pages/education-degree/tsconfig.json +++ b/libs/portals/my-pages/education-degree/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../tsconfig.base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "jsx": "react-jsx", "allowJs": true diff --git a/libs/portals/my-pages/education-degree/tsconfig.lib.json b/libs/portals/my-pages/education-degree/tsconfig.lib.json index d547efefcf0b..bc322cb27aa7 100644 --- a/libs/portals/my-pages/education-degree/tsconfig.lib.json +++ b/libs/portals/my-pages/education-degree/tsconfig.lib.json @@ -5,8 +5,8 @@ "types": ["node"] }, "files": [ - "../../../../node_modules/@nx/react/typings/cssmodule.d.ts", - "../../../../node_modules/@nx/react/typings/image.d.ts" + "../../../node_modules/@nx/react/typings/cssmodule.d.ts", + "../../../node_modules/@nx/react/typings/image.d.ts" ], "exclude": [ "**/*.spec.ts", diff --git a/libs/portals/my-pages/education-license/tsconfig.json b/libs/portals/my-pages/education-license/tsconfig.json index 4aee5eb425be..45d3d172fde4 100644 --- a/libs/portals/my-pages/education-license/tsconfig.json +++ b/libs/portals/my-pages/education-license/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../tsconfig.base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "jsx": "react-jsx", "allowJs": true diff --git a/libs/portals/my-pages/education-license/tsconfig.lib.json b/libs/portals/my-pages/education-license/tsconfig.lib.json index d547efefcf0b..bc322cb27aa7 100644 --- a/libs/portals/my-pages/education-license/tsconfig.lib.json +++ b/libs/portals/my-pages/education-license/tsconfig.lib.json @@ -5,8 +5,8 @@ "types": ["node"] }, "files": [ - "../../../../node_modules/@nx/react/typings/cssmodule.d.ts", - "../../../../node_modules/@nx/react/typings/image.d.ts" + "../../../node_modules/@nx/react/typings/cssmodule.d.ts", + "../../../node_modules/@nx/react/typings/image.d.ts" ], "exclude": [ "**/*.spec.ts", diff --git a/libs/portals/my-pages/education-student-assessment/tsconfig.json b/libs/portals/my-pages/education-student-assessment/tsconfig.json index 4aee5eb425be..45d3d172fde4 100644 --- a/libs/portals/my-pages/education-student-assessment/tsconfig.json +++ b/libs/portals/my-pages/education-student-assessment/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../tsconfig.base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "jsx": "react-jsx", "allowJs": true diff --git a/libs/portals/my-pages/education-student-assessment/tsconfig.lib.json b/libs/portals/my-pages/education-student-assessment/tsconfig.lib.json index d547efefcf0b..bc322cb27aa7 100644 --- a/libs/portals/my-pages/education-student-assessment/tsconfig.lib.json +++ b/libs/portals/my-pages/education-student-assessment/tsconfig.lib.json @@ -5,8 +5,8 @@ "types": ["node"] }, "files": [ - "../../../../node_modules/@nx/react/typings/cssmodule.d.ts", - "../../../../node_modules/@nx/react/typings/image.d.ts" + "../../../node_modules/@nx/react/typings/cssmodule.d.ts", + "../../../node_modules/@nx/react/typings/image.d.ts" ], "exclude": [ "**/*.spec.ts", diff --git a/libs/portals/my-pages/education/tsconfig.json b/libs/portals/my-pages/education/tsconfig.json index 4aee5eb425be..45d3d172fde4 100644 --- a/libs/portals/my-pages/education/tsconfig.json +++ b/libs/portals/my-pages/education/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../tsconfig.base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "jsx": "react-jsx", "allowJs": true diff --git a/libs/portals/my-pages/education/tsconfig.lib.json b/libs/portals/my-pages/education/tsconfig.lib.json index 9668c0996130..1d50996ab513 100644 --- a/libs/portals/my-pages/education/tsconfig.lib.json +++ b/libs/portals/my-pages/education/tsconfig.lib.json @@ -4,20 +4,16 @@ "outDir": "../../../../dist/out-tsc", "types": ["node"] }, - "files": [ - "../../../../node_modules/@nx/react/typings/cssmodule.d.ts", - "../../../../node_modules/@nx/react/typings/image.d.ts" - ], "exclude": [ "**/*.spec.ts", "**/*.test.ts", "**/*.spec.tsx", "**/*.test.tsx", - "**/*.spec.js", - "**/*.test.js", - "**/*.spec.jsx", - "**/*.test.jsx", "jest.config.ts" ], - "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] + "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"], + "files": [ + "../../../node_modules/@nx/react/typings/cssmodule.d.ts", + "../../../node_modules/@nx/react/typings/image.d.ts" + ] } diff --git a/libs/portals/my-pages/education/tsconfig.spec.json b/libs/portals/my-pages/education/tsconfig.spec.json index 47e877796468..9c453e8cbe59 100644 --- a/libs/portals/my-pages/education/tsconfig.spec.json +++ b/libs/portals/my-pages/education/tsconfig.spec.json @@ -18,7 +18,7 @@ "jest.config.ts" ], "files": [ - "../../../../node_modules/@nx/react/typings/cssmodule.d.ts", - "../../../../node_modules/@nx/react/typings/image.d.ts" + "../../../node_modules/@nx/react/typings/cssmodule.d.ts", + "../../../node_modules/@nx/react/typings/image.d.ts" ] } diff --git a/libs/portals/my-pages/finance/tsconfig.json b/libs/portals/my-pages/finance/tsconfig.json index 4aee5eb425be..45d3d172fde4 100644 --- a/libs/portals/my-pages/finance/tsconfig.json +++ b/libs/portals/my-pages/finance/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../tsconfig.base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "jsx": "react-jsx", "allowJs": true diff --git a/libs/portals/my-pages/finance/tsconfig.lib.json b/libs/portals/my-pages/finance/tsconfig.lib.json index df044b77b9a5..1d50996ab513 100644 --- a/libs/portals/my-pages/finance/tsconfig.lib.json +++ b/libs/portals/my-pages/finance/tsconfig.lib.json @@ -13,7 +13,7 @@ ], "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"], "files": [ - "../../../../node_modules/@nx/react/typings/cssmodule.d.ts", - "../../../../node_modules/@nx/react/typings/image.d.ts" + "../../../node_modules/@nx/react/typings/cssmodule.d.ts", + "../../../node_modules/@nx/react/typings/image.d.ts" ] } diff --git a/libs/portals/my-pages/graphql/tsconfig.json b/libs/portals/my-pages/graphql/tsconfig.json index 55d3aaeea3e3..f094e2ad0a12 100644 --- a/libs/portals/my-pages/graphql/tsconfig.json +++ b/libs/portals/my-pages/graphql/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../tsconfig.base.json", + "extends": "../../../tsconfig.base.json", "include": [], "files": [], "references": [ diff --git a/libs/portals/my-pages/health/tsconfig.json b/libs/portals/my-pages/health/tsconfig.json index 4aee5eb425be..45d3d172fde4 100644 --- a/libs/portals/my-pages/health/tsconfig.json +++ b/libs/portals/my-pages/health/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../tsconfig.base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "jsx": "react-jsx", "allowJs": true diff --git a/libs/portals/my-pages/health/tsconfig.lib.json b/libs/portals/my-pages/health/tsconfig.lib.json index df044b77b9a5..1d50996ab513 100644 --- a/libs/portals/my-pages/health/tsconfig.lib.json +++ b/libs/portals/my-pages/health/tsconfig.lib.json @@ -13,7 +13,7 @@ ], "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"], "files": [ - "../../../../node_modules/@nx/react/typings/cssmodule.d.ts", - "../../../../node_modules/@nx/react/typings/image.d.ts" + "../../../node_modules/@nx/react/typings/cssmodule.d.ts", + "../../../node_modules/@nx/react/typings/image.d.ts" ] } diff --git a/libs/portals/my-pages/information/tsconfig.json b/libs/portals/my-pages/information/tsconfig.json index f621b7e9bab5..4f13c3264df0 100644 --- a/libs/portals/my-pages/information/tsconfig.json +++ b/libs/portals/my-pages/information/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../tsconfig.base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "jsx": "react-jsx", "allowJs": true, diff --git a/libs/portals/my-pages/information/tsconfig.lib.json b/libs/portals/my-pages/information/tsconfig.lib.json index d547efefcf0b..bc322cb27aa7 100644 --- a/libs/portals/my-pages/information/tsconfig.lib.json +++ b/libs/portals/my-pages/information/tsconfig.lib.json @@ -5,8 +5,8 @@ "types": ["node"] }, "files": [ - "../../../../node_modules/@nx/react/typings/cssmodule.d.ts", - "../../../../node_modules/@nx/react/typings/image.d.ts" + "../../../node_modules/@nx/react/typings/cssmodule.d.ts", + "../../../node_modules/@nx/react/typings/image.d.ts" ], "exclude": [ "**/*.spec.ts", diff --git a/libs/portals/my-pages/law-and-order/src/screens/Subpoena/Subpoena.graphql b/libs/portals/my-pages/law-and-order/src/screens/Subpoena/Subpoena.graphql index 5134408d6183..1551e0e65a5f 100644 --- a/libs/portals/my-pages/law-and-order/src/screens/Subpoena/Subpoena.graphql +++ b/libs/portals/my-pages/law-and-order/src/screens/Subpoena/Subpoena.graphql @@ -21,8 +21,6 @@ query GetSubpoena($input: LawAndOrderSubpoenaInput!, $locale: String!) { texts { confirmation description - information - deadline } } } diff --git a/libs/portals/my-pages/law-and-order/src/screens/Subpoena/Subpoena.tsx b/libs/portals/my-pages/law-and-order/src/screens/Subpoena/Subpoena.tsx index 06b836951131..819da2fc84f4 100644 --- a/libs/portals/my-pages/law-and-order/src/screens/Subpoena/Subpoena.tsx +++ b/libs/portals/my-pages/law-and-order/src/screens/Subpoena/Subpoena.tsx @@ -134,13 +134,9 @@ const Subpoena = () => { )} - {subpoena.texts?.information ?? - formatMessage(messages.subpoenaInfoText)} - - - {subpoena.texts?.deadline ?? - formatMessage(messages.subpoenaInfoText2)} + {formatMessage(messages.subpoenaInfoText)} + {formatMessage(messages.subpoenaInfoText2)} {!loading && subpoena.data.hasChosen === false && ( diff --git a/libs/portals/my-pages/law-and-order/tsconfig.json b/libs/portals/my-pages/law-and-order/tsconfig.json index c88d07daddd5..4daaf45cd328 100644 --- a/libs/portals/my-pages/law-and-order/tsconfig.json +++ b/libs/portals/my-pages/law-and-order/tsconfig.json @@ -16,5 +16,5 @@ "path": "./tsconfig.spec.json" } ], - "extends": "../../../../tsconfig.base.json" + "extends": "../../../tsconfig.base.json" } diff --git a/libs/portals/my-pages/licenses/tsconfig.json b/libs/portals/my-pages/licenses/tsconfig.json index 4aee5eb425be..45d3d172fde4 100644 --- a/libs/portals/my-pages/licenses/tsconfig.json +++ b/libs/portals/my-pages/licenses/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../tsconfig.base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "jsx": "react-jsx", "allowJs": true diff --git a/libs/portals/my-pages/licenses/tsconfig.lib.json b/libs/portals/my-pages/licenses/tsconfig.lib.json index d547efefcf0b..bc322cb27aa7 100644 --- a/libs/portals/my-pages/licenses/tsconfig.lib.json +++ b/libs/portals/my-pages/licenses/tsconfig.lib.json @@ -5,8 +5,8 @@ "types": ["node"] }, "files": [ - "../../../../node_modules/@nx/react/typings/cssmodule.d.ts", - "../../../../node_modules/@nx/react/typings/image.d.ts" + "../../../node_modules/@nx/react/typings/cssmodule.d.ts", + "../../../node_modules/@nx/react/typings/image.d.ts" ], "exclude": [ "**/*.spec.ts", diff --git a/libs/portals/my-pages/occupational-licenses/tsconfig.json b/libs/portals/my-pages/occupational-licenses/tsconfig.json index 4aee5eb425be..45d3d172fde4 100644 --- a/libs/portals/my-pages/occupational-licenses/tsconfig.json +++ b/libs/portals/my-pages/occupational-licenses/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../tsconfig.base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "jsx": "react-jsx", "allowJs": true diff --git a/libs/portals/my-pages/occupational-licenses/tsconfig.lib.json b/libs/portals/my-pages/occupational-licenses/tsconfig.lib.json index d547efefcf0b..bc322cb27aa7 100644 --- a/libs/portals/my-pages/occupational-licenses/tsconfig.lib.json +++ b/libs/portals/my-pages/occupational-licenses/tsconfig.lib.json @@ -5,8 +5,8 @@ "types": ["node"] }, "files": [ - "../../../../node_modules/@nx/react/typings/cssmodule.d.ts", - "../../../../node_modules/@nx/react/typings/image.d.ts" + "../../../node_modules/@nx/react/typings/cssmodule.d.ts", + "../../../node_modules/@nx/react/typings/image.d.ts" ], "exclude": [ "**/*.spec.ts", diff --git a/libs/portals/my-pages/petitions/src/screens/PetitionsTable/ExportPetition/index.tsx b/libs/portals/my-pages/petitions/src/screens/PetitionsTable/ExportPetition/index.tsx index c906594ad194..f8b85267e442 100644 --- a/libs/portals/my-pages/petitions/src/screens/PetitionsTable/ExportPetition/index.tsx +++ b/libs/portals/my-pages/petitions/src/screens/PetitionsTable/ExportPetition/index.tsx @@ -10,10 +10,12 @@ import * as styles from '../styles.css' import { m } from '../../../lib/messages' import copyToClipboard from 'copy-to-clipboard' import { toast } from 'react-toastify' +import { EndorsementList } from '@island.is/api/schema' import { useMutation } from '@apollo/client' import { ExportList } from '../../queries' interface Props { + petition?: EndorsementList petitionId: string dropdownItems?: { href?: string diff --git a/libs/portals/my-pages/petitions/src/screens/PetitionsTable/index.tsx b/libs/portals/my-pages/petitions/src/screens/PetitionsTable/index.tsx index 4105e4a83be1..fd4fabdd3ac6 100644 --- a/libs/portals/my-pages/petitions/src/screens/PetitionsTable/index.tsx +++ b/libs/portals/my-pages/petitions/src/screens/PetitionsTable/index.tsx @@ -37,7 +37,11 @@ const PetitionsTable = (data: { {formatMessage(m.petitionsOverview)} - {data.canEdit && } + + {data.canEdit && ( + + )} + diff --git a/libs/portals/my-pages/petitions/tsconfig.json b/libs/portals/my-pages/petitions/tsconfig.json index 4aee5eb425be..45d3d172fde4 100644 --- a/libs/portals/my-pages/petitions/tsconfig.json +++ b/libs/portals/my-pages/petitions/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../tsconfig.base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "jsx": "react-jsx", "allowJs": true diff --git a/libs/portals/my-pages/petitions/tsconfig.lib.json b/libs/portals/my-pages/petitions/tsconfig.lib.json index ae80da66e65a..6394d9f9cd81 100644 --- a/libs/portals/my-pages/petitions/tsconfig.lib.json +++ b/libs/portals/my-pages/petitions/tsconfig.lib.json @@ -5,8 +5,8 @@ "types": ["node"] }, "files": [ - "../../../../node_modules/@nx/react/typings/cssmodule.d.ts", - "../../../../node_modules/@nx/react/typings/image.d.ts" + "../../../node_modules/@nx/react/typings/cssmodule.d.ts", + "../../../node_modules/@nx/react/typings/image.d.ts" ], "exclude": ["**/*.spec.ts", "**/*.test.ts", "**/*.spec.tsx", "**/*.test.tsx"], "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] diff --git a/libs/portals/my-pages/restrictions/tsconfig.json b/libs/portals/my-pages/restrictions/tsconfig.json index 93614c49d348..89f8ac0850c1 100644 --- a/libs/portals/my-pages/restrictions/tsconfig.json +++ b/libs/portals/my-pages/restrictions/tsconfig.json @@ -13,5 +13,5 @@ "path": "./tsconfig.lib.json" } ], - "extends": "../../../../tsconfig.base.json" + "extends": "../../../tsconfig.base.json" } diff --git a/libs/portals/my-pages/sessions/tsconfig.json b/libs/portals/my-pages/sessions/tsconfig.json index 3512bf7afeea..4b421814593c 100644 --- a/libs/portals/my-pages/sessions/tsconfig.json +++ b/libs/portals/my-pages/sessions/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../tsconfig.base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "jsx": "react-jsx", "allowJs": true, diff --git a/libs/portals/my-pages/sessions/tsconfig.lib.json b/libs/portals/my-pages/sessions/tsconfig.lib.json index 9668c0996130..b1fdab0f8413 100644 --- a/libs/portals/my-pages/sessions/tsconfig.lib.json +++ b/libs/portals/my-pages/sessions/tsconfig.lib.json @@ -5,8 +5,8 @@ "types": ["node"] }, "files": [ - "../../../../node_modules/@nx/react/typings/cssmodule.d.ts", - "../../../../node_modules/@nx/react/typings/image.d.ts" + "../../../node_modules/@nx/react/typings/cssmodule.d.ts", + "../../../node_modules/@nx/react/typings/image.d.ts" ], "exclude": [ "**/*.spec.ts", diff --git a/libs/portals/my-pages/signature-collection/tsconfig.lib.json b/libs/portals/my-pages/signature-collection/tsconfig.lib.json index ae80da66e65a..6394d9f9cd81 100644 --- a/libs/portals/my-pages/signature-collection/tsconfig.lib.json +++ b/libs/portals/my-pages/signature-collection/tsconfig.lib.json @@ -5,8 +5,8 @@ "types": ["node"] }, "files": [ - "../../../../node_modules/@nx/react/typings/cssmodule.d.ts", - "../../../../node_modules/@nx/react/typings/image.d.ts" + "../../../node_modules/@nx/react/typings/cssmodule.d.ts", + "../../../node_modules/@nx/react/typings/image.d.ts" ], "exclude": ["**/*.spec.ts", "**/*.test.ts", "**/*.spec.tsx", "**/*.test.tsx"], "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] diff --git a/libs/portals/my-pages/social-insurance-maintenance/tsconfig.json b/libs/portals/my-pages/social-insurance-maintenance/tsconfig.json index 93614c49d348..89f8ac0850c1 100644 --- a/libs/portals/my-pages/social-insurance-maintenance/tsconfig.json +++ b/libs/portals/my-pages/social-insurance-maintenance/tsconfig.json @@ -13,5 +13,5 @@ "path": "./tsconfig.lib.json" } ], - "extends": "../../../../tsconfig.base.json" + "extends": "../../../tsconfig.base.json" } diff --git a/libs/portals/my-pages/social-insurance-maintenance/tsconfig.lib.json b/libs/portals/my-pages/social-insurance-maintenance/tsconfig.lib.json index ae80da66e65a..6394d9f9cd81 100644 --- a/libs/portals/my-pages/social-insurance-maintenance/tsconfig.lib.json +++ b/libs/portals/my-pages/social-insurance-maintenance/tsconfig.lib.json @@ -5,8 +5,8 @@ "types": ["node"] }, "files": [ - "../../../../node_modules/@nx/react/typings/cssmodule.d.ts", - "../../../../node_modules/@nx/react/typings/image.d.ts" + "../../../node_modules/@nx/react/typings/cssmodule.d.ts", + "../../../node_modules/@nx/react/typings/image.d.ts" ], "exclude": ["**/*.spec.ts", "**/*.test.ts", "**/*.spec.tsx", "**/*.test.tsx"], "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] diff --git a/libs/react-spa/bff/src/lib/BffPoller.tsx b/libs/react-spa/bff/src/lib/BffPoller.tsx index 8374bbcdc5b0..223640bcdaf8 100644 --- a/libs/react-spa/bff/src/lib/BffPoller.tsx +++ b/libs/react-spa/bff/src/lib/BffPoller.tsx @@ -7,7 +7,7 @@ import { useBffBroadcaster, useUserInfo, } from './bff.hooks' -import { isNewUser } from './bff.utils' +import { isNewSession } from './bff.utils' type BffPollerProps = { children: ReactNode @@ -78,8 +78,8 @@ export const BffPoller = ({ // If user polling fails, likely due to 401, then sign in. signIn() } else if (newUser) { - // If user has changed (e.g. delegation switch), then notifiy tabs/windows/iframes and execute the callback. - if (isNewUser(newUser, userInfo)) { + // If session has changed (e.g. delegation switch), then notifiy tabs/windows/iframes and execute the callback. + if (isNewSession(newUser, userInfo)) { // Note! The tab, window, or iframe that sends this message will not receive it. // This is because the BroadcastChannel API does not broadcast messages to the sender. // Therefore we need to manually handle the new session in the current tab/window, by calling the newSessionCb(). diff --git a/libs/react-spa/bff/src/lib/BffProvider.tsx b/libs/react-spa/bff/src/lib/BffProvider.tsx index df65376bc1c5..184c770f0b05 100644 --- a/libs/react-spa/bff/src/lib/BffProvider.tsx +++ b/libs/react-spa/bff/src/lib/BffProvider.tsx @@ -8,7 +8,7 @@ import { BffSessionExpiredModal } from './BffSessionExpiredModal' import { ErrorScreen } from './ErrorScreen' import { BffBroadcastEvents, useBffBroadcaster } from './bff.hooks' import { ActionType, LoggedInState, initialState, reducer } from './bff.state' -import { createBffUrlGenerator, isNewUser } from './bff.utils' +import { createBffUrlGenerator, isNewSession } from './bff.utils' const BFF_SERVER_UNAVAILABLE = 'BFF_SERVER_UNAVAILABLE' @@ -45,7 +45,7 @@ export const BffProvider = ({ if ( isLoggedIn && event.data.type === BffBroadcastEvents.NEW_SESSION && - isNewUser(state.userInfo, event.data.userInfo) + isNewSession(state.userInfo, event.data.userInfo) ) { setSessionExpiredScreen(true) } else if (event.data.type === BffBroadcastEvents.LOGOUT) { diff --git a/libs/react-spa/bff/src/lib/bff.utils.ts b/libs/react-spa/bff/src/lib/bff.utils.ts index a7af87244515..1c7ce0f0e1d9 100644 --- a/libs/react-spa/bff/src/lib/bff.utils.ts +++ b/libs/react-spa/bff/src/lib/bff.utils.ts @@ -38,8 +38,11 @@ export const createQueryStr = (params: Record) => { } /** - * Checks if the user is a new user compared to the old user, by comparing the nationalId's + * This method checks if the user has a new session */ -export const isNewUser = (oldUser: BffUser, newUser: BffUser) => { - return oldUser.profile.nationalId !== newUser.profile.nationalId +export const isNewSession = (oldUser: BffUser, newUser: BffUser) => { + const oldSid = oldUser.profile.sid + const newSid = newUser.profile.sid + + return oldSid && newSid && oldSid !== newSid } diff --git a/libs/shared/types/src/lib/api-cms-domain.ts b/libs/shared/types/src/lib/api-cms-domain.ts index 3f34d00e515f..8bfa67cde1eb 100644 --- a/libs/shared/types/src/lib/api-cms-domain.ts +++ b/libs/shared/types/src/lib/api-cms-domain.ts @@ -6,7 +6,6 @@ export enum CustomPageUniqueIdentifier { PensionCalculator = 'PensionCalculator', OfficialJournalOfIceland = 'OfficialJournalOfIceland', Vacancies = 'Vacancies', - Grants = 'Grants', } export interface StatisticSourceValue { diff --git a/libs/testing/e2e/README.md b/libs/testing/e2e/README.md index 40fedb39f715..7fa5ac12b3db 100644 --- a/libs/testing/e2e/README.md +++ b/libs/testing/e2e/README.md @@ -1,220 +1,48 @@ -# E2E Testing Library +# E2E Testing -This library contains utility functions, shared configuration, and documentation to assist with end-to-end (E2E) testing for all apps using Playwright. +This library was generated with [Nx](https://nx.dev). It contains utility functions and configuration files that assist with end-to-end (E2E) testing in Playwright for various apps. -## 📚 Overview +## Overview -### Contents +This library includes: -- **🔧 Helper Functions**: Utility functions to simplify and standardize E2E testing across apps. -- **⚙️ Shared Playwright Configuration**: A common configuration used as the base for all app-specific Playwright configurations. -- **🌍 Multi-Environment Testing**: Support for running tests in `local`, `dev`, `staging`, and `prod` environments. +- **Helper Functions:** Utility functions designed to streamline E2E testing with Playwright. These functions cater to different applications across the project and help automate common testing workflows. +- **Global Playwright Configuration:** The `createGlobalConfig` function provides a shared Playwright configuration used across multiple applications. It standardizes the testing environment. -## 🚀 How to Use This Library +## Mockoon Usage Guide for E2E Tests -### Importing Helper Functions +This section explains how to use [Mockoon](https://mockoon.com/) to set up mock APIs for end-to-end (e2e) testing. -To use helper functions in your tests, import them directly from this library: +### What is Mockoon? -```typescript -import { myHelperFunction } from '@island.is/testing/e2e' -``` - -### Extending the Common Playwright Config - -Each app should create its own `playwright.config.ts` file that extends the shared configuration: - -```typescript -import { createPlaywrightConfig } from '@island.is/testing/e2e' - -const playwrightConfig = createPlaywrightConfig({ - webServerUrl: '', - command: '', - // Add any app-specific configurations here -}) - -export default playwrightConfig -``` - -## 🏃 Running Tests - -Use the following command structure to run tests for any app: - -```bash -yarn e2e -``` - -### Useful Playwright Commands and Flags - -- **Run with UI Mode**: Launch the tests with a UI to select and debug tests interactively. - - ```bash - yarn e2e --ui - ``` - -- **Run Tests Without Caching**: Ensure a fresh run of tests without using cached results. - - ```bash - yarn e2e --skip-nx-cache - ``` - -- **Run Tests with Tags**: Use tags to include or exclude specific tests. - - ```bash - # Run only tests tagged with @fast - yarn e2e --grep @fast - - # Exclude tests tagged with @fast - yarn e2e --grep-invert @fast - - # Run tests tagged with either @fast or @slow - yarn e2e --grep "@fast|@slow" - ``` - -- **View the Test Report**: After running tests, use this command to view the generated report: - - ```bash - yarn playwright show-report - ``` - -- **Run Specific Tests**: Use `--grep` to run tests matching a specific pattern: - - ```bash - yarn e2e --grep "Home Page Test" - ``` - -- **Debug Mode**: Run tests in debug mode for better visibility: - - ```bash - yarn e2e --debug - ``` +[Mockoon](https://mockoon.com/) is an open-source tool for creating mock APIs quickly and easily. It allows developers to simulate backend servers without relying on live backend services. This is especially useful for e2e testing, where consistency and repeatability of backend responses are important. -For more details on Playwright commands and flags, refer to the [official documentation](https://playwright.dev/docs/test-cli) +Mockoon provides both a graphical user interface (GUI) for managing API mock files and a command-line interface (CLI) for running these mock APIs in various environments, such as pipelines. -## ✍️ Writing Tests +### Opening an Existing Mock File in Mockoon -Run `yarn playwright codegen --output ` and modify the output. The selectors need special attention; they should be transformed to use roles or `data-testid` attributes for stability (see below on how to). +To view or modify an existing mock file: -### 🤔 What to Test +1. Open Mockoon. +2. Click on **+** and then click on **Open Local Environment**. +3. Choose the desired mock file, such as `apps//e2e/mocks/.json`. -Writing tests for every possible combination is time-consuming for you and the CI pipeline, with diminishing value beyond the most common cases. +This will load the mock configuration into the Mockoon UI, allowing you to inspect and edit the mock endpoints. -You should therefore aim to write test for: +### Creating a Mock File with Mockoon UI -- Most common usage patterns -- Usage/patterns that MUST NOT break -- Problematic cases likely to cause an error/bug +To create or modify a mock file: -### 🏗️ Test structure +1. Download and install [Mockoon](https://mockoon.com/download/) if you haven't already. +2. Open Mockoon and create a new environment: + - Click on **+** and then click on **New Local Environment**. + - Nema your mock file and choose a location for it e.g. `apps//e2e/mocks/.json`. + - Add endpoints, routes, and response details as needed. -Test cases are written in spec files. Tests are tagged based on their execution time or other criteria. For example, you can use tags like `@fast` for quick tests and `@slow` for longer-running tests. Here is an example of the folder layout for testing the search engine and front-page of the `web` project: +### Running a Mockoon Server with the CLI -```shell -web/ (app name) -├── home-page.spec.ts (feature name, kebab-case) -└── search.spec.ts -``` - -### 🗃️ Spec files - -A spec file should have only one description (`test.describe`) of what part of an app is being tested. Therein can be one or more test cases (`test`) with a description of what scenario each test case is testing. Setup and tear down can be done in `test.beforeAll`, `test.beforeEach`, `test.afterAll`, and `test.afterEach`. You should not _rely_ on `after*` ever running, and you should prepare your environment every time _before_ each test. For example: - -```jsx -test.describe('Overview part of banking app', () => { - test.beforeAll(() => { - // Create/clear database - // Seed database - }) - - /* NOTE: there is no guarantee this will run */ - test.afterAll(() => { - // Tear down database - // Log out - }) - - test.beforeEach(() => { - // Log in - // Basic state reset, e.g. clear inbox - }) - - test('should get paid', { tag: '@slow' }, () => { - // Make user get money using page.selector, page.click, etc. - // Verify money is present - }) -}) -``` - -Each test case (`test`) should test a specific scenario from end-to-end. If your test is getting long and complicated consider breaking it up within a `test` with `test.step`; each step will run in succession and the failure/success report is easier to read. Let’s take the operating licence application as an example; test various routes/cases: - -- Hotel permit with food, but no alcohol -- Hotel permit with food and alcohol -- Bar with only alcohol -- Home accommodation (AirBnB style), no food, no alcohol - -### 🧰 Using Fixtures and Mocking Server Responses - -Fixtures are essential for setting up controlled test data to ensure predictable test behavior. Mocking server responses can help simulate specific backend scenarios without relying on live data. Use the following approach to mock server responses: - -```typescript -await page.route('/api/endpoint', (route) => - route.fulfill({ - status: 200, - body: JSON.stringify({ key: 'mockData' }), - }), -) -``` - -### 😬 Tricky element searching - -Some apps, like service-portal and application-system-form, load their components _very_ asynchronously. This can be an issue when targeting some elements, but they do not appear on the first page load, but instead load after the basic page has loaded. - -In such cases you can wait for the elements to exist with `page.waitFor*` ([docs](https://playwright.dev/docs/api/class-page#page-wait-for-event)): - -```jsx -// Wait for there to be at least 3 checkboxes -await page.waitForSelector(':nth-match("role=checkbox", 3') -// Wait for any arbitrary function -await page.waitForFunction(async () => { - const timer = page.locator('role=timer') - const timeLeft = await timer.textContent() - return Number(timeLeft) < 10 -}) -``` - -## 🤖 Mockoon Usage Guide for E2E Tests - -### ❓ What is Mockoon? - -[Mockoon](https://mockoon.com/) is a tool for creating mock APIs, useful for simulating backend services during E2E testing. - -### 📂 Opening and Creating Mock Files - -- **To open an existing mock file**: Navigate to `apps//e2e/mocks/.json` in the Mockoon UI. -- **To create a new mock file**: - 1. Download [Mockoon](https://mockoon.com/download/). - 2. Create a new environment and save it to `apps//e2e/mocks/`. - -### 🖥️ Running Mockoon Server with CLI - -To start a mock server: +To run a mock server with the cli, use the following command: ```bash -yarn mockoon-cli start --data ./apps//e2e/mocks/.json --port +yarn mockoon-cli start --data ./apps//e2e/mocks/.json --port ``` - -## 🛠️ Troubleshooting and FAQs - -### 🔄 Common Issues - -- **500: Internal Server Error**: If not related to your code, contact the DevOps team. -- **💀 ESOCKETTIMEDOUT**: Likely an infrastructure issue. Reach out to DevOps if persistent. -- **⌛ Tests Timing Out**: Increase the timeout if known network issues exist: - - ```typescript - await page.goto('/my-url', { timeout: Timeout.medium }) - ``` - -## 📖 Additional Resources - -- Refer to each app directory's `README.md` for app-specific details and usage instructions. -- Check out the [Playwright Documentation](https://playwright.dev) for further information. diff --git a/libs/testing/e2e/src/index.ts b/libs/testing/e2e/src/index.ts index f7fba34a0d79..1e4c8baa004e 100644 --- a/libs/testing/e2e/src/index.ts +++ b/libs/testing/e2e/src/index.ts @@ -9,5 +9,4 @@ export * from './lib/session/session' export * from './lib/modules/urls' export * from './lib/helpers/utils' export * from './lib/config/playwright-config' -export { test, expect } from '@playwright/test' -export type { Page, Locator, BrowserContext } from '@playwright/test' +export { test, expect, Page, Locator, BrowserContext } from '@playwright/test' diff --git a/libs/testing/e2e/src/lib/config/playwright-config.ts b/libs/testing/e2e/src/lib/config/playwright-config.ts index d888387b0874..e8f9336fc70a 100644 --- a/libs/testing/e2e/src/lib/config/playwright-config.ts +++ b/libs/testing/e2e/src/lib/config/playwright-config.ts @@ -25,7 +25,7 @@ export const createPlaywrightConfig = ({ webServerUrl, port, command, - cwd, + cwd = '../../../', timeoutMs = 5 * 60 * 1000, }: PlaywrightConfigParams) => defineConfig({ diff --git a/libs/testing/e2e/src/lib/helpers/api-tools.ts b/libs/testing/e2e/src/lib/helpers/api-tools.ts index a4fd275f2143..09e4042c1b3f 100644 --- a/libs/testing/e2e/src/lib/helpers/api-tools.ts +++ b/libs/testing/e2e/src/lib/helpers/api-tools.ts @@ -1,4 +1,4 @@ -import { Page, BrowserContext } from '@playwright/test' +import { Page } from '@playwright/test' /** * Waits for a network request to complete and verifies its operation name. @@ -21,19 +21,3 @@ export const verifyRequestCompletion = async ( return await response.json() } - -/** - * Creates a new page in the given browser context and navigates to the specified URL. - * - * @param context - The browser context in which to create the new page. - * @param url - The URL to navigate to. - * @returns A promise that resolves to the created page. - */ -export const createPageAndNavigate = async ( - context: BrowserContext, - url: string, -) => { - const page = await context.newPage() - await page.goto(url) - return page -} diff --git a/libs/user-monitoring/src/lib/user-monitoring.ts b/libs/user-monitoring/src/lib/user-monitoring.ts index 478d458340a7..e1315a4118b5 100644 --- a/libs/user-monitoring/src/lib/user-monitoring.ts +++ b/libs/user-monitoring/src/lib/user-monitoring.ts @@ -20,7 +20,7 @@ const initDdRum = (params: DdRumInitParams) => { version: params.version, sampleRate: 100, sessionReplaySampleRate: 0, - trackInteractions: false, + trackInteractions: true, allowedTracingOrigins: [ 'https://island.is', /https:\/\/.*\.island\.is/, diff --git a/package.json b/package.json index 6b6e2e8ef17a..0a9d57c78aec 100644 --- a/package.json +++ b/package.json @@ -58,8 +58,8 @@ "codegen": "node scripts/codegen.js", "schemas": "echo 'The `schemas` script is deprecated. Please run `yarn codegen` instead.' && node scripts/codegen.js", "check-tags": "yarn ts scripts/check-tags.ts", - "charts": "cd infra/ && yarn charts", - "infra": "cd infra/ && yarn infra", + "charts": "yarn infra charts", + "infra": "node scripts/infra-cmds.js", "run-proxies": "./scripts/run-proxies.sh", "proxies": "yarn run-proxies", "hmr-benchmark": "node -r esbuild-register scripts/hmr-benchmark.ts" @@ -330,7 +330,6 @@ "@babel/preset-env": "7.22.5", "@babel/preset-react": "7.22.5", "@babel/preset-typescript": "7.22.5", - "@formatjs/cli": "6.3.11", "@gperdomor/nx-docker": "0.2.3", "@graphql-codegen/cli": "2.12.0", "@graphql-codegen/fragment-matcher": "3.3.1", diff --git a/scripts/infra-cmds.js b/scripts/infra-cmds.js new file mode 100644 index 000000000000..be0fd7dd3d12 --- /dev/null +++ b/scripts/infra-cmds.js @@ -0,0 +1,80 @@ +const fs = require('fs') +const path = require('path') +const { spawn } = require('child_process') +const yargs = require('yargs/yargs') +const { hideBin } = require('yargs/helpers') +const winston = require('winston') +const { combine, printf, colorize } = winston.format +const { Console } = winston.transports + +const loadCommands = () => { + try { + const packageJsonPath = path.join(__dirname, '..', 'infra', 'package.json') + const packageJsonContents = fs.readFileSync(packageJsonPath, 'utf8') + const packageJson = JSON.parse(packageJsonContents) + const cmds = Object.keys(packageJson.scripts) + return cmds + } catch (e) { + throw new Error(`Failed to load commands: ${e.message}`) + } +} + +const formatMessage = winston.format((info) => { + const { warningType, title, description } = info.message + info.message = `[${warningType}Warning]: ${title}\n\n${description}\n` + return info +}) + +const logger = winston.createLogger({ + level: 'warn', + format: combine( + formatMessage(), + colorize({ all: true }), + printf((info) => `${info.message}`), + ), + transports: [new Console()], +}) + +yargs(hideBin(process.argv)) + .command({ + command: 'install', + describe: 'Install infra dependencies', + handler: (argv) => { + const arg = argv['_'] + const run = spawn(`yarn`, ['./infra', arg], { + shell: true, + stdio: 'inherit', + }) + + run.on('exit', (code) => { + if (code !== 0) process.exit(code) + }) + }, + }) + .command( + '$0', + 'infra', + () => {}, + (argv) => { + const cmds = loadCommands() + const [cmd, ...rest] = argv._ ?? [] + const args = cmds.includes(cmd) ? [cmd, ...rest] : ['cli', ...rest] + if (!cmd) { + logger.warn({ + warningType: 'Deprecation', + title: '"yarn infra" command will not install dependencies', + description: + 'For better separation of installing and running the infra cli, install the infra deps separately:\n\nyarn infra install', + }) + } + const run = spawn(`yarn`, ['./infra', args], { + shell: true, + stdio: 'inherit', + }) + run.on('exit', (code) => { + if (code !== 0) process.exit(code) + }) + }, + ) + .help() + .parse() diff --git a/yarn.lock b/yarn.lock index ba4c34531891..89e37c725552 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11866,41 +11866,6 @@ __metadata: languageName: node linkType: hard -"@formatjs/cli@npm:6.3.11": - version: 6.3.11 - resolution: "@formatjs/cli@npm:6.3.11" - peerDependencies: - "@glimmer/env": ^0.1.7 - "@glimmer/reference": ^0.91.1 || ^0.92.0 - "@glimmer/syntax": ^0.92.0 - "@glimmer/validator": ^0.92.0 - "@vue/compiler-core": ^3.4.0 - content-tag: ^2.0.1 - ember-template-recast: ^6.1.4 - vue: ^3.4.0 - peerDependenciesMeta: - "@glimmer/env": - optional: true - "@glimmer/reference": - optional: true - "@glimmer/syntax": - optional: true - "@glimmer/validator": - optional: true - "@vue/compiler-core": - optional: true - content-tag: - optional: true - ember-template-recast: - optional: true - vue: - optional: true - bin: - formatjs: bin/formatjs - checksum: 1b4a5ee4d371e4ba993e1fd3f7474b333022f27727d27a0560703fecae1fea584ff2b65ca52f474f5b3a11f4e64be056273266f0c1fbc0ffda86e8e812f97264 - languageName: node - linkType: hard - "@formatjs/ecma402-abstract@npm:1.14.3": version: 1.14.3 resolution: "@formatjs/ecma402-abstract@npm:1.14.3" @@ -38642,7 +38607,6 @@ __metadata: "@dnd-kit/core": 6.1.0 "@dnd-kit/sortable": 8.0.0 "@elastic/elasticsearch": 7.7.1 - "@formatjs/cli": 6.3.11 "@formatjs/intl-datetimeformat": 4.1.6 "@formatjs/intl-locale": 2.4.33 "@formatjs/intl-numberformat": 7.1.5