diff --git a/apps/contentful-apps/pages/fields/admin-only-boolean-field.tsx b/apps/contentful-apps/pages/fields/admin-only-boolean-field.tsx new file mode 100644 index 000000000000..3bf79f5d25a3 --- /dev/null +++ b/apps/contentful-apps/pages/fields/admin-only-boolean-field.tsx @@ -0,0 +1,16 @@ +import { FieldExtensionSDK } from '@contentful/app-sdk' +import { Paragraph } from '@contentful/f36-components' +import { BooleanEditor } from '@contentful/field-editor-boolean' +import { useSDK } from '@contentful/react-apps-toolkit' + +const AdminOnlyBooleanField = () => { + const sdk = useSDK() + + if (!sdk.user.spaceMembership.admin) { + return (Only admins can edit this field) + } + + return +} + +export default AdminOnlyBooleanField diff --git a/apps/judicial-system/api/src/app/modules/case-list/models/caseList.model.ts b/apps/judicial-system/api/src/app/modules/case-list/models/caseList.model.ts index 6372ac2e17d2..b5bf4659ebcc 100644 --- a/apps/judicial-system/api/src/app/modules/case-list/models/caseList.model.ts +++ b/apps/judicial-system/api/src/app/modules/case-list/models/caseList.model.ts @@ -11,6 +11,7 @@ import { CourtSessionType, IndictmentCaseReviewDecision, IndictmentDecision, + PunishmentType, } from '@island.is/judicial-system/types' import { Defendant } from '../../defendant' @@ -142,4 +143,10 @@ export class CaseListEntry { @Field(() => String, { nullable: true }) readonly indictmentCompletedDate?: string + + // TEMP: Use with caution! This key will never be populated. + // It was added to bypass table component type checks for a required custom sort key + // until we have a resolution on how to handle multiple defendants in the case list + @Field(() => PunishmentType, { nullable: true }) + readonly defendantsPunishmentType?: PunishmentType } diff --git a/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts b/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts index 589709247bd9..437900ffb488 100644 --- a/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts +++ b/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts @@ -110,6 +110,9 @@ export class Defendant { @Field(() => String, { nullable: true }) readonly sentToPrisonAdminDate?: string + @Field(() => String, { nullable: true }) + readonly openedByPrisonAdminDate?: string + @Field(() => PunishmentType, { nullable: true }) readonly punishmentType?: PunishmentType } 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 75fe945637d3..ff03a3b13def 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 @@ -417,6 +417,16 @@ export const caseListInclude: Includeable[] = [ as: 'defendants', required: false, order: [['created', 'ASC']], + include: [ + { + model: DefendantEventLog, + as: 'eventLogs', + required: false, + where: { eventType: defendantEventTypes }, + order: [['created', 'DESC']], + separate: true, + }, + ], separate: true, }, { @@ -1302,18 +1312,29 @@ export class CaseService { (defendant) => defendant.id === updatedDefendant.id, )?.subpoenas?.[0]?.id !== updatedDefendant.subpoenas?.[0]?.id, ) - .map((updatedDefendant) => ({ - type: MessageType.DELIVERY_TO_POLICE_SUBPOENA, - user, - caseId: theCase.id, - elementId: [ - updatedDefendant.id, - updatedDefendant.subpoenas?.[0].id ?? '', - ], - })) + .map((updatedDefendant) => [ + { + type: MessageType.DELIVERY_TO_POLICE_SUBPOENA, + user, + caseId: theCase.id, + elementId: [ + updatedDefendant.id, + updatedDefendant.subpoenas?.[0].id ?? '', + ], + }, + { + type: MessageType.DELIVERY_TO_COURT_SUBPOENA, + user, + caseId: theCase.id, + elementId: [ + updatedDefendant.id, + updatedDefendant.subpoenas?.[0].id ?? '', + ], + }, + ]) if (messages && messages.length > 0) { - return this.messageService.sendMessagesToQueue(messages) + return this.messageService.sendMessagesToQueue(messages.flat()) } } @@ -1416,7 +1437,10 @@ export class CaseService { await this.addMessagesForCourtCaseConnectionToQueue(updatedCase, user) } } else { - if (updatedCase.prosecutorId !== theCase.prosecutorId) { + if ( + !isIndictment && + updatedCase.prosecutorId !== theCase.prosecutorId + ) { // New prosecutor await this.addMessagesForProsecutorChangeToQueue(updatedCase, user) } diff --git a/apps/judicial-system/backend/src/app/modules/case/interceptors/case.interceptor.ts b/apps/judicial-system/backend/src/app/modules/case/interceptors/case.interceptor.ts index 85e302ad6754..adfb51abe7f2 100644 --- a/apps/judicial-system/backend/src/app/modules/case/interceptors/case.interceptor.ts +++ b/apps/judicial-system/backend/src/app/modules/case/interceptors/case.interceptor.ts @@ -7,6 +7,8 @@ import { NestInterceptor, } from '@nestjs/common' +import { DefendantEventType } from '@island.is/judicial-system/types' + import { Defendant, DefendantEventLog } from '../../defendant' import { Case } from '../models/case.model' import { CaseString } from '../models/caseString.model' @@ -15,8 +17,15 @@ export const transformDefendants = (defendants?: Defendant[]) => { return defendants?.map((defendant) => ({ ...defendant.toJSON(), sentToPrisonAdminDate: defendant.isSentToPrisonAdmin - ? DefendantEventLog.sentToPrisonAdminDate(defendant.eventLogs)?.created + ? DefendantEventLog.getDefendantEventLogTypeDate({ + defendantEventLogs: defendant.eventLogs, + eventType: DefendantEventType.SENT_TO_PRISON_ADMIN, + }) : undefined, + openedByPrisonAdminDate: DefendantEventLog.getDefendantEventLogTypeDate({ + defendantEventLogs: defendant.eventLogs, + eventType: DefendantEventType.OPENED_BY_PRISON_ADMIN, + }), })) } diff --git a/apps/judicial-system/backend/src/app/modules/case/interceptors/defendantIndictmentAccessed.interceptor.ts b/apps/judicial-system/backend/src/app/modules/case/interceptors/defendantIndictmentAccessed.interceptor.ts new file mode 100644 index 000000000000..64d122d8db7c --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/case/interceptors/defendantIndictmentAccessed.interceptor.ts @@ -0,0 +1,63 @@ +import { + CallHandler, + ExecutionContext, + Injectable, + NestInterceptor, +} from '@nestjs/common' + +import { + DefendantEventType, + isIndictmentCase, + isPrisonAdminUser, + User, +} from '@island.is/judicial-system/types' + +import { DefendantEventLog, DefendantService } from '../../defendant' +import { Case } from '../models/case.model' + +const hasValidOpenByPrisonAdminEvent = ( + defendantEventLogs: DefendantEventLog[], +) => { + const sentToPrisonAdminDate = DefendantEventLog.getDefendantEventLogTypeDate({ + defendantEventLogs, + eventType: DefendantEventType.SENT_TO_PRISON_ADMIN, + }) + const openedByPrisonAdminDate = + DefendantEventLog.getDefendantEventLogTypeDate({ + defendantEventLogs, + eventType: DefendantEventType.OPENED_BY_PRISON_ADMIN, + }) + return ( + sentToPrisonAdminDate && + openedByPrisonAdminDate && + sentToPrisonAdminDate <= openedByPrisonAdminDate + ) +} + +@Injectable() +export class DefendantIndictmentAccessedInterceptor implements NestInterceptor { + constructor(private readonly defendantService: DefendantService) {} + + intercept(context: ExecutionContext, next: CallHandler) { + const request = context.switchToHttp().getRequest() + const user: User = request.user + const theCase: Case = request.case + + if (isIndictmentCase(theCase.type) && isPrisonAdminUser(user)) { + const defendantsIndictmentNotOpened = theCase.defendants?.filter( + ({ isSentToPrisonAdmin, eventLogs = [] }) => + isSentToPrisonAdmin && !hasValidOpenByPrisonAdminEvent(eventLogs), + ) + + // create new events for all defendants that prison admin has not accessed according to defendant event logs + defendantsIndictmentNotOpened?.forEach((defendant) => + this.defendantService.createDefendantEvent({ + caseId: theCase.id, + defendantId: defendant.id, + eventType: DefendantEventType.OPENED_BY_PRISON_ADMIN, + }), + ) + } + return next.handle() + } +} 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 d48a86633f14..5b65a20a1abe 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 @@ -116,7 +116,10 @@ export class InternalCaseController { ) } - @UseGuards(CaseExistsGuard) + @UseGuards( + CaseExistsGuard, + new CaseTypeGuard([...restrictionCases, ...investigationCases]), + ) @Post( `case/:caseId/${messageEndpoint[MessageType.DELIVERY_TO_COURT_PROSECUTOR]}`, ) diff --git a/apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.controller.ts b/apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.controller.ts index c263e83f455d..be63d588328f 100644 --- a/apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.controller.ts @@ -30,13 +30,16 @@ import type { User as TUser } from '@island.is/judicial-system/types' import { CaseState, CaseType, + DefendantEventType, indictmentCases, investigationCases, restrictionCases, + UserRole, } from '@island.is/judicial-system/types' import { nowFactory } from '../../factories' import { defenderRule, prisonSystemStaffRule } from '../../guards' +import { DefendantService } from '../defendant' import { EventService } from '../event' import { User } from '../user' import { TransitionCaseDto } from './dto/transitionCase.dto' @@ -57,6 +60,7 @@ import { } from './guards/rolesRules' import { CaseInterceptor } from './interceptors/case.interceptor' import { CompletedAppealAccessedInterceptor } from './interceptors/completedAppealAccessed.interceptor' +import { DefendantIndictmentAccessedInterceptor } from './interceptors/defendantIndictmentAccessed.interceptor' import { LimitedAccessCaseFileInterceptor } from './interceptors/limitedAccessCaseFile.interceptor' import { Case } from './models/case.model' import { transitionCase } from './state/case.state' @@ -73,6 +77,7 @@ export class LimitedAccessCaseController { private readonly limitedAccessCaseService: LimitedAccessCaseService, private readonly eventService: EventService, private readonly pdfService: PdfService, + private readonly defendantService: DefendantService, @Inject(LOGGER_PROVIDER) private readonly logger: Logger, ) {} @@ -84,6 +89,7 @@ export class LimitedAccessCaseController { ) @RolesRules(prisonSystemStaffRule, defenderRule) @UseInterceptors( + DefendantIndictmentAccessedInterceptor, CompletedAppealAccessedInterceptor, LimitedAccessCaseFileInterceptor, CaseInterceptor, @@ -100,7 +106,7 @@ export class LimitedAccessCaseController { ): Promise { this.logger.debug(`Getting limitedAccess case ${caseId} by id`) - if (!theCase.openedByDefender) { + if (user.role === UserRole.DEFENDER && !theCase.openedByDefender) { const updated = await this.limitedAccessCaseService.update( theCase, { openedByDefender: nowFactory() }, 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..51d7fd6c5d6e 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 @@ -897,12 +897,24 @@ describe('CaseController - Update', () => { caseId: theCase.id, elementId: [defendantId1, subpoenaId1], }, + { + type: MessageType.DELIVERY_TO_COURT_SUBPOENA, + user, + caseId: theCase.id, + elementId: [defendantId1, subpoenaId1], + }, { type: MessageType.DELIVERY_TO_POLICE_SUBPOENA, user, caseId: theCase.id, elementId: [defendantId2, subpoenaId2], }, + { + type: MessageType.DELIVERY_TO_COURT_SUBPOENA, + user, + caseId: theCase.id, + elementId: [defendantId2, subpoenaId2], + }, ]) }) }) diff --git a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverProsecutorToCourtGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverProsecutorToCourtGuards.spec.ts index 4e5a488ca75b..3c13895d77c9 100644 --- a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverProsecutorToCourtGuards.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverProsecutorToCourtGuards.spec.ts @@ -1,6 +1,10 @@ -import { CanActivate } from '@nestjs/common' +import { + investigationCases, + restrictionCases, +} 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 prosecutor to court guards', () => { @@ -14,19 +18,12 @@ describe('InternalCaseController - Deliver prosecutor to court guards', () => { ) }) - it('should have one guards', () => { - expect(guards).toHaveLength(1) - }) - - describe('CaseExistsGuard', () => { - let guard: CanActivate - - beforeEach(() => { - guard = new guards[0]() - }) - - it('should have CaseExistsGuard as guard 1', () => { - expect(guard).toBeInstanceOf(CaseExistsGuard) + it('should have the right guard configuration', () => { + expect(guards).toHaveLength(2) + expect(new guards[0]()).toBeInstanceOf(CaseExistsGuard) + expect(guards[1]).toBeInstanceOf(CaseTypeGuard) + expect(guards[1]).toEqual({ + allowedCaseTypes: [...restrictionCases, ...investigationCases], }) }) }) diff --git a/apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getById.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getById.spec.ts index 034bded816f0..4036446f36ed 100644 --- a/apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getById.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getById.spec.ts @@ -1,6 +1,6 @@ import { uuid } from 'uuidv4' -import type { User } from '@island.is/judicial-system/types' +import { type User, UserRole } from '@island.is/judicial-system/types' import { createTestingCaseModule } from '../createTestingCaseModule' @@ -14,14 +14,18 @@ interface Then { error: Error } -type GivenWhenThen = (caseId: string, theCase: Case) => Promise +type GivenWhenThen = ( + caseId: string, + theCase: Case, + user?: User, +) => Promise describe('LimitedAccessCaseController - Get by id', () => { let givenWhenThen: GivenWhenThen const openedBeforeDate = randomDate() const openedNowDate = randomDate() const caseId = uuid() - const user = { id: uuid() } as User + const defaultUser = { id: uuid() } as User let mockCaseModel: typeof Case @@ -42,7 +46,11 @@ describe('LimitedAccessCaseController - Get by id', () => { const mockFindOne = mockCaseModel.findOne as jest.Mock mockFindOne.mockResolvedValue(updatedCase) - givenWhenThen = async (caseId: string, theCase: Case) => { + givenWhenThen = async ( + caseId: string, + theCase: Case, + user = defaultUser, + ) => { const then = {} as Then try { @@ -79,11 +87,11 @@ describe('LimitedAccessCaseController - Get by id', () => { describe('case exists and has not been opened by defender before', () => { const theCase = { id: caseId } as Case - + const user = { ...defaultUser, role: UserRole.DEFENDER } as User let then: Then beforeEach(async () => { - then = await givenWhenThen(caseId, theCase) + then = await givenWhenThen(caseId, theCase, user) }) it('should update openedByDefender and return case', () => { 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 33f9c74c7c83..1cfd7b5f014b 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 @@ -34,6 +34,7 @@ export enum CourtDocumentFolder { CASE_DOCUMENTS = 'Gögn málsins', COURT_DOCUMENTS = 'Dómar, úrskurðir og Þingbók', APPEAL_DOCUMENTS = 'Kæra til Landsréttar', + SUBPOENA_DOCUMENTS = 'Boðanir', } export type Subtype = Exclude | IndictmentSubtype @@ -342,6 +343,7 @@ export class CourtService { return await this.courtClientService.createCase(courtId, { caseType: isIndictment ? 'S - Ákærumál' : 'R - Rannsóknarmál', + // TODO: send a list of subtypes when CourtService supports it subtype: courtSubtype as string, status: 'Skráð', receivalDate: formatISO(receivalDate, { representation: 'date' }), 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 6276f0634115..d1f24d828393 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 @@ -271,6 +271,22 @@ export class DefendantService { return updatedDefendant } + async createDefendantEvent({ + caseId, + defendantId, + eventType, + }: { + caseId: string + defendantId: string + eventType: DefendantEventType + }): Promise { + await this.defendantEventLogModel.create({ + caseId, + defendantId, + eventType, + }) + } + async updateIndictmentCaseDefendant( theCase: Case, defendant: Defendant, @@ -284,7 +300,7 @@ export class DefendantService { ) if (update.isSentToPrisonAdmin) { - this.defendantEventLogModel.create({ + this.createDefendantEvent({ caseId: theCase.id, defendantId: defendant.id, eventType: DefendantEventType.SENT_TO_PRISON_ADMIN, diff --git a/apps/judicial-system/backend/src/app/modules/defendant/models/defendantEventLog.model.ts b/apps/judicial-system/backend/src/app/modules/defendant/models/defendantEventLog.model.ts index ca0a332704ee..df19027fd0f9 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/models/defendantEventLog.model.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/models/defendantEventLog.model.ts @@ -20,11 +20,17 @@ import { Defendant } from './defendant.model' timestamps: true, }) export class DefendantEventLog extends Model { - static sentToPrisonAdminDate(defendantEventLogs?: DefendantEventLog[]) { + // gets the latest log date of a given type, since the defendant event logs are sorted + static getDefendantEventLogTypeDate({ + defendantEventLogs, + eventType, + }: { + defendantEventLogs?: DefendantEventLog[] + eventType: DefendantEventType + }) { return defendantEventLogs?.find( - (defendantEventLog) => - defendantEventLog.eventType === DefendantEventType.SENT_TO_PRISON_ADMIN, - ) + (defendantEventLog) => defendantEventLog.eventType === eventType, + )?.created } @Column({ 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 87402f7b3ada..2937e657a3f4 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 @@ -67,7 +67,7 @@ export class InternalSubpoenaController { ) @ApiOkResponse({ type: DeliverResponse, - description: 'Delivers a subpoena to police', + description: 'Delivers a subpoena to the police', }) deliverSubpoenaToPolice( @Param('caseId') caseId: string, @@ -79,7 +79,7 @@ export class InternalSubpoenaController { @Body() deliverDto: DeliverDto, ): Promise { this.logger.debug( - `Delivering subpoena ${subpoenaId} to police for defendant ${defendantId} of case ${caseId}`, + `Delivering subpoena ${subpoenaId} pdf to police for defendant ${defendantId} of case ${caseId}`, ) return this.subpoenaService.deliverSubpoenaToPolice( @@ -89,4 +89,40 @@ export class InternalSubpoenaController { deliverDto.user, ) } + + @UseGuards( + CaseExistsGuard, + new CaseTypeGuard(indictmentCases), + DefendantExistsGuard, + SubpoenaExistsGuard, + ) + @Post( + `case/:caseId/${ + messageEndpoint[MessageType.DELIVERY_TO_COURT_SUBPOENA] + }/:defendantId/:subpoenaId`, + ) + @ApiOkResponse({ + type: DeliverResponse, + description: 'Delivers a subpoena to the court', + }) + deliverSubpoenaToCourt( + @Param('caseId') caseId: string, + @Param('defendantId') defendantId: string, + @Param('subpoenaId') subpoenaId: string, + @CurrentCase() theCase: Case, + @CurrentDefendant() defendant: Defendant, + @CurrentSubpoena() subpoena: Subpoena, + @Body() deliverDto: DeliverDto, + ): Promise { + this.logger.debug( + `Delivering subpoena ${subpoenaId} pdf to court for defendant ${defendantId} of case ${caseId}`, + ) + + return this.subpoenaService.deliverSubpoenaToCourt( + theCase, + defendant, + subpoena, + deliverDto.user, + ) + } } 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..05be74045e18 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,6 +4,7 @@ import { SequelizeModule } from '@nestjs/sequelize' import { MessageModule } from '@island.is/judicial-system/message' import { CaseModule } from '../case/case.module' +import { CourtModule } from '../court/court.module' import { DefendantModule } from '../defendant/defendant.module' import { Defendant } from '../defendant/models/defendant.model' import { EventModule } from '../event/event.module' @@ -23,6 +24,7 @@ import { SubpoenaService } from './subpoena.service' forwardRef(() => MessageModule), forwardRef(() => EventModule), forwardRef(() => DefendantModule), + CourtModule, 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 e2d3116d4aa5..a83405e8a488 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 @@ -31,6 +31,7 @@ import { import { Case } from '../case/models/case.model' import { PdfService } from '../case/pdf.service' +import { CourtDocumentFolder, CourtService } from '../court' import { DefendantService } from '../defendant/defendant.service' import { Defendant } from '../defendant/models/defendant.model' import { EventService } from '../event' @@ -93,6 +94,7 @@ export class SubpoenaService { private readonly fileService: FileService, private readonly eventService: EventService, private readonly defendantService: DefendantService, + private readonly courtService: CourtService, @Inject(LOGGER_PROVIDER) private readonly logger: Logger, ) {} @@ -356,6 +358,41 @@ export class SubpoenaService { } } + async deliverSubpoenaToCourt( + theCase: Case, + defendant: Defendant, + subpoena: Subpoena, + user: TUser, + ): Promise { + return this.pdfService + .getSubpoenaPdf(theCase, defendant, subpoena) + .then(async (pdf) => { + const fileName = `Fyrirkall - ${defendant.name}` + + return this.courtService.createDocument( + user, + theCase.id, + theCase.courtId, + theCase.courtCaseNumber, + CourtDocumentFolder.SUBPOENA_DOCUMENTS, + fileName, + `${fileName}.pdf`, + 'application/pdf', + pdf, + ) + }) + .then(() => ({ delivered: true })) + .catch((reason) => { + // Tolerate failure, but log error + this.logger.warn( + `Failed to upload subpoena ${subpoena.id} pdf to court for defendant ${defendant.id} of case ${theCase.id}`, + { reason }, + ) + + return { delivered: false } + }) + } + async getSubpoena(subpoena: Subpoena, user?: TUser): Promise { if (!subpoena.subpoenaId) { // The subpoena has not been delivered to the police 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..c4982ece5817 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,6 +13,7 @@ import { import { MessageService } from '@island.is/judicial-system/message' import { CaseService, PdfService } from '../../case' +import { CourtService } from '../../court' import { Defendant, DefendantService } from '../../defendant' import { EventService } from '../../event' import { FileService } from '../../file' @@ -24,6 +25,7 @@ import { Subpoena } from '../models/subpoena.model' import { SubpoenaController } from '../subpoena.controller' import { SubpoenaService } from '../subpoena.service' +jest.mock('@island.is/judicial-system/message') jest.mock('../../user/user.service') jest.mock('../../case/case.service') jest.mock('../../case/pdf.service') @@ -31,7 +33,7 @@ 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') +jest.mock('../../court/court.service') export const createTestingSubpoenaModule = async () => { const subpoenaModule = await Test.createTestingModule({ @@ -51,6 +53,7 @@ export const createTestingSubpoenaModule = async () => { FileService, EventService, DefendantService, + CourtService, { provide: LOGGER_PROVIDER, useValue: { @@ -94,6 +97,8 @@ export const createTestingSubpoenaModule = async () => { const fileService = subpoenaModule.get(FileService) + const courtService = subpoenaModule.get(CourtService) + const subpoenaModel = await subpoenaModule.resolve( getModelToken(Subpoena), ) @@ -118,6 +123,7 @@ export const createTestingSubpoenaModule = async () => { pdfService, policeService, fileService, + courtService, subpoenaModel, subpoenaService, subpoenaController, diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/deliverSubpoanaToCourtGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/deliverSubpoanaToCourtGuards.spec.ts new file mode 100644 index 000000000000..448185532c87 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/deliverSubpoanaToCourtGuards.spec.ts @@ -0,0 +1,29 @@ +import { indictmentCases } from '@island.is/judicial-system/types' + +import { CaseExistsGuard, CaseTypeGuard } from '../../../case' +import { DefendantExistsGuard } from '../../../defendant' +import { SubpoenaExistsGuard } from '../../guards/subpoenaExists.guard' +import { InternalSubpoenaController } from '../../internalSubpoena.controller' + +describe('InternalSubpoenaController - Deliver subpoena to court guards', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let guards: any[] + + beforeEach(() => { + guards = Reflect.getMetadata( + '__guards__', + InternalSubpoenaController.prototype.deliverSubpoenaToCourt, + ) + }) + + it('should have the right guard configuration', () => { + expect(guards).toHaveLength(4) + expect(new guards[0]()).toBeInstanceOf(CaseExistsGuard) + expect(guards[1]).toBeInstanceOf(CaseTypeGuard) + expect(guards[1]).toEqual({ + allowedCaseTypes: indictmentCases, + }) + expect(new guards[2]()).toBeInstanceOf(DefendantExistsGuard) + expect(new guards[3]()).toBeInstanceOf(SubpoenaExistsGuard) + }) +}) diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/deliverSubpoanaToPoliceGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/deliverSubpoanaToPoliceGuards.spec.ts new file mode 100644 index 000000000000..4f1663e3cc03 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/deliverSubpoanaToPoliceGuards.spec.ts @@ -0,0 +1,29 @@ +import { indictmentCases } from '@island.is/judicial-system/types' + +import { CaseExistsGuard, CaseTypeGuard } from '../../../case' +import { DefendantExistsGuard } from '../../../defendant' +import { SubpoenaExistsGuard } from '../../guards/subpoenaExists.guard' +import { InternalSubpoenaController } from '../../internalSubpoena.controller' + +describe('InternalSubpoenaController - Deliver subpoena to police guards', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let guards: any[] + + beforeEach(() => { + guards = Reflect.getMetadata( + '__guards__', + InternalSubpoenaController.prototype.deliverSubpoenaToPolice, + ) + }) + + it('should have the right guard configuration', () => { + expect(guards).toHaveLength(4) + expect(new guards[0]()).toBeInstanceOf(CaseExistsGuard) + expect(guards[1]).toBeInstanceOf(CaseTypeGuard) + expect(guards[1]).toEqual({ + allowedCaseTypes: indictmentCases, + }) + expect(new guards[2]()).toBeInstanceOf(DefendantExistsGuard) + expect(new guards[3]()).toBeInstanceOf(SubpoenaExistsGuard) + }) +}) diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/deliverSubpoenaToCourt.spec.ts b/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/deliverSubpoenaToCourt.spec.ts new file mode 100644 index 000000000000..1601f396fc59 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/deliverSubpoenaToCourt.spec.ts @@ -0,0 +1,111 @@ +import { uuid } from 'uuidv4' + +import { createTestingSubpoenaModule } from '../createTestingSubpoenaModule' + +import { Case, PdfService } from '../../../case' +import { CourtService } from '../../../court' +import { Defendant } from '../../../defendant' +import { DeliverDto } from '../../dto/deliver.dto' +import { DeliverResponse } from '../../models/deliver.response' +import { Subpoena } from '../../models/subpoena.model' + +interface Then { + result: DeliverResponse + error: Error +} + +type GivenWhenThen = () => Promise + +describe('InternalSubpoenaController - Deliver subpoena to court', () => { + const caseId = uuid() + const courtId = uuid() + const courtCaseNumber = uuid() + const subpoenaId = uuid() + const defendantId = uuid() + const defendantName = uuid() + + const subpoena = { id: subpoenaId } as Subpoena + const defendant = { + id: defendantId, + name: defendantName, + subpoenas: [subpoena], + } as Defendant + const theCase = { + id: caseId, + courtId, + courtCaseNumber, + defendants: [defendant], + } as Case + const user = { id: uuid() } + const dto = { user } as DeliverDto + + let mockPdfService: PdfService + let mockCourtService: CourtService + let givenWhenThen: GivenWhenThen + + beforeEach(async () => { + const { courtService, pdfService, internalSubpoenaController } = + await createTestingSubpoenaModule() + + mockPdfService = pdfService + const mockGetSubpoenaPdf = mockPdfService.getSubpoenaPdf as jest.Mock + mockGetSubpoenaPdf.mockRejectedValue(new Error('Some error')) + + mockCourtService = courtService + const mockCreateDocument = mockCourtService.createDocument as jest.Mock + mockCreateDocument.mockRejectedValue(new Error('Some error')) + + givenWhenThen = async () => { + const then = {} as Then + + await internalSubpoenaController + .deliverSubpoenaToCourt( + caseId, + defendantId, + subpoenaId, + theCase, + defendant, + subpoena, + dto, + ) + .then((result) => (then.result = result)) + .catch((error) => (then.error = error)) + + return then + } + }) + + describe('subpoena delivered to court', () => { + const subpoenaPdf = uuid() + let then: Then + + beforeEach(async () => { + const mockGetSubpoenaPdf = mockPdfService.getSubpoenaPdf as jest.Mock + mockGetSubpoenaPdf.mockResolvedValue(subpoenaPdf) + const mockCreateDocument = mockCourtService.createDocument as jest.Mock + mockCreateDocument.mockResolvedValue('') + + then = await givenWhenThen() + }) + + it('should deliver the subpoena', () => { + expect(mockPdfService.getSubpoenaPdf).toBeCalledWith( + theCase, + defendant, + subpoena, + ) + expect(mockCourtService.createDocument).toBeCalledWith( + user, + caseId, + courtId, + courtCaseNumber, + 'Boðanir', + `Fyrirkall - ${defendantName}`, + `Fyrirkall - ${defendantName}.pdf`, + 'application/pdf', + subpoenaPdf, + ) + expect(then.result).toEqual({ delivered: true }) + }) + }) +}) 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..ff02a709d797 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 @@ -2,14 +2,14 @@ import { uuid } from 'uuidv4' import { createTestingSubpoenaModule } from '../createTestingSubpoenaModule' -import { Case } from '../../../case' +import { Case, PdfService } 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' interface Then { + result: DeliverResponse error: Error } @@ -22,57 +22,59 @@ 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 delivered = { delivered: true } as DeliverResponse + const theCase = { id: caseId, defendants: [defendant] } as Case + const user = { id: uuid() } + const dto = { user } as DeliverDto - let mockSubpoenaService: SubpoenaService + let mockPdfService: PdfService let givenWhenThen: GivenWhenThen beforeEach(async () => { - const { subpoenaService, internalSubpoenaController } = + const { pdfService, internalSubpoenaController } = await createTestingSubpoenaModule() - mockSubpoenaService = subpoenaService - - const deliverSubpoenaToPoliceMock = jest.fn() - mockSubpoenaService.deliverSubpoenaToPolice = deliverSubpoenaToPoliceMock - - deliverSubpoenaToPoliceMock.mockResolvedValueOnce(delivered) + mockPdfService = pdfService + const mockGetSubpoenaPdf = mockPdfService.getSubpoenaPdf as jest.Mock + mockGetSubpoenaPdf.mockRejectedValue(new Error('Some error')) givenWhenThen = async () => { const then = {} as Then - try { - await internalSubpoenaController.deliverSubpoenaToPolice( + await internalSubpoenaController + .deliverSubpoenaToPolice( caseId, defendantId, subpoenaId, theCase, defendant, subpoena, - user, + dto, ) - } catch (error) { - then.error = error as Error - } + .then((result) => (then.result = result)) + .catch((error) => (then.error = error)) return then } }) describe('subpoena delivered to police', () => { + const subpoenaPdf = uuid() + let then: Then + beforeEach(async () => { - await givenWhenThen() + const mockGetSubpoenaPdf = mockPdfService.getSubpoenaPdf as jest.Mock + mockGetSubpoenaPdf.mockResolvedValue(subpoenaPdf) + + then = await givenWhenThen() }) it('should call deliverSubpoenaToPolice', () => { - expect(mockSubpoenaService.deliverSubpoenaToPolice).toHaveBeenCalledWith( + expect(mockPdfService.getSubpoenaPdf).toBeCalledWith( theCase, defendant, subpoena, - user.user, ) + // TODO: complete tests when all indictments are generated }) }) }) diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/updateSubpoeanaGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/updateSubpoeanaGuards.spec.ts index 1cad7bd28cdb..33106b7cc5a8 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/updateSubpoeanaGuards.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/updateSubpoeanaGuards.spec.ts @@ -1,7 +1,7 @@ import { PoliceSubpoenaExistsGuard } from '../../guards/policeSubpoenaExists.guard' import { InternalSubpoenaController } from '../../internalSubpoena.controller' -describe('InternalSubpoenaController - Update subpoena guards', () => { +describe('InternalSubpoenaController - Update subpoena guards', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any let guards: any[] diff --git a/apps/judicial-system/web/messages/Core/tables.ts b/apps/judicial-system/web/messages/Core/tables.ts index b2b2500d65bb..c17703f0fbf6 100644 --- a/apps/judicial-system/web/messages/Core/tables.ts +++ b/apps/judicial-system/web/messages/Core/tables.ts @@ -113,6 +113,11 @@ export const tables = defineMessages({ defaultMessage: 'Dómstóll', description: 'Notaður sem titill fyrir dómstóll dálk í lista yfir mál.', }, + punishmentType: { + id: 'judicial.system.core:tables.punishment_type', + defaultMessage: 'Refsitegund', + description: 'Notaður sem titill fyrir refsitegund dálk í lista yfir mál.', + }, sentencingDate: { id: 'judicial.system.core:tables.sentencing_date', defaultMessage: 'Dómsuppkvaðning', diff --git a/apps/judicial-system/web/src/components/FormProvider/case.graphql b/apps/judicial-system/web/src/components/FormProvider/case.graphql index 4aec6d983d4b..069e36645c3f 100644 --- a/apps/judicial-system/web/src/components/FormProvider/case.graphql +++ b/apps/judicial-system/web/src/components/FormProvider/case.graphql @@ -35,6 +35,7 @@ query Case($input: CaseQueryInput!) { subpoenaType isSentToPrisonAdmin sentToPrisonAdminDate + openedByPrisonAdminDate punishmentType subpoenas { id diff --git a/apps/judicial-system/web/src/components/FormProvider/limitedAccessCase.graphql b/apps/judicial-system/web/src/components/FormProvider/limitedAccessCase.graphql index dea05680c538..8d31030894bc 100644 --- a/apps/judicial-system/web/src/components/FormProvider/limitedAccessCase.graphql +++ b/apps/judicial-system/web/src/components/FormProvider/limitedAccessCase.graphql @@ -47,6 +47,7 @@ query LimitedAccessCase($input: CaseQueryInput!) { subpoenaType isSentToPrisonAdmin sentToPrisonAdminDate + openedByPrisonAdminDate punishmentType subpoenas { id diff --git a/apps/judicial-system/web/src/components/Table/Table.tsx b/apps/judicial-system/web/src/components/Table/Table.tsx index 8e572422391c..13e6a3cad088 100644 --- a/apps/judicial-system/web/src/components/Table/Table.tsx +++ b/apps/judicial-system/web/src/components/Table/Table.tsx @@ -180,6 +180,8 @@ const Table: FC = (props) => { switch (column) { case 'defendants': return entry.defendants?.[0]?.name ?? '' + case 'defendantsPunishmentType': + return entry.defendants?.[0]?.punishmentType ?? '' case 'courtCaseNumber': return courtAbbreviation ? `${courtAbbreviation}: ${entry.courtCaseNumber}` diff --git a/apps/judicial-system/web/src/components/Tags/CaseTag.strings.ts b/apps/judicial-system/web/src/components/Tags/CaseTag.strings.ts index 28f7807d82f3..0119c6c284ab 100644 --- a/apps/judicial-system/web/src/components/Tags/CaseTag.strings.ts +++ b/apps/judicial-system/web/src/components/Tags/CaseTag.strings.ts @@ -97,4 +97,29 @@ export const strings = defineMessages({ defaultMessage: 'Afturkallað', description: 'Notað fyrir "Afturkallað" tagg', }, + punishmentTypeImprisonment: { + id: 'judicial.system.core:case_tag.punishment_type_imprisonment', + defaultMessage: 'Óskb.', + description: 'Notað fyrir "Óskilorðsbundið" tagg', + }, + punishmentTypeProbation: { + id: 'judicial.system.core:case_tag.punishment_type_probation', + defaultMessage: 'Skb.', + description: 'Notað fyrir "Skilorðsbundið" tagg', + }, + punishmentTypeFine: { + id: 'judicial.system.core:case_tag.punishment_type_fine', + defaultMessage: 'Sekt', + description: 'Notað fyrir "Sekt" tagg', + }, + punishmentTypeIndictmentRulingDecisionFine: { + id: 'judicial.system.core:case_tag.punishment_type_indictment_ruling_decision_fine', + defaultMessage: 'VL', + description: 'Notað fyrir "Viðurlagaákvörðun" tagg', + }, + punishmentTypeSignedFineInvitation: { + id: 'judicial.system.core:case_tag.punishment_type_signed_fine_invitation', + defaultMessage: 'ÁS', + description: 'Notað fyrir "Áritað sektarboð" tagg', + }, }) diff --git a/apps/judicial-system/web/src/components/Tags/utils.ts b/apps/judicial-system/web/src/components/Tags/utils.ts index e4f1b3ac9f83..51cc830a199e 100644 --- a/apps/judicial-system/web/src/components/Tags/utils.ts +++ b/apps/judicial-system/web/src/components/Tags/utils.ts @@ -2,6 +2,7 @@ import { TagVariant } from '@island.is/island-ui/core' import { isDistrictCourtUser, isPublicProsecutorUser, + PunishmentType, } from '@island.is/judicial-system/types' import { @@ -119,3 +120,50 @@ export const getIndictmentRulingDecisionTag = ( return { color: 'darkerBlue', text: strings.complete } } } + +export const getPunishmentTypeTag = ( + punishmentType?: PunishmentType | null, +): { + color: TagVariant + text: { id: string; defaultMessage: string; description: string } +} | null => { + if (!punishmentType) return null + + const getPunishmentTypeLabel = (punishmentType?: PunishmentType | null) => { + switch (punishmentType) { + case PunishmentType.IMPRISONMENT: + return strings.punishmentTypeImprisonment + case PunishmentType.PROBATION: + return strings.punishmentTypeProbation + case PunishmentType.FINE: + return strings.punishmentTypeFine + case PunishmentType.INDICTMENT_RULING_DECISION_FINE: + return strings.punishmentTypeIndictmentRulingDecisionFine + case PunishmentType.SIGNED_FINE_INVITATION: + return strings.punishmentTypeSignedFineInvitation + default: + return strings.unknown + } + } + + return { + color: 'red' as TagVariant, + text: getPunishmentTypeLabel(punishmentType), + } +} + +export const getPrisonCaseStateTag = ( + prisonCaseState: CaseState, +): { + color: TagVariant + text: { id: string; defaultMessage: string; description: string } +} => { + switch (prisonCaseState) { + case CaseState.NEW: + return { color: 'purple', text: strings.new } + case CaseState.RECEIVED: + return { color: 'blue', text: strings.received } + default: + return { color: 'darkerBlue', text: strings.complete } + } +} diff --git a/apps/judicial-system/web/src/routes/Defender/Cases/components/DefenderCasesTable.tsx b/apps/judicial-system/web/src/routes/Defender/Cases/components/DefenderCasesTable.tsx index 169584028e9b..7a52c93d46ff 100644 --- a/apps/judicial-system/web/src/routes/Defender/Cases/components/DefenderCasesTable.tsx +++ b/apps/judicial-system/web/src/routes/Defender/Cases/components/DefenderCasesTable.tsx @@ -57,6 +57,9 @@ export const DefenderCasesTable: FC = ({ ) { return entry.defendants[0].name ?? '' } + if (column === 'courtDate') { + return entry.courtDate + } return entry.created } const { sortedData, requestSort, getClassNamesFor, isActiveColumn } = useSort( @@ -129,9 +132,13 @@ export const DefenderCasesTable: FC = ({ ) : ( - - {formatMessage(tables.hearingArrangementDate)} - + requestSort('courtDate')} + sortAsc={getClassNamesFor('courtDate') === 'ascending'} + sortDes={getClassNamesFor('courtDate') === 'descending'} + isActive={isActiveColumn('courtDate')} + /> )} diff --git a/apps/judicial-system/web/src/routes/Prison/IndictmentOverview/IndictmentOverview.strings.ts b/apps/judicial-system/web/src/routes/Prison/IndictmentOverview/IndictmentOverview.strings.ts index d574238883e5..bc87e97beef8 100644 --- a/apps/judicial-system/web/src/routes/Prison/IndictmentOverview/IndictmentOverview.strings.ts +++ b/apps/judicial-system/web/src/routes/Prison/IndictmentOverview/IndictmentOverview.strings.ts @@ -14,7 +14,12 @@ export const strings = defineMessages({ indictmentCompletedTitle: { id: 'judicial.system.core:indictment_overview.indictment_completed_title', defaultMessage: 'Dómsuppkvaðning {date}', - description: 'Titill á yfirliti ákæru fyrir fangelsi', + description: 'Undirtitill á yfirliti ákæru fyrir fangelsi', + }, + indictmentReceivedTitle: { + id: 'judicial.system.core:indictment_overview.indictment_received_title', + defaultMessage: 'Móttekið {date}', + description: 'Undirtitill á yfirliti ákæru fyrir fangelsi', }, infoCardDefendantsTitle: { id: 'judicial.system.core:indictment_overview.info_card_defendants_title', diff --git a/apps/judicial-system/web/src/routes/Prison/IndictmentOverview/IndictmentOverview.tsx b/apps/judicial-system/web/src/routes/Prison/IndictmentOverview/IndictmentOverview.tsx index beab42f136a8..82065184df56 100644 --- a/apps/judicial-system/web/src/routes/Prison/IndictmentOverview/IndictmentOverview.tsx +++ b/apps/judicial-system/web/src/routes/Prison/IndictmentOverview/IndictmentOverview.tsx @@ -81,6 +81,13 @@ const IndictmentOverview = () => { })} )} + {defendant?.openedByPrisonAdminDate && ( + + {formatMessage(strings.indictmentReceivedTitle, { + date: formatDate(defendant.openedByPrisonAdminDate, 'PPP'), + })} + + )} diff --git a/apps/judicial-system/web/src/routes/PublicProsecutor/Indictments/Overview/Overview.tsx b/apps/judicial-system/web/src/routes/PublicProsecutor/Indictments/Overview/Overview.tsx index 36abf477db77..5c6980497124 100644 --- a/apps/judicial-system/web/src/routes/PublicProsecutor/Indictments/Overview/Overview.tsx +++ b/apps/judicial-system/web/src/routes/PublicProsecutor/Indictments/Overview/Overview.tsx @@ -121,48 +121,50 @@ export const Overview = () => { - - - {fm(strings.reviewerSubtitle, { - isFine: - workingCase.indictmentRulingDecision === - CaseIndictmentRulingDecision.FINE, - indictmentAppealDeadline: formatDate( - workingCase.indictmentAppealDeadline, - ), - appealDeadlineIsInThePast: - workingCase.indictmentVerdictAppealDeadlineExpired, - })} - - } - /> - - { + setSelectedIndictmentReviewer(value as Option) + }} + isDisabled={loading} + required + /> + + + )} { { title: formatMessage(tables.court), }, + { + title: formatMessage(tables.punishmentType), + sortable: { isSortable: true, key: 'defendantsPunishmentType' }, + }, { title: capitalize(formatMessage(tables.sentencingDate)), }, @@ -211,17 +221,43 @@ export const PrisonCases: FC = () => { { cell: (row) => , }, + { + cell: (row) => { + const punishmentType = isNonEmptyArray(row.defendants) + ? row.defendants[0].punishmentType + : undefined + const punishmentTypeTag = getPunishmentTypeTag(punishmentType) + return punishmentTypeTag ? ( + + ) : null + }, + }, { cell: (row) => ( ), }, { - cell: () => ( - - {'Nýtt'} - - ), + cell: (row) => { + const prisonCaseState = + row.defendants && + row.defendants?.length > 0 && + row.defendants[0].openedByPrisonAdminDate + ? CaseState.RECEIVED + : CaseState.NEW + const prisonCaseStateTag = + getPrisonCaseStateTag(prisonCaseState) + + return ( + + ) + }, }, ]} generateContextMenuItems={(row) => [openCaseInNewTabMenuItem(row.id)]} diff --git a/apps/judicial-system/web/src/routes/Shared/Cases/cases.graphql b/apps/judicial-system/web/src/routes/Shared/Cases/cases.graphql index 4001b83c6fcb..b188cbfac1aa 100644 --- a/apps/judicial-system/web/src/routes/Shared/Cases/cases.graphql +++ b/apps/judicial-system/web/src/routes/Shared/Cases/cases.graphql @@ -28,7 +28,10 @@ query Cases { defenderChoice verdictViewDate isSentToPrisonAdmin + punishmentType + openedByPrisonAdminDate } + defendantsPunishmentType courtDate isValidToDateInThePast initialRulingDate diff --git a/apps/judicial-system/web/src/routes/Shared/Cases/prisonCases.graphql b/apps/judicial-system/web/src/routes/Shared/Cases/prisonCases.graphql index 04f635815e1e..98b6483bff99 100644 --- a/apps/judicial-system/web/src/routes/Shared/Cases/prisonCases.graphql +++ b/apps/judicial-system/web/src/routes/Shared/Cases/prisonCases.graphql @@ -26,6 +26,8 @@ query PrisonCases { name noNationalId defenderChoice + punishmentType + openedByPrisonAdminDate } courtDate isValidToDateInThePast diff --git a/apps/judicial-system/web/src/utils/arrayHelpers.spec.ts b/apps/judicial-system/web/src/utils/arrayHelpers.spec.ts new file mode 100644 index 000000000000..fa49af5eefe5 --- /dev/null +++ b/apps/judicial-system/web/src/utils/arrayHelpers.spec.ts @@ -0,0 +1,45 @@ +import { isEmptyArray, isNonEmptyArray, isPresentArray } from './arrayHelpers' + +describe('arrayHelpers', () => { + describe('isPresentArray', () => { + const testCases = [ + { input: undefined, expected: false }, + { input: null, expected: false }, + { input: [], expected: true }, + { input: [1], expected: true }, + ] + testCases.forEach(({ input, expected }) => { + it(`should return ${expected} for input ${input}`, () => { + expect(isPresentArray(input)).toBe(expected) + }) + }) + }) + + describe('isEmptyArray', () => { + const testCases = [ + { input: undefined, expected: false }, + { input: null, expected: false }, + { input: [], expected: true }, + { input: [1], expected: false }, + ] + testCases.forEach(({ input, expected }) => { + it(`should return ${expected} for input ${input}`, () => { + expect(isEmptyArray(input)).toBe(expected) + }) + }) + }) + + describe('isNonEmptyArray', () => { + const testCases = [ + { input: undefined, expected: false }, + { input: null, expected: false }, + { input: [], expected: false }, + { input: [1], expected: true }, + ] + testCases.forEach(({ input, expected }) => { + it(`should return ${expected} for input ${input}`, () => { + expect(isNonEmptyArray(input)).toBe(expected) + }) + }) + }) +}) diff --git a/apps/judicial-system/web/src/utils/arrayHelpers.ts b/apps/judicial-system/web/src/utils/arrayHelpers.ts new file mode 100644 index 000000000000..8b2d62131b7b --- /dev/null +++ b/apps/judicial-system/web/src/utils/arrayHelpers.ts @@ -0,0 +1,8 @@ +export const isPresentArray = (arr: T[] | undefined | null): arr is T[] => + arr !== undefined && arr !== null && Array.isArray(arr) + +export const isEmptyArray = (arr: T[] | undefined | null): arr is T[] => + isPresentArray(arr) && arr?.length === 0 + +export const isNonEmptyArray = (arr: T[] | undefined | null): arr is T[] => + isPresentArray(arr) && arr.length > 0 diff --git a/apps/native/app/android/app/build.gradle b/apps/native/app/android/app/build.gradle index 997472187673..93c2535cc85d 100644 --- a/apps/native/app/android/app/build.gradle +++ b/apps/native/app/android/app/build.gradle @@ -103,7 +103,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode getMyVersionCode(143) - versionName "1.4.8" + versionName "1.4.9" manifestPlaceholders = [ appAuthRedirectScheme: "is.island.app" // project.config.get("BUNDLE_ID_ANDROID") ] diff --git a/apps/native/app/ios/IslandApp/Info.plist b/apps/native/app/ios/IslandApp/Info.plist index 42e798076119..b60a26dfc867 100644 --- a/apps/native/app/ios/IslandApp/Info.plist +++ b/apps/native/app/ios/IslandApp/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.4.8 + 1.4.9 CFBundleSignature ???? CFBundleURLTypes diff --git a/apps/native/app/src/messages/en.ts b/apps/native/app/src/messages/en.ts index 24a6e3413d63..df10cf9a258e 100644 --- a/apps/native/app/src/messages/en.ts +++ b/apps/native/app/src/messages/en.ts @@ -665,15 +665,15 @@ export const en: TranslatedMessages = { // health - organ donation 'health.organDonation': 'Organ Donation', - 'health.organDonation.change': 'Breyta afstöðu', - 'health.organDonation.isDonor': 'Ég er líffæragjafi', + 'health.organDonation.change': 'Change selection', + 'health.organDonation.isDonor': 'I am an organ donor', 'health.organDonation.isDonorWithLimitations': - 'Ég heimila líffæragjöf, með takmörkunum.', - 'health.organDonation.isNotDonor': 'Ég heimila ekki líffæragjöf', + 'I allow organ donation, with restrictions.', + 'health.organDonation.isNotDonor': 'I forbid organ donation', 'health.organDonation.isDonorDescription': - 'Öll líffærin mín má nota til ígræðslu.', + 'My organs can all be used for transplantation.', 'health.organDonation.isNotDonorDescription': - 'Engin líffæri má nota til ígræðslu.', + 'No organs can be used for transplantation.', 'health.organDonation.isDonorWithLimitationsDescription': - 'Öll líffærin mín má nota til ígræðslu fyrir utan: {limitations}.', + 'All organs can be used for transplantation except: {limitations}.', } diff --git a/apps/web/components/Organization/Slice/OverviewLinks/OverviewLinks.tsx b/apps/web/components/Organization/Slice/OverviewLinks/OverviewLinks.tsx index 8b1a4604f4e3..1decf6b1bf48 100644 --- a/apps/web/components/Organization/Slice/OverviewLinks/OverviewLinks.tsx +++ b/apps/web/components/Organization/Slice/OverviewLinks/OverviewLinks.tsx @@ -55,23 +55,18 @@ export const OverviewLinksSlice: React.FC< key={index} direction={leftImage ? 'row' : 'rowReverse'} > - - - {/** - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error make web strict */} - - - + {image?.url && ( + + + + + + )} + > + {!indexableBySearchEngine && ( + + )} + { const formattedStartDate = format( new Date(item.startDate), - 'dd. MMMM yyyy', + 'd. MMMM yyyy', { locale: localeMap[locale], }, diff --git a/apps/web/screens/Article/Article.tsx b/apps/web/screens/Article/Article.tsx index 1ca78a49e537..16bfd88f3b59 100644 --- a/apps/web/screens/Article/Article.tsx +++ b/apps/web/screens/Article/Article.tsx @@ -262,12 +262,14 @@ interface ArticleSidebarProps { article: Article activeSlug?: string | string[] n: (s: string) => string + organizationNamespace: Record } const ArticleSidebar: FC> = ({ article, activeSlug, n, + organizationNamespace, }) => { const { linkResolver } = useLinkResolver() const { activeLocale } = useI18n() @@ -296,7 +298,9 @@ const ArticleSidebar: FC> = ({ {article?.organization && article.organization.length > 0 && ( > = ({ /> )} {article?.subArticles && article.subArticles.length > 0 && ( - + )} []; slug: string }[] stepperNamespace: GetNamespaceQuery['getNamespace'] + organizationNamespace: Record } const ArticleScreen: Screen = ({ @@ -332,6 +342,7 @@ const ArticleScreen: Screen = ({ namespace, stepperNamespace, stepOptionsFromNamespace, + organizationNamespace, }) => { const { activeLocale } = useI18n() const portalRef = useRef() @@ -540,7 +551,8 @@ const ArticleScreen: Screen = ({ // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error make web strict title: article.organization[0].title, - label: n('organization'), + label: + organizationNamespace?.['organization'] ?? n('organization'), // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error make web strict href: getOrganizationLink( @@ -616,6 +628,7 @@ const ArticleScreen: Screen = ({ article={article} n={n} activeSlug={query.subSlug} + organizationNamespace={organizationNamespace} /> } @@ -648,6 +661,7 @@ const ArticleScreen: Screen = ({ n={n} activeSlug={query.subSlug} isMenuDialog + organizationNamespace={organizationNamespace} /> )} @@ -774,6 +788,7 @@ const ArticleScreen: Screen = ({ n={n} activeSlug={query.subSlug} isMenuDialog + organizationNamespace={organizationNamespace} /> )} @@ -910,7 +925,7 @@ ArticleScreen.getProps = async ({ apolloClient, query, locale }) => { const organizationNamespace = extractNamespaceFromOrganization( article.organization?.[0], - ) + ) as Record return { article, @@ -918,6 +933,7 @@ ArticleScreen.getProps = async ({ apolloClient, query, locale }) => { stepOptionsFromNamespace, stepperNamespace, customTopLoginButtonItem: organizationNamespace?.customTopLoginButtonItem, + organizationNamespace, ...getThemeConfig(article), } } diff --git a/apps/web/screens/queries/Organization.tsx b/apps/web/screens/queries/Organization.tsx index 8a1c92b5a600..5a04733342db 100644 --- a/apps/web/screens/queries/Organization.tsx +++ b/apps/web/screens/queries/Organization.tsx @@ -127,6 +127,7 @@ export const GET_ORGANIZATION_PAGE_QUERY = gql` slug title description + canBeFoundInSearchResults topLevelNavigation { links { label diff --git a/charts/islandis/values.staging.yaml b/charts/islandis/values.staging.yaml index f7c409b592b4..2a2928d07132 100644 --- a/charts/islandis/values.staging.yaml +++ b/charts/islandis/values.staging.yaml @@ -1743,7 +1743,7 @@ search-indexer-service: cpuAverageUtilization: 90 nginxRequestsIrate: 5 replicas: - max: 3 + max: 1 min: 1 image: repository: '821090935708.dkr.ecr.eu-west-1.amazonaws.com/services-search-indexer' @@ -1821,7 +1821,7 @@ search-indexer-service: pvcs: [] replicaCount: default: 1 - max: 3 + max: 1 min: 1 resources: limits: diff --git a/charts/services/search-indexer-service/values.staging.yaml b/charts/services/search-indexer-service/values.staging.yaml index ba80482ce963..39cebc125692 100644 --- a/charts/services/search-indexer-service/values.staging.yaml +++ b/charts/services/search-indexer-service/values.staging.yaml @@ -49,7 +49,7 @@ hpa: cpuAverageUtilization: 90 nginxRequestsIrate: 5 replicas: - max: 3 + max: 1 min: 1 image: repository: '821090935708.dkr.ecr.eu-west-1.amazonaws.com/services-search-indexer' @@ -127,7 +127,7 @@ progressDeadlineSeconds: 1500 pvcs: [] replicaCount: default: 1 - max: 3 + max: 1 min: 1 resources: limits: diff --git a/infra/src/dsl/output-generators/map-to-helm-values.ts b/infra/src/dsl/output-generators/map-to-helm-values.ts index e18e6ab23e21..79724c6a0914 100644 --- a/infra/src/dsl/output-generators/map-to-helm-values.ts +++ b/infra/src/dsl/output-generators/map-to-helm-values.ts @@ -109,7 +109,7 @@ const serializeService: SerializeMethod = async ( result.resources = serviceDef.resources // replicas - if (env1.type == 'staging') { + if (env1.type == 'staging' && service.name.indexOf('search-indexer') == -1) { result.replicaCount = { min: 1, max: 3, diff --git a/libs/api/domains/health-directorate/src/lib/health-directorate.service.ts b/libs/api/domains/health-directorate/src/lib/health-directorate.service.ts index 9c4bdfc139ed..61395b471319 100644 --- a/libs/api/domains/health-directorate/src/lib/health-directorate.service.ts +++ b/libs/api/domains/health-directorate/src/lib/health-directorate.service.ts @@ -28,14 +28,20 @@ export class HealthDirectorateService { if (data === null) { return null } + const hasExceptionComment: boolean = + data.exceptionComment !== undefined && data.exceptionComment.length > 0 + const hasExceptions: boolean = + data.exceptions !== undefined && data.exceptions.length > 0 const donorStatus: Donor = { - isDonor: data?.isDonor ?? true, + isDonor: data.isDonor, limitations: { hasLimitations: - ((data?.exceptions?.length ?? 0) > 0 && data?.isDonor) ?? false, - limitedOrgansList: data?.exceptions, - comment: data?.exceptionComment, + ((hasExceptionComment || hasExceptions) && data.isDonor) ?? false, + limitedOrgansList: data.exceptions, + comment: data.exceptionComment, }, + isMinor: data.isMinor ?? false, + isTemporaryResident: data.isTemporaryResident ?? false, } return donorStatus } @@ -62,11 +68,15 @@ export class HealthDirectorateService { input: DonorInput, locale: Locale, ): Promise { + const filteredList = + input.organLimitations?.filter((item) => item !== 'other') ?? [] + return await this.organDonationApi.updateOrganDonation( auth, { isDonor: input.isDonor, - exceptions: input.organLimitations ?? [], + exceptions: filteredList, + exceptionComment: input.comment, }, locale === 'is' ? organLocale.Is : organLocale.En, ) diff --git a/libs/api/domains/health-directorate/src/lib/models/organ-donation.model.ts b/libs/api/domains/health-directorate/src/lib/models/organ-donation.model.ts index 8893f2149324..824124479661 100644 --- a/libs/api/domains/health-directorate/src/lib/models/organ-donation.model.ts +++ b/libs/api/domains/health-directorate/src/lib/models/organ-donation.model.ts @@ -33,6 +33,12 @@ export class Donor { @Field(() => Limitations, { nullable: true }) limitations?: Limitations + + @Field(() => Boolean, { defaultValue: false }) + isMinor!: boolean + + @Field(() => Boolean, { defaultValue: false }) + isTemporaryResident!: boolean } @ObjectType('HealthDirectorateOrganDonation') @@ -54,4 +60,7 @@ export class DonorInput { @Field(() => [String], { nullable: true }) organLimitations?: string[] + + @Field({ nullable: true }) + comment?: string } diff --git a/libs/api/domains/official-journal-of-iceland/src/lib/models/advert.input.ts b/libs/api/domains/official-journal-of-iceland/src/lib/models/advert.input.ts index 868f78ae3130..0e17f4f59ecd 100644 --- a/libs/api/domains/official-journal-of-iceland/src/lib/models/advert.input.ts +++ b/libs/api/domains/official-journal-of-iceland/src/lib/models/advert.input.ts @@ -1,5 +1,5 @@ import { AdvertSignatureTypeEnum } from '@island.is/clients/official-journal-of-iceland' -import { InputType, Field, registerEnumType } from '@nestjs/graphql' +import { InputType, Field, registerEnumType, Int } from '@nestjs/graphql' registerEnumType(AdvertSignatureTypeEnum, { name: 'OfficialJournalOfIcelandAdvertSignatureType', @@ -10,10 +10,10 @@ export class AdvertsInput { @Field(() => String, { nullable: true }) search?: string - @Field(() => Number, { nullable: true }) + @Field(() => Int, { nullable: true }) page?: number - @Field(() => Number, { nullable: true }) + @Field(() => Int, { nullable: true }) pageSize?: number @Field(() => [String], { nullable: true }) @@ -43,10 +43,10 @@ export class TypeQueryParams { @Field(() => String, { nullable: true }) department?: string - @Field(() => Number, { nullable: true }) + @Field(() => Int, { nullable: true }) page?: number - @Field(() => Number, { nullable: true }) + @Field(() => Int, { nullable: true }) pageSize?: number } @@ -61,10 +61,10 @@ export class QueryParams { @Field(() => String, { nullable: true }) search?: string - @Field(() => Number, { nullable: true }) + @Field(() => Int, { nullable: true }) page?: number - @Field(() => Number, { nullable: true }) + @Field(() => Int, { nullable: true }) pageSize?: number } @@ -134,3 +134,15 @@ export class SubmitApplicationInput { @Field(() => AdvertSignature) signature!: AdvertSignature } + +@InputType('OfficialJournalOfIcelandMainTypesInput') +export class MainTypesQueryParams { + @Field(() => String, { nullable: true }) + department?: string + + @Field(() => Int, { nullable: true }) + page?: number + + @Field(() => Int, { nullable: true }) + pageSize?: number +} diff --git a/libs/api/domains/official-journal-of-iceland/src/lib/models/advert.model.ts b/libs/api/domains/official-journal-of-iceland/src/lib/models/advert.model.ts index 30d22c9840f5..70a9135f7d45 100644 --- a/libs/api/domains/official-journal-of-iceland/src/lib/models/advert.model.ts +++ b/libs/api/domains/official-journal-of-iceland/src/lib/models/advert.model.ts @@ -136,3 +136,21 @@ export class Advert { @Field(() => AdvertDocument) document!: AdvertDocument } + +@ObjectType('OfficialJournalOfIcelandAdvertsMainType') +export class AdvertMainType { + @Field() + id!: string + + @Field() + title!: string + + @Field() + slug!: string + + @Field(() => AdvertEntity) + department!: AdvertEntity + + @Field(() => [AdvertType]) + types!: AdvertType[] +} diff --git a/libs/api/domains/official-journal-of-iceland/src/lib/models/advert.response.ts b/libs/api/domains/official-journal-of-iceland/src/lib/models/advert.response.ts index 633157789400..baa06f7a4f6f 100644 --- a/libs/api/domains/official-journal-of-iceland/src/lib/models/advert.response.ts +++ b/libs/api/domains/official-journal-of-iceland/src/lib/models/advert.response.ts @@ -5,6 +5,7 @@ import { AdvertEntity, AdvertMainCategory, AdvertType, + AdvertMainType, } from './advert.model' import { AdvertPaging } from './advert-paging.model' @@ -79,3 +80,12 @@ export class AdvertResponse { @Field(() => Advert) advert?: Advert } + +@ObjectType('OfficialJournalOfIcelandMainTypesResponse') +export class MainTypesResponse { + @Field(() => [AdvertMainType]) + mainTypes!: AdvertMainType[] + + @Field(() => AdvertPaging) + paging!: AdvertPaging +} diff --git a/libs/api/domains/official-journal-of-iceland/src/lib/officialJournalOfIceland.resolver.ts b/libs/api/domains/official-journal-of-iceland/src/lib/officialJournalOfIceland.resolver.ts index cde591d90f87..1fddbf0d108a 100644 --- a/libs/api/domains/official-journal-of-iceland/src/lib/officialJournalOfIceland.resolver.ts +++ b/libs/api/domains/official-journal-of-iceland/src/lib/officialJournalOfIceland.resolver.ts @@ -8,6 +8,7 @@ import { AdvertsInput, QueryParams, TypeQueryParams, + MainTypesQueryParams, } from './models/advert.input' import { AdvertCategoryResponse, @@ -19,6 +20,7 @@ import { AdvertsResponse, AdvertTypeResponse, AdvertTypesResponse, + MainTypesResponse, } from './models/advert.response' import { Features } from '@island.is/feature-flags' import { FeatureFlag } from '@island.is/nest/feature-flags' @@ -72,6 +74,13 @@ export class OfficialJournalOfIcelandResolver { return this.ojoiService.getAdvertTypes(params) } + @Query(() => MainTypesResponse, { + name: 'officialJournalOfIcelandMainTypes', + }) + getAdvertMainTypes(@Args('params') params: MainTypesQueryParams) { + return this.ojoiService.getMainTypes(params) + } + @Query(() => AdvertMainCategoriesResponse, { name: 'officialJournalOfIcelandMainCategories', }) diff --git a/libs/api/domains/official-journal-of-iceland/src/lib/officialJournalOfIceland.service.ts b/libs/api/domains/official-journal-of-iceland/src/lib/officialJournalOfIceland.service.ts index 954a7ab1d8ad..39d9c4c2507d 100644 --- a/libs/api/domains/official-journal-of-iceland/src/lib/officialJournalOfIceland.service.ts +++ b/libs/api/domains/official-journal-of-iceland/src/lib/officialJournalOfIceland.service.ts @@ -8,6 +8,7 @@ import { AdvertSingleParams, QueryParams, TypeQueryParams, + MainTypesQueryParams, } from './models/advert.input' import { AdvertCategoryResponse, @@ -17,6 +18,7 @@ import { AdvertResponse, AdvertsResponse, AdvertTypesResponse, + MainTypesResponse, } from './models/advert.response' import { CasesInProgressResponse } from './models/cases.response' @@ -54,6 +56,10 @@ export class OfficialJournalOfIcelandService { return await this.ojoiService.getAdvertTypes(params) } + async getMainTypes(params: MainTypesQueryParams): Promise { + return await this.ojoiService.getAdvertMainTypes(params) + } + async getInstitutions( params: QueryParams, ): Promise { diff --git a/libs/api/domains/user-profile/src/lib/V1/userProfile.service.ts b/libs/api/domains/user-profile/src/lib/V1/userProfile.service.ts deleted file mode 100644 index 07e9f769b098..000000000000 --- a/libs/api/domains/user-profile/src/lib/V1/userProfile.service.ts +++ /dev/null @@ -1,282 +0,0 @@ -import { Injectable } from '@nestjs/common' -import { logger } from '@island.is/logging' -import { ForbiddenError } from 'apollo-server-express' - -import { - ConfirmationDtoResponse, - CreateUserProfileDto, - UpdateUserProfileDto, - UserProfileApi, - UserProfileControllerCreateRequest, - UserProfileControllerUpdateRequest, -} from '@island.is/clients/user-profile' -import { handle204, handle404 } from '@island.is/clients/middlewares' -import { UpdateUserProfileInput } from '../dto/updateUserProfileInput' -import { CreateUserProfileInput } from '../dto/createUserProfileInput' -import { CreateSmsVerificationInput } from '../dto/createSmsVerificationInput' -import { CreateEmailVerificationInput } from '../dto/createEmalVerificationInput' -import { ConfirmSmsVerificationInput } from '../dto/confirmSmsVerificationInput' -import { ConfirmEmailVerificationInput } from '../dto/confirmEmailVerificationInput' -import { UserProfile } from '../userProfile.model' -import { Auth, AuthMiddleware, User } from '@island.is/auth-nest-tools' -import { IslykillService } from '../islykill.service' -import { DataStatus } from '../types/dataStatus.enum' - -@Injectable() -export class UserProfileServiceV1 { - constructor( - private userProfileApi: UserProfileApi, - private readonly islyklarService: IslykillService, - ) {} - - userProfileApiWithAuth(auth: Auth) { - return this.userProfileApi.withMiddleware(new AuthMiddleware(auth)) - } - - async getIslykillProfile(user: User) { - try { - const islyklarData = await this.islyklarService.getIslykillSettings( - user.nationalId, - ) - - return { - nationalId: user.nationalId, - emailVerified: false, - mobilePhoneNumberVerified: false, - documentNotifications: false, - emailStatus: DataStatus.NOT_VERIFIED, - mobileStatus: DataStatus.NOT_VERIFIED, - - // Islyklar data: - mobilePhoneNumber: islyklarData?.mobile, - email: islyklarData?.email, - canNudge: islyklarData?.canNudge, - bankInfo: islyklarData?.bankInfo, - } - } catch (e) { - logger.error(JSON.stringify(e)) - return null - } - } - - async getUserProfile(user: User) { - try { - const profile = await handle204( - this.userProfileApiWithAuth( - user, - ).userProfileControllerFindOneByNationalIdRaw({ - nationalId: user.nationalId, - }), - ) - - if (profile === null) { - /** - * Even if userProfileApiWithAuth does not exist. - * Islykill data might exist for the user, so we need to get that, with default values in the userprofile data. - */ - return await this.getIslykillProfile(user) - } - - const islyklarData = await this.islyklarService.getIslykillSettings( - user.nationalId, - ) - return { - ...profile, - // Temporary solution while we still run the old user profile service. - mobilePhoneNumber: islyklarData?.mobile, - email: islyklarData?.email, - canNudge: islyklarData?.canNudge, - bankInfo: islyklarData?.bankInfo, - emailNotifications: islyklarData?.canNudge, - } - } catch (error) { - handle404(error) - } - } - - async createUserProfile( - input: CreateUserProfileInput, - user: User, - ): Promise { - const createUserDto: CreateUserProfileDto = { - nationalId: user.nationalId, - //temporary as schemas where not working properly - locale: input.locale as string, - smsCode: input.smsCode, - emailCode: input.emailCode, - emailStatus: input.emailStatus, - mobileStatus: input.mobileStatus, - - /** - * Mobile and email will be within islykill service - * Only here for verification purposes in userProfile.controller. - * Will be removed in controller before saving to db - */ - mobilePhoneNumber: input.mobilePhoneNumber, - email: input.email, - } - const request: UserProfileControllerCreateRequest = { - createUserProfileDto: createUserDto, - } - - const userProfileResponse = await this.userProfileApiWithAuth( - user, - ).userProfileControllerCreate(request) - - if (input.email || input.mobilePhoneNumber) { - const islyklarData = await this.islyklarService.getIslykillSettings( - user.nationalId, - ) - - const emailVerified = - userProfileResponse.emailStatus === DataStatus.VERIFIED - const mobileVerified = - userProfileResponse.mobileStatus === DataStatus.VERIFIED - if ( - (input.email && !emailVerified) || - (input.mobilePhoneNumber && !mobileVerified) - ) { - throw new ForbiddenError('Updating value verification invalid') - } - - if (islyklarData.noUserFound) { - await this.islyklarService.createIslykillSettings(user.nationalId, { - email: emailVerified ? input.email : undefined, - mobile: mobileVerified ? input.mobilePhoneNumber : undefined, - }) - } else { - await this.islyklarService.updateIslykillSettings(user.nationalId, { - email: emailVerified ? input.email : islyklarData.email, - mobile: mobileVerified - ? input.mobilePhoneNumber - : islyklarData.mobile, - bankInfo: islyklarData.bankInfo, - canNudge: islyklarData.canNudge, - }) - } - } - - return userProfileResponse - } - - async updateMeUserProfile( - input: UpdateUserProfileInput, - user: User, - ): Promise { - const updateUserDto: UpdateUserProfileDto = { - //temporary as schemas where not working properly - locale: input.locale as string, - documentNotifications: input.documentNotifications, - smsCode: input.smsCode, - emailCode: input.emailCode, - - /** - * Mobile and email will be within islykill service - * Only here for verification purposes in userProfile.controller. - * Will be removed in controller before saving to db - */ - mobilePhoneNumber: input.mobilePhoneNumber, - email: input.email, - } - const request: UserProfileControllerUpdateRequest = { - nationalId: user.nationalId, - updateUserProfileDto: updateUserDto, - } - - const islyklarData = await this.islyklarService.getIslykillSettings( - user.nationalId, - ) - - const updatedUserProfile = await this.userProfileApiWithAuth( - user, - ).userProfileControllerUpdate(request) - - const emailVerified = updatedUserProfile.emailStatus === DataStatus.VERIFIED - const mobileVerified = - updatedUserProfile.mobileStatus === DataStatus.VERIFIED - if ( - (input.email && !emailVerified) || - (input.mobilePhoneNumber && !mobileVerified) - ) { - throw new ForbiddenError('Updating value verification invalid') - } - - if (islyklarData.noUserFound) { - await this.islyklarService.createIslykillSettings(user.nationalId, { - email: input.email && emailVerified ? input.email : islyklarData.email, - mobile: - input.mobilePhoneNumber && mobileVerified - ? input.mobilePhoneNumber - : islyklarData.mobile, - }) - } else { - await this.islyklarService.updateIslykillSettings(user.nationalId, { - email: input.email && emailVerified ? input.email : islyklarData.email, - mobile: - input.mobilePhoneNumber && mobileVerified - ? input.mobilePhoneNumber - : islyklarData.mobile, - canNudge: input.canNudge ?? islyklarData.canNudge, - bankInfo: input.bankInfo ?? islyklarData.bankInfo, - }) - } - - return updatedUserProfile - } - - async createSmsVerification( - input: CreateSmsVerificationInput, - user: User, - ): Promise { - const createSmsVerificationDto = { nationalId: user.nationalId, ...input } - await this.userProfileApiWithAuth( - user, - ).userProfileControllerCreateSmsVerification({ createSmsVerificationDto }) - } - - async createEmailVerification( - input: CreateEmailVerificationInput, - user: User, - ): Promise { - const createEmailVerificationDto = { nationalId: user.nationalId, ...input } - await this.userProfileApiWithAuth( - user, - ).userProfileControllerCreateEmailVerification({ - createEmailVerificationDto, - }) - } - - async resendEmailVerification(user: User): Promise { - await this.userProfileApiWithAuth( - user, - ).userProfileControllerRecreateVerification({ - nationalId: user.nationalId, - }) - } - - async confirmSms( - input: ConfirmSmsVerificationInput, - user: User, - ): Promise { - const { ...confirmSmsDto } = input - return await this.userProfileApiWithAuth( - user, - ).userProfileControllerConfirmSms({ - nationalId: user.nationalId, - confirmSmsDto, - }) - } - - async confirmEmail( - input: ConfirmEmailVerificationInput, - user: User, - ): Promise { - const { ...confirmEmailDto } = input - return await this.userProfileApiWithAuth( - user, - ).userProfileControllerConfirmEmail({ - nationalId: user.nationalId, - confirmEmailDto, - }) - } -} diff --git a/libs/api/domains/user-profile/src/lib/V2/userProfile.service.ts b/libs/api/domains/user-profile/src/lib/V2/userProfile.service.ts index 7dd68700fe33..5181db290e3e 100644 --- a/libs/api/domains/user-profile/src/lib/V2/userProfile.service.ts +++ b/libs/api/domains/user-profile/src/lib/V2/userProfile.service.ts @@ -1,22 +1,24 @@ -import { BadRequestException, Injectable } from '@nestjs/common' +import { Injectable } from '@nestjs/common' +import { Auth, AuthMiddleware, User } from '@island.is/auth-nest-tools' import { - ConfirmationDtoResponse, - UserProfileControllerFindUserProfileClientTypeEnum, PostNudgeDtoNudgeTypeEnum, + UserProfileControllerFindUserProfileClientTypeEnum, V2MeApi, V2UsersApi, } from '@island.is/clients/user-profile' -import { Auth, AuthMiddleware, User } from '@island.is/auth-nest-tools' -import { IslykillService } from '../islykill.service' -import { UserProfile } from '../userProfile.model' +import { ApolloError } from 'apollo-server-express' +import { AdminUserProfile } from '../adminUserProfile.model' import { ActorProfile, ActorProfileResponse } from '../dto/actorProfile' -import { UpdateUserProfileInput } from '../dto/updateUserProfileInput' -import { CreateSmsVerificationInput } from '../dto/createSmsVerificationInput' import { CreateEmailVerificationInput } from '../dto/createEmalVerificationInput' +import { CreateSmsVerificationInput } from '../dto/createSmsVerificationInput' +import { DeleteIslykillValueInput } from '../dto/deleteIslykillValueInput' import { UpdateActorProfileInput } from '../dto/updateActorProfileInput' -import { AdminUserProfile } from '../adminUserProfile.model' +import { UpdateUserProfileInput } from '../dto/updateUserProfileInput' +import { IslykillService } from '../islykill.service' +import { DeleteIslykillSettings } from '../models/deleteIslykillSettings.model' +import { UserProfile } from '../userProfile.model' @Injectable() export class UserProfileServiceV2 { @@ -134,24 +136,6 @@ export class UserProfileServiceV2 { }) } - async confirmSms(): Promise { - throw new BadRequestException( - 'For User Profile V2 call updateUserProfile instead with mobilePhoneNumberVerificationCode', - ) - } - - async confirmEmail(): Promise { - throw new BadRequestException( - 'For User Profile V2 call updateUserProfile instead with emailVerificationCode', - ) - } - - async resendEmailVerification(): Promise { - throw new BadRequestException( - 'For User Profile V2 call createEmailVerification instead with email again', - ) - } - async getUserProfiles(user: User, search: string) { return this.v2UserProfileApiWithAuth( user, @@ -184,4 +168,26 @@ export class UserProfileServiceV2 { patchUserProfileDto: input, }) } + + async deleteIslykillValue( + input: DeleteIslykillValueInput, + user: User, + ): Promise { + const { nationalId } = await this.updateMeUserProfile( + { + ...(input.email && { email: '' }), + ...(input.mobilePhoneNumber && { mobilePhoneNumber: '' }), + }, + user, + ) + + if (!nationalId) { + throw new ApolloError('Failed to update user profile') + } + + return { + nationalId, + valid: true, + } + } } diff --git a/libs/api/domains/user-profile/src/lib/dto/confirmEmailVerificationInput.ts b/libs/api/domains/user-profile/src/lib/dto/confirmEmailVerificationInput.ts deleted file mode 100644 index a3d812048967..000000000000 --- a/libs/api/domains/user-profile/src/lib/dto/confirmEmailVerificationInput.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Field, InputType } from '@nestjs/graphql' -import { IsString } from 'class-validator' - -@InputType() -export class ConfirmEmailVerificationInput { - @Field(() => String) - @IsString() - hash!: string - - @Field(() => String) - @IsString() - email!: string -} diff --git a/libs/api/domains/user-profile/src/lib/dto/confirmSmsVerificationInput.ts b/libs/api/domains/user-profile/src/lib/dto/confirmSmsVerificationInput.ts deleted file mode 100644 index 6cd072bc950d..000000000000 --- a/libs/api/domains/user-profile/src/lib/dto/confirmSmsVerificationInput.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Field, InputType } from '@nestjs/graphql' -import { IsString } from 'class-validator' - -@InputType() -export class ConfirmSmsVerificationInput { - @Field(() => String) - @IsString() - code!: string - - @Field(() => String) - @IsString() - mobilePhoneNumber!: string -} diff --git a/libs/api/domains/user-profile/src/lib/userProfile.module.ts b/libs/api/domains/user-profile/src/lib/userProfile.module.ts index 973ad3c0f021..81683496ee17 100644 --- a/libs/api/domains/user-profile/src/lib/userProfile.module.ts +++ b/libs/api/domains/user-profile/src/lib/userProfile.module.ts @@ -11,7 +11,6 @@ import { IdentityClientModule } from '@island.is/clients/identity' import { FeatureFlagModule } from '@island.is/nest/feature-flags' import { UserProfileServiceV2 } from './V2/userProfile.service' -import { UserProfileServiceV1 } from './V1/userProfile.service' import { AdminUserProfileResolver } from './adminUserProfile.resolver' import { ActorProfileResolver } from './actorProfile.resolver' @@ -26,7 +25,6 @@ export class UserProfileModule { providers: [ UserProfileService, UserProfileServiceV2, - UserProfileServiceV1, UserProfileResolver, ActorProfileResolver, AdminUserProfileResolver, diff --git a/libs/api/domains/user-profile/src/lib/userProfile.resolver.ts b/libs/api/domains/user-profile/src/lib/userProfile.resolver.ts index 22aa08b390d6..59d978256274 100644 --- a/libs/api/domains/user-profile/src/lib/userProfile.resolver.ts +++ b/libs/api/domains/user-profile/src/lib/userProfile.resolver.ts @@ -1,5 +1,5 @@ -import { Args, Mutation, Query, Resolver } from '@nestjs/graphql' import { UseGuards } from '@nestjs/common' +import { Args, Mutation, Query, Resolver } from '@nestjs/graphql' import type { User } from '@island.is/auth-nest-tools' import { @@ -8,21 +8,19 @@ import { ScopesGuard, } from '@island.is/auth-nest-tools' -import { ConfirmEmailVerificationInput } from './dto/confirmEmailVerificationInput' -import { ConfirmSmsVerificationInput } from './dto/confirmSmsVerificationInput' -import { CreateSmsVerificationInput } from './dto/createSmsVerificationInput' import { CreateEmailVerificationInput } from './dto/createEmalVerificationInput' +import { CreateSmsVerificationInput } from './dto/createSmsVerificationInput' import { CreateUserProfileInput } from './dto/createUserProfileInput' -import { UpdateUserProfileInput } from './dto/updateUserProfileInput' import { DeleteIslykillValueInput } from './dto/deleteIslykillValueInput' -import { UserProfile } from './userProfile.model' -import { ConfirmResponse, Response } from './response.model' -import { DeleteIslykillSettings } from './models/deleteIslykillSettings.model' -import { UserProfileService } from './userProfile.service' -import { UserDeviceToken } from './userDeviceToken.model' -import { UserDeviceTokenInput } from './dto/userDeviceTokenInput' import { DeleteTokenResponse } from './dto/deleteTokenResponse' +import { UpdateUserProfileInput } from './dto/updateUserProfileInput' +import { UserDeviceTokenInput } from './dto/userDeviceTokenInput' +import { DeleteIslykillSettings } from './models/deleteIslykillSettings.model' import { UserProfileLocale } from './models/userProfileLocale.model' +import { Response } from './response.model' +import { UserDeviceToken } from './userDeviceToken.model' +import { UserProfile } from './userProfile.model' +import { UserProfileService } from './userProfile.service' @UseGuards(IdsUserGuard, ScopesGuard) @Resolver() @@ -85,28 +83,6 @@ export class UserProfileResolver { return Promise.resolve({ created: true }) } - @Mutation(() => Response, { nullable: true }) - async resendEmailVerification(@CurrentUser() user: User): Promise { - await this.userProfileService.resendEmailVerification(user) - return Promise.resolve({ created: true }) - } - - @Mutation(() => ConfirmResponse, { nullable: true }) - confirmSmsVerification( - @Args('input') input: ConfirmSmsVerificationInput, - @CurrentUser() user: User, - ): Promise { - return this.userProfileService.confirmSms(input, user) - } - - @Mutation(() => ConfirmResponse, { nullable: true }) - confirmEmailVerification( - @Args('input') input: ConfirmEmailVerificationInput, - @CurrentUser() user: User, - ): Promise { - return this.userProfileService.confirmEmail(input, user) - } - @Mutation(() => UserDeviceToken) addUserProfileDeviceToken( @Args('input') input: UserDeviceTokenInput, diff --git a/libs/api/domains/user-profile/src/lib/userProfile.service.ts b/libs/api/domains/user-profile/src/lib/userProfile.service.ts index 0b51690dfc9c..71947e3eb5a2 100644 --- a/libs/api/domains/user-profile/src/lib/userProfile.service.ts +++ b/libs/api/domains/user-profile/src/lib/userProfile.service.ts @@ -1,52 +1,35 @@ import { Injectable } from '@nestjs/common' import { ApolloError } from 'apollo-server-express' +import { Auth, AuthMiddleware, User } from '@island.is/auth-nest-tools' +import { handle204 } from '@island.is/clients/middlewares' import { ActorLocaleLocaleEnum, - ConfirmationDtoResponse, UserProfileApi, } from '@island.is/clients/user-profile' -import { handle204 } from '@island.is/clients/middlewares' -import { FeatureFlagService, Features } from '@island.is/nest/feature-flags' -import { Auth, AuthMiddleware, User } from '@island.is/auth-nest-tools' -import { DeleteIslykillSettings } from './models/deleteIslykillSettings.model' -import { UpdateUserProfileInput } from './dto/updateUserProfileInput' -import { CreateUserProfileInput } from './dto/createUserProfileInput' -import { CreateSmsVerificationInput } from './dto/createSmsVerificationInput' import { CreateEmailVerificationInput } from './dto/createEmalVerificationInput' -import { ConfirmSmsVerificationInput } from './dto/confirmSmsVerificationInput' -import { ConfirmEmailVerificationInput } from './dto/confirmEmailVerificationInput' +import { CreateSmsVerificationInput } from './dto/createSmsVerificationInput' +import { CreateUserProfileInput } from './dto/createUserProfileInput' import { DeleteIslykillValueInput } from './dto/deleteIslykillValueInput' -import { UserProfile } from './userProfile.model' +import { UpdateActorProfileInput } from './dto/updateActorProfileInput' +import { UpdateUserProfileInput } from './dto/updateUserProfileInput' import { UserDeviceTokenInput } from './dto/userDeviceTokenInput' -import { UserProfileServiceV1 } from './V1/userProfile.service' +import { DeleteIslykillSettings } from './models/deleteIslykillSettings.model' +import { UserProfile } from './userProfile.model' import { UserProfileServiceV2 } from './V2/userProfile.service' -import { UpdateActorProfileInput } from './dto/updateActorProfileInput' @Injectable() export class UserProfileService { constructor( private userProfileApi: UserProfileApi, private userProfileServiceV2: UserProfileServiceV2, - private userProfileServiceV1: UserProfileServiceV1, - private featureFlagService: FeatureFlagService, ) {} userProfileApiWithAuth(auth: Auth) { return this.userProfileApi.withMiddleware(new AuthMiddleware(auth)) } - private async getService(user: User) { - const isV2 = await this.featureFlagService.getValue( - Features.isIASSpaPagesEnabled, - false, - user, - ) - - return isV2 ? this.userProfileServiceV2 : this.userProfileServiceV1 - } - async getUserProfileLocale(user: User) { // This always calls the user profile API V1, which calls the actor API const locale = await handle204( @@ -61,38 +44,6 @@ export class UserProfileService { } } - async getUserProfile(user: User) { - const service = await this.getService(user) - - return service.getUserProfile(user) - } - - async createUserProfile( - input: CreateUserProfileInput, - user: User, - ): Promise { - const service = await this.getService(user) - - return service.createUserProfile(input, user) - } - - async updateMeUserProfile( - input: UpdateUserProfileInput, - user: User, - ): Promise { - const service = await this.getService(user) - - return service.updateMeUserProfile(input, user) - } - - async updateUserProfile( - nationalId: string, - input: UpdateUserProfileInput, - user: User, - ): Promise { - return this.userProfileServiceV2.updateUserProfile(input, user, nationalId) - } - async deleteIslykillValue( input: DeleteIslykillValueInput, user: User, @@ -115,46 +66,44 @@ export class UserProfileService { } } - async createSmsVerification( - input: CreateSmsVerificationInput, - user: User, - ): Promise { - const service = await this.getService(user) - - return service.createSmsVerification(input, user) + async getUserProfile(user: User) { + return this.userProfileServiceV2.getUserProfile(user) } - async createEmailVerification( - input: CreateEmailVerificationInput, + async createUserProfile( + input: CreateUserProfileInput, user: User, - ): Promise { - const service = await this.getService(user) - - return service.createEmailVerification(input, user) + ): Promise { + return this.userProfileServiceV2.createUserProfile(input, user) } - async resendEmailVerification(user: User): Promise { - const service = await this.getService(user) - - return service.resendEmailVerification(user) + async updateMeUserProfile( + input: UpdateUserProfileInput, + user: User, + ): Promise { + return this.userProfileServiceV2.updateMeUserProfile(input, user) } - async confirmSms( - input: ConfirmSmsVerificationInput, + async updateUserProfile( + nationalId: string, + input: UpdateUserProfileInput, user: User, - ): Promise { - const service = await this.getService(user) - - return service.confirmSms(input, user) + ): Promise { + return this.userProfileServiceV2.updateUserProfile(input, user, nationalId) } - async confirmEmail( - input: ConfirmEmailVerificationInput, + async createSmsVerification( + input: CreateSmsVerificationInput, user: User, - ): Promise { - const service = await this.getService(user) + ): Promise { + return this.userProfileServiceV2.createSmsVerification(input, user) + } - return service.confirmEmail(input, user) + async createEmailVerification( + input: CreateEmailVerificationInput, + user: User, + ): Promise { + return this.userProfileServiceV2.createEmailVerification(input, user) } addDeviceToken(input: UserDeviceTokenInput, user: User) { diff --git a/libs/application/core/src/lib/fieldBuilders.ts b/libs/application/core/src/lib/fieldBuilders.ts index b8f46194e70f..3b48a68fb268 100644 --- a/libs/application/core/src/lib/fieldBuilders.ts +++ b/libs/application/core/src/lib/fieldBuilders.ts @@ -21,7 +21,6 @@ import { FormTextArray, KeyValueField, LinkField, - MaybeWithApplicationAndField, MessageWithLinkButtonField, Option, PaymentChargeOverviewField, @@ -586,6 +585,7 @@ export const buildExpandableDescriptionField = ( component: FieldComponents.EXPANDABLE_DESCRIPTION, } } + export const buildAlertMessageField = ( data: Omit, ): AlertMessageField => { @@ -842,7 +842,6 @@ export const buildFieldsRepeaterField = ( ): FieldsRepeaterField => { const { fields, - table, title, titleVariant, formTitle, @@ -861,7 +860,6 @@ export const buildFieldsRepeaterField = ( type: FieldTypes.FIELDS_REPEATER, component: FieldComponents.FIELDS_REPEATER, fields, - table, title, titleVariant, formTitle, @@ -923,9 +921,12 @@ export const buildStaticTableField = ( } export const buildSliderField = ( - data: Omit, + data: Omit, ): SliderField => { const { + id, + title, + titleVariant = 'h2', condition, min = 0, max = 10, @@ -933,28 +934,29 @@ export const buildSliderField = ( snap = true, trackStyle, calculateCellStyle, - showLabel = false, - showMinMaxLabels = false, showRemainderOverlay = true, showProgressOverlay = true, showToolTip = false, label, + showLabel = false, + showMinMaxLabels = false, rangeDates, currentIndex, onChange, onChangeEnd, labelMultiplier = 1, - id, saveAsString, marginTop, marginBottom, } = data return { - title: '', + component: FieldComponents.SLIDER, id, + title, + titleVariant, + condition, children: undefined, type: FieldTypes.SLIDER, - component: FieldComponents.SLIDER, min, max, step, @@ -972,7 +974,6 @@ export const buildSliderField = ( onChange, onChangeEnd, labelMultiplier, - condition, saveAsString, marginTop, marginBottom, diff --git a/libs/application/template-api-modules/src/lib/modules/templates/inheritance-report/utils/mappers.ts b/libs/application/template-api-modules/src/lib/modules/templates/inheritance-report/utils/mappers.ts index b4e0e2ab0f8e..87fbccb3d32e 100644 --- a/libs/application/template-api-modules/src/lib/modules/templates/inheritance-report/utils/mappers.ts +++ b/libs/application/template-api-modules/src/lib/modules/templates/inheritance-report/utils/mappers.ts @@ -157,6 +157,7 @@ export const expandAnswers = ( deceasedShare: gun.deceasedShare ?? '', deceasedShareEnabled: gun.deceasedShareEnabled ?? [], deceasedShareAmount: gun.deceasedShareAmount ?? 0, + enabled: gun.enabled ?? true, } }), total: answers.assets.guns?.total ?? 0, @@ -199,6 +200,7 @@ export const expandAnswers = ( deceasedShare: realEstate.deceasedShare ?? '0', deceasedShareEnabled: realEstate.deceasedShareEnabled ?? [], deceasedShareAmount: realEstate.deceasedShareAmount ?? 0, + enabled: realEstate.enabled ?? true, } }), total: answers.assets.realEstate?.total ?? 0, @@ -227,6 +229,7 @@ export const expandAnswers = ( deceasedShare: vehicle.deceasedShare ?? '0', deceasedShareEnabled: vehicle.deceasedShareEnabled ?? [], deceasedShareAmount: vehicle?.deceasedShareAmount ?? 0, + enabled: vehicle.enabled ?? true, } }), total: answers.assets.vehicles?.total ?? 0, diff --git a/libs/application/templates/accident-notification/src/fields/ApplicationStatus/StatusStep/index.tsx b/libs/application/templates/accident-notification/src/fields/ApplicationStatus/StatusStep/index.tsx index 48e0043813d3..7ee02130ecca 100644 --- a/libs/application/templates/accident-notification/src/fields/ApplicationStatus/StatusStep/index.tsx +++ b/libs/application/templates/accident-notification/src/fields/ApplicationStatus/StatusStep/index.tsx @@ -1,10 +1,29 @@ -import { Box, Button, Tag, Text } from '@island.is/island-ui/core' +import { Box, Button, Tag, TagVariant, Text } from '@island.is/island-ui/core' import { useLocale } from '@island.is/localization' -import React, { FC } from 'react' import * as styles from './ReviewSection.css' -import { ReviewSectionProps } from './types' +import { MessageDescriptor } from 'react-intl' -export const StatusStep: FC> = ({ +export type ActionProps = { + title: string + description: string + fileNames?: string + actionButtonTitle: string + hasActionButtonIcon?: boolean + showAlways?: boolean + cta?: () => void +} + +type Props = { + title: string + description: string + hasActionMessage: boolean + action?: ActionProps + visible?: boolean + tagText: MessageDescriptor | string + tagVariant: TagVariant +} + +export const StatusStep = ({ title, description, tagVariant = 'blue', @@ -12,7 +31,7 @@ export const StatusStep: FC> = ({ hasActionMessage, action, visible = true, -}) => { +}: Props) => { const { formatMessage } = useLocale() const handleOnCTAButtonClick = () => { action?.cta && action.cta() diff --git a/libs/application/templates/accident-notification/src/fields/ApplicationStatus/StatusStep/types.ts b/libs/application/templates/accident-notification/src/fields/ApplicationStatus/StatusStep/types.ts index 195eba30c7ee..96926fa90211 100644 --- a/libs/application/templates/accident-notification/src/fields/ApplicationStatus/StatusStep/types.ts +++ b/libs/application/templates/accident-notification/src/fields/ApplicationStatus/StatusStep/types.ts @@ -1,16 +1,3 @@ -import { TagVariant } from '@island.is/island-ui/core' -import { MessageDescriptor } from '@formatjs/intl' - -export interface ActionProps { - title: string - description: string - fileNames?: string - actionButtonTitle: string - hasActionButtonIcon?: boolean - showAlways?: boolean - cta?: () => void -} - export enum AccidentNotificationStatusEnum { ACCEPTED = 'ACCEPTED', REFUSED = 'REFUSED', @@ -18,26 +5,6 @@ export enum AccidentNotificationStatusEnum { INPROGRESSWAITINGFORDOCUMENT = 'INPROGRESSWAITINGFORDOCUMENT', } -export interface ReviewSectionProps { - title: string - description: string - hasActionMessage: boolean - action?: ActionProps - visible?: boolean - tagText: MessageDescriptor | string - tagVariant: TagVariant -} - -export interface Steps { - title: string - description: string - hasActionMessage: boolean - action?: ActionProps - visible?: boolean - tagText: MessageDescriptor | string - tagVariant: TagVariant -} - export interface SubmittedApplicationData { data?: { documentId: string diff --git a/libs/application/templates/accident-notification/src/fields/ApplicationStatus/applicationStatusUtils.ts b/libs/application/templates/accident-notification/src/fields/ApplicationStatus/applicationStatusUtils.ts index a74efdac944b..da17ae9b693d 100644 --- a/libs/application/templates/accident-notification/src/fields/ApplicationStatus/applicationStatusUtils.ts +++ b/libs/application/templates/accident-notification/src/fields/ApplicationStatus/applicationStatusUtils.ts @@ -1,16 +1,33 @@ -import { FormatMessage, FormValue } from '@island.is/application/types' +import { + FormatMessage, + FormValue, + TagVariant, +} from '@island.is/application/types' import { inReview } from '../../lib/messages' -import { AccidentNotificationStatusEnum, Steps } from './StatusStep/types' +import { AccidentNotificationStatusEnum } from './StatusStep/types' import { getValueViaPath } from '@island.is/application/core' import { ReviewApprovalEnum } from '../../types' import { AccidentNotificationStatus } from '@island.is/api/schema' import { - hasReceivedAllDocuments, + hasReceivedConfirmation, isInjuredAndRepresentativeOfCompanyOrInstitute, shouldRequestReview, -} from '../../utils' -import { hasReceivedConfirmation } from '../../utils/hasReceivedConfirmation' +} from '../../utils/miscUtils' + import { AccidentNotificationAnswers } from '../..' +import { MessageDescriptor } from 'react-intl' +import { ActionProps } from './StatusStep' +import { hasReceivedAllDocuments } from '../../utils/documentUtils' + +type Steps = { + title: string + description: string + hasActionMessage: boolean + action?: ActionProps + visible?: boolean + tagText: MessageDescriptor | string + tagVariant: TagVariant +} export const tagMapperApplicationStatus = { [AccidentNotificationStatusEnum.ACCEPTED]: { diff --git a/libs/application/templates/accident-notification/src/fields/ApplicationStatus/index.tsx b/libs/application/templates/accident-notification/src/fields/ApplicationStatus/index.tsx index 0055324cd571..36aec0ba358a 100644 --- a/libs/application/templates/accident-notification/src/fields/ApplicationStatus/index.tsx +++ b/libs/application/templates/accident-notification/src/fields/ApplicationStatus/index.tsx @@ -15,13 +15,11 @@ import { useFormContext } from 'react-hook-form' import { getAccidentStatusQuery } from '../../hooks/useLazyStatusOfNotification' import { inReview } from '../../lib/messages' import { ReviewApprovalEnum, SubmittedApplicationData } from '../../types' -import { - getErrorMessageForMissingDocuments, - isUniqueAssignee, -} from '../../utils' +import { isUniqueAssignee } from '../../utils/miscUtils' import { StatusStep } from './StatusStep' import { ApplicationStatusProps } from './StatusStep/types' import { getStatusAndApproval, getSteps } from './applicationStatusUtils' +import { getErrorMessageForMissingDocuments } from '../../utils/documentUtils' export const ApplicationStatus = ({ goToScreen, diff --git a/libs/application/templates/accident-notification/src/fields/DateOfAccident/index.tsx b/libs/application/templates/accident-notification/src/fields/DateOfAccident/index.tsx index 27627c0f8f2b..e51b848862e3 100644 --- a/libs/application/templates/accident-notification/src/fields/DateOfAccident/index.tsx +++ b/libs/application/templates/accident-notification/src/fields/DateOfAccident/index.tsx @@ -1,16 +1,15 @@ import { IsHealthInsuredInput } from '@island.is/api/schema' -import { FieldBaseProps } from '@island.is/application/types' +import { FieldBaseProps, NO, YES } from '@island.is/application/types' import { Box, Input } from '@island.is/island-ui/core' import { useLocale } from '@island.is/localization' import { DatePickerController } from '@island.is/shared/form-fields' -import React, { FC, useCallback, useEffect, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' import { Controller, useFormContext } from 'react-hook-form' -import { NO, YES } from '../../utils/constants' import { useLazyIsHealthInsured } from '../../hooks/useLazyIsHealthInsured' import { AccidentNotification } from '../../lib/dataSchema' import { accidentDetails } from '../../lib/messages' -export const DateOfAccident: FC> = ({ +export const DateOfAccident = ({ application, field, error, diff --git a/libs/application/templates/accident-notification/src/fields/FormOverview/ValueLine.tsx b/libs/application/templates/accident-notification/src/fields/FormOverview/ValueLine.tsx index 6e2101294be2..5c2fd31ef800 100644 --- a/libs/application/templates/accident-notification/src/fields/FormOverview/ValueLine.tsx +++ b/libs/application/templates/accident-notification/src/fields/FormOverview/ValueLine.tsx @@ -1,22 +1,17 @@ import { Box, Bullet, BulletList, Text } from '@island.is/island-ui/core' import { Colors } from '@island.is/island-ui/theme' import { useLocale } from '@island.is/localization' -import React, { FC } from 'react' import { MessageDescriptor } from 'react-intl' import * as styles from './FormOverview.css' import cn from 'classnames' -interface ValueLineProps { +type ValueLineProps = { label: string | MessageDescriptor value: string | MessageDescriptor color?: Colors } -export const ValueLine: FC> = ({ - label, - value, - color, -}) => { +export const ValueLine = ({ label, value, color }: ValueLineProps) => { const { formatMessage } = useLocale() return ( @@ -32,10 +27,7 @@ interface FileValueLineProps { files: MessageDescriptor[] | undefined } -export const FileValueLine: FC> = ({ - label, - files, -}) => { +export const FileValueLine = ({ label, files }: FileValueLineProps) => { const { formatMessage } = useLocale() return ( diff --git a/libs/application/templates/accident-notification/src/fields/FormOverview/index.tsx b/libs/application/templates/accident-notification/src/fields/FormOverview/index.tsx index 3eb21101ca94..07eb4ffa93b4 100644 --- a/libs/application/templates/accident-notification/src/fields/FormOverview/index.tsx +++ b/libs/application/templates/accident-notification/src/fields/FormOverview/index.tsx @@ -1,5 +1,5 @@ import { formatText } from '@island.is/application/core' -import { FieldBaseProps, FormValue } from '@island.is/application/types' +import { FieldBaseProps, FormValue, YES } from '@island.is/application/types' import { formatPhoneNumber, ReviewGroup, @@ -17,8 +17,7 @@ import format from 'date-fns/format' import is from 'date-fns/locale/is' import parseISO from 'date-fns/parseISO' import kennitala from 'kennitala' -import React, { FC } from 'react' -import { States, YES } from '../../utils/constants' +import { States } from '../../utils/constants' import { AccidentNotification } from '../../lib/dataSchema' import { accidentDetails, @@ -38,24 +37,30 @@ import { sportsClubInfo, workMachine, } from '../../lib/messages' +import * as styles from './FormOverview.css' +import { FileValueLine, ValueLine } from './ValueLine' import { getAttachmentTitles, + returnMissingDocumentsList, +} from '../../utils/documentUtils' +import { getWorkplaceData, - hideLocationAndPurpose, isAgricultureAccident, isFishermanAccident, isGeneralWorkplaceAccident, - isHomeActivitiesAccident, isMachineRelatedAccident, isProfessionalAthleteAccident, +} from '../../utils/occupationUtils' +import { isReportingOnBehalfOfChild, isReportingOnBehalfOfEmployee, isReportingOnBehalfOfInjured, +} from '../../utils/reportingUtils' +import { hideLocationAndPurpose } from '../../utils/miscUtils' +import { + isHomeActivitiesAccident, isWorkAccident, - returnMissingDocumentsList, -} from '../../utils' -import * as styles from './FormOverview.css' -import { FileValueLine, ValueLine } from './ValueLine' +} from '../../utils/accidentUtils' interface SubmittedApplicationData { data?: { @@ -63,7 +68,7 @@ interface SubmittedApplicationData { } } -interface FormOverviewProps { +type Props = { field: { props: { isAssignee: boolean @@ -71,9 +76,11 @@ interface FormOverviewProps { } } -export const FormOverview: FC< - React.PropsWithChildren -> = ({ application, goToScreen, field }) => { +export const FormOverview = ({ + application, + goToScreen, + field, +}: FieldBaseProps & Props) => { const isAssignee = field?.props?.isAssignee || false const answers = application.answers as AccidentNotification const { formatMessage } = useLocale() diff --git a/libs/application/templates/accident-notification/src/fields/FormOverviewInReview/ConfirmationModal.tsx b/libs/application/templates/accident-notification/src/fields/FormOverviewInReview/ConfirmationModal.tsx index 237d074014ff..46c2ccca0fcb 100644 --- a/libs/application/templates/accident-notification/src/fields/FormOverviewInReview/ConfirmationModal.tsx +++ b/libs/application/templates/accident-notification/src/fields/FormOverviewInReview/ConfirmationModal.tsx @@ -3,11 +3,10 @@ import { Application, DefaultEvents } from '@island.is/application/types' import { SUBMIT_APPLICATION } from '@island.is/application/graphql' import { Box, Button, Icon, ModalBase, Text } from '@island.is/island-ui/core' import { useLocale } from '@island.is/localization' -import React, { FC } from 'react' import { inReview } from '../../lib/messages' import * as styles from './ConfirmationModal.css' -type ConfirmationModalProps = { +type Props = { visibility: boolean setVisibility: (visibility: boolean) => void title: string @@ -20,9 +19,7 @@ type ConfirmationModalProps = { refetch?: () => void } -export const ConfirmationModal: FC< - React.PropsWithChildren -> = ({ +export const ConfirmationModal = ({ visibility, setVisibility, title, @@ -33,7 +30,7 @@ export const ConfirmationModal: FC< application, comment = '', refetch, -}) => { +}: Props) => { const { formatMessage } = useLocale() const [submitApplication, { loading: loadingSubmit }] = useMutation( SUBMIT_APPLICATION, @@ -68,6 +65,7 @@ export const ConfirmationModal: FC< const closeModal = () => { setVisibility(false) } + return ( -> = ({ application, field, refetch, goToScreen }) => { +export const FormOverviewInReview = ({ + application, + field, + refetch, + goToScreen, +}: Props & FieldBaseProps) => { const isAssignee = field?.props?.isAssignee || false const { formatMessage } = useLocale() const reviewApproval = getValueViaPath( diff --git a/libs/application/templates/accident-notification/src/fields/HiddenInformation/index.tsx b/libs/application/templates/accident-notification/src/fields/HiddenInformation/index.tsx index 337319b0c86e..a8e458076fd0 100644 --- a/libs/application/templates/accident-notification/src/fields/HiddenInformation/index.tsx +++ b/libs/application/templates/accident-notification/src/fields/HiddenInformation/index.tsx @@ -1,10 +1,9 @@ import { FieldBaseProps } from '@island.is/application/types' -import React, { FC } from 'react' import { useFormContext } from 'react-hook-form' import { AccidentNotification } from '../../lib/dataSchema' -import { isInjuredAndRepresentativeOfCompanyOrInstitute } from '../../utils' +import { isInjuredAndRepresentativeOfCompanyOrInstitute } from '../../utils/miscUtils' -interface HiddenInformationProps { +type Props = { field: { props: { id: string @@ -12,9 +11,10 @@ interface HiddenInformationProps { } } -export const HiddenInformation: FC< - React.PropsWithChildren -> = ({ application, field }) => { +export const HiddenInformation = ({ + application, + field, +}: Props & FieldBaseProps) => { const { register, setValue } = useFormContext() const { id } = field.props diff --git a/libs/application/templates/accident-notification/src/fields/ProxyDocument/index.tsx b/libs/application/templates/accident-notification/src/fields/ProxyDocument/index.tsx index 76d7906957b0..51895095fb08 100644 --- a/libs/application/templates/accident-notification/src/fields/ProxyDocument/index.tsx +++ b/libs/application/templates/accident-notification/src/fields/ProxyDocument/index.tsx @@ -1,12 +1,8 @@ -import { FieldBaseProps } from '@island.is/application/types' import { Box, Button, Inline, Text } from '@island.is/island-ui/core' import { useLocale } from '@island.is/localization' -import React, { FC } from 'react' import { powerOfAttorney } from '../../lib/messages' -export const ProxyDocument: FC< - React.PropsWithChildren -> = () => { +export const ProxyDocument = () => { const { formatMessage } = useLocale() return ( diff --git a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/accidentDetailSubSection.ts b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/accidentDetailSubSection.ts index bb8693200540..ce37b82a5b37 100644 --- a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/accidentDetailSubSection.ts +++ b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/accidentDetailSubSection.ts @@ -8,8 +8,9 @@ import { buildTextField, } from '@island.is/application/core' import { accidentDetails } from '../../../lib/messages' -import { isDateOlderThanAYear, isHomeActivitiesAccident } from '../../../utils' -import { isHealthInsured } from '../../../utils/isHealthInsured' +import { isDateOlderThanAYear } from '../../../utils/dateUtils' +import { isHealthInsured } from '../../../utils/miscUtils' +import { isHomeActivitiesAccident } from '../../../utils/accidentUtils' // Details of the accident export const accidentDetailsSubSection = buildSubSection({ diff --git a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/accidentTypeSubSection.ts b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/accidentTypeSubSection.ts index 387eb21ad6c9..8a81fb15fdf7 100644 --- a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/accidentTypeSubSection.ts +++ b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/accidentTypeSubSection.ts @@ -1,6 +1,6 @@ import { buildRadioField, buildSubSection } from '@island.is/application/core' import { accidentType } from '../../../lib/messages' -import { getAccidentTypeOptions } from '../../../utils' +import { getAccidentTypeOptions } from '../../../utils/getOptions' export const accidentTypeSubSection = buildSubSection({ id: 'accidentType.section', diff --git a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/attachmentsSubSection.ts b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/attachmentsSubSection.ts index e8c71b90e331..9b239dc5d8b0 100644 --- a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/attachmentsSubSection.ts +++ b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/attachmentsSubSection.ts @@ -15,18 +15,12 @@ import { fatalAccidentAttachment, injuredPersonInformation, } from '../../../lib/messages' -import { - isFatalAccident, - isReportingOnBehalfOfInjured, - isRepresentativeOfCompanyOrInstitute, -} from '../../../utils' +import { isRepresentativeOfCompanyOrInstitute } from '../../../utils/miscUtils' import { AttachmentsEnum } from '../../../types' -import { - FILE_SIZE_LIMIT, - NO, - UPLOAD_ACCEPT, - YES, -} from '../../../utils/constants' +import { FILE_SIZE_LIMIT, UPLOAD_ACCEPT } from '../../../utils/constants' +import { isReportingOnBehalfOfInjured } from '../../../utils/reportingUtils' +import { NO, YES } from '@island.is/application/types' +import { isFatalAccident } from '../../../utils/accidentUtils' // Injury certificate and fatal accident section export const attachmentsSubSection = buildSubSection({ diff --git a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/companyInfoSubSection.ts b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/companyInfoSubSection.ts index 05cc9f837316..e9d181e3a167 100644 --- a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/companyInfoSubSection.ts +++ b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/companyInfoSubSection.ts @@ -9,11 +9,11 @@ import { companyInfo, representativeInfo } from '../../../lib/messages' import { isAgricultureAccident, isGeneralWorkplaceAccident, - isHomeActivitiesAccident, - isInjuredAndRepresentativeOfCompanyOrInstitute, isInternshipStudiesAccident, - isReportingOnBehalfOfEmployee, -} from '../../../utils' +} from '../../../utils/occupationUtils' +import { isReportingOnBehalfOfEmployee } from '../../../utils/reportingUtils' +import { isHomeActivitiesAccident } from '../../../utils/accidentUtils' +import { isInjuredAndRepresentativeOfCompanyOrInstitute } from '../../../utils/miscUtils' // Company information if work accident without the injured being a fisherman or in agriculture export const companyInfoSubSection = buildSubSection({ diff --git a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/fishingCompanyInfoSubSection.ts b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/fishingCompanyInfoSubSection.ts index 03efd89ffdaf..d009af8a3b5c 100644 --- a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/fishingCompanyInfoSubSection.ts +++ b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/fishingCompanyInfoSubSection.ts @@ -5,12 +5,10 @@ import { buildSubSection, buildTextField, } from '@island.is/application/core' -import { - isFishermanAccident, - isInjuredAndRepresentativeOfCompanyOrInstitute, - isReportingOnBehalfOfEmployee, -} from '../../../utils' +import { isInjuredAndRepresentativeOfCompanyOrInstitute } from '../../../utils/miscUtils' import { fishingCompanyInfo, representativeInfo } from '../../../lib/messages' +import { isReportingOnBehalfOfEmployee } from '../../../utils/reportingUtils' +import { isFishermanAccident } from '../../../utils/occupationUtils' // fishery information if fisherman export const fishingCompanyInfoSubSection = buildSubSection({ diff --git a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/locationSubSection.ts b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/locationSubSection.ts index be855a49a6f0..1afcdb3d604c 100644 --- a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/locationSubSection.ts +++ b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/locationSubSection.ts @@ -11,19 +11,6 @@ import { locationAndPurpose, sportsClubInfo, } from '../../../lib/messages' -import { - hideLocationAndPurpose, - isAgricultureAccident, - isFishermanAccident, - isGeneralWorkplaceAccident, - isHomeActivitiesAccident, - isInternshipStudiesAccident, - isProfessionalAthleteAccident, - isRescueWorkAccident, - isStudiesAccident, -} from '../../../utils' -import { NO, YES } from '../../../utils/constants' -import { isSportAccidentAndEmployee } from '../../../utils/isSportAccidentAndEmployee' import { AgricultureAccidentLocationEnum, FishermanWorkplaceAccidentLocationEnum, @@ -33,6 +20,21 @@ import { RescueWorkAccidentLocationEnum, StudiesAccidentLocationEnum, } from '../../../types' +import { + isAgricultureAccident, + isFishermanAccident, + isGeneralWorkplaceAccident, + isInternshipStudiesAccident, + isProfessionalAthleteAccident, + isSportAccidentAndEmployee, +} from '../../../utils/occupationUtils' +import { NO, YES } from '@island.is/application/types' +import { + isHomeActivitiesAccident, + isRescueWorkAccident, + isStudiesAccident, +} from '../../../utils/accidentUtils' +import { hideLocationAndPurpose } from '../../../utils/miscUtils' // Location Subsection export const locationSubSection = buildSubSection({ diff --git a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/rescueSquadInfoSubSection.ts b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/rescueSquadInfoSubSection.ts index 640aa635f36b..55960413bba6 100644 --- a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/rescueSquadInfoSubSection.ts +++ b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/rescueSquadInfoSubSection.ts @@ -6,11 +6,9 @@ import { buildTextField, } from '@island.is/application/core' import { representativeInfo, rescueSquadInfo } from '../../../lib/messages' -import { - isInjuredAndRepresentativeOfCompanyOrInstitute, - isReportingOnBehalfOfEmployee, - isRescueWorkAccident, -} from '../../../utils' +import { isInjuredAndRepresentativeOfCompanyOrInstitute } from '../../../utils/miscUtils' +import { isRescueWorkAccident } from '../../../utils/accidentUtils' +import { isReportingOnBehalfOfEmployee } from '../../../utils/reportingUtils' // Rescue squad information when accident is related to rescue squad export const rescueSquadInfoSubSection = buildSubSection({ diff --git a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/schoolInfoSubSection.ts b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/schoolInfoSubSection.ts index 148f9ec35872..8f26c1c56fef 100644 --- a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/schoolInfoSubSection.ts +++ b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/schoolInfoSubSection.ts @@ -6,12 +6,10 @@ import { buildTextField, } from '@island.is/application/core' import { representativeInfo, schoolInfo } from '../../../lib/messages' -import { - isInjuredAndRepresentativeOfCompanyOrInstitute, - isInternshipStudiesAccident, - isReportingOnBehalfOfEmployee, - isStudiesAccident, -} from '../../../utils' +import { isStudiesAccident } from '../../../utils/accidentUtils' +import { isInternshipStudiesAccident } from '../../../utils/occupationUtils' +import { isReportingOnBehalfOfEmployee } from '../../../utils/reportingUtils' +import { isInjuredAndRepresentativeOfCompanyOrInstitute } from '../../../utils/miscUtils' // School information if school accident export const schoolInfoSubSection = buildSubSection({ diff --git a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/sportsClubInfoSubSection.ts b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/sportsClubInfoSubSection.ts index 7914ecbe0158..8410b242d596 100644 --- a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/sportsClubInfoSubSection.ts +++ b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/sportsClubInfoSubSection.ts @@ -5,12 +5,10 @@ import { buildSubSection, buildTextField, } from '@island.is/application/core' -import { - isInjuredAndRepresentativeOfCompanyOrInstitute, - isProfessionalAthleteAccident, - isReportingOnBehalfOfEmployee, -} from '../../../utils' +import { isInjuredAndRepresentativeOfCompanyOrInstitute } from '../../../utils/miscUtils' import { representativeInfo, sportsClubInfo } from '../../../lib/messages' +import { isProfessionalAthleteAccident } from '../../../utils/occupationUtils' +import { isReportingOnBehalfOfEmployee } from '../../../utils/reportingUtils' // Sports club information when the injured has a sports related accident export const sportsClubInfoSubSection = buildSubSection({ diff --git a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/studiesAccidentSubSection.ts b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/studiesAccidentSubSection.ts index 87bead5e5d5c..19346e794c61 100644 --- a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/studiesAccidentSubSection.ts +++ b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/studiesAccidentSubSection.ts @@ -4,8 +4,8 @@ import { buildSubSection, } from '@island.is/application/core' import { accidentType } from '../../../lib/messages' -import { isStudiesAccident } from '../../../utils' import { StudiesAccidentTypeEnum } from '../../../types' +import { isStudiesAccident } from '../../../utils/accidentUtils' export const studiesAccidentSubSection = buildSubSection({ id: 'studiesAccident.subSection', diff --git a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/workAccidentSubSection.ts b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/workAccidentSubSection.ts index db49a860e014..1bb5b00d63ac 100644 --- a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/workAccidentSubSection.ts +++ b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/workAccidentSubSection.ts @@ -11,12 +11,10 @@ import { attachments, injuredPersonInformation, } from '../../../lib/messages' -import { - isAgricultureAccident, - isReportingOnBehalfSelf, - isWorkAccident, -} from '../../../utils' import { WorkAccidentTypeEnum } from '../../../types' +import { isWorkAccident } from '../../../utils/accidentUtils' +import { isAgricultureAccident } from '../../../utils/occupationUtils' +import { isReportingOnBehalfSelf } from '../../../utils/reportingUtils' export const workAccidentSubSection = buildSubSection({ id: 'workAccident.subSection', diff --git a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/workMachineSubSection.ts b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/workMachineSubSection.ts index bdfa59c43e79..0f5a41785262 100644 --- a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/workMachineSubSection.ts +++ b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/aboutTheAccidentSection/workMachineSubSection.ts @@ -8,9 +8,9 @@ import { application, workMachine } from '../../../lib/messages' import { isAgricultureAccident, isGeneralWorkplaceAccident, -} from '../../../utils' -import { isSportAccidentAndEmployee } from '../../../utils/isSportAccidentAndEmployee' -import { NO, YES } from '../../../utils/constants' + isSportAccidentAndEmployee, +} from '../../../utils/occupationUtils' +import { NO, YES } from '@island.is/application/types' // Workmachine information only applicable to generic workplace accidents export const workMachineSubSection = buildSubSection({ diff --git a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/applicantInformationSection.ts b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/applicantInformationSection.ts index f91bcfaaee43..0c948df0a9f0 100644 --- a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/applicantInformationSection.ts +++ b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/applicantInformationSection.ts @@ -1,6 +1,7 @@ import { buildSection } from '@island.is/application/core' import { applicantInformation } from '../../lib/messages' import { applicantInformationMultiField } from '@island.is/application/ui-forms' + export const applicantInformationSection = buildSection({ id: 'informationAboutApplicantSection', title: applicantInformation.general.title, diff --git a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/whoIsTheNotificationForSection/childInCustodySubSection.ts b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/whoIsTheNotificationForSection/childInCustodySubSection.ts index 9587578fe81c..a826034c5773 100644 --- a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/whoIsTheNotificationForSection/childInCustodySubSection.ts +++ b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/whoIsTheNotificationForSection/childInCustodySubSection.ts @@ -4,7 +4,7 @@ import { buildTextField, } from '@island.is/application/core' import { childInCustody } from '../../../lib/messages' -import { isReportingOnBehalfOfChild } from '../../../utils' +import { isReportingOnBehalfOfChild } from '../../../utils/reportingUtils' export const childInCustodySubSection = buildSubSection({ id: 'childInCustody.section', diff --git a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/whoIsTheNotificationForSection/injuredPersonInformationSubSection.ts b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/whoIsTheNotificationForSection/injuredPersonInformationSubSection.ts index 643cc77ad62d..0b5acc8a391a 100644 --- a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/whoIsTheNotificationForSection/injuredPersonInformationSubSection.ts +++ b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/whoIsTheNotificationForSection/injuredPersonInformationSubSection.ts @@ -8,7 +8,7 @@ import { injuredPersonInformation } from '../../../lib/messages' import { isReportingOnBehalfOfEmployee, isReportingOnBehalfOfInjured, -} from '../../../utils' +} from '../../../utils/reportingUtils' export const injuredPersonInformationSubSection = buildSubSection({ id: 'injuredPersonInformation.section', diff --git a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/whoIsTheNotificationForSection/juridicialPersonCompanySubSection.ts b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/whoIsTheNotificationForSection/juridicialPersonCompanySubSection.ts index 5d747453c1d0..ea81a695800d 100644 --- a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/whoIsTheNotificationForSection/juridicialPersonCompanySubSection.ts +++ b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/whoIsTheNotificationForSection/juridicialPersonCompanySubSection.ts @@ -5,8 +5,8 @@ import { buildTextField, } from '@island.is/application/core' import { juridicalPerson } from '../../../lib/messages' -import { isReportingOnBehalfOfEmployee } from '../../../utils' -import { YES } from '../../../utils/constants' +import { isReportingOnBehalfOfEmployee } from '../../../utils/reportingUtils' +import { YES } from '@island.is/application/types' export const juridicalPersonCompanySubSection = buildSubSection({ id: 'juridicalPerson.company', diff --git a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/whoIsTheNotificationForSection/powerOfAttorneySubSection.ts b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/whoIsTheNotificationForSection/powerOfAttorneySubSection.ts index eb16cefeec0f..7556f6c32cbc 100644 --- a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/whoIsTheNotificationForSection/powerOfAttorneySubSection.ts +++ b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/whoIsTheNotificationForSection/powerOfAttorneySubSection.ts @@ -7,7 +7,7 @@ import { } from '@island.is/application/core' import { powerOfAttorney } from '../../../lib/messages' import { PowerOfAttorneyUploadEnum } from '../../../types' -import { isPowerOfAttorney } from '../../../utils' +import { isPowerOfAttorney } from '../../../utils/miscUtils' export const powerOfAttorneySubSection = buildSubSection({ id: 'powerOfAttorney.type.section', diff --git a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/whoIsTheNotificationForSection/powerOfAttorneyUploadSubSection.ts b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/whoIsTheNotificationForSection/powerOfAttorneyUploadSubSection.ts index 00ec8aff756d..437a4072b541 100644 --- a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/whoIsTheNotificationForSection/powerOfAttorneyUploadSubSection.ts +++ b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/whoIsTheNotificationForSection/powerOfAttorneyUploadSubSection.ts @@ -6,11 +6,12 @@ import { } from '@island.is/application/core' import { error, powerOfAttorney } from '../../../lib/messages' import { FILE_SIZE_LIMIT, UPLOAD_ACCEPT } from '../../../utils/constants' -import { isUploadNow } from '../../../utils/isUploadNow' +import { isUploadNow } from '../../../utils/documentUtils' export const powerOfAttorneyUploadSubSection = buildSubSection({ id: 'powerOfAttorney.upload.section', title: powerOfAttorney.upload.sectionTitle, + condition: (formValue) => isUploadNow(formValue), children: [ buildMultiField({ id: 'powerOfAttorney', @@ -37,5 +38,4 @@ export const powerOfAttorneyUploadSubSection = buildSubSection({ ], }), ], - condition: (formValue) => isUploadNow(formValue), }) diff --git a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/whoIsTheNotificationForSection/whoIsTheNotificationForMultiField.ts b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/whoIsTheNotificationForSection/whoIsTheNotificationForMultiField.ts index d59f1f345a83..96665ccc5d98 100644 --- a/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/whoIsTheNotificationForSection/whoIsTheNotificationForMultiField.ts +++ b/libs/application/templates/accident-notification/src/forms/AccidentNotificationForm/whoIsTheNotificationForSection/whoIsTheNotificationForMultiField.ts @@ -3,7 +3,7 @@ import { whoIsTheNotificationFor } from '../../../lib/messages' import { whoIsTheNotificationForOptions, whoIsTheNotificationForProcureOptions, -} from '../../../utils/getWhoIstheNotificationForOptions' +} from '../../../utils/getOptions' export const whoIsTheNotificationForMultiField = buildMultiField({ id: 'whoIsTheNotificationFor', diff --git a/libs/application/templates/accident-notification/src/forms/InReviewForm/addAttachmentsSection.ts b/libs/application/templates/accident-notification/src/forms/InReviewForm/addAttachmentsSection.ts index f0c88a39c67a..a53e2afcabfc 100644 --- a/libs/application/templates/accident-notification/src/forms/InReviewForm/addAttachmentsSection.ts +++ b/libs/application/templates/accident-notification/src/forms/InReviewForm/addAttachmentsSection.ts @@ -13,11 +13,10 @@ import { hasReceivedInjuryCertificate, hasReceivedPoliceReport, hasReceivedProxyDocument, - isFatalAccident, - isPowerOfAttorney, - isReportingOnBehalfOfInjured, - isUniqueAssignee, -} from '../../utils' +} from '../../utils/documentUtils' +import { isPowerOfAttorney, isUniqueAssignee } from '../../utils/miscUtils' +import { isReportingOnBehalfOfInjured } from '../../utils/reportingUtils' +import { isFatalAccident } from '../../utils/accidentUtils' export const addAttachmentsSection = (isAssignee?: boolean) => buildSection({ diff --git a/libs/application/templates/accident-notification/src/index.ts b/libs/application/templates/accident-notification/src/index.ts index 512077174291..2d4f5c15801b 100644 --- a/libs/application/templates/accident-notification/src/index.ts +++ b/libs/application/templates/accident-notification/src/index.ts @@ -2,7 +2,7 @@ import AccidentNotificationTemplate from './lib/AccidentNotificationTemplate' import { AccidentNotification } from './lib/dataSchema' import * as appMessages from './lib/messages' import { OnBehalf } from './types' -import * as appUtils from './utils' +import * as appUtils from './utils/miscUtils' export const getFields = () => import('./fields') diff --git a/libs/application/templates/accident-notification/src/lib/dataSchema.ts b/libs/application/templates/accident-notification/src/lib/dataSchema.ts index afb53946fe3b..3ba93cdc8e15 100644 --- a/libs/application/templates/accident-notification/src/lib/dataSchema.ts +++ b/libs/application/templates/accident-notification/src/lib/dataSchema.ts @@ -1,7 +1,6 @@ import { applicantInformationSchema } from '@island.is/application/ui-forms' import * as kennitala from 'kennitala' import { z } from 'zod' -import { YES } from '../utils/constants' import { AccidentTypeEnum, AgricultureAccidentLocationEnum, @@ -19,10 +18,11 @@ import { ReviewApprovalEnum, OnBehalf, Status, - ChoiceEnum, + YesOrNo, } from '../types' -import { isValid24HFormatTime } from '../utils' import { error } from './messages/error' +import { isValid24HFormatTime } from '../utils/dateUtils' +import { YES } from '@island.is/application/types' const FileSchema = z.object({ name: z.string(), @@ -99,7 +99,7 @@ const accidentDetails = z.object({ dateOfAccident: z.string().refine((x) => x.trim().length > 0, { params: error.invalidValue, }), - isHealthInsured: z.nativeEnum(ChoiceEnum).optional(), + isHealthInsured: z.nativeEnum(YesOrNo).optional(), timeOfAccident: z .string() .refine((x) => (x ? isValid24HFormatTime(x) : false), { @@ -193,8 +193,8 @@ export const AccidentNotificationSchema = z.object({ info: z.object({ onBehalf: z.nativeEnum(OnBehalf), }), - timePassedHindrance: z.nativeEnum(ChoiceEnum), - carAccidentHindrance: z.nativeEnum(ChoiceEnum), + timePassedHindrance: z.nativeEnum(YesOrNo), + carAccidentHindrance: z.nativeEnum(YesOrNo), applicant: applicantInformationSchema(), whoIsTheNotificationFor: z.object({ answer: z.nativeEnum(WhoIsTheNotificationForEnum), @@ -213,13 +213,13 @@ export const AccidentNotificationSchema = z.object({ ]), }), attachments, - wasTheAccidentFatal: z.nativeEnum(ChoiceEnum), - fatalAccidentUploadDeathCertificateNow: z.nativeEnum(ChoiceEnum), + wasTheAccidentFatal: z.nativeEnum(YesOrNo), + fatalAccidentUploadDeathCertificateNow: z.nativeEnum(YesOrNo), accidentDetails, isRepresentativeOfCompanyOrInstitue: z.array(z.string()).optional(), fishingShipInfo, onPayRoll: z.object({ - answer: z.nativeEnum(ChoiceEnum), + answer: z.nativeEnum(YesOrNo), }), locationAndPurpose: z.object({ location: z.string().refine((x) => x.trim().length > 0, { @@ -240,7 +240,7 @@ export const AccidentNotificationSchema = z.object({ shipLocation: z.object({ answer: z.nativeEnum(FishermanWorkplaceAccidentShipLocationEnum), }), - workMachineRadio: z.nativeEnum(ChoiceEnum), + workMachineRadio: z.nativeEnum(YesOrNo), workMachine: z.object({ descriptionOfMachine: z.string().refine((x) => x.trim().length > 0, { params: error.invalidValue, diff --git a/libs/application/templates/accident-notification/src/lib/messages/externalData.ts b/libs/application/templates/accident-notification/src/lib/messages/externalData.ts index 869618dbdc54..4f1342fc8544 100644 --- a/libs/application/templates/accident-notification/src/lib/messages/externalData.ts +++ b/libs/application/templates/accident-notification/src/lib/messages/externalData.ts @@ -12,6 +12,24 @@ export const externalData = { defaultMessage: 'Meðferð á gögnum', description: 'Data handling list item title', }, + bulletOne: { + id: 'an.application:section.agreementDescription.BulletOne', + defaultMessage: + 'Þegar tilkynning um slys er send Sjúkratryggingum Íslands mun stofnunin miðla upplýsingum um afstöðu til bótaskyldu með þeim atvinnurekanda eða íþróttafélagi sem á í hlut. Ástæða þess er að umræddir aðilar kunna að eiga rétt á endurgreiðslu útlagðs kostnaðar og/eða dagpeningum ef greidd hafa verið laun í veikindaforföllum vegna slyssins. Þessir aðilar fá aldrei afhentar heilsufars- eða sjúkraskrárupplýsingar.', + description: 'List item 1 on data gathering information', + }, + bulletTwo: { + id: 'an.application:section.agreementDescription.BulletTwo', + defaultMessage: + 'Vinnueftirlit ríkisins kann einnig að fá afrit af tilkynningunni undir ákveðnum kringumstæðum á grundvelli 4. mgr. 79. gr. laga nr. 46/1980 sem og Rannsóknarnefnd samgönguslysa á grundvelli 12. og 16. gr. laga nr. 18/2013.', + description: 'List item 2 on data gathering information', + }, + bulletThree: { + id: 'an.application:section.agreementDescription.BulletThree', + defaultMessage: + 'Eitthvað óvænt verður að hafa gerst sem veldur tjóni á líkama hins tryggða og áhorfandi getur áttað sig á að hafi gerst.', + description: 'List item 3 on data gathering information', + }, bulletFour: { id: 'an.application:section.agreementDescription.BulletFour', defaultMessage: diff --git a/libs/application/templates/accident-notification/src/types/index.ts b/libs/application/templates/accident-notification/src/types/index.ts index da316a4fd5f7..58dac3b071d9 100644 --- a/libs/application/templates/accident-notification/src/types/index.ts +++ b/libs/application/templates/accident-notification/src/types/index.ts @@ -1,4 +1,4 @@ -import { NO, YES } from '../utils/constants' +import { companyInfo, representativeInfo } from '../lib/messages' export type CompanyInfo = { nationalRegistrationId: string @@ -53,11 +53,6 @@ export enum OnBehalf { OTHERS = 'others', } -export enum ChoiceEnum { - YES = 'yes', - NO = 'no', -} - export enum WhoIsTheNotificationForEnum { JURIDICALPERSON = 'juridicalPerson', ME = 'me', @@ -73,7 +68,10 @@ export enum AccidentTypeEnum { SPORTS = 'sports', } -export type YesOrNo = typeof NO | typeof YES +export enum YesOrNo { + YES = 'yes', + NO = 'no', +} export enum AttachmentsEnum { INJURYCERTIFICATE = 'injuryCertificate', @@ -229,3 +227,13 @@ export type RepresentativeInfoV2 = { nationalId?: string | null phoneNumber?: string | null } + +export type WorkplaceData = { + companyInfo: CompanyInfo + representitive: RepresentativeInfo + companyInfoMsg: typeof companyInfo + representitiveMsg: typeof representativeInfo + type: WorkAccidentTypeEnum | AccidentTypeEnum + onPayRoll?: YesOrNo + screenId: string +} diff --git a/libs/application/templates/accident-notification/src/utils/accidentNotificationUtils.spec.ts b/libs/application/templates/accident-notification/src/utils/accidentNotificationUtils.spec.ts deleted file mode 100644 index 1e1d888a4745..000000000000 --- a/libs/application/templates/accident-notification/src/utils/accidentNotificationUtils.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { isValid24HFormatTime } from './index' -describe('Health insurance utils', () => { - const validString = '2359' - const tooLongString = '23599' - const threeLetterString = '123' - const twoLetterString = '12' - const oneLetterString = '1' - const emptyString = '' - const illlegalHourString = '2459' - const illlegalMinuteString = '2364' - - describe('Check time format validation logic', () => { - it('should return true for 2359', () => { - expect(isValid24HFormatTime(validString)).toEqual(true) - }) - it('should return false for 23599', () => { - expect(isValid24HFormatTime(tooLongString)).toEqual(false) - }) - it('should return false for 123', () => { - expect(isValid24HFormatTime(threeLetterString)).toEqual(false) - }) - it('should return false for 12', () => { - expect(isValid24HFormatTime(twoLetterString)).toEqual(false) - }) - it('should return false for 1', () => { - expect(isValid24HFormatTime(oneLetterString)).toEqual(false) - }) - it('should return false for empty string', () => { - expect(isValid24HFormatTime(emptyString)).toEqual(false) - }) - it('should return false for 2459', () => { - expect(isValid24HFormatTime(illlegalHourString)).toEqual(false) - }) - it('should return false for 2364', () => { - expect(isValid24HFormatTime(illlegalMinuteString)).toEqual(false) - }) - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/accidentUtils.spec.ts b/libs/application/templates/accident-notification/src/utils/accidentUtils.spec.ts new file mode 100644 index 000000000000..8fbbccbf2274 --- /dev/null +++ b/libs/application/templates/accident-notification/src/utils/accidentUtils.spec.ts @@ -0,0 +1,157 @@ +import { FormValue, NO, YES } from '@island.is/application/types' +import { AccidentTypeEnum } from '../types' +import { + getInjuredPersonInformation, + isFatalAccident, + isHomeActivitiesAccident, + isRescueWorkAccident, + isStudiesAccident, + isWorkAccident, +} from './accidentUtils' + +describe('isHomeActivitiesAccident', () => { + const homeActivitiesAccident: FormValue = { + accidentType: { radioButton: AccidentTypeEnum.HOMEACTIVITIES }, + } + + const someOtherAccident: FormValue = { + accidentType: { radioButton: AccidentTypeEnum.RESCUEWORK }, + } + + const emptyObject = {} + + it('should return true for home activity accidents', () => { + expect(isHomeActivitiesAccident(homeActivitiesAccident)).toEqual(true) + }) + it('should return false for accidents other than home activity accidents', () => { + expect(isHomeActivitiesAccident(someOtherAccident)).toEqual(false) + }) + it('should return false for empty object', () => { + expect(isHomeActivitiesAccident(emptyObject)).toEqual(false) + }) +}) +describe('isWorkAccident', () => { + const workAccident: FormValue = { + accidentType: { radioButton: AccidentTypeEnum.WORK }, + } + + const someOtherAccident: FormValue = { + accidentType: { radioButton: AccidentTypeEnum.HOMEACTIVITIES }, + } + + const emptyObject = {} + + it('should return true for work accidents', () => { + expect(isWorkAccident(workAccident)).toEqual(true) + }) + it('should return false for accidents other than work', () => { + expect(isWorkAccident(someOtherAccident)).toEqual(false) + }) + it('should return false for empty object', () => { + expect(isWorkAccident(emptyObject)).toEqual(false) + }) +}) + +describe('isRescueWorkAccident', () => { + const rescueWorkAccident: FormValue = { + accidentType: { radioButton: AccidentTypeEnum.RESCUEWORK }, + } + + const someOtherAccident: FormValue = { + accidentType: { radioButton: AccidentTypeEnum.HOMEACTIVITIES }, + } + + const emptyObject = {} + + it('should return true for rescue work accidents', () => { + expect(isRescueWorkAccident(rescueWorkAccident)).toEqual(true) + }) + it('should return false for accidents other than rescue work', () => { + expect(isRescueWorkAccident(someOtherAccident)).toEqual(false) + }) + it('should return false for empty object', () => { + expect(isRescueWorkAccident(emptyObject)).toEqual(false) + }) +}) + +describe('isStudiesAccident', () => { + const studiesAccident: FormValue = { + accidentType: { radioButton: AccidentTypeEnum.STUDIES }, + } + + const someOtherAccident: FormValue = { + accidentType: { radioButton: AccidentTypeEnum.HOMEACTIVITIES }, + } + + const emptyObject = {} + + it('should return true for studies accidents', () => { + expect(isStudiesAccident(studiesAccident)).toEqual(true) + }) + it('should return false for accidents other than studies', () => { + expect(isStudiesAccident(someOtherAccident)).toEqual(false) + }) + it('should return false for empty object', () => { + expect(isStudiesAccident(emptyObject)).toEqual(false) + }) +}) + +describe('getInjuredPersonInformation', () => { + const injuredPersonInformation: FormValue = { + injuredPersonInformation: { + email: 'kalli@palli.is', + name: 'Kalli', + }, + } + + const emptyInjuredPersonInformation: FormValue = { + injuredPersonInformation: { + email: '', + name: '', + }, + } + + it('Should return the email of the injured person', () => { + expect( + getInjuredPersonInformation(injuredPersonInformation)?.email, + ).toEqual('kalli@palli.is') + }) + + it('Should return the name of the injured person', () => { + expect(getInjuredPersonInformation(injuredPersonInformation)?.name).toEqual( + 'Kalli', + ) + }) + + it('Should return empty string for email if not provided', () => { + expect( + getInjuredPersonInformation(emptyInjuredPersonInformation)?.email, + ).toEqual('') + }) + + it('Should return empty string for name if not provided', () => { + expect( + getInjuredPersonInformation(emptyInjuredPersonInformation)?.name, + ).toEqual('') + }) +}) + +describe('isFatalAccident', () => { + const fatal: FormValue = { + wasTheAccidentFatal: YES, + } + + const notFatal: FormValue = { + wasTheAccidentFatal: NO, + } + + it('should return true for a fatal accident', () => { + expect(isFatalAccident(fatal)).toEqual(true) + }) + it('should return false for a non fatal accident', () => { + expect(isFatalAccident(notFatal)).toEqual(false) + }) + it('should return false for empty object', () => { + expect(isFatalAccident({})).toEqual(false) + }) +}) diff --git a/libs/application/templates/accident-notification/src/utils/accidentUtils.ts b/libs/application/templates/accident-notification/src/utils/accidentUtils.ts new file mode 100644 index 000000000000..94f8954d5954 --- /dev/null +++ b/libs/application/templates/accident-notification/src/utils/accidentUtils.ts @@ -0,0 +1,60 @@ +import { getValueViaPath } from '@island.is/application/core' +import { FormValue, YES } from '@island.is/application/types' +import { AccidentTypeEnum, YesOrNo } from '../types' + +export const isWorkAccident = (formValue: FormValue) => { + const accidentType = getValueViaPath( + formValue, + 'accidentType.radioButton', + ) + return accidentType === AccidentTypeEnum.WORK +} + +export const isHomeActivitiesAccident = (formValue: FormValue) => { + const workAccidentType = getValueViaPath( + formValue, + 'accidentType.radioButton', + ) + return workAccidentType === AccidentTypeEnum.HOMEACTIVITIES +} + +export const isRescueWorkAccident = (formValue: FormValue) => { + const accidentType = getValueViaPath( + formValue, + 'accidentType.radioButton', + ) + return accidentType === AccidentTypeEnum.RESCUEWORK +} + +export const isStudiesAccident = (formValue: FormValue) => { + const accidentType = getValueViaPath( + formValue, + 'accidentType.radioButton', + ) + return accidentType === AccidentTypeEnum.STUDIES +} + +export const getInjuredPersonInformation = (answers: FormValue) => { + const injuredPersonsEmail = getValueViaPath( + answers, + 'injuredPersonInformation.email', + ) + + const injuredPersonsName = getValueViaPath( + answers, + 'injuredPersonInformation.name', + ) + + return { + email: injuredPersonsEmail, + name: injuredPersonsName, + } +} + +export const isFatalAccident = (formValue: FormValue) => { + const wasTheAccidentFatal = getValueViaPath( + formValue, + 'wasTheAccidentFatal', + ) + return wasTheAccidentFatal === YES +} diff --git a/libs/application/templates/accident-notification/src/utils/constants/index.ts b/libs/application/templates/accident-notification/src/utils/constants/index.ts index c13413335ab9..b57a23b4f76a 100644 --- a/libs/application/templates/accident-notification/src/utils/constants/index.ts +++ b/libs/application/templates/accident-notification/src/utils/constants/index.ts @@ -1,6 +1,3 @@ -export const YES = 'yes' -export const NO = 'no' - export const UPLOAD_ACCEPT = '.pdf, .doc, .docx, .rtf, .jpg, .jpeg, .png, .heic' export const FILE_SIZE_LIMIT = 10000000 // 10MB diff --git a/libs/application/templates/accident-notification/src/utils/isDateOlderThanAYear.spec.ts b/libs/application/templates/accident-notification/src/utils/dateUtils.spec.ts similarity index 61% rename from libs/application/templates/accident-notification/src/utils/isDateOlderThanAYear.spec.ts rename to libs/application/templates/accident-notification/src/utils/dateUtils.spec.ts index 60fae84cec61..1045a0918ffe 100644 --- a/libs/application/templates/accident-notification/src/utils/isDateOlderThanAYear.spec.ts +++ b/libs/application/templates/accident-notification/src/utils/dateUtils.spec.ts @@ -1,5 +1,6 @@ import { FormValue } from '@island.is/application/types' -import { isDateOlderThanAYear } from './isDateOlderThanAYear' +import { isDateOlderThanAYear, isValid24HFormatTime } from './dateUtils' + describe('isDateOlderThanAYear', () => { const yesterday = new Date() yesterday.setDate(yesterday.getDate() - 1) @@ -27,3 +28,29 @@ describe('isDateOlderThanAYear', () => { expect(isDateOlderThanAYear(emptyObject)).toEqual(false) }) }) + +describe('isValid24HFormatTime', () => { + it.each(['0000', '2359', '1234'])( + 'should return true for valid time', + (time) => { + const result = isValid24HFormatTime(time) + expect(result).toBeTruthy() + }, + ) + + it.each([ + '2534', + '1265', + '2360', + '2400', + '12:34', + '', + '1', + '12', + '123', + '12345', + ])('should return false for invalid time', (time) => { + const result = isValid24HFormatTime(time) + expect(result).toBeFalsy() + }) +}) diff --git a/libs/application/templates/accident-notification/src/utils/isDateOlderThanAYear.ts b/libs/application/templates/accident-notification/src/utils/dateUtils.ts similarity index 60% rename from libs/application/templates/accident-notification/src/utils/isDateOlderThanAYear.ts rename to libs/application/templates/accident-notification/src/utils/dateUtils.ts index e554a1df59f4..1e7f2d43b42f 100644 --- a/libs/application/templates/accident-notification/src/utils/isDateOlderThanAYear.ts +++ b/libs/application/templates/accident-notification/src/utils/dateUtils.ts @@ -10,9 +10,18 @@ const getDateAYearBack = () => { export const isDateOlderThanAYear = (answers: FormValue) => { const aYearAgo = getDateAYearBack() - const date = getValueViaPath( + const date = getValueViaPath( answers, 'accidentDetails.dateOfAccident', - ) as string + ) return !!date && new Date(date).getTime() < aYearAgo.getTime() } + +export const isValid24HFormatTime = (value: string) => { + if (value.length !== 4) return false + const hours = parseInt(value.slice(0, 2)) + const minutes = parseInt(value.slice(2, 4)) + if (hours > 23) return false + if (minutes > 59) return false + return true +} diff --git a/libs/application/templates/accident-notification/src/utils/documentUtils.spec.ts b/libs/application/templates/accident-notification/src/utils/documentUtils.spec.ts new file mode 100644 index 000000000000..4ab59d4aa1d7 --- /dev/null +++ b/libs/application/templates/accident-notification/src/utils/documentUtils.spec.ts @@ -0,0 +1,316 @@ +import { FormatMessage } from '@island.is/localization' + +import { AccidentNotification } from '../lib/dataSchema' +import { + AttachmentsEnum, + PowerOfAttorneyUploadEnum, + WhoIsTheNotificationForEnum, + YesOrNo, +} from '../types' +import { + getAttachmentTitles, + isUploadNow, + returnMissingDocumentsList, +} from './documentUtils' +import { + getErrorMessageForMissingDocuments, + hasMissingDocuments, + hasReceivedAllDocuments, +} from './documentUtils' +import { FormValue } from '@island.is/application/types' + +describe('hasMissingDocuments', () => { + it('should return true when missing documents', () => { + expect(hasMissingDocuments(getMissingDocuments())).toEqual(true) + }) + + it('should return false when no missing documents', () => { + expect(hasMissingDocuments(getNoMissingDocuments())).toEqual(false) + }) +}) + +describe('getErrorMessageForMissingDocuments', () => { + const formatMessage: FormatMessage = jest.fn().mockReturnValue('test.pdf') + + beforeEach(() => { + jest.clearAllMocks() + }) + + it('should return error message for missing documents', () => { + const result = getErrorMessageForMissingDocuments( + getMissingDocuments(), + formatMessage, + false, + ) + expect(result).toEqual('test.pdf, test.pdf, test.pdf') + expect(formatMessage).toHaveBeenCalledTimes(3) + }) + + it('should return empty string when no documents are missing', () => { + const docs = getNoMissingDocuments() + const result = getErrorMessageForMissingDocuments(docs, formatMessage, true) + expect(result).toBe('') + expect(formatMessage).not.toHaveBeenCalled() + }) +}) + +describe('hasReceivedAllDocuments', () => { + const testCases = [ + { who: WhoIsTheNotificationForEnum.ME, fatal: YesOrNo.NO }, + { who: WhoIsTheNotificationForEnum.JURIDICALPERSON, fatal: YesOrNo.NO }, + { who: WhoIsTheNotificationForEnum.POWEROFATTORNEY, fatal: YesOrNo.YES }, + { who: WhoIsTheNotificationForEnum.POWEROFATTORNEY, fatal: YesOrNo.NO }, + ] + it.each(testCases)( + 'should return true when all documents are received', + (data) => { + const answers = getNoMissingDocuments() as AccidentNotification + answers.whoIsTheNotificationFor.answer = data.who + answers.wasTheAccidentFatal = data.fatal + expect(hasReceivedAllDocuments(answers)).toEqual(true) + }, + ) + + it.each(testCases)('should return false when missing documents', (data) => { + const answers = getMissingDocuments() as AccidentNotification + answers.whoIsTheNotificationFor.answer = data.who + answers.wasTheAccidentFatal = data.fatal + expect(hasReceivedAllDocuments(answers)).toEqual(false) + }) +}) + +const EMPTY_FILE: never[] = [] + +const SAMPLE_FILE = { + name: 'test.pdf', + url: 'https://test.pdf', +} as const +const createAttachment = () => ({ file: [SAMPLE_FILE] }) + +const getMissingDocuments = (): FormValue => ({ + whoIsTheNotificationFor: { + answer: WhoIsTheNotificationForEnum.POWEROFATTORNEY, + }, + wasTheAccidentFatal: YesOrNo.YES, + injuryCertificate: { + answer: AttachmentsEnum.SENDCERTIFICATELATER, + }, + accidentStatus: { + receivedAttachments: { + InjuryCertificate: false, + PoliceReport: false, + DeathCertificate: false, + ProxyDocument: false, + }, + }, + attachments: { + injuryCertificateFile: { file: EMPTY_FILE }, + deathCertificateFile: { file: EMPTY_FILE }, + powerOfAttorneyFile: { file: EMPTY_FILE }, + }, +}) + +const getNoMissingDocuments = (): FormValue => ({ + whoIsTheNotificationFor: { + answer: WhoIsTheNotificationForEnum.POWEROFATTORNEY, + }, + wasTheAccidentFatal: YesOrNo.YES, + injuryCertificate: { + answer: AttachmentsEnum.SENDCERTIFICATELATER, + }, + accidentStatus: { + receivedAttachments: { + InjuryCertificate: true, + PoliceReport: true, + DeathCertificate: true, + ProxyDocument: true, + }, + }, + attachments: { + injuryCertificateFile: { + file: [createAttachment()], + }, + deathCertificateFile: { + file: [createAttachment()], + }, + powerOfAttorneyFile: { + file: [createAttachment()], + }, + }, +}) + +describe('getAttachmentTitles', () => { + it.each([ + AttachmentsEnum.SENDCERTIFICATELATER, + AttachmentsEnum.HOSPITALSENDSCERTIFICATE, + ])('should return attachment titles', (injuryCertificate) => { + const answers = { + injuryCertificate: { + answer: injuryCertificate, + }, + attachments: { + deathCertificateFile: { + file: [ + { + name: 'test.pdf', + url: 'https://test.pdf', + }, + ], + }, + injuryCertificateFile: { + file: [ + { + name: 'test.pdf', + url: 'https://test.pdf', + }, + ], + }, + powerOfAttorneyFile: { + file: [ + { + name: 'test.pdf', + url: 'https://test.pdf', + }, + ], + }, + additionalFiles: { + file: [ + { + name: 'test.pdf', + url: 'https://test.pdf', + }, + ], + }, + additionalFilesFromReviewer: { + file: [ + { + name: 'test.pdf', + url: 'https://test.pdf', + }, + ], + }, + }, + } + + // Semi annoying push stuff here because order matters for strict equals + const isHospitalSendsCertificate = + injuryCertificate === AttachmentsEnum.HOSPITALSENDSCERTIFICATE + const expectedResults = [] + expectedResults.push({ + id: 'an.application:attachments.documentNames.deathCertificate', + defaultMessage: 'Lögregluskýrsla', + description: 'Name of police report for in review', + }) + if (!isHospitalSendsCertificate) { + expectedResults.push({ + id: 'an.application:attachments.documentNames.injuryCertificate', + defaultMessage: 'Áverkavottorð', + description: 'Name of injury certificate for in review', + }) + } + expectedResults.push({ + id: 'an.application:attachments.documentNames.powerOfAttorney', + defaultMessage: 'Umboð', + description: 'Name of power of attorney document for in review', + }) + if (isHospitalSendsCertificate) { + expectedResults.push({ + id: 'an.application:overview.labels.hospitalSendsCertificate', + defaultMessage: + 'Bráðamóttökuskrá - Ég mun óska eftir því að Landspítalinn sendi bráðamóttökuskrá til Sjúkratrygginga Íslands', + description: 'Label for hospital sends certificate in document list', + }) + } + expectedResults.push({ + id: 'an.application:attachments.documentNames.additionalDocumentsFromApplicant', + defaultMessage: 'Auka fylgiskjöl frá umsækjanda', + description: + 'Name of additional attachments for in review from applicant', + }) + expectedResults.push({ + id: 'an.application:attachments.documentNames.additionalDocumentsFromReviewer', + defaultMessage: 'Auka fylgiskjöl frá forsvarsmanni', + description: 'Name of additional attachments for in review from reviewer', + }) + + const result = getAttachmentTitles( + answers as unknown as AccidentNotification, + ) + expect(result).toStrictEqual(expectedResults) + }) +}) + +describe('returnMissingDocumentsList', () => { + it('should return missing documents list', () => { + const formatMessage: FormatMessage = jest.fn().mockReturnValue('test.pdf') + const missingDocuments = { + whoIsTheNotificationFor: { + answer: WhoIsTheNotificationForEnum.POWEROFATTORNEY, + }, + wasTheAccidentFatal: YesOrNo.YES, + injuryCertificate: { + answer: AttachmentsEnum.SENDCERTIFICATELATER, + }, + attachments: { + injuryCertificateFile: { + file: [], + }, + deathCertificateFile: { + file: [], + }, + powerOfAttorneyFile: { + file: [], + }, + }, + } + const result = returnMissingDocumentsList( + missingDocuments as unknown as AccidentNotification, + formatMessage, + ) + expect(result).toEqual('test.pdf, test.pdf, test.pdf') + }) +}) + +describe('isUploadNow', () => { + const powerOfAttorneyReporterWithUploadNow: FormValue = { + whoIsTheNotificationFor: { + answer: WhoIsTheNotificationForEnum.POWEROFATTORNEY, + }, + powerOfAttorney: { + type: PowerOfAttorneyUploadEnum.UPLOADNOW, + }, + } + + const powerOfAttorneyReporterWithUploadLater: FormValue = { + whoIsTheNotificationFor: { + answer: WhoIsTheNotificationForEnum.POWEROFATTORNEY, + }, + powerOfAttorney: { + type: PowerOfAttorneyUploadEnum.UPLOADLATER, + }, + } + + const reportingForSelf: FormValue = { + whoIsTheNotificationFor: { + answer: WhoIsTheNotificationForEnum.ME, + }, + } + + const emptyObject = {} + + it('should return true for power of attorney reporter with upload now', () => { + expect(isUploadNow(powerOfAttorneyReporterWithUploadNow)).toEqual(true) + }) + + it('should return false for power of attorney reporter with upload later', () => { + expect(isUploadNow(powerOfAttorneyReporterWithUploadLater)).toEqual(false) + }) + + it('should return false for reporting for yourself', () => { + expect(isUploadNow(reportingForSelf)).toEqual(false) + }) + + it('should return false for empty object', () => { + expect(isUploadNow(emptyObject)).toEqual(false) + }) +}) diff --git a/libs/application/templates/accident-notification/src/utils/documentUtils.ts b/libs/application/templates/accident-notification/src/utils/documentUtils.ts new file mode 100644 index 000000000000..e352dfe2997e --- /dev/null +++ b/libs/application/templates/accident-notification/src/utils/documentUtils.ts @@ -0,0 +1,295 @@ +import { getValueViaPath, YES } from '@island.is/application/core' +import { AccidentNotification } from '../lib/dataSchema' +import { + AttachmentsEnum, + FileType, + PowerOfAttorneyUploadEnum, + WhoIsTheNotificationForEnum, +} from '../types' +import { attachments, overview } from '../lib/messages' +import { FormatMessage } from '@island.is/localization' +import { FormValue } from '@island.is/application/types' +import { + AccidentNotificationAttachmentStatus, + AccidentNotifTypes, + YesOrNo, +} from '../types' +import { + isReportingOnBehalfOfEmployee, + isReportingOnBehalfSelf, +} from './reportingUtils' +import { isFatalAccident } from './accidentUtils' + +export const hasAttachment = (attachment: Array | undefined) => + attachment && attachment.length > 0 + +const includesAttachment = ( + answers: FormValue, + attachmentType: AccidentNotifTypes, +): boolean => { + const accidentNotifications = + getValueViaPath( + answers, + 'accidentStatus.receivedAttachments', + ) + return accidentNotifications?.[attachmentType] || false +} + +export const hasReceivedInjuryCertificate = (answers: FormValue) => { + return includesAttachment(answers, 'InjuryCertificate') +} + +export const hasReceivedProxyDocument = (answers: FormValue) => { + return includesAttachment(answers, 'ProxyDocument') +} + +export const hasReceivedPoliceReport = (answers: FormValue) => { + return includesAttachment(answers, 'PoliceReport') +} + +export const hasReceivedInjuryCertificateOrAddedToAnswers = ( + answers: FormValue, +) => { + const injuryCertificateFile = getValueViaPath>( + answers, + 'attachments.injuryCertificateFile.file', + [{ key: '', name: '' }], + ) + + return ( + hasReceivedInjuryCertificate(answers) || + hasAttachment(injuryCertificateFile) + ) +} + +export const hasReceivedProxyDocumentOrAddedToAnswers = ( + answers: FormValue, +) => { + const powerOfAttorneyFile = getValueViaPath>( + answers, + 'attachments.powerOfAttorneyFile.file', + [{ key: '', name: '' }], + ) + + return hasReceivedProxyDocument(answers) || hasAttachment(powerOfAttorneyFile) +} + +export const hasReceivedPoliceReportOrAddedToAnswers = (answers: FormValue) => { + const deathCertificateFile = getValueViaPath>( + answers, + 'attachments.deathCertificateFile.file', + [{ key: '', name: '' }], + ) + + return hasReceivedPoliceReport(answers) || hasAttachment(deathCertificateFile) +} + +export const hasReceivedAllDocuments = (answers: FormValue) => { + // Reporting for self or as juridicial person only injury certificate relevent + if ( + isReportingOnBehalfSelf(answers) || + isReportingOnBehalfOfEmployee(answers) + ) { + return hasReceivedInjuryCertificate(answers) + } else { + // If fatal and not report for self or as juridicial all documents are relevant + if (isFatalAccident(answers)) { + return ( + hasReceivedPoliceReport(answers) && + hasReceivedProxyDocument(answers) && + hasReceivedInjuryCertificate(answers) + ) + } else { + return ( + hasReceivedProxyDocument(answers) && + hasReceivedInjuryCertificate(answers) + ) + } + } +} + +export const getErrorMessageForMissingDocuments = ( + answers: FormValue, + formatMessage: FormatMessage, + isAssigneeAndUnique: boolean, +) => { + const whoIsTheNotificationFor = getValueViaPath( + answers, + 'whoIsTheNotificationFor.answer', + ) + const wasTheAccidentFatal = getValueViaPath( + answers, + 'wasTheAccidentFatal', + ) + const missingDocuments = [] + + if (!hasReceivedInjuryCertificateOrAddedToAnswers(answers)) { + missingDocuments.push( + formatMessage(attachments.documentNames.injuryCertificate), + ) + } + + // Only show this to applicant or assignee that is also the applicant + if ( + whoIsTheNotificationFor === WhoIsTheNotificationForEnum.POWEROFATTORNEY && + !hasReceivedProxyDocumentOrAddedToAnswers(answers) && + !isAssigneeAndUnique + ) { + missingDocuments.push( + formatMessage(attachments.documentNames.powerOfAttorneyDocument), + ) + } + + if ( + wasTheAccidentFatal === YES && + !hasReceivedPoliceReportOrAddedToAnswers(answers) + ) { + missingDocuments.push( + formatMessage(attachments.documentNames.deathCertificate), + ) + } + + return missingDocuments.join(', ') +} + +export const hasMissingInjuryCertificate = (answers: FormValue) => { + const injuryCertificate = getValueViaPath( + answers, + 'injuryCertificate.answer', + ) + return injuryCertificate === AttachmentsEnum.SENDCERTIFICATELATER +} + +export const hasMissingDeathCertificate = (answers: FormValue) => { + const wasTheAccidentFatal = getValueViaPath( + answers, + 'wasTheAccidentFatal', + ) + return wasTheAccidentFatal === YES +} + +export const hasMissingPowerOfAttorneyFile = (answers: FormValue): boolean => { + const whoIsTheNotificationFor = getValueViaPath( + answers, + 'whoIsTheNotificationFor.answer', + ) + return whoIsTheNotificationFor === WhoIsTheNotificationForEnum.POWEROFATTORNEY +} + +export const hasMissingDocuments = (answers: FormValue) => { + const injuryCertificateFile = getValueViaPath>( + answers, + 'attachments.injuryCertificateFile.file', + ) + + const deathCertificateFile = getValueViaPath>( + answers, + 'attachments.deathCertificateFile.file', + ) + + const powerOfAttorneyFile = getValueViaPath>( + answers, + 'attachments.powerOfAttorneyFile.file', + ) + + return ( + (hasMissingInjuryCertificate(answers) && + !hasAttachment(injuryCertificateFile)) || + (hasMissingDeathCertificate(answers) && + !hasAttachment(deathCertificateFile)) || + (hasMissingPowerOfAttorneyFile(answers) && + !hasAttachment(powerOfAttorneyFile)) + ) +} + +export const getAttachmentTitles = (answers: AccidentNotification) => { + const deathCertificateFile = + answers.attachments?.deathCertificateFile?.file || undefined + const injuryCertificateFile = + answers.attachments?.injuryCertificateFile?.file || undefined + const powerOfAttorneyFile = + answers.attachments?.powerOfAttorneyFile?.file || undefined + const additionalFiles = + answers.attachments?.additionalFiles?.file || undefined + const additionalFilesFromReviewer = + answers.attachments?.additionalFilesFromReviewer?.file || undefined + + const files = [] + + if (hasAttachment(deathCertificateFile)) + files.push(attachments.documentNames.deathCertificate) + if ( + hasAttachment(injuryCertificateFile) && + getValueViaPath(answers, 'injuryCertificate.answer') !== + AttachmentsEnum.HOSPITALSENDSCERTIFICATE + ) + files.push(attachments.documentNames.injuryCertificate) + if (hasAttachment(powerOfAttorneyFile)) + files.push(attachments.documentNames.powerOfAttorneyDocument) + if ( + answers.injuryCertificate?.answer === + AttachmentsEnum.HOSPITALSENDSCERTIFICATE + ) + files.push(overview.labels.hospitalSendsCertificate) + if (hasAttachment(additionalFiles)) + files.push(attachments.documentNames.additionalDocumentsFromApplicant) + if (hasAttachment(additionalFilesFromReviewer)) + files.push(attachments.documentNames.additionalDocumentsFromReviewer) + + return files +} + +export const returnMissingDocumentsList = ( + answers: AccidentNotification, + formatMessage: FormatMessage, +) => { + const injuryCertificate = answers.injuryCertificate + const whoIsTheNotificationFor = answers.whoIsTheNotificationFor.answer + const wasTheAccidentFatal = answers.wasTheAccidentFatal + const missingDocuments = [] + + if ( + injuryCertificate?.answer === AttachmentsEnum.SENDCERTIFICATELATER && + !hasAttachment(answers.attachments?.injuryCertificateFile?.file) + ) { + missingDocuments.push( + formatMessage(attachments.documentNames.injuryCertificate), + ) + } + + // Only show this to applicant or assignee that is also the applicant + if ( + whoIsTheNotificationFor === WhoIsTheNotificationForEnum.POWEROFATTORNEY && + !hasAttachment(answers.attachments?.powerOfAttorneyFile?.file) + ) { + missingDocuments.push( + formatMessage(attachments.documentNames.powerOfAttorneyDocument), + ) + } + + if ( + wasTheAccidentFatal === YES && + !hasAttachment(answers.attachments?.deathCertificateFile?.file) + ) { + missingDocuments.push( + formatMessage(attachments.documentNames.deathCertificate), + ) + } + + return missingDocuments.join(', ') +} + +export const isUploadNow = (formValue: FormValue) => { + const whoIsTheNotificationFor = getValueViaPath( + formValue, + 'whoIsTheNotificationFor.answer', + ) + const powerOfAttorneyType = getValueViaPath( + formValue, + 'powerOfAttorney.type', + ) + return ( + whoIsTheNotificationFor === WhoIsTheNotificationForEnum.POWEROFATTORNEY && + powerOfAttorneyType === PowerOfAttorneyUploadEnum.UPLOADNOW + ) +} diff --git a/libs/application/templates/accident-notification/src/utils/fishermanUtils.spec.ts b/libs/application/templates/accident-notification/src/utils/fishermanUtils.spec.ts deleted file mode 100644 index 03141eb17bdd..000000000000 --- a/libs/application/templates/accident-notification/src/utils/fishermanUtils.spec.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { - AccidentTypeEnum, - FishermanWorkplaceAccidentLocationEnum, - WorkAccidentTypeEnum, -} from '../types' -import { isAboardShip, isFishermanAccident } from './fishermanUtils' -describe('isFishermanAccident', () => { - const fishermanAccident: FormValue = { - workAccident: { type: WorkAccidentTypeEnum.FISHERMAN }, - accidentType: { radioButton: AccidentTypeEnum.WORK }, - } - - const someOtherAccident: FormValue = { - workAccident: { type: WorkAccidentTypeEnum.PROFESSIONALATHLETE }, - } - - const emptyObject = {} - - it('should return true for fisherman accidents', () => { - expect(isFishermanAccident(fishermanAccident)).toEqual(true) - }) - it('should return false for workplace accidents other than fisherman', () => { - expect(isFishermanAccident(someOtherAccident)).toEqual(false) - }) - it('should return false for empty object', () => { - expect(isFishermanAccident(emptyObject)).toEqual(false) - }) -}) - -describe('isAboardShip', () => { - const onTheShipLocation: FormValue = { - accidentLocation: { - answer: FishermanWorkplaceAccidentLocationEnum.ONTHESHIP, - }, - workAccident: { type: WorkAccidentTypeEnum.FISHERMAN }, - accidentType: { radioButton: AccidentTypeEnum.WORK }, - } - - const someOtherLocation: FormValue = { - accidentLocation: { answer: FishermanWorkplaceAccidentLocationEnum.OTHER }, - workAccident: { type: WorkAccidentTypeEnum.FISHERMAN }, - accidentType: { radioButton: AccidentTypeEnum.WORK }, - } - - const emptyObject = {} - - it('should return true for fisherman work accident that happens on a ship', () => { - expect(isAboardShip(onTheShipLocation)).toEqual(true) - }) - it('should return false for fisherman work accident that happens else where', () => { - expect(isAboardShip(someOtherLocation)).toEqual(false) - }) - it('should return false for empty object', () => { - expect(isAboardShip(emptyObject)).toEqual(false) - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/fishermanUtils.ts b/libs/application/templates/accident-notification/src/utils/fishermanUtils.ts deleted file mode 100644 index 280165f94136..000000000000 --- a/libs/application/templates/accident-notification/src/utils/fishermanUtils.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { FormValue } from '@island.is/application/types' -import { - FishermanWorkplaceAccidentLocationEnum, - WorkAccidentTypeEnum, -} from '../types' -import { isWorkAccident } from './isWorkAccident' - -// As this is a second question the user is asked there is a case where he could go back and select home activities and keep the workaccident type. -// Therefore we need to check also whether this is a work accident -export const isFishermanAccident = (formValue: FormValue) => { - const workAccidentType = getValueViaPath( - formValue, - 'workAccident.type', - ) as WorkAccidentTypeEnum - return ( - workAccidentType === WorkAccidentTypeEnum.FISHERMAN && - isWorkAccident(formValue) - ) -} - -// As this is a third question the user is asked there is a case where he could go back -// and select home activities and keep the workaccident type or go back and change where the -// accident happened. -// Therefore we need to check ifFishermanAccident function again -export const isAboardShip = (formValue: FormValue) => { - const fishermanWorkplaceAccidentLocationAnswer = getValueViaPath( - formValue, - 'accidentLocation.answer', - ) as FishermanWorkplaceAccidentLocationEnum - return ( - isFishermanAccident(formValue) && - fishermanWorkplaceAccidentLocationAnswer === - FishermanWorkplaceAccidentLocationEnum.ONTHESHIP - ) -} diff --git a/libs/application/templates/accident-notification/src/utils/getAccidentTypeOptions.ts b/libs/application/templates/accident-notification/src/utils/getAccidentTypeOptions.ts deleted file mode 100644 index ce91825ff6c9..000000000000 --- a/libs/application/templates/accident-notification/src/utils/getAccidentTypeOptions.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { accidentType } from '../lib/messages' -import { AccidentTypeEnum } from '../types' -import { isReportingOnBehalfOfChild } from './isReportingOnBehalfOfChild' -import { isReportingOnBehalfOfEmployee } from './isReportingOnBehalfOfEmployee' - -export const getAccidentTypeOptions = (answers: FormValue) => { - const options = [ - { - value: AccidentTypeEnum.WORK, - label: accidentType.labels.work, - }, - { - value: AccidentTypeEnum.RESCUEWORK, - label: accidentType.labels.rescueWork, - }, - { - value: AccidentTypeEnum.STUDIES, - label: accidentType.labels.studies, - }, - { - value: AccidentTypeEnum.SPORTS, - label: accidentType.labels.sports, - }, - ] - - if ( - !isReportingOnBehalfOfEmployee(answers) && - !isReportingOnBehalfOfChild(answers) - ) { - options.unshift({ - value: AccidentTypeEnum.HOMEACTIVITIES, - label: accidentType.labels.homeActivities, - }) - } - - return options -} diff --git a/libs/application/templates/accident-notification/src/utils/getInjuredPersonInformation.spec.ts b/libs/application/templates/accident-notification/src/utils/getInjuredPersonInformation.spec.ts deleted file mode 100644 index d5cdb40fada9..000000000000 --- a/libs/application/templates/accident-notification/src/utils/getInjuredPersonInformation.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { getInjuredPersonInformation } from './getInjuredPersonInformation' - -describe('getInjuredPersonInformation', () => { - const injuredPersonInformation: FormValue = { - injuredPersonInformation: { - email: 'kalli@palli.is', - name: 'Kalli', - }, - } - - const emptyInjuredPersonInformation: FormValue = { - injuredPersonInformation: { - email: '', - name: '', - }, - } - - it('should return an array of length 4 when submitting on behalf of employee', () => { - expect( - getInjuredPersonInformation(injuredPersonInformation)?.email, - ).toEqual('kalli@palli.is') - }) - - it('should return an array of length 5 when not submitting on behalf of employee', () => { - expect(getInjuredPersonInformation(injuredPersonInformation)?.name).toEqual( - 'Kalli', - ) - }) - - it('should return an array of length 5 for empty object', () => { - expect( - getInjuredPersonInformation(emptyInjuredPersonInformation)?.email, - ).toEqual('') - }) - - it('should have work as first option when submitting on behalf of employee', () => { - expect( - getInjuredPersonInformation(emptyInjuredPersonInformation)?.name, - ).toEqual('') - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/getInjuredPersonInformation.ts b/libs/application/templates/accident-notification/src/utils/getInjuredPersonInformation.ts deleted file mode 100644 index 4a2560a62074..000000000000 --- a/libs/application/templates/accident-notification/src/utils/getInjuredPersonInformation.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { FormValue } from '@island.is/application/types' - -export const getInjuredPersonInformation = (answers: FormValue) => { - const injuredPersonsEmail = getValueViaPath( - answers, - 'injuredPersonInformation.email', - ) as string - - const injuredPersonsName = getValueViaPath( - answers, - 'injuredPersonInformation.name', - ) as string - const injuredPersonsInformation = { - email: injuredPersonsEmail, - name: injuredPersonsName, - } - return injuredPersonsInformation -} diff --git a/libs/application/templates/accident-notification/src/utils/getAccidentTypeOptions.spec.ts b/libs/application/templates/accident-notification/src/utils/getOptions.spec.ts similarity index 96% rename from libs/application/templates/accident-notification/src/utils/getAccidentTypeOptions.spec.ts rename to libs/application/templates/accident-notification/src/utils/getOptions.spec.ts index 0d252b1e768b..b884f68e3347 100644 --- a/libs/application/templates/accident-notification/src/utils/getAccidentTypeOptions.spec.ts +++ b/libs/application/templates/accident-notification/src/utils/getOptions.spec.ts @@ -1,6 +1,6 @@ import { FormValue } from '@island.is/application/types' import { AccidentTypeEnum, WhoIsTheNotificationForEnum } from '../types' -import { getAccidentTypeOptions } from './getAccidentTypeOptions' +import { getAccidentTypeOptions } from './getOptions' describe('getAccidentTypeOptions', () => { const onBehalfOfEmployee: FormValue = { diff --git a/libs/application/templates/accident-notification/src/utils/getOptions.ts b/libs/application/templates/accident-notification/src/utils/getOptions.ts new file mode 100644 index 000000000000..e2945ae17ad4 --- /dev/null +++ b/libs/application/templates/accident-notification/src/utils/getOptions.ts @@ -0,0 +1,72 @@ +import { WhoIsTheNotificationForEnum } from '../types' +import { whoIsTheNotificationFor } from '../lib/messages' +import { FormValue } from '@island.is/application/types' +import { accidentType } from '../lib/messages' +import { AccidentTypeEnum } from '../types' +import { + isReportingOnBehalfOfChild, + isReportingOnBehalfOfEmployee, +} from './reportingUtils' + +export const getAccidentTypeOptions = (answers: FormValue) => { + const options = [ + { + value: AccidentTypeEnum.WORK, + label: accidentType.labels.work, + }, + { + value: AccidentTypeEnum.RESCUEWORK, + label: accidentType.labels.rescueWork, + }, + { + value: AccidentTypeEnum.STUDIES, + label: accidentType.labels.studies, + }, + { + value: AccidentTypeEnum.SPORTS, + label: accidentType.labels.sports, + }, + ] + + if ( + !isReportingOnBehalfOfEmployee(answers) && + !isReportingOnBehalfOfChild(answers) + ) { + options.unshift({ + value: AccidentTypeEnum.HOMEACTIVITIES, + label: accidentType.labels.homeActivities, + }) + } + + return options +} + +export const whoIsTheNotificationForOptions = [ + { + value: WhoIsTheNotificationForEnum.ME, + label: whoIsTheNotificationFor.labels.me, + }, + { + value: WhoIsTheNotificationForEnum.POWEROFATTORNEY, + label: whoIsTheNotificationFor.labels.powerOfAttorney, + }, + { + value: WhoIsTheNotificationForEnum.JURIDICALPERSON, + label: whoIsTheNotificationFor.labels.juridicalPerson, + }, + { + value: WhoIsTheNotificationForEnum.CHILDINCUSTODY, + label: whoIsTheNotificationFor.labels.childInCustody, + }, +] + +export const whoIsTheNotificationForProcureOptions = [ + { + value: WhoIsTheNotificationForEnum.POWEROFATTORNEY, + label: whoIsTheNotificationFor.labels.powerOfAttorneyProcure, + }, + { + value: WhoIsTheNotificationForEnum.JURIDICALPERSON, + label: whoIsTheNotificationFor.labels.juridicalPerson, + }, +] diff --git a/libs/application/templates/accident-notification/src/utils/getWhoIstheNotificationForOptions.ts b/libs/application/templates/accident-notification/src/utils/getWhoIstheNotificationForOptions.ts deleted file mode 100644 index 28910229cf83..000000000000 --- a/libs/application/templates/accident-notification/src/utils/getWhoIstheNotificationForOptions.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { WhoIsTheNotificationForEnum } from '../types' -import { whoIsTheNotificationFor } from '../lib/messages' - -export const whoIsTheNotificationForOptions = [ - { - value: WhoIsTheNotificationForEnum.ME, - label: whoIsTheNotificationFor.labels.me, - }, - { - value: WhoIsTheNotificationForEnum.POWEROFATTORNEY, - label: whoIsTheNotificationFor.labels.powerOfAttorney, - }, - { - value: WhoIsTheNotificationForEnum.JURIDICALPERSON, - label: whoIsTheNotificationFor.labels.juridicalPerson, - }, - { - value: WhoIsTheNotificationForEnum.CHILDINCUSTODY, - label: whoIsTheNotificationFor.labels.childInCustody, - }, -] - -export const whoIsTheNotificationForProcureOptions = [ - { - value: WhoIsTheNotificationForEnum.POWEROFATTORNEY, - label: whoIsTheNotificationFor.labels.powerOfAttorneyProcure, - }, - { - value: WhoIsTheNotificationForEnum.JURIDICALPERSON, - label: whoIsTheNotificationFor.labels.juridicalPerson, - }, -] diff --git a/libs/application/templates/accident-notification/src/utils/getWorkplaceData.spec.ts b/libs/application/templates/accident-notification/src/utils/getWorkplaceData.spec.ts deleted file mode 100644 index 45a3cb6d6a11..000000000000 --- a/libs/application/templates/accident-notification/src/utils/getWorkplaceData.spec.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { YES } from './constants' -import { AccidentTypeEnum, WorkAccidentTypeEnum } from '../types' -import { getWorkplaceData } from './getWorkplaceData' - -describe('getWorkplaceData', () => { - const generalWorkplaceAccident: FormValue = { - workAccident: { type: WorkAccidentTypeEnum.GENERAL }, - accidentType: { radioButton: AccidentTypeEnum.WORK }, - companyInfo: {}, - } - - const professionalAthleteAccident: FormValue = { - accidentType: { radioButton: AccidentTypeEnum.SPORTS }, - companyInfo: { onPayRoll: { answer: YES } }, - } - - const rescueWorkAccident: FormValue = { - accidentType: { radioButton: AccidentTypeEnum.RESCUEWORK }, - } - - const studiesAccident: FormValue = { - accidentType: { radioButton: AccidentTypeEnum.STUDIES }, - } - - const fishermanAccident: FormValue = { - workAccident: { type: WorkAccidentTypeEnum.FISHERMAN }, - accidentType: { radioButton: AccidentTypeEnum.WORK }, - } - - const emptyObject = {} - - it('should return general work type data for general work accidents', () => { - expect(getWorkplaceData(generalWorkplaceAccident)?.type).toEqual( - AccidentTypeEnum.WORK, - ) - }) - - it('should return sports type data for professional athlete accidents', () => { - expect(getWorkplaceData(professionalAthleteAccident)?.type).toEqual( - AccidentTypeEnum.SPORTS, - ) - }) - - it('should return employee information for professional athlete accidents', () => { - expect( - getWorkplaceData(professionalAthleteAccident)?.companyInfo.onPayRoll - ?.answer, - ).toBe(YES) - }) - - it('should not return employee information for general workplace accident', () => { - expect( - getWorkplaceData(generalWorkplaceAccident)?.companyInfo.onPayRoll, - ).toBe(undefined) - }) - - it('should return rescue work type data for rescue work accidents', () => { - expect(getWorkplaceData(rescueWorkAccident)?.type).toEqual( - AccidentTypeEnum.RESCUEWORK, - ) - }) - - it('should return studies type data for student accidents', () => { - expect(getWorkplaceData(studiesAccident)?.type).toEqual( - AccidentTypeEnum.STUDIES, - ) - }) - - it('should return fisherman type data for fisherman accidents', () => { - expect(getWorkplaceData(fishermanAccident)?.type).toEqual( - WorkAccidentTypeEnum.FISHERMAN, - ) - }) - - it('should return undefined for empty object', () => { - expect(getWorkplaceData(emptyObject)?.type).toEqual(undefined) - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/getWorkplaceData.ts b/libs/application/templates/accident-notification/src/utils/getWorkplaceData.ts deleted file mode 100644 index ccabbb5fe7aa..000000000000 --- a/libs/application/templates/accident-notification/src/utils/getWorkplaceData.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { FormValue } from '@island.is/application/types' -import { - isGeneralWorkplaceAccident, - isStudiesAccident, - isFishermanAccident, - isProfessionalAthleteAccident, - isRescueWorkAccident, -} from './' -import { - companyInfo, - fishingCompanyInfo, - rescueSquadInfo, - schoolInfo, - sportsClubInfo, - representativeInfo, -} from '../lib/messages' -import { - AccidentTypeEnum, - CompanyInfo, - RepresentativeInfo, - WorkAccidentTypeEnum, - YesOrNo, -} from '../types' -import { isHomeActivitiesAccident } from './isHomeActivitiesAccident' - -interface WorkplaceData { - companyInfo: CompanyInfo - representitive: RepresentativeInfo - companyInfoMsg: typeof companyInfo - representitiveMsg: typeof representativeInfo - type: WorkAccidentTypeEnum | AccidentTypeEnum - onPayRoll?: YesOrNo - screenId: string -} - -export const getWorkplaceData = ( - answers: FormValue, -): WorkplaceData | undefined => { - if (isHomeActivitiesAccident(answers)) { - return - } - - const workplaceData = { - companyInfo: getValueViaPath(answers, 'companyInfo') as CompanyInfo, - representitive: getValueViaPath( - answers, - 'representative', - ) as RepresentativeInfo, - representitiveMsg: representativeInfo, - } as WorkplaceData - - if (isGeneralWorkplaceAccident(answers)) - return { - ...workplaceData, - companyInfoMsg: companyInfo, - type: AccidentTypeEnum.WORK, - screenId: 'companyInfo', - } - - if (isStudiesAccident(answers)) - return { - ...workplaceData, - companyInfoMsg: schoolInfo, - type: AccidentTypeEnum.STUDIES, - screenId: 'schoolInfo', - } - - if (isFishermanAccident(answers)) - return { - ...workplaceData, - companyInfoMsg: fishingCompanyInfo, - type: WorkAccidentTypeEnum.FISHERMAN, - screenId: 'fishingCompanyInfo', - } - - if (isProfessionalAthleteAccident(answers)) - return { - ...workplaceData, - onPayRoll: getValueViaPath(answers, 'onPayRoll.answer') as YesOrNo, - companyInfoMsg: sportsClubInfo, - type: AccidentTypeEnum.SPORTS, - screenId: 'sportsClubInfo', - } - - if (isRescueWorkAccident(answers)) - return { - ...workplaceData, - companyInfoMsg: rescueSquadInfo, - type: AccidentTypeEnum.RESCUEWORK, - screenId: 'rescueSquad', - } -} diff --git a/libs/application/templates/accident-notification/src/utils/hasMissingDocuments.spec.ts b/libs/application/templates/accident-notification/src/utils/hasMissingDocuments.spec.ts deleted file mode 100644 index 945c4858ce4f..000000000000 --- a/libs/application/templates/accident-notification/src/utils/hasMissingDocuments.spec.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { - getErrorMessageForMissingDocuments, - hasMissingDocuments, - hasReceivedAllDocuments, -} from './hasMissingDocuments' -import { WhoIsTheNotificationForEnum, AttachmentsEnum } from '../types' -import { NO, YES } from './constants' -import { FormatMessage } from '@island.is/localization' -import { FormValue } from '@island.is/application/types' -import { AccidentNotification } from '../lib/dataSchema' - -describe('hasMissingDocuments', () => { - it('should return true when missing documents', () => { - expect(hasMissingDocuments(getMissingDocuments())).toEqual(true) - }) - - it('should return false when no missing documents', () => { - expect(hasMissingDocuments(getNoMissingDocuments())).toEqual(false) - }) -}) - -describe('getErrorMessageForMissingDocuments', () => { - const formatMessage: FormatMessage = jest.fn().mockReturnValue('test.pdf') - - beforeEach(() => { - jest.clearAllMocks() - }) - - it('should return error message for missing documents', () => { - const result = getErrorMessageForMissingDocuments( - getMissingDocuments(), - formatMessage, - false, - ) - expect(result).toEqual('test.pdf, test.pdf, test.pdf') - expect(formatMessage).toHaveBeenCalledTimes(3) - }) - - it('should return empty string when no documents are missing', () => { - const docs = getNoMissingDocuments() - const result = getErrorMessageForMissingDocuments(docs, formatMessage, true) - expect(result).toBe('') - expect(formatMessage).not.toHaveBeenCalled() - }) -}) - -describe('hasReceivedAllDocuments', () => { - const testCases = [ - { who: WhoIsTheNotificationForEnum.ME, fatal: NO }, - { who: WhoIsTheNotificationForEnum.JURIDICALPERSON, fatal: NO }, - { who: WhoIsTheNotificationForEnum.POWEROFATTORNEY, fatal: YES }, - { who: WhoIsTheNotificationForEnum.POWEROFATTORNEY, fatal: NO }, - ] - it.each(testCases)( - 'should return true when all documents are received', - (data) => { - const answers = getNoMissingDocuments() as AccidentNotification - answers.whoIsTheNotificationFor.answer = data.who - answers.wasTheAccidentFatal = data.fatal as unknown as 'no' | 'yes' - expect(hasReceivedAllDocuments(answers)).toEqual(true) - }, - ) - - it.each(testCases)('should return false when missing documents', (data) => { - const answers = getMissingDocuments() as AccidentNotification - answers.whoIsTheNotificationFor.answer = data.who - answers.wasTheAccidentFatal = data.fatal as unknown as 'no' | 'yes' - expect(hasReceivedAllDocuments(answers)).toEqual(false) - }) -}) - -const EMPTY_FILE: never[] = [] - -const SAMPLE_FILE = { - name: 'test.pdf', - url: 'https://test.pdf', -} as const -const createAttachment = () => ({ file: [SAMPLE_FILE] }) - -const getMissingDocuments = (): FormValue => ({ - whoIsTheNotificationFor: { - answer: WhoIsTheNotificationForEnum.POWEROFATTORNEY, - }, - wasTheAccidentFatal: YES, - injuryCertificate: { - answer: AttachmentsEnum.SENDCERTIFICATELATER, - }, - accidentStatus: { - receivedAttachments: { - InjuryCertificate: false, - PoliceReport: false, - DeathCertificate: false, - ProxyDocument: false, - }, - }, - attachments: { - injuryCertificateFile: { file: EMPTY_FILE }, - deathCertificateFile: { file: EMPTY_FILE }, - powerOfAttorneyFile: { file: EMPTY_FILE }, - }, -}) - -const getNoMissingDocuments = (): FormValue => ({ - whoIsTheNotificationFor: { - answer: WhoIsTheNotificationForEnum.POWEROFATTORNEY, - }, - wasTheAccidentFatal: YES, - injuryCertificate: { - answer: AttachmentsEnum.SENDCERTIFICATELATER, - }, - accidentStatus: { - receivedAttachments: { - InjuryCertificate: true, - PoliceReport: true, - DeathCertificate: true, - ProxyDocument: true, - }, - }, - attachments: { - injuryCertificateFile: { - file: [createAttachment()], - }, - deathCertificateFile: { - file: [createAttachment()], - }, - powerOfAttorneyFile: { - file: [createAttachment()], - }, - }, -}) diff --git a/libs/application/templates/accident-notification/src/utils/hasMissingDocuments.ts b/libs/application/templates/accident-notification/src/utils/hasMissingDocuments.ts deleted file mode 100644 index d262c07b6226..000000000000 --- a/libs/application/templates/accident-notification/src/utils/hasMissingDocuments.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { FormValue } from '@island.is/application/types' -import { FormatMessage } from '@island.is/localization' -import { AttachmentsEnum, FileType, WhoIsTheNotificationForEnum } from '..' -import { YES } from './constants' -import { attachments } from '../lib/messages' -import { - AccidentNotificationAttachmentStatus, - AccidentNotifTypes, - YesOrNo, -} from '../types' -import { isFatalAccident } from './isFatalAccident' -import { isReportingOnBehalfSelf } from './isReportingBehalfOfSelf' -import { isReportingOnBehalfOfEmployee } from './isReportingOnBehalfOfEmployee' - -const hasAttachment = (attachment: FileType[] | undefined) => - attachment && attachment.length > 0 - -const includesAttachment = ( - answers: FormValue, - attachmentType: AccidentNotifTypes, -): boolean => { - const accidentNotifications = getValueViaPath( - answers, - 'accidentStatus.receivedAttachments', - ) as AccidentNotificationAttachmentStatus - return accidentNotifications?.[attachmentType] || false -} - -export const hasReceivedInjuryCertificate = (answers: FormValue) => { - return includesAttachment(answers, 'InjuryCertificate') -} - -export const hasReceivedProxyDocument = (answers: FormValue) => { - return includesAttachment(answers, 'ProxyDocument') -} - -export const hasReceivedPoliceReport = (answers: FormValue) => { - return includesAttachment(answers, 'PoliceReport') -} - -export const hasReceivedInjuryCertificateOrAddedToAnswers = ( - answers: FormValue, -) => { - const injuryCertificateFile = getValueViaPath( - answers, - 'attachments.injuryCertificateFile.file', - {}, - ) as FileType[] - - return ( - hasReceivedInjuryCertificate(answers) || - hasAttachment(injuryCertificateFile) - ) -} - -export const hasReceivedProxyDocumentOrAddedToAnswers = ( - answers: FormValue, -) => { - const powerOfAttorneyFile = getValueViaPath( - answers, - 'attachments.powerOfAttorneyFile.file', - {}, - ) as FileType[] - - return hasReceivedProxyDocument(answers) || hasAttachment(powerOfAttorneyFile) -} - -export const hasReceivedPoliceReportOrAddedToAnswers = (answers: FormValue) => { - const deathCertificateFile = getValueViaPath( - answers, - 'attachments.deathCertificateFile.file', - {}, - ) as FileType[] - - return hasReceivedPoliceReport(answers) || hasAttachment(deathCertificateFile) -} - -export const hasReceivedAllDocuments = (answers: FormValue) => { - // Reporting for self or as juridicial person only injury certificate relevent - if ( - isReportingOnBehalfSelf(answers) || - isReportingOnBehalfOfEmployee(answers) - ) { - return hasReceivedInjuryCertificate(answers) - } else { - // If fatal and not report for self or as juridicial all documents are relevant - if (isFatalAccident(answers)) { - return ( - hasReceivedPoliceReport(answers) && - hasReceivedProxyDocument(answers) && - hasReceivedInjuryCertificate(answers) - ) - } else { - return ( - hasReceivedProxyDocument(answers) && - hasReceivedInjuryCertificate(answers) - ) - } - } -} - -export const getErrorMessageForMissingDocuments = ( - answers: FormValue, - formatMessage: FormatMessage, - isAssigneeAndUnique: boolean, -) => { - const whoIsTheNotificationFor = getValueViaPath( - answers, - 'whoIsTheNotificationFor.answer', - ) - const wasTheAccidentFatal = getValueViaPath( - answers, - 'wasTheAccidentFatal', - ) as YesOrNo - const missingDocuments = [] - - if (!hasReceivedInjuryCertificateOrAddedToAnswers(answers)) { - missingDocuments.push( - formatMessage(attachments.documentNames.injuryCertificate), - ) - } - - // Only show this to applicant or assignee that is also the applicant - if ( - whoIsTheNotificationFor === WhoIsTheNotificationForEnum.POWEROFATTORNEY && - !hasReceivedProxyDocumentOrAddedToAnswers(answers) && - !isAssigneeAndUnique - ) { - missingDocuments.push( - formatMessage(attachments.documentNames.powerOfAttorneyDocument), - ) - } - - if ( - wasTheAccidentFatal === YES && - !hasReceivedPoliceReportOrAddedToAnswers(answers) - ) { - missingDocuments.push( - formatMessage(attachments.documentNames.deathCertificate), - ) - } - - return missingDocuments.join(', ') -} - -export const hasMissingInjuryCertificate = (answers: FormValue) => { - const injuryCertificate = getValueViaPath( - answers, - 'injuryCertificate.answer', - ) as AttachmentsEnum - return injuryCertificate === AttachmentsEnum.SENDCERTIFICATELATER -} - -export const hasMissingDeathCertificate = (answers: FormValue) => { - const wasTheAccidentFatal = getValueViaPath( - answers, - 'wasTheAccidentFatal', - ) as YesOrNo - return wasTheAccidentFatal === YES -} - -export const hasMissingPowerOfAttorneyFile = (answers: FormValue): boolean => { - const whoIsTheNotificationFor = getValueViaPath( - answers, - 'whoIsTheNotificationFor.answer', - ) as WhoIsTheNotificationForEnum - return whoIsTheNotificationFor === WhoIsTheNotificationForEnum.POWEROFATTORNEY -} - -export const hasMissingDocuments = (answers: FormValue) => { - const injuryCertificateFile = getValueViaPath( - answers, - 'attachments.injuryCertificateFile.file', - ) as FileType[] - - const deathCertificateFile = getValueViaPath( - answers, - 'attachments.deathCertificateFile.file', - ) as FileType[] - - const powerOfAttorneyFile = getValueViaPath( - answers, - 'attachments.powerOfAttorneyFile.file', - ) as FileType[] - - return ( - (hasMissingInjuryCertificate(answers) && - !hasAttachment(injuryCertificateFile)) || - (hasMissingDeathCertificate(answers) && - !hasAttachment(deathCertificateFile)) || - (hasMissingPowerOfAttorneyFile(answers) && - !hasAttachment(powerOfAttorneyFile)) - ) -} diff --git a/libs/application/templates/accident-notification/src/utils/hasReceivedConfirmation.spec.ts b/libs/application/templates/accident-notification/src/utils/hasReceivedConfirmation.spec.ts deleted file mode 100644 index a778beed75ab..000000000000 --- a/libs/application/templates/accident-notification/src/utils/hasReceivedConfirmation.spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { hasReceivedConfirmation } from './hasReceivedConfirmation' -import { WhoIsTheNotificationForEnum } from '../types' -describe('hasReceivedConfirmation', () => { - const confirmedJuridicial: FormValue = { - whoIsTheNotificationFor: { - answer: WhoIsTheNotificationForEnum.JURIDICALPERSON, - }, - accidentStatus: { - receivedConfirmations: { - InjuredOrRepresentativeParty: true, - }, - }, - } - - const confirmedMe: FormValue = { - whoIsTheNotificationFor: { - answer: WhoIsTheNotificationForEnum.ME, - }, - accidentStatus: { - receivedConfirmations: { - CompanyParty: true, - }, - }, - } - - const notConfirmed: FormValue = { - accidentStatus: { - receivedConfirmations: false, - }, - } - - it.each([ - { for: 'juridical person', input: confirmedJuridicial, expected: true }, - { for: 'company', input: confirmedMe, expected: true }, - { for: 'not confirmed', input: notConfirmed, expected: false }, - ])( - 'should return $expected when confirmation is $for', - ({ input, expected }) => { - expect(hasReceivedConfirmation(input)).toEqual(expected) - }, - ) -}) diff --git a/libs/application/templates/accident-notification/src/utils/hasReceivedConfirmation.ts b/libs/application/templates/accident-notification/src/utils/hasReceivedConfirmation.ts deleted file mode 100644 index d3796bbe8ede..000000000000 --- a/libs/application/templates/accident-notification/src/utils/hasReceivedConfirmation.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { AccidentNotificationConfirmation } from '@island.is/api/schema' -import { getValueViaPath } from '@island.is/application/core' -import { FormValue } from '@island.is/application/types' -import { isReportingOnBehalfOfEmployee } from './isReportingOnBehalfOfEmployee' - -export const hasReceivedConfirmation = (answers: FormValue) => { - const accidentConfirmations = getValueViaPath( - answers, - 'accidentStatus.receivedConfirmations', - ) as AccidentNotificationConfirmation - - // if juridical person then the injured or the power of attorney holder has to confirm - if (isReportingOnBehalfOfEmployee(answers)) { - return !!accidentConfirmations.InjuredOrRepresentativeParty - } - - // as there isn't an juridical person reporting, this must be someone reporting for the injured - // or the injured himself and that requires the companies confirmation - return !!accidentConfirmations.CompanyParty -} diff --git a/libs/application/templates/accident-notification/src/utils/hideLocationAndPurpose.spec.ts b/libs/application/templates/accident-notification/src/utils/hideLocationAndPurpose.spec.ts deleted file mode 100644 index e6c00793695b..000000000000 --- a/libs/application/templates/accident-notification/src/utils/hideLocationAndPurpose.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { - GeneralWorkplaceAccidentLocationEnum, - StudiesAccidentLocationEnum, -} from '../types' -import { hideLocationAndPurpose } from './hideLocationAndPurpose' -describe('hideLocationAndPurpose', () => { - const accidentLocationAtWorkplace: FormValue = { - accidentLocation: { - answer: GeneralWorkplaceAccidentLocationEnum.ATTHEWORKPLACE, - }, - } - - const accidentLocationAtSchoole: FormValue = { - accidentLocation: { answer: StudiesAccidentLocationEnum.ATTHESCHOOL }, - } - - const someOtherLocation: FormValue = { - accidentLocation: { answer: GeneralWorkplaceAccidentLocationEnum.OTHER }, - } - - const emptyObject = {} - - it('should return true for accident location at workplace', () => { - expect(hideLocationAndPurpose(accidentLocationAtWorkplace)).toEqual(true) - }) - it('should return true for accident location at school', () => { - expect(hideLocationAndPurpose(accidentLocationAtSchoole)).toEqual(true) - }) - it('should return false for accident location elsewhere', () => { - expect(hideLocationAndPurpose(someOtherLocation)).toEqual(false) - }) - it('should return false for empty object', () => { - expect(hideLocationAndPurpose(emptyObject)).toEqual(false) - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/hideLocationAndPurpose.ts b/libs/application/templates/accident-notification/src/utils/hideLocationAndPurpose.ts deleted file mode 100644 index c0d1d5496027..000000000000 --- a/libs/application/templates/accident-notification/src/utils/hideLocationAndPurpose.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { FormValue } from '@island.is/application/types' -import { - GeneralWorkplaceAccidentLocationEnum, - ProfessionalAthleteAccidentLocationEnum, - StudiesAccidentLocationEnum, -} from '../types' -import { isHomeActivitiesAccident } from './isHomeActivitiesAccident' - -// Location and purpose of accident only relevant in work and studies and never in home -// activities -export const hideLocationAndPurpose = (formValue: FormValue) => { - const answer = getValueViaPath(formValue, 'accidentLocation.answer') as - | GeneralWorkplaceAccidentLocationEnum - | StudiesAccidentLocationEnum - | ProfessionalAthleteAccidentLocationEnum - - if (isHomeActivitiesAccident(formValue)) { - return true - } - return ( - answer === GeneralWorkplaceAccidentLocationEnum.ATTHEWORKPLACE || - answer === StudiesAccidentLocationEnum.ATTHESCHOOL || - answer === ProfessionalAthleteAccidentLocationEnum.SPORTCLUBSFACILITES - ) -} diff --git a/libs/application/templates/accident-notification/src/utils/index.spec.ts b/libs/application/templates/accident-notification/src/utils/index.spec.ts deleted file mode 100644 index 9bde4080715f..000000000000 --- a/libs/application/templates/accident-notification/src/utils/index.spec.ts +++ /dev/null @@ -1,180 +0,0 @@ -import { FormatMessage } from '@island.is/localization' -import { YES } from './constants' -import { AccidentNotification } from '../lib/dataSchema' -import { AttachmentsEnum, WhoIsTheNotificationForEnum } from '../types' -import { - isValid24HFormatTime, - formatPhonenumber, - getAttachmentTitles, - returnMissingDocumentsList, -} from './index' - -describe('isValid24HFormatTime', () => { - it.each(['0000', '2359', '1234'])( - 'should return true for valid time', - (time) => { - const result = isValid24HFormatTime(time) - expect(result).toBeTruthy() - }, - ) - - it.each([ - '2534', - '1265', - '2360', - '2400', - '12:34', - '', - '1', - '12', - '123', - '12345', - ])('should return false for invalid time', (time) => { - const result = isValid24HFormatTime(time) - expect(result).toBeFalsy() - }) -}) - -describe('formatPhonenumber', () => { - it.each([ - { input: '1234567', expected: '123-4567' }, - { input: '1234567891011', expected: '123-4567891011' }, - { input: 'ABCDEF@!()', expected: 'ABC-DEF@!()' }, - { input: '123', expected: '123' }, - ])('should format phone number', ({ input, expected }) => { - const result = formatPhonenumber(input) - expect(result).toBe(expected) - }) -}) - -describe('getAttachmentTitles', () => { - it.each([ - AttachmentsEnum.SENDCERTIFICATELATER, - AttachmentsEnum.HOSPITALSENDSCERTIFICATE, - ])('should return attachment titles', (injuryCertificate) => { - const answers = { - injuryCertificate: { - answer: injuryCertificate, - }, - attachments: { - deathCertificateFile: { - file: [ - { - name: 'test.pdf', - url: 'https://test.pdf', - }, - ], - }, - injuryCertificateFile: { - file: [ - { - name: 'test.pdf', - url: 'https://test.pdf', - }, - ], - }, - powerOfAttorneyFile: { - file: [ - { - name: 'test.pdf', - url: 'https://test.pdf', - }, - ], - }, - additionalFiles: { - file: [ - { - name: 'test.pdf', - url: 'https://test.pdf', - }, - ], - }, - additionalFilesFromReviewer: { - file: [ - { - name: 'test.pdf', - url: 'https://test.pdf', - }, - ], - }, - }, - } - - // Semi annoying push stuff here because order matters for strict equals - const isHospitalSendsCertificate = - injuryCertificate === AttachmentsEnum.HOSPITALSENDSCERTIFICATE - const expectedResults = [] - expectedResults.push({ - id: 'an.application:attachments.documentNames.deathCertificate', - defaultMessage: 'Lögregluskýrsla', - description: 'Name of police report for in review', - }) - if (!isHospitalSendsCertificate) { - expectedResults.push({ - id: 'an.application:attachments.documentNames.injuryCertificate', - defaultMessage: 'Áverkavottorð', - description: 'Name of injury certificate for in review', - }) - } - expectedResults.push({ - id: 'an.application:attachments.documentNames.powerOfAttorney', - defaultMessage: 'Umboð', - description: 'Name of power of attorney document for in review', - }) - if (isHospitalSendsCertificate) { - expectedResults.push({ - id: 'an.application:overview.labels.hospitalSendsCertificate', - defaultMessage: - 'Bráðamóttökuskrá - Ég mun óska eftir því að Landspítalinn sendi bráðamóttökuskrá til Sjúkratrygginga Íslands', - description: 'Label for hospital sends certificate in document list', - }) - } - expectedResults.push({ - id: 'an.application:attachments.documentNames.additionalDocumentsFromApplicant', - defaultMessage: 'Auka fylgiskjöl frá umsækjanda', - description: - 'Name of additional attachments for in review from applicant', - }) - expectedResults.push({ - id: 'an.application:attachments.documentNames.additionalDocumentsFromReviewer', - defaultMessage: 'Auka fylgiskjöl frá forsvarsmanni', - description: 'Name of additional attachments for in review from reviewer', - }) - - const result = getAttachmentTitles( - answers as unknown as AccidentNotification, - ) - expect(result).toStrictEqual(expectedResults) - }) -}) - -describe('returnMissingDocumentsList', () => { - it('should return missing documents list', () => { - const formatMessage: FormatMessage = jest.fn().mockReturnValue('test.pdf') - const missingDocuments = { - whoIsTheNotificationFor: { - answer: WhoIsTheNotificationForEnum.POWEROFATTORNEY, - }, - wasTheAccidentFatal: YES, - injuryCertificate: { - answer: AttachmentsEnum.SENDCERTIFICATELATER, - }, - attachments: { - injuryCertificateFile: { - file: [], - }, - deathCertificateFile: { - file: [], - }, - powerOfAttorneyFile: { - file: [], - }, - }, - } - const result = returnMissingDocumentsList( - missingDocuments as unknown as AccidentNotification, - formatMessage, - ) - expect(result).toEqual('test.pdf, test.pdf, test.pdf') - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/index.ts b/libs/application/templates/accident-notification/src/utils/index.ts deleted file mode 100644 index 2c1059df92f6..000000000000 --- a/libs/application/templates/accident-notification/src/utils/index.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { AttachmentsEnum, FileType, WhoIsTheNotificationForEnum } from '..' -import { getValueViaPath } from '@island.is/application/core' -import { YES } from './constants' -import { AccidentNotification } from '../lib/dataSchema' -import { attachments, overview } from '../lib/messages' -import { FormatMessage } from '@island.is/localization' - -export const isValid24HFormatTime = (value: string) => { - if (value.length !== 4) return false - const hours = parseInt(value.slice(0, 2)) - const minutes = parseInt(value.slice(2, 4)) - if (hours > 23) return false - if (minutes > 59) return false - return true -} - -export const formatPhonenumber = (value: string) => { - const splitAt = (index: number) => (x: string) => - [x.slice(0, index), x.slice(index)] - if (value.length > 3) return splitAt(3)(value).join('-') - return value -} - -const hasAttachment = (attachment: FileType[] | undefined) => - attachment && attachment.length > 0 - -export const getAttachmentTitles = (answers: AccidentNotification) => { - const deathCertificateFile = - answers.attachments?.deathCertificateFile?.file || undefined - const injuryCertificateFile = - answers.attachments?.injuryCertificateFile?.file || undefined - const powerOfAttorneyFile = - answers.attachments?.powerOfAttorneyFile?.file || undefined - const additionalFiles = - answers.attachments?.additionalFiles?.file || undefined - const additionalFilesFromReviewer = - answers.attachments?.additionalFilesFromReviewer?.file || undefined - - const files = [] - - if (hasAttachment(deathCertificateFile)) - files.push(attachments.documentNames.deathCertificate) - if ( - hasAttachment(injuryCertificateFile) && - getValueViaPath(answers, 'injuryCertificate.answer') !== - AttachmentsEnum.HOSPITALSENDSCERTIFICATE - ) - files.push(attachments.documentNames.injuryCertificate) - if (hasAttachment(powerOfAttorneyFile)) - files.push(attachments.documentNames.powerOfAttorneyDocument) - if ( - answers.injuryCertificate?.answer === - AttachmentsEnum.HOSPITALSENDSCERTIFICATE - ) - files.push(overview.labels.hospitalSendsCertificate) - if (hasAttachment(additionalFiles)) - files.push(attachments.documentNames.additionalDocumentsFromApplicant) - if (hasAttachment(additionalFilesFromReviewer)) - files.push(attachments.documentNames.additionalDocumentsFromReviewer) - - return files -} - -export const returnMissingDocumentsList = ( - answers: AccidentNotification, - formatMessage: FormatMessage, -) => { - const injuryCertificate = answers.injuryCertificate - const whoIsTheNotificationFor = answers.whoIsTheNotificationFor.answer - const wasTheAccidentFatal = answers.wasTheAccidentFatal - const missingDocuments = [] - - if ( - injuryCertificate?.answer === AttachmentsEnum.SENDCERTIFICATELATER && - !hasAttachment(answers.attachments?.injuryCertificateFile?.file) - ) { - missingDocuments.push( - formatMessage(attachments.documentNames.injuryCertificate), - ) - } - - // Only show this to applicant or assignee that is also the applicant - if ( - whoIsTheNotificationFor === WhoIsTheNotificationForEnum.POWEROFATTORNEY && - !hasAttachment(answers.attachments?.powerOfAttorneyFile?.file) - ) { - missingDocuments.push( - formatMessage(attachments.documentNames.powerOfAttorneyDocument), - ) - } - - if ( - wasTheAccidentFatal === YES && - !hasAttachment(answers.attachments?.deathCertificateFile?.file) - ) { - missingDocuments.push( - formatMessage(attachments.documentNames.deathCertificate), - ) - } - - return missingDocuments.join(', ') -} - -export * from './fishermanUtils' -export * from './getAccidentTypeOptions' -export * from './getInjuredPersonInformation' -export * from './getWorkplaceData' -export * from './hasMissingDocuments' -export * from './hideLocationAndPurpose' -export * from './isAgricultureAccident' -export * from './isDateOlderThanAYear' -export * from './isGeneralWorkplaceAccident' -export * from './isHomeActivitiesAccident' -export * from './isInternshipStudiesAccident' -export * from './isMachineRelatedAccident' -export * from './isProfessionalAthleteAccident' -export * from './isReportingOnBehalfOfChild' -export * from './isReportingOnBehalfOfEmployee' -export * from './isReportingOnBehalfOfInjured' -export * from './isRepresentativeOfCompanyOrInstitute' -export * from './isRescueWorkAccident' -export * from './isStudiesAccident' -export * from './isWorkAccident' -export * from './isPowerOfAttorney' -export * from './isRepresentativeOfCompanyOrInstitute' -export * from './isFatalAccident' -export * from './isReportingBehalfOfSelf' -export * from './isOfWorkTypeAccident' -export * from './shouldRequestReview' -export * from './isUniqueAssignee' diff --git a/libs/application/templates/accident-notification/src/utils/isAgricultureAccident.spec.ts b/libs/application/templates/accident-notification/src/utils/isAgricultureAccident.spec.ts deleted file mode 100644 index 8cbb0f338cfc..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isAgricultureAccident.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { AccidentTypeEnum, WorkAccidentTypeEnum } from '../types' -import { isAgricultureAccident } from './isAgricultureAccident' -describe('isAgricultureAccident', () => { - const agricultureAccident: FormValue = { - workAccident: { type: WorkAccidentTypeEnum.AGRICULTURE }, - accidentType: { radioButton: AccidentTypeEnum.WORK }, - } - - const someOtherAccident: FormValue = { - workAccident: { type: WorkAccidentTypeEnum.PROFESSIONALATHLETE }, - } - - const emptyObject = {} - - it('should return true for agriculture accidents', () => { - expect(isAgricultureAccident(agricultureAccident)).toEqual(true) - }) - it('should return false for workplace accidents other than agriculture', () => { - expect(isAgricultureAccident(someOtherAccident)).toEqual(false) - }) - it('should return false for empty object', () => { - expect(isAgricultureAccident(emptyObject)).toEqual(false) - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/isAgricultureAccident.ts b/libs/application/templates/accident-notification/src/utils/isAgricultureAccident.ts deleted file mode 100644 index e13dea0a0da9..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isAgricultureAccident.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { FormValue } from '@island.is/application/types' -import { WorkAccidentTypeEnum } from '../types' -import { isWorkAccident } from './isWorkAccident' - -// As this is a second question the user is asked there is a case where he could go back and select home activities and keep the agriculture type. -// Therefore we need to check also whether this is a work accident -export const isAgricultureAccident = (formValue: FormValue) => { - const workAccidentType = getValueViaPath( - formValue, - 'workAccident.type', - ) as WorkAccidentTypeEnum - return ( - workAccidentType === WorkAccidentTypeEnum.AGRICULTURE && - isWorkAccident(formValue) - ) -} diff --git a/libs/application/templates/accident-notification/src/utils/isFatalAccident.spec.ts b/libs/application/templates/accident-notification/src/utils/isFatalAccident.spec.ts deleted file mode 100644 index 67b9f7bb25ec..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isFatalAccident.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { isFatalAccident } from './isFatalAccident' -import { NO, YES } from './constants' - -describe('isFatalAccident', () => { - const fatal: FormValue = { - wasTheAccidentFatal: YES, - } - - const notFatal: FormValue = { - wasTheAccidentFatal: NO, - } - - it('should return true for a fatal accident', () => { - expect(isFatalAccident(fatal)).toEqual(true) - }) - it('should return false for a non fatal accident', () => { - expect(isFatalAccident(notFatal)).toEqual(false) - }) - it('should return false for empty object', () => { - expect(isFatalAccident({})).toEqual(false) - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/isFatalAccident.ts b/libs/application/templates/accident-notification/src/utils/isFatalAccident.ts deleted file mode 100644 index 4094c3efbc99..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isFatalAccident.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { FormValue } from '@island.is/application/types' -import { YES } from './constants' -import { YesOrNo } from '../types' - -export const isFatalAccident = (formValue: FormValue) => { - const wasTheAccidentFatal = getValueViaPath( - formValue, - 'wasTheAccidentFatal', - ) as YesOrNo - return wasTheAccidentFatal === YES -} diff --git a/libs/application/templates/accident-notification/src/utils/isGeneralWorkplaceAccident.spec.ts b/libs/application/templates/accident-notification/src/utils/isGeneralWorkplaceAccident.spec.ts deleted file mode 100644 index 1949e40394b6..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isGeneralWorkplaceAccident.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { AccidentTypeEnum, WorkAccidentTypeEnum } from '../types' -import { isGeneralWorkplaceAccident } from './isGeneralWorkplaceAccident' -describe('isGeneralWorkplaceAccident', () => { - const generalWorkplaceAccident: FormValue = { - workAccident: { type: WorkAccidentTypeEnum.GENERAL }, - accidentType: { radioButton: AccidentTypeEnum.WORK }, - } - - const someOtherAccident: FormValue = { - workAccident: { type: WorkAccidentTypeEnum.PROFESSIONALATHLETE }, - } - - const emptyObject = {} - - it('should return true for general workplace accidents', () => { - expect(isGeneralWorkplaceAccident(generalWorkplaceAccident)).toEqual(true) - }) - it('should return false for workplace accidents other than general', () => { - expect(isGeneralWorkplaceAccident(someOtherAccident)).toEqual(false) - }) - it('should return false for empty object', () => { - expect(isGeneralWorkplaceAccident(emptyObject)).toEqual(false) - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/isGeneralWorkplaceAccident.ts b/libs/application/templates/accident-notification/src/utils/isGeneralWorkplaceAccident.ts deleted file mode 100644 index 7be7ff2e3c8e..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isGeneralWorkplaceAccident.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { FormValue } from '@island.is/application/types' -import { WorkAccidentTypeEnum } from '../types' -import { isWorkAccident } from './isWorkAccident' - -// As this is a second question the user is asked there is a case where he could go back and select home activities and keep the workaccident type. -// Therefore we need to check also whether this is a work accident -export const isGeneralWorkplaceAccident = (formValue: FormValue) => { - const workAccidentType = getValueViaPath( - formValue, - 'workAccident.type', - ) as string - return ( - workAccidentType === WorkAccidentTypeEnum.GENERAL && - isWorkAccident(formValue) - ) -} diff --git a/libs/application/templates/accident-notification/src/utils/isHealthInsured.spec.ts b/libs/application/templates/accident-notification/src/utils/isHealthInsured.spec.ts deleted file mode 100644 index a5e8d11ce750..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isHealthInsured.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { isHealthInsured } from './isHealthInsured' - -describe('isHealthInsured', () => { - const healthInsured = { - accidentDetails: { - isHealthInsured: 'yes', - }, - } - - const notHealthInsured = { - accidentDetails: { - isHealthInsured: 'no', - }, - } - - it('should return true when health insured is yes', () => { - expect(isHealthInsured(healthInsured)).toEqual(true) - }) - - it('should return false when health insured is no', () => { - expect(isHealthInsured(notHealthInsured)).toEqual(false) - }) - - it('should return true when health insured is undefined', () => { - expect(isHealthInsured({})).toEqual(true) - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/isHealthInsured.ts b/libs/application/templates/accident-notification/src/utils/isHealthInsured.ts deleted file mode 100644 index e0e6765d67e1..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isHealthInsured.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { FormValue } from '@island.is/application/types' - -export const isHealthInsured = (formValue: FormValue) => { - const isHealthInsured = getValueViaPath( - formValue, - 'accidentDetails.isHealthInsured', - ) as string - if (isHealthInsured === undefined) return true - return isHealthInsured === 'yes' -} diff --git a/libs/application/templates/accident-notification/src/utils/isHomeActivitiesAccident.spec.ts b/libs/application/templates/accident-notification/src/utils/isHomeActivitiesAccident.spec.ts deleted file mode 100644 index c418a2b87656..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isHomeActivitiesAccident.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { AccidentTypeEnum } from '../types' -import { isHomeActivitiesAccident } from './isHomeActivitiesAccident' -describe('isHomeActivitiesAccident', () => { - const homeActivitiesAccident: FormValue = { - accidentType: { radioButton: AccidentTypeEnum.HOMEACTIVITIES }, - } - - const someOtherAccident: FormValue = { - accidentType: { radioButton: AccidentTypeEnum.RESCUEWORK }, - } - - const emptyObject = {} - - it('should return true for home activity accidents', () => { - expect(isHomeActivitiesAccident(homeActivitiesAccident)).toEqual(true) - }) - it('should return false for accidents other than home activity accidents', () => { - expect(isHomeActivitiesAccident(someOtherAccident)).toEqual(false) - }) - it('should return false for empty object', () => { - expect(isHomeActivitiesAccident(emptyObject)).toEqual(false) - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/isHomeActivitiesAccident.ts b/libs/application/templates/accident-notification/src/utils/isHomeActivitiesAccident.ts deleted file mode 100644 index 4aa71f8c1902..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isHomeActivitiesAccident.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { FormValue } from '@island.is/application/types' -import { AccidentTypeEnum } from '../types' - -export const isHomeActivitiesAccident = (formValue: FormValue) => { - const workAccidentType = getValueViaPath( - formValue, - 'accidentType.radioButton', - ) as AccidentTypeEnum - return workAccidentType === AccidentTypeEnum.HOMEACTIVITIES -} diff --git a/libs/application/templates/accident-notification/src/utils/isInternshipStudiesAccident.spec.ts b/libs/application/templates/accident-notification/src/utils/isInternshipStudiesAccident.spec.ts deleted file mode 100644 index ac468eb6b2bb..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isInternshipStudiesAccident.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { StudiesAccidentTypeEnum } from '../types' -import { isInternshipStudiesAccident } from './isInternshipStudiesAccident' -describe('isInternshipStudiesAccident', () => { - const studiesAccidentType: FormValue = { - studiesAccident: { type: StudiesAccidentTypeEnum.INTERNSHIP }, - } - - const someOtherAccidentType: FormValue = { - studiesAccident: { type: StudiesAccidentTypeEnum.APPRENTICESHIP }, - } - - const emptyObject = {} - - it('should return true for studies accidents', () => { - expect(isInternshipStudiesAccident(studiesAccidentType)).toEqual(true) - }) - it('should return false for accidents other than studies', () => { - expect(isInternshipStudiesAccident(someOtherAccidentType)).toEqual(false) - }) - it('should return false for empty object', () => { - expect(isInternshipStudiesAccident(emptyObject)).toEqual(false) - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/isInternshipStudiesAccident.ts b/libs/application/templates/accident-notification/src/utils/isInternshipStudiesAccident.ts deleted file mode 100644 index 07b7a859e87f..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isInternshipStudiesAccident.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { FormValue } from '@island.is/application/types' -import { StudiesAccidentTypeEnum } from '../types' - -export const isInternshipStudiesAccident = (formValue: FormValue) => { - const studiesAccidentType = getValueViaPath( - formValue, - 'studiesAccident.type', - ) as StudiesAccidentTypeEnum - return studiesAccidentType === StudiesAccidentTypeEnum.INTERNSHIP -} diff --git a/libs/application/templates/accident-notification/src/utils/isMachineRelatedAccident.spec.ts b/libs/application/templates/accident-notification/src/utils/isMachineRelatedAccident.spec.ts deleted file mode 100644 index 017a1e679a26..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isMachineRelatedAccident.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { NO, YES } from './constants' -import { AccidentTypeEnum, WorkAccidentTypeEnum } from '../types' -import { isMachineRelatedAccident } from './isMachineRelatedAccident' -describe('isMachineRelatedAccident', () => { - const machineRelatedAccident: FormValue = { - workAccident: { type: WorkAccidentTypeEnum.GENERAL }, - accidentType: { radioButton: AccidentTypeEnum.WORK }, - workMachineRadio: YES, - } - - const nonMachineRelatedAccident: FormValue = { - workAccident: { type: WorkAccidentTypeEnum.GENERAL }, - accidentType: { radioButton: AccidentTypeEnum.WORK }, - workMachineRadio: NO, - } - - const someOtherAccident: FormValue = { - workAccident: { type: WorkAccidentTypeEnum.PROFESSIONALATHLETE }, - } - - const emptyObject = {} - - it('should return true for machine related general work accidents', () => { - expect(isMachineRelatedAccident(machineRelatedAccident)).toEqual(true) - }) - it('should return false for non machine related general work accidents', () => { - expect(isMachineRelatedAccident(nonMachineRelatedAccident)).toEqual(false) - }) - it('should return false for workplace accidents other than general', () => { - expect(isMachineRelatedAccident(someOtherAccident)).toEqual(false) - }) - it('should return false for empty object', () => { - expect(isMachineRelatedAccident(emptyObject)).toEqual(false) - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/isMachineRelatedAccident.ts b/libs/application/templates/accident-notification/src/utils/isMachineRelatedAccident.ts deleted file mode 100644 index fd3cabc5347b..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isMachineRelatedAccident.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { FormValue } from '@island.is/application/types' -import { YES } from './constants' -import { YesOrNo } from '../types' -import { isGeneralWorkplaceAccident } from './isGeneralWorkplaceAccident' - -export const isMachineRelatedAccident = (formValue: FormValue) => { - const workMachineAnswer = getValueViaPath( - formValue, - 'workMachineRadio', - ) as YesOrNo - return isGeneralWorkplaceAccident(formValue) && workMachineAnswer === YES -} diff --git a/libs/application/templates/accident-notification/src/utils/isOfWorkTypeAccident.ts b/libs/application/templates/accident-notification/src/utils/isOfWorkTypeAccident.ts deleted file mode 100644 index 7ecb4c31b781..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isOfWorkTypeAccident.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { AccidentNotificationAnswers } from '..' -import { WorkAccidentTypeEnum } from '../types' - -export const isOfWorkAccidentType = ( - answers: Partial, - type: WorkAccidentTypeEnum, -) => { - const workAccidentType = getValueViaPath( - answers, - 'workAccident.type', - ) as WorkAccidentTypeEnum - return workAccidentType === type -} diff --git a/libs/application/templates/accident-notification/src/utils/isPowerOfAttorney.spec.ts b/libs/application/templates/accident-notification/src/utils/isPowerOfAttorney.spec.ts deleted file mode 100644 index 1c5ade01d399..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isPowerOfAttorney.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { WhoIsTheNotificationForEnum } from '../types' -import { isPowerOfAttorney } from './isPowerOfAttorney' - -describe('isPowerOfAttorney', () => { - const powerOfAttorneyReporter: FormValue = { - whoIsTheNotificationFor: { - answer: WhoIsTheNotificationForEnum.POWEROFATTORNEY, - }, - } - - const juridicialPersonReporter: FormValue = { - whoIsTheNotificationFor: { - answer: WhoIsTheNotificationForEnum.JURIDICALPERSON, - }, - } - - const reportingForSelf: FormValue = { - whoIsTheNotificationFor: { answer: WhoIsTheNotificationForEnum.ME }, - } - - const emptyObject = {} - - it('should return true for power of attorney reporter', () => { - expect(isPowerOfAttorney(powerOfAttorneyReporter)).toEqual(true) - }) - - it('should return false for power of juridical person reporter', () => { - expect(isPowerOfAttorney(juridicialPersonReporter)).toEqual(false) - }) - - it('should return false for reporting for yourself', () => { - expect(isPowerOfAttorney(reportingForSelf)).toEqual(false) - }) - - it('should return false for empty object', () => { - expect(isPowerOfAttorney(emptyObject)).toEqual(false) - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/isPowerOfAttorney.ts b/libs/application/templates/accident-notification/src/utils/isPowerOfAttorney.ts deleted file mode 100644 index 46b4d177bc5e..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isPowerOfAttorney.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { FormValue } from '@island.is/application/types' -import { WhoIsTheNotificationForEnum } from '../types' - -export const isPowerOfAttorney = (formValue: FormValue) => { - const reportingOnBehalfType = getValueViaPath( - formValue, - 'whoIsTheNotificationFor.answer', - ) as WhoIsTheNotificationForEnum - return reportingOnBehalfType === WhoIsTheNotificationForEnum.POWEROFATTORNEY -} diff --git a/libs/application/templates/accident-notification/src/utils/isProfessionalAthleteAccident.spec.ts b/libs/application/templates/accident-notification/src/utils/isProfessionalAthleteAccident.spec.ts deleted file mode 100644 index 22c0f49af199..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isProfessionalAthleteAccident.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { AccidentTypeEnum, WorkAccidentTypeEnum } from '../types' -import { isProfessionalAthleteAccident } from './isProfessionalAthleteAccident' - -describe('isProfessionalAthleteAccident', () => { - const professionalAthleteAccidentRadio: FormValue = { - accidentType: { radioButton: AccidentTypeEnum.SPORTS }, - } - - const professionalAthleteAccidentSecondaryWorkQuestion: FormValue = { - workAccident: { type: WorkAccidentTypeEnum.PROFESSIONALATHLETE }, - accidentType: { radioButton: AccidentTypeEnum.WORK }, - } - - const someOtherAccident: FormValue = { - workAccident: { type: WorkAccidentTypeEnum.FISHERMAN }, - } - - const emptyObject = {} - - it('should return true for professional athlete accidents', () => { - expect( - isProfessionalAthleteAccident(professionalAthleteAccidentRadio), - ).toEqual(true) - }) - - it('should return true for professional athlete accident when user picked work related and then sports related', () => { - expect( - isProfessionalAthleteAccident( - professionalAthleteAccidentSecondaryWorkQuestion, - ), - ).toEqual(true) - }) - - it('should return false for workplace accidents other than professional athlete', () => { - expect(isProfessionalAthleteAccident(someOtherAccident)).toEqual(false) - }) - it('should return false for empty object', () => { - expect(isProfessionalAthleteAccident(emptyObject)).toEqual(false) - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/isProfessionalAthleteAccident.ts b/libs/application/templates/accident-notification/src/utils/isProfessionalAthleteAccident.ts deleted file mode 100644 index e1b61896a622..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isProfessionalAthleteAccident.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { FormValue } from '@island.is/application/types' -import { AccidentTypeEnum, WorkAccidentTypeEnum } from '../types' -import { isWorkAccident } from './isWorkAccident' - -// Specific case check here since the accident can be a sports accident if he picks sports in the first question where -// he is asked what the circumstances of the accident were. But that user could also select work and then a sport related -// accident since the question can be missunderstood by the user so we are funneling both cases into the same flow -export const isProfessionalAthleteAccident = (formValue: FormValue) => { - const workAccidentType = getValueViaPath( - formValue, - 'accidentType.radioButton', - ) as AccidentTypeEnum - const workAccidentSecondaryType = getValueViaPath( - formValue, - 'workAccident.type', - ) as WorkAccidentTypeEnum - return ( - workAccidentType === AccidentTypeEnum.SPORTS || - (workAccidentSecondaryType === WorkAccidentTypeEnum.PROFESSIONALATHLETE && - isWorkAccident(formValue)) - ) -} diff --git a/libs/application/templates/accident-notification/src/utils/isReportingBehalfOfSelf.spec.ts b/libs/application/templates/accident-notification/src/utils/isReportingBehalfOfSelf.spec.ts deleted file mode 100644 index 98b80807ac4a..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isReportingBehalfOfSelf.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { isReportingOnBehalfSelf } from './isReportingBehalfOfSelf' -import { WhoIsTheNotificationForEnum } from '../types' - -describe('isRepresentativeOfCompanyOrInstitute', () => { - const self: FormValue = { - whoIsTheNotificationFor: { - answer: WhoIsTheNotificationForEnum.ME, - }, - } - - const notSelf: FormValue = { - whoIsTheNotificationFor: { - answer: WhoIsTheNotificationForEnum.POWEROFATTORNEY, - }, - } - - it('should return true when someone is reporting on behalf of themselves', () => { - expect(isReportingOnBehalfSelf(self)).toEqual(true) - }) - it('should return false when someone is not reporting on behalf of themselves', () => { - expect(isReportingOnBehalfSelf(notSelf)).toEqual(false) - }) - it('should return false for empty object', () => { - expect(isReportingOnBehalfSelf({})).toEqual(false) - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/isReportingBehalfOfSelf.ts b/libs/application/templates/accident-notification/src/utils/isReportingBehalfOfSelf.ts deleted file mode 100644 index 90b3784bc058..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isReportingBehalfOfSelf.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { FormValue } from '@island.is/application/types' -import { WhoIsTheNotificationForEnum } from '../types' - -export const isReportingOnBehalfSelf = (formValue: FormValue) => { - const whoIsTheNotificationFor = getValueViaPath( - formValue, - 'whoIsTheNotificationFor.answer', - ) as WhoIsTheNotificationForEnum - return whoIsTheNotificationFor === WhoIsTheNotificationForEnum.ME -} diff --git a/libs/application/templates/accident-notification/src/utils/isReportingOnBehalfOfChild.spec.ts b/libs/application/templates/accident-notification/src/utils/isReportingOnBehalfOfChild.spec.ts deleted file mode 100644 index f778067b61c1..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isReportingOnBehalfOfChild.spec.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { WhoIsTheNotificationForEnum } from '../types' -import { isReportingOnBehalfOfChild } from './isReportingOnBehalfOfChild' -describe('isReportingOnBehalfOfChild', () => { - const onBehalfOfChild: FormValue = { - whoIsTheNotificationFor: { - answer: WhoIsTheNotificationForEnum.CHILDINCUSTODY, - }, - } - - const onBehalfOfSomeOtherPerson: FormValue = { - whoIsTheNotificationFor: { - answer: WhoIsTheNotificationForEnum.POWEROFATTORNEY, - }, - } - - const emptyObject = {} - - it('should return true if reporting on behalf of child', () => { - expect(isReportingOnBehalfOfChild(onBehalfOfChild)).toEqual(true) - }) - it('should return false if reporting on behalf of some other person', () => { - expect(isReportingOnBehalfOfChild(onBehalfOfSomeOtherPerson)).toEqual(false) - }) - it('should return false for empty object', () => { - expect(isReportingOnBehalfOfChild(emptyObject)).toEqual(false) - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/isReportingOnBehalfOfChild.ts b/libs/application/templates/accident-notification/src/utils/isReportingOnBehalfOfChild.ts deleted file mode 100644 index d3193b011c11..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isReportingOnBehalfOfChild.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { FormValue } from '@island.is/application/types' -import { WhoIsTheNotificationForEnum } from '../types' - -export const isReportingOnBehalfOfChild = (formValue: FormValue) => { - const whoIsTheNotificationFor = getValueViaPath( - formValue, - 'whoIsTheNotificationFor.answer', - ) as WhoIsTheNotificationForEnum - return whoIsTheNotificationFor === WhoIsTheNotificationForEnum.CHILDINCUSTODY -} diff --git a/libs/application/templates/accident-notification/src/utils/isReportingOnBehalfOfEmployee.spec.ts b/libs/application/templates/accident-notification/src/utils/isReportingOnBehalfOfEmployee.spec.ts deleted file mode 100644 index 50c105735b0d..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isReportingOnBehalfOfEmployee.spec.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { WhoIsTheNotificationForEnum } from '../types' -import { isReportingOnBehalfOfEmployee } from './isReportingOnBehalfOfEmployee' -describe('isReportingOnBehalfOfEmployee', () => { - const onBehalfOfEmployee: FormValue = { - whoIsTheNotificationFor: { - answer: WhoIsTheNotificationForEnum.JURIDICALPERSON, - }, - } - - const onBehalfOfSomeOtherPerson: FormValue = { - whoIsTheNotificationFor: { - answer: WhoIsTheNotificationForEnum.POWEROFATTORNEY, - }, - } - - const emptyObject = {} - - it('should return true if reporting on behalf of employee', () => { - expect(isReportingOnBehalfOfEmployee(onBehalfOfEmployee)).toEqual(true) - }) - it('should return false if reporting on behalf of some other person', () => { - expect(isReportingOnBehalfOfEmployee(onBehalfOfSomeOtherPerson)).toEqual( - false, - ) - }) - it('should return false for empty object', () => { - expect(isReportingOnBehalfOfEmployee(emptyObject)).toEqual(false) - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/isReportingOnBehalfOfEmployee.ts b/libs/application/templates/accident-notification/src/utils/isReportingOnBehalfOfEmployee.ts deleted file mode 100644 index d00c7ed47f7f..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isReportingOnBehalfOfEmployee.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { FormValue } from '@island.is/application/types' -import { WhoIsTheNotificationForEnum } from '../types' - -export const isReportingOnBehalfOfEmployee = (formValue: FormValue) => { - const whoIsTheNotificationFor = getValueViaPath( - formValue, - 'whoIsTheNotificationFor.answer', - ) as WhoIsTheNotificationForEnum - return whoIsTheNotificationFor === WhoIsTheNotificationForEnum.JURIDICALPERSON -} diff --git a/libs/application/templates/accident-notification/src/utils/isReportingOnBehalfOfInjured.spec.ts b/libs/application/templates/accident-notification/src/utils/isReportingOnBehalfOfInjured.spec.ts deleted file mode 100644 index efce07880e51..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isReportingOnBehalfOfInjured.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { WhoIsTheNotificationForEnum } from '../types' -import { isReportingOnBehalfOfInjured } from './isReportingOnBehalfOfInjured' - -describe('isReportingOnBehalfOfInjured', () => { - const powerOfAttorneyReporter: FormValue = { - whoIsTheNotificationFor: { - answer: WhoIsTheNotificationForEnum.POWEROFATTORNEY, - }, - } - - const juridicialPersonReporter: FormValue = { - whoIsTheNotificationFor: { - answer: WhoIsTheNotificationForEnum.JURIDICALPERSON, - }, - } - - const reportingForSelf: FormValue = { - whoIsTheNotificationFor: { answer: WhoIsTheNotificationForEnum.ME }, - } - - const emptyObject = {} - - it('should return true for power of attorney reporter', () => { - expect(isReportingOnBehalfOfInjured(powerOfAttorneyReporter)).toEqual(true) - }) - - it('should return true for power of juridical person reporter', () => { - expect(isReportingOnBehalfOfInjured(juridicialPersonReporter)).toEqual(true) - }) - - it('should return false for reporting for yourself', () => { - expect(isReportingOnBehalfOfInjured(reportingForSelf)).toEqual(false) - }) - - it('should return false for empty object', () => { - expect(isReportingOnBehalfOfInjured(emptyObject)).toEqual(false) - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/isReportingOnBehalfOfInjured.ts b/libs/application/templates/accident-notification/src/utils/isReportingOnBehalfOfInjured.ts deleted file mode 100644 index d0ac674762ae..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isReportingOnBehalfOfInjured.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { FormValue } from '@island.is/application/types' -import { WhoIsTheNotificationForEnum } from '../types' - -export const isReportingOnBehalfOfInjured = (formValue: FormValue) => { - const whoIsTheNotificationFor = getValueViaPath( - formValue, - 'whoIsTheNotificationFor.answer', - ) as WhoIsTheNotificationForEnum - return ( - whoIsTheNotificationFor === WhoIsTheNotificationForEnum.JURIDICALPERSON || - whoIsTheNotificationFor === WhoIsTheNotificationForEnum.POWEROFATTORNEY - ) -} diff --git a/libs/application/templates/accident-notification/src/utils/isRepresentativeOfCompanyOrInstitute.spec.ts b/libs/application/templates/accident-notification/src/utils/isRepresentativeOfCompanyOrInstitute.spec.ts deleted file mode 100644 index aeff9475efef..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isRepresentativeOfCompanyOrInstitute.spec.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { WhoIsTheNotificationForEnum } from '../types' - -import { - isInjuredAndRepresentativeOfCompanyOrInstitute, - isRepresentativeOfCompanyOrInstitute, -} from './isRepresentativeOfCompanyOrInstitute' -import { NO, YES } from './constants' - -const emptyObject = {} - -describe('isRepresentativeOfCompanyOrInstitute', () => { - const representative: FormValue = { - whoIsTheNotificationFor: { - answer: WhoIsTheNotificationForEnum.JURIDICALPERSON, - }, - } - - const notRepresentative: FormValue = { - whoIsTheNotificationFor: { - answer: WhoIsTheNotificationForEnum.ME, - }, - } - - const emptyRepresentative: FormValue = { - whoIsTheNotificationFor: {}, - } - - it('should return true for someone that is a representative of the company or institue', () => { - expect(isRepresentativeOfCompanyOrInstitute(representative)).toEqual(true) - }) - it('should return false for someone that isnt a representative of the company or institue', () => { - expect(isRepresentativeOfCompanyOrInstitute(notRepresentative)).toEqual( - false, - ) - }) - it('should return false for empty object', () => { - expect(isRepresentativeOfCompanyOrInstitute(emptyObject)).toEqual(false) - }) - it('should return false for empty whoIsTheNotificationFor', () => { - expect(isRepresentativeOfCompanyOrInstitute(emptyRepresentative)).toEqual( - false, - ) - }) -}) - -describe('isInjuredAndRepresentativeOfCompanyOrInstitute', () => { - const representative: FormValue = { - isRepresentativeOfCompanyOrInstitute: YES, - } - - const notRepresentative: FormValue = { - isRepresentativeOfCompanyOrInstitute: NO, - } - - it('should return true for someone that is a representative of the company or institute', () => { - expect( - isInjuredAndRepresentativeOfCompanyOrInstitute(representative), - ).toEqual(true) - }) - - it('should return false for someone that isnt a representative of the company or institute', () => { - expect( - isInjuredAndRepresentativeOfCompanyOrInstitute(notRepresentative), - ).toEqual(false) - }) - - it('should return false for empty object', () => { - expect(isInjuredAndRepresentativeOfCompanyOrInstitute(emptyObject)).toEqual( - false, - ) - }) - - it('should return false for garbage string', () => { - expect( - isInjuredAndRepresentativeOfCompanyOrInstitute({ - isRepresentativeOfCompanyOrInstitute: 'garbage', - }), - ).toEqual(false) - }) - - it('should return false for object with non string value', () => { - expect( - isInjuredAndRepresentativeOfCompanyOrInstitute({ - isRepresentativeOfCompanyOrInstitute: true, - }), - ).toEqual(false) - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/isRepresentativeOfCompanyOrInstitute.ts b/libs/application/templates/accident-notification/src/utils/isRepresentativeOfCompanyOrInstitute.ts deleted file mode 100644 index f8c98b7b61f5..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isRepresentativeOfCompanyOrInstitute.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { FormValue } from '@island.is/application/types' -import { YES } from './constants' -import { WhoIsTheNotificationForEnum } from '../types' - -export const isRepresentativeOfCompanyOrInstitute = (formValue: FormValue) => { - return ( - getValueViaPath(formValue, 'whoIsTheNotificationFor.answer') === - WhoIsTheNotificationForEnum.JURIDICALPERSON - ) -} - -export const isInjuredAndRepresentativeOfCompanyOrInstitute = ( - formValue: FormValue, -) => { - return formValue.isRepresentativeOfCompanyOrInstitute?.toString() === YES -} diff --git a/libs/application/templates/accident-notification/src/utils/isRescueWorkAccident.spec.ts b/libs/application/templates/accident-notification/src/utils/isRescueWorkAccident.spec.ts deleted file mode 100644 index 1cdf033a88e6..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isRescueWorkAccident.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { AccidentTypeEnum } from '../types' -import { isRescueWorkAccident } from './isRescueWorkAccident' -describe('isRescueWorkAccident', () => { - const rescueWorkAccident: FormValue = { - accidentType: { radioButton: AccidentTypeEnum.RESCUEWORK }, - } - - const someOtherAccident: FormValue = { - accidentType: { radioButton: AccidentTypeEnum.HOMEACTIVITIES }, - } - - const emptyObject = {} - - it('should return true for rescue work accidents', () => { - expect(isRescueWorkAccident(rescueWorkAccident)).toEqual(true) - }) - it('should return false for accidents other than rescue work', () => { - expect(isRescueWorkAccident(someOtherAccident)).toEqual(false) - }) - it('should return false for empty object', () => { - expect(isRescueWorkAccident(emptyObject)).toEqual(false) - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/isRescueWorkAccident.ts b/libs/application/templates/accident-notification/src/utils/isRescueWorkAccident.ts deleted file mode 100644 index 2fd410280af8..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isRescueWorkAccident.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { FormValue } from '@island.is/application/types' -import { AccidentTypeEnum } from '../types' - -export const isRescueWorkAccident = (formValue: FormValue) => { - const accidentType = getValueViaPath( - formValue, - 'accidentType.radioButton', - ) as AccidentTypeEnum - return accidentType === AccidentTypeEnum.RESCUEWORK -} diff --git a/libs/application/templates/accident-notification/src/utils/isSportAccidentAndEmployee.spec.ts b/libs/application/templates/accident-notification/src/utils/isSportAccidentAndEmployee.spec.ts deleted file mode 100644 index a095aa9370ef..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isSportAccidentAndEmployee.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { AccidentTypeEnum, WorkAccidentTypeEnum } from '../types' -import exp from 'constants' -import { isSportAccidentAndEmployee } from './isSportAccidentAndEmployee' -import { accidentType } from '../lib/messages' - -describe('isSportAccidentAndEmployee', () => { - const sportAccidentRadio: FormValue = { - accidentType: { radioButton: AccidentTypeEnum.SPORTS }, - onPayRoll: { answer: 'yes' }, - } - - const someOtherAccident: FormValue = { - workAccident: { type: AccidentTypeEnum.HOMEACTIVITIES }, - onPayRoll: { answer: 'yes' }, - } - - const notOnPayroll: FormValue = { - accidentType: { radioButton: AccidentTypeEnum.SPORTS }, - onPayRoll: { answer: 'no' }, - } - - it('should return true for sport accidents where the person is also an employee of the sports club', () => { - expect(isSportAccidentAndEmployee(sportAccidentRadio)).toEqual(true) - }) - - it('should return false for other accidents', () => { - expect(isSportAccidentAndEmployee(someOtherAccident)).toEqual(false) - }) - - it('should return false if the person is not on payroll', () => { - expect(isSportAccidentAndEmployee(notOnPayroll)).toEqual(false) - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/isSportAccidentAndEmployee.ts b/libs/application/templates/accident-notification/src/utils/isSportAccidentAndEmployee.ts deleted file mode 100644 index e214ce3c414f..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isSportAccidentAndEmployee.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Answer, FormValue } from '@island.is/application/types' -import { AccidentTypeEnum, YesOrNo } from '../types' -import { getValueViaPath } from '@island.is/application/core' - -// When a person is hurt in a sports accident and is an employee of the sport, the accident -// is considered a work accident. This function checks if both conditions are met. -export const isSportAccidentAndEmployee = (formValue: FormValue): boolean => { - const workAccidentType = getValueViaPath( - formValue, - 'accidentType.radioButton', - ) as AccidentTypeEnum - const onPayRoll = getValueViaPath(formValue, 'onPayRoll.answer') as YesOrNo - - if (workAccidentType === AccidentTypeEnum.SPORTS && onPayRoll === 'yes') { - return true - } - - return false -} diff --git a/libs/application/templates/accident-notification/src/utils/isStudiesAccident.spec.ts b/libs/application/templates/accident-notification/src/utils/isStudiesAccident.spec.ts deleted file mode 100644 index b4e40c6e8e4e..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isStudiesAccident.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { AccidentTypeEnum } from '../types' -import { isStudiesAccident } from './isStudiesAccident' -describe('isStudiesAccident', () => { - const studiesAccident: FormValue = { - accidentType: { radioButton: AccidentTypeEnum.STUDIES }, - } - - const someOtherAccident: FormValue = { - accidentType: { radioButton: AccidentTypeEnum.HOMEACTIVITIES }, - } - - const emptyObject = {} - - it('should return true for studies accidents', () => { - expect(isStudiesAccident(studiesAccident)).toEqual(true) - }) - it('should return false for accidents other than studies', () => { - expect(isStudiesAccident(someOtherAccident)).toEqual(false) - }) - it('should return false for empty object', () => { - expect(isStudiesAccident(emptyObject)).toEqual(false) - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/isStudiesAccident.ts b/libs/application/templates/accident-notification/src/utils/isStudiesAccident.ts deleted file mode 100644 index 9de0fa5042f3..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isStudiesAccident.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { FormValue } from '@island.is/application/types' -import { AccidentTypeEnum } from '../types' - -export const isStudiesAccident = (formValue: FormValue) => { - const accidentType = getValueViaPath( - formValue, - 'accidentType.radioButton', - ) as AccidentTypeEnum - return accidentType === AccidentTypeEnum.STUDIES -} diff --git a/libs/application/templates/accident-notification/src/utils/isUniqueAssignee.spec.ts b/libs/application/templates/accident-notification/src/utils/isUniqueAssignee.spec.ts deleted file mode 100644 index 707eeca6673b..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isUniqueAssignee.spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { isUniqueAssignee } from './isUniqueAssignee' -describe('isUniqueAssignee', () => { - // Gervimenn nationalIds: - const applicant = '0101051450' - const assigneeSameAsApplicant = '0101051450' - const uniqueAssignee = '0102491479' - const isAssignee = true - - const applicationSameAsApplicant: FormValue = { - applicant: { - nationalId: applicant, - }, - representative: { - nationalId: assigneeSameAsApplicant, - }, - } - - const applicationUniqueAssignee: FormValue = { - applicant: { - nationalId: applicant, - }, - representative: { - nationalId: uniqueAssignee, - }, - } - - it('should return false for assignee that is the same as applicant', () => { - expect(isUniqueAssignee(applicationSameAsApplicant, isAssignee)).toEqual( - false, - ) - }) - it('should return true for assignee that is unique', () => { - expect(isUniqueAssignee(applicationUniqueAssignee, isAssignee)).toEqual( - true, - ) - }) - it('should return false for not being assignee', () => { - expect(isUniqueAssignee(applicationUniqueAssignee, !isAssignee)).toEqual( - false, - ) - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/isUniqueAssignee.ts b/libs/application/templates/accident-notification/src/utils/isUniqueAssignee.ts deleted file mode 100644 index b1ba7fe86444..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isUniqueAssignee.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { FormValue } from '@island.is/application/types' - -export const isUniqueAssignee = ( - formValue: FormValue, - isAssignee: boolean, -): boolean => { - const applicant = getValueViaPath(formValue, 'applicant.nationalId') - const assignee = getValueViaPath(formValue, 'representative.nationalId') - const isSamePerson = applicant === assignee - - return !isSamePerson && isAssignee -} diff --git a/libs/application/templates/accident-notification/src/utils/isUploadNow.spec.ts b/libs/application/templates/accident-notification/src/utils/isUploadNow.spec.ts deleted file mode 100644 index f080cf0092e5..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isUploadNow.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { - PowerOfAttorneyUploadEnum, - WhoIsTheNotificationForEnum, -} from '../types' -import { isUploadNow } from './isUploadNow' - -describe('isUploadNow', () => { - const powerOfAttorneyReporterWithUploadNow: FormValue = { - whoIsTheNotificationFor: { - answer: WhoIsTheNotificationForEnum.POWEROFATTORNEY, - }, - powerOfAttorney: { - type: PowerOfAttorneyUploadEnum.UPLOADNOW, - }, - } - - const powerOfAttorneyReporterWithUploadLater: FormValue = { - whoIsTheNotificationFor: { - answer: WhoIsTheNotificationForEnum.POWEROFATTORNEY, - }, - powerOfAttorney: { - type: PowerOfAttorneyUploadEnum.UPLOADLATER, - }, - } - - const reportingForSelf: FormValue = { - whoIsTheNotificationFor: { - answer: WhoIsTheNotificationForEnum.ME, - }, - } - - const emptyObject = {} - - it('should return true for power of attorney reporter with upload now', () => { - expect(isUploadNow(powerOfAttorneyReporterWithUploadNow)).toEqual(true) - }) - - it('should return false for power of attorney reporter with upload later', () => { - expect(isUploadNow(powerOfAttorneyReporterWithUploadLater)).toEqual(false) - }) - - it('should return false for reporting for yourself', () => { - expect(isUploadNow(reportingForSelf)).toEqual(false) - }) - - it('should return false for empty object', () => { - expect(isUploadNow(emptyObject)).toEqual(false) - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/isUploadNow.ts b/libs/application/templates/accident-notification/src/utils/isUploadNow.ts deleted file mode 100644 index 94d87d93940a..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isUploadNow.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { FormValue } from '@island.is/application/types' -import { - PowerOfAttorneyUploadEnum, - WhoIsTheNotificationForEnum, -} from '../types' - -export const isUploadNow = (formValue: FormValue) => { - const whoIsTheNotificationFor = getValueViaPath( - formValue, - 'whoIsTheNotificationFor.answer', - ) as WhoIsTheNotificationForEnum - const powerOfAttorneyType = getValueViaPath( - formValue, - 'powerOfAttorney.type', - ) as PowerOfAttorneyUploadEnum - return ( - whoIsTheNotificationFor === WhoIsTheNotificationForEnum.POWEROFATTORNEY && - powerOfAttorneyType === PowerOfAttorneyUploadEnum.UPLOADNOW - ) -} diff --git a/libs/application/templates/accident-notification/src/utils/isWorkAccident.spec.ts b/libs/application/templates/accident-notification/src/utils/isWorkAccident.spec.ts deleted file mode 100644 index a3aaae308e53..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isWorkAccident.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { FormValue } from '@island.is/application/types' -import { AccidentTypeEnum } from '../types' -import { isWorkAccident } from './isWorkAccident' -describe('isWorkAccident', () => { - const workAccident: FormValue = { - accidentType: { radioButton: AccidentTypeEnum.WORK }, - } - - const someOtherAccident: FormValue = { - accidentType: { radioButton: AccidentTypeEnum.HOMEACTIVITIES }, - } - - const emptyObject = {} - - it('should return true for work accidents', () => { - expect(isWorkAccident(workAccident)).toEqual(true) - }) - it('should return false for accidents other than work', () => { - expect(isWorkAccident(someOtherAccident)).toEqual(false) - }) - it('should return false for empty object', () => { - expect(isWorkAccident(emptyObject)).toEqual(false) - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/isWorkAccident.ts b/libs/application/templates/accident-notification/src/utils/isWorkAccident.ts deleted file mode 100644 index d88e055641be..000000000000 --- a/libs/application/templates/accident-notification/src/utils/isWorkAccident.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { getValueViaPath } from '@island.is/application/core' -import { FormValue } from '@island.is/application/types' -import { AccidentTypeEnum } from '../types' - -export const isWorkAccident = (formValue: FormValue) => { - const accidentType = getValueViaPath( - formValue, - 'accidentType.radioButton', - ) as AccidentTypeEnum - return accidentType === AccidentTypeEnum.WORK -} diff --git a/libs/application/templates/accident-notification/src/utils/miscUtils.spec.ts b/libs/application/templates/accident-notification/src/utils/miscUtils.spec.ts new file mode 100644 index 000000000000..a415c9b0cc91 --- /dev/null +++ b/libs/application/templates/accident-notification/src/utils/miscUtils.spec.ts @@ -0,0 +1,313 @@ +import { + formatPhonenumber, + hasReceivedConfirmation, + hideLocationAndPurpose, + isHealthInsured, + isInjuredAndRepresentativeOfCompanyOrInstitute, + isPowerOfAttorney, + isRepresentativeOfCompanyOrInstitute, + isUniqueAssignee, + shouldRequestReview, +} from './miscUtils' +import { FormValue, NO, YES } from '@island.is/application/types' +import { + AccidentTypeEnum, + GeneralWorkplaceAccidentLocationEnum, + StudiesAccidentLocationEnum, + WhoIsTheNotificationForEnum, + WorkAccidentTypeEnum, +} from '../types' +import { AccidentNotificationAnswers } from '..' + +describe('formatPhonenumber', () => { + it.each([ + { input: '1234567', expected: '123-4567' }, + { input: '1234567891011', expected: '123-4567891011' }, + { input: 'ABCDEF@!()', expected: 'ABC-DEF@!()' }, + { input: '123', expected: '123' }, + ])('should format phone number', ({ input, expected }) => { + const result = formatPhonenumber(input) + expect(result).toBe(expected) + }) +}) + +describe('hasReceivedConfirmation', () => { + const confirmedJuridicial: FormValue = { + whoIsTheNotificationFor: { + answer: WhoIsTheNotificationForEnum.JURIDICALPERSON, + }, + accidentStatus: { + receivedConfirmations: { + InjuredOrRepresentativeParty: true, + }, + }, + } + + const confirmedMe: FormValue = { + whoIsTheNotificationFor: { + answer: WhoIsTheNotificationForEnum.ME, + }, + accidentStatus: { + receivedConfirmations: { + CompanyParty: true, + }, + }, + } + + const notConfirmed: FormValue = { + accidentStatus: { + receivedConfirmations: false, + }, + } + + it.each([ + { for: 'juridical person', input: confirmedJuridicial, expected: true }, + { for: 'company', input: confirmedMe, expected: true }, + { for: 'not confirmed', input: notConfirmed, expected: false }, + ])( + 'should return $expected when confirmation is $for', + ({ input, expected }) => { + expect(hasReceivedConfirmation(input)).toEqual(expected) + }, + ) +}) + +describe('hideLocationAndPurpose', () => { + const accidentLocationAtWorkplace: FormValue = { + accidentLocation: { + answer: GeneralWorkplaceAccidentLocationEnum.ATTHEWORKPLACE, + }, + } + + const accidentLocationAtSchoole: FormValue = { + accidentLocation: { answer: StudiesAccidentLocationEnum.ATTHESCHOOL }, + } + + const someOtherLocation: FormValue = { + accidentLocation: { answer: GeneralWorkplaceAccidentLocationEnum.OTHER }, + } + + const emptyObject = {} + + it('should return true for accident location at workplace', () => { + expect(hideLocationAndPurpose(accidentLocationAtWorkplace)).toEqual(true) + }) + it('should return true for accident location at school', () => { + expect(hideLocationAndPurpose(accidentLocationAtSchoole)).toEqual(true) + }) + it('should return false for accident location elsewhere', () => { + expect(hideLocationAndPurpose(someOtherLocation)).toEqual(false) + }) + it('should return false for empty object', () => { + expect(hideLocationAndPurpose(emptyObject)).toEqual(false) + }) +}) + +describe('isHealthInsured', () => { + const healthInsured = { + accidentDetails: { + isHealthInsured: 'yes', + }, + } + + const notHealthInsured = { + accidentDetails: { + isHealthInsured: 'no', + }, + } + + it('should return true when health insured is yes', () => { + expect(isHealthInsured(healthInsured)).toEqual(true) + }) + + it('should return false when health insured is no', () => { + expect(isHealthInsured(notHealthInsured)).toEqual(false) + }) + + it('should return true when health insured is undefined', () => { + expect(isHealthInsured({})).toEqual(true) + }) +}) + +describe('isPowerOfAttorney', () => { + const powerOfAttorneyReporter: FormValue = { + whoIsTheNotificationFor: { + answer: WhoIsTheNotificationForEnum.POWEROFATTORNEY, + }, + } + + const juridicialPersonReporter: FormValue = { + whoIsTheNotificationFor: { + answer: WhoIsTheNotificationForEnum.JURIDICALPERSON, + }, + } + + const reportingForSelf: FormValue = { + whoIsTheNotificationFor: { answer: WhoIsTheNotificationForEnum.ME }, + } + + const emptyObject = {} + + it('should return true for power of attorney reporter', () => { + expect(isPowerOfAttorney(powerOfAttorneyReporter)).toEqual(true) + }) + + it('should return false for power of juridical person reporter', () => { + expect(isPowerOfAttorney(juridicialPersonReporter)).toEqual(false) + }) + + it('should return false for reporting for yourself', () => { + expect(isPowerOfAttorney(reportingForSelf)).toEqual(false) + }) + + it('should return false for empty object', () => { + expect(isPowerOfAttorney(emptyObject)).toEqual(false) + }) +}) + +describe('isRepresentativeOfCompanyOrInstitute', () => { + const representative: FormValue = { + whoIsTheNotificationFor: { + answer: WhoIsTheNotificationForEnum.JURIDICALPERSON, + }, + } + + const notRepresentative: FormValue = { + whoIsTheNotificationFor: { + answer: WhoIsTheNotificationForEnum.ME, + }, + } + + const emptyRepresentative: FormValue = { + whoIsTheNotificationFor: {}, + } + + it('should return true for someone that is a representative of the company or institue', () => { + expect(isRepresentativeOfCompanyOrInstitute(representative)).toEqual(true) + }) + it('should return false for someone that isnt a representative of the company or institue', () => { + expect(isRepresentativeOfCompanyOrInstitute(notRepresentative)).toEqual( + false, + ) + }) + it('should return false for empty object', () => { + expect(isRepresentativeOfCompanyOrInstitute(emptyObject)).toEqual(false) + }) + it('should return false for empty whoIsTheNotificationFor', () => { + expect(isRepresentativeOfCompanyOrInstitute(emptyRepresentative)).toEqual( + false, + ) + }) +}) + +const emptyObject = {} + +describe('isInjuredAndRepresentativeOfCompanyOrInstitute', () => { + const representative: FormValue = { + isRepresentativeOfCompanyOrInstitute: YES, + } + + const notRepresentative: FormValue = { + isRepresentativeOfCompanyOrInstitute: NO, + } + + it('should return true for someone that is a representative of the company or institute', () => { + expect( + isInjuredAndRepresentativeOfCompanyOrInstitute(representative), + ).toEqual(true) + }) + + it('should return false for someone that isnt a representative of the company or institute', () => { + expect( + isInjuredAndRepresentativeOfCompanyOrInstitute(notRepresentative), + ).toEqual(false) + }) + + it('should return false for empty object', () => { + expect(isInjuredAndRepresentativeOfCompanyOrInstitute(emptyObject)).toEqual( + false, + ) + }) + + it('should return false for garbage string', () => { + expect( + isInjuredAndRepresentativeOfCompanyOrInstitute({ + isRepresentativeOfCompanyOrInstitute: 'garbage', + }), + ).toEqual(false) + }) + + it('should return false for object with non string value', () => { + expect( + isInjuredAndRepresentativeOfCompanyOrInstitute({ + isRepresentativeOfCompanyOrInstitute: true, + }), + ).toEqual(false) + }) +}) + +describe('isUniqueAssignee', () => { + // Gervimenn nationalIds: + const applicant = '0101051450' + const assigneeSameAsApplicant = '0101051450' + const uniqueAssignee = '0102491479' + const isAssignee = true + + const applicationSameAsApplicant: FormValue = { + applicant: { + nationalId: applicant, + }, + representative: { + nationalId: assigneeSameAsApplicant, + }, + } + + const applicationUniqueAssignee: FormValue = { + applicant: { + nationalId: applicant, + }, + representative: { + nationalId: uniqueAssignee, + }, + } + + it('should return false for assignee that is the same as applicant', () => { + expect(isUniqueAssignee(applicationSameAsApplicant, isAssignee)).toEqual( + false, + ) + }) + it('should return true for assignee that is unique', () => { + expect(isUniqueAssignee(applicationUniqueAssignee, isAssignee)).toEqual( + true, + ) + }) + it('should return false for not being assignee', () => { + expect(isUniqueAssignee(applicationUniqueAssignee, !isAssignee)).toEqual( + false, + ) + }) +}) + +describe('shouldRequestReview', () => { + const agricultureAccident: Partial = { + workAccident: { type: WorkAccidentTypeEnum.AGRICULTURE }, + } + + const accidentAtHome: Partial = { + accidentType: { radioButton: AccidentTypeEnum.HOMEACTIVITIES }, + } + + const normalWorkAccident: Partial = { + workAccident: { type: WorkAccidentTypeEnum.GENERAL }, + } + + it('should return false for work accidents', () => { + expect(shouldRequestReview(agricultureAccident)).toEqual(false) + }) + it('should return true for general work accident', () => { + expect(shouldRequestReview(normalWorkAccident)).toEqual(true) + }) + it('should return false for home accident', () => { + expect(shouldRequestReview(accidentAtHome)).toEqual(false) + }) +}) diff --git a/libs/application/templates/accident-notification/src/utils/miscUtils.ts b/libs/application/templates/accident-notification/src/utils/miscUtils.ts new file mode 100644 index 000000000000..5ae53a9533cc --- /dev/null +++ b/libs/application/templates/accident-notification/src/utils/miscUtils.ts @@ -0,0 +1,137 @@ +import { getValueViaPath } from '@island.is/application/core' +import { FormValue, YES } from '@island.is/application/types' +import { isReportingOnBehalfOfEmployee as isReportingOnBehalfOfEmployeeOrginal } from './reportingUtils' +import { + GeneralWorkplaceAccidentLocationEnum, + ProfessionalAthleteAccidentLocationEnum, + StudiesAccidentLocationEnum, + WhoIsTheNotificationForEnum, + WorkAccidentTypeEnum, + YesOrNo, +} from '../types' +import { + getInjuredPersonInformation as getInjuredPersonInformationOrginal, + isHomeActivitiesAccident, +} from './accidentUtils' +import { AccidentNotificationAnswers } from '..' +import { + getWorkplaceData as getWorkplaceDataOrginal, + isOfWorkAccidentType, +} from './occupationUtils' + +export const formatPhonenumber = (value: string) => { + const splitAt = (index: number) => (x: string) => + [x.slice(0, index), x.slice(index)] + if (value.length > 3) return splitAt(3)(value).join('-') + return value +} + +export const hasReceivedConfirmation = (answers: FormValue) => { + // The fetched value is actually typed as AccidentNotificationConfirmation, but importing that type breaks when codegen is run after cleanup + const accidentConfirmations = getValueViaPath( + answers, + 'accidentStatus.receivedConfirmations', + ) as { + InjuredOrRepresentativeParty: boolean | undefined + CompanyParty: boolean | undefined + } + + // if juridical person then the injured or the power of attorney holder has to confirm + if (isReportingOnBehalfOfEmployee(answers)) { + return !!accidentConfirmations.InjuredOrRepresentativeParty + } + + // as there isn't an juridical person reporting, this must be someone reporting for the injured + // or the injured himself and that requires the companies confirmation + return !!accidentConfirmations.CompanyParty +} + +// Location and purpose of accident only relevant in work and studies and never in home +// activities +export const hideLocationAndPurpose = (formValue: FormValue) => { + const answer = getValueViaPath(formValue, 'accidentLocation.answer') as + | GeneralWorkplaceAccidentLocationEnum + | StudiesAccidentLocationEnum + | ProfessionalAthleteAccidentLocationEnum + + if (isHomeActivitiesAccident(formValue)) { + return true + } + return ( + answer === GeneralWorkplaceAccidentLocationEnum.ATTHEWORKPLACE || + answer === StudiesAccidentLocationEnum.ATTHESCHOOL || + answer === ProfessionalAthleteAccidentLocationEnum.SPORTCLUBSFACILITES + ) +} + +export const isHealthInsured = (formValue: FormValue) => { + const isHealthInsured = getValueViaPath( + formValue, + 'accidentDetails.isHealthInsured', + ) + if (isHealthInsured === undefined) return true + return isHealthInsured === 'yes' +} + +export const isPowerOfAttorney = (formValue: FormValue) => { + const reportingOnBehalfType = getValueViaPath( + formValue, + 'whoIsTheNotificationFor.answer', + ) + return reportingOnBehalfType === WhoIsTheNotificationForEnum.POWEROFATTORNEY +} + +export const isRepresentativeOfCompanyOrInstitute = (formValue: FormValue) => { + return ( + getValueViaPath( + formValue, + 'whoIsTheNotificationFor.answer', + ) === WhoIsTheNotificationForEnum.JURIDICALPERSON + ) +} + +export const isInjuredAndRepresentativeOfCompanyOrInstitute = ( + formValue: FormValue, +) => { + return formValue.isRepresentativeOfCompanyOrInstitute?.toString() === YES +} + +export const isUniqueAssignee = ( + formValue: FormValue, + isAssignee: boolean, +): boolean => { + const applicant = getValueViaPath(formValue, 'applicant.nationalId') + const assignee = getValueViaPath( + formValue, + 'representative.nationalId', + ) + const isSamePerson = applicant === assignee + + return !isSamePerson && isAssignee +} + +export const shouldRequestReview = ( + answers: Partial, +): boolean => { + const ishome = isHomeActivitiesAccident(answers) + const isAgriculture = isOfWorkAccidentType( + answers, + WorkAccidentTypeEnum.AGRICULTURE, + ) + + const isEitherHomeOrAgriculture = ishome || isAgriculture + + return !isEitherHomeOrAgriculture +} + +export const isReportingOnBehalfOfEmployee = (answers: FormValue) => { + return isReportingOnBehalfOfEmployeeOrginal(answers) +} + +export const getWorkplaceData = (answers: FormValue) => { + return getWorkplaceDataOrginal(answers) +} + +export const getInjuredPersonInformation = (answers: FormValue) => { + return getInjuredPersonInformationOrginal(answers) +} diff --git a/libs/application/templates/accident-notification/src/utils/occupationUtils.spec.ts b/libs/application/templates/accident-notification/src/utils/occupationUtils.spec.ts new file mode 100644 index 000000000000..4a76aafb7521 --- /dev/null +++ b/libs/application/templates/accident-notification/src/utils/occupationUtils.spec.ts @@ -0,0 +1,312 @@ +import { FormValue, NO, YES } from '@island.is/application/types' +import { + AccidentTypeEnum, + FishermanWorkplaceAccidentLocationEnum, + StudiesAccidentTypeEnum, + WorkAccidentTypeEnum, +} from '../types' +import { + getWorkplaceData, + isAboardShip, + isAgricultureAccident, + isFishermanAccident, + isGeneralWorkplaceAccident, + isInternshipStudiesAccident, + isMachineRelatedAccident, + isProfessionalAthleteAccident, + isSportAccidentAndEmployee, +} from './occupationUtils' + +describe('isAboardShip', () => { + const onTheShipLocation: FormValue = { + accidentLocation: { + answer: FishermanWorkplaceAccidentLocationEnum.ONTHESHIP, + }, + workAccident: { type: WorkAccidentTypeEnum.FISHERMAN }, + accidentType: { radioButton: AccidentTypeEnum.WORK }, + } + + const someOtherLocation: FormValue = { + accidentLocation: { answer: FishermanWorkplaceAccidentLocationEnum.OTHER }, + workAccident: { type: WorkAccidentTypeEnum.FISHERMAN }, + accidentType: { radioButton: AccidentTypeEnum.WORK }, + } + + const emptyObject = {} + + it('should return true for fisherman work accident that happens on a ship', () => { + expect(isAboardShip(onTheShipLocation)).toEqual(true) + }) + it('should return false for fisherman work accident that happens else where', () => { + expect(isAboardShip(someOtherLocation)).toEqual(false) + }) + it('should return false for empty object', () => { + expect(isAboardShip(emptyObject)).toEqual(false) + }) +}) + +describe('getWorkplaceData', () => { + const generalWorkplaceAccident: FormValue = { + workAccident: { type: WorkAccidentTypeEnum.GENERAL }, + accidentType: { radioButton: AccidentTypeEnum.WORK }, + companyInfo: {}, + } + + const professionalAthleteAccident: FormValue = { + accidentType: { radioButton: AccidentTypeEnum.SPORTS }, + companyInfo: { onPayRoll: { answer: YES } }, + } + + const rescueWorkAccident: FormValue = { + accidentType: { radioButton: AccidentTypeEnum.RESCUEWORK }, + } + + const studiesAccident: FormValue = { + accidentType: { radioButton: AccidentTypeEnum.STUDIES }, + } + + const fishermanAccident: FormValue = { + workAccident: { type: WorkAccidentTypeEnum.FISHERMAN }, + accidentType: { radioButton: AccidentTypeEnum.WORK }, + } + + const emptyObject = {} + + it('should return general work type data for general work accidents', () => { + expect(getWorkplaceData(generalWorkplaceAccident)?.type).toEqual( + AccidentTypeEnum.WORK, + ) + }) + + it('should return sports type data for professional athlete accidents', () => { + expect(getWorkplaceData(professionalAthleteAccident)?.type).toEqual( + AccidentTypeEnum.SPORTS, + ) + }) + + it('should return employee information for professional athlete accidents', () => { + expect( + getWorkplaceData(professionalAthleteAccident)?.companyInfo.onPayRoll + ?.answer, + ).toBe(YES) + }) + + it('should not return employee information for general workplace accident', () => { + expect( + getWorkplaceData(generalWorkplaceAccident)?.companyInfo.onPayRoll, + ).toBe(undefined) + }) + + it('should return rescue work type data for rescue work accidents', () => { + expect(getWorkplaceData(rescueWorkAccident)?.type).toEqual( + AccidentTypeEnum.RESCUEWORK, + ) + }) + + it('should return studies type data for student accidents', () => { + expect(getWorkplaceData(studiesAccident)?.type).toEqual( + AccidentTypeEnum.STUDIES, + ) + }) + + it('should return fisherman type data for fisherman accidents', () => { + expect(getWorkplaceData(fishermanAccident)?.type).toEqual( + WorkAccidentTypeEnum.FISHERMAN, + ) + }) + + it('should return undefined for empty object', () => { + expect(getWorkplaceData(emptyObject)?.type).toEqual(undefined) + }) +}) + +describe('isInternshipStudiesAccident', () => { + const studiesAccidentType: FormValue = { + studiesAccident: { type: StudiesAccidentTypeEnum.INTERNSHIP }, + } + + const someOtherAccidentType: FormValue = { + studiesAccident: { type: StudiesAccidentTypeEnum.APPRENTICESHIP }, + } + + const emptyObject = {} + + it('should return true for studies accidents', () => { + expect(isInternshipStudiesAccident(studiesAccidentType)).toEqual(true) + }) + it('should return false for accidents other than studies', () => { + expect(isInternshipStudiesAccident(someOtherAccidentType)).toEqual(false) + }) + it('should return false for empty object', () => { + expect(isInternshipStudiesAccident(emptyObject)).toEqual(false) + }) +}) + +describe('isMachineRelatedAccident', () => { + const machineRelatedAccident: FormValue = { + workAccident: { type: WorkAccidentTypeEnum.GENERAL }, + accidentType: { radioButton: AccidentTypeEnum.WORK }, + workMachineRadio: YES, + } + + const nonMachineRelatedAccident: FormValue = { + workAccident: { type: WorkAccidentTypeEnum.GENERAL }, + accidentType: { radioButton: AccidentTypeEnum.WORK }, + workMachineRadio: NO, + } + + const someOtherAccident: FormValue = { + workAccident: { type: WorkAccidentTypeEnum.PROFESSIONALATHLETE }, + } + + const emptyObject = {} + + it('should return true for machine related general work accidents', () => { + expect(isMachineRelatedAccident(machineRelatedAccident)).toEqual(true) + }) + it('should return false for non machine related general work accidents', () => { + expect(isMachineRelatedAccident(nonMachineRelatedAccident)).toEqual(false) + }) + it('should return false for workplace accidents other than general', () => { + expect(isMachineRelatedAccident(someOtherAccident)).toEqual(false) + }) + it('should return false for empty object', () => { + expect(isMachineRelatedAccident(emptyObject)).toEqual(false) + }) +}) + +describe('isSportAccidentAndEmployee', () => { + const sportAccidentRadio: FormValue = { + accidentType: { radioButton: AccidentTypeEnum.SPORTS }, + onPayRoll: { answer: 'yes' }, + } + + const someOtherAccident: FormValue = { + workAccident: { type: AccidentTypeEnum.HOMEACTIVITIES }, + onPayRoll: { answer: 'yes' }, + } + + const notOnPayroll: FormValue = { + accidentType: { radioButton: AccidentTypeEnum.SPORTS }, + onPayRoll: { answer: 'no' }, + } + + it('should return true for sport accidents where the person is also an employee of the sports club', () => { + expect(isSportAccidentAndEmployee(sportAccidentRadio)).toEqual(true) + }) + + it('should return false for other accidents', () => { + expect(isSportAccidentAndEmployee(someOtherAccident)).toEqual(false) + }) + + it('should return false if the person is not on payroll', () => { + expect(isSportAccidentAndEmployee(notOnPayroll)).toEqual(false) + }) +}) + +describe('isFishermanAccident', () => { + const fishermanAccident: FormValue = { + workAccident: { type: WorkAccidentTypeEnum.FISHERMAN }, + accidentType: { radioButton: AccidentTypeEnum.WORK }, + } + + const someOtherAccident: FormValue = { + workAccident: { type: WorkAccidentTypeEnum.PROFESSIONALATHLETE }, + } + + const emptyObject = {} + + it('should return true for fisherman accidents', () => { + expect(isFishermanAccident(fishermanAccident)).toEqual(true) + }) + it('should return false for workplace accidents other than fisherman', () => { + expect(isFishermanAccident(someOtherAccident)).toEqual(false) + }) + it('should return false for empty object', () => { + expect(isFishermanAccident(emptyObject)).toEqual(false) + }) +}) + +describe('isAgricultureAccident', () => { + const agricultureAccident: FormValue = { + workAccident: { type: WorkAccidentTypeEnum.AGRICULTURE }, + accidentType: { radioButton: AccidentTypeEnum.WORK }, + } + + const someOtherAccident: FormValue = { + workAccident: { type: WorkAccidentTypeEnum.PROFESSIONALATHLETE }, + } + + const emptyObject = {} + + it('should return true for agriculture accidents', () => { + expect(isAgricultureAccident(agricultureAccident)).toEqual(true) + }) + it('should return false for workplace accidents other than agriculture', () => { + expect(isAgricultureAccident(someOtherAccident)).toEqual(false) + }) + it('should return false for empty object', () => { + expect(isAgricultureAccident(emptyObject)).toEqual(false) + }) +}) + +describe('isProfessionalAthleteAccident', () => { + const professionalAthleteAccidentRadio: FormValue = { + accidentType: { radioButton: AccidentTypeEnum.SPORTS }, + } + + const professionalAthleteAccidentSecondaryWorkQuestion: FormValue = { + workAccident: { type: WorkAccidentTypeEnum.PROFESSIONALATHLETE }, + accidentType: { radioButton: AccidentTypeEnum.WORK }, + } + + const someOtherAccident: FormValue = { + workAccident: { type: WorkAccidentTypeEnum.FISHERMAN }, + } + + const emptyObject = {} + + it('should return true for professional athlete accidents', () => { + expect( + isProfessionalAthleteAccident(professionalAthleteAccidentRadio), + ).toEqual(true) + }) + + it('should return true for professional athlete accident when user picked work related and then sports related', () => { + expect( + isProfessionalAthleteAccident( + professionalAthleteAccidentSecondaryWorkQuestion, + ), + ).toEqual(true) + }) + + it('should return false for workplace accidents other than professional athlete', () => { + expect(isProfessionalAthleteAccident(someOtherAccident)).toEqual(false) + }) + it('should return false for empty object', () => { + expect(isProfessionalAthleteAccident(emptyObject)).toEqual(false) + }) +}) + +describe('isGeneralWorkplaceAccident', () => { + const generalWorkplaceAccident: FormValue = { + workAccident: { type: WorkAccidentTypeEnum.GENERAL }, + accidentType: { radioButton: AccidentTypeEnum.WORK }, + } + + const someOtherAccident: FormValue = { + workAccident: { type: WorkAccidentTypeEnum.PROFESSIONALATHLETE }, + } + + const emptyObject = {} + + it('should return true for general workplace accidents', () => { + expect(isGeneralWorkplaceAccident(generalWorkplaceAccident)).toEqual(true) + }) + it('should return false for workplace accidents other than general', () => { + expect(isGeneralWorkplaceAccident(someOtherAccident)).toEqual(false) + }) + it('should return false for empty object', () => { + expect(isGeneralWorkplaceAccident(emptyObject)).toEqual(false) + }) +}) diff --git a/libs/application/templates/accident-notification/src/utils/occupationUtils.ts b/libs/application/templates/accident-notification/src/utils/occupationUtils.ts new file mode 100644 index 000000000000..79351222b877 --- /dev/null +++ b/libs/application/templates/accident-notification/src/utils/occupationUtils.ts @@ -0,0 +1,203 @@ +import { getValueViaPath, YES } from '@island.is/application/core' +import { FormValue } from '@island.is/application/types' +import { + AccidentTypeEnum, + CompanyInfo, + FishermanWorkplaceAccidentLocationEnum, + RepresentativeInfo, + StudiesAccidentTypeEnum, + WorkAccidentTypeEnum, + WorkplaceData, + YesOrNo, +} from '../types' +import { + isHomeActivitiesAccident, + isRescueWorkAccident, + isStudiesAccident, + isWorkAccident, +} from './accidentUtils' +import { + companyInfo, + fishingCompanyInfo, + representativeInfo, + rescueSquadInfo, + schoolInfo, + sportsClubInfo, +} from '../lib/messages' +import { AccidentNotificationAnswers } from '..' + +// As this is a third question the user is asked there is a case where he could go back +// and select home activities and keep the workaccident type or go back and change where the +// accident happened. +// Therefore we need to check ifFishermanAccident function again +export const isAboardShip = (formValue: FormValue) => { + const fishermanWorkplaceAccidentLocationAnswer = + getValueViaPath( + formValue, + 'accidentLocation.answer', + ) + return ( + isFishermanAccident(formValue) && + fishermanWorkplaceAccidentLocationAnswer === + FishermanWorkplaceAccidentLocationEnum.ONTHESHIP + ) +} + +export const getWorkplaceData = ( + answers: FormValue, +): WorkplaceData | undefined => { + if (isHomeActivitiesAccident(answers)) { + return + } + + const workplaceData = { + companyInfo: getValueViaPath(answers, 'companyInfo'), + representitive: getValueViaPath( + answers, + 'representative', + ), + representitiveMsg: representativeInfo, + } as WorkplaceData + + if (isGeneralWorkplaceAccident(answers)) + return { + ...workplaceData, + companyInfoMsg: companyInfo, + type: AccidentTypeEnum.WORK, + screenId: 'companyInfo', + } + + if (isStudiesAccident(answers)) + return { + ...workplaceData, + companyInfoMsg: schoolInfo, + type: AccidentTypeEnum.STUDIES, + screenId: 'schoolInfo', + } + + if (isFishermanAccident(answers)) + return { + ...workplaceData, + companyInfoMsg: fishingCompanyInfo, + type: WorkAccidentTypeEnum.FISHERMAN, + screenId: 'fishingCompanyInfo', + } + + if (isProfessionalAthleteAccident(answers)) + return { + ...workplaceData, + onPayRoll: getValueViaPath(answers, 'onPayRoll.answer'), + companyInfoMsg: sportsClubInfo, + type: AccidentTypeEnum.SPORTS, + screenId: 'sportsClubInfo', + } + + if (isRescueWorkAccident(answers)) + return { + ...workplaceData, + companyInfoMsg: rescueSquadInfo, + type: AccidentTypeEnum.RESCUEWORK, + screenId: 'rescueSquad', + } +} + +export const isInternshipStudiesAccident = (formValue: FormValue) => { + const studiesAccidentType = getValueViaPath( + formValue, + 'studiesAccident.type', + ) + return studiesAccidentType === StudiesAccidentTypeEnum.INTERNSHIP +} + +export const isMachineRelatedAccident = (formValue: FormValue) => { + const workMachineAnswer = getValueViaPath( + formValue, + 'workMachineRadio', + ) + return isGeneralWorkplaceAccident(formValue) && workMachineAnswer === YES +} + +// When a person is hurt in a sports accident and is an employee of the sport, the accident +// is considered a work accident. This function checks if both conditions are met. +export const isSportAccidentAndEmployee = (formValue: FormValue): boolean => { + const workAccidentType = getValueViaPath( + formValue, + 'accidentType.radioButton', + ) as AccidentTypeEnum + const onPayRoll = getValueViaPath(formValue, 'onPayRoll.answer') as YesOrNo + + if (workAccidentType === AccidentTypeEnum.SPORTS && onPayRoll === 'yes') { + return true + } + + return false +} + +// As this is a second question the user is asked there is a case where he could go back and select home activities and keep the workaccident type. +// Therefore we need to check also whether this is a work accident +export const isFishermanAccident = (formValue: FormValue) => { + const workAccidentType = getValueViaPath( + formValue, + 'workAccident.type', + ) + return ( + workAccidentType === WorkAccidentTypeEnum.FISHERMAN && + isWorkAccident(formValue) + ) +} + +// As this is a second question the user is asked there is a case where he could go back and select home activities and keep the agriculture type. +// Therefore we need to check also whether this is a work accident +export const isAgricultureAccident = (formValue: FormValue) => { + const workAccidentType = getValueViaPath( + formValue, + 'workAccident.type', + ) + return ( + workAccidentType === WorkAccidentTypeEnum.AGRICULTURE && + isWorkAccident(formValue) + ) +} + +// Specific case check here since the accident can be a sports accident if he picks sports in the first question where +// he is asked what the circumstances of the accident were. But that user could also select work and then a sport related +// accident since the question can be missunderstood by the user so we are funneling both cases into the same flow +export const isProfessionalAthleteAccident = (formValue: FormValue) => { + const workAccidentType = getValueViaPath( + formValue, + 'accidentType.radioButton', + ) + const workAccidentSecondaryType = getValueViaPath( + formValue, + 'workAccident.type', + ) + return ( + workAccidentType === AccidentTypeEnum.SPORTS || + (workAccidentSecondaryType === WorkAccidentTypeEnum.PROFESSIONALATHLETE && + isWorkAccident(formValue)) + ) +} + +// As this is a second question the user is asked there is a case where he could go back and select home activities and keep the workaccident type. +// Therefore we need to check also whether this is a work accident +export const isGeneralWorkplaceAccident = (formValue: FormValue) => { + const workAccidentType = getValueViaPath( + formValue, + 'workAccident.type', + ) + return ( + workAccidentType === WorkAccidentTypeEnum.GENERAL && + isWorkAccident(formValue) + ) +} + +export const isOfWorkAccidentType = ( + answers: Partial, + type: WorkAccidentTypeEnum, +) => { + const workAccidentType = getValueViaPath( + answers, + 'workAccident.type', + ) + return workAccidentType === type +} diff --git a/libs/application/templates/accident-notification/src/utils/reportingUtils.spec.ts b/libs/application/templates/accident-notification/src/utils/reportingUtils.spec.ts new file mode 100644 index 000000000000..1cad36fc260c --- /dev/null +++ b/libs/application/templates/accident-notification/src/utils/reportingUtils.spec.ts @@ -0,0 +1,122 @@ +import { FormValue } from '@island.is/application/types' +import { WhoIsTheNotificationForEnum } from '../types' +import { + isReportingOnBehalfOfChild, + isReportingOnBehalfOfEmployee, + isReportingOnBehalfOfInjured, + isReportingOnBehalfSelf, +} from './reportingUtils' + +describe('isReportingOnBehalfOfInjured', () => { + const powerOfAttorneyReporter: FormValue = { + whoIsTheNotificationFor: { + answer: WhoIsTheNotificationForEnum.POWEROFATTORNEY, + }, + } + + const juridicialPersonReporter: FormValue = { + whoIsTheNotificationFor: { + answer: WhoIsTheNotificationForEnum.JURIDICALPERSON, + }, + } + + const reportingForSelf: FormValue = { + whoIsTheNotificationFor: { answer: WhoIsTheNotificationForEnum.ME }, + } + + const emptyObject = {} + + it('should return true for power of attorney reporter', () => { + expect(isReportingOnBehalfOfInjured(powerOfAttorneyReporter)).toEqual(true) + }) + + it('should return true for power of juridical person reporter', () => { + expect(isReportingOnBehalfOfInjured(juridicialPersonReporter)).toEqual(true) + }) + + it('should return false for reporting for yourself', () => { + expect(isReportingOnBehalfOfInjured(reportingForSelf)).toEqual(false) + }) + + it('should return false for empty object', () => { + expect(isReportingOnBehalfOfInjured(emptyObject)).toEqual(false) + }) +}) + +describe('isReportingOnBehalfOfEmployee', () => { + const onBehalfOfEmployee: FormValue = { + whoIsTheNotificationFor: { + answer: WhoIsTheNotificationForEnum.JURIDICALPERSON, + }, + } + + const onBehalfOfSomeOtherPerson: FormValue = { + whoIsTheNotificationFor: { + answer: WhoIsTheNotificationForEnum.POWEROFATTORNEY, + }, + } + + const emptyObject = {} + + it('should return true if reporting on behalf of employee', () => { + expect(isReportingOnBehalfOfEmployee(onBehalfOfEmployee)).toEqual(true) + }) + it('should return false if reporting on behalf of some other person', () => { + expect(isReportingOnBehalfOfEmployee(onBehalfOfSomeOtherPerson)).toEqual( + false, + ) + }) + it('should return false for empty object', () => { + expect(isReportingOnBehalfOfEmployee(emptyObject)).toEqual(false) + }) +}) + +describe('isReportingOnBehalfOfChild', () => { + const onBehalfOfChild: FormValue = { + whoIsTheNotificationFor: { + answer: WhoIsTheNotificationForEnum.CHILDINCUSTODY, + }, + } + + const onBehalfOfSomeOtherPerson: FormValue = { + whoIsTheNotificationFor: { + answer: WhoIsTheNotificationForEnum.POWEROFATTORNEY, + }, + } + + const emptyObject = {} + + it('should return true if reporting on behalf of child', () => { + expect(isReportingOnBehalfOfChild(onBehalfOfChild)).toEqual(true) + }) + it('should return false if reporting on behalf of some other person', () => { + expect(isReportingOnBehalfOfChild(onBehalfOfSomeOtherPerson)).toEqual(false) + }) + it('should return false for empty object', () => { + expect(isReportingOnBehalfOfChild(emptyObject)).toEqual(false) + }) +}) + +describe('isRepresentativeOfCompanyOrInstitute', () => { + const self: FormValue = { + whoIsTheNotificationFor: { + answer: WhoIsTheNotificationForEnum.ME, + }, + } + + const notSelf: FormValue = { + whoIsTheNotificationFor: { + answer: WhoIsTheNotificationForEnum.POWEROFATTORNEY, + }, + } + + it('should return true when someone is reporting on behalf of themselves', () => { + expect(isReportingOnBehalfSelf(self)).toEqual(true) + }) + it('should return false when someone is not reporting on behalf of themselves', () => { + expect(isReportingOnBehalfSelf(notSelf)).toEqual(false) + }) + it('should return false for empty object', () => { + expect(isReportingOnBehalfSelf({})).toEqual(false) + }) +}) diff --git a/libs/application/templates/accident-notification/src/utils/reportingUtils.ts b/libs/application/templates/accident-notification/src/utils/reportingUtils.ts new file mode 100644 index 000000000000..ecba491760ad --- /dev/null +++ b/libs/application/templates/accident-notification/src/utils/reportingUtils.ts @@ -0,0 +1,38 @@ +import { getValueViaPath } from '@island.is/application/core' +import { FormValue } from '@island.is/application/types' +import { WhoIsTheNotificationForEnum } from '../types' + +export const isReportingOnBehalfOfInjured = (formValue: FormValue) => { + const whoIsTheNotificationFor = getValueViaPath( + formValue, + 'whoIsTheNotificationFor.answer', + ) + return ( + whoIsTheNotificationFor === WhoIsTheNotificationForEnum.JURIDICALPERSON || + whoIsTheNotificationFor === WhoIsTheNotificationForEnum.POWEROFATTORNEY + ) +} + +export const isReportingOnBehalfOfEmployee = (formValue: FormValue) => { + const whoIsTheNotificationFor = getValueViaPath( + formValue, + 'whoIsTheNotificationFor.answer', + ) + return whoIsTheNotificationFor === WhoIsTheNotificationForEnum.JURIDICALPERSON +} + +export const isReportingOnBehalfOfChild = (formValue: FormValue) => { + const whoIsTheNotificationFor = getValueViaPath( + formValue, + 'whoIsTheNotificationFor.answer', + ) + return whoIsTheNotificationFor === WhoIsTheNotificationForEnum.CHILDINCUSTODY +} + +export const isReportingOnBehalfSelf = (formValue: FormValue) => { + const whoIsTheNotificationFor = getValueViaPath( + formValue, + 'whoIsTheNotificationFor.answer', + ) + return whoIsTheNotificationFor === WhoIsTheNotificationForEnum.ME +} diff --git a/libs/application/templates/accident-notification/src/utils/shouldRequestReview.spec.ts b/libs/application/templates/accident-notification/src/utils/shouldRequestReview.spec.ts deleted file mode 100644 index cef1aa791e08..000000000000 --- a/libs/application/templates/accident-notification/src/utils/shouldRequestReview.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { AccidentNotificationAnswers } from '..' -import { AccidentTypeEnum, WorkAccidentTypeEnum } from '../types' -import { shouldRequestReview } from './shouldRequestReview' -describe('shouldRequestReview', () => { - const agricultureAccident: Partial = { - workAccident: { type: WorkAccidentTypeEnum.AGRICULTURE }, - } - - const accidentAtHome: Partial = { - accidentType: { radioButton: AccidentTypeEnum.HOMEACTIVITIES }, - } - - const normalWorkAccident: Partial = { - workAccident: { type: WorkAccidentTypeEnum.GENERAL }, - } - - it('should return false for work accidents', () => { - expect(shouldRequestReview(agricultureAccident)).toEqual(false) - }) - it('should return true for general work accident', () => { - expect(shouldRequestReview(normalWorkAccident)).toEqual(true) - }) - it('should return false for home accident', () => { - expect(shouldRequestReview(accidentAtHome)).toEqual(false) - }) -}) diff --git a/libs/application/templates/accident-notification/src/utils/shouldRequestReview.ts b/libs/application/templates/accident-notification/src/utils/shouldRequestReview.ts deleted file mode 100644 index f4773098dc74..000000000000 --- a/libs/application/templates/accident-notification/src/utils/shouldRequestReview.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { AccidentNotificationAnswers, utils, WorkAccidentTypeEnum } from '..' - -export const shouldRequestReview = ( - answers: Partial, -): boolean => { - const ishome = utils.isHomeActivitiesAccident(answers) - const isAgriculture = utils.isOfWorkAccidentType( - answers, - WorkAccidentTypeEnum.AGRICULTURE, - ) - - const isEitherHomeOrAgriculture = ishome || isAgriculture - - return !isEitherHomeOrAgriculture -} diff --git a/libs/application/templates/aosh/register-new-machine/src/fields/LocationInputField/index.tsx b/libs/application/templates/aosh/register-new-machine/src/fields/LocationInputField/index.tsx new file mode 100644 index 000000000000..37a39cf795b5 --- /dev/null +++ b/libs/application/templates/aosh/register-new-machine/src/fields/LocationInputField/index.tsx @@ -0,0 +1,50 @@ +import { FieldBaseProps } from '@island.is/application/types' +import { FC, useState } from 'react' +import { useFormContext } from 'react-hook-form' +import { coreErrorMessages } from '@island.is/application/core' +import { AboutMachine } from '../../lib/dataSchema' +import { Box } from '@island.is/island-ui/core' +import { InputController } from '@island.is/shared/form-fields' +import { useLocale } from '@island.is/localization' +import { machine } from '../../lib/messages' + +export const LocationInputField: FC> = ( + props, +) => { + const { field, setBeforeSubmitCallback } = props + const { formatMessage } = useLocale() + const { watch } = useFormContext() + const [displayError, setDisplayError] = useState(false) + const watchMachine = watch('machine.aboutMachine') as AboutMachine + const location = watch(field.id) as string + const categoryValue = 'Fólkslyftur og vörulyftur' + + setBeforeSubmitCallback?.(async () => { + if ( + watchMachine.category?.nameIs === categoryValue && + location.length === 0 + ) { + setDisplayError(true) + return [false, ''] + } + return [true, null] + }) + + return ( + + setDisplayError(false)} + error={ + displayError && location.length === 0 + ? formatMessage(coreErrorMessages.defaultError) + : undefined + } + /> + + ) +} diff --git a/libs/application/templates/aosh/register-new-machine/src/fields/index.ts b/libs/application/templates/aosh/register-new-machine/src/fields/index.ts index cf08b813c38b..b33672f5ec00 100644 --- a/libs/application/templates/aosh/register-new-machine/src/fields/index.ts +++ b/libs/application/templates/aosh/register-new-machine/src/fields/index.ts @@ -3,3 +3,4 @@ export { Overview } from './Overview' export { AboutMachine } from './AboutMachine' export { TechnicalInfo } from './TechnicalInfo' export { ChangeAnswers } from './ChangeAnswers' +export { LocationInputField } from './LocationInputField' diff --git a/libs/application/templates/aosh/register-new-machine/src/forms/RegisterMachineForm/MachineSection/MachineBasicInformation.ts b/libs/application/templates/aosh/register-new-machine/src/forms/RegisterMachineForm/MachineSection/MachineBasicInformation.ts index 22afa79349c9..716b3af34f9e 100644 --- a/libs/application/templates/aosh/register-new-machine/src/forms/RegisterMachineForm/MachineSection/MachineBasicInformation.ts +++ b/libs/application/templates/aosh/register-new-machine/src/forms/RegisterMachineForm/MachineSection/MachineBasicInformation.ts @@ -122,11 +122,11 @@ export const MachineBasicInformation = buildSubSection({ titleVariant: 'h5', marginTop: 3, }), - buildTextField({ + buildCustomField({ id: 'machine.basicInformation.location', - title: machine.labels.basicMachineInformation.location, + title: '', width: 'half', - maxLength: 255, + component: 'LocationInputField', }), buildTextField({ id: 'machine.basicInformation.cargoFileNumber', diff --git a/libs/application/templates/aosh/register-new-machine/tsconfig.lib.json b/libs/application/templates/aosh/register-new-machine/tsconfig.lib.json index 8d36d2eaf5af..cfc356e7214b 100644 --- a/libs/application/templates/aosh/register-new-machine/tsconfig.lib.json +++ b/libs/application/templates/aosh/register-new-machine/tsconfig.lib.json @@ -2,23 +2,22 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../../../dist/out-tsc", - "types": [ - "node", - - "@nx/react/typings/cssmodule.d.ts", - "@nx/react/typings/image.d.ts" - ] + "types": ["node"] }, + "files": [ + "../../../../../node_modules/@nx/react/typings/cssmodule.d.ts", + "../../../../../node_modules/@nx/react/typings/image.d.ts" + ], "exclude": [ - "jest.config.ts", - "src/**/*.spec.ts", - "src/**/*.test.ts", - "src/**/*.spec.tsx", - "src/**/*.test.tsx", - "src/**/*.spec.js", - "src/**/*.test.js", - "src/**/*.spec.jsx", - "src/**/*.test.jsx" + "**/*.spec.ts", + "**/*.test.ts", + "**/*.spec.tsx", + "**/*.test.tsx", + "**/*.spec.js", + "**/*.test.js", + "**/*.spec.jsx", + "**/*.test.jsx", + "jest.config.ts" ], - "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"] + "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] } diff --git a/libs/application/templates/funding-government-projects/src/forms/FundingGovernmentProjectsForm.ts b/libs/application/templates/funding-government-projects/src/forms/FundingGovernmentProjectsForm.ts index 2b5ffa9d68f6..126b4f812e12 100644 --- a/libs/application/templates/funding-government-projects/src/forms/FundingGovernmentProjectsForm.ts +++ b/libs/application/templates/funding-government-projects/src/forms/FundingGovernmentProjectsForm.ts @@ -123,6 +123,7 @@ export const FundingGovernmentProjectsForm: Form = buildForm({ }), buildSliderField({ id: 'project.refundableYears', + title: '', label: { singular: shared.yearSingular, plural: shared.yearPlural, diff --git a/libs/application/templates/inheritance-report/src/forms/prerequisites.ts b/libs/application/templates/inheritance-report/src/forms/prerequisites.ts index 74b17ab2358b..5296679ded4b 100644 --- a/libs/application/templates/inheritance-report/src/forms/prerequisites.ts +++ b/libs/application/templates/inheritance-report/src/forms/prerequisites.ts @@ -38,8 +38,6 @@ export const getForm = ({ value: ESTATE_INHERITANCE, label: m.preDataCollectionApplicationForDefault, disabled: !allowEstateApplication, - //TODO: remove tooltip when this application is ready to go live - tooltip: m.preDataCollectionApplicationForDefaultTooltip, }, { value: PREPAID_INHERITANCE, 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 a8b9ab5fe2fd..b2c3d8b511ab 100644 --- a/libs/application/templates/inheritance-report/src/forms/sections/assets.ts +++ b/libs/application/templates/inheritance-report/src/forms/sections/assets.ts @@ -345,7 +345,7 @@ export const assets = buildSection({ [ESTATE_INHERITANCE]: m.bankAccountCapital, [PREPAID_INHERITANCE]: m.bankAccountCapitalPrePaid, }, - id: 'propertyValuation', + id: 'amount', required: true, currency: true, }, @@ -376,7 +376,7 @@ export const assets = buildSection({ fromExternalData: 'bankAccounts', skipPushRight: true, repeaterButtonText: m.bankAccountRepeaterButton, - sumField: 'propertyValuation', + sumField: 'amount', sumField2: 'exchangeRateOrInterest', }, ), @@ -494,13 +494,19 @@ export const assets = buildSection({ format: '######-####', }, { - title: m.stocksFaceValue, + title: { + [ESTATE_INHERITANCE]: m.stocksFaceValue, + [PREPAID_INHERITANCE]: m.stocksFaceValuePrePaid, + }, id: 'amount', currency: true, required: true, }, { - title: m.stocksRateOfChange, + title: { + [ESTATE_INHERITANCE]: m.stocksRateOfChange, + [PREPAID_INHERITANCE]: m.stocksRateOfChangePrePaid, + }, id: 'exchangeRateOrInterest', type: 'currency', required: true, diff --git a/libs/application/templates/inheritance-report/src/forms/sections/debtsAndFuneralCost.ts b/libs/application/templates/inheritance-report/src/forms/sections/debtsAndFuneralCost.ts index ed46966a1955..8651c95870f1 100644 --- a/libs/application/templates/inheritance-report/src/forms/sections/debtsAndFuneralCost.ts +++ b/libs/application/templates/inheritance-report/src/forms/sections/debtsAndFuneralCost.ts @@ -87,6 +87,9 @@ export const debtsAndFuneralCost = buildSection({ { label: DebtTypes.OtherDebts, }, + { + label: DebtTypes.InsuranceInstitute, + }, ], }, ), 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..89606f99a45b 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 @@ -20,7 +20,7 @@ export const prePaidApplicant = buildSection({ children: [ buildNationalIdWithNameField({ id: 'prePaidApplicant', - title: m.name, + title: '', width: 'full', required: true, }), diff --git a/libs/application/templates/inheritance-report/src/forms/sections/prepaidInheritance/inheritanceExecutor.ts b/libs/application/templates/inheritance-report/src/forms/sections/prepaidInheritance/inheritanceExecutor.ts index a411304e2a32..593efcd2b8ae 100644 --- a/libs/application/templates/inheritance-report/src/forms/sections/prepaidInheritance/inheritanceExecutor.ts +++ b/libs/application/templates/inheritance-report/src/forms/sections/prepaidInheritance/inheritanceExecutor.ts @@ -26,7 +26,7 @@ export const inheritanceExecutor = buildSection({ }), buildNationalIdWithNameField({ id: 'executors.executor', - title: m.name, + title: '', required: true, }), buildTextField({ @@ -73,7 +73,7 @@ export const inheritanceExecutor = buildSection({ }), buildNationalIdWithNameField({ id: 'executors.spouse', - title: m.name, + title: '', required: true, condition: (answers) => !!((answers.executors as any)?.includeSpouse as Array) diff --git a/libs/application/templates/inheritance-report/src/lib/dataSchema.ts b/libs/application/templates/inheritance-report/src/lib/dataSchema.ts index 3e437681836f..0e77b8b62b09 100644 --- a/libs/application/templates/inheritance-report/src/lib/dataSchema.ts +++ b/libs/application/templates/inheritance-report/src/lib/dataSchema.ts @@ -55,6 +55,7 @@ const assetSchema = ({ withShare }: { withShare?: boolean } = {}) => .refine((v) => (withShare ? validateAssetNumber(v) : true)), description: z.string(), propertyValuation: z.string(), + enabled: z.boolean(), ...(withShare ? { share: z.string() } : {}), ...deceasedShare, }) diff --git a/libs/application/templates/inheritance-report/src/lib/messages.ts b/libs/application/templates/inheritance-report/src/lib/messages.ts index 84f67d71a06a..e0b816cab74e 100644 --- a/libs/application/templates/inheritance-report/src/lib/messages.ts +++ b/libs/application/templates/inheritance-report/src/lib/messages.ts @@ -708,11 +708,21 @@ export const m = defineMessages({ }, stocksFaceValue: { id: 'ir.application:stocksFaceValue', + defaultMessage: 'Nafnverð á dánardegi', + description: '', + }, + stocksFaceValuePrePaid: { + id: 'ir.application:stocksFaceValuePrePaid', defaultMessage: 'Nafnverð', description: '', }, stocksRateOfChange: { id: 'ir.application:stocksRateOfChange', + defaultMessage: 'Gengi á dánardegi', + description: '', + }, + stocksRateOfChangePrePaid: { + id: 'ir.application:stocksRateOfChangePrePaid', defaultMessage: 'Gengi', description: '', }, diff --git a/libs/application/templates/inheritance-report/src/types.ts b/libs/application/templates/inheritance-report/src/types.ts index 5b4b730a8fbb..c23a10263a35 100644 --- a/libs/application/templates/inheritance-report/src/types.ts +++ b/libs/application/templates/inheritance-report/src/types.ts @@ -305,4 +305,5 @@ export enum DebtTypes { PropertyFees = 'Fasteignagjöld', OtherDebts = 'Aðrar skuldir', PublicCharges = 'Opinber gjöld', + InsuranceInstitute = 'Tryggingarstofnun ríkisins', } diff --git a/libs/application/templates/official-journal-of-iceland/src/components/htmlEditor/HTMLEditor.tsx b/libs/application/templates/official-journal-of-iceland/src/components/htmlEditor/HTMLEditor.tsx index f8cb1367ec92..0b91a291d5e7 100644 --- a/libs/application/templates/official-journal-of-iceland/src/components/htmlEditor/HTMLEditor.tsx +++ b/libs/application/templates/official-journal-of-iceland/src/components/htmlEditor/HTMLEditor.tsx @@ -3,7 +3,7 @@ import { Editor, EditorFileUploader } from '@island.is/regulations-tools/Editor' import { useEffect, useRef, useState } from 'react' import { Controller } from 'react-hook-form' import { classes, editorWrapper, errorStyle } from './HTMLEditor.css' -import { Box, Text } from '@island.is/island-ui/core' +import { Box, Stack, Text } from '@island.is/island-ui/core' type Props = { title?: string name: string @@ -50,12 +50,8 @@ export const HTMLEditor = ({ defaultValue={initialValue} render={({ field: { onChange: updateFormValue, value } }) => { return ( - <> - {title && ( - - {title} - - )} + + {title && {title}} {error &&
{error}
} - +
) }} /> diff --git a/libs/application/templates/official-journal-of-iceland/src/components/input/OJOISelectController.tsx b/libs/application/templates/official-journal-of-iceland/src/components/input/OJOISelectController.tsx index 00eac25ff080..799ed40704d8 100644 --- a/libs/application/templates/official-journal-of-iceland/src/components/input/OJOISelectController.tsx +++ b/libs/application/templates/official-journal-of-iceland/src/components/input/OJOISelectController.tsx @@ -4,7 +4,12 @@ import { useApplication } from '../../hooks/useUpdateApplication' import { OJOIApplication } from '../../lib/types' import { useFormContext } from 'react-hook-form' import set from 'lodash/set' -import { Select, SkeletonLoader } from '@island.is/island-ui/core' +import { + Box, + Select, + SkeletonLoader, + useBreakpoint, +} from '@island.is/island-ui/core' import { OJOI_INPUT_HEIGHT } from '../../lib/constants' import { isBaseEntity } from '../../lib/utils' import { getValueViaPath } from '@island.is/application/core' @@ -23,6 +28,7 @@ type Props = { loading?: boolean applicationId: string disabled?: boolean + width?: 'full' | 'half' onBeforeChange?: (answers: OJOIApplication['answers'], value: T) => void onChange?: (value: T) => void } @@ -36,6 +42,7 @@ export const OJOISelectController = ({ loading, applicationId, disabled, + width = 'full', onBeforeChange, onChange, }: Props) => { @@ -46,6 +53,9 @@ export const OJOISelectController = ({ const { setValue } = useFormContext() + const { xs, sm, md } = useBreakpoint() + const isSmOrSmaller = xs || (sm && !md) + const placeholderText = typeof placeholder === 'string' ? placeholder : f(placeholder) @@ -68,35 +78,33 @@ export const OJOISelectController = ({ return opt.value.id === defaultVal.id } - return false + return undefined }) - if (loading) { - return ( - - ) - } - return ( - { + if (!opt?.value) return + return handleChange(opt.value) + }} + /> + )} +
) } diff --git a/libs/application/templates/official-journal-of-iceland/src/fields/Advert.tsx b/libs/application/templates/official-journal-of-iceland/src/fields/Advert.tsx index 0e81b3062f40..fcaa5cfdb6fc 100644 --- a/libs/application/templates/official-journal-of-iceland/src/fields/Advert.tsx +++ b/libs/application/templates/official-journal-of-iceland/src/fields/Advert.tsx @@ -1,8 +1,7 @@ import { InputFields, OJOIFieldBaseProps } from '../lib/types' -import { Box } from '@island.is/island-ui/core' +import { Stack } from '@island.is/island-ui/core' import { FormGroup } from '../components/form/FormGroup' import { advert } from '../lib/messages' -import * as styles from './Advert.css' import { useDepartments } from '../hooks/useDepartments' import { OJOISelectController } from '../components/input/OJOISelectController' import { useTypes } from '../hooks/useTypes' @@ -12,25 +11,23 @@ import { useFormContext } from 'react-hook-form' import { useApplication } from '../hooks/useUpdateApplication' import set from 'lodash/set' import { HTMLEditor } from '../components/htmlEditor/HTMLEditor' -import { getAdvertMarkup } from '../lib/utils' +import { cleanTypename, getAdvertMarkup } from '../lib/utils' +import { DEPARTMENT_A } from '../lib/constants' export const Advert = ({ application }: OJOIFieldBaseProps) => { const { setValue } = useFormContext() const { application: currentApplication } = useApplication({ applicationId: application.id, }) + const { departments, loading: loadingDepartments } = useDepartments() - const { - getLazyTypes, - types, - loading: loadingTypes, - } = useTypes({ - initalDepartmentId: application.answers?.advert?.department?.id, - }) - const titlePreview = getAdvertMarkup({ - type: currentApplication.answers.advert?.type?.title, - title: currentApplication.answers.advert?.title, + const defaultDepartment = + application.answers?.advert?.department?.id || DEPARTMENT_A + + const { getLazyMainTypes, mainTypes, mainTypeLoading } = useTypes({ + initalDepartmentId: defaultDepartment, + pageSize: 300, }) const departmentOptions = departments?.map((d) => ({ @@ -42,20 +39,28 @@ export const Advert = ({ application }: OJOIFieldBaseProps) => { }, })) - const typeOptions = types?.map((d) => ({ + const mainTypeOptions = mainTypes?.map((d) => ({ label: d.title, - value: { - id: d.id, - title: d.title, - slug: d.slug, - }, + value: d, })) + const currentTypes = + currentApplication?.answers?.advert?.mainType?.types?.map((d) => ({ + label: d.title, + value: d, + })) ?? [] + + const titlePreview = getAdvertMarkup({ + type: currentApplication.answers.advert?.type?.title, + title: currentApplication.answers.advert?.title, + }) + return ( - <> + - + { set(answers, InputFields.advert.type, null) }} onChange={(value) => - getLazyTypes({ + getLazyMainTypes({ variables: { params: { department: value.id, @@ -78,19 +83,36 @@ export const Advert = ({ application }: OJOIFieldBaseProps) => { }) } /> - - + { + if (value.types.length === 1) { + const cleaned = cleanTypename(value.types[0]) + set(answers, InputFields.advert.type, cleaned) + } else { + set(answers, InputFields.advert.type, null) + } + }} /> - - + + {currentTypes.length > 1 && ( + + )} + { textarea={true} maxLength={600} /> - - + - + - + { applicationId={application.id} disabled={true} /> - - + { // because this is not a controlled component onChange={(value) => setValue(InputFields.advert.html, value)} /> - + - + ) } diff --git a/libs/application/templates/official-journal-of-iceland/src/fields/AdvertModal.tsx b/libs/application/templates/official-journal-of-iceland/src/fields/AdvertModal.tsx index 8327cefa5e93..5020d1b8fbe7 100644 --- a/libs/application/templates/official-journal-of-iceland/src/fields/AdvertModal.tsx +++ b/libs/application/templates/official-journal-of-iceland/src/fields/AdvertModal.tsx @@ -26,6 +26,7 @@ import debounce from 'lodash/debounce' import { InputFields } from '../lib/types' import { useFormContext } from 'react-hook-form' import { OfficialJournalOfIcelandAdvert } from '@island.is/api/schema' +import { cleanTypename } from '../lib/utils' type Props = { applicationId: string visible: boolean @@ -75,20 +76,12 @@ export const AdvertModal = ({ return } - const clean = (obj: { - __typename?: string - id: string - title: string - slug: string - }) => { - const { __typename: _, ...rest } = obj - return rest - } - - const department = clean(advert.department) - const type = clean(advert.type) + const department = cleanTypename(advert.department) + const type = cleanTypename(advert.type) - const categories = advert.categories.map((category) => clean(category)) + const categories = advert.categories.map((category) => + cleanTypename(category), + ) setValue(InputFields.advert.department, department) setValue(InputFields.advert.type, type) diff --git a/libs/application/templates/official-journal-of-iceland/src/graphql/queries.ts b/libs/application/templates/official-journal-of-iceland/src/graphql/queries.ts index ebe437b894ad..cfe4f70ab68b 100644 --- a/libs/application/templates/official-journal-of-iceland/src/graphql/queries.ts +++ b/libs/application/templates/official-journal-of-iceland/src/graphql/queries.ts @@ -117,6 +117,38 @@ export const ADVERT_QUERY = gql` } ` +export const MAIN_TYPES_QUERY = gql` + query AdvertMainTypes($params: OfficialJournalOfIcelandMainTypesInput!) { + officialJournalOfIcelandMainTypes(params: $params) { + mainTypes { + id + title + slug + department { + id + title + slug + } + types { + id + title + slug + } + } + paging { + page + pageSize + totalPages + totalItems + hasNextPage + hasPreviousPage + nextPage + previousPage + } + } + } +` + export const TYPES_QUERY = gql` query AdvertTypes($params: OfficialJournalOfIcelandTypesInput!) { officialJournalOfIcelandTypes(params: $params) { diff --git a/libs/application/templates/official-journal-of-iceland/src/hooks/useTypes.ts b/libs/application/templates/official-journal-of-iceland/src/hooks/useTypes.ts index 8cd4ff5678ed..7ff98ea1949f 100644 --- a/libs/application/templates/official-journal-of-iceland/src/hooks/useTypes.ts +++ b/libs/application/templates/official-journal-of-iceland/src/hooks/useTypes.ts @@ -1,7 +1,10 @@ import { useLazyQuery, useQuery } from '@apollo/client' -import { OfficialJournalOfIcelandAdvertsTypesResponse } from '@island.is/api/schema' +import { + OfficialJournalOfIcelandAdvertsTypesResponse, + OfficialJournalOfIcelandMainTypesResponse, +} from '@island.is/api/schema' -import { TYPES_QUERY } from '../graphql/queries' +import { MAIN_TYPES_QUERY, TYPES_QUERY } from '../graphql/queries' type UseTypesParams = { initalDepartmentId?: string @@ -14,6 +17,10 @@ type TypesResponse = { officialJournalOfIcelandTypes: OfficialJournalOfIcelandAdvertsTypesResponse } +type MainTypesResponse = { + officialJournalOfIcelandMainTypes: OfficialJournalOfIcelandMainTypesResponse +} + type TypesVariables = { params: { department?: string @@ -50,6 +57,16 @@ export const useTypes = ({ }, ) + const { + data: mainTypesData, + loading: mainTypeLoading, + error: mainTypeError, + } = useQuery(MAIN_TYPES_QUERY, { + variables: { + params: params, + }, + }) + const [ getLazyTypes, { data: lazyTypes, loading: lazyTypesLoading, error: lazyTypesError }, @@ -57,11 +74,33 @@ export const useTypes = ({ fetchPolicy: 'network-only', }) + const [ + getLazyMainTypes, + { + data: lazyMainTypes, + loading: lazyMainTypesLoading, + error: lazyMainTypesError, + }, + ] = useLazyQuery(MAIN_TYPES_QUERY, { + fetchPolicy: 'network-only', + }) + const currentTypes = lazyTypes ? lazyTypes.officialJournalOfIcelandTypes.types : data?.officialJournalOfIcelandTypes.types + const currentMainTypes = lazyMainTypes + ? lazyMainTypes.officialJournalOfIcelandMainTypes.mainTypes + : mainTypesData?.officialJournalOfIcelandMainTypes.mainTypes + return { + mainTypes: currentMainTypes, + mainTypeLoading, + mainTypeError, + lazyMainTypesLoading, + lazyMainTypesError, + getLazyMainTypes, + lazyMainTypes: lazyMainTypes?.officialJournalOfIcelandMainTypes.mainTypes, lazyTypes: lazyTypes?.officialJournalOfIcelandTypes.types, lazyTypesLoading, lazyTypesError, diff --git a/libs/application/templates/official-journal-of-iceland/src/lib/constants.ts b/libs/application/templates/official-journal-of-iceland/src/lib/constants.ts index e05b15dcaf66..ed4ea3efc463 100644 --- a/libs/application/templates/official-journal-of-iceland/src/lib/constants.ts +++ b/libs/application/templates/official-journal-of-iceland/src/lib/constants.ts @@ -14,6 +14,10 @@ export enum AnswerOption { NO = 'no', } +export const DEPARTMENT_A = 'a-deild' +export const DEPARTMENT_B = 'b-deild' +export const DEPARTMENT_C = 'c-deild' + export enum ApplicationAttachmentType { ORIGINAL = 'frumrit', ADDITIONS = 'fylgiskjol', diff --git a/libs/application/templates/official-journal-of-iceland/src/lib/dataSchema.ts b/libs/application/templates/official-journal-of-iceland/src/lib/dataSchema.ts index 046cbeed2664..8dcfe02c7bbd 100644 --- a/libs/application/templates/official-journal-of-iceland/src/lib/dataSchema.ts +++ b/libs/application/templates/official-journal-of-iceland/src/lib/dataSchema.ts @@ -69,6 +69,9 @@ const advertSchema = z .object({ department: baseEntitySchema.optional(), type: baseEntitySchema.optional().nullable(), + mainType: baseEntitySchema + .extend({ types: z.array(baseEntitySchema).optional() }) + .optional(), title: z.string().optional(), html: z.string().optional(), requestedDate: z.string().optional(), diff --git a/libs/application/templates/official-journal-of-iceland/src/lib/messages/advert.ts b/libs/application/templates/official-journal-of-iceland/src/lib/messages/advert.ts index 681773cf8393..0bde1ec2be09 100644 --- a/libs/application/templates/official-journal-of-iceland/src/lib/messages/advert.ts +++ b/libs/application/templates/official-journal-of-iceland/src/lib/messages/advert.ts @@ -63,15 +63,27 @@ export const advert = { description: 'Placeholder for the department input', }, }), + mainType: defineMessages({ + label: { + id: 'ojoi.application:advert.inputs.mainType.label', + defaultMessage: 'Tegund birtingar', + description: 'Label for the main type input', + }, + placeholder: { + id: 'ojoi.application:advert.inputs.mainType.placeholder', + defaultMessage: 'Veldu tegund birtingar', + description: 'Placeholder for the main type input', + }, + }), type: defineMessages({ label: { id: 'ojoi.application:advert.inputs.type.label', - defaultMessage: 'Tegund birtingar', + defaultMessage: 'Undirtegund birtingar', description: 'Label for the type input', }, placeholder: { id: 'ojoi.application:advert.inputs.type.placeholder', - defaultMessage: 'Veldu tegund birtingar', + defaultMessage: 'Veldu undirtegund birtingar', description: 'Placeholder for the type input', }, }), diff --git a/libs/application/templates/official-journal-of-iceland/src/lib/types.ts b/libs/application/templates/official-journal-of-iceland/src/lib/types.ts index 1529e2d56442..032d13c63692 100644 --- a/libs/application/templates/official-journal-of-iceland/src/lib/types.ts +++ b/libs/application/templates/official-journal-of-iceland/src/lib/types.ts @@ -12,6 +12,7 @@ export const InputFields = { }, [Routes.ADVERT]: { department: 'advert.department', + mainType: 'advert.mainType', type: 'advert.type', title: 'advert.title', html: 'advert.html', diff --git a/libs/application/templates/official-journal-of-iceland/src/lib/utils.ts b/libs/application/templates/official-journal-of-iceland/src/lib/utils.ts index 15699f900241..fdfaad3ab0d9 100644 --- a/libs/application/templates/official-journal-of-iceland/src/lib/utils.ts +++ b/libs/application/templates/official-journal-of-iceland/src/lib/utils.ts @@ -388,3 +388,13 @@ export const convertNumberToRoman = (num: number) => { const roman = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X'] return roman[num - 1] } + +export const cleanTypename = (obj: { + __typename?: string + id: string + title: string + slug: string +}) => { + const { __typename: _, ...rest } = obj + return rest +} diff --git a/libs/application/templates/official-journal-of-iceland/src/screens/InvolvedPartyScreen.tsx b/libs/application/templates/official-journal-of-iceland/src/screens/InvolvedPartyScreen.tsx index 7ed23a605848..b35efca32233 100644 --- a/libs/application/templates/official-journal-of-iceland/src/screens/InvolvedPartyScreen.tsx +++ b/libs/application/templates/official-journal-of-iceland/src/screens/InvolvedPartyScreen.tsx @@ -90,21 +90,20 @@ export const InvolvedPartyScreen = ({ /> )} - - { - setSubmitButtonDisabled && setSubmitButtonDisabled(false) - }} - /> - + { + setSubmitButtonDisabled && setSubmitButtonDisabled(false) + }} + /> ) diff --git a/libs/application/templates/parental-leave/src/forms/ParentalLeaveForm.ts b/libs/application/templates/parental-leave/src/forms/ParentalLeaveForm.ts index 6272df76192c..1ea7f6cdf6ec 100644 --- a/libs/application/templates/parental-leave/src/forms/ParentalLeaveForm.ts +++ b/libs/application/templates/parental-leave/src/forms/ParentalLeaveForm.ts @@ -1223,6 +1223,7 @@ export const ParentalLeaveForm: Form = buildForm({ children: [ buildSliderField({ id: 'multipleBirthsRequestDays', + title: '', label: { singular: parentalLeaveFormMessages.shared.day, plural: parentalLeaveFormMessages.shared.days, @@ -1317,6 +1318,7 @@ export const ParentalLeaveForm: Form = buildForm({ children: [ buildSliderField({ id: 'requestRights.requestDays', + title: '', label: { singular: parentalLeaveFormMessages.shared.day, plural: parentalLeaveFormMessages.shared.days, @@ -1365,6 +1367,7 @@ export const ParentalLeaveForm: Form = buildForm({ children: [ buildSliderField({ id: 'giveRights.giveDays', + title: '', label: { singular: parentalLeaveFormMessages.shared.day, plural: parentalLeaveFormMessages.shared.days, diff --git a/libs/application/templates/reference-template/README.md b/libs/application/templates/reference-template/README.md index c4af4abb3e17..85b75745395d 100644 --- a/libs/application/templates/reference-template/README.md +++ b/libs/application/templates/reference-template/README.md @@ -66,3 +66,51 @@ To access the list of national ids for applicantActors that have come in contact ```ts const applicantActors = application.applicantActors ``` + +## Coding guidelines + +The aim is to have all applications to be coded in a similar way, so that every developer that is familiar with the application system can open any application and everything is consistent and feels familiar. + +- Reduce the amount of custom components to a minimum. +- Reduce the use of the "as" and "any" keywords as much as possible. Both of those keywords are tricking the linter to accept code that would otherwise throw errors and might cause hard to trace bugs. +- Try to use the `getValueViaPath` function to access the answers of the application. It makes accessing nested values easier and the code more readable. Note that this function is generic and a type can be provided to make sure the type of the value is correct E.g: + +`getValueViaPath(application.answers, 'some.nested.value', 'optional fallback')` + +- Don't use fake steps, stepper should only be showing steps of the current form. On the first step in the main form, there shouldn't be a back button or the illusion that you can go back to the prerequsites step. + +## Folder structure + +|-- assets/--------------------------------# optional folder for assets like images, icons, etc. +| +|-- components/----------------------------# optional folder for React components that are used by custom components. +| +|-- dataProviders/-------------------------# folder for data providers. +| +|-- fields/--------------------------------# optional folder for custom components if the application needs any. +|-- |-- index.ts---------------------------# Exports all fields from the folder. +|-- |-- myCustomComponent/-----------------# Folder for a custom component, camelCase. +|-- |-- |-- MyCustomComponent.tsx----------# React component file, PascalCase. +|-- |-- |-- MyCustomComponent.css.ts-------# CSS file, PascalCase. +| +|-- forms/---------------------------------# folder for forms. More about form folder structure in the form folder README. +|-- |-- prerequisitesForm/ +|-- |-- mainForm/ +|-- |-- conclusionForm/--------------------# More forms if needed +| +|-- graphql/-------------------------------# optional folder for graphql queries and mutations. +| +|-- lib/-----------------------------------# folder for data schema, messages, and the main template file. +|-- |-- dataScema.ts-----------------------# Validation for the application. +|-- |-- mainTemplate.ts--------------------# Main template file. State machine for the application, mapUsersToRole and more +|-- |-- messages.ts------------------------# File for all text that appears on the screen, synced with Contentful. +|-- |-- messages/--------------------------# optional folder for messages if there is a need to have the messages more organized. +| +|-- shared/--------------------------------# optional folder for code that might be needed in the template-api-modules or +|------------------------------------------# other places outside the template. +| +|-- utils/---------------------------------# folder for utility functions, constants, enums and types. +|-- |-- constants.ts-----------------------# Constants for the application. +|-- |-- enums.ts---------------------------# Enums for the application. +|-- |-- types.ts---------------------------# Types for the application. +|-- |-- helperFunctions.ts-----------------# Helper functions for the application, this can be many files. diff --git a/libs/application/templates/reference-template/src/assets/README.md b/libs/application/templates/reference-template/src/assets/README.md new file mode 100644 index 000000000000..2355070ab130 --- /dev/null +++ b/libs/application/templates/reference-template/src/assets/README.md @@ -0,0 +1,4 @@ +# Assets + +This folder is optional and can be used to store assets like images, icons, etc. +Organization within this folder id up to the developer. diff --git a/libs/application/templates/reference-template/src/assets/akureyri.svg b/libs/application/templates/reference-template/src/assets/akureyri.svg new file mode 100644 index 000000000000..1ad88a2af433 --- /dev/null +++ b/libs/application/templates/reference-template/src/assets/akureyri.svg @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/application/templates/reference-template/src/assets/isafjardarbaer.svg b/libs/application/templates/reference-template/src/assets/isafjardarbaer.svg new file mode 100644 index 000000000000..5ec71102e333 --- /dev/null +++ b/libs/application/templates/reference-template/src/assets/isafjardarbaer.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/libs/application/templates/reference-template/src/assets/plate-110-510.tsx b/libs/application/templates/reference-template/src/assets/plate-110-510.tsx new file mode 100644 index 000000000000..f27f9a43737a --- /dev/null +++ b/libs/application/templates/reference-template/src/assets/plate-110-510.tsx @@ -0,0 +1,40 @@ +export const plate110 = () => ( + + + + + + + +) diff --git a/libs/application/templates/reference-template/src/assets/plate-155-305.tsx b/libs/application/templates/reference-template/src/assets/plate-155-305.tsx new file mode 100644 index 000000000000..0cd3f1607825 --- /dev/null +++ b/libs/application/templates/reference-template/src/assets/plate-155-305.tsx @@ -0,0 +1,41 @@ +export const plate155 = () => ( + + + + + + + +) diff --git a/libs/application/templates/reference-template/src/assets/plate-200-280.tsx b/libs/application/templates/reference-template/src/assets/plate-200-280.tsx new file mode 100644 index 000000000000..623c96946c55 --- /dev/null +++ b/libs/application/templates/reference-template/src/assets/plate-200-280.tsx @@ -0,0 +1,40 @@ +export const plate200 = () => ( + + + + + + + +) diff --git a/libs/application/templates/reference-template/src/assets/sambandid.svg b/libs/application/templates/reference-template/src/assets/sambandid.svg new file mode 100644 index 000000000000..a88407920cb4 --- /dev/null +++ b/libs/application/templates/reference-template/src/assets/sambandid.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/libs/application/templates/reference-template/src/components/Logo/Logo.tsx b/libs/application/templates/reference-template/src/components/Logo/Logo.tsx new file mode 100644 index 000000000000..375ce16ce50f --- /dev/null +++ b/libs/application/templates/reference-template/src/components/Logo/Logo.tsx @@ -0,0 +1,31 @@ +import { useEffect, useState } from 'react' +import { Application } from '@island.is/application/types' + +type Props = { + application: Application +} + +// In the prereq when there is no applicant, the logo is the default one. +// When there is an applicant, the logo is dynamic based on the applicant's identity number. +// Gervimaður Færeyjar shows the logo of Ísafjarðarbær and all others show the logo of Akureyri. +export const Logo = ({ application }: Props) => { + const [logo, setLogo] = useState() + + useEffect(() => { + const getLogo = async () => { + const applicant = application.applicant + + const town = !applicant + ? 'sambandid' + : applicant === '0101302399' + ? 'isafjardarbaer' + : 'akureyri' + + const svgLogo = await import(`../../assets/${town}.svg`) + setLogo(svgLogo.default) + } + getLogo() + }, []) + + return Municipality logo +} diff --git a/libs/application/templates/reference-template/src/components/README.md b/libs/application/templates/reference-template/src/components/README.md new file mode 100644 index 000000000000..6b3d80449453 --- /dev/null +++ b/libs/application/templates/reference-template/src/components/README.md @@ -0,0 +1,3 @@ +# The components folder + +This folder contains all React components that are not custom components on their own but are used by custom components or a component for the form logo if it is dynamic. diff --git a/libs/application/templates/reference-template/src/dataProviders/README.md b/libs/application/templates/reference-template/src/dataProviders/README.md new file mode 100644 index 000000000000..3f47dfa8bfa2 --- /dev/null +++ b/libs/application/templates/reference-template/src/dataProviders/README.md @@ -0,0 +1,3 @@ +# Data Providers + +This folder is for all the data providers that are run in the prerequisites form. diff --git a/libs/application/templates/reference-template/src/dataProviders/index.ts b/libs/application/templates/reference-template/src/dataProviders/index.ts index 29a2b6528729..e60ea65b2a39 100644 --- a/libs/application/templates/reference-template/src/dataProviders/index.ts +++ b/libs/application/templates/reference-template/src/dataProviders/index.ts @@ -1,9 +1,6 @@ import { defineTemplateApi } from '@island.is/application/types' import { MockProviderApi } from '@island.is/application/types' -import { - NationalRegistryUserApi, - UserProfileApi, -} from '@island.is/application/types' +import { NationalRegistryUserApi } from '@island.is/application/types' export interface MyParameterType { id: number } diff --git a/libs/application/templates/reference-template/src/fields/Overview/Overview.tsx b/libs/application/templates/reference-template/src/fields/Overview/Overview.tsx new file mode 100644 index 000000000000..dc6e84162996 --- /dev/null +++ b/libs/application/templates/reference-template/src/fields/Overview/Overview.tsx @@ -0,0 +1,67 @@ +import { FieldBaseProps, StaticText } from '@island.is/application/types' +import { Box, GridColumn, GridRow, Text } from '@island.is/island-ui/core' +import { formatText, getValueViaPath } from '@island.is/application/core' +import { useLocale } from '@island.is/localization' +import { ReviewGroup } from '@island.is/application/ui-components' +import { States } from '../../utils/constants' +import { m } from '../../lib/messages' + +type TableRepeaterAnswers = { + fullName: string + nationalId: string + relation: string +} + +const KeyValue = ({ label, value }: { label: StaticText; value: string }) => { + const { formatMessage } = useLocale() + return ( + + + {formatMessage(label)} + + {value} + + ) +} + +export const Overview = ({ application, goToScreen }: FieldBaseProps) => { + const { formatMessage } = useLocale() + + const changeScreens = (screen: string) => { + if (goToScreen) goToScreen(screen) + } + + const tableRepeaterAnswers = getValueViaPath>( + application.answers, + 'tableRepeater', + ) + + return ( + + + changeScreens('tableRepeater')} + isEditable={application.state === States.DRAFT} + > + + + + Values from the table repeater + + {tableRepeaterAnswers && + tableRepeaterAnswers.map((ans, i) => { + return ( + + ) + })} + + + + {/* More review groups as needed... */} + + ) +} diff --git a/libs/application/templates/reference-template/src/fields/README.md b/libs/application/templates/reference-template/src/fields/README.md new file mode 100644 index 000000000000..2bb8efcb7efd --- /dev/null +++ b/libs/application/templates/reference-template/src/fields/README.md @@ -0,0 +1,21 @@ +# The fields folder + +This folder contains all custom components that are used by the application. + +## Organisation + +- All components should be in a folder that holds all files for that component. This includes the .tsx file, possibly a .css.ts file and maybe others. + The folders should be named like the component, but with the first letter in lowercase (camelCase), and then the .tsx and .css.ts files should be capitalized (PascalCase). +- The folder should have an index.ts file that re-exports all the components. +- The index.ts file in the /src folder should then re-export the components in the /fields folder for the template loader. + +## Useage of custom components + +Before creating a custom component, you should: + +1. Try to use the shared components, `buildTextField`, `buildCheckboxField`, `buildSelectField`, `buildFileUploadField` and so on. This is most preferable to make the look and feel of the application more consistent and uniform. +2. If the shared components almost fullfill your needs but you need something more, consider consulting with the designer of the application and try to adjust the design to the built in components. +3. If the design can not be adjusted to the built in components, then consult Norda if a shared component can possibly be adjusted or expanded to fulfill your needs. +4. Is there another application that has made a similar custom component before? If so, then it should be a shared component. +5. If you still need a new component, ask yourself if this is a component that another application might also need in the future. If so make the new component shared. +6. Make a custom component if none of the above apply. diff --git a/libs/application/templates/reference-template/src/fields/exampleCustomComponent/ExampleCustomComponent.css.ts b/libs/application/templates/reference-template/src/fields/exampleCustomComponent/ExampleCustomComponent.css.ts new file mode 100644 index 000000000000..de8bdda26696 --- /dev/null +++ b/libs/application/templates/reference-template/src/fields/exampleCustomComponent/ExampleCustomComponent.css.ts @@ -0,0 +1,10 @@ +import { theme } from '@island.is/island-ui/theme' +import { style } from '@vanilla-extract/css' + +export const boldNames = style({ + fontWeight: 'bold', +}) + +export const bottomBorderRadius = style({ + borderRadius: `0 0 ${theme.border.radius.large} ${theme.border.radius.large}`, +}) diff --git a/libs/application/templates/reference-template/src/fields/exampleCustomComponent/ExampleCustomComponent.tsx b/libs/application/templates/reference-template/src/fields/exampleCustomComponent/ExampleCustomComponent.tsx new file mode 100644 index 000000000000..cdb92ea3e1fc --- /dev/null +++ b/libs/application/templates/reference-template/src/fields/exampleCustomComponent/ExampleCustomComponent.tsx @@ -0,0 +1,37 @@ +import { FieldBaseProps } from '@island.is/application/types' +import { useLocale } from '@island.is/localization' +import * as styles from './ExampleCustomComponent.css' +import { Box, Text } from '@island.is/island-ui/core' +import { m } from '../../lib/messages' + +interface Props { + field: { + props: { + someData: Array + } + } +} + +export const ExampleCustomComponent = ({ field }: Props & FieldBaseProps) => { + const { formatMessage } = useLocale() + const { someData } = field.props + if (!someData) return null + + return ( + + + {formatMessage(m.customComponentAbout)} + + + {someData.map((item) => ( +

{item}

+ ))} +
+
+ ) +} diff --git a/libs/application/templates/reference-template/src/fields/index.ts b/libs/application/templates/reference-template/src/fields/index.ts index c953dd689bb2..03cac91d34b8 100644 --- a/libs/application/templates/reference-template/src/fields/index.ts +++ b/libs/application/templates/reference-template/src/fields/index.ts @@ -1,2 +1,4 @@ export { default as ExampleCountryField } from './ExampleCountryField' export { default as CustomRepeater } from './CustomRepeater' +export { ExampleCustomComponent } from './exampleCustomComponent/ExampleCustomComponent' +export { Overview } from './Overview/Overview' diff --git a/libs/application/templates/reference-template/src/forms/ExampleForm.ts b/libs/application/templates/reference-template/src/forms/ExampleForm.ts deleted file mode 100644 index c402a020c9fd..000000000000 --- a/libs/application/templates/reference-template/src/forms/ExampleForm.ts +++ /dev/null @@ -1,310 +0,0 @@ -import { - buildCheckboxField, - buildForm, - buildDescriptionField, - buildMultiField, - buildRadioField, - buildSection, - buildSubmitField, - buildSubSection, - buildTextField, - buildFileUploadField, - buildRedirectToServicePortalField, - buildSelectField, - buildPhoneField, - buildHiddenInput, - buildHiddenInputWithWatchedValue, - buildTableRepeaterField, -} from '@island.is/application/core' -import { - Comparators, - Form, - FormModes, - FormValue, -} from '@island.is/application/types' -import { ApiActions } from '../shared' -import { m } from '../lib/messages' - -export const ExampleForm: Form = buildForm({ - id: 'ExampleFormDraft', - title: 'Atvinnuleysisbætur', - mode: FormModes.DRAFT, - children: [ - buildSection({ - id: 'conditions', - title: m.conditionsSection, - children: [], - }), - buildSection({ - id: 'tableRepeaterWithPhone', - title: 'Table repeater', - children: [ - buildTableRepeaterField({ - id: 'rentalHousingLandlordInfoTable', - title: '', - marginTop: 1, - fields: { - name: { - component: 'input', - label: 'test 1', - width: 'half', - }, - nationalId: { - component: 'input', - label: 'test 2', - format: '######-####', - width: 'half', - }, - phone: { - component: 'phone', - label: 'test 3', - format: '###-####', - width: 'half', - }, - email: { - component: 'input', - label: 'test 4', - type: 'email', - width: 'half', - }, - isRepresentative: { - component: 'checkbox', - large: true, - displayInTable: false, - label: 'test 5', - options: [ - { - label: 'test 6', - value: 'YES', - }, - ], - }, - }, - table: { - header: [ - 'nameInputLabel', - 'nationalIdHeaderLabel', - 'phoneInputLabel', - 'emailInputLabel', - ], - }, - }), - ], - }), - buildSection({ - id: 'intro', - title: m.introSection, - children: [ - buildDescriptionField({ - id: 'field', - title: m.introField, - description: (application) => ({ - ...m.introIntroduction, - values: { name: application.answers.name }, - }), - }), - buildMultiField({ - id: 'about', - title: m.about.defaultMessage, - children: [ - buildTextField({ - id: 'person.name', - title: m.personName, - }), - buildHiddenInput({ - id: 'person.someHiddenInputRequired', - defaultValue: () => { - return 'validAnswer' - }, - }), - buildHiddenInputWithWatchedValue({ - id: 'person.someHiddenInputWatchedRequired', - watchValue: 'person.name', - valueModifier: (watchedValue: any) => { - return watchedValue + 'Valid' - }, - }), - buildTextField({ - id: 'person.nationalId', - title: m.nationalId, - width: 'half', - }), - buildTextField({ - id: 'person.age', - title: m.age, - width: 'half', - }), - buildTextField({ - id: 'person.email', - title: m.email, - width: 'half', - }), - buildPhoneField({ - id: 'person.phoneNumber', - title: m.phoneNumber, - width: 'half', - condition: { - questionId: 'person.age', - isMultiCheck: false, - comparator: Comparators.GTE, - value: '18', - }, - }), - ], - }), - buildFileUploadField({ - id: 'attachments', - title: (application, locale) => { - if (locale === 'is') { - return 'Viðhengi' - } - return 'Attachments' - }, - introduction: 'Hér getur þú bætt við viðhengjum við umsóknina þína.', - uploadMultiple: true, - }), - ], - }), - buildSection({ - id: 'career', - title: m.career, - children: [ - buildSubSection({ - id: 'history', - title: m.history, - children: [ - buildSelectField({ - id: 'careerIndustry', - title: m.careerIndustry, - description: m.careerIndustryDescription, - required: true, - options: (options, application, locale) => { - if (locale === 'is') { - return [ - { label: locale, value: locale }, - { label: 'Hugbúnaður', value: 'software' }, - { label: 'Fjármál', value: 'finance' }, - { label: 'Efnahagsráðgjöf', value: 'consulting' }, - { label: 'Önnur', value: 'other' }, - ] - } - return [ - { label: locale, value: locale }, - { label: 'Software', value: 'software' }, - { label: 'Finance', value: 'finance' }, - { label: 'Consulting', value: 'consulting' }, - { label: 'Other', value: 'other' }, - ] - }, - }), - buildRadioField({ - id: 'careerHistory', - title: m.careerHistory, - options: [ - { value: 'yes', label: m.yesOptionLabel }, - { value: 'no', label: m.noOptionLabel }, - ], - condition: (formValue: FormValue) => { - return ( - (formValue as { person: { age: string } })?.person?.age >= - '18' - ) - }, - }), - buildMultiField({ - id: 'careerHistoryDetails', - title: '', - children: [ - buildCheckboxField({ - id: 'careerHistoryDetails.careerHistoryCompanies', - title: m.careerHistoryCompanies, - options: [ - { value: 'government', label: m.governmentOptionLabel }, - { value: 'aranja', label: 'Aranja' }, - { value: 'advania', label: 'Advania' }, - { value: 'other', label: 'Annað' }, - ], - }), - buildTextField({ - id: 'careerHistoryDetails.careerHistoryOther', - title: m.careerHistoryOther, - }), - ], - }), - ], - }), - buildSubSection({ - id: 'future', - title: m.future, - children: [ - buildTextField({ - id: 'dreamJob', - title: m.dreamJob, - }), - ], - }), - buildSubSection({ - id: 'assignee', - title: m.assigneeTitle, - children: [ - buildTextField({ - id: 'assigneeEmail', - title: m.assignee, - }), - ], - }), - ], - }), - buildSection({ - id: 'confirmation', - title: 'Staðfesta', - children: [ - buildMultiField({ - title: '', - children: [ - buildSubmitField({ - id: 'submit', - placement: 'footer', - title: 'Senda inn umsókn', - actions: [ - { event: 'SUBMIT', name: 'Senda inn umsókn', type: 'primary' }, - ], - }), - buildDescriptionField({ - id: 'overview', - title: 'Takk fyrir að sækja um', - description: - 'Með því að smella á "Senda" hér að neðan, þá sendist umsóknin inn til úrvinnslu. Við látum þig vita þegar hún er samþykkt eða henni er hafnað.', - }), - ], - }), - buildRedirectToServicePortalField({ - id: 'redirect', - title: '', - }), - buildDescriptionField({ - id: 'final', - title: 'Takk', - description: (application) => { - const sendApplicationActionResult = - application.externalData[ApiActions.createApplication] - - let id = 'unknown' - if (sendApplicationActionResult) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - id = sendApplicationActionResult.data.id - } - - return { - ...m.outroMessage, - values: { - id, - }, - } - }, - }), - ], - }), - ], -}) diff --git a/libs/application/templates/reference-template/src/forms/Prerequisites.ts b/libs/application/templates/reference-template/src/forms/Prerequisites.ts deleted file mode 100644 index 27de62ca28ef..000000000000 --- a/libs/application/templates/reference-template/src/forms/Prerequisites.ts +++ /dev/null @@ -1,118 +0,0 @@ -import get from 'lodash/get' -import { - buildForm, - buildDescriptionField, - buildMultiField, - buildSection, - buildSubmitField, - buildExternalDataProvider, - buildDataProviderItem, -} from '@island.is/application/core' -import { Application, Form, FormModes } from '@island.is/application/types' -import { m } from '../lib/messages' - -import { UserProfileApi } from '@island.is/application/types' -import { - ReferenceDataApi, - MyMockProvider, - NationalRegistryApi, -} from '../dataProviders' - -export const Prerequisites: Form = buildForm({ - id: 'PrerequisitesDraft', - title: 'Skilyrði', - mode: FormModes.DRAFT, - children: [ - buildSection({ - id: 'conditions', - title: m.conditionsSection, - children: [ - buildExternalDataProvider({ - id: 'approveExternalData', - title: 'Utanaðkomandi gögn', - dataProviders: [ - buildDataProviderItem({ - provider: UserProfileApi, - title: 'User profile', - subTitle: 'User profile', - }), - buildDataProviderItem({ - provider: ReferenceDataApi, - title: 'getReferenceData', - subTitle: 'Reference data', - }), - buildDataProviderItem({ - provider: NationalRegistryApi, - title: 'Þjóðskrá', - subTitle: 'Upplýsingar um þig í Þjóðskrá.', - }), - buildDataProviderItem({ - provider: MyMockProvider, - title: 'Mock Data', - subTitle: 'Returns data for mocking', - }), - ], - }), - buildMultiField({ - id: 'externalDataSuccess', - title: 'Tókst að sækja gögn', - children: [ - buildDescriptionField({ - id: 'externalDataSuccessDescription', - title: '', - description: (application: Application) => - `Gildið frá data provider: ${get( - application.externalData, - 'getReferenceData.data.referenceData.numbers', - 'fannst ekki', - )}`, - }), - buildDescriptionField({ - id: 'externalDataSuccessDescription.mock', - title: '', - description: (application: Application) => - `Gildið frá mock data provider: ${get( - application.externalData, - 'referenceMock.data.mockObject.mockString', - 'fannst ekki', - )}`, - }), - buildSubmitField({ - id: 'toDraft', - placement: 'footer', - title: 'Hefja umsókn', - refetchApplicationAfterSubmit: true, - actions: [ - { - event: 'SUBMIT', - name: 'Hefja umsókn', - type: 'primary', - }, - ], - }), - ], - }), - buildDescriptionField({ - id: 'neverDisplayed', - title: '', - description: '', - }), - ], - }), - buildSection({ - id: 'intro', - title: m.introSection, - children: [], - }), - buildSection({ - id: 'career', - title: m.career, - children: [], - }), - buildSection({ - id: 'confirmation', - title: 'Staðfesta', - children: [], - }), - ], -}) diff --git a/libs/application/templates/reference-template/src/forms/README.md b/libs/application/templates/reference-template/src/forms/README.md new file mode 100644 index 000000000000..27ec67b4d251 --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/README.md @@ -0,0 +1,48 @@ +# The forms folder + +This folder contains all the forms that are used by the application. + +At minimum there should be a prerequisites form, application form and a confirmation form to reflect the possible states of an application. +For more complicated applications you could have different forms depending on the user type and you could have more states like approved, rejected, waiting to assign... + +## Organization + +All forms should be in a folder with the same name as the form. The folder and files in it should follow camelCase, PascalCase should be reserved for React components. +A simple form with one screen, like the prerequisites form can be just a single file. +Form with more than one section and possibly subsections should be broken down into one file per screen. + +Example folder structure: + +| /prerequisitesForm +| |-- prerequisitesForm.tsx + +| /applicationForm +| |-- index.ts (This file has a buildForm function) +| |-- /section1 +| | |-- index.ts (This file has a buildSection function and imports the subsection childs) +| | |-- subsection1.ts (This file has a buildSubSection function) +| | |-- subsection2.ts (This file has a buildSubSection function) +| | |-- subsection3.ts (This file has a buildSubSection function) +| |-- /section2 +| | |-- index.ts (This file has a buildSection function) +| |-- /section3 +| | |-- index.ts (This file has a buildSection function) + +| /confirmationForm +| |-- confirmationForm.ts + +## BuildForm, buildSection, buildSubSection and buildMultiField + +Those are the four building blocks of each form. + +The root of each form is a buildForm function and that is the only thing that can be imported into the state machine in the main template file. + +The buildForm function has an array of children. Those children should be buildSection functions. + +BuildSection will be displayed in the stepper as top level section. The children array of a buildSection can be buildSubSection, buildMultiField or a regular buildField function. + +BuildSubSection is a subsection of a buildSection and will be displayed in the stepper as a subsection of the buildSection. The children array of a buildSubSection can be buildMultiField or buildField functions. + +BuildMultiField is a wrapper around many buildField functions. if a buildSection or buildSubSection doesn't have a buildMultiField, it will only display one field at a time and stepping through the screens will be strange in regards to the stepper. + +In most cases you want to use one buildMultiField as the child of a buildSection or buildSubSection since in most cases you want to display multiple fields at a time on the screen. diff --git a/libs/application/templates/reference-template/src/forms/Approved.ts b/libs/application/templates/reference-template/src/forms/approvedForm/approvedForm.ts similarity index 100% rename from libs/application/templates/reference-template/src/forms/Approved.ts rename to libs/application/templates/reference-template/src/forms/approvedForm/approvedForm.ts diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/commonActionsSection/conditions2Subsection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/commonActionsSection/conditions2Subsection.ts new file mode 100644 index 000000000000..1ecda91a53d1 --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/commonActionsSection/conditions2Subsection.ts @@ -0,0 +1,27 @@ +import { + buildDescriptionField, + buildSubSection, + getValueViaPath, + YES, +} from '@island.is/application/core' + +export const conditions2Subsection = buildSubSection({ + condition: (answers) => { + const checkbox2Value = getValueViaPath>( + answers, + 'conditions2Checkbox', + ) + + return checkbox2Value ? checkbox2Value[0] === YES : false + }, + id: 'condition2Subsection', + title: 'This section is conditional', + children: [ + buildDescriptionField({ + id: 'condition2Description', + title: 'This screens visibility is based in previous answers', + description: + 'With this functionality, the application can branch and collect different data for different users', + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/commonActionsSection/conditionsSubsection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/commonActionsSection/conditionsSubsection.ts new file mode 100644 index 000000000000..7456d5b66dfd --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/commonActionsSection/conditionsSubsection.ts @@ -0,0 +1,92 @@ +import { + buildCheckboxField, + buildDescriptionField, + buildHiddenInput, + buildMultiField, + buildSubSection, + buildTextField, + getValueViaPath, + YES, +} from '@island.is/application/core' +import { m } from '../../../lib/messages' + +export const conditionsSubsection = buildSubSection({ + id: 'conditionsSubsection', + title: 'Conditions', + children: [ + buildMultiField({ + id: 'conditionsMultiField', + title: 'Conditions', + children: [ + buildDescriptionField({ + id: 'conditionsDescription', + title: '', + description: + 'It is possible to condition both single fields and text or an entire section/subsection', + marginBottom: 2, + }), + buildDescriptionField({ + id: 'conditionsDescription2', + title: '', + description: m.conditionsDescription2, + marginBottom: 2, + }), + buildDescriptionField({ + id: 'conditionsDescription3', + title: '', + description: + 'The visibility of everything can be dependent on the users answers in the application or data that has been fetched and placed in externalData.', + marginBottom: 2, + }), + buildCheckboxField({ + id: 'conditionsCheckbox', + title: 'Skilyrði fyrir staka reiti', + options: [ + { + label: 'Check this box to see an extra field appear', + value: YES, + }, + ], + }), + buildTextField({ + condition: (answers) => { + const checkboxValue = getValueViaPath>( + answers, + 'conditionsCheckbox', + ) + + return checkboxValue ? checkboxValue[0] === YES : false + }, + id: 'conditionsTextField', + variant: 'textarea', + rows: 8, + title: 'This field is only available if the box above is checked', + }), + buildCheckboxField({ + id: 'conditions2Checkbox', + title: 'Skilyrði fyrir section/subsection', + options: [ + { + label: + 'Check this box to see a new subsection appear in the stepper ------>', + value: YES, + }, + ], + }), + buildHiddenInput({ + // This is a bit of a hack, but in order for the stepper to update and show the conditionally added step, there + // has to be a field on the current step with a matching condition. + condition: (answers) => { + const checkboxValue = getValueViaPath>( + answers, + 'conditions2Checkbox', + ) + + return checkboxValue ? checkboxValue[0] === YES : false + }, + id: 'conditionsTextField', + }), + ], + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/commonActionsSection/getDataFromExternalDataSection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/commonActionsSection/getDataFromExternalDataSection.ts new file mode 100644 index 000000000000..b922ca75751a --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/commonActionsSection/getDataFromExternalDataSection.ts @@ -0,0 +1,63 @@ +import { + buildDescriptionField, + buildMultiField, + buildSubSection, + getValueViaPath, +} from '@island.is/application/core' +import { Application } from '@island.is/application/types' + +export const getDataFromExternalDataSubsection = buildSubSection({ + id: 'getDataFromExternalData', + title: 'External data', + children: [ + buildMultiField({ + id: 'externalDataSuccess', + title: '', + children: [ + buildDescriptionField({ + id: 'externalDataSuccessTitle', + title: 'Example of data being fetched from external data', + marginBottom: [4], + }), + buildDescriptionField({ + id: 'externalDataSuccessDescription', + title: 'Value from data provider', + titleVariant: 'h4', + description: (application: Application) => { + const value = getValueViaPath( + application.externalData, + 'getReferenceData.data.referenceData.applicantName', + ) + + return value ?? 'Not found' + }, + }), + buildDescriptionField({ + id: 'externalDataSuccessDescription2', + title: '', + description: (application: Application) => { + const value = getValueViaPath( + application.externalData, + 'getReferenceData.data.referenceData.numbers', + ) + + return value ? `${value}` : 'Not found' + }, + marginBottom: [2], + }), + buildDescriptionField({ + id: 'externalDataSuccessDescriptionMock', + title: 'Value from mock data provider', + titleVariant: 'h4', + description: (application: Application) => { + const value = getValueViaPath( + application.externalData, + 'referenceMock.data.mockObject.mockString', + ) + return value ?? 'Not found' + }, + }), + ], + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/commonActionsSection/index.ts b/libs/application/templates/reference-template/src/forms/exampleForm/commonActionsSection/index.ts new file mode 100644 index 000000000000..f58a19aa98d8 --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/commonActionsSection/index.ts @@ -0,0 +1,16 @@ +import { buildSection } from '@island.is/application/core' +import { conditionsSubsection } from './conditionsSubsection' +import { conditions2Subsection } from './conditions2Subsection' +import { getDataFromExternalDataSubsection } from './getDataFromExternalDataSection' +import { validationSubsection } from './validadionSubsection' + +export const commonActionsSection = buildSection({ + id: 'commonActions', + title: 'Common actions', + children: [ + getDataFromExternalDataSubsection, + validationSubsection, + conditionsSubsection, + conditions2Subsection, + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/commonActionsSection/validadionSubsection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/commonActionsSection/validadionSubsection.ts new file mode 100644 index 000000000000..c40b13340bef --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/commonActionsSection/validadionSubsection.ts @@ -0,0 +1,54 @@ +import { + buildDescriptionField, + buildMultiField, + buildRadioField, + buildSubSection, + buildTextField, +} from '@island.is/application/core' +import { m } from '../../../lib/messages' +import { radioValidationExampleEnum } from '../../../utils/types' + +export const validationSubsection = buildSubSection({ + id: 'validationSubsection', + title: 'Validation', + children: [ + buildMultiField({ + id: 'validationMultiField', + title: 'Validation', + children: [ + buildDescriptionField({ + id: 'validationDescriptionField', + title: '', + description: m.validationDescription, + marginBottom: 2, + }), + buildDescriptionField({ + id: 'validationDescriptionField2', + title: '', + description: + 'All fields on this page have validation that must be filled out to continue', + }), + buildTextField({ + id: 'validation.validationTextField', + title: 'Must be 3 characters or more', + required: true, // Adds the red star to the field + }), + buildDescriptionField({ + id: 'validation.validationDescriptionField3', + title: '', + description: m.validationDescription3, + marginTop: 4, + }), + buildRadioField({ + id: 'validation.validationRadioField', + title: '', + options: [ + { label: 'Option 1', value: radioValidationExampleEnum.OPTION_1 }, + { label: 'Option 2', value: radioValidationExampleEnum.OPTION_2 }, + { label: 'Option 3', value: radioValidationExampleEnum.OPTION_3 }, + ], + }), + ], + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/compositeFieldsSection/applicantInfoSubsection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/compositeFieldsSection/applicantInfoSubsection.ts new file mode 100644 index 000000000000..7f157f065962 --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/compositeFieldsSection/applicantInfoSubsection.ts @@ -0,0 +1,9 @@ +import { buildSubSection } from '@island.is/application/core' +import { applicantInformationMultiField } from '@island.is/application/ui-forms' + +export const applicantInfoSubsection = buildSubSection({ + id: 'aplicantInfoSubsection', + title: 'Aplicant Info Subsection', + // Common form, fills automatically with applicant information + children: [applicantInformationMultiField()], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/compositeFieldsSection/bankAccountSubsection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/compositeFieldsSection/bankAccountSubsection.ts new file mode 100644 index 000000000000..8351750821f1 --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/compositeFieldsSection/bankAccountSubsection.ts @@ -0,0 +1,15 @@ +import { + buildBankAccountField, + buildSubSection, +} from '@island.is/application/core' + +export const bankAccountSubsection = buildSubSection({ + id: 'bankAccountSubSection', + title: 'Bank account', + children: [ + buildBankAccountField({ + id: 'bankAccountfield', + title: 'Bank account field', + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/compositeFieldsSection/index.ts b/libs/application/templates/reference-template/src/forms/exampleForm/compositeFieldsSection/index.ts new file mode 100644 index 000000000000..a4ffc6c2e115 --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/compositeFieldsSection/index.ts @@ -0,0 +1,14 @@ +import { buildSection } from '@island.is/application/core' +import { nationalIdWithNameSubsection } from './nationalIdWithNameSubsection' +import { applicantInfoSubsection } from './applicantInfoSubsection' +import { bankAccountSubsection } from './bankAccountSubsection' + +export const compositeFieldsSection = buildSection({ + id: 'compositeFieldsSection', + title: 'Composite fields', + children: [ + nationalIdWithNameSubsection, + applicantInfoSubsection, + bankAccountSubsection, + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/compositeFieldsSection/nationalIdWithNameSubsection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/compositeFieldsSection/nationalIdWithNameSubsection.ts new file mode 100644 index 000000000000..3e86de1f4078 --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/compositeFieldsSection/nationalIdWithNameSubsection.ts @@ -0,0 +1,15 @@ +import { + buildNationalIdWithNameField, + buildSubSection, +} from '@island.is/application/core' + +export const nationalIdWithNameSubsection = buildSubSection({ + id: 'nationalIdWithNameSubsection', + title: 'National ID with name subsection', + children: [ + buildNationalIdWithNameField({ + id: 'pickRole.electPerson', + title: 'Lookup name by national ID', + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/customSection/customSection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/customSection/customSection.ts new file mode 100644 index 000000000000..bbf89d7f9240 --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/customSection/customSection.ts @@ -0,0 +1,42 @@ +import { + buildCustomField, + buildDescriptionField, + buildMultiField, + buildSection, +} from '@island.is/application/core' +import { m } from '../../../lib/messages' + +export const customSection = buildSection({ + id: 'customSection', + title: 'Custom section', + children: [ + buildMultiField({ + id: 'customMultiField', + title: '', + children: [ + buildDescriptionField({ + id: 'customDescription', + title: 'Custom Components', + description: m.customComponentDescription, + marginBottom: [2], + }), + buildDescriptionField({ + id: 'customDescription2', + title: '', + description: m.customComponentNumberedList, + marginBottom: [2], + }), + buildCustomField( + { + id: 'customComponent', + title: 'The custom component', + component: 'ExampleCustomComponent', + }, + { + someData: ['foo', 'bar', 'baz'], + }, + ), + ], + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/index.ts b/libs/application/templates/reference-template/src/forms/exampleForm/index.ts new file mode 100644 index 000000000000..3ae9601aad80 --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/index.ts @@ -0,0 +1,94 @@ +import { createElement } from 'react' +import { + buildForm, + buildDescriptionField, + buildMultiField, + buildSection, + buildSubmitField, + buildRedirectToServicePortalField, +} from '@island.is/application/core' +import { Application, Form, FormModes } from '@island.is/application/types' +import { ApiActions } from '../../shared' +import { introSection } from './introSection/introSection' +import { simpleInputsSection } from './simpleInputsSection' +import { compositeFieldsSection } from './compositeFieldsSection' +import { commonActionsSection } from './commonActionsSection' +import { customSection } from './customSection/customSection' +import { overviewSection } from './overviewSection/overviewSection' +import { noInputFieldsSection } from './noInputFieldsSection' +import { Logo } from '../../components/Logo/Logo' +import { tablesAndRepeatersSection } from './tablesAndRepeatersSection' +import { m } from '../../lib/messages' + +export const ExampleForm: Form = buildForm({ + id: 'ExampleFormDraft', + title: 'Main form', + mode: FormModes.DRAFT, + // The logo prop can either take in a React component or a function that returns a React component. + // Dynamic logo can be based on answers or external data + logo: (application: Application) => { + const logo = createElement(Logo, { application }) + return () => logo + }, + children: [ + introSection, + commonActionsSection, + noInputFieldsSection, + simpleInputsSection, + compositeFieldsSection, + tablesAndRepeatersSection, + customSection, + overviewSection, + buildSection({ + id: 'confirmation', + title: 'Staðfesta', + children: [ + buildMultiField({ + title: '', + children: [ + buildSubmitField({ + id: 'submit', + placement: 'footer', + title: 'Senda inn umsókn', + actions: [ + { event: 'SUBMIT', name: 'Senda inn umsókn', type: 'primary' }, + ], + }), + buildDescriptionField({ + id: 'overview', + title: 'Thank you for applying', + description: + 'By clicking "Submit" below, the application will be sent for processing. We will let you know when it is accepted or rejected.', + }), + ], + }), + buildRedirectToServicePortalField({ + id: 'redirect', + title: '', + }), + buildDescriptionField({ + id: 'final', + title: 'Takk', + description: (application) => { + const sendApplicationActionResult = + application.externalData[ApiActions.createApplication] + + let id = 'unknown' + if (sendApplicationActionResult) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + id = sendApplicationActionResult.data.id + } + + return { + ...m.outroMessage, + values: { + id, + }, + } + }, + }), + ], + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/introSection/introSection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/introSection/introSection.ts new file mode 100644 index 000000000000..a840bc32ffde --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/introSection/introSection.ts @@ -0,0 +1,30 @@ +import { + buildDescriptionField, + buildMultiField, + buildSection, +} from '@island.is/application/core' +import { m } from '../../../lib/messages' + +export const introSection = buildSection({ + id: 'introSection', + title: 'Inngangur', + children: [ + buildMultiField({ + id: 'intro', + title: m.introTitle, + children: [ + buildDescriptionField({ + id: 'introDescription', + title: '', + description: m.introDescription, + marginBottom: 2, + }), + buildDescriptionField({ + id: 'introDescription2', + title: '', + description: m.introDescription2, + }), + ], + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/noInputFieldsSection/accordionSubsection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/noInputFieldsSection/accordionSubsection.ts new file mode 100644 index 000000000000..d21002cfaa9b --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/noInputFieldsSection/accordionSubsection.ts @@ -0,0 +1,27 @@ +import { + buildAccordionField, + buildSubSection, +} from '@island.is/application/core' + +export const accordionSubsection = buildSubSection({ + id: 'accordionSubsection', + title: 'Accordion', + children: [ + buildAccordionField({ + id: 'accordion', + title: 'Accordion', + accordionItems: [ + { + itemTitle: 'Item 1', + itemContent: + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.', + }, + { + itemTitle: 'Item 2', + itemContent: + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', + }, + ], + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/noInputFieldsSection/actionCardSubsection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/noInputFieldsSection/actionCardSubsection.ts new file mode 100644 index 000000000000..9c8716473b04 --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/noInputFieldsSection/actionCardSubsection.ts @@ -0,0 +1,64 @@ +import { + buildActionCardListField, + buildSubSection, +} from '@island.is/application/core' + +export const actionCardSubsection = buildSubSection({ + id: 'actionCardSubsection', + title: 'Action card', + children: [ + buildActionCardListField({ + id: 'actionCardList', + title: 'Action cards with buttons', + items: (application, lang) => { + return [ + { + eyebrow: 'Card eyebrow', + heading: 'Card heading, all the options', + headingVariant: 'h3', + text: 'Card text, lorem ipsum dolor sit amet dolor sit amet lorem ipsum dolor sit amet', + cta: { + label: 'Card cta', + variant: 'primary', + external: true, + to: 'https://www.google.com', + }, + tag: { + label: 'Outlined label', + outlined: true, + variant: 'blue', + }, + }, + { + heading: 'Card heading', + headingVariant: 'h4', + tag: { + label: 'Not outlined label', + outlined: false, + variant: 'blue', + }, + cta: { + label: 'Card cta', + variant: 'primary', + external: true, + to: 'https://www.google.com', + }, + unavailable: { + label: 'Unavailable label', + message: 'Unavailable message', + }, + }, + { + heading: 'Card heading', + headingVariant: 'h4', + tag: { + label: 'White label', + outlined: true, + variant: 'white', + }, + }, + ] + }, + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/noInputFieldsSection/descriptionSubsection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/noInputFieldsSection/descriptionSubsection.ts new file mode 100644 index 000000000000..43dab73b1328 --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/noInputFieldsSection/descriptionSubsection.ts @@ -0,0 +1,84 @@ +import { + buildDescriptionField, + buildMultiField, + buildSubSection, +} from '@island.is/application/core' +import { m } from '../../../lib/messages' + +export const descriptionSubsection = buildSubSection({ + id: 'descriptionSubsection', + title: 'Description', + children: [ + buildMultiField({ + id: 'descriptionMultiField', + title: 'Textar með buildDescriptionField', + children: [ + buildDescriptionField({ + id: 'description1', + title: '', + description: m.descriptionFieldDescription, + marginBottom: [2], + }), + buildDescriptionField({ + id: 'description2', + title: '', + description: m.descriptionFieldDescription2, + marginBottom: [2], + }), + + buildDescriptionField({ + id: 'description3', + title: 'Default title size ', + description: 'Description inserted as a regular string', + marginBottom: [2], + tooltip: 'Tooltip text', + titleTooltip: 'Title tooltip text', + }), + buildDescriptionField({ + id: 'description4', + title: 'h1 title size', + titleVariant: 'h1', + description: m.regularTextExample, + marginBottom: [2], + }), + buildDescriptionField({ + id: 'description5', + title: 'h2 title size (same as default)', + titleVariant: 'h2', + description: m.markdownHeadingExample, + marginBottom: [2], + }), + buildDescriptionField({ + id: 'description6', + title: 'h3 title size', + titleVariant: 'h3', + description: m.markdownBulletListExample, + marginBottom: [2], + }), + buildDescriptionField({ + id: 'description7', + title: 'h4 title size', + titleVariant: 'h4', + description: m.markdownNumberedListExample, + marginBottom: [2], + }), + buildDescriptionField({ + id: 'description8', + title: 'h5 title size', + titleVariant: 'h5', + description: { + ...m.markdownMiscExample, + values: { value1: 'MY VALUE' }, + }, + marginBottom: [2], + }), + buildDescriptionField({ + id: 'description9', + title: '', + description: m.markdownCodeExample, + marginBottom: [2], + }), + ], + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/noInputFieldsSection/dividerSubsection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/noInputFieldsSection/dividerSubsection.ts new file mode 100644 index 000000000000..b591c9c3509a --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/noInputFieldsSection/dividerSubsection.ts @@ -0,0 +1,25 @@ +import { + buildDividerField, + buildMultiField, + buildSubSection, +} from '@island.is/application/core' + +export const dividerSubsection = buildSubSection({ + id: 'dividerSubsection', + title: 'Divider', + children: [ + buildMultiField({ + id: 'dividerMultiField', + title: 'Divider', + children: [ + buildDividerField({}), + buildDividerField({ + title: 'Divider with title', + }), + buildDividerField({ + color: 'red600', + }), + ], + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/noInputFieldsSection/index.ts b/libs/application/templates/reference-template/src/forms/exampleForm/noInputFieldsSection/index.ts new file mode 100644 index 000000000000..08fc70f9ad47 --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/noInputFieldsSection/index.ts @@ -0,0 +1,17 @@ +import { buildSection } from '@island.is/application/core' +import { descriptionSubsection } from './descriptionSubsection' +import { dividerSubsection } from './dividerSubsection' +import { accordionSubsection } from './accordionSubsection' +import { actionCardSubsection } from './actionCardSubsection' +import { keyValueSubsection } from './keyValueSubsection' +export const noInputFieldsSection = buildSection({ + id: 'noInputFieldsSection', + title: 'Fields without inputs', + children: [ + descriptionSubsection, + dividerSubsection, + accordionSubsection, + actionCardSubsection, + keyValueSubsection, + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/noInputFieldsSection/keyValueSubsection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/noInputFieldsSection/keyValueSubsection.ts new file mode 100644 index 000000000000..3e07e42070ec --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/noInputFieldsSection/keyValueSubsection.ts @@ -0,0 +1,46 @@ +import { + buildKeyValueField, + buildMultiField, + buildSubSection, +} from '@island.is/application/core' + +export const keyValueSubsection = buildSubSection({ + id: 'keyValueSubsection', + title: 'Key-Value', + children: [ + buildMultiField({ + id: 'keyValueMultiField', + title: 'Key-Value', + children: [ + buildKeyValueField({ + label: 'Key value label', + value: 'One value', + }), + buildKeyValueField({ + label: 'Key value with divider', + value: ['Many values', 'Value 2', 'Value 3'], + divider: true, + }), + buildKeyValueField({ + label: 'Key value displax flex', + value: ['Value', 'Value 2', 'Value 3'], + divider: true, + display: 'flex', + }), + buildKeyValueField({ + label: 'Key value half width', + value: 'One value', + divider: true, + width: 'half', + }), + buildKeyValueField({ + label: 'Key value half width', + value: 'Custom colspan', + divider: true, + width: 'half', + colSpan: '5/12', + }), + ], + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/overviewSection/overviewSection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/overviewSection/overviewSection.ts new file mode 100644 index 000000000000..6bf72bd620d8 --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/overviewSection/overviewSection.ts @@ -0,0 +1,43 @@ +import { + buildSection, + buildMultiField, + buildDescriptionField, + buildCustomField, + buildSubmitField, +} from '@island.is/application/core' +import { m } from '../../../lib/messages' +import { DefaultEvents } from '@island.is/application/types' + +export const overviewSection = buildSection({ + id: 'overview', + title: 'Overview', + children: [ + buildMultiField({ + id: 'overviewMultiField', + title: '', + children: [ + buildDescriptionField({ + id: 'overview', + title: 'Overview', + description: m.overviewDescription, + }), + buildCustomField({ + id: 'customComponent', + title: '', + component: 'Overview', + }), + buildSubmitField({ + id: 'submitApplication', + title: '', + actions: [ + { + event: DefaultEvents.SUBMIT, + name: m.overviewSubmit, + type: 'primary', + }, + ], + }), + ], + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/asyncSelectSubsection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/asyncSelectSubsection.ts new file mode 100644 index 000000000000..8ebd875bb2c4 --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/asyncSelectSubsection.ts @@ -0,0 +1,77 @@ +import { + buildAsyncSelectField, + buildMultiField, + buildSubSection, +} from '@island.is/application/core' +import { FriggSchoolsByMunicipality } from '../../../utils/types' +import { friggSchoolsByMunicipalityQuery } from '../../../graphql/sampleQuery' + +export const asyncSelectSubsection = buildSubSection({ + id: 'asyncSelectSubsection', + title: 'Async Select Subsection', + children: [ + buildMultiField({ + id: 'asyncSelectMultiField', + title: 'Async Select', + children: [ + buildAsyncSelectField({ + id: 'asyncSelect', + title: 'Async Select', + placeholder: 'Placeholder text', + loadingError: 'Loading error', + loadOptions: async ({ apolloClient }) => { + const { data } = + await apolloClient.query({ + query: friggSchoolsByMunicipalityQuery, + }) + + return ( + data?.friggSchoolsByMunicipality?.map((municipality) => ({ + value: municipality.name, + label: municipality.name, + })) ?? [] + ) + }, + }), + buildAsyncSelectField({ + id: 'asyncSelectSearchable', + title: 'Async Select Searchable', + isSearchable: true, + loadingError: 'Loading error', + loadOptions: async ({ apolloClient }) => { + const { data } = + await apolloClient.query({ + query: friggSchoolsByMunicipalityQuery, + }) + + return ( + data?.friggSchoolsByMunicipality?.map((municipality) => ({ + value: municipality.name, + label: municipality.name, + })) ?? [] + ) + }, + }), + buildAsyncSelectField({ + id: 'asyncSelectMulti', + title: 'Async Select Multi select', + isMulti: true, + loadingError: 'Loading error', + loadOptions: async ({ apolloClient }) => { + const { data } = + await apolloClient.query({ + query: friggSchoolsByMunicipalityQuery, + }) + + return ( + data?.friggSchoolsByMunicipality?.map((municipality) => ({ + value: municipality.name, + label: municipality.name, + })) ?? [] + ) + }, + }), + ], + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/checkboxSubsection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/checkboxSubsection.ts new file mode 100644 index 000000000000..e6c7230dbd83 --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/checkboxSubsection.ts @@ -0,0 +1,37 @@ +import { + buildCheckboxField, + buildMultiField, + buildSubSection, +} from '@island.is/application/core' +import { checkboxOptions } from '../../../utils/options' + +export const checkboxSubsection = buildSubSection({ + id: 'checkbox', + title: 'Checkboxes', + children: [ + buildMultiField({ + id: 'checkboxMultiField', + title: 'Checkboxes', + children: [ + buildCheckboxField({ + id: 'checkbox', + title: 'Full width checkboxes', + options: checkboxOptions, // Importing options from utils makes the template much more readable + }), + buildCheckboxField({ + id: 'checkboxHalf', + title: 'Half width checkboxes', + width: 'half', + options: checkboxOptions, + }), + buildCheckboxField({ + id: 'checkboxHalfStrong', + title: 'Half width strong checkboxes', + width: 'half', + strong: true, + options: checkboxOptions, + }), + ], + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/companySearchSubsection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/companySearchSubsection.ts new file mode 100644 index 000000000000..8376800ce7f6 --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/companySearchSubsection.ts @@ -0,0 +1,33 @@ +import { + buildCompanySearchField, + buildMultiField, + buildSubSection, +} from '@island.is/application/core' + +export const companySearchSubsection = buildSubSection({ + id: 'companySearchSubsection', + title: 'Company Search Subsection', + children: [ + buildMultiField({ + id: 'companySearchMultiField', + title: 'Company Search MultiField', + children: [ + buildCompanySearchField({ + id: 'companySearch', + title: 'Company Search', + placeholder: 'Search for a company', + }), + buildCompanySearchField({ + id: 'companySearchShouldIncludeIsatNumber', + title: 'Company Search Should include ISAT number', + shouldIncludeIsatNumber: true, + }), + buildCompanySearchField({ + id: 'companySearchCheckIfEmployerIsOnForbiddenList', + title: 'Company Search Check if employer is on forbidden list', + checkIfEmployerIsOnForbiddenList: true, + }), + ], + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/dateSubsection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/dateSubsection.ts new file mode 100644 index 000000000000..d113c21aabda --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/dateSubsection.ts @@ -0,0 +1,54 @@ +import { + buildDateField, + buildMultiField, + buildSubSection, +} from '@island.is/application/core' +import { minDate, maxDate } from '../../../utils/dateUtils' + +export const dateSubsection = buildSubSection({ + id: 'date', + title: 'Date', + children: [ + buildMultiField({ + id: 'dateMultiField', + title: 'Date fields', + children: [ + buildDateField({ + id: 'date', + title: 'Regular datepicker', + }), + buildDateField({ + id: 'halfDate', + title: 'Half datepicker', + width: 'half', + }), + buildDateField({ + id: 'minAndMaxDate', + title: 'Min and max dates datepicker', + width: 'half', + minDate: minDate, + maxDate: maxDate, + }), + buildDateField({ + id: 'whiteDate', + title: 'White datepicker (try to use blue if possible)', + width: 'half', + backgroundColor: 'white', + }), + buildDateField({ + id: 'placeholderDate', + title: 'Placeholder datepicker', + placeholder: 'Select a date', + width: 'half', + }), + buildDateField({ + id: 'readOnlyDate', + title: 'Readonly datepicker', + width: 'half', + readOnly: true, + defaultValue: '2024-01-01', + }), + ], + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/displayFieldSubsection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/displayFieldSubsection.ts new file mode 100644 index 000000000000..13bce1204a8e --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/displayFieldSubsection.ts @@ -0,0 +1,104 @@ +import { + buildSubSection, + buildDisplayField, + buildMultiField, + buildDescriptionField, + buildTextField, + getValueViaPath, + buildRadioField, +} from '@island.is/application/core' + +export const displayFieldSubsection = buildSubSection({ + id: 'displayFieldSubsection', + title: 'Display Field', + children: [ + buildMultiField({ + id: 'displayField', + title: 'Display Field', + children: [ + buildDescriptionField({ + id: 'displayFieldDescription', + title: '', + description: + 'Display field is just a read only input field behind the scenes. What is special about the display field is that the value takes a function that listens to changes in answers and updates the value accordingly. This is useful for displaying sums, multiples or anything else that is calculated from other fields.', + }), + buildTextField({ + id: 'input1', + title: 'Input 1', + variant: 'currency', + width: 'half', + rightAlign: true, + }), + buildTextField({ + id: 'input2', + title: 'Input 2', + variant: 'currency', + width: 'half', + rightAlign: true, + }), + buildTextField({ + id: 'input3', + title: 'Input 3', + variant: 'currency', + width: 'half', + rightAlign: true, + }), + buildDisplayField({ + id: 'displayField', + title: 'Display Field', + variant: 'currency', + label: 'Sum of inputs 1, 2 and 3', + rightAlign: true, + value: (answers) => { + const value1 = Number(getValueViaPath(answers, 'input1')) + const value2 = Number(getValueViaPath(answers, 'input2')) + const value3 = Number(getValueViaPath(answers, 'input3')) + return `${value1 + value2 + value3}` + }, + }), + + buildTextField({ + id: 'input4', + title: 'Upphæð leigu', + variant: 'currency', + width: 'half', + rightAlign: true, + }), + buildRadioField({ + id: 'radioFieldForDisplayField', + title: 'Trygging fyrir íbúð', + width: 'half', + options: [ + { label: 'Einföld leiga', value: '1' }, + { label: 'Tvöföld leiga', value: '2' }, + { label: 'Þreföld leiga', value: '3' }, + { label: 'Önnur upphæð', value: 'other' }, + ], + }), + buildDisplayField({ + id: 'displayField2', + title: 'Upphæð leigu', + variant: 'currency', + rightAlign: true, + value: (answers) => { + const value4 = Number(getValueViaPath(answers, 'input4')) + const value5 = getValueViaPath( + answers, + 'radioFieldForDisplayField', + ) + + if (!value4 || !value5) { + return '' + } + + if (value5 === 'other') { + return 'Önnur upphæð' + } + + return `${value4 * Number(value5)}` + }, + }), + ], + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/fileUploadSubsection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/fileUploadSubsection.ts new file mode 100644 index 000000000000..434a89b55c66 --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/fileUploadSubsection.ts @@ -0,0 +1,15 @@ +import { + buildFileUploadField, + buildSubSection, +} from '@island.is/application/core' + +export const fileUploadSubsection = buildSubSection({ + id: 'fileUpload', + title: 'File Upload', + children: [ + buildFileUploadField({ + id: 'fileUpload', + title: 'File Upload', + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/index.ts b/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/index.ts new file mode 100644 index 000000000000..d4f165e3fb9f --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/index.ts @@ -0,0 +1,30 @@ +import { buildSection } from '@island.is/application/core' +import { textInputSubsection } from './textInputSubsection' +import { checkboxSubsection } from './checkboxSubsection' +import { radioSubsection } from './radioSubsection' +import { selectSubsection } from './selectSubsection' +import { phoneSubsection } from './phoneSubsection' +import { dateSubsection } from './dateSubsection' +import { fileUploadSubsection } from './fileUploadSubsection' +import { sliderSubsection } from './sliderSubsection' +import { companySearchSubsection } from './companySearchSubsection' +import { asyncSelectSubsection } from './asyncSelectSubsection' +import { displayFieldSubsection } from './displayFieldSubsection' + +export const simpleInputsSection = buildSection({ + id: 'simpleInputsSection', + title: 'Simple inputs', + children: [ + textInputSubsection, + displayFieldSubsection, + checkboxSubsection, + radioSubsection, + selectSubsection, + asyncSelectSubsection, + companySearchSubsection, + phoneSubsection, + dateSubsection, + fileUploadSubsection, + sliderSubsection, + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/phoneSubsection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/phoneSubsection.ts new file mode 100644 index 000000000000..d3946b41a517 --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/phoneSubsection.ts @@ -0,0 +1,48 @@ +import { + buildMultiField, + buildPhoneField, + buildSubSection, +} from '@island.is/application/core' + +export const phoneSubsection = buildSubSection({ + id: 'phone', + title: 'Phone', + children: [ + buildMultiField({ + id: 'phoneMultiField', + title: 'Phone fields', + children: [ + buildPhoneField({ + id: 'phone', + title: 'Regular', + }), + buildPhoneField({ + id: 'halfPhone', + title: 'Half', + width: 'half', + }), + buildPhoneField({ + id: 'whitePhone', + title: 'White (try to use blue if possible)', + backgroundColor: 'white', + }), + buildPhoneField({ + id: 'placeholderPhone', + title: 'Placeholder', + placeholder: 'Enter your phone number', + }), + buildPhoneField({ + id: 'countrySelectPhone', + title: 'Country select', + enableCountrySelector: true, + }), + buildPhoneField({ + id: 'countrySelectOnlyAllowedPhone', + title: 'Limited country select', + enableCountrySelector: true, + allowedCountryCodes: ['IS', 'IM', 'VC', 'AI', 'AW'], + }), + ], + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/radioSubsection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/radioSubsection.ts new file mode 100644 index 000000000000..9a64349466b6 --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/radioSubsection.ts @@ -0,0 +1,59 @@ +import { + buildMultiField, + buildRadioField, + buildSubSection, +} from '@island.is/application/core' +import { + defaultRadioOption, + radioIllustrationOptions, + radioOptions, +} from '../../../utils/options' + +export const radioSubsection = buildSubSection({ + id: 'radio', + title: 'Radio', + children: [ + buildMultiField({ + id: 'radioMultiField', + title: 'Radio fields', + children: [ + buildRadioField({ + id: 'radio', + title: 'Full width radio', + options: radioOptions, + }), + buildRadioField({ + id: 'halfRadio', + title: 'Half width radio', + width: 'half', + options: radioOptions, + }), + buildRadioField({ + id: 'smallButtonsRadio', + title: 'Small radio buttons', + options: radioOptions, + largeButtons: false, + }), + buildRadioField({ + id: 'radioIllustrations', + title: 'Radio with illustrations', + options: radioIllustrationOptions, + widthWithIllustration: '1/3', + hasIllustration: true, + }), + buildRadioField({ + id: 'defaultRadio', + title: 'Radio with a default value', + options: radioOptions, + defaultValue: defaultRadioOption, + }), + buildRadioField({ + id: 'WhiteBackgroundRadio', + title: 'White background (try to use blue if possible)', + options: radioOptions, + backgroundColor: 'white', + }), + ], + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/selectSubsection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/selectSubsection.ts new file mode 100644 index 000000000000..b781ab57a7e7 --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/selectSubsection.ts @@ -0,0 +1,51 @@ +import { + buildMultiField, + buildSelectField, + buildSubSection, +} from '@island.is/application/core' +import { selectOptions } from '../../../utils/options' +import { title } from 'process' + +export const selectSubsection = buildSubSection({ + id: 'select', + title: 'Select', + children: [ + buildMultiField({ + id: 'selectMultiField', + title: 'Select fields', + children: [ + buildSelectField({ + id: 'select', + title: 'Regular select', + options: selectOptions, + }), + buildSelectField({ + id: 'halfSelect', + title: 'Half select', + options: selectOptions, + width: 'half', + }), + buildSelectField({ + id: 'whiteSelect', + title: 'White (try to use blue if possible)', + options: selectOptions, + width: 'half', + backgroundColor: 'white', + }), + buildSelectField({ + id: 'placeholderSelect', + title: 'Placeholder select', + options: selectOptions, + placeholder: 'Select an option', + width: 'half', + }), + buildSelectField({ + id: 'multiSelect', + title: 'Multi select', + options: selectOptions, + isMulti: true, + }), + ], + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/sliderSubsection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/sliderSubsection.ts new file mode 100644 index 000000000000..80cf17ab9c6c --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/sliderSubsection.ts @@ -0,0 +1,134 @@ +import { + buildMultiField, + buildSliderField, + buildSubSection, +} from '@island.is/application/core' + +export const sliderSubsection = buildSubSection({ + id: 'sliderSubsection', + title: 'Slider Field', + children: [ + buildMultiField({ + id: 'slider', + title: '', + children: [ + buildSliderField({ + id: 'basicSlider', + title: 'Basic slider', + min: 1, + max: 10, + trackStyle: { gridTemplateRows: 5 }, + calculateCellStyle: () => { + return { + background: 'ccccd8', + } + }, + label: { + singular: 'day', + plural: 'days', + }, + }), + buildSliderField({ + id: 'basicSliderWithLabels', + title: 'Slider with labels', + min: 1, + max: 10, + trackStyle: { gridTemplateRows: 5 }, + calculateCellStyle: () => { + return { + background: 'ccccd8', + } + }, + label: { + singular: 'day', + plural: 'days', + }, + showMinMaxLabels: true, + showLabel: true, + showToolTip: true, + }), + buildSliderField({ + id: 'basicSliderWithProgressOverlay', + title: 'Slider with progress overlay', + min: 1, + max: 10, + trackStyle: { gridTemplateRows: 5 }, + calculateCellStyle: () => { + return { + background: 'ccccd8', + } + }, + label: { + singular: 'day', + plural: 'days', + }, + showRemainderOverlay: true, + showProgressOverlay: true, + }), + buildSliderField({ + id: 'basicSliderRangeDates', + title: 'Slider with range dates', + min: 1, + max: 10, + trackStyle: { gridTemplateRows: 5 }, + calculateCellStyle: () => { + return { + background: 'ccccd8', + } + }, + label: { + singular: 'day', + plural: 'days', + }, + rangeDates: { + start: { + date: '2024-01-01', + message: 'Start date', + }, + end: { + date: '2024-01-10', + message: 'End date', + }, + }, + showToolTip: true, + }), + buildSliderField({ + id: 'basicSliderWithSteps', + title: 'Slider with steps', + min: 1, + max: 10, + step: 2, + trackStyle: { gridTemplateRows: 5 }, + calculateCellStyle: () => { + return { + background: 'ccccd8', + } + }, + label: { + singular: 'day', + plural: 'days', + }, + }), + + buildSliderField({ + id: 'thickSlider', + title: 'Thick slider', + min: 0, + max: 100, + step: 1, + label: { + singular: 'day', + plural: 'days', + }, + defaultValue: 5, + trackStyle: { gridTemplateRows: 200 }, + calculateCellStyle: () => { + return { + background: 'ccccd8', + } + }, + }), + ], + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/textInputSubsection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/textInputSubsection.ts new file mode 100644 index 000000000000..6068f5f7fb17 --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/simpleInputsSection/textInputSubsection.ts @@ -0,0 +1,119 @@ +import { + buildMultiField, + buildSubSection, + buildTextField, +} from '@island.is/application/core' + +export const textInputSubsection = buildSubSection({ + id: 'textInput', + title: 'Text Input', + children: [ + buildMultiField({ + id: 'textInput', + title: 'Text input', + children: [ + buildTextField({ + id: 'textInput', + title: '', + placeholder: 'The most basic text input', + }), + buildTextField({ + id: 'halfTextInput', + title: '', + placeholder: 'Half width text input', + width: 'half', + }), + buildTextField({ + id: 'rightAlignedTextInput', + title: '', + placeholder: 'Right aligned', + width: 'half', + rightAlign: true, + }), + buildTextField({ + id: 'readOnlyTextInput', + title: '', + defaultValue: 'Read only', + width: 'half', + readOnly: true, + }), + buildTextField({ + id: 'maxLengthTextInput', + title: '', + placeholder: 'Max length 3', + width: 'half', + maxLength: 3, + }), + buildTextField({ + id: 'formatTextInput', + title: '', + placeholder: 'Special format (kennitala)', + format: '######-####', + }), + buildTextField({ + id: 'whiteBackgroundTextInput', + title: '', + placeholder: 'White background (try to use blue if possible)', + backgroundColor: 'white', + }), + buildTextField({ + id: 'textTextInput', + title: '', + placeholder: 'Variant text (same as default)', + variant: 'text', + width: 'half', + }), + buildTextField({ + id: 'emailTextInput', + title: '', + placeholder: 'Variant email', + variant: 'email', + width: 'half', + }), + buildTextField({ + id: 'numberTextInput', + title: '', + placeholder: 'Variant number', + variant: 'number', + width: 'half', + }), + buildTextField({ + id: 'currencyTextInput', + title: '', + placeholder: 'Variant currency', + variant: 'currency', + width: 'half', + }), + buildTextField({ + id: 'currencyTextInput2', + title: '', + placeholder: 'Variant currency ($)', + variant: 'currency', + width: 'half', + suffix: ' $', + }), + buildTextField({ + id: 'currencyTextInput3', + title: '', + placeholder: 'Variant currency (€)', + variant: 'currency', + width: 'half', + suffix: ' €', + }), + buildTextField({ + id: 'telTextInput', + title: '', + placeholder: 'Variant tel (try to use buildPhoneField)', + variant: 'tel', + }), + buildTextField({ + id: 'textAreaTextInput', + title: '', + placeholder: 'Textarea', + variant: 'textarea', + rows: 10, + }), + ], + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/tablesAndRepeatersSection/StaticTableSubsection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/tablesAndRepeatersSection/StaticTableSubsection.ts new file mode 100644 index 000000000000..2f74e254c30e --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/tablesAndRepeatersSection/StaticTableSubsection.ts @@ -0,0 +1,48 @@ +import { + buildSubSection, + buildStaticTableField, +} from '@island.is/application/core' + +export const staticTableSubsection = buildSubSection({ + id: 'staticTableSubsection', + title: 'Static table', + children: [ + buildStaticTableField({ + title: 'Static table', + // Header, rows and summary can also be functions that access external data or answers + header: [ + 'Table heading 1', + 'Table heading 2', + 'Table heading 3', + 'Table heading 4', + ], + rows: [ + [ + 'Row 1, Column 1', + 'Row 1, Column 2', + 'Row 1, Column 3', + 'Row 1, Column 4', + ], + [ + 'Row 2, Column 1', + 'Row 2, Column 2', + 'Row 2, Column 3', + 'Row 2, Column 4', + ], + [ + 'Row 3, Column 1', + 'Row 3, Column 2', + 'Row 3, Column 3', + 'Row 3, Column 4', + ], + ], + // Summary is optional + summary: [ + { + label: 'Summary label', + value: 'Summary value', + }, + ], + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/tablesAndRepeatersSection/fieldsRepeaterSubsection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/tablesAndRepeatersSection/fieldsRepeaterSubsection.ts new file mode 100644 index 000000000000..a38927bf858a --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/tablesAndRepeatersSection/fieldsRepeaterSubsection.ts @@ -0,0 +1,79 @@ +import { + buildDescriptionField, + buildFieldsRepeaterField, + buildMultiField, + buildSubSection, +} from '@island.is/application/core' + +export const fieldsRepeaterSubsection = buildSubSection({ + id: 'fieldsRepeaterSubsection', + title: 'Fields Repeater Field', + children: [ + buildMultiField({ + id: 'fieldsRepeater', + title: 'Fields Repeater', + children: [ + buildDescriptionField({ + id: 'fieldsRepeaterDescription', + title: '', + description: + 'FieldsRepeater works similarly to tableRepeater, in that it contains a set of fields to fill out and this set can be repeated as many times as needed. The difference is that in tableRepeater, the values go into a table, while in fieldsRepeater, all fields created are always visible.', + }), + buildFieldsRepeaterField({ + id: 'fieldsRepeater', + title: 'Fields Repeater', + formTitle: 'Title for each form', + width: 'half', + fields: { + input: { + component: 'input', + label: 'Regular input', + width: 'half', + type: 'text', + format: '######-####', + }, + select: { + component: 'select', + label: 'Select', + width: 'half', + options: [ + { label: 'Option 1', value: 'option1' }, + { label: 'Option 2', value: 'option2' }, + ], + }, + radio: { + component: 'radio', + width: 'half', + options: [ + { label: 'Option 1', value: 'option1' }, + { label: 'Option 2', value: 'option2' }, + { label: 'Option 3', value: 'option3' }, + ], + }, + checkbox: { + component: 'checkbox', + options: [ + { label: 'Option 1', value: 'option1' }, + { label: 'Option 2', value: 'option2' }, + ], + }, + date: { + component: 'date', + label: 'Date', + width: 'half', + }, + nationalIdWithName: { + component: 'nationalIdWithName', + label: 'National ID with name', + }, + phone: { + component: 'phone', + label: 'Phone', + width: 'half', + }, + }, + }), + ], + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/tablesAndRepeatersSection/index.ts b/libs/application/templates/reference-template/src/forms/exampleForm/tablesAndRepeatersSection/index.ts new file mode 100644 index 000000000000..3345edd8491e --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/tablesAndRepeatersSection/index.ts @@ -0,0 +1,14 @@ +import { buildSection } from '@island.is/application/core' +import { tableRepeaterSubsection } from './tableRepeaterSubsection' +import { staticTableSubsection } from './StaticTableSubsection' +import { fieldsRepeaterSubsection } from './fieldsRepeaterSubsection' + +export const tablesAndRepeatersSection = buildSection({ + id: 'tablesAndRepeatersSection', + title: 'Tables and repeaters', + children: [ + staticTableSubsection, + tableRepeaterSubsection, + fieldsRepeaterSubsection, + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/exampleForm/tablesAndRepeatersSection/tableRepeaterSubsection.ts b/libs/application/templates/reference-template/src/forms/exampleForm/tablesAndRepeatersSection/tableRepeaterSubsection.ts new file mode 100644 index 000000000000..06c95a4d4e12 --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/exampleForm/tablesAndRepeatersSection/tableRepeaterSubsection.ts @@ -0,0 +1,138 @@ +import { + buildDescriptionField, + buildMultiField, + buildSubSection, + buildTableRepeaterField, +} from '@island.is/application/core' + +export const tableRepeaterSubsection = buildSubSection({ + id: 'repeater', + title: 'Table Repeater', + children: [ + buildMultiField({ + id: 'tableRepeater', + title: 'Table Repeater Field', + children: [ + buildDescriptionField({ + id: 'tableRepeaterDescription', + title: '', + description: + 'In the table repeater, you create a small form that the user fills out and the answers are then sorted into a table. Only one instance of this form is visible at a time. In the table, you can delete and edit rows, and you can disable this functionality. You can also insert data into the table from answers or external data, similar to staticTable.', + marginBottom: 2, + }), + buildDescriptionField({ + id: 'tableRepeaterDescription2', + title: '', + description: + 'In the table repeater, you can use input, select, radio, checkbox, date, nationalIdWithName and phone.', + }), + buildTableRepeaterField({ + id: 'tableRepeater', + title: 'Table Repeater Field', + formTitle: 'Table Repeater Form Title', // Todo: doesn't work + addItemButtonText: 'Custom Add item text', + saveItemButtonText: 'Custom Save item text', + removeButtonTooltipText: 'Custom Remove item text', + editButtonTooltipText: 'Custom Edit item text', + editField: true, + maxRows: 10, + getStaticTableData: (_application) => { + // Possibility to populate the table with data from the answers or external data + // Populated data will not be editable or deletable + return [ + { + input: 'John Doe', + select: 'option 1', + radio: 'option 2', + checkbox: 'option 3', + date: '2024-01-01', + name: 'Test Name', + nationalId: '000000-0000', + phone: '6666666', + }, + { + input: 'Jane Doe', + select: 'option 1', + radio: 'option 2', + checkbox: 'option 3', + date: '2024-01-01', + name: 'Test Name 2', + nationalId: '100000-0000', + phone: '6666666', + }, + ] + }, + // Possible fields: input, select, radio, checkbox, date, nationalIdWithName + fields: { + input: { + component: 'input', + label: 'Regular input', + width: 'half', + required: true, + type: 'text', + }, + select: { + component: 'select', + label: 'Select', + width: 'half', + options: [ + { label: 'Option 1', value: 'option1' }, + { label: 'Option 2', value: 'option2' }, + ], + }, + radio: { + component: 'radio', + width: 'half', + options: [ + { label: 'Option 1', value: 'option1' }, + { label: 'Option 2', value: 'option2' }, + { label: 'Option 3', value: 'option3' }, + ], + }, + checkbox: { + component: 'checkbox', + options: [ + { label: 'Option 1', value: 'option1' }, + { label: 'Option 2', value: 'option2' }, + ], + }, + date: { + component: 'date', + label: 'Date', + width: 'half', + }, + nationalIdWithName: { + component: 'nationalIdWithName', + label: 'National ID with name', + }, + phone: { + component: 'phone', + label: 'Phone', + width: 'half', + }, + }, + table: { + // Format values for display in the table + format: { + input: (value) => `${value} - custom format`, + nationalIdWithName: (value) => { + return `${value} - custom format` + }, + }, + // Overwrite header for the table. If not provided, the labels from the fields will be used + header: [ + 'Input', + 'Select', + 'Radio', + 'Checkbox', + 'Date', + 'Name', + 'NationalId', + 'Phone', + ], + }, + }), + ], + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/PendingReview.ts b/libs/application/templates/reference-template/src/forms/pendingReviewForm/pendingReview.ts similarity index 78% rename from libs/application/templates/reference-template/src/forms/PendingReview.ts rename to libs/application/templates/reference-template/src/forms/pendingReviewForm/pendingReview.ts index 4ae331750e1e..23830ee5fd0e 100644 --- a/libs/application/templates/reference-template/src/forms/PendingReview.ts +++ b/libs/application/templates/reference-template/src/forms/pendingReviewForm/pendingReview.ts @@ -8,8 +8,8 @@ export const PendingReview: Form = buildForm({ children: [ buildDescriptionField({ id: 'inReview', - title: 'Í vinnslu', - description: 'Umsókn þín um ökunám er nú í vinnslu. ', + title: 'In review', + description: 'Your application is now in review. ', }), ], }) diff --git a/libs/application/templates/reference-template/src/forms/prerequisitesForm/prerequisitesForm.ts b/libs/application/templates/reference-template/src/forms/prerequisitesForm/prerequisitesForm.ts new file mode 100644 index 000000000000..7918bc4f5f8d --- /dev/null +++ b/libs/application/templates/reference-template/src/forms/prerequisitesForm/prerequisitesForm.ts @@ -0,0 +1,84 @@ +import { + buildForm, + buildSection, + buildExternalDataProvider, + buildDataProviderItem, + buildSubmitField, + coreMessages, +} from '@island.is/application/core' +import { + Application, + DefaultEvents, + Form, + FormModes, +} from '@island.is/application/types' +import { UserProfileApi } from '@island.is/application/types' +import { + ReferenceDataApi, + MyMockProvider, + NationalRegistryApi, +} from '../../dataProviders' +import { createElement } from 'react' +import { Logo } from '../../components/Logo/Logo' + +export const Prerequisites: Form = buildForm({ + id: 'PrerequisitesDraft', + title: 'Forkröfur', + mode: FormModes.NOT_STARTED, + // The logo prop can either take in a React component or a function that returns a React component. + // Dynamic logo can be based on answers or external data + logo: (application: Application) => { + const logo = createElement(Logo, { application }) + return () => logo + }, + renderLastScreenButton: true, + children: [ + buildSection({ + id: 'conditions', + title: '', // If this is empty, we will not have a visible stepper on the right side of the screen. + tabTitle: 'Forkröfur', // If there is no stepper, add tabTitle to have a title on the browser tab. + children: [ + buildExternalDataProvider({ + id: 'approveExternalData', + title: 'External data', + dataProviders: [ + buildDataProviderItem({ + provider: UserProfileApi, + title: 'User profile', + subTitle: 'User profile', + }), + buildDataProviderItem({ + provider: ReferenceDataApi, + title: 'getReferenceData', + subTitle: 'Reference data', + }), + buildDataProviderItem({ + provider: NationalRegistryApi, + title: 'National Registry', + subTitle: 'Information about you in the National Registry.', + }), + buildDataProviderItem({ + provider: MyMockProvider, + title: 'Mock Data', + subTitle: 'Returns data for mocking', + }), + ], + // Button to trigger the submit event to move the application from the NOT_STARTED state to the DRAFT state. + submitField: buildSubmitField({ + id: 'submit', + placement: 'footer', + title: '', + refetchApplicationAfterSubmit: true, + actions: [ + { + event: DefaultEvents.SUBMIT, + name: coreMessages.buttonNext, + type: 'primary', + }, + ], + }), + }), + ], + }), + ], +}) diff --git a/libs/application/templates/reference-template/src/forms/Rejected.ts b/libs/application/templates/reference-template/src/forms/rejectedForm/rejectedForm.ts similarity index 74% rename from libs/application/templates/reference-template/src/forms/Rejected.ts rename to libs/application/templates/reference-template/src/forms/rejectedForm/rejectedForm.ts index 9e0a4b189554..c3149752b103 100644 --- a/libs/application/templates/reference-template/src/forms/Rejected.ts +++ b/libs/application/templates/reference-template/src/forms/rejectedForm/rejectedForm.ts @@ -8,8 +8,8 @@ export const Rejected: Form = buildForm({ children: [ buildDescriptionField({ id: 'rejected', - title: 'Því miður...', - description: 'Umsókn þinni verið hafnað! Það er frekar leiðinlegt.', + title: 'Sorry...', + description: 'Your application has been rejected! It is pretty sad.', }), ], }) diff --git a/libs/application/templates/reference-template/src/forms/ReviewApplication.ts b/libs/application/templates/reference-template/src/forms/reviewApplicationForm/reviewApplication.ts similarity index 90% rename from libs/application/templates/reference-template/src/forms/ReviewApplication.ts rename to libs/application/templates/reference-template/src/forms/reviewApplicationForm/reviewApplication.ts index bd1b2815fafe..57bc24e5e6af 100644 --- a/libs/application/templates/reference-template/src/forms/ReviewApplication.ts +++ b/libs/application/templates/reference-template/src/forms/reviewApplicationForm/reviewApplication.ts @@ -10,7 +10,7 @@ import { buildTextField, } from '@island.is/application/core' import { Form, FormModes } from '@island.is/application/types' -import { m } from '../lib/messages' +import { m } from '../../lib/messages' export const ReviewApplication: Form = buildForm({ id: 'ExampleInReview', @@ -58,7 +58,7 @@ export const ReviewApplication: Form = buildForm({ buildDividerField({ title: 'Atvinna' }), buildRadioField({ id: 'careerHistory', - title: m.careerHistory, + title: 'def', width: 'half', disabled: true, options: [ @@ -68,7 +68,7 @@ export const ReviewApplication: Form = buildForm({ }), buildCheckboxField({ id: 'careerHistoryDetails.careerHistoryCompanies', - title: m.careerHistoryCompanies, + title: 'abc', disabled: true, width: 'half', options: [ @@ -81,17 +81,17 @@ export const ReviewApplication: Form = buildForm({ buildTextField({ id: 'careerHistoryDetails.careerHistoryOther', disabled: true, - title: m.careerHistoryOther, + title: 'ghi', }), buildTextField({ id: 'dreamJob', - title: m.dreamJob, + title: 'jkl', disabled: true, }), buildSubmitField({ id: 'approvedByReviewer', placement: 'screen', - title: 'Samþykkirðu þessa umsókn?', + title: 'Do yo uapprove this application?', actions: [ { event: 'APPROVE', name: 'Samþykkja', type: 'primary' }, { event: 'REJECT', name: 'Hafna', type: 'reject' }, @@ -103,7 +103,7 @@ export const ReviewApplication: Form = buildForm({ id: 'final', title: 'Takk fyrir', description: - 'Úrvinnslu þinni er lokið. Umsókn er komin áfram í ferlinu.', + 'Your processing is complete. The application has been forwarded to the next step.', }), ], }), diff --git a/libs/application/templates/reference-template/src/forms/WaitingToAssign.ts b/libs/application/templates/reference-template/src/forms/waitingToAssignForm/waitingToAssignForm.ts similarity index 76% rename from libs/application/templates/reference-template/src/forms/WaitingToAssign.ts rename to libs/application/templates/reference-template/src/forms/waitingToAssignForm/waitingToAssignForm.ts index e92b2e0a9cb3..06c29afc238b 100644 --- a/libs/application/templates/reference-template/src/forms/WaitingToAssign.ts +++ b/libs/application/templates/reference-template/src/forms/waitingToAssignForm/waitingToAssignForm.ts @@ -8,7 +8,7 @@ import { Form, FormModes } from '@island.is/application/types' export const PendingReview: Form = buildForm({ id: 'ExamplePending', - title: 'Í vinnslu', + title: 'In review', mode: FormModes.IN_PROGRESS, children: [ buildMultiField({ @@ -16,15 +16,15 @@ export const PendingReview: Form = buildForm({ children: [ buildDescriptionField({ id: 'waitingToAssign', - title: 'Í bið', - description: 'Beðið eftir umsjón.', + title: 'In review', + description: 'Waiting for review.', }), buildSubmitField({ id: 'submitWaiting', placement: 'footer', - title: 'Halda áfram', + title: 'Continue', refetchApplicationAfterSubmit: true, - actions: [{ event: 'SUBMIT', name: 'Halda áfram', type: 'primary' }], + actions: [{ event: 'SUBMIT', name: 'Continue', type: 'primary' }], }), ], }), diff --git a/libs/application/templates/reference-template/src/graphql/README.md b/libs/application/templates/reference-template/src/graphql/README.md new file mode 100644 index 000000000000..a9bcbd3f5a95 --- /dev/null +++ b/libs/application/templates/reference-template/src/graphql/README.md @@ -0,0 +1,3 @@ +# Graphql + +This folder is optional and can be used for all the graphql queries and mutations that are used by the application. diff --git a/libs/application/templates/reference-template/src/graphql/sampleQuery.ts b/libs/application/templates/reference-template/src/graphql/sampleQuery.ts new file mode 100644 index 000000000000..ff815976888a --- /dev/null +++ b/libs/application/templates/reference-template/src/graphql/sampleQuery.ts @@ -0,0 +1,18 @@ +import gql from 'graphql-tag' +export const friggSchoolsByMunicipalityQuery = gql` + query FriggSchoolsByMunicipality { + friggSchoolsByMunicipality { + id + nationalId + name + type + children { + id + nationalId + name + type + gradeLevels + } + } + } +` diff --git a/libs/application/templates/reference-template/src/lib/README.md b/libs/application/templates/reference-template/src/lib/README.md new file mode 100644 index 000000000000..538841b09b22 --- /dev/null +++ b/libs/application/templates/reference-template/src/lib/README.md @@ -0,0 +1,17 @@ +# The lib folder + +This folder contains all the data schema, messages, and the main template file. + +## Data schema + +The data schema defines what the answer object should look like. All validation is done with zod. + +## Messages + +Messages can either be one single file with all messages needed for the application. If more organization is needed, /messages can also be a folder with the messages split up into multiple files. + +## Main template file + +The main template file holds the state machine for the application. It defines how the application should flow from one state to the next and which form to load in each state. Here you can also leverage the `mapUserToRole` function to define which role the user has in the application. This allows you to display different forms to different users even if the application is in the same state. This can be usefull for applications that can both be done as an individual and as a procurer. + +The state machine also holds the actions that are run when the application is in a certain state. diff --git a/libs/application/templates/reference-template/src/lib/ReferenceApplicationTemplate.ts b/libs/application/templates/reference-template/src/lib/ReferenceApplicationTemplate.ts index 579378424e97..c4cd3c8b8fd3 100644 --- a/libs/application/templates/reference-template/src/lib/ReferenceApplicationTemplate.ts +++ b/libs/application/templates/reference-template/src/lib/ReferenceApplicationTemplate.ts @@ -1,3 +1,14 @@ +/* + *** + *** The state machine is for this template is as follows: + *** + *** /-> Approved + *** Prerequisites -> Draft -> Waiting to assign -> In review -- + *** Λ | \-> Rejected + *** |_____________| + *** + */ + import { DefaultStateLifeCycle, getValueViaPath, @@ -26,16 +37,8 @@ import { MyMockProvider, NationalRegistryApi, } from '../dataProviders' -import { ExampleSchema } from './dataSchema' - -const States = { - prerequisites: 'prerequisites', - draft: 'draft', - inReview: 'inReview', - approved: 'approved', - rejected: 'rejected', - waitingToAssign: 'waitingToAssign', -} +import { dataSchema } from './dataSchema' +import { States } from '../utils/constants' type ReferenceTemplateEvent = | { type: DefaultEvents.APPROVE } @@ -50,19 +53,19 @@ enum Roles { } const determineMessageFromApplicationAnswers = (application: Application) => { - const careerHistory = getValueViaPath( + const careerHistory = getValueViaPath( application.answers, 'careerHistory', undefined, - ) as string | undefined - const careerIndustry = getValueViaPath( + ) + const careerIndustry = getValueViaPath( application.answers, 'careerIndustry', undefined, - ) as string | undefined + ) if (careerHistory === 'no') { - return m.nameApplicationNeverWorkedBefore + return 'abcdef' } if (careerIndustry) { return { @@ -72,6 +75,7 @@ const determineMessageFromApplicationAnswers = (application: Application) => { } return m.name } + const ReferenceApplicationTemplate: ApplicationTemplate< ApplicationContext, ApplicationStateSchema, @@ -81,14 +85,14 @@ const ReferenceApplicationTemplate: ApplicationTemplate< name: determineMessageFromApplicationAnswers, institution: m.institutionName, translationNamespaces: [ApplicationConfigurations.ExampleForm.translation], - dataSchema: ExampleSchema, + dataSchema: dataSchema, featureFlag: Features.exampleApplication, allowMultipleApplicationsInDraft: true, stateMachineConfig: { - initial: States.prerequisites, + initial: States.PREREQUISITES, states: { - [States.prerequisites]: { + [States.PREREQUISITES]: { meta: { name: 'Skilyrði', progress: 0, @@ -103,8 +107,8 @@ const ReferenceApplicationTemplate: ApplicationTemplate< { id: Roles.APPLICANT, formLoader: () => - import('../forms/Prerequisites').then((module) => - Promise.resolve(module.Prerequisites), + import('../forms/prerequisitesForm/prerequisitesForm').then( + (module) => Promise.resolve(module.Prerequisites), ), actions: [ { event: 'SUBMIT', name: 'Staðfesta', type: 'primary' }, @@ -131,15 +135,14 @@ const ReferenceApplicationTemplate: ApplicationTemplate< ], }, on: { - SUBMIT: { - target: States.draft, + [DefaultEvents.SUBMIT]: { + target: States.DRAFT, }, }, }, - [States.draft]: { + [States.DRAFT]: { meta: { - name: 'Umsókn um ökunám', - + name: 'Dæmi um umsókn', actionCard: { description: m.draftDescription, historyLogs: { @@ -154,7 +157,7 @@ const ReferenceApplicationTemplate: ApplicationTemplate< { id: Roles.APPLICANT, formLoader: () => - import('../forms/ExampleForm').then((module) => + import('../forms/exampleForm').then((module) => Promise.resolve(module.ExampleForm), ), actions: [ @@ -166,14 +169,14 @@ const ReferenceApplicationTemplate: ApplicationTemplate< ], }, on: { - SUBMIT: [ + [DefaultEvents.SUBMIT]: [ { - target: States.waitingToAssign, + target: States.WAITINGTOASSIGN, }, ], }, }, - [States.waitingToAssign]: { + [States.WAITINGTOASSIGN]: { meta: { name: 'Waiting to assign', progress: 0.75, @@ -207,8 +210,8 @@ const ReferenceApplicationTemplate: ApplicationTemplate< { id: Roles.APPLICANT, formLoader: () => - import('../forms/WaitingToAssign').then((val) => - Promise.resolve(val.PendingReview), + import('../forms/waitingToAssignForm/waitingToAssignForm').then( + (val) => Promise.resolve(val.PendingReview), ), read: 'all', write: 'all', @@ -217,20 +220,20 @@ const ReferenceApplicationTemplate: ApplicationTemplate< { id: Roles.ASSIGNEE, formLoader: () => - import('../forms/WaitingToAssign').then((val) => - Promise.resolve(val.PendingReview), + import('../forms/waitingToAssignForm/waitingToAssignForm').then( + (val) => Promise.resolve(val.PendingReview), ), read: 'all', }, ], }, on: { - SUBMIT: { target: States.inReview }, - ASSIGN: { target: States.inReview }, - EDIT: { target: States.draft }, + [DefaultEvents.SUBMIT]: { target: States.INREVIEW }, + [DefaultEvents.ASSIGN]: { target: States.INREVIEW }, + [DefaultEvents.EDIT]: { target: States.DRAFT }, }, }, - [States.inReview]: { + [States.INREVIEW]: { meta: { name: 'In Review', progress: 0.75, @@ -263,8 +266,8 @@ const ReferenceApplicationTemplate: ApplicationTemplate< { id: Roles.ASSIGNEE, formLoader: () => - import('../forms/ReviewApplication').then((val) => - Promise.resolve(val.ReviewApplication), + import('../forms/reviewApplicationForm/reviewApplication').then( + (val) => Promise.resolve(val.ReviewApplication), ), actions: [ { event: 'APPROVE', name: 'Samþykkja', type: 'primary' }, @@ -279,7 +282,7 @@ const ReferenceApplicationTemplate: ApplicationTemplate< { id: Roles.APPLICANT, formLoader: () => - import('../forms/PendingReview').then((val) => + import('../forms/pendingReviewForm/pendingReview').then((val) => Promise.resolve(val.PendingReview), ), read: 'all', @@ -287,11 +290,11 @@ const ReferenceApplicationTemplate: ApplicationTemplate< ], }, on: { - APPROVE: { target: States.approved }, - REJECT: { target: States.rejected }, + [DefaultEvents.APPROVE]: { target: States.APPROVED }, + [DefaultEvents.REJECT]: { target: States.REJECTED }, }, }, - [States.approved]: { + [States.APPROVED]: { meta: { name: 'Approved', progress: 1, @@ -301,7 +304,7 @@ const ReferenceApplicationTemplate: ApplicationTemplate< { id: Roles.APPLICANT, formLoader: () => - import('../forms/Approved').then((val) => + import('../forms/approvedForm/approvedForm').then((val) => Promise.resolve(val.Approved), ), read: 'all', @@ -309,18 +312,17 @@ const ReferenceApplicationTemplate: ApplicationTemplate< ], }, }, - [States.rejected]: { + [States.REJECTED]: { meta: { name: 'Rejected', progress: 1, status: 'rejected', lifecycle: DefaultStateLifeCycle, - roles: [ { id: Roles.APPLICANT, formLoader: () => - import('../forms/Rejected').then((val) => + import('../forms/rejectedForm/rejectedForm').then((val) => Promise.resolve(val.Rejected), ), }, diff --git a/libs/application/templates/reference-template/src/lib/dataSchema.ts b/libs/application/templates/reference-template/src/lib/dataSchema.ts index e4945ef27981..88387124a8c7 100644 --- a/libs/application/templates/reference-template/src/lib/dataSchema.ts +++ b/libs/application/templates/reference-template/src/lib/dataSchema.ts @@ -1,7 +1,21 @@ +/* + * DataSchema uses Zod to validate the answers object and can be used to refine values, provide + * error messages, and more. + * + * When checking if a value is of an enum type, use z.nativeEnum instead of z.enum. This eliminates the need to list up all possible values in the enum. + */ + import { z } from 'zod' import * as kennitala from 'kennitala' import { isValidNumber } from 'libphonenumber-js' import { m } from './messages' +import { + ApprovedByReviewerEnum, + CareerHistoryEnum, + CareerIndustryEnum, + YesNoEnum, +} from '../utils/constants' +import { radioValidationExampleEnum } from '../utils/types' const careerHistoryCompaniesValidation = (data: any) => { // Applicant selected other but didnt supply the reason so we dont allow it @@ -13,6 +27,18 @@ const careerHistoryCompaniesValidation = (data: any) => { } return true } + +const personSchema = z.object({ + name: z.string().min(1).max(256), + age: z.string().refine((x) => { + const asNumber = parseInt(x) + if (isNaN(asNumber)) { + return false + } + return asNumber > 15 + }), +}) + export const ExampleSchema = z.object({ approveExternalData: z.boolean().refine((v) => v), tableRepeaterField: z.array( @@ -58,32 +84,37 @@ export const ExampleSchema = z.object({ // .string() // .refine((x) => x.includes('Valid')), }), - careerHistory: z.enum(['yes', 'no']).optional(), - careerIndustry: z.enum(['software', 'finance', 'consulting', 'other']), - careerHistoryDetails: z - .object({ - careerHistoryCompanies: z - .array(z.enum(['government', 'aranja', 'advania', 'other'])) - .nonempty(), - careerHistoryOther: z.string(), - }) - .partial() - .refine((data) => careerHistoryCompaniesValidation(data), { - params: m.careerHistoryOtherError, - path: ['careerHistoryOther'], - }), - deepNestedValues: z.object({ - something: z.object({ - very: z.object({ - deep: z.object({ + nationalId: z.string().refine((n) => n && !kennitala.isValid(n), { + params: m.dataSchemeNationalId, + }), + phoneNumber: z + .string() + .refine(isValidNumber, { params: m.dataSchemePhoneNumber }), + email: z.string().email(), +}) + +const careerHistoryDetailsSchema = z + .object({ + careerHistoryCompanies: z.array(z.nativeEnum(CareerHistoryEnum)).nonempty(), + careerHistoryOther: z.string(), + }) + .partial() + .refine((data) => careerHistoryCompaniesValidation(data), { + params: m.regularTextExample, + path: ['careerHistoryOther'], + }) + +const deepNestedSchema = z.object({ + something: z.object({ + very: z.object({ + deep: z.object({ + so: z.object({ so: z.object({ - so: z.object({ + very: z.object({ very: z.object({ - very: z.object({ - deep: z.object({ - nested: z.object({ - value: z.string(), - }), + deep: z.object({ + nested: z.object({ + value: z.string(), }), }), }), @@ -93,7 +124,27 @@ export const ExampleSchema = z.object({ }), }), }), +}) + +const validationSchema = z.object({ + validationTextField: z.string().min(3, { + message: 'Custom validation message', + }), + validationRadioField: z.nativeEnum(radioValidationExampleEnum), +}) + +// The exported dataSchema should be as flat and easy to read as possible. +export const dataSchema = z.object({ + approveExternalData: z.boolean().refine((v) => v), + person: personSchema, + careerHistory: z.nativeEnum(YesNoEnum).optional(), + careerIndustry: z.nativeEnum(CareerIndustryEnum), + careerHistoryDetails: careerHistoryDetailsSchema, + deepNestedValues: deepNestedSchema, dreamJob: z.string().optional(), assigneeEmail: z.string().email(), - approvedByReviewer: z.enum(['APPROVE', 'REJECT']), + approvedByReviewer: z.nativeEnum(ApprovedByReviewerEnum), + validation: validationSchema, }) + +export type AnswersSchema = z.infer diff --git a/libs/application/templates/reference-template/src/lib/messages.ts b/libs/application/templates/reference-template/src/lib/messages.ts index c12509c9901e..9b332c340cf1 100644 --- a/libs/application/templates/reference-template/src/lib/messages.ts +++ b/libs/application/templates/reference-template/src/lib/messages.ts @@ -3,7 +3,7 @@ import { defineMessages } from 'react-intl' export const m = defineMessages({ conditionsSection: { id: 'example.application:conditions.section', - defaultMessage: 'Skilyrði', + defaultMessage: 'Conditions', description: 'Some description', }, institutionName: { @@ -13,118 +13,79 @@ export const m = defineMessages({ }, name: { id: 'example.application:name', - defaultMessage: 'Umsókn', - description: `Application's name`, - }, - nameApplicationNeverWorkedBefore: { - id: 'example.application:name.application.never.worked.before', - defaultMessage: 'Umsókn - Aldrei unnið áður', + defaultMessage: 'Application', description: `Application's name`, }, nameApplicationWithValue: { id: 'example.application:name.application.with.value', - defaultMessage: 'Umsókn {value}', + defaultMessage: 'Application {value}', description: `Application's name with value`, }, draftTitle: { id: 'example.application:draft.title', - defaultMessage: 'Drög', + defaultMessage: 'Draft', description: 'First state title', }, draftDescription: { id: 'example.application:draft.description', - defaultMessage: 'Notendur hafa ekkert að gera á þessu stigi', + defaultMessage: 'Users have nothing to do at this stage', description: 'Description of the state', }, introSection: { id: 'example.application:intro.section', - defaultMessage: 'Upplýsingar', + defaultMessage: 'Information', description: 'Some description', }, - introField: { - id: 'example.application:intro.field', - defaultMessage: 'Velkomin(n)', + introTitle: { + id: 'example.application:intro.title', + defaultMessage: 'Welcome', description: 'Some description', }, - introIntroduction: { - id: 'example.application:intro.introduction', + introDescription: { + id: 'example.application:intro.description', defaultMessage: - '*Hello*, **{name}**! [This is a link to Google!](http://google.com)', + 'This application shows how to build an application and what components are available. Each application is split into a few different forms that are loaded depending on the application state and/or the user role. Right now the application is showing the "exampleForm" and the application state is "draft". This means that you got through the first form "prerequisitesForm" where the state was "prerequisites".', + description: 'Some description', + }, + introDescription2: { + id: 'example.application:intro.description2', + defaultMessage: + 'This form covers all possible components that the application system offers and they are shown with different settings that results in the components appearing or behaving in different ways.', description: 'Some description', }, about: { id: 'example.application:about', - defaultMessage: 'Um þig', + defaultMessage: 'About you', description: 'Some description', }, personName: { id: 'example.application:person.name', - defaultMessage: 'Nafn', + defaultMessage: 'Name', description: 'Some description', }, nationalId: { id: 'example.application:person.nationalId', - defaultMessage: 'Kennitala', + defaultMessage: 'National ID', description: 'Some description', }, age: { id: 'example.application:person.age', - defaultMessage: 'Aldur', + defaultMessage: 'Age', description: 'Some description', }, email: { id: 'example.application:person.email', - defaultMessage: 'Netfang', + defaultMessage: 'Email', description: 'Some description', }, phoneNumber: { id: 'example.application:person.phoneNumber', - defaultMessage: 'Símanúmer', - description: 'Some description', - }, - career: { - id: 'example.application:career', - defaultMessage: 'Starfsferill', - description: 'Some description', - }, - history: { - id: 'example.application:history', - defaultMessage: 'Hvar hefur þú unnið áður?', - description: 'Some description', - }, - careerIndustry: { - id: 'example.application:career.industry', - defaultMessage: 'Starfsgeiri', - description: 'Some description', - }, - careerIndustryDescription: { - id: 'example.application:career.industryDescription', - defaultMessage: 'Í hvaða geira hefur þú unnið?', - description: 'Some description', - }, - careerHistory: { - id: 'example.application:careerHistory', - defaultMessage: 'Hefurðu unnið yfir höfuð einhvern tímann áður?', - description: 'Some description', - }, - careerHistoryCompanies: { - id: 'example.application:careerHistoryCompanies', - defaultMessage: 'Hefurðu unnið fyrir eftirfarandi aðila?', - description: 'Some description', - }, - future: { - id: 'example.application:future', - defaultMessage: 'Hvar langar þig að vinna?', - description: 'Some description', - }, - dreamJob: { - id: 'example.application:dreamJob', - defaultMessage: 'Einhver draumavinnustaður?', + defaultMessage: 'Phone number', description: 'Some description', }, assigneeTitle: { id: 'example.application:assigneeTitle', - defaultMessage: 'Hver á að fara yfir?', + defaultMessage: 'Who should review?', description: 'Some description', }, assignee: { @@ -134,12 +95,12 @@ export const m = defineMessages({ }, yesOptionLabel: { id: 'example.application:yes.option.label', - defaultMessage: 'Já', + defaultMessage: 'Yes', description: 'Some description', }, noOptionLabel: { id: 'example.application:no.option.label', - defaultMessage: 'Nei', + defaultMessage: 'No', description: 'Some description', }, governmentOptionLabel: { @@ -155,29 +116,118 @@ export const m = defineMessages({ }, dataSchemePhoneNumber: { id: 'example.application:dataSchema.phoneNumber', - defaultMessage: 'Símanúmerið þarf að vera gilt.', + defaultMessage: 'Phone number must be valid.', description: 'Error message when phone number is invalid.', }, dataSchemeNationalId: { id: 'example.application:dataSchema.national.id', - defaultMessage: 'Kennitala þarf að vera gild.', + defaultMessage: 'National ID must be valid.', description: 'Error message when the kennitala is invalid.', }, - careerHistoryOther: { - id: 'example.application:careerHistory.other', - defaultMessage: 'Hvern hefur þú unnið fyrir áður?', + approvedByReviewerError: { + id: 'example.application:approvedByReviewerError', + defaultMessage: + 'Please indicate whether the application is approved or not', description: 'Some description', }, - careerHistoryOtherError: { - id: 'example.application:careerHistory.othertError', + regularTextExample: { + id: 'example.application:regularTextExample', defaultMessage: - 'Vinsamlegast tilgreindu fyrir hvern þú hefur unnið fyrir áður?', - description: 'Some description', + 'Regular text coming from a message file, this can be overwritten in contentful by webmaster', + description: 'Example use of messages', }, - approvedByReviewerError: { - id: 'example.application:approvedByReviewerError', + markdownHeadingExample: { + id: 'example.application:markdownHeadingExample#markdown', defaultMessage: - 'Vinsamlegast tilgreindu hvort umsóknin sé samþykkt eða ekki', - description: 'Some description', + '# Markdown heading 1\n ## Markdown heading 2\n ### Markdown heading 3\n #### Markdown heading 4\n Regular markdown text', + description: 'Example use of markdown', + }, + markdownBulletListExample: { + id: 'example.application:markdownBulletListExample#markdown', + defaultMessage: + 'Markdown bullet list\n * bullet 1\n * bullet 2\n * bullet 3', + description: 'Example use of markdown', + }, + markdownNumberedListExample: { + id: 'example.application:markdownNumberedListExample#markdown', + defaultMessage: + 'Markdown numbered list\n 1. number 1\n 2. number 2\n 3. number 3', + description: 'Example use of markdown', + }, + markdownMiscExample: { + id: 'example.application:markdownMiscExample#markdown', + defaultMessage: + 'A markdown link will open in a new tab: [This is a link to Google!](http://google.com)\n\n Markdown value inserted {value1}\n\n **Bold text**\n\n *Italic text*\n\n ***Bold and italic text***', + description: 'Example use of markdown link', + }, + markdownCodeExample: { + id: 'example.application:markdownCodeExample#markdown', + defaultMessage: + 'Markdown code inline `const x = 123` with text\n\n Code block\n\n ```\n const x = 123\n if (x < 100) {\n return true\n }\n```', + description: 'Example use of markdown code', + }, + customComponentDescription: { + id: 'example.application:customComponentDescription', + defaultMessage: + 'Before you make a custom component, go through this list to determine if you really need a custom component. A custom component should be the last option you go for when building an application.', + description: 'Rules for custom components', + }, + customComponentNumberedList: { + id: 'example.application:customComponentDescription#markdown', + defaultMessage: + '1. Try to use the shared components, such as `buildTextField`, `buildCheckboxField`, `buildSelectField`, `buildFileUploadField`, and others. This approach ensures a more consistent and uniform look and feel for the application.\n- If the shared components almost fulfill your needs but require slight adjustments, consult with the designer of the application to explore adapting the design to the built-in components.\n- If the design cannot be adjusted to the built-in components, consult Norda to determine if the shared components can be modified or expanded to meet your requirements.\n- Check if another application has created a similar custom component before. If so, it should be made into a shared component.\n- If you still need a new component, evaluate whether it is something that other applications might need in the future. If so, make the new component shared.\n- Create a custom component only if none of the above conditions apply.', + description: 'Rules for custom components', + }, + customComponentAbout: { + id: 'example.application:customComponentAbout', + defaultMessage: + 'Custom components are just regular React components. They can take in some data you specify in the template and they have access to the application object. They can also be styled with vanilla-extract.', + description: 'About custom components', + }, + overviewTitle: { + id: 'example.application:overviewTitle', + defaultMessage: 'Overview', + description: 'Overview title', + }, + overviewDescription: { + id: 'example.application:overviewTitle', + defaultMessage: + 'At the moment the form overview is a custom component. The plan is to make this a shared component in the near future.', + description: 'Overview title', + }, + overviewSubmit: { + id: 'example.application:overviewSubmit', + defaultMessage: 'Submit', + description: 'Overview title', + }, + validationDescription: { + id: 'example.application:validation.description#markdown', + defaultMessage: + 'Any field can have the value `required: true`, which uses the built-in functionality of `html` and can be disabled by inspecting the DOM for the page.\n\n Generally, it is best to put everything that needs to be filled out or needs to be filled out in a certain way in `/lib/dataSchema.ts`. For validation, use *zod*.', + description: 'Validation description', + }, + validationDescription3: { + id: 'example.application:validation.description3#markdown', + defaultMessage: + 'If the options in a field are all in one `enum`, then use `z.nativeEnum()` in zod validation to skip listing all the options in the enum as is required when using `z.enum()`.', + description: 'Validation description', + }, + conditionsDescription2: { + id: 'example.application:conditions.description2#markdown', + defaultMessage: + 'This is done in the same way in both cases. Everything should be able to take in `condition` as a parameter and condition takes in a function `(answers, externalData) => { ... }` that returns `true` or `false`.', + description: 'Validation description', + }, + descriptionFieldDescription: { + id: 'example.application:descriptionFieldDescription', + defaultMessage: + 'All text that appears in applications should come from `lib/messages.ts`. This text is then loaded into contentful by running `yarn nx run :extract-strings`, where `` is the name of the application as it is written in `project.json` in the relevant template. For this application it is `yarn nx run application-templates-reference-template:extract-strings`. In contentful, the content manager or administrator of the organization is responsible for adding English translations and updating the text from what the developer puts in `defaultMessage`.', + description: 'Description field description', + }, + descriptionFieldDescription2: { + id: 'example.application:descriptionFieldDescription2', + defaultMessage: + 'Here is a list of all the options for text from buildDescriptionField. Most of them are related to adding `#markdown` to the id to be able to use markdown in the text. It is also possible to put variables into the text and control title font sizes.', + description: 'Description field description', }, }) diff --git a/libs/application/templates/reference-template/src/utils/README.md b/libs/application/templates/reference-template/src/utils/README.md new file mode 100644 index 000000000000..1de70d5a8989 --- /dev/null +++ b/libs/application/templates/reference-template/src/utils/README.md @@ -0,0 +1,5 @@ +# Utils + +This folder is for all the utility functions, constants, enums and types. + +The organization of this folder is up to the developer. Try to aim for a happy medium between small focused files and too many files that bloat the folder. The aim is that a developer can enter this folder and understand what the utils are for and have a nice overview of the utils. diff --git a/libs/application/templates/reference-template/src/utils/constants.ts b/libs/application/templates/reference-template/src/utils/constants.ts new file mode 100644 index 000000000000..b44f3134b11a --- /dev/null +++ b/libs/application/templates/reference-template/src/utils/constants.ts @@ -0,0 +1,32 @@ +export enum States { + PREREQUISITES = 'prerequisites', + DRAFT = 'draft', + INREVIEW = 'inReview', + APPROVED = 'approved', + REJECTED = 'rejected', + WAITINGTOASSIGN = 'waitingToAssign', +} + +export enum YesNoEnum { + YES = 'yes', + NO = 'no', +} + +export enum CareerHistoryEnum { + GOVERNMENT = 'government', + ARANJA = 'aranja', + ADVANIA = 'advania', + OTHER = 'other', +} + +export enum CareerIndustryEnum { + SOFTWARE = 'software', + FINANCE = 'finance', + CONSULTING = 'consulting', + OTHER = 'other', +} + +export enum ApprovedByReviewerEnum { + APPROVE = 'APPROVE', + REJECT = 'REJECT', +} diff --git a/libs/application/templates/reference-template/src/utils/dateUtils.ts b/libs/application/templates/reference-template/src/utils/dateUtils.ts new file mode 100644 index 000000000000..5b2eee38976b --- /dev/null +++ b/libs/application/templates/reference-template/src/utils/dateUtils.ts @@ -0,0 +1,3 @@ +export const oneDayInMs = 24 * 60 * 60 * 1000 +export const minDate = new Date(Date.now() - 3 * oneDayInMs) // Three days ago +export const maxDate = new Date(Date.now() + 3 * oneDayInMs) // Three days from now diff --git a/libs/application/templates/reference-template/src/utils/options.ts b/libs/application/templates/reference-template/src/utils/options.ts new file mode 100644 index 000000000000..cdb78dfdd452 --- /dev/null +++ b/libs/application/templates/reference-template/src/utils/options.ts @@ -0,0 +1,68 @@ +import { plate110 } from '../assets/plate-110-510' +import { plate200 } from '../assets/plate-200-280' +import { plate155 } from '../assets/plate-155-305' + +export const checkboxOptions = [ + { + label: 'Checkbox 1', + value: 'checkbox1', + }, + { + label: 'Checkbox 2', + value: 'checkbox2', + }, + { + label: 'Checkbox 3', + value: 'checkbox3', + }, +] + +export const radioOptions = [ + { + label: 'Radio 1', + value: 'radio1', + }, + { + label: 'Radio 2', + value: 'radio2', + }, + { + label: 'Radio 3', + value: 'radio3', + }, +] + +export const defaultRadioOption = 'radio2' + +export const radioIllustrationOptions = [ + { + label: 'Radio 1', + value: 'radio1', + illustration: plate110, + }, + { + label: 'Radio 2', + value: 'radio2', + illustration: plate200, + }, + { + label: 'Radio 3', + value: 'radio3', + illustration: plate155, + }, +] + +export const selectOptions = [ + { + label: 'Select 1', + value: 'select1', + }, + { + label: 'Select 2', + value: 'select2', + }, + { + label: 'Select 3', + value: 'select3', + }, +] diff --git a/libs/application/templates/reference-template/src/utils/types.ts b/libs/application/templates/reference-template/src/utils/types.ts new file mode 100644 index 000000000000..23f160994302 --- /dev/null +++ b/libs/application/templates/reference-template/src/utils/types.ts @@ -0,0 +1,36 @@ +export enum OrganizationModelTypeEnum { + Municipality = 'Municipality', + National = 'National', + School = 'School', +} + +export type FriggSchoolsByMunicipality = { + __typename?: 'Query' + friggSchoolsByMunicipality?: Array<{ + __typename?: 'EducationFriggOrganizationModel' + id: string + nationalId: string + name: string + type: OrganizationModelTypeEnum + children?: Array<{ + __typename?: 'EducationFriggOrganizationModel' + id: string + nationalId: string + name: string + type: OrganizationModelTypeEnum + gradeLevels?: Array | null + }> | null + }> | null +} + +export type TableRepeaterAnswers = { + fullName: string + nationalId: string + relation: string +} + +export enum radioValidationExampleEnum { + OPTION_1 = 'option1', + OPTION_2 = 'option2', + OPTION_3 = 'option3', +} diff --git a/libs/application/types/src/lib/Fields.ts b/libs/application/types/src/lib/Fields.ts index e161ed324e09..5788d6ccac29 100644 --- a/libs/application/types/src/lib/Fields.ts +++ b/libs/application/types/src/lib/Fields.ts @@ -654,19 +654,6 @@ export type FieldsRepeaterField = BaseField & { */ minRows?: number maxRows?: number - table?: { - /** - * List of strings to render, - * if not provided it will be auto generated from the fields - */ - header?: StaticText[] - /** - * List of field id's to render, - * if not provided it will be auto generated from the fields - */ - rows?: string[] - format?: Record string | StaticText> - } } export type AccordionItem = { @@ -752,6 +739,7 @@ export interface SliderField extends BaseField { readonly type: FieldTypes.SLIDER readonly color?: Colors component: FieldComponents.SLIDER + titleVariant?: TitleVariants min: number max: MaybeWithApplicationAndField step?: number diff --git a/libs/application/ui-fields/src/lib/DescriptionFormField/DescriptionFormField.tsx b/libs/application/ui-fields/src/lib/DescriptionFormField/DescriptionFormField.tsx index 0be2ba24e8a2..44732c8fd465 100644 --- a/libs/application/ui-fields/src/lib/DescriptionFormField/DescriptionFormField.tsx +++ b/libs/application/ui-fields/src/lib/DescriptionFormField/DescriptionFormField.tsx @@ -16,11 +16,13 @@ export const DescriptionFormField: FC< > = ({ application, field, showFieldName }) => { const { formatMessage, lang: locale } = useLocale() + const { space: paddingTop = 2, marginBottom, marginTop } = field + return ( {showFieldName && ( diff --git a/libs/application/ui-fields/src/lib/FieldsRepeaterFormField/FieldsRepeaterFormField.tsx b/libs/application/ui-fields/src/lib/FieldsRepeaterFormField/FieldsRepeaterFormField.tsx index 9edc6dd1890e..7504599709ac 100644 --- a/libs/application/ui-fields/src/lib/FieldsRepeaterFormField/FieldsRepeaterFormField.tsx +++ b/libs/application/ui-fields/src/lib/FieldsRepeaterFormField/FieldsRepeaterFormField.tsx @@ -148,7 +148,7 @@ export const FieldsRepeaterFormField = ({ {Array.from({ length: numberOfItems }).map((_i, i) => ( {(formTitleNumbering !== 'none' || formTitle) && ( - + {formTitleNumbering === 'prefix' ? `${i + 1}. ` : ''} {formTitle && diff --git a/libs/application/ui-fields/src/lib/FieldsRepeaterFormField/FieldsRepeaterItem.tsx b/libs/application/ui-fields/src/lib/FieldsRepeaterFormField/FieldsRepeaterItem.tsx index 4fd041e97550..df4f221ff91e 100644 --- a/libs/application/ui-fields/src/lib/FieldsRepeaterFormField/FieldsRepeaterItem.tsx +++ b/libs/application/ui-fields/src/lib/FieldsRepeaterFormField/FieldsRepeaterItem.tsx @@ -172,17 +172,17 @@ export const Item = ({ } if (component === 'radio') { DefaultValue = - (getValueViaPath(application.answers, id) as string[]) ?? + getValueViaPath>(application.answers, id) ?? getDefaultValue(item, application, activeValues) } if (component === 'checkbox') { DefaultValue = - (getValueViaPath(application.answers, id) as string[]) ?? + getValueViaPath>(application.answers, id) ?? getDefaultValue(item, application, activeValues) } if (component === 'date') { DefaultValue = - (getValueViaPath(application.answers, id) as string) ?? + getValueViaPath(application.answers, id) ?? getDefaultValue(item, application, activeValues) } @@ -216,6 +216,7 @@ export const Item = ({ }} application={application} defaultValue={DefaultValue} + large={true} {...props} /> diff --git a/libs/application/ui-fields/src/lib/RadioFormField/RadioFormField.tsx b/libs/application/ui-fields/src/lib/RadioFormField/RadioFormField.tsx index 0ca14e94e256..4a973727eee0 100644 --- a/libs/application/ui-fields/src/lib/RadioFormField/RadioFormField.tsx +++ b/libs/application/ui-fields/src/lib/RadioFormField/RadioFormField.tsx @@ -50,11 +50,13 @@ export const RadioFormField: FC> = ({ [options, application, locale], ) + const paddingTop = field.space ?? 2 + return ( @@ -80,7 +82,7 @@ export const RadioFormField: FC> = ({ /> )} - + > = ({ }))} onSelect={field.onSelect} hasIllustration={hasIllustration} + paddingBottom={0} + paddingTop={2} /> diff --git a/libs/application/ui-fields/src/lib/SelectFormField/SelectFormField.tsx b/libs/application/ui-fields/src/lib/SelectFormField/SelectFormField.tsx index d71c3f42a390..204b3ba45fd7 100644 --- a/libs/application/ui-fields/src/lib/SelectFormField/SelectFormField.tsx +++ b/libs/application/ui-fields/src/lib/SelectFormField/SelectFormField.tsx @@ -1,5 +1,4 @@ import React, { FC, useMemo } from 'react' - import { formatText, buildFieldOptions, diff --git a/libs/application/ui-fields/src/lib/SliderFormField/SliderFormField.stories.mdx b/libs/application/ui-fields/src/lib/SliderFormField/SliderFormField.stories.mdx index eed93c43088e..6a909d1bb270 100644 --- a/libs/application/ui-fields/src/lib/SliderFormField/SliderFormField.stories.mdx +++ b/libs/application/ui-fields/src/lib/SliderFormField/SliderFormField.stories.mdx @@ -36,6 +36,7 @@ You can create a SliderFormField using the following function `buildSliderField` code={dedent(` buildSliderField({ id: 'field.id', + title: 'Slider title', label: { singular: 'day', plural: 'days', @@ -62,6 +63,7 @@ The previous configuration object will result in the following component: application={createMockApplication()} field={{ id: 'field.id', + title: 'Slider title', label: { singular: 'day', plural: 'days', diff --git a/libs/application/ui-fields/src/lib/SliderFormField/SliderFormField.tsx b/libs/application/ui-fields/src/lib/SliderFormField/SliderFormField.tsx index c0209d71e0c5..06d6431bf173 100644 --- a/libs/application/ui-fields/src/lib/SliderFormField/SliderFormField.tsx +++ b/libs/application/ui-fields/src/lib/SliderFormField/SliderFormField.tsx @@ -9,7 +9,7 @@ import { Slider } from '@island.is/application/ui-components' import { getValueViaPath } from '@island.is/application/core' import { useLocale } from '@island.is/localization' import { formatText } from '@island.is/application/core' -import { Box } from '@island.is/island-ui/core' +import { Text, Box } from '@island.is/island-ui/core' import { getDefaultValue } from '../../getDefaultValue' type SliderFormFieldProps = { @@ -20,6 +20,27 @@ type SliderFormFieldProps = { export const SliderFormField: FC< React.PropsWithChildren > = ({ application, field }) => { + const { + id, + min, + max, + trackStyle, + calculateCellStyle, + showLabel, + showMinMaxLabels, + showRemainderOverlay, + showProgressOverlay, + showToolTip, + label, + rangeDates, + onChangeEnd, + labelMultiplier, + snap, + step, + saveAsString, + marginTop, + marginBottom, + } = field const { clearErrors, setValue } = useFormContext() const { formatMessage } = useLocale() const computeMax = ( @@ -36,57 +57,49 @@ export const SliderFormField: FC< const finalMax = useMemo( () => computeMax( - field.max as MaybeWithApplicationAndField, + max as MaybeWithApplicationAndField, application, field, ), - [field, application], + [field, max, application], ) return ( - + ( { - clearErrors(field.id) - const value = field.saveAsString ? String(val) : val + clearErrors(id) + const value = saveAsString ? String(val) : val onChange(value) - setValue(field.id, value) + setValue(id, value) }} - onChangeEnd={field.onChangeEnd} - labelMultiplier={field.labelMultiplier} + onChangeEnd={onChangeEnd} + labelMultiplier={labelMultiplier} /> )} /> diff --git a/libs/application/ui-fields/src/lib/TableRepeaterFormField/TableRepeaterItem.tsx b/libs/application/ui-fields/src/lib/TableRepeaterFormField/TableRepeaterItem.tsx index 5388065f176f..a65e25164f5b 100644 --- a/libs/application/ui-fields/src/lib/TableRepeaterFormField/TableRepeaterItem.tsx +++ b/libs/application/ui-fields/src/lib/TableRepeaterFormField/TableRepeaterItem.tsx @@ -216,6 +216,7 @@ export const Item = ({ }} application={application} defaultValue={DefaultValue} + large={true} {...props} /> diff --git a/libs/clients/health-directorate/src/lib/clients/organ-donation/clientConfig.json b/libs/clients/health-directorate/src/lib/clients/organ-donation/clientConfig.json index b83056a0484c..8612145527f7 100644 --- a/libs/clients/health-directorate/src/lib/clients/organ-donation/clientConfig.json +++ b/libs/clients/health-directorate/src/lib/clients/organ-donation/clientConfig.json @@ -1,18 +1,11 @@ { "openapi": "3.0.0", "paths": { - "/v1/me/organ-donor-status": { + "/v1/donation-exceptions": { "get": { - "operationId": "MeDonorStatusController_getOrganDonorStatus", - "description": "Get user's donation-exception donor status", + "operationId": "DonationExceptionController_getOrgans", + "description": "Gets a list of organs that can be omitted from an donation-exception donation", "parameters": [ - { - "name": "ip", - "required": false, - "in": "query", - "description": "The IP address of the user", - "schema": { "type": "string" } - }, { "name": "locale", "required": false, @@ -26,7 +19,10 @@ "description": "", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/OrganDonorDto" } + "schema": { + "type": "array", + "items": { "$ref": "#/components/schemas/OrganDto" } + } } } }, @@ -63,37 +59,38 @@ } } }, - "tags": ["me/organ-donor-status"] - }, - "post": { - "operationId": "MeDonorStatusController_updateOrganDonorStatus", - "description": "Update user's donation-exception donor status", + "tags": ["donation-exceptions"] + } + }, + "/v1/me/organ-donor-status": { + "get": { + "operationId": "MeDonorStatusController_getOrganDonorStatus", + "description": "Get user's donation-exception donor status", "parameters": [ { - "name": "ip", + "name": "locale", "required": false, "in": "query", - "description": "The IP address of the user", - "schema": { "type": "string" } + "description": "The locale to use for the response", + "schema": { "$ref": "#/components/schemas/Locale" } }, { - "name": "locale", + "name": "ip", "required": false, "in": "query", - "description": "The locale to use for the response", - "schema": { "$ref": "#/components/schemas/Locale" } + "description": "The IP address of the user", + "schema": { "type": "string" } } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { "$ref": "#/components/schemas/UpdateOrganDonorDto" } - } - } - }, "responses": { - "200": { "description": "" }, + "200": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/OrganDonorDto" } + } + } + }, "400": { "description": "", "content": { @@ -128,12 +125,10 @@ } }, "tags": ["me/organ-donor-status"] - } - }, - "/v1/donation-exceptions": { - "get": { - "operationId": "DonationExceptionController_getOrgans", - "description": "Gets a list of organs that can be omitted from an donation-exception donation", + }, + "post": { + "operationId": "MeDonorStatusController_updateOrganDonorStatus", + "description": "Update user's donation-exception donor status", "parameters": [ { "name": "locale", @@ -141,20 +136,25 @@ "in": "query", "description": "The locale to use for the response", "schema": { "$ref": "#/components/schemas/Locale" } + }, + { + "name": "ip", + "required": false, + "in": "query", + "description": "The IP address of the user", + "schema": { "type": "string" } } ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { "$ref": "#/components/schemas/OrganDto" } - } - } + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/UpdateOrganDonorDto" } } - }, + } + }, + "responses": { + "200": { "description": "" }, "400": { "description": "", "content": { @@ -188,7 +188,7 @@ } } }, - "tags": ["donation-exceptions"] + "tags": ["me/organ-donor-status"] } } }, @@ -226,24 +226,11 @@ }, "required": ["id", "name"] }, - "OrganDonorDto": { - "type": "object", - "properties": { - "isDonor": { "type": "boolean" }, - "exceptions": { - "type": "array", - "items": { "$ref": "#/components/schemas/OrganDto" } - }, - "exceptionComment": { "type": "string" }, - "registrationDate": { "format": "date-time", "type": "string" } - }, - "required": ["isDonor", "exceptions"] - }, "HttpProblemResponse": { "type": "object", "properties": { "type": { - "type": "object", + "type": "string", "description": "A URI reference that identifies the problem type" }, "title": { @@ -262,12 +249,27 @@ }, "required": ["type", "title"] }, + "OrganDonorDto": { + "type": "object", + "properties": { + "isDonor": { "type": "boolean" }, + "exceptions": { + "type": "array", + "items": { "$ref": "#/components/schemas/OrganDto" } + }, + "exceptionComment": { "type": "string" }, + "registrationDate": { "format": "date-time", "type": "string" }, + "isMinor": { "type": "boolean" }, + "isTemporaryResident": { "type": "boolean" } + }, + "required": ["isDonor", "exceptions"] + }, "UpdateOrganDonorDto": { "type": "object", "properties": { "isDonor": { "type": "boolean" }, "exceptions": { "type": "array", "items": { "type": "string" } }, - "exceptionComment": { "type": "object" } + "exceptionComment": { "type": "string" } }, "required": ["isDonor", "exceptions"] } diff --git a/libs/clients/official-journal-of-iceland/public/src/clientConfig.json b/libs/clients/official-journal-of-iceland/public/src/clientConfig.json index 092ccd7520e1..6206b173263a 100644 --- a/libs/clients/official-journal-of-iceland/public/src/clientConfig.json +++ b/libs/clients/official-journal-of-iceland/public/src/clientConfig.json @@ -169,15 +169,40 @@ } } }, - "/api/v1/types/{id}": { + "/api/v1/maincategories": { "get": { - "operationId": "getAdvertTypeById", + "operationId": "getMainCategories", "parameters": [ { - "name": "id", - "required": true, - "in": "path", + "name": "search", + "description": "String to search for", + "required": false, + "in": "query", "schema": { "type": "string" } + }, + { + "name": "ids", + "required": false, + "in": "query", + "schema": { + "default": [], + "type": "array", + "items": { "type": "string" } + } + }, + { + "name": "page", + "description": "Page number to return.", + "required": false, + "in": "query", + "schema": { "type": "number" } + }, + { + "name": "pageSize", + "description": "Page size number to return.", + "required": false, + "in": "query", + "schema": { "type": "number" } } ], "responses": { @@ -186,7 +211,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetAdvertTypeResponse" + "$ref": "#/components/schemas/GetMainCategoriesResponse" } } } @@ -194,23 +219,26 @@ } } }, - "/api/v1/types": { + "/api/v1/categories": { "get": { - "operationId": "getAdvertTypes", + "operationId": "getCategories", "parameters": [ { - "name": "department", - "description": "Department slug to get categories for.", + "name": "search", + "description": "String to search for", "required": false, "in": "query", "schema": { "type": "string" } }, { - "name": "search", - "description": "String to search for in types.", + "name": "ids", "required": false, "in": "query", - "schema": { "type": "string" } + "schema": { + "default": [], + "type": "array", + "items": { "type": "string" } + } }, { "name": "page", @@ -221,7 +249,7 @@ }, { "name": "pageSize", - "description": "Number of items per page.", + "description": "Page size number to return.", "required": false, "in": "query", "schema": { "type": "number" } @@ -233,7 +261,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetAdvertTypesResponse" + "$ref": "#/components/schemas/GetCategoriesResponse" } } } @@ -241,9 +269,9 @@ } } }, - "/api/v1/maincategories": { + "/api/v1/institutions": { "get": { - "operationId": "getMainCategories", + "operationId": "getInstitutions", "parameters": [ { "name": "search", @@ -283,7 +311,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetMainCategoriesResponse" + "$ref": "#/components/schemas/GetInstitutionsResponse" } } } @@ -291,37 +319,36 @@ } } }, - "/api/v1/categories": { + "/api/v1/signatures": { "get": { - "operationId": "getCategories", + "operationId": "getSignatures", "parameters": [ { - "name": "search", - "description": "String to search for", + "description": "Search for a specific signature by id", "required": false, + "name": "id", "in": "query", "schema": { "type": "string" } }, { - "name": "ids", + "description": "Search for a specific signature by type", + "example": "Regular", "required": false, + "name": "type", "in": "query", - "schema": { - "default": [], - "type": "array", - "items": { "type": "string" } - } + "schema": { "type": "string" } }, { - "name": "page", - "description": "Page number to return.", + "description": "Search for a specific signature", + "example": "Dagur B. Eggertsson", "required": false, + "name": "search", "in": "query", - "schema": { "type": "number" } + "schema": { "type": "string" } }, { - "name": "pageSize", - "description": "Page size number to return.", + "name": "page", + "description": "Page number to return.", "required": false, "in": "query", "schema": { "type": "number" } @@ -333,7 +360,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetCategoriesResponse" + "$ref": "#/components/schemas/GetAdvertSignatureResponse" } } } @@ -341,9 +368,9 @@ } } }, - "/api/v1/institutions": { + "/api/v1/cases": { "get": { - "operationId": "getInstitutions", + "operationId": "getCasesInProgress", "parameters": [ { "name": "search", @@ -383,7 +410,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetInstitutionsResponse" + "$ref": "#/components/schemas/GetCasesInProgressReponse" } } } @@ -391,115 +418,384 @@ } } }, - "/api/v1/signatures": { + "/api/v1/error": { "get": { - "operationId": "getSignatures", + "operationId": "error", + "parameters": [], + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ValidationResponse" } + } + } + } + } + } + }, + "/api/v1/advert-types/types": { + "get": { + "operationId": "getTypes", + "summary": "", "parameters": [ { - "description": "Search for a specific signature by id", "required": false, + "description": "Search by id", "name": "id", "in": "query", "schema": { "type": "string" } }, { - "description": "Search for a specific signature by type", - "example": "Regular", "required": false, - "name": "type", + "description": "Filter by unassigned", + "name": "unassigned", + "in": "query", + "schema": { "type": "boolean" } + }, + { + "required": false, + "description": "Search by main type id", + "name": "mainType", "in": "query", "schema": { "type": "string" } }, { - "description": "Search for a specific signature", - "example": "Dagur B. Eggertsson", "required": false, + "description": "Search by title", "name": "search", "in": "query", "schema": { "type": "string" } }, { + "required": false, + "description": "Search by slug", + "name": "slug", + "in": "query", + "schema": { "type": "string" } + }, + { + "required": false, + "description": "Search by department slug, title or id", + "name": "department", + "in": "query", + "schema": { "type": "string" } + }, + { + "required": false, + "description": "The page number", "name": "page", - "description": "Page number to return.", + "in": "query", + "schema": { "type": "number" } + }, + { "required": false, + "description": "The page size", + "name": "pageSize", "in": "query", "schema": { "type": "number" } } ], "responses": { - "default": { + "200": { "description": "", "content": { "application/json": { - "schema": { - "$ref": "#/components/schemas/GetAdvertSignatureResponse" - } + "schema": { "$ref": "#/components/schemas/GetAdvertTypes" } + } + } + }, + "400": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/AdvertTypeError" } + } + } + }, + "500": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/AdvertTypeError" } } } } } } }, - "/api/v1/cases": { + "/api/v1/advert-types/main-types": { "get": { - "operationId": "getCasesInProgress", + "operationId": "getMainTypes", + "summary": "", "parameters": [ { + "required": false, + "description": "Search by id", + "name": "id", + "in": "query", + "schema": { "type": "string" } + }, + { + "required": false, + "description": "Filter by unassigned", + "name": "unassigned", + "in": "query", + "schema": { "type": "boolean" } + }, + { + "required": false, + "description": "Search by main type id", + "name": "mainType", + "in": "query", + "schema": { "type": "string" } + }, + { + "required": false, + "description": "Search by title", "name": "search", - "description": "String to search for", + "in": "query", + "schema": { "type": "string" } + }, + { "required": false, + "description": "Search by slug", + "name": "slug", "in": "query", "schema": { "type": "string" } }, { - "name": "ids", "required": false, + "description": "Search by department slug, title or id", + "name": "department", "in": "query", - "schema": { - "default": [], - "type": "array", - "items": { "type": "string" } - } + "schema": { "type": "string" } }, { - "name": "page", - "description": "Page number to return.", "required": false, + "description": "The page number", + "name": "page", "in": "query", "schema": { "type": "number" } }, { - "name": "pageSize", - "description": "Page size number to return.", "required": false, + "description": "The page size", + "name": "pageSize", "in": "query", "schema": { "type": "number" } } ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/GetAdvertMainTypes" } + } + } + }, + "400": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/AdvertTypeError" } + } + } + }, + "500": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/AdvertTypeError" } + } + } + } + } + } + }, + "/api/v1/advert-types/types/{id}": { + "get": { + "operationId": "getTypeById", + "summary": "", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/GetAdvertType" } + } + } + }, + "400": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/AdvertTypeError" } + } + } + }, + "404": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/AdvertTypeError" } + } + } + }, + "500": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/AdvertTypeError" } + } + } + } + } + } + }, + "/api/v1/advert-types/main-types/{id}": { + "get": { + "operationId": "getMainTypeById", + "summary": "", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/GetAdvertMainType" } + } + } + }, + "400": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/AdvertTypeError" } + } + } + }, + "404": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/AdvertTypeError" } + } + } + }, + "500": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/AdvertTypeError" } + } + } + } + } + } + }, + "/api/v1/pdf/case/{id}": { + "get": { + "operationId": "getPdfByCaseId", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], "responses": { "default": { "description": "", "content": { "application/json": { - "schema": { - "$ref": "#/components/schemas/GetCasesInProgressReponse" - } + "schema": { "$ref": "#/components/schemas/GetPdfRespone" } } } } } } }, - "/api/v1/error": { + "/api/v1/pdf/application/{id}": { "get": { - "operationId": "error", - "parameters": [], + "operationId": "getPdfByApplicationId", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], "responses": { "default": { "description": "", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ValidationResponse" } + "schema": { "$ref": "#/components/schemas/GetPdfRespone" } + } + } + } + } + } + }, + "/api/v1/pdf/case/{id}/url": { + "get": { + "operationId": "getPdfUrlByCaseId", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/GetPdfUrlResponse" } + } + } + } + } + } + }, + "/api/v1/pdf/application/{id}/url": { + "get": { + "operationId": "getPdfUrlByApplicationId", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { + "default": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/GetPdfUrlResponse" } } } } @@ -551,23 +847,18 @@ "properties": { "id": { "type": "string", - "description": "Unique ID for the advert type, GUID format.", - "example": "00000000-0000-0000-0000-000000000000", - "nullable": false + "description": "The id of the main advert type" }, "title": { "type": "string", - "description": "Title of the advert type, always uppercased.", - "example": "AUGLÝSING" + "description": "The title of the main advert type" }, "slug": { "type": "string", - "description": "Slug of the advert type, used in URLs and API requests.", - "example": "auglysing" + "description": "The slug of the main advert type" }, "department": { - "description": "Department the advert type belongs to.", - "nullable": true, + "description": "The department of the main advert type", "allOf": [{ "$ref": "#/components/schemas/Department" }] } }, @@ -977,31 +1268,6 @@ }, "required": ["departments", "paging"] }, - "GetAdvertTypeResponse": { - "type": "object", - "properties": { - "type": { - "description": "Advert type", - "allOf": [{ "$ref": "#/components/schemas/AdvertType" }] - } - }, - "required": ["type"] - }, - "GetAdvertTypesResponse": { - "type": "object", - "properties": { - "types": { - "description": "List of advert types", - "type": "array", - "items": { "$ref": "#/components/schemas/AdvertType" } - }, - "paging": { - "description": "Paging info", - "allOf": [{ "$ref": "#/components/schemas/Paging" }] - } - }, - "required": ["types", "paging"] - }, "MainCategory": { "type": "object", "properties": { @@ -1190,6 +1456,108 @@ } }, "required": ["message", "statusCode"] + }, + "GetAdvertTypes": { + "type": "object", + "properties": { + "types": { + "description": "List of advert types", + "type": "array", + "items": { "$ref": "#/components/schemas/AdvertType" } + }, + "paging": { + "description": "Paging information", + "allOf": [{ "$ref": "#/components/schemas/Paging" }] + } + }, + "required": ["types", "paging"] + }, + "AdvertTypeError": { + "type": "object", + "properties": { + "errorType": { + "type": "string", + "enum": ["DuplicateError", "ValidationError", "NotFoundError"] + }, + "name": { "type": "string" }, + "message": { "type": "string" }, + "severity": { "type": "string", "enum": ["info", "warning", "error"] } + }, + "required": ["errorType", "name", "message", "severity"] + }, + "AdvertMainType": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The id of the main advert type" + }, + "title": { + "type": "string", + "description": "The title of the main advert type" + }, + "slug": { + "type": "string", + "description": "The slug of the main advert type" + }, + "department": { + "description": "The department this main type belongs to", + "allOf": [{ "$ref": "#/components/schemas/Department" }] + }, + "types": { + "description": "All types under this main type", + "type": "array", + "items": { "$ref": "#/components/schemas/AdvertType" } + } + }, + "required": ["id", "title", "slug", "department", "types"] + }, + "GetAdvertMainTypes": { + "type": "object", + "properties": { + "mainTypes": { + "description": "List of all main advert types", + "type": "array", + "items": { "$ref": "#/components/schemas/AdvertMainType" } + }, + "paging": { + "description": "Paging information", + "allOf": [{ "$ref": "#/components/schemas/Paging" }] + } + }, + "required": ["mainTypes", "paging"] + }, + "GetAdvertType": { + "type": "object", + "properties": { + "type": { + "description": "The advert type", + "allOf": [{ "$ref": "#/components/schemas/AdvertType" }] + } + }, + "required": ["type"] + }, + "GetAdvertMainType": { + "type": "object", + "properties": { + "mainType": { + "description": "The main advert type", + "allOf": [{ "$ref": "#/components/schemas/AdvertMainType" }] + } + }, + "required": ["mainType"] + }, + "GetPdfRespone": { + "type": "object", + "properties": { + "content": { "type": "string", "description": "Base64 encoded PDF" } + }, + "required": ["content"] + }, + "GetPdfUrlResponse": { + "type": "object", + "properties": { "url": { "type": "string" } }, + "required": ["url"] } } } diff --git a/libs/clients/official-journal-of-iceland/public/src/lib/officialJournalOfIcelandClient.service.ts b/libs/clients/official-journal-of-iceland/public/src/lib/officialJournalOfIcelandClient.service.ts index 1ab4b33951c9..a600c48cb36e 100644 --- a/libs/clients/official-journal-of-iceland/public/src/lib/officialJournalOfIcelandClient.service.ts +++ b/libs/clients/official-journal-of-iceland/public/src/lib/officialJournalOfIcelandClient.service.ts @@ -7,11 +7,13 @@ import { GetDepartmentsRequest, GetInstitutionsRequest, GetMainCategoriesRequest, - GetAdvertTypesRequest, GetDepartmentByIdRequest, - GetAdvertTypeByIdRequest, GetCasesInProgressRequest, + GetTypeByIdRequest, + GetTypesRequest, + GetMainTypesRequest, } from '../../gen/fetch/apis' +import { GetAdvertMainTypes } from '../../gen/fetch' @Injectable() export class OfficialJournalOfIcelandClientService { @@ -33,12 +35,18 @@ export class OfficialJournalOfIcelandClientService { return this.api.getDepartments(params ?? {}) } - public async getAdvertTypeById(params: GetAdvertTypeByIdRequest) { - return this.api.getAdvertTypeById(params) + public async getAdvertTypeById(params: GetTypeByIdRequest) { + return this.api.getTypeById(params) } - public async getAdvertTypes(params: GetAdvertTypesRequest) { - return this.api.getAdvertTypes(params) + public async getAdvertMainTypes( + params: GetMainTypesRequest, + ): Promise { + return this.api.getMainTypes(params) + } + + public async getAdvertTypes(params: GetTypesRequest) { + return this.api.getTypes(params) } public async getMainCategories(params: GetMainCategoriesRequest) { diff --git a/libs/cms/src/lib/generated/contentfulTypes.d.ts b/libs/cms/src/lib/generated/contentfulTypes.d.ts index 24d4c5a81f65..2f49a4508978 100644 --- a/libs/cms/src/lib/generated/contentfulTypes.d.ts +++ b/libs/cms/src/lib/generated/contentfulTypes.d.ts @@ -3249,6 +3249,9 @@ export interface IOrganizationPageFields { /** Sitemap */ sitemap?: ISitemap | undefined + + /** Can be found in search results */ + canBeFoundInSearchResults?: boolean | undefined } export interface IOrganizationPage extends Entry { diff --git a/libs/cms/src/lib/models/organizationPage.model.ts b/libs/cms/src/lib/models/organizationPage.model.ts index 4ca0e4b3eb98..233b62efafff 100644 --- a/libs/cms/src/lib/models/organizationPage.model.ts +++ b/libs/cms/src/lib/models/organizationPage.model.ts @@ -86,6 +86,9 @@ export class OrganizationPage { @CacheField(() => OrganizationPageTopLevelNavigation, { nullable: true }) topLevelNavigation?: OrganizationPageTopLevelNavigation | null + + @Field(() => Boolean, { nullable: true }) + canBeFoundInSearchResults?: boolean } export const mapOrganizationPage = ({ @@ -144,5 +147,6 @@ export const mapOrganizationPage = ({ ? mapImage(fields.defaultHeaderImage) : undefined, topLevelNavigation, + canBeFoundInSearchResults: fields.canBeFoundInSearchResults ?? true, } } diff --git a/libs/cms/src/lib/search/importers/organizationPage.service.ts b/libs/cms/src/lib/search/importers/organizationPage.service.ts index 9a60a31bf272..984d64527323 100644 --- a/libs/cms/src/lib/search/importers/organizationPage.service.ts +++ b/libs/cms/src/lib/search/importers/organizationPage.service.ts @@ -15,7 +15,8 @@ export class OrganizationPageSyncService return entries.filter( (entry: Entry): entry is IOrganizationPage => entry.sys.contentType.sys.id === 'organizationPage' && - !!entry.fields.title, + !!entry.fields.title && + (entry.fields.canBeFoundInSearchResults ?? true), ) } diff --git a/libs/cms/src/lib/search/importers/organizationSubpage.service.ts b/libs/cms/src/lib/search/importers/organizationSubpage.service.ts index 7edf40d5a224..5113a03ff260 100644 --- a/libs/cms/src/lib/search/importers/organizationSubpage.service.ts +++ b/libs/cms/src/lib/search/importers/organizationSubpage.service.ts @@ -24,7 +24,10 @@ export class OrganizationSubpageSyncService !!entry.fields.slug && !!entry.fields.organizationPage?.fields?.slug && // Standalone organization pages have their own search, we don't want subpages there to be found in the global search - entry.fields.organizationPage.fields.theme !== 'standalone', + entry.fields.organizationPage.fields.theme !== 'standalone' && + // Subpage should not be searchable if the organization frontpage isn't searchable + (entry.fields.organizationPage.fields.canBeFoundInSearchResults ?? + true), ) } diff --git a/libs/feature-flags/src/lib/features.ts b/libs/feature-flags/src/lib/features.ts index 5cc65a03a541..858df10f9027 100644 --- a/libs/feature-flags/src/lib/features.ts +++ b/libs/feature-flags/src/lib/features.ts @@ -63,6 +63,9 @@ export enum Features { //Is social administration payment plan 2025 enabled? isServicePortalPaymentPlan2025Enabled = 'isServicePortalPaymentPlan2025Enabled', + //Is vehicle bulk mileage graph enabled? + isServicePortalVehicleBulkMileageSubdataPageEnabled = 'isServicePortalVehicleBulkMileageSubdataPageEnabled', + //Possible universities isUniversityOfAkureyriEnabled = 'isUniversityOfAkureyriEnabled', isAgriculturalUniversityOfIcelandEnabled = 'isAgriculturalUniversityOfIcelandEnabled', diff --git a/libs/judicial-system/message/src/lib/message.ts b/libs/judicial-system/message/src/lib/message.ts index bb32cdc84e05..2472dfa4c37c 100644 --- a/libs/judicial-system/message/src/lib/message.ts +++ b/libs/judicial-system/message/src/lib/message.ts @@ -11,6 +11,7 @@ export enum MessageType { DELIVERY_TO_COURT_CASE_FILE = 'DELIVERY_TO_COURT_CASE_FILE', DELIVERY_TO_COURT_CASE_FILES_RECORD = 'DELIVERY_TO_COURT_CASE_FILES_RECORD', DELIVERY_TO_COURT_REQUEST = 'DELIVERY_TO_COURT_REQUEST', + DELIVERY_TO_COURT_SUBPOENA = 'DELIVERY_TO_COURT_SUBPOENA', DELIVERY_TO_COURT_COURT_RECORD = 'DELIVERY_TO_COURT_COURT_RECORD', DELIVERY_TO_COURT_SIGNED_RULING = 'DELIVERY_TO_COURT_SIGNED_RULING', DELIVERY_TO_COURT_CASE_CONCLUSION = 'DELIVERY_TO_COURT_CASE_CONCLUSION', @@ -49,6 +50,7 @@ export const messageEndpoint: { [key in MessageType]: string } = { DELIVERY_TO_COURT_CASE_FILE: 'deliverCaseFileToCourt', DELIVERY_TO_COURT_CASE_FILES_RECORD: 'deliverCaseFilesRecordToCourt', DELIVERY_TO_COURT_REQUEST: 'deliverRequestToCourt', + DELIVERY_TO_COURT_SUBPOENA: 'deliverSubpoenaToCourt', DELIVERY_TO_COURT_COURT_RECORD: 'deliverCourtRecordToCourt', DELIVERY_TO_COURT_SIGNED_RULING: 'deliverSignedRulingToCourt', DELIVERY_TO_COURT_CASE_CONCLUSION: 'deliverCaseConclusionToCourt', diff --git a/libs/judicial-system/types/src/lib/eventLog.ts b/libs/judicial-system/types/src/lib/eventLog.ts index fca7b91f81ea..5aa84c863960 100644 --- a/libs/judicial-system/types/src/lib/eventLog.ts +++ b/libs/judicial-system/types/src/lib/eventLog.ts @@ -15,6 +15,7 @@ export const eventTypes = Object.values(EventType) export enum DefendantEventType { SENT_TO_PRISON_ADMIN = 'SENT_TO_PRISON_ADMIN', + OPENED_BY_PRISON_ADMIN = 'OPENED_BY_PRISON_ADMIN', } export const defendantEventTypes = Object.values(DefendantEventType) diff --git a/libs/portals/my-pages/assets/src/lib/messages.ts b/libs/portals/my-pages/assets/src/lib/messages.ts index 3e6a455f3bac..e8ec6fdca6c3 100644 --- a/libs/portals/my-pages/assets/src/lib/messages.ts +++ b/libs/portals/my-pages/assets/src/lib/messages.ts @@ -292,10 +292,34 @@ export const vehicleMessage = defineMessages({ id: 'sp.vehicles:permno', defaultMessage: 'Fastanúmer', }, + viewChart: { + id: 'sp.vehicles:view-chart', + defaultMessage: 'Sjá línurit', + }, + viewTable: { + id: 'sp.vehicles:view-table', + defaultMessage: 'Sjá töflu', + }, + registrationHistory: { + id: 'sp.vehicles:registration-history', + defaultMessage: 'Skráningarsaga', + }, + viewRegistrationHistory: { + id: 'sp.vehicles:view-registration-history', + defaultMessage: 'Sjá alla skráningarsögu', + }, lastRegistration: { id: 'sp.vehicles:last-registration', defaultMessage: 'Síðasta skráning', }, + lastStatus: { + id: 'sp.vehicles:last-status', + defaultMessage: 'Síðasta staða', + }, + lastRegistered: { + id: 'sp.vehicles:last-registered', + defaultMessage: 'Síðast skráð', + }, verno: { id: 'sp.vehicles:verno', defaultMessage: 'Verksmiðjunúmer', @@ -518,6 +542,10 @@ export const vehicleMessage = defineMessages({ id: 'sp.vehicles:total-weight', defaultMessage: 'Heildarþyngd', }, + total: { + id: 'sp.vehicles:total', + defaultMessage: 'Samtals:', + }, width: { id: 'sp.vehicles:width', defaultMessage: 'Breidd', diff --git a/libs/portals/my-pages/assets/src/lib/navigation.ts b/libs/portals/my-pages/assets/src/lib/navigation.ts index 150c8dd15d07..ca8c691d26f2 100644 --- a/libs/portals/my-pages/assets/src/lib/navigation.ts +++ b/libs/portals/my-pages/assets/src/lib/navigation.ts @@ -45,11 +45,7 @@ export const assetsNavigation: PortalNavigationItem = { ], }, { - name: m.vehiclesLookup, - path: AssetsPaths.AssetsVehiclesLookup, - }, - { - name: m.vehiclesBulkMileage, + name: m.vehiclesRegisterMileage, path: AssetsPaths.AssetsVehiclesBulkMileage, children: [ { @@ -71,6 +67,11 @@ export const assetsNavigation: PortalNavigationItem = { }, ], }, + { + name: m.vehiclesLookup, + path: AssetsPaths.AssetsVehiclesLookup, + }, + { name: m.vehiclesHistory, path: AssetsPaths.AssetsVehiclesHistory, diff --git a/libs/portals/my-pages/assets/src/screens/VehicleBulkMileage/VehicleBulkMileage.tsx b/libs/portals/my-pages/assets/src/screens/VehicleBulkMileage/VehicleBulkMileage.tsx index c2964c3ee81a..7e58161e7feb 100644 --- a/libs/portals/my-pages/assets/src/screens/VehicleBulkMileage/VehicleBulkMileage.tsx +++ b/libs/portals/my-pages/assets/src/screens/VehicleBulkMileage/VehicleBulkMileage.tsx @@ -3,7 +3,6 @@ import { Box, Filter, FilterInput, - Inline, Checkbox, Pagination, Text, @@ -16,7 +15,6 @@ import { SAMGONGUSTOFA_SLUG, LinkButton, IntroWrapper, - formatDate, } from '@island.is/portals/my-pages/core' import { vehicleMessage as messages, vehicleMessage } from '../../lib/messages' import * as styles from './VehicleBulkMileage.css' @@ -41,6 +39,7 @@ const VehicleBulkMileage = () => { const [totalPages, setTotalPages] = useState(1) const [search, setSearch] = useState() const [filterValue, setFilterValue] = useState(false) + const [displayFilters, setDisplayFilters] = useState(false) const [vehicleListQuery, { data, loading, error, called }] = useVehiclesListLazyQuery() @@ -118,6 +117,12 @@ const VehicleBulkMileage = () => { const methods = useForm() + useEffect(() => { + if (!displayFilters) { + setDisplayFilters((data?.vehiclesListV3?.totalRecords ?? 0) > 10) + } + }, [data, displayFilters]) + return ( @@ -143,62 +148,68 @@ const VehicleBulkMileage = () => { } serviceProviderSlug={SAMGONGUSTOFA_SLUG} serviceProviderTooltip={formatMessage(m.vehiclesTooltip)} - buttonGroup={[ - , - , - ]} + buttonGroup={ + displayFilters + ? [ + , + , + ] + : undefined + } > - - { - console.log('clear') - }} - align="left" - reverse - filterInput={ - { - setSearch(search) - }} - name={formatMessage(m.searchLabel)} - placeholder={formatMessage(vehicleMessage.searchForPlate)} - /> - } - > - - - {formatMessage(m.filterBy)} - - setFilterValue(!filterValue)} - /> - - - + {displayFilters && ( + + { + setFilterValue(false) + }} + align="left" + reverse + filterInput={ + { + setSearch(search) + }} + name={formatMessage(m.searchLabel)} + placeholder={formatMessage(vehicleMessage.searchForPlate)} + /> + } + > + + + {formatMessage(m.filterBy)} + + setFilterValue(!filterValue)} + /> + + + + )} {error && !loading && } {!error && ( diff --git a/libs/portals/my-pages/assets/src/screens/VehicleBulkMileage/VehicleBulkMileageRow.tsx b/libs/portals/my-pages/assets/src/screens/VehicleBulkMileage/VehicleBulkMileageRow.tsx index b4661d31d6a4..206acc4fbc76 100644 --- a/libs/portals/my-pages/assets/src/screens/VehicleBulkMileage/VehicleBulkMileageRow.tsx +++ b/libs/portals/my-pages/assets/src/screens/VehicleBulkMileage/VehicleBulkMileageRow.tsx @@ -10,18 +10,22 @@ import { import { useEffect, useState, useCallback, useMemo } from 'react' import { + EmptyTable, ExpandRow, NestedFullTable, formatDate, m, } from '@island.is/portals/my-pages/core' -import { AlertMessage, Box } from '@island.is/island-ui/core' import { vehicleMessage } from '../../lib/messages' import { VehicleBulkMileageSaveButton } from './VehicleBulkMileageSaveButton' import { InputController } from '@island.is/shared/form-fields' import * as styles from './VehicleBulkMileage.css' import { displayWithUnit } from '../../utils/displayWithUnit' import { isReadDateToday } from '../../utils/readDate' +import { AlertMessage, Box, Text } from '@island.is/island-ui/core' +import format from 'date-fns/format' +import { VehicleBulkMileageSubData } from './VehicleBulkMileageSubData' +import { Features, useFeatureFlagClient } from '@island.is/react/feature-flags' const ORIGIN_CODE = 'ISLAND.IS' @@ -41,6 +45,22 @@ export const VehicleBulkMileageRow = ({ vehicle }: Props) => { const [postError, setPostError] = useState(null) const [postStatus, setPostStatus] = useState('initial') + const [showSubdata, setShowSubdata] = useState(false) + const featureFlagClient = useFeatureFlagClient() + useEffect(() => { + const isFlagEnabled = async () => { + const ffEnabled = await featureFlagClient.getValue( + Features.isServicePortalVehicleBulkMileageSubdataPageEnabled, + false, + ) + if (ffEnabled) { + setShowSubdata(ffEnabled as boolean) + } + } + isFlagEnabled() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + const [ executeRegistrationsQuery, { data, loading, error, refetch: registrationsRefetch }, @@ -236,10 +256,17 @@ export const VehicleBulkMileageRow = ({ vehicle }: Props) => { onExpandCallback={executeRegistrationsQuery} data={[ { - value: vehicle.vehicleType, + value: ( + + {vehicle.vehicleType} + {vehicle.vehicleId} + + ), }, { - value: vehicle.vehicleId, + value: vehicle.lastMileageRegistration?.date + ? format(vehicle.lastMileageRegistration?.date, 'dd.MM.yyyy') + : '-', }, { value: vehicle.lastMileageRegistration?.mileage @@ -257,6 +284,8 @@ export const VehicleBulkMileageRow = ({ vehicle }: Props) => { control={control} id={vehicle.vehicleId} name={vehicle.vehicleId} + backgroundColor="blue" + placeholder="km" type="number" suffix=" km" thousandSeparator @@ -375,6 +404,20 @@ export const VehicleBulkMileageRow = ({ vehicle }: Props) => { type="error" message={formatMessage(vehicleMessage.mileageHistoryFetchFailed)} /> + ) : showSubdata ? ( + data?.vehiclesMileageRegistrationHistory ? ( + + ) : ( + + ) ) : ( > => { + const filteredData = data.mileageRegistrationHistory?.reduce< + Record + >((acc, current) => { + if (!current.mileage || !current.date || !current.originCode) { + return acc + } + + const currentDate = new Date(current.date).toISOString() + //01.01.1993-ISLAND.IS + const mashedKey = currentDate + '-' + current.originCode + //If the "mashed" key isn't in the key array, add it. + if (!Object.keys(acc).includes(mashedKey)) { + acc[mashedKey] = { date: currentDate, mileage: current.mileage } + } + return acc + }, {}) + + if (!filteredData) { + return [] + } + + return Object.values(filteredData) + .sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()) + .map((item) => ({ + date: format(new Date(item.date), 'dd.MM.yyyy'), + mileage: item.mileage, + })) +} + +export const VehicleBulkMileageSubData = ({ + vehicleId, + loading, + data, +}: Props) => { + const { formatMessage } = useLocale() + + const [displayMode, setDisplayMode] = useState<'chart' | 'table'>('table') + + const nestedTable = useMemo(() => { + if (!data) { + return [[]] + } + const tableData: Array> = [[]] + for (const mileageRegistration of data.mileageRegistrationHistory?.slice( + 0, + 5, + ) ?? []) { + if (mileageRegistration) { + tableData.push([ + formatDate(mileageRegistration.date), + mileageRegistration.originCode, + displayWithUnit(mileageRegistration.mileage, 'km', true), + ]) + } + } + + return tableData + }, [data]) + + return ( + + + + {formatMessage(vehicleMessage.registrationHistory)} + + + + {displayMode === 'table' ? ( + + ) : data.mileageRegistrationHistory ? ( + `${numberFormat(arg)} km`, + }} + /> + ) : undefined} + + + + + + + ) +} diff --git a/libs/portals/my-pages/assets/src/screens/VehicleBulkMileage/VehicleBulkMileageTable.tsx b/libs/portals/my-pages/assets/src/screens/VehicleBulkMileage/VehicleBulkMileageTable.tsx index 0cc14273bbe8..e4bec2aafdf9 100644 --- a/libs/portals/my-pages/assets/src/screens/VehicleBulkMileage/VehicleBulkMileageTable.tsx +++ b/libs/portals/my-pages/assets/src/screens/VehicleBulkMileage/VehicleBulkMileageTable.tsx @@ -1,10 +1,12 @@ -import { Table as T, Box } from '@island.is/island-ui/core' +import { Table as T, Box, Text } from '@island.is/island-ui/core' import { useLocale } from '@island.is/localization' import { EmptyTable, ExpandHeader } from '@island.is/portals/my-pages/core' import { vehicleMessage } from '../../lib/messages' import { useMemo } from 'react' import { VehicleType } from './types' import { VehicleBulkMileageRow } from './VehicleBulkMileageRow' +import { displayWithUnit } from '../../utils/displayWithUnit' +import { useFormContext } from 'react-hook-form' interface Props { vehicles: Array @@ -23,6 +25,35 @@ const VehicleBulkMileageTable = ({ vehicles, loading }: Props) => { )) }, [formatMessage, vehicles]) + const { getValues, watch } = useFormContext() + + const totalLastMileage = useMemo(() => { + if (!vehicles.length) { + return 0 + } + + return vehicles.reduce( + (totalMileage, vehicle) => + totalMileage + (vehicle.lastMileageRegistration?.mileage ?? 0), + 0, + ) + }, [vehicles]) + + const formValues = watch() + + const totalRegisteredMileage = useMemo(() => { + const values = getValues() + + return Object.values(values).reduce((total, mileage) => { + const number = Number.parseInt(mileage) + if (!number || Number.isNaN(number)) { + return total + } + return total + number + }, 0) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [formValues]) + return (
@@ -32,13 +63,46 @@ const VehicleBulkMileageTable = ({ vehicles, loading }: Props) => { data={[ { value: '', printHidden: true }, { value: formatMessage(vehicleMessage.type) }, - { value: formatMessage(vehicleMessage.permno) }, - { value: formatMessage(vehicleMessage.lastRegistration) }, + { value: formatMessage(vehicleMessage.lastRegistered) }, + { value: formatMessage(vehicleMessage.lastStatus) }, { value: formatMessage(vehicleMessage.odometer) }, { value: '', printHidden: true }, ]} /> - {rows} + + {rows} + {rows.length > 0 && ( + + + + + {formatMessage(vehicleMessage.total)} + + + + + + + + + {displayWithUnit(totalLastMileage, 'km', true)} + + + + + + + {displayWithUnit(totalRegisteredMileage, 'km', true)} + + + + + )} + )} {(!rows.length || loading) && ( diff --git a/libs/portals/my-pages/assets/src/screens/VehicleMileage/VehicleMileage.tsx b/libs/portals/my-pages/assets/src/screens/VehicleMileage/VehicleMileage.tsx index 6b7aee95f6c7..9fd231e906cf 100644 --- a/libs/portals/my-pages/assets/src/screens/VehicleMileage/VehicleMileage.tsx +++ b/libs/portals/my-pages/assets/src/screens/VehicleMileage/VehicleMileage.tsx @@ -1,5 +1,5 @@ import { useParams } from 'react-router-dom' -import { useState } from 'react' +import { useEffect, useState } from 'react' import { InputController } from '@island.is/shared/form-fields' import { useForm } from 'react-hook-form' import { @@ -21,6 +21,7 @@ import { SAMGONGUSTOFA_SLUG, IntroHeader, icelandLocalTime, + SimpleBarChart, } from '@island.is/portals/my-pages/core' import { isReadDateToday } from '../../utils/readDate' @@ -33,6 +34,9 @@ import { import { displayWithUnit } from '../../utils/displayWithUnit' import * as styles from './VehicleMileage.css' import { Problem } from '@island.is/react-spa/shared' +import { VehicleMileageDetail } from '@island.is/api/schema' +import format from 'date-fns/format' +import { Features, useFeatureFlagClient } from '@island.is/react/feature-flags' const ORIGIN_CODE = 'ISLAND.IS' @@ -44,6 +48,11 @@ interface FormData { odometerStatus: number } +interface ChartProps { + date: string + mileage: number +} + const VehicleMileage = () => { useNamespaces('sp.vehicles') const { formatMessage } = useLocale() @@ -56,6 +65,22 @@ const VehicleMileage = () => { reset, } = useForm() + const [showChart, setShowChart] = useState(false) + const featureFlagClient = useFeatureFlagClient() + useEffect(() => { + const isFlagEnabled = async () => { + const ffEnabled = await featureFlagClient.getValue( + Features.isServicePortalVehicleBulkMileageSubdataPageEnabled, + false, + ) + if (ffEnabled) { + setShowChart(ffEnabled as boolean) + } + } + isFlagEnabled() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + const clearAll = () => { // Clear form, state and errors on success. setFormValue('') @@ -133,6 +158,43 @@ const VehicleMileage = () => { return } + const parseChartData = ( + data: Array, + ): Array> => { + const filteredData = data?.reduce>( + (acc, current) => { + if ( + !current.mileageNumber || + !current.readDate || + !current.originCode + ) { + return acc + } + + const currentDate = new Date(current.readDate).toISOString() + //01.01.1993-ISLAND.IS + const mashedKey = currentDate + '-' + current.originCode + //If the "mashed" key isn't in the key array, add it. + if (!Object.keys(acc).includes(mashedKey)) { + acc[mashedKey] = { date: currentDate, mileage: current.mileageNumber } + } + return acc + }, + {}, + ) + + if (!filteredData) { + return [] + } + + return Object.values(filteredData) + .sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()) + .map((item) => ({ + date: format(new Date(item.date), 'dd.MM.yyyy'), + mileage: item.mileage, + })) + } + return ( <> @@ -312,49 +374,78 @@ const VehicleMileage = () => { )} {!loading && !error && hasData && ( - - - - - {formatMessage(m.overview)} - - - - - - - - - {formatMessage(m.date)} - - {formatMessage(messages.vehicleMileageRegistration)} - - - {formatMessage(messages.odometer)} - - - - - {details?.map((item, i) => ( - - - {item.readDate - ? icelandLocalTime(item.readDate) - : ''} - - - {item.originCode} - - - {displayWithUnit(item.mileageNumber, 'km', true)} - + <> + + + + + {formatMessage(m.overview)} + + + + + + + + + + {formatMessage(m.date)} + + + {formatMessage(messages.vehicleMileageRegistration)} + + + {formatMessage(messages.odometer)} + - ))} - - - - - + + + {details?.map((item, i) => ( + + + {item.readDate + ? icelandLocalTime(item.readDate) + : ''} + + + {item.originCode} + + + {displayWithUnit(item.mileageNumber, 'km', true)} + + + ))} + + + + + + {showChart && ( + + + + )} + )} diff --git a/libs/portals/my-pages/assets/src/utils/displayWithUnit.ts b/libs/portals/my-pages/assets/src/utils/displayWithUnit.ts index aa310d4e7d77..e313dd5cc7b5 100644 --- a/libs/portals/my-pages/assets/src/utils/displayWithUnit.ts +++ b/libs/portals/my-pages/assets/src/utils/displayWithUnit.ts @@ -5,8 +5,16 @@ export const displayWithUnit = ( unit: 'kg' | 'cc' | 'hö' | 'mm' | 'g/km' | 'km', formatNumber?: boolean, ) => { - if (value) { - return `${formatNumber ? numberFormat(+value) : value} ${unit}` + //Explict checking because 0 is falsy. + if (value === null || value === undefined) { + return '' } - return '' + + const number = +value + + if (Number.isNaN(number)) { + return '' + } + + return `${formatNumber ? numberFormat(number) : value} ${unit}` } diff --git a/libs/portals/my-pages/core/src/components/Charts/SimpleBarChart/SimpleBarChart.tsx b/libs/portals/my-pages/core/src/components/Charts/SimpleBarChart/SimpleBarChart.tsx new file mode 100644 index 000000000000..ba7bd5427c41 --- /dev/null +++ b/libs/portals/my-pages/core/src/components/Charts/SimpleBarChart/SimpleBarChart.tsx @@ -0,0 +1,135 @@ +import { + BarChart, + Bar, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + ResponsiveContainer, +} from 'recharts' +import { + CustomizedAxisTick, + RenderLegend, + CustomTooltip, + YAxisLabel, +} from '../sharedChartComponents' +import { Box } from '@island.is/island-ui/core' +import * as styles from './styles.css' +import { theme } from '@island.is/island-ui/theme' + +interface Axis { + label?: string + datakey: string + valueFormat?: (arg: number) => string +} + +interface BarType { + datakey: string +} + +interface TooltipType { + labels: Record + valueFormat?: (arg: number) => string +} + +interface GraphDataProps { + title?: string + data: Array> + datakeys: Array + bars: Array + xAxis: Axis + yAxis: Axis + tooltip?: TooltipType +} + +export const SimpleBarChart = ({ + title, + data, + datakeys, + bars, + xAxis, + yAxis, + tooltip, +}: GraphDataProps) => { + return ( + + + + + + + + + } + padding={{ left: 30 }} + tickLine={false} + /> + } /> + + } + /> + + } + /> + {bars.map((item: BarType, index: number) => ( + + ))} + + + + + + + ) +} + +export default SimpleBarChart diff --git a/libs/portals/my-pages/core/src/components/Charts/SimpleBarChart/styles.css.ts b/libs/portals/my-pages/core/src/components/Charts/SimpleBarChart/styles.css.ts new file mode 100644 index 000000000000..a5c91da05a86 --- /dev/null +++ b/libs/portals/my-pages/core/src/components/Charts/SimpleBarChart/styles.css.ts @@ -0,0 +1,43 @@ +import { style } from '@vanilla-extract/css' + +export const frameWrapper = style({ + //height: '646px', + width: '100%', + minHeight: 124, + boxSizing: 'border-box', + position: 'relative', + background: 'transparent', + overflowX: 'scroll', +}) + +export const outerWrapper = style({ + width: '100%', + height: '100%', + borderTopLeftRadius: '8px', + borderTopRightRadius: '8px', + alignItems: 'center', +}) + +export const innerWrapper = style({ + minHeight: '80px', + borderTopLeftRadius: '8px', + borderTopRightRadius: '8px', +}) + +export const graphWrapper = style({ + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + width: '100%', + height: '100%', +}) + +export const graphParent = style({ + width: '100%', + height: '420px', +}) + +export const pie = style({ + width: '100%', + height: 'fit-content', +}) diff --git a/libs/portals/my-pages/core/src/components/Charts/charts.css.ts b/libs/portals/my-pages/core/src/components/Charts/charts.css.ts new file mode 100644 index 000000000000..e894605b5e69 --- /dev/null +++ b/libs/portals/my-pages/core/src/components/Charts/charts.css.ts @@ -0,0 +1,54 @@ +import { style } from '@vanilla-extract/css' + +export const list = style({ + color: '#00003C', + display: 'inline-flex', + alignItems: 'center', + fontFamily: 'IBM Plex Sans', + fontStyle: 'normal', + fontWeight: 'normal', + fontSize: '14px', + lineHeight: '24px', + flexWrap: 'nowrap', +}) + +export const dot = style({ + width: '12px', + height: '12px', + borderWidth: '3px', + borderStyle: 'solid', + borderRadius: '12px', + marginRight: '2px', + marginLeft: '8px', +}) + +export const listWrapper = style({ + display: 'flex', + flexDirection: 'row', + justifyContent: 'flex-start', + flexWrap: 'wrap', +}) + +export const wrapper = style({ + paddingTop: '37px', +}) + +export const title = style({ + fontFamily: 'IBM Plex Sans', + fontStyle: 'normal', + fontWeight: 600, + fontSize: '14px', + lineHeight: '16px', + color: '#00003C', + paddingBottom: '12px', +}) + +export const tooltip = style({ + display: 'inline-block', + backgroundColor: '#F2F7FF', + borderRadius: '8px', + padding: '20px', + maxWidth: '240px', + fontSize: '15px', + lineHeight: '20px', +}) diff --git a/libs/portals/my-pages/core/src/components/Charts/index.ts b/libs/portals/my-pages/core/src/components/Charts/index.ts new file mode 100644 index 000000000000..ac68afc50713 --- /dev/null +++ b/libs/portals/my-pages/core/src/components/Charts/index.ts @@ -0,0 +1 @@ +export * from './SimpleBarChart/SimpleBarChart' diff --git a/libs/portals/my-pages/core/src/components/Charts/sharedChartComponents.tsx b/libs/portals/my-pages/core/src/components/Charts/sharedChartComponents.tsx new file mode 100644 index 000000000000..9c76da7bc149 --- /dev/null +++ b/libs/portals/my-pages/core/src/components/Charts/sharedChartComponents.tsx @@ -0,0 +1,211 @@ +import React from 'react' +import * as styles from './charts.css' +import cn from 'classnames' +import { LegendProps, TooltipProps } from 'recharts' +import { Box, Text } from '@island.is/island-ui/core' +import { isDefined } from '@island.is/shared/utils' + +interface AxisTickProps { + x?: number + y?: number + className?: string + payload?: { value: string } + valueFormat?: (arg: number) => string +} + +interface CustomTooltipProps extends TooltipProps { + valueLabels?: Record + valueFormat?: (arg: number) => string +} + +export const CustomTooltip = ({ + payload, + active, + label, + valueLabels, + valueFormat, +}: CustomTooltipProps) => { + if (active && payload && payload.length) { + return ( + + {label} + {payload + .map((item, index) => { + if (!item.value) return null + + return ( + +
+ + {valueLabels && item.dataKey + ? valueLabels[item.dataKey] + : item.name} + : {valueFormat ? valueFormat(item.value) : item.value} + + + ) + }) + .filter(isDefined)} + + ) + } + + return null +} + +export const CustomizedAxisTick = ({ + x, + y, + className, + payload, +}: AxisTickProps) => { + const xAxis = className?.includes('xAxis') + return ( + + + {payload?.value} {xAxis} + + + ) +} + +export const CustomizedRightAxisTick = ({ x, y, payload }: AxisTickProps) => { + return ( + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore make web strict + + + { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore make web strict + payload.value + } + + + ) +} +interface CustomLegendProps extends LegendProps { + title?: string + labels?: Record +} +export const RenderLegend = ({ payload, title, labels }: CustomLegendProps) => { + if (!payload || !payload.values) { + return null + } + + return ( + + {[...payload.values()].map((item) => ( + + + {labels?.[item.value] ?? item.value} + + ))} + + ) +} + +// Define the type for the destructured props +type CustomLabelProps = { + cx: number + cy: number + midAngle: number + outerRadius: number + innerRadius: number + percent: number +} + +const RADIAN = Math.PI / 180 +export const renderCustomizedLabel = ({ + cx, + cy, + midAngle, + outerRadius, + innerRadius, + percent, +}: CustomLabelProps) => { + const radius = innerRadius + (outerRadius - innerRadius) * 1.2 + const x = cx + radius * Math.cos(-midAngle * RADIAN) + const y = cy + radius * Math.sin(-midAngle * RADIAN) + + return ( + outerRadius ? 'middle' : 'end'} + dominantBaseline="central" + fontSize="12px" + > + {`${(percent * 100).toFixed(0)}%`} + + ) +} + +interface yAxisLabelProps { + label?: string + labelRight?: string + rightPadding?: number +} +export const YAxisLabel = ({ + label, + labelRight, + rightPadding = 0, +}: yAxisLabelProps) => { + return ( + + {label && {label}} + {labelRight && {labelRight}} + + ) +} + +export const COLORS = [ + '#FFF066', + '#FF99B9', + '#C3ABD9', + '#D799C7', + '#99F4EA', + '#B5B6EC', + '#FF0050', + '#00B39E', + '#0061FF', + '#E6CF00', + '#6A2EA0', + '#00E4CA', + '#FFFCE0', + '#9A0074', + '#99C0FF', +] + +export default RenderLegend diff --git a/libs/portals/my-pages/core/src/components/EmptyTable/EmptyTable.tsx b/libs/portals/my-pages/core/src/components/EmptyTable/EmptyTable.tsx index 3ed19c1b5a44..1b12a3a6efe7 100644 --- a/libs/portals/my-pages/core/src/components/EmptyTable/EmptyTable.tsx +++ b/libs/portals/my-pages/core/src/components/EmptyTable/EmptyTable.tsx @@ -1,4 +1,4 @@ -import { Box, LoadingDots, Text } from '@island.is/island-ui/core' +import { Box, BoxProps, LoadingDots, Text } from '@island.is/island-ui/core' import { useLocale } from '@island.is/localization' import { MessageDescriptor } from 'react-intl' import * as styles from './EmptyTable.css' @@ -6,9 +6,14 @@ import * as styles from './EmptyTable.css' type Props = { message?: string | MessageDescriptor loading?: boolean + background?: BoxProps['background'] } -export const EmptyTable: React.FC = ({ message, loading }) => { +export const EmptyTable: React.FC = ({ + message, + loading, + background, +}) => { const { formatMessage } = useLocale() const msg = message @@ -18,7 +23,7 @@ export const EmptyTable: React.FC = ({ message, loading }) => { : null return ( - + {loading && } {!loading && message && ( diff --git a/libs/portals/my-pages/core/src/index.ts b/libs/portals/my-pages/core/src/index.ts index f87e890391dc..ddb3535f0f7d 100644 --- a/libs/portals/my-pages/core/src/index.ts +++ b/libs/portals/my-pages/core/src/index.ts @@ -9,6 +9,7 @@ export * from './components/EmptyState/EmptyState' export * from './components/EmptyState/EmptyImage' export * from './components/EmptyState/EmptyImgSmall' export * from './components/Menu/Menu' +export * from './components/Charts' export * from './components/Modal/Modal' export * from './components/ConfirmationModal/ConfirmationModal' export * from './components/UserInfoLine/UserInfoLine' diff --git a/libs/portals/my-pages/core/src/lib/messages.ts b/libs/portals/my-pages/core/src/lib/messages.ts index eb534bda0c73..3f30f63dd7dd 100644 --- a/libs/portals/my-pages/core/src/lib/messages.ts +++ b/libs/portals/my-pages/core/src/lib/messages.ts @@ -313,6 +313,10 @@ export const m = defineMessages({ id: 'service.portal:vehicles-bulk-mileage', defaultMessage: 'Magnskráning kílómetrastöðu', }, + vehiclesRegisterMileage: { + id: 'service.portal:vehicles-register-mileage', + defaultMessage: 'Skrá kílómetrastöðu', + }, vehiclesBulkMileageUpload: { id: 'service.portal:vehicles-bulk-mileage-upload', defaultMessage: 'Magnskrá með skjali', diff --git a/libs/portals/my-pages/graphql/src/hooks/profile/useResendEmailVerification.ts b/libs/portals/my-pages/graphql/src/hooks/profile/useResendEmailVerification.ts deleted file mode 100644 index efd1394a3455..000000000000 --- a/libs/portals/my-pages/graphql/src/hooks/profile/useResendEmailVerification.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { useMutation } from '@apollo/client' -import { Mutation } from '@island.is/api/schema' -import { RESEND_EMAIL_VERIFICATION } from '../../lib/mutations/resendEmailVerification' - -export const useResendEmailVerification = () => { - const [resendEmailVerificationMutation, { loading, error }] = useMutation< - Mutation, - {} - >(RESEND_EMAIL_VERIFICATION) - - const resendEmailVerification = () => { - return resendEmailVerificationMutation() - } - - return { - resendEmailVerification, - loading, - error, - } -} diff --git a/libs/portals/my-pages/graphql/src/index.ts b/libs/portals/my-pages/graphql/src/index.ts index f94f76e92d26..527d31e51137 100644 --- a/libs/portals/my-pages/graphql/src/index.ts +++ b/libs/portals/my-pages/graphql/src/index.ts @@ -13,7 +13,6 @@ export * from './lib/queries/getUserProfile' export * from './lib/queries/alertBanners' export * from './lib/mutations/createUserProfile' export * from './lib/mutations/updateUserProfile' -export * from './lib/mutations/resendEmailVerification' export * from './lib/mutations/createPkPass' export * from './lib/client' export * from './hooks/documents/useListDocuments' @@ -24,7 +23,6 @@ export * from './hooks/profile/useUpdateUserProfile' export * from './hooks/profile/useCreateUserProfile' export * from './hooks/profile/useVerifySms' export * from './hooks/profile/useVerifyEmail' -export * from './hooks/profile/useResendEmailVerification' export * from './hooks/profile/useUpdateOrCreateUserProfile' export * from './hooks/profile/useDeleteIslykillValue' export * from './hooks/passport/usePassport' diff --git a/libs/portals/my-pages/graphql/src/lib/mutations/resendEmailVerification.ts b/libs/portals/my-pages/graphql/src/lib/mutations/resendEmailVerification.ts deleted file mode 100644 index 7e14c6320678..000000000000 --- a/libs/portals/my-pages/graphql/src/lib/mutations/resendEmailVerification.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { gql } from '@apollo/client' - -export const RESEND_EMAIL_VERIFICATION = gql` - mutation resendEmailVerification { - resendEmailVerification { - created - } - } -` diff --git a/libs/portals/my-pages/health/src/lib/messages.ts b/libs/portals/my-pages/health/src/lib/messages.ts index d22f5798bed3..67c32b9383c0 100644 --- a/libs/portals/my-pages/health/src/lib/messages.ts +++ b/libs/portals/my-pages/health/src/lib/messages.ts @@ -1024,10 +1024,22 @@ export const messages = defineMessages({ defaultMessage: 'Textareitur má ekki vera tómur sé þessi valkostur valinn. ', }, + organMinor: { + id: 'sp.health:organ-minor', + defaultMessage: 'Til að geta gerst líffæragjafi þarftu að vera 18 ára.', + }, + organTemporaryNationalId: { + id: 'sp.health:organ-temporary-national-id', + defaultMessage: 'Líffæragjöf er ekki heimiluð á kerfiskennitölur.', + }, other: { id: 'sp.health:other-lower-case', defaultMessage: 'annað', }, + otherPascalCase: { + id: 'sp.health:other', + defaultMessage: 'Annað', + }, registrationComplete: { id: 'sp.health:registration-complete', defaultMessage: 'Skráning tókst', diff --git a/libs/portals/my-pages/health/src/module.tsx b/libs/portals/my-pages/health/src/module.tsx index 5d9cc4fdfccd..2f6f92805030 100644 --- a/libs/portals/my-pages/health/src/module.tsx +++ b/libs/portals/my-pages/health/src/module.tsx @@ -59,7 +59,7 @@ const OrganDonation = lazy(() => ) const OrganDonationRegistration = lazy(() => - import('./screens/OrganDonationRegistration/RegistrationForm'), + import('./screens/OrganDonation/components/RegistrationForm'), ) const Vaccinations = lazy(() => diff --git a/libs/portals/my-pages/health/src/screens/OrganDonation/OrganDonation.css.ts b/libs/portals/my-pages/health/src/screens/OrganDonation/OrganDonation.css.ts new file mode 100644 index 000000000000..4dfc4127851b --- /dev/null +++ b/libs/portals/my-pages/health/src/screens/OrganDonation/OrganDonation.css.ts @@ -0,0 +1,32 @@ +import { style, keyframes } from '@vanilla-extract/css' +import { theme } from '@island.is/island-ui/theme' + +export const buttonContainer = style({ + gap: theme.spacing[2], +}) + +const fadeIn = keyframes({ + from: { + opacity: 0, + }, + to: { + opacity: 1, + }, +}) + +const fadeOut = keyframes({ + from: { + opacity: 1, + }, + to: { + opacity: 0, + }, +}) + +export const commentVisible = style({ + animation: `${fadeIn} 0.5s forwards`, +}) + +export const commentHidden = style({ + animation: `${fadeOut} 0.5s forwards`, +}) diff --git a/libs/portals/my-pages/health/src/screens/OrganDonation/OrganDonation.graphql b/libs/portals/my-pages/health/src/screens/OrganDonation/OrganDonation.graphql index d120a592b167..6790da16ee2b 100644 --- a/libs/portals/my-pages/health/src/screens/OrganDonation/OrganDonation.graphql +++ b/libs/portals/my-pages/health/src/screens/OrganDonation/OrganDonation.graphql @@ -10,21 +10,8 @@ query getDonorStatus($locale: String) { } comment } - } - } -} - -query getOrgansList($locale: String) { - healthDirectorateOrganDonation(locale: $locale) { - donor { - isDonor - limitations { - hasLimitations - limitedOrgansList { - id - name - } - } + isMinor + isTemporaryResident } organList { id diff --git a/libs/portals/my-pages/health/src/screens/OrganDonation/OrganDonation.tsx b/libs/portals/my-pages/health/src/screens/OrganDonation/OrganDonation.tsx index 7efcd493e3b5..7ef00c370fb5 100644 --- a/libs/portals/my-pages/health/src/screens/OrganDonation/OrganDonation.tsx +++ b/libs/portals/my-pages/health/src/screens/OrganDonation/OrganDonation.tsx @@ -1,15 +1,17 @@ +import { Box, Button, Text } from '@island.is/island-ui/core' import { useLocale, useNamespaces } from '@island.is/localization' import { ActionCard, CardLoader, - IntroHeader, + IntroWrapper, LinkResolver, } from '@island.is/portals/my-pages/core' +import { Problem } from '@island.is/react-spa/shared' import { messages as m } from '../../lib/messages' -import { Button, Box, Text } from '@island.is/island-ui/core' import { HealthPaths } from '../../lib/paths' -import { Problem } from '@island.is/react-spa/shared' import { useGetDonorStatusQuery } from './OrganDonation.generated' +import { NoAccess } from './components/NoAccess' +import { getOrganText } from './helpers/textMapper' const OrganDonation = () => { useNamespaces('sp.health') @@ -22,30 +24,39 @@ const OrganDonation = () => { }, }) const donorStatus = data?.healthDirectorateOrganDonation.donor - const cardText: string = donorStatus?.isDonor - ? donorStatus?.limitations?.hasLimitations - ? [ - formatMessage(m.iAmOrganDonorWithExceptionsText), - donorStatus?.limitations.limitedOrgansList - ?.map((organ) => organ.name) - .join(', '), - ].join(' ') + '.' - : formatMessage(m.iAmOrganDonorText) - : formatMessage(m.iAmNotOrganDonorText) + const isMinor = donorStatus?.isMinor + const isTemporaryResident = donorStatus?.isTemporaryResident + + const comment = donorStatus?.limitations?.comment + + const allLimitations: Array = + donorStatus?.limitations?.limitedOrgansList?.map((item) => item.name) ?? [] - const heading = donorStatus?.isDonor - ? donorStatus.limitations?.hasLimitations - ? formatMessage(m.iAmOrganDonorWithExceptions) - : formatMessage(m.iAmOrganDonor) - : formatMessage(m.iAmNotOrganDonor) + if (comment !== undefined && comment !== null && comment.length > 0) { + allLimitations.push(comment) + } + + const texts = getOrganText( + donorStatus?.isDonor ?? true, + donorStatus?.limitations?.hasLimitations ?? false, + { + iAmOrganDonorWithExceptionsText: formatMessage( + m.iAmOrganDonorWithExceptionsText, + ), + iAmNotOrganDonorText: formatMessage(m.iAmOrganDonorText), + iAmOrganDonorText: formatMessage(m.iAmNotOrganDonorText), + iAmOrganDonorWithExceptions: formatMessage(m.iAmOrganDonorWithExceptions), + iAmOrganDonor: formatMessage(m.iAmOrganDonor), + iAmNotOrganDonor: formatMessage(m.iAmNotOrganDonor), + }, + allLimitations, + ) return ( - - - + { - - + , + ]} + > {loading && ( )} - {!error && !loading && donorStatus !== null && ( - - - {formatMessage(m.takeOnOrganDonation)} - - - + {!error && + !loading && + !isMinor && + !isTemporaryResident && + donorStatus !== null && ( + + + {formatMessage(m.takeOnOrganDonation)} + + + + )} + {!error && !loading && (isMinor || isTemporaryResident) && ( + )} {error && !loading && } {!error && !loading && data === null && ( )} - + ) } diff --git a/libs/portals/my-pages/health/src/screens/OrganDonation/components/Limitations.tsx b/libs/portals/my-pages/health/src/screens/OrganDonation/components/Limitations.tsx new file mode 100644 index 000000000000..ab0e895e30a3 --- /dev/null +++ b/libs/portals/my-pages/health/src/screens/OrganDonation/components/Limitations.tsx @@ -0,0 +1,113 @@ +import { HealthDirectorateOrganDonationOrgan } from '@island.is/api/schema' +import { + Box, + Checkbox, + Divider, + GridColumn, + GridContainer, + GridRow, + Input, + Stack, +} from '@island.is/island-ui/core' +import { useLocale, useNamespaces } from '@island.is/localization' +import { useState } from 'react' +import { messages } from '../../../lib/messages' +import * as styles from '../OrganDonation.css' + +interface LimitationsProps { + data: HealthDirectorateOrganDonationOrgan[] + selected?: string[] | null + exceptionComment?: string +} + +const Limitations = ({ + data, + selected, + exceptionComment, +}: LimitationsProps) => { + useNamespaces('sp.health') + const { formatMessage } = useLocale() + const [checked, setChecked] = useState>(selected ?? []) + const [comment, setComment] = useState(exceptionComment ?? '') + + const handleCheckboxChange = (id: string, isChecked: boolean) => { + setChecked((prevState) => + isChecked ? [...prevState, id] : prevState.filter((item) => item !== id), + ) + } + + return ( + + + + + {data?.map((y, yi) => ( + + + handleCheckboxChange(y.id ?? '', e.target.checked) + } + checked={checked.includes(y.id ?? '')} + /> + + ))} + + { + setComment(e.target.value) + handleCheckboxChange('other', e.target.checked) + }} + checked={checked.includes('other')} + /> + + + + {checked.includes('other') && ( + + + + + setComment(e.target.value)} + value={comment} + /> + + + + + )} + + ) +} + +export default Limitations diff --git a/libs/portals/my-pages/health/src/screens/OrganDonationRegistration/Loader.tsx b/libs/portals/my-pages/health/src/screens/OrganDonation/components/Loader.tsx similarity index 100% rename from libs/portals/my-pages/health/src/screens/OrganDonationRegistration/Loader.tsx rename to libs/portals/my-pages/health/src/screens/OrganDonation/components/Loader.tsx index b907c382c105..59bf49245f20 100644 --- a/libs/portals/my-pages/health/src/screens/OrganDonationRegistration/Loader.tsx +++ b/libs/portals/my-pages/health/src/screens/OrganDonation/components/Loader.tsx @@ -1,6 +1,6 @@ -import React from 'react' import { Box, SkeletonLoader, Stack } from '@island.is/island-ui/core' import { useIsMobile } from '@island.is/portals/my-pages/core' +import React from 'react' interface Props { amount?: number diff --git a/libs/portals/my-pages/health/src/screens/OrganDonation/components/NoAccess.tsx b/libs/portals/my-pages/health/src/screens/OrganDonation/components/NoAccess.tsx new file mode 100644 index 000000000000..369589995cd1 --- /dev/null +++ b/libs/portals/my-pages/health/src/screens/OrganDonation/components/NoAccess.tsx @@ -0,0 +1,13 @@ +import { AlertMessage, GridColumn, GridRow } from '@island.is/island-ui/core' + +export const NoAccess = ({ text }: { text: string }) => { + return ( + + + + + + ) +} + +export default NoAccess diff --git a/libs/portals/my-pages/health/src/screens/OrganDonationRegistration/RegistrationForm.tsx b/libs/portals/my-pages/health/src/screens/OrganDonation/components/RegistrationForm.tsx similarity index 77% rename from libs/portals/my-pages/health/src/screens/OrganDonationRegistration/RegistrationForm.tsx rename to libs/portals/my-pages/health/src/screens/OrganDonation/components/RegistrationForm.tsx index 6ef3db623dcf..4b4db5a87fcf 100644 --- a/libs/portals/my-pages/health/src/screens/OrganDonationRegistration/RegistrationForm.tsx +++ b/libs/portals/my-pages/health/src/screens/OrganDonation/components/RegistrationForm.tsx @@ -1,58 +1,68 @@ import { Box, + Button, RadioButton, Stack, Text, - Button, toast, - LoadingDots, } from '@island.is/island-ui/core' import { useLocale, useNamespaces } from '@island.is/localization' import { - IntroHeader, + IntroWrapper, LinkResolver, m as coreMessages, } from '@island.is/portals/my-pages/core' -import { messages } from '../..' -import { useEffect, useState } from 'react' -import React from 'react' -import { HealthPaths } from '../../lib/paths' -import * as styles from './OrganDonationRegistration.css' -import Limitations from './Limitations' +import React, { useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' +import { messages } from '../../..' +import { HealthPaths } from '../../../lib/paths' +import * as styles from '../OrganDonation.css' import { - useGetOrgansListQuery, + useGetDonorStatusQuery, useUpdateOrganDonationInfoMutation, -} from '../OrganDonation/OrganDonation.generated' +} from '../OrganDonation.generated' +import Limitations from './Limitations' import { Loader } from './Loader' +import { NoAccess } from './NoAccess' const OPT_IN = 'opt-in' const OPT_IN_EXCEPTIONS = 'opt-in-exceptions' const OPT_OUT = 'opt-out' -export const Form2 = () => { +export const OrganRegistrationForm = () => { useNamespaces('sp.health') const { formatMessage, lang } = useLocale() const navigate = useNavigate() - const { data, loading } = useGetOrgansListQuery({ + const { data, loading } = useGetDonorStatusQuery({ variables: { locale: lang }, fetchPolicy: 'no-cache', }) const isDonor = data?.healthDirectorateOrganDonation.donor?.isDonor + const isMinor = data?.healthDirectorateOrganDonation.donor?.isMinor + const isTemporaryResident = + data?.healthDirectorateOrganDonation.donor?.isTemporaryResident const hasLimitations = data?.healthDirectorateOrganDonation.donor?.limitations?.hasLimitations const allLimitations = data?.healthDirectorateOrganDonation.organList + const exceptionComment = + data?.healthDirectorateOrganDonation.donor?.limitations?.comment + const selectedLimitations = data?.healthDirectorateOrganDonation.donor?.limitations?.limitedOrgansList?.map( (item) => item.id, ) + + const updatedLimitations = selectedLimitations + ? [...selectedLimitations, ...(exceptionComment?.length ? ['other'] : [])] + : [] const donorStatus = isDonor ? hasLimitations ? OPT_IN_EXCEPTIONS : OPT_IN : OPT_OUT + const [radioValue, setRadioValue] = useState(donorStatus) const [updateDonorStatus, { loading: submitLoading }] = @@ -76,8 +86,8 @@ export const Form2 = () => { e.preventDefault() const formData = new FormData(e.currentTarget) const data = Object.fromEntries(formData.entries()) - const idKey = 'selected-limitations-' + const otherLimitations = data['otherLimitatons'].toString() const limitations = Object.keys(data) .filter((key) => key.includes(idKey)) .map((key) => key.replace(idKey, '').toLowerCase()) @@ -87,6 +97,7 @@ export const Form2 = () => { input: { isDonor: radioValue === OPT_IN || radioValue === OPT_IN_EXCEPTIONS, organLimitations: radioValue === OPT_IN_EXCEPTIONS ? limitations : [], + comment: otherLimitations, }, locale: lang, }, @@ -94,16 +105,24 @@ export const Form2 = () => { } return ( - - + {formatMessage(messages.changeTake)} {loading && } - {!loading && ( + {!loading && (isMinor || isTemporaryResident) && ( + + )} + {!loading && !isMinor && !isTemporaryResident && ( { radioValue === OPT_IN_EXCEPTIONS && ( )} @@ -187,8 +207,8 @@ export const Form2 = () => { )} - + ) } -export default Form2 +export default OrganRegistrationForm diff --git a/libs/portals/my-pages/health/src/screens/OrganDonation/helpers/textMapper.ts b/libs/portals/my-pages/health/src/screens/OrganDonation/helpers/textMapper.ts new file mode 100644 index 000000000000..7793edbf10bf --- /dev/null +++ b/libs/portals/my-pages/health/src/screens/OrganDonation/helpers/textMapper.ts @@ -0,0 +1,27 @@ +export const getOrganText = ( + isDonor: boolean, + hasLimitations: boolean, + texts: { + iAmOrganDonorWithExceptionsText: string + iAmOrganDonorText: string + iAmNotOrganDonorText: string + iAmOrganDonorWithExceptions: string + iAmOrganDonor: string + iAmNotOrganDonor: string + }, + limitations: string[], +) => { + const limitationText = hasLimitations + ? texts.iAmOrganDonorWithExceptionsText + ' ' + limitations.join(', ') + : texts.iAmOrganDonorText + + const cardText: string = isDonor ? limitationText : texts.iAmNotOrganDonorText + + const heading = isDonor + ? hasLimitations + ? texts.iAmOrganDonorWithExceptions + : texts.iAmOrganDonor + : texts.iAmNotOrganDonor + + return { cardText, heading } +} diff --git a/libs/portals/my-pages/health/src/screens/OrganDonationRegistration/Limitations.tsx b/libs/portals/my-pages/health/src/screens/OrganDonationRegistration/Limitations.tsx deleted file mode 100644 index e625cdf7a3a4..000000000000 --- a/libs/portals/my-pages/health/src/screens/OrganDonationRegistration/Limitations.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { Box, Checkbox, Divider, Stack } from '@island.is/island-ui/core' -import React, { useState } from 'react' -import { useNamespaces } from '@island.is/localization' -import { HealthDirectorateOrganDonationOrgan } from '@island.is/api/schema' - -interface LimitationsProps { - data: HealthDirectorateOrganDonationOrgan[] - selected?: string[] | null -} - -const Limitations = ({ data, selected }: LimitationsProps) => { - useNamespaces('sp.health') - const [checked, setChecked] = useState>(selected ?? []) - const handleCheckboxChange = (id: string, isChecked: boolean) => { - setChecked((prevState) => - isChecked ? [...prevState, id] : prevState.filter((item) => item !== id), - ) - } - - //const input = data.find((x) => x.type === 'input') - - return ( - - - - - {data?.map( - (y, yi) => ( - // y.type === 'checkbox' && ( - - - - handleCheckboxChange(y.id ?? '', e.target.checked) - } - checked={checked.includes(y.id ?? '')} - /> - - ), - // ), - )} - - - {/* This is commented out because of feature that was removed. May be included later on */} - {/* {input && checked.includes(input.name.toLowerCase()) && ( - - - - - - - - - - )} */} - - ) -} - -export default Limitations diff --git a/libs/portals/my-pages/health/src/screens/OrganDonationRegistration/OrganDonationRegistration.css.ts b/libs/portals/my-pages/health/src/screens/OrganDonationRegistration/OrganDonationRegistration.css.ts deleted file mode 100644 index e985fe3863ac..000000000000 --- a/libs/portals/my-pages/health/src/screens/OrganDonationRegistration/OrganDonationRegistration.css.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { style } from '@vanilla-extract/css' -import { theme } from '@island.is/island-ui/theme' - -export const buttonContainer = style({ - gap: theme.spacing[2], -}) diff --git a/libs/portals/my-pages/information/src/components/PersonalInformation/Forms/ProfileForm/ProfileForm.tsx b/libs/portals/my-pages/information/src/components/PersonalInformation/Forms/ProfileForm/ProfileForm.tsx index 3572dd96d6c1..a18dc425f3ef 100644 --- a/libs/portals/my-pages/information/src/components/PersonalInformation/Forms/ProfileForm/ProfileForm.tsx +++ b/libs/portals/my-pages/information/src/components/PersonalInformation/Forms/ProfileForm/ProfileForm.tsx @@ -66,7 +66,6 @@ export const ProfileForm: FC> = ({ const [internalLoading, setInternalLoading] = useState(false) const [showPaperMail, setShowPaperMail] = useState(false) const [showDropModal, setShowDropModal] = useState() - const [v2UserProfileEnabled, setV2UserProfileEnabled] = useState(false) const { deleteIslykillValue, loading: deleteLoading } = useDeleteIslykillValue() const userInfo = useUserInfo() @@ -76,23 +75,6 @@ export const ProfileForm: FC> = ({ const [confirmNudge] = useConfirmNudgeMutation() const isCompany = userInfo?.profile?.subjectType === 'legalEntity' - const isV2UserProfileEnabled = async () => { - const ffEnabled = await featureFlagClient.getValue( - Features.isIASSpaPagesEnabled, - false, - ) - - if (ffEnabled) { - setV2UserProfileEnabled(!isCompany) - } - } - - /* Should disable email and phone input with deeplink to IDS */ - useEffect(() => { - isV2UserProfileEnabled() - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []) - /** * Creates a link to the IDS user profile page. * By setting the state to update, the user will exit the onboarding process after updating the desired field. @@ -210,7 +192,7 @@ export const ProfileForm: FC> = ({ loading={userLoading} > {!userLoading && - (v2UserProfileEnabled ? ( + (!isCompany ? ( > = ({ loading={userLoading} > {!userLoading && - (v2UserProfileEnabled ? ( + (!isCompany ? ( void hasIllustration?: boolean + paddingBottom?: BoxProps['paddingBottom'] + paddingTop?: BoxProps['paddingTop'] } export const RadioController: FC> = ({ @@ -54,6 +57,8 @@ export const RadioController: FC> = ({ split = '1/1', smallScreenSplit = '1/1', hasIllustration = false, + paddingBottom = 2, + paddingTop = 0, }) => { const { clearErrors, setValue } = useFormContext() @@ -66,7 +71,8 @@ export const RadioController: FC> = ({ {options.map((option, index) => ( > = ({ ))} {error && ( - + )}