diff --git a/.eslintrc.json b/.eslintrc.json index 4f495e599f14..f38cda91babc 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -311,6 +311,11 @@ { "selector": "enumMember", "format": ["camelCase", "UPPER_CASE", "PascalCase"] + }, + { + "selector": "import", + "leadingUnderscore": "allow", + "format": ["camelCase", "PascalCase"] } ], "@typescript-eslint/no-empty-interface": [ diff --git a/.github/actions/get-cache/action.yml b/.github/actions/get-cache/action.yml index a4f1184142d6..e6a561b20c58 100644 --- a/.github/actions/get-cache/action.yml +++ b/.github/actions/get-cache/action.yml @@ -34,8 +34,11 @@ runs: ENABLE_CACHE: ${{ inputs.enable-cache }} NODE_OPTIONS: --max-old-space-size=8192 run: | - echo $_CACHE_KEYS + # This is because of a weird bug where Github would write as root to .cache + export COREPACK_HOME=/github/home/.corepack-cache + export CYPRESS_CACHE_FOLDER=/github/home/.cypress-cache + echo "COREPACK_HOME=$COREPACK_HOME" >> $GITHUB_ENV + echo "CYPRESS_CACHE_FOLDER=$CYPRESS_CACHE_FOLDER" >> $GITHUB_ENV cd scripts/ci/cache - yarn install --immutable + npx yarn install --immutable node cache-action.mjs - echo $_CACHE_KEYS diff --git a/.github/workflows/config-values.yaml b/.github/workflows/config-values.yaml index 8383cef82c2c..e8ee060b7add 100644 --- a/.github/workflows/config-values.yaml +++ b/.github/workflows/config-values.yaml @@ -74,7 +74,7 @@ jobs: uses: ./.github/actions/cache with: path: infra/node_modules - key: ${{ runner.os }}-${{ hashFiles('infra/yarn.lock') }}-infra + key: ${{ runner.os }}-${{ hashFiles('infra/yarn.lock') }}-infra-2 - name: Check cache success run: '[[ "${{ steps.node-modules.outputs.success }}" != "false" ]] || exit 1' @@ -94,7 +94,7 @@ jobs: - name: Commit any changes to charts if: ${{ github.event_name == 'pull_request' }} run: | - yarn --cwd infra charts + (cd infra && yarn charts) ./infra/scripts/ci/git-check-dirty.sh "charts/" "charts" "dirtybot" check-secrets: diff --git a/.github/workflows/datadog-analysis.yml b/.github/workflows/datadog-analysis.yml new file mode 100644 index 000000000000..c02d93f7e3c0 --- /dev/null +++ b/.github/workflows/datadog-analysis.yml @@ -0,0 +1,44 @@ +name: Datadog Static Analysis +on: + push: + branches: + - 'main' + - 'release/**' + - 'pre-release/**' + paths-ignore: + - '**/*.md' + workflow_dispatch: + create: + pull_request: + types: + - opened + - synchronize + +jobs: + static-analysis: + runs-on: ec2-runners + container: + image: public.ecr.aws/m3u4c4h9/island-is/actions-runner-public:latest + name: Datadog Static Analyzer + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Check code meets quality and security standards + id: datadog-static-analysis + uses: DataDog/datadog-static-analyzer-github-action@v1 + with: + dd_api_key: ${{ secrets.DD_API_KEY }} + dd_app_key: ${{ secrets.DD_APP_KEY }} + dd_service: islandis + dd_env: ci + dd_site: datadoghq.eu + cpu_count: 2 + - name: Check imported libraries are secure and compliant + id: datadog-software-composition-analysis + uses: DataDog/datadog-sca-github-action@main + with: + dd_api_key: ${{ secrets.DD_API_KEY }} + dd_app_key: ${{ secrets.DD_APP_KEY }} + dd_service: islandis + dd_env: ci + dd_site: datadoghq.eu diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 9b8ba9183a8b..b2c2529b425b 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -259,7 +259,7 @@ jobs: echo "SHA='$SHA', retrived from action environment" fi echo "Using SHA='$SHA' as docker tag sha" - export DOCKER_TAG=${DOCKER_BRANCH_TAG}_${SHA:0:7}_${GITHUB_RUN_NUMBER} + export DOCKER_TAG=${DOCKER_BRANCH_TAG}_${SHA}_${GITHUB_RUN_NUMBER} echo "Docker tag will be ${DOCKER_TAG}" echo "DOCKER_TAG=${DOCKER_TAG}" >> $GITHUB_OUTPUT echo "DOCKER_TAG=$DOCKER_TAG" >> $GITHUB_ENV diff --git a/apps/judicial-system/api/src/app/modules/case/models/case.model.ts b/apps/judicial-system/api/src/app/modules/case/models/case.model.ts index 0a054c4dce2a..51ec5ec640dd 100644 --- a/apps/judicial-system/api/src/app/modules/case/models/case.model.ts +++ b/apps/judicial-system/api/src/app/modules/case/models/case.model.ts @@ -447,4 +447,10 @@ export class Case { @Field(() => String, { nullable: true }) readonly indictmentCompletedDate?: string + + @Field(() => Case, { nullable: true }) + readonly mergeCase?: Case + + @Field(() => [Case], { nullable: true }) + readonly mergedCases?: Case[] } 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 d6e4bb100b3a..cc761d4b363a 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 @@ -73,9 +73,6 @@ export class Defendant { @Field(() => DefenderChoice, { nullable: true }) readonly defenderChoice?: DefenderChoice - @Field(() => Boolean, { nullable: true }) - readonly acceptCompensationClaim?: boolean - @Field(() => SubpoenaType, { nullable: true }) readonly subpoenaType?: SubpoenaType } diff --git a/apps/judicial-system/backend/migrations/20240706153647-update-case.js b/apps/judicial-system/backend/migrations/20240706153647-update-case.js new file mode 100644 index 000000000000..c2cc153b3fe1 --- /dev/null +++ b/apps/judicial-system/backend/migrations/20240706153647-update-case.js @@ -0,0 +1,29 @@ +'use strict' + +module.exports = { + up: (queryInterface, Sequelize) => { + return queryInterface.sequelize.transaction((t) => + queryInterface.addColumn( + 'case', + 'merge_case_id', + { + type: Sequelize.UUID, + references: { + model: 'case', + key: 'id', + }, + allowNull: true, + }, + { transaction: t }, + ), + ) + }, + + down: (queryInterface) => { + return queryInterface.sequelize.transaction((t) => + queryInterface.removeColumn('case', 'merge_case_id', { + transaction: t, + }), + ) + }, +} diff --git a/apps/judicial-system/backend/migrations/20240710090032-update-defendant.js b/apps/judicial-system/backend/migrations/20240710090032-update-defendant.js new file mode 100644 index 000000000000..148dec7855ef --- /dev/null +++ b/apps/judicial-system/backend/migrations/20240710090032-update-defendant.js @@ -0,0 +1,22 @@ +module.exports = { + up: (queryInterface) => { + return queryInterface.sequelize.transaction((t) => + queryInterface.removeColumn('defendant', 'accept_compensation_claim', { + transaction: t, + }), + ) + }, + down: (queryInterface, Sequelize) => { + return queryInterface.sequelize.transaction((t) => + queryInterface.addColumn( + 'defendant', + 'accept_compensation_claim', + { + type: Sequelize.BOOLEAN, + allowNull: true, + }, + { transaction: t }, + ), + ) + }, +} diff --git a/apps/judicial-system/backend/src/app/formatters/courtRecord.spec.ts b/apps/judicial-system/backend/src/app/formatters/courtRecord.spec.ts index d85262d6caaa..6f7888d0f2f8 100644 --- a/apps/judicial-system/backend/src/app/formatters/courtRecord.spec.ts +++ b/apps/judicial-system/backend/src/app/formatters/courtRecord.spec.ts @@ -9,9 +9,8 @@ describe('formatCourtEndDate', () => { onError: jest.fn, }).formatMessage - function fn(startDate?: Date, endDate?: Date) { - return formatCourtEndDate(formatMessage, startDate, endDate) - } + const fn = (startDate?: Date, endDate?: Date) => + formatCourtEndDate(formatMessage, startDate, endDate) test('should return court still in progress', () => { const startDate = undefined diff --git a/apps/judicial-system/backend/src/app/modules/case/case.controller.ts b/apps/judicial-system/backend/src/app/modules/case/case.controller.ts index e73b47b71811..ab71f5d339ec 100644 --- a/apps/judicial-system/backend/src/app/modules/case/case.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/case/case.controller.ts @@ -84,7 +84,6 @@ import { courtOfAppealsRegistrarUpdateRule, districtCourtAssistantTransitionRule, districtCourtAssistantUpdateRule, - districtCourtJudgeSignRulingRule, districtCourtJudgeTransitionRule, districtCourtJudgeUpdateRule, districtCourtRegistrarTransitionRule, @@ -765,7 +764,7 @@ export class CaseController { new CaseTypeGuard([...restrictionCases, ...investigationCases]), CaseWriteGuard, ) - @RolesRules(districtCourtJudgeSignRulingRule) + @RolesRules(districtCourtJudgeRule) @Post('case/:caseId/ruling/signature') @ApiCreatedResponse({ type: SigningServiceResponse, @@ -778,6 +777,12 @@ export class CaseController { ): Promise { this.logger.debug(`Requesting a signature for the ruling of case ${caseId}`) + if (user.id !== theCase.judgeId) { + throw new ForbiddenException( + 'A ruling must be signed by the assigned judge', + ) + } + return this.caseService.requestRulingSignature(theCase).catch((error) => { if (error instanceof DokobitError) { throw new HttpException( @@ -802,7 +807,7 @@ export class CaseController { new CaseTypeGuard([...restrictionCases, ...investigationCases]), CaseWriteGuard, ) - @RolesRules(districtCourtJudgeSignRulingRule) + @RolesRules(districtCourtJudgeRule) @Get('case/:caseId/ruling/signature') @ApiOkResponse({ type: SignatureConfirmationResponse, @@ -817,6 +822,12 @@ export class CaseController { ): Promise { this.logger.debug(`Confirming a signature for the ruling of case ${caseId}`) + if (user.id !== theCase.judgeId) { + throw new ForbiddenException( + 'A ruling must be signed by the assigned judge', + ) + } + return this.caseService.getRulingSignatureConfirmation( theCase, user, diff --git a/apps/judicial-system/backend/src/app/modules/case/case.service.ts b/apps/judicial-system/backend/src/app/modules/case/case.service.ts index 95fe5ea86d23..f0aaccb3b42d 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 @@ -299,6 +299,8 @@ export const include: Includeable[] = [ where: { commentType: { [Op.in]: commentTypes } }, }, { model: Notification, as: 'notifications' }, + { model: Case, as: 'mergeCase' }, + { model: Case, as: 'mergedCases', separate: true }, ] export const order: OrderItem[] = [ diff --git a/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts b/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts index eed487be849c..5ffcac1c0125 100644 --- a/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts +++ b/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts @@ -656,12 +656,12 @@ export class InternalCaseService { theCase.judge?.nationalId === nationalId ? { name: theCase.judge?.name, - role: UserRole.DISTRICT_COURT_JUDGE, + role: theCase.judge?.role, } : theCase.registrar?.nationalId === nationalId ? { name: theCase.registrar?.name, - role: UserRole.DISTRICT_COURT_REGISTRAR, + role: theCase.registrar?.role, } : {} diff --git a/apps/judicial-system/backend/src/app/modules/case/models/case.model.ts b/apps/judicial-system/backend/src/app/modules/case/models/case.model.ts index 50901392b33a..96453bfa9e01 100644 --- a/apps/judicial-system/backend/src/app/modules/case/models/case.model.ts +++ b/apps/judicial-system/backend/src/app/modules/case/models/case.model.ts @@ -1045,4 +1045,26 @@ export class Case extends Model { }) @ApiPropertyOptional({ enum: CourtSessionType }) courtSessionType?: CourtSessionType + + /********** + * The surrogate key of the case an indictment was merged in to - only used if the has been merged + **********/ + @ForeignKey(() => Case) + @Column({ type: DataType.UUID, allowNull: true }) + @ApiPropertyOptional({ type: String }) + mergeCaseId?: string + + /********** + * The case this was merged in to - only used if the case was merged + **********/ + @BelongsTo(() => Case, 'mergeCaseId') + @ApiPropertyOptional({ type: () => Case }) + mergeCase?: Case + + // /********** + // * The cases that have been merged in to the current case - only used if the case was merged + // **********/ + @HasMany(() => Case, 'mergeCaseId') + @ApiPropertyOptional({ type: () => Case }) + mergedCases?: Case[] } diff --git a/apps/judicial-system/backend/src/app/modules/case/test/caseController/getRulingSignatureConfirmationRolesRules.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/caseController/getRulingSignatureConfirmationRolesRules.spec.ts index e41962b38178..a7899946e2e3 100644 --- a/apps/judicial-system/backend/src/app/modules/case/test/caseController/getRulingSignatureConfirmationRolesRules.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/test/caseController/getRulingSignatureConfirmationRolesRules.spec.ts @@ -1,6 +1,5 @@ +import { districtCourtJudgeRule } from '../../../../guards' import { CaseController } from '../../case.controller' -import { districtCourtJudgeSignRulingRule } from '../../guards/rolesRules' - describe('CaseController - Get ruling signature confirmation rules', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any let rules: any[] @@ -14,6 +13,6 @@ describe('CaseController - Get ruling signature confirmation rules', () => { it('should give permission to one roles', () => { expect(rules).toHaveLength(1) - expect(rules).toContain(districtCourtJudgeSignRulingRule) + expect(rules).toContain(districtCourtJudgeRule) }) }) diff --git a/apps/judicial-system/backend/src/app/modules/case/test/caseController/requestRulingSignatureRolesRules.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/caseController/requestRulingSignatureRolesRules.spec.ts index 81f956445bfd..400de1843470 100644 --- a/apps/judicial-system/backend/src/app/modules/case/test/caseController/requestRulingSignatureRolesRules.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/test/caseController/requestRulingSignatureRolesRules.spec.ts @@ -1,6 +1,5 @@ +import { districtCourtJudgeRule } from '../../../../guards' import { CaseController } from '../../case.controller' -import { districtCourtJudgeSignRulingRule } from '../../guards/rolesRules' - describe('CaseController - Request ruling signature rules', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any let rules: any[] @@ -14,6 +13,6 @@ describe('CaseController - Request ruling signature rules', () => { it('should give permission to one roles', () => { expect(rules).toHaveLength(1) - expect(rules).toContain(districtCourtJudgeSignRulingRule) + expect(rules).toContain(districtCourtJudgeRule) }) }) diff --git a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentAssignedRolesToCourt.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentAssignedRolesToCourt.spec.ts index aeba60fde5c3..9188a9c8b4a5 100644 --- a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentAssignedRolesToCourt.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentAssignedRolesToCourt.spec.ts @@ -30,7 +30,11 @@ describe('InternalCaseController - Deliver assigned roles for indictment case to type: CaseType.INDICTMENT, court: { name: courtName }, courtCaseNumber, - judge: { name: 'Test Dómari', nationalId: '0101010101' }, + judge: { + name: 'Test Dómari', + nationalId: '0101010101', + role: UserRole.DISTRICT_COURT_JUDGE, + }, registrar: { name: 'Test Ritari', nationalId: '0202020202' }, } as Case diff --git a/apps/judicial-system/backend/src/app/modules/defendant/dto/updateDefendant.dto.ts b/apps/judicial-system/backend/src/app/modules/defendant/dto/updateDefendant.dto.ts index 4db409aa7e19..a2e68e97a596 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/dto/updateDefendant.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/dto/updateDefendant.dto.ts @@ -89,11 +89,6 @@ export class UpdateDefendantDto { @ApiPropertyOptional({ type: Date }) readonly verdictViewDate?: Date - @IsOptional() - @IsBoolean() - @ApiPropertyOptional({ type: Boolean }) - readonly acceptCompensationClaim?: boolean - @IsOptional() @IsEnum(SubpoenaType) @ApiPropertyOptional({ enum: SubpoenaType }) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/internalDefendant.controller.ts b/apps/judicial-system/backend/src/app/modules/defendant/internalDefendant.controller.ts index 2272c3027e47..1fbefa6b9af2 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/internalDefendant.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/internalDefendant.controller.ts @@ -65,8 +65,7 @@ export class InternalDefendantController { @Patch('defense/:defendantNationalId') @ApiOkResponse({ type: Defendant, - description: - 'Assigns defense choice and compensation claim decision to defendant', + description: 'Assigns defense choice to defendant', }) async assignDefender( @Param('caseId') caseId: string, diff --git a/apps/judicial-system/backend/src/app/modules/defendant/models/defendant.model.ts b/apps/judicial-system/backend/src/app/modules/defendant/models/defendant.model.ts index 7d965782151a..093344f50641 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/models/defendant.model.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/models/defendant.model.ts @@ -124,10 +124,6 @@ export class Defendant extends Model { @ApiPropertyOptional({ type: Date }) verdictViewDate?: Date - @Column({ type: DataType.BOOLEAN, allowNull: true }) - @ApiPropertyOptional({ type: Boolean }) - acceptCompensationClaim?: boolean - @Column({ type: DataType.ENUM, allowNull: true, diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/dto/subpoena.dto.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/dto/subpoena.dto.ts index 6a1dfd44d60a..5da527ace61d 100644 --- a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/dto/subpoena.dto.ts +++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/dto/subpoena.dto.ts @@ -20,9 +20,4 @@ export class UpdateSubpoenaDto { @IsString() @ApiProperty({ type: String, required: false }) defenderNationalId?: string - - @IsOptional() - @IsBoolean() - @ApiProperty({ type: Boolean, required: false }) - acceptCompensationClaim?: boolean } diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/internal/internalCase.response.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/internal/internalCase.response.ts index f6a1fa53a693..0f310e71aa91 100644 --- a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/internal/internalCase.response.ts +++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/internal/internalCase.response.ts @@ -28,7 +28,6 @@ interface Defendant { defenderEmail?: string defenderPhoneNumber?: string defenderChoice?: DefenderChoice - acceptCompensationClaim?: boolean } interface DateLog { diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/subpoena.response.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/subpoena.response.ts index 7be08add3f7c..c6c022006edc 100644 --- a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/subpoena.response.ts +++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/subpoena.response.ts @@ -39,9 +39,6 @@ export class SubpoenaResponse { @ApiProperty({ type: () => DefenderInfo }) defenderInfo?: DefenderInfo - @ApiProperty({ type: () => Boolean }) - acceptCompensationClaim?: boolean - static fromInternalCaseResponse( internalCase: InternalCaseResponse, defendantNationalId: string, @@ -100,7 +97,6 @@ export class SubpoenaResponse { : undefined, } : undefined, - acceptCompensationClaim: defendantInfo?.acceptCompensationClaim, } } } diff --git a/apps/judicial-system/web/src/components/FormProvider/case.graphql b/apps/judicial-system/web/src/components/FormProvider/case.graphql index 48d6add3c279..4f0b041ca681 100644 --- a/apps/judicial-system/web/src/components/FormProvider/case.graphql +++ b/apps/judicial-system/web/src/components/FormProvider/case.graphql @@ -268,5 +268,11 @@ query Case($input: CaseQueryInput!) { indictmentReviewDecision indictmentCompletedDate courtSessionType + mergeCase { + id + } + mergedCases { + id + } } } diff --git a/apps/judicial-system/web/src/components/InfoCard/DefendantInfo/DefendantInfo.spec.ts b/apps/judicial-system/web/src/components/InfoCard/DefendantInfo/DefendantInfo.spec.ts new file mode 100644 index 000000000000..6b4c61c26478 --- /dev/null +++ b/apps/judicial-system/web/src/components/InfoCard/DefendantInfo/DefendantInfo.spec.ts @@ -0,0 +1,73 @@ +import { ServiceRequirement } from '@island.is/judicial-system-web/src/graphql/schema' + +import { getAppealExpirationInfo } from './DefendantInfo' + +describe('DefendantInfo', () => { + describe('getAdditionalDataSections', () => { + beforeAll(() => { + jest.useFakeTimers() + jest.setSystemTime(new Date('2024-07-08')) + }) + + afterAll(() => { + jest.useRealTimers() + }) + + test('should return the correct string if viewDate is not provided', () => { + const viewDate = undefined + const serviceRequirement = ServiceRequirement.NOT_REQUIRED + + const dataSections = getAppealExpirationInfo(viewDate, serviceRequirement) + + expect(dataSections.message.id).toStrictEqual( + 'judicial.system.core:info_card.defendant_info.appeal_date_not_begun', + ) + }) + + test('should return the correct string if serviceRequirement is NOT_REQUIRED', () => { + const viewDate = '2024-07-08' + const serviceRequirement = ServiceRequirement.NOT_REQUIRED + + const dataSections = getAppealExpirationInfo(viewDate, serviceRequirement) + + expect(dataSections.message.id).toStrictEqual( + 'judicial.system.core:info_card.defendant_info.service_requirement_not_required', + ) + }) + + test('should return the correct string if serviceRequirement is NOT_APPLICABLE', () => { + const viewDate = '2024-07-08' + const serviceRequirement = ServiceRequirement.NOT_APPLICABLE + + const dataSections = getAppealExpirationInfo(viewDate, serviceRequirement) + + expect(dataSections.message.id).toStrictEqual( + 'judicial.system.core:info_card.defendant_info.service_requirement_not_applicable', + ) + }) + + test('should return the correct string if appeal expiration date is in the future', () => { + const viewDate = '2024-07-08' + const serviceRequirement = ServiceRequirement.REQUIRED + + const dataSections = getAppealExpirationInfo(viewDate, serviceRequirement) + + expect(dataSections.message.id).toStrictEqual( + 'judicial.system.core:info_card.defendant_info.appeal_expiration_date', + ) + expect(dataSections.data).toStrictEqual('5.08.2024') + }) + + test('should return the correct string if appeal expiration date is in the past', () => { + const viewDate = '2024-06-09' + const serviceRequirement = ServiceRequirement.REQUIRED + + const dataSections = getAppealExpirationInfo(viewDate, serviceRequirement) + + expect(dataSections.message.id).toStrictEqual( + 'judicial.system.core:info_card.defendant_info.appeal_date_expired', + ) + expect(dataSections.data).toStrictEqual('7.07.2024') + }) + }) +}) diff --git a/apps/judicial-system/web/src/components/InfoCard/DefendantInfo/DefendantInfo.strings.ts b/apps/judicial-system/web/src/components/InfoCard/DefendantInfo/DefendantInfo.strings.ts index 7b2b9fbb4d4e..13619e64e891 100644 --- a/apps/judicial-system/web/src/components/InfoCard/DefendantInfo/DefendantInfo.strings.ts +++ b/apps/judicial-system/web/src/components/InfoCard/DefendantInfo/DefendantInfo.strings.ts @@ -18,6 +18,16 @@ export const strings = defineMessages({ description: 'Notaður til að láta vita að áfrýjunarfrestur dómfellda er ekki hafinn.', }, + serviceRequirementNotRequired: { + id: 'judicial.system.core:info_card.defendant_info.service_requirement_not_required', + defaultMessage: 'Dómfelldi var viðstaddur dómþing', + description: 'Notað til að láta vita birting dóms er ekki nauðsynleg.', + }, + serviceRequirementNotApplicable: { + id: 'judicial.system.core:info_card.defendant_info.service_requirement_not_applicable', + defaultMessage: 'Birting dóms ekki þörf', + description: 'Notað til að láta vita birting dóms er ekki nauðsynleg.', + }, defender: { id: 'judicial.system.core:info_card.defendant_info.defender', defaultMessage: 'Verjandi', diff --git a/apps/judicial-system/web/src/components/InfoCard/DefendantInfo/DefendantInfo.tsx b/apps/judicial-system/web/src/components/InfoCard/DefendantInfo/DefendantInfo.tsx index e9c5652abef9..a23ac79d4997 100644 --- a/apps/judicial-system/web/src/components/InfoCard/DefendantInfo/DefendantInfo.tsx +++ b/apps/judicial-system/web/src/components/InfoCard/DefendantInfo/DefendantInfo.tsx @@ -9,7 +9,10 @@ import { Text, } from '@island.is/island-ui/core' import { formatDate, formatDOB } from '@island.is/judicial-system/formatters' -import { Defendant } from '@island.is/judicial-system-web/src/graphql/schema' +import { + Defendant, + ServiceRequirement, +} from '@island.is/judicial-system-web/src/graphql/schema' import { strings } from './DefendantInfo.strings' import { link } from '../../MarkdownWrapper/MarkdownWrapper.css' @@ -30,6 +33,34 @@ interface DefendantInfoProps { displayVerdictViewDate?: boolean } +export const getAppealExpirationInfo = ( + viewDate?: string | null, + serviceRequirement?: ServiceRequirement | null, +) => { + if (!viewDate) { + return { message: strings.appealDateNotBegun, data: null } + } + + if (serviceRequirement === ServiceRequirement.NOT_REQUIRED) { + return { message: strings.serviceRequirementNotRequired, data: null } + } + + if (serviceRequirement === ServiceRequirement.NOT_APPLICABLE) { + return { message: strings.serviceRequirementNotApplicable, data: null } + } + + const today = new Date() + const expiryDate = new Date(viewDate) + expiryDate.setDate(expiryDate.getDate() + 28) + + const message = + today < expiryDate + ? strings.appealExpirationDate + : strings.appealDateExpired + + return { message, data: formatDate(expiryDate, 'P') } +} + export const DefendantInfo: FC = (props) => { const { defendant, @@ -40,23 +71,10 @@ export const DefendantInfo: FC = (props) => { } = props const { formatMessage } = useIntl() - const getAppealExpirationInfo = (viewDate?: string) => { - if (!viewDate) { - return formatMessage(strings.appealDateNotBegun) - } - - const today = new Date() - const expiryDate = new Date(viewDate) - expiryDate.setDate(expiryDate.getDate() + 28) - - const message = - today < expiryDate - ? strings.appealExpirationDate - : strings.appealDateExpired - return formatMessage(message, { - appealExpirationDate: formatDate(expiryDate, 'P'), - }) - } + const appealExpirationInfo = getAppealExpirationInfo( + defendant.verdictViewDate, + defendant.serviceRequirement, + ) return (
= (props) => { {displayAppealExpirationInfo && ( - {getAppealExpirationInfo(defendant.verdictViewDate ?? '')} + {formatMessage(appealExpirationInfo.message, { + appealExpirationDate: appealExpirationInfo.data, + })} )} diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Conclusion/Conclusion.strings.ts b/apps/judicial-system/web/src/routes/Court/Indictments/Conclusion/Conclusion.strings.ts index 49ff2c06756a..9c4a47021732 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Conclusion/Conclusion.strings.ts +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Conclusion/Conclusion.strings.ts @@ -93,6 +93,12 @@ export const strings = defineMessages({ description: 'Notaður sem texti í niðurfelling máls valmöguleika á Niðurstaða skrefi.', }, + merge: { + id: 'judicial.system.core:court.indictments.conclusion.merge', + defaultMessage: 'Sameinað öðru máli', + description: + 'Notaður sem texti í Sameinað valmöguleika á Niðurstaða skrefi.', + }, courtRecordTitle: { id: 'judicial.system.core:court.indictments.conclusion.court_record_title', defaultMessage: 'Þingbók', diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Conclusion/Conclusion.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Conclusion/Conclusion.tsx index 657689a9345c..6ef21ea0ca90 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Conclusion/Conclusion.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Conclusion/Conclusion.tsx @@ -234,6 +234,7 @@ const Conclusion: FC = () => { ) case CaseIndictmentRulingDecision.FINE: case CaseIndictmentRulingDecision.CANCELLATION: + case CaseIndictmentRulingDecision.MERGE: return uploadFiles.some( (file) => file.category === CaseFileCategory.COURT_RECORD && @@ -437,6 +438,21 @@ const Conclusion: FC = () => { label={formatMessage(strings.dismissal)} /> + + { + setSelectedDecision(CaseIndictmentRulingDecision.MERGE) + }} + large + backgroundColor="white" + label={formatMessage(strings.merge)} + /> + = ({ workingCase, setWorkingCase }) => { {workingCase.defendants && workingCase.defendants.length > 0 && ( - - - {`${formatMessage(m.defendantTitleV2)} `} - - * - - - +
= ({ workingCase, setWorkingCase }) => { )} - - - {formatMessage(m.prosecutorTitleV2)}{' '} - - * - - - +
{ + describe('isDefendantInfoActionButtonDisabled', () => { + test('should return true if defendant vervidictViewDate is not null', () => { + const verdictViewDate = '2024-07-08' + const serviceRequirement = ServiceRequirement.REQUIRED + + const res = isDefendantInfoActionButtonDisabled({ + id: 'id', + verdictViewDate, + serviceRequirement, + }) + + expect(res).toEqual(true) + }) + + test('should return true if defendant service requirement is NOT_APPLICABLE', () => { + const verdictViewDate = null + const serviceRequirement = ServiceRequirement.NOT_APPLICABLE + + const res = isDefendantInfoActionButtonDisabled({ + id: 'id', + verdictViewDate, + serviceRequirement, + }) + + expect(res).toEqual(true) + }) + + test('should return true if defendant service requirement is NOT_REQUIRED', () => { + const verdictViewDate = null + const serviceRequirement = ServiceRequirement.NOT_REQUIRED + + const res = isDefendantInfoActionButtonDisabled({ + id: 'id', + verdictViewDate, + serviceRequirement, + }) + + expect(res).toEqual(true) + }) + }) +}) 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 64fde6470844..0f1c9c6b1d23 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 @@ -25,7 +25,10 @@ import { UserContext, } from '@island.is/judicial-system-web/src/components' import { useProsecutorSelectionUsersQuery } from '@island.is/judicial-system-web/src/components/ProsecutorSelection/prosecutorSelectionUsers.generated' -import { Defendant } from '@island.is/judicial-system-web/src/graphql/schema' +import { + Defendant, + ServiceRequirement, +} from '@island.is/judicial-system-web/src/graphql/schema' import { formatDateForServer, useCase, @@ -35,6 +38,18 @@ import { import { strings } from './Overview.strings' type VisibleModal = 'REVIEWER_ASSIGNED' | 'DEFENDANT_VIEWS_VERDICT' +export const isDefendantInfoActionButtonDisabled = (defendant: Defendant) => { + switch (defendant.serviceRequirement) { + case ServiceRequirement.NOT_APPLICABLE: + case ServiceRequirement.NOT_REQUIRED: + return true + case ServiceRequirement.REQUIRED: + return defendant.verdictViewDate !== null + default: + return false + } +} + export const Overview = () => { const router = useRouter() const { formatMessage: fm } = useIntl() @@ -138,8 +153,7 @@ export const Overview = () => { setModalVisible('DEFENDANT_VIEWS_VERDICT') }, icon: 'mailOpen', - isDisabled: (defendant) => - defendant.verdictViewDate !== null, + isDisabled: isDefendantInfoActionButtonDisabled, } : undefined } diff --git a/apps/judicial-system/web/src/utils/hooks/useSections/index.ts b/apps/judicial-system/web/src/utils/hooks/useSections/index.ts index 1d45a4c36d37..70c1eea18a4d 100644 --- a/apps/judicial-system/web/src/utils/hooks/useSections/index.ts +++ b/apps/judicial-system/web/src/utils/hooks/useSections/index.ts @@ -398,7 +398,9 @@ const useSections = ( user?: User, ): RouteSection => { const { id, type, state } = workingCase - const caseHasBeenReceivedByCourt = state === CaseState.RECEIVED + const substepsShouldBeHidden = + state === CaseState.RECEIVED || + state === CaseState.WAITING_FOR_CANCELLATION const isTrafficViolation = isTrafficViolationCase(workingCase) return { @@ -409,7 +411,7 @@ const useSections = ( state !== CaseState.RECEIVED && !isCompletedCase(state), // Prosecutor can only view the overview when case has been received by court - children: caseHasBeenReceivedByCourt + children: substepsShouldBeHidden ? [] : [ { @@ -536,7 +538,7 @@ const useSections = ( ), ), href: `${constants.INDICTMENTS_CASE_FILES_ROUTE}/${id}`, - isActive: caseHasBeenReceivedByCourt + isActive: substepsShouldBeHidden ? false : isActive(constants.INDICTMENTS_CASE_FILES_ROUTE), onClick: diff --git a/apps/judicial-system/web/src/utils/hooks/useSections/useSections.spec.tsx b/apps/judicial-system/web/src/utils/hooks/useSections/useSections.spec.tsx index eeddc8ce4cdb..1d09bf1918dc 100644 --- a/apps/judicial-system/web/src/utils/hooks/useSections/useSections.spec.tsx +++ b/apps/judicial-system/web/src/utils/hooks/useSections/useSections.spec.tsx @@ -144,4 +144,22 @@ describe('useSections getSections', () => { { children: [], isActive: false, name: expect.any(String) }, ]) }) + + it('should return the correct sections for indictment cases in WAITING_FOR_CANCELLATION state', () => { + const { result } = renderHook(() => useSections(), { wrapper }) + const c: Case = { + type: CaseType.INDICTMENT, + created: faker.date.past().toISOString(), + modified: faker.date.past().toISOString(), + id: faker.datatype.uuid(), + state: CaseState.WAITING_FOR_CANCELLATION, + policeCaseNumbers: [], + } + + expect(result.current.getSections(c, u)).toStrictEqual([ + { children: [], isActive: true, name: expect.any(String) }, + { children: [], isActive: false, name: expect.any(String) }, + { children: [], isActive: false, name: expect.any(String) }, + ]) + }) }) diff --git a/apps/native/app/src/messages/en.ts b/apps/native/app/src/messages/en.ts index 088a4249a8be..5dfabc036a6e 100644 --- a/apps/native/app/src/messages/en.ts +++ b/apps/native/app/src/messages/en.ts @@ -77,12 +77,16 @@ export const en: TranslatedMessages = { 'settings.usersettings.email': 'Email', 'settings.usersettings.bankinfo': 'Bank info', 'settings.communication.groupTitle': 'Notifications and communication', - 'settings.communication.newDocumentsNotifications': - 'Get notifications of new documents', - 'settings.communication.appUpdatesNotifications': - 'Get notifications about app updates', - 'settings.communication.applicationsNotifications': - 'Get notifications about application status updates', + 'settings.communication.newNotificationsEmailLabel': 'Notifications in email', + 'settings.communication.newNotificationsEmailDescription': + 'Government agencies can send you notifications in email.', + 'settings.communication.newNotificationsErrorTitle': 'Error', + 'settings.communication.newNotificationsErrorDescription': + 'Failed to update settings', + 'settings.communication.newNotificationsInAppLabel': + 'Notifications in the Island.is app', + 'settings.communication.newNotificationsInAppDescription': + 'Government agencies can send you notifications in the Island.is app.', 'settings.security.privacyTitle': 'Privacy Policy', 'settings.security.privacySubTitle': 'Digital Iceland', 'settings.security.groupTitle': 'Security and privacy', @@ -113,6 +117,15 @@ export const en: TranslatedMessages = { 'settings.about.codePushLabel': 'Updates', 'settings.about.codePushLoading': 'Loading...', 'settings.about.codePushUpToDate': 'The app is up to date', + 'settings.about.codePushUpToDateTitle': 'Up to date', + 'settings.about.codePushUpdateCancelledTitle': 'Update cancelled', + 'settings.about.codePushUpdateCancelledDescription': + 'The update was cancelled', + 'settings.about.codePushUpdateInstalledTitle': 'Update installed', + 'settings.about.codePushUpdateInstalledDescription': + 'The app has been updated', + 'settings.about.codePushUpdateErrorTitle': 'Unknown error', + 'settings.about.codePushUpdateErrorDescription': 'An unknown error occurred', // user: personal info 'user.natreg.infoBox': 'Your registration at Registers Iceland', diff --git a/apps/native/app/src/messages/is.ts b/apps/native/app/src/messages/is.ts index 4db3ed1677be..5deb9f43a606 100644 --- a/apps/native/app/src/messages/is.ts +++ b/apps/native/app/src/messages/is.ts @@ -76,12 +76,17 @@ export const is = { 'settings.usersettings.email': 'Netfang', 'settings.usersettings.bankinfo': 'Reikningsnúmer', 'settings.communication.groupTitle': 'Tilkynningar og samskipti', - 'settings.communication.newDocumentsNotifications': - 'Fá tilkynningar um ný skjöl', - 'settings.communication.appUpdatesNotifications': - 'Fá tilkynningar um nýjungar í appinu', - 'settings.communication.applicationsNotifications': - 'Fá tilkynningar um stöðu umsókna', + 'settings.communication.newNotificationsEmailLabel': + 'Tilkynningar í tölvupósti', + 'settings.communication.newNotificationsEmailDescription': + 'Stofnanir geta sent þér tilkynningu með tölvupósti.', + 'settings.communication.newNotificationsErrorTitle': 'Villa', + 'settings.communication.newNotificationsErrorDescription': + 'Ekki tókst að uppfæra stillingar', + 'settings.communication.newNotificationsInAppLabel': + 'Tilkynningar í Ísland.is appinu', + 'settings.communication.newNotificationsInAppDescription': + 'Stofnanir geta sent þér tilkynningu í Ísland.is appið.', 'settings.security.privacyTitle': 'Persónuverndarstefna', 'settings.security.privacySubTitle': 'Stafrænt Íslands', 'settings.security.groupTitle': 'Öryggi og persónuvernd', @@ -111,6 +116,16 @@ export const is = { 'settings.about.codePushLabel': 'Uppfærsla', 'settings.about.codePushLoading': 'Hleð...', 'settings.about.codePushUpToDate': 'Appið er uppfært', + 'settings.about.codePushUpToDateTitle': 'Uppfært', + 'settings.about.codePushUpdateCancelledTitle': 'Hætt við uppfærslu', + 'settings.about.codePushUpdateCancelledDescription': + 'Það var hætt við uppfærslu', + 'settings.about.codePushUpdateInstalledTitle': 'Uppfærsla tókst', + 'settings.about.codePushUpdateInstalledDescription': + 'Appið hefur verið uppfært', + 'settings.about.codePushUpdateErrorTitle': 'Villa', + 'settings.about.codePushUpdateErrorDescription': + 'Villa kom upp við uppfærslu', // user: personal info 'user.natreg.infoBox': 'Þín skráning í Þjóðskrá Íslands', @@ -261,7 +276,7 @@ export const is = { 'vehicle.mileage.inputLabel': 'Skrá kílómetrastöðu', 'vehicle.mileage.inputSubmitButton': 'Skrá', 'vehicle.mileage.registerIntervalCopy': - 'Aðeins má skrá kílómetrastöðu einu sinn á hverjum 30 dögum', + 'Aðeins má skrá kílómetrastöðu einu sinni á hverjum 30 dögum', 'vehicle.mileage.youAreNotAllowedCopy': 'Eingöngu aðaleigandi eða umráðamaður yfir bifreið lánastofnunnar geta skráð kílómetrastöðu', 'vehicle.mileage.moreInformationCopy': 'Sjá nánari upplýsingar á Ísland.is', diff --git a/apps/native/app/src/screens/applications/applications.tsx b/apps/native/app/src/screens/applications/applications.tsx index c55829b09dd1..59f9517fa50d 100644 --- a/apps/native/app/src/screens/applications/applications.tsx +++ b/apps/native/app/src/screens/applications/applications.tsx @@ -203,6 +203,7 @@ export const ApplicationsScreen: NavigationFunctionComponent = ({ loading={applicationsRes.loading} componentId={componentId} hideAction={true} + hideSeeAllButton={true} /> diff --git a/apps/native/app/src/screens/home/applications-module.tsx b/apps/native/app/src/screens/home/applications-module.tsx index d4fdc0f73e2f..fe39aaf0e9c4 100644 --- a/apps/native/app/src/screens/home/applications-module.tsx +++ b/apps/native/app/src/screens/home/applications-module.tsx @@ -24,6 +24,7 @@ interface ApplicationsModuleProps { loading: boolean componentId: string hideAction?: boolean + hideSeeAllButton?: boolean } export const ApplicationsModule = React.memo( @@ -32,6 +33,7 @@ export const ApplicationsModule = React.memo( loading, componentId, hideAction, + hideSeeAllButton = false, }: ApplicationsModuleProps) => { const intl = useIntl() const count = applications.length @@ -72,18 +74,12 @@ export const ApplicationsModule = React.memo( /> )) - // The RN types are not up-to-date with these props which seem to have been added in RN 71. - const imageProps = { - height: 90, - width: 42, - } - return ( navigateTo(`/applications`)}> navigateTo('/applications')} style={{ @@ -115,7 +111,7 @@ export const ApplicationsModule = React.memo( } link={ diff --git a/apps/native/app/src/screens/more/more.tsx b/apps/native/app/src/screens/more/more.tsx index 181a9a70ea27..daae40fda679 100644 --- a/apps/native/app/src/screens/more/more.tsx +++ b/apps/native/app/src/screens/more/more.tsx @@ -1,9 +1,14 @@ -import { ListButton, UserCard } from '@ui' +import { FamilyMemberCard, ListButton } from '@ui' import React from 'react' import { useIntl } from 'react-intl' -import { Image, SafeAreaView, ScrollView } from 'react-native' +import { + Image, + SafeAreaView, + ScrollView, + TouchableHighlight, +} from 'react-native' import { NavigationFunctionComponent } from 'react-native-navigation' -import styled from 'styled-components/native' +import styled, { useTheme } from 'styled-components/native' import assetsIcon from '../../assets/icons/assets.png' import familyIcon from '../../assets/icons/family.png' import financeIcon from '../../assets/icons/finance.png' @@ -67,6 +72,7 @@ const { useNavigationOptions, getNavigationOptions } = export const MoreScreen: NavigationFunctionComponent = ({ componentId }) => { const authStore = useAuthStore() const intl = useIntl() + const theme = useTheme() const showFinances = useFeatureFlag('isFinancesEnabled', false) const showAirDiscount = useFeatureFlag('isAirDiscountEnabled', false) @@ -83,16 +89,19 @@ export const MoreScreen: NavigationFunctionComponent = ({ componentId }) => { }} > - navigateTo(`/personalinfo`), - }, - ]} - /> + { + navigateTo('/personalinfo') + }} + > + + { return RNAlert.alert( intl.formatMessage({ id: 'settings.security.removePasskeyPromptTitle' }), @@ -182,7 +186,47 @@ export const SettingsScreen: NavigationFunctionComponent = ({ }, }) .catch((err) => { - RNAlert.alert('Villa', err.message) + console.error(err) + RNAlert.alert( + intl.formatMessage({ + id: 'settings.communication.newNotificationsErrorTitle', + }), + intl.formatMessage({ + id: 'settings.communication.newNotificationsErrorDescription', + }), + ) + }) + } + + function updateEmailNotifications(value: boolean) { + client + .mutate({ + mutation: UpdateProfileDocument, + update(cache, { data }) { + cache.modify({ + fields: { + getUserProfile: (existing) => { + return { ...existing, ...data?.updateProfile } + }, + }, + }) + }, + variables: { + input: { + canNudge: value, + }, + }, + }) + .catch((err) => { + console.error(err) + RNAlert.alert( + intl.formatMessage({ + id: 'settings.communication.newNotificationsErrorTitle', + }), + intl.formatMessage({ + id: 'settings.communication.newNotificationsErrorDescription', + }), + ) }) } @@ -191,6 +235,7 @@ export const SettingsScreen: NavigationFunctionComponent = ({ setDocumentNotifications( userProfile.data?.getUserProfile?.documentNotifications, ) + setEmailNotifications(!!userProfile.data?.getUserProfile?.canNudge) } }, [userProfile]) @@ -296,7 +341,33 @@ export const SettingsScreen: NavigationFunctionComponent = ({ > { + updateEmailNotifications(value) + setEmailNotifications(value) + }} + disabled={userProfile.loading && !userProfile.data} + value={emailNotifications} + thumbColor={Platform.select({ android: theme.color.dark100 })} + trackColor={{ + false: theme.color.dark200, + true: theme.color.blue400, + }} + /> + } + /> + } /> - {/* } - /> - - } - /> */} diff --git a/apps/native/app/src/screens/vehicles/vehicle-mileage.screen.tsx b/apps/native/app/src/screens/vehicles/vehicle-mileage.screen.tsx index 68737de92072..aea157b4b206 100644 --- a/apps/native/app/src/screens/vehicles/vehicle-mileage.screen.tsx +++ b/apps/native/app/src/screens/vehicles/vehicle-mileage.screen.tsx @@ -75,9 +75,10 @@ export const VehicleMileageScreen: NavigationFunctionComponent<{ }, }, }) - const [postMileage] = usePostVehicleMileageMutation({ - refetchQueries: [GetVehicleMileageDocument, GetVehicleDocument], - }) + const [postMileage, { loading: loadingSubmit }] = + usePostVehicleMileageMutation({ + refetchQueries: [GetVehicleMileageDocument, GetVehicleDocument], + }) const [updateMileage] = useUpdateVehicleMileageMutation({ refetchQueries: [GetVehicleMileageDocument, GetVehicleDocument], }) @@ -317,7 +318,7 @@ export const VehicleMileageScreen: NavigationFunctionComponent<{ id: 'vehicle.mileage.inputSubmitButton', })} onPress={onSubmit} - disabled={!canRegisterMileage} + disabled={!canRegisterMileage || loadingSubmit} /> diff --git a/apps/native/app/src/screens/vehicles/vehicles.tsx b/apps/native/app/src/screens/vehicles/vehicles.tsx index f7a208c22e58..c445b50f5d54 100644 --- a/apps/native/app/src/screens/vehicles/vehicles.tsx +++ b/apps/native/app/src/screens/vehicles/vehicles.tsx @@ -20,7 +20,6 @@ import { } from '../../graphql/types/schema' import { createNavigationOptionHooks } from '../../hooks/create-navigation-option-hooks' import { useConnectivityIndicator } from '../../hooks/use-connectivity-indicator' -import { getRightButtons } from '../../utils/get-main-root' import { testIDs } from '../../utils/test-ids' import { VehicleItem } from './components/vehicle-item' diff --git a/apps/native/app/src/ui/index.ts b/apps/native/app/src/ui/index.ts index f42d11f5f202..5f6e09d8eb2e 100644 --- a/apps/native/app/src/ui/index.ts +++ b/apps/native/app/src/ui/index.ts @@ -19,7 +19,6 @@ export * from './lib/card/finance-status-card' export * from './lib/card/status-card' export * from './lib/card/welcome-card' export * from './lib/card/link-card' -export * from './lib/card/user-card' export * from './lib/detail/content' export * from './lib/detail/header' export * from './lib/empty-state/empty-card' diff --git a/apps/native/app/src/ui/lib/card/card.stories.tsx b/apps/native/app/src/ui/lib/card/card.stories.tsx index 32c0f6e86602..212c78ec751e 100644 --- a/apps/native/app/src/ui/lib/card/card.stories.tsx +++ b/apps/native/app/src/ui/lib/card/card.stories.tsx @@ -16,7 +16,6 @@ import { FamilyMemberCard } from './family-member-card' import { LicenseCard } from './license-card' import { NotificationCard } from './notification-card' import { StatusCard } from './status-card' -import { UserCard } from './user-card' import { VehicleCard } from './vehicle-card' import { WelcomeCard } from './welcome-card' @@ -115,20 +114,6 @@ storiesOf('Cards', module) ) }) - .add('User Card', () => { - const userName = text('User name', 'Jón Jónsson') - const ssn = text('Ssn number', '010125-4529') - const actionTitle = text('Action Title', 'Skoða upplýsingar') - return ( - console.log('Action press') }, - ]} - /> - ) - }) .add('Notification Card With Actions', () => { const title = text('Notification Card Title', 'Ökuskýrteini') const message = text( diff --git a/apps/native/app/src/ui/lib/card/family-member-card.tsx b/apps/native/app/src/ui/lib/card/family-member-card.tsx index 4ffc66cd879f..8163abf80470 100644 --- a/apps/native/app/src/ui/lib/card/family-member-card.tsx +++ b/apps/native/app/src/ui/lib/card/family-member-card.tsx @@ -3,14 +3,13 @@ import { Image } from 'react-native' import styled from 'styled-components/native' import chevronForward from '../../assets/icons/chevron-forward.png' import { dynamicColor } from '../../utils' -import { font } from '../../utils/font' import { Avatar } from '../avatar/avatar' +import { Typography } from '../typography/typography' const Host = styled.View` display: flex; flex-direction: row; - padding: ${({ theme }) => theme.spacing[3]}px; - padding-right: ${({ theme }) => theme.spacing[1]}px; + padding: ${({ theme }) => theme.spacing[2]}px; border-radius: ${({ theme }) => theme.border.radius.large}; border-width: ${({ theme }) => theme.border.width.standard}px; border-color: ${dynamicColor( @@ -29,28 +28,16 @@ const Content = styled.View` ` const ImageWrap = styled.View` - margin-right: ${({ theme }) => theme.spacing[3]}px; + margin-right: ${({ theme }) => theme.spacing[2]}px; ` -const Title = styled.Text` +const Title = styled(Typography)` padding-right: ${({ theme }) => theme.spacing[1]}px; - margin-bottom: ${({ theme }) => theme.spacing[1]}px; - - ${font({ - fontWeight: '600', - lineHeight: 24, - fontSize: 18, - })} + margin-bottom: ${({ theme }) => theme.spacing.smallGutter}px; ` -const Text = styled.Text` +const Text = styled(Typography)` padding-right: ${({ theme }) => theme.spacing[2]}px; - - ${font({ - fontWeight: '300', - lineHeight: 24, - fontSize: 16, - })} ` const Icon = styled.View` @@ -65,11 +52,13 @@ interface FamilyMemberCardProps { export function FamilyMemberCard({ name, nationalId }: FamilyMemberCardProps) { return ( - - - + {name.length ? ( + + + + ) : null} - {name} + {name} {nationalId} diff --git a/apps/native/app/src/ui/lib/card/user-card.tsx b/apps/native/app/src/ui/lib/card/user-card.tsx deleted file mode 100644 index 28ae54a8b383..000000000000 --- a/apps/native/app/src/ui/lib/card/user-card.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import React from 'react' -import styled from 'styled-components/native' -import { dynamicColor } from '../../utils/dynamic-color' -import { font } from '../../utils/font' -import { Avatar } from '../avatar/avatar' - -const Host = styled.View` - width: 100%; - border-radius: ${({ theme }) => theme.border.radius.large}; - border-width: ${({ theme }) => theme.border.width.standard}px; - border-color: ${dynamicColor( - ({ theme }) => ({ - dark: theme.shades.dark.shade300, - light: theme.color.blue200, - }), - true, - )}; - margin-bottom: ${({ theme }) => theme.spacing[2]}px; -` - -const Content = styled.View` - padding: ${({ theme }) => theme.spacing[3]}px; - justify-content: center; - align-items: center; -` - -const Name = styled.Text` - margin-top: ${({ theme }) => theme.spacing[2]}px; - margin-bottom: ${({ theme }) => theme.spacing[1]}px; - - ${font({ - fontWeight: '600', - fontSize: 20, - lineHeight: 26, - })} -` - -const Ssn = styled.Text` - ${font({ - fontWeight: '300', - lineHeight: 24, - })} -` - -const ActionsContainer = styled.View` - border-top-width: ${({ theme }) => theme.border.width.standard}px; - border-top-color: ${dynamicColor( - (props) => ({ - light: props.theme.color.blue200, - dark: 'shade300', - }), - true, - )}; - flex-direction: row; -` - -const ActionButton = styled.TouchableOpacity<{ border: boolean }>` - flex: 1; - align-items: center; - justify-content: center; - padding: ${({ theme }) => theme.spacing[2]}px; - border-left-width: ${({ theme }) => theme.border.width.standard}px; - border-left-color: ${dynamicColor( - ({ theme, border }) => (!border ? 'transparent' : theme.color.blue200), - true, - )}; -` - -const ActionText = styled.Text` - ${font({ - fontWeight: '600', - color: ({ theme }) => theme.color.blue400, - })} - text-align: center; -` - -interface UserCardProps { - name?: string - ssn?: string - actions: Array<{ text: string; onPress(): void }> -} - -export function UserCard({ name, ssn, actions = [] }: UserCardProps) { - return ( - - - {name && } - {name} - {ssn} - - {actions.length ? ( - - {actions.map(({ text, onPress }, i) => ( - - {text} - - ))} - - ) : null} - - ) -} diff --git a/apps/native/app/src/ui/lib/card/vehicle-card.tsx b/apps/native/app/src/ui/lib/card/vehicle-card.tsx index 391fd831129e..6687b6e2eb20 100644 --- a/apps/native/app/src/ui/lib/card/vehicle-card.tsx +++ b/apps/native/app/src/ui/lib/card/vehicle-card.tsx @@ -3,7 +3,7 @@ import { Image } from 'react-native' import styled from 'styled-components/native' import chevronForward from '../../assets/icons/chevron-forward.png' import { dynamicColor } from '../../utils' -import { font } from '../../utils/font' +import { Typography } from '../typography/typography' const Host = styled.View` display: flex; @@ -28,32 +28,23 @@ const Content = styled.View` align-items: flex-start; ` -const Title = styled.Text` +const Title = styled(Typography)` padding-right: ${({ theme }) => theme.spacing[1]}px; margin-bottom: ${({ theme }) => theme.spacing[1]}px; - - ${font({ - fontWeight: '600', - lineHeight: 24, - fontSize: 18, - })} ` -const Text = styled.Text` +const Text = styled(Typography)` padding-right: ${({ theme }) => theme.spacing[2]}px; - margin-bottom: ${({ theme }) => theme.spacing[2]}px; - - ${font({ - fontWeight: '300', - lineHeight: 24, - fontSize: 16, - })} ` const Icon = styled.View` margin-left: auto; ` +const Label = styled.View` + margin-top: ${({ theme }) => theme.spacing[2]}px; +` + interface VehicleCardProps { title?: string | null color?: string | null @@ -65,11 +56,11 @@ export function VehicleCard({ title, color, number, label }: VehicleCardProps) { return ( - {title} + {title} {color} - {number} - {label} + {label && } diff --git a/apps/native/app/src/utils/get-main-root.ts b/apps/native/app/src/utils/get-main-root.ts index 1b4506a69a54..f3cc3d083080 100644 --- a/apps/native/app/src/utils/get-main-root.ts +++ b/apps/native/app/src/utils/get-main-root.ts @@ -16,7 +16,7 @@ export const getRightButtons = ({ } = {}): OptionsTopBarButton[] => { const iconBackground = { color: 'transparent', - cornerRadius: 8, + cornerRadius: 4, width: theme.spacing[4], height: theme.spacing[4], } diff --git a/apps/service-portal/src/components/DocumentsEmpty/DocumentsEmpty.tsx b/apps/service-portal/src/components/DocumentsEmpty/DocumentsEmpty.tsx index 516d77d1db4b..545cfa65235c 100644 --- a/apps/service-portal/src/components/DocumentsEmpty/DocumentsEmpty.tsx +++ b/apps/service-portal/src/components/DocumentsEmpty/DocumentsEmpty.tsx @@ -38,7 +38,7 @@ export const DocumentsEmpty = ({ hasDelegationAccess }: Props) => { ? './assets/images/nodata.svg' : './assets/images/jobsGrid.svg' } - alt="No access" + alt="" className={styles.img} /> } diff --git a/apps/services/endorsements/api/src/app/modules/endorsementList/endorsementList.model.ts b/apps/services/endorsements/api/src/app/modules/endorsementList/endorsementList.model.ts index e91bf7dbeb1a..b67a6c26c42d 100644 --- a/apps/services/endorsements/api/src/app/modules/endorsementList/endorsementList.model.ts +++ b/apps/services/endorsements/api/src/app/modules/endorsementList/endorsementList.model.ts @@ -96,6 +96,9 @@ export class EndorsementList extends Model { @HasMany(() => Endorsement) endorsements?: Endorsement[] + @ApiProperty() + endorsementCounter?: number + @ApiProperty() @Column({ type: DataType.JSONB, diff --git a/apps/services/endorsements/api/src/app/modules/endorsementList/endorsementList.service.ts b/apps/services/endorsements/api/src/app/modules/endorsementList/endorsementList.service.ts index 8cef2a0cc416..d5696e1155cb 100644 --- a/apps/services/endorsements/api/src/app/modules/endorsementList/endorsementList.service.ts +++ b/apps/services/endorsements/api/src/app/modules/endorsementList/endorsementList.service.ts @@ -5,7 +5,7 @@ import { BadRequestException, } from '@nestjs/common' import { InjectModel } from '@nestjs/sequelize' -import { Op } from 'sequelize' +import { Op, Sequelize } from 'sequelize' import type { Logger } from '@island.is/logging' import { LOGGER_PROVIDER } from '@island.is/logging' import { EndorsementList } from './endorsementList.model' @@ -76,8 +76,28 @@ export class EndorsementListService { after: query.after, before: query.before, primaryKeyField: 'counter', - orderOption: [['counter', 'DESC']], + orderOption: [ + ['endorsementCounter', 'DESC'], + ['counter', 'DESC'], + ], where: where, + attributes: { + include: [ + [ + Sequelize.fn('COUNT', Sequelize.col('endorsements.id')), + 'endorsementCounter', + ], + ], + }, + include: [ + { + model: Endorsement, + required: false, // Required false for left outer join so that counts come for 0 as well + duplicating: false, + attributes: [], + }, + ], + group: ['EndorsementList.id'], }) } diff --git a/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/notificationsWorker.service.spec.ts b/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/notificationsWorker.service.spec.ts index 5dcb6a88bc3f..aaaf8aec510b 100644 --- a/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/notificationsWorker.service.spec.ts +++ b/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/notificationsWorker.service.spec.ts @@ -167,7 +167,7 @@ describe('NotificationsWorkerService', () => { template: expect.objectContaining({ body: expect.arrayContaining([ expect.objectContaining({ - component: 'Button', + component: 'ImageWithLink', context: expect.objectContaining({ href: 'https://island.is/minarsidur/postholf', }), @@ -189,7 +189,7 @@ describe('NotificationsWorkerService', () => { template: expect.objectContaining({ body: expect.arrayContaining([ expect.objectContaining({ - component: 'Button', + component: 'ImageWithLink', context: expect.objectContaining({ href: 'https://island.is/minarsidur/postholf', }), @@ -250,7 +250,7 @@ describe('NotificationsWorkerService', () => { template: expect.objectContaining({ body: expect.arrayContaining([ expect.objectContaining({ - component: 'Button', + component: 'ImageWithLink', context: expect.objectContaining({ href: 'https://island.is/minarsidur/postholf', }), @@ -272,7 +272,7 @@ describe('NotificationsWorkerService', () => { template: expect.objectContaining({ body: expect.arrayContaining([ expect.objectContaining({ - component: 'Button', + component: 'ImageWithLink', context: expect.objectContaining({ href: `https://island.is/minarsidur/login?login_hint=${delegationSubjectId}&target_link_uri=https://island.is/minarsidur/postholf`, }), @@ -307,7 +307,7 @@ describe('NotificationsWorkerService', () => { template: expect.objectContaining({ body: expect.arrayContaining([ expect.objectContaining({ - component: 'Button', + component: 'ImageWithLink', context: expect.objectContaining({ href: notServicePortalUrl, }), @@ -329,7 +329,7 @@ describe('NotificationsWorkerService', () => { template: expect.objectContaining({ body: expect.arrayContaining([ expect.objectContaining({ - component: 'Button', + component: 'ImageWithLink', context: expect.objectContaining({ href: notServicePortalUrl, }), diff --git a/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/notificationsWorker.service.ts b/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/notificationsWorker.service.ts index fbf514a4ba2b..f29420bfbbc4 100644 --- a/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/notificationsWorker.service.ts +++ b/apps/services/user-notification/src/app/modules/notifications/notificationsWorker/notificationsWorker.service.ts @@ -143,7 +143,7 @@ export class NotificationsWorkerService implements OnApplicationBootstrap { { component: 'Image', context: { - src: join(__dirname, `./assets/images/logo.jpg`), + src: join(__dirname, `./assets/images/island-2x-logo.png`), alt: 'Ísland.is logo', }, }, @@ -171,9 +171,15 @@ export class NotificationsWorkerService implements OnApplicationBootstrap { ...(formattedTemplate.clickActionUrl ? [ { - component: 'Button', + component: 'ImageWithLink', context: { - copy: `${isEnglish ? 'View on' : 'Skoða á'} island.is`, + src: join( + __dirname, + `./assets/images/${ + isEnglish ? 'en' : 'is' + }-button-open.png`, + ), + alt: isEnglish ? 'Open mailbox' : 'Opna Pósthólf', href: this.getClickActionUrl(formattedTemplate, subjectId), }, }, diff --git a/apps/services/user-notification/src/assets/images/en-button-open.png b/apps/services/user-notification/src/assets/images/en-button-open.png new file mode 100644 index 000000000000..ab0144dc9ec9 Binary files /dev/null and b/apps/services/user-notification/src/assets/images/en-button-open.png differ diff --git a/apps/services/user-notification/src/assets/images/is-button-open.png b/apps/services/user-notification/src/assets/images/is-button-open.png new file mode 100644 index 000000000000..5d2a07e9db64 Binary files /dev/null and b/apps/services/user-notification/src/assets/images/is-button-open.png differ diff --git a/apps/services/user-notification/src/assets/images/island-2x-logo.png b/apps/services/user-notification/src/assets/images/island-2x-logo.png new file mode 100644 index 000000000000..d6ad022534e1 Binary files /dev/null and b/apps/services/user-notification/src/assets/images/island-2x-logo.png differ diff --git a/apps/web/components/OfficialJournalOfIceland/OJOIHomeIntro.tsx b/apps/web/components/OfficialJournalOfIceland/OJOIHomeIntro.tsx index d4978dd4cbcb..2a1ac7bdffb4 100644 --- a/apps/web/components/OfficialJournalOfIceland/OJOIHomeIntro.tsx +++ b/apps/web/components/OfficialJournalOfIceland/OJOIHomeIntro.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from 'react' +import { ReactNode } from 'react' import { Box, diff --git a/apps/web/components/OfficialJournalOfIceland/OJOISearchGridView.tsx b/apps/web/components/OfficialJournalOfIceland/OJOISearchGridView.tsx index f4c669f98144..b926848235ba 100644 --- a/apps/web/components/OfficialJournalOfIceland/OJOISearchGridView.tsx +++ b/apps/web/components/OfficialJournalOfIceland/OJOISearchGridView.tsx @@ -1,6 +1,5 @@ -import { Locale } from '@island.is/shared/types' - import { Stack } from '@island.is/island-ui/core' +import { Locale } from '@island.is/shared/types' import { OfficialJournalOfIcelandAdvertsResponse } from '@island.is/web/graphql/schema' import { useLinkResolver } from '@island.is/web/hooks' diff --git a/apps/web/components/OfficialJournalOfIceland/OJOISearchListView.tsx b/apps/web/components/OfficialJournalOfIceland/OJOISearchListView.tsx index 958503d1b716..8cbcf8420a1e 100644 --- a/apps/web/components/OfficialJournalOfIceland/OJOISearchListView.tsx +++ b/apps/web/components/OfficialJournalOfIceland/OJOISearchListView.tsx @@ -1,8 +1,8 @@ import format from 'date-fns/format' import is from 'date-fns/locale/is' -import { Locale } from '@island.is/shared/types' import { LinkV2, Table as T, Text } from '@island.is/island-ui/core' +import { Locale } from '@island.is/shared/types' import { OfficialJournalOfIcelandAdvertsResponse } from '@island.is/web/graphql/schema' import { useLinkResolver } from '@island.is/web/hooks' diff --git a/apps/web/components/OfficialJournalOfIceland/OJOIWrapper.tsx b/apps/web/components/OfficialJournalOfIceland/OJOIWrapper.tsx index 4d72479367c4..3c4a0918b500 100644 --- a/apps/web/components/OfficialJournalOfIceland/OJOIWrapper.tsx +++ b/apps/web/components/OfficialJournalOfIceland/OJOIWrapper.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode, useEffect, useState } from 'react' +import { ReactNode, useEffect, useState } from 'react' import { useWindowSize } from 'react-use' import NextLink from 'next/link' @@ -12,7 +12,7 @@ import { } from '@island.is/island-ui/core' import { theme } from '@island.is/island-ui/theme' import { Footer as WebFooter } from '@island.is/web/components' -import { Image, Organization } from '@island.is/web/graphql/schema' +import { Organization } from '@island.is/web/graphql/schema' import { usePlausiblePageview } from '@island.is/web/hooks' import SidebarLayout from '@island.is/web/screens/Layouts/SidebarLayout' @@ -135,12 +135,11 @@ export const OJOIWrapper = ({ {!sidebarContent && children} - + diff --git a/apps/web/screens/OfficialJournalOfIceland/OJOIAdvert.tsx b/apps/web/screens/OfficialJournalOfIceland/OJOIAdvert.tsx index 92fcc0abc82c..2709b2766eae 100644 --- a/apps/web/screens/OfficialJournalOfIceland/OJOIAdvert.tsx +++ b/apps/web/screens/OfficialJournalOfIceland/OJOIAdvert.tsx @@ -1,7 +1,7 @@ import { useIntl } from 'react-intl' -import { Locale } from '@island.is/shared/types' import { Box, Button, Link, Stack, Text } from '@island.is/island-ui/core' +import { Locale } from '@island.is/shared/types' import { ContentLanguage, CustomPageUniqueIdentifier, diff --git a/apps/web/screens/OfficialJournalOfIceland/OJOICategories.tsx b/apps/web/screens/OfficialJournalOfIceland/OJOICategories.tsx index b03b4338998e..519fbbc80e87 100644 --- a/apps/web/screens/OfficialJournalOfIceland/OJOICategories.tsx +++ b/apps/web/screens/OfficialJournalOfIceland/OJOICategories.tsx @@ -1,6 +1,5 @@ import { useCallback, useEffect, useMemo, useState } from 'react' import { useIntl } from 'react-intl' -import { Locale } from '@island.is/shared/types' import debounce from 'lodash/debounce' import { useRouter } from 'next/router' @@ -18,6 +17,7 @@ import { Text, } from '@island.is/island-ui/core' import { debounceTime } from '@island.is/shared/constants' +import { Locale } from '@island.is/shared/types' import { ContentLanguage, CustomPageUniqueIdentifier, diff --git a/apps/web/screens/OfficialJournalOfIceland/OJOIHome.tsx b/apps/web/screens/OfficialJournalOfIceland/OJOIHome.tsx index 2cb2b12905fd..bce39b678cce 100644 --- a/apps/web/screens/OfficialJournalOfIceland/OJOIHome.tsx +++ b/apps/web/screens/OfficialJournalOfIceland/OJOIHome.tsx @@ -1,5 +1,4 @@ import { useIntl } from 'react-intl' -import { Locale } from '@island.is/shared/types' import NextLink from 'next/link' import { @@ -13,6 +12,7 @@ import { Stack, Text, } from '@island.is/island-ui/core' +import { Locale } from '@island.is/shared/types' import { SLICE_SPACING } from '@island.is/web/constants' import { ContentLanguage, diff --git a/apps/web/screens/OfficialJournalOfIceland/OJOISearch.tsx b/apps/web/screens/OfficialJournalOfIceland/OJOISearch.tsx index 5d248425a3a7..c253999cd919 100644 --- a/apps/web/screens/OfficialJournalOfIceland/OJOISearch.tsx +++ b/apps/web/screens/OfficialJournalOfIceland/OJOISearch.tsx @@ -1,6 +1,5 @@ import { useEffect, useMemo, useState } from 'react' import { useIntl } from 'react-intl' -import { Locale } from '@island.is/shared/types' import debounce from 'lodash/debounce' import { useRouter } from 'next/router' import { useLazyQuery } from '@apollo/client' @@ -16,6 +15,7 @@ import { Text, } from '@island.is/island-ui/core' import { debounceTime } from '@island.is/shared/constants' +import { Locale } from '@island.is/shared/types' import { ContentLanguage, CustomPageUniqueIdentifier, @@ -487,7 +487,7 @@ OJOISearch.getProps = async ({ apolloClient, locale }) => { } return { - initialAdverts: officialJournalOfIcelandAdverts.adverts, + initialAdverts: officialJournalOfIcelandAdverts?.adverts, categories: officialJournalOfIcelandCategories?.categories, departments: officialJournalOfIcelandDepartments?.departments, types: officialJournalOfIcelandTypes?.types, diff --git a/libs/api/domains/driving-license/src/lib/graphql/models/drivingLicense.model.ts b/libs/api/domains/driving-license/src/lib/graphql/models/drivingLicense.model.ts index 3d9850a8518e..3ed73a7f4082 100644 --- a/libs/api/domains/driving-license/src/lib/graphql/models/drivingLicense.model.ts +++ b/libs/api/domains/driving-license/src/lib/graphql/models/drivingLicense.model.ts @@ -2,6 +2,7 @@ import { Field, ObjectType, ID } from '@nestjs/graphql' import { Disqualification } from './disqualification.model' import { Eligibility } from './eligibility.model' +import { Remark } from './remark.model' @ObjectType() export class DrivingLicense { @@ -21,7 +22,7 @@ export class DrivingLicense { categories!: Eligibility[] @Field(() => [String]) - remarks?: string[] + remarks?: Remark[] @Field(() => Disqualification, { nullable: true }) disqualification?: Disqualification diff --git a/libs/api/domains/driving-license/src/lib/graphql/models/remark.model.ts b/libs/api/domains/driving-license/src/lib/graphql/models/remark.model.ts new file mode 100644 index 000000000000..fa1eb122a446 --- /dev/null +++ b/libs/api/domains/driving-license/src/lib/graphql/models/remark.model.ts @@ -0,0 +1,10 @@ +import { Field, ObjectType } from '@nestjs/graphql' + +@ObjectType() +export class Remark { + @Field() + code!: string + + @Field() + description!: string +} diff --git a/libs/api/domains/endorsement-system/src/lib/models/endorsementList.model.ts b/libs/api/domains/endorsement-system/src/lib/models/endorsementList.model.ts index 7c60ea4a7216..2c03818d359a 100644 --- a/libs/api/domains/endorsement-system/src/lib/models/endorsementList.model.ts +++ b/libs/api/domains/endorsement-system/src/lib/models/endorsementList.model.ts @@ -39,4 +39,7 @@ export class EndorsementList { @Field({ nullable: true }) owner?: string + + @Field({ nullable: true }) + endorsementCounter?: number } diff --git a/libs/application/template-api-modules/src/lib/modules/shared/api/driving-license/driving-license.service.ts b/libs/application/template-api-modules/src/lib/modules/shared/api/driving-license/driving-license.service.ts index 7c847aa8b4fb..20c8339802e8 100644 --- a/libs/application/template-api-modules/src/lib/modules/shared/api/driving-license/driving-license.service.ts +++ b/libs/application/template-api-modules/src/lib/modules/shared/api/driving-license/driving-license.service.ts @@ -147,7 +147,13 @@ export class DrivingLicenseProviderService extends BaseTemplateApiService { currentLicense: fakeData.currentLicense === 'temp' ? 'B' : null, remarks: fakeData.remarks === YES - ? ['Gervilimur eða gervilimir/stoðtæki fyrir fætur og hendur.'] + ? [ + { + code: '', + description: + 'Gervilimur eða gervilimir/stoðtæki fyrir fætur og hendur.', + }, + ] : undefined, } } diff --git a/libs/application/template-api-modules/src/lib/modules/shared/api/driving-license/types.ts b/libs/application/template-api-modules/src/lib/modules/shared/api/driving-license/types.ts index 7e9215db5949..feb739d69f94 100644 --- a/libs/application/template-api-modules/src/lib/modules/shared/api/driving-license/types.ts +++ b/libs/application/template-api-modules/src/lib/modules/shared/api/driving-license/types.ts @@ -29,9 +29,14 @@ export type DrivingLicenseCategory = { nr?: string | null } +export interface Remark { + code: string + description: string +} + export type DrivingLicense = { currentLicense: string | null - remarks?: string[] + remarks?: Remark[] categories?: DrivingLicenseCategory[] id?: number birthCountry?: string | null diff --git a/libs/application/templates/driving-license/src/fields/EligibilitySummary/useEligibility.ts b/libs/application/templates/driving-license/src/fields/EligibilitySummary/useEligibility.ts index ed1d8389e302..ed58a4119d0f 100644 --- a/libs/application/templates/driving-license/src/fields/EligibilitySummary/useEligibility.ts +++ b/libs/application/templates/driving-license/src/fields/EligibilitySummary/useEligibility.ts @@ -5,8 +5,10 @@ import { useQuery, gql } from '@apollo/client' import { B_FULL, BE, + codesRequiringHealthCertificate, DrivingLicenseApplicationFor, DrivingLicenseFakeData, + otherLicenseCategories, YES, } from '../../lib/constants' import { fakeEligibility } from './fakeEligibility' @@ -69,26 +71,21 @@ export const useEligibility = ( 'currentLicense.data', ) const hasQualityPhoto = - getValueViaPath(application.externalData, 'qualityPhoto.data') ?? - false - const hasOtherLicenseCategories = ( + getValueViaPath( + application.externalData, + 'qualityPhoto.data.hasQualityPhoto', + ) ?? false + + const hasOtherCategoryOrHealthRemarks = ( currentLicense: DrivingLicense | undefined, ) => { return ( - (currentLicense?.categories.some( - (license) => - license.nr === 'C' || - license.nr === 'C1' || - license.nr === 'CE' || - license.nr === 'D' || - license.nr === 'D1' || - license.nr === 'DE', + (currentLicense?.categories.some((license) => + otherLicenseCategories.includes(license.nr), ) ?? false) || - currentLicense?.remarks?.some((x) => - x.includes( - 'Réttindi til farþegaflutninga í atvinnuskyni fyrir B-flokk.', - ), + currentLicense?.remarks?.some((remark) => + codesRequiringHealthCertificate.includes(remark.code), ) ) } @@ -130,13 +127,13 @@ export const useEligibility = ( : (data.drivingLicenseApplicationEligibility?.isEligible ?? false) && !hasGlasses && hasQualityPhoto && - !hasOtherLicenseCategories(currentLicense), + !hasOtherCategoryOrHealthRemarks(currentLicense), requirements: [ ...eligibility, { key: RequirementKey.BeRequiresHealthCertificate, requirementMet: - !hasGlasses && !hasOtherLicenseCategories(currentLicense), + !hasGlasses && !hasOtherCategoryOrHealthRemarks(currentLicense), }, { key: RequirementKey.HasNoPhoto, diff --git a/libs/application/templates/driving-license/src/fields/HealthRemarks.tsx b/libs/application/templates/driving-license/src/fields/HealthRemarks.tsx index f0b5160a3c87..7aacd4d20af4 100644 --- a/libs/application/templates/driving-license/src/fields/HealthRemarks.tsx +++ b/libs/application/templates/driving-license/src/fields/HealthRemarks.tsx @@ -5,14 +5,14 @@ import { FieldBaseProps } from '@island.is/application/types' import { m } from '../lib/messages' import { useLocale } from '@island.is/localization' import { useFormContext } from 'react-hook-form' -import { YES, NO } from '../lib/constants' -import { DrivingLicense } from '../lib/types' +import { YES, NO, codesRequiringHealthCertificate } from '../lib/constants' +import { DrivingLicense, Remark } from '../lib/types' const HealthRemarks: FC> = ({ application, }) => { const { formatMessage } = useLocale() - const remarks: string[] = + const remarks: Remark[] = getValueViaPath( application.externalData, 'currentLicense.data', @@ -21,7 +21,12 @@ const HealthRemarks: FC> = ({ const { setValue } = useFormContext() useEffect(() => { - setValue('hasHealthRemarks', remarks?.length > 0 ? YES : NO) + setValue( + 'hasHealthRemarks', + remarks.some((r) => codesRequiringHealthCertificate.includes(r.code)) + ? YES + : NO, + ) }, [remarks, setValue]) return ( @@ -32,7 +37,7 @@ const HealthRemarks: FC> = ({ message={ formatText(m.healthRemarksDescription, application, formatMessage) + ' ' + - remarks?.join(', ') || '' + remarks?.map((r) => r.description).join(', ') || '' } /> diff --git a/libs/application/templates/driving-license/src/lib/constants.ts b/libs/application/templates/driving-license/src/lib/constants.ts index b500e079da6f..56cbbae8f584 100644 --- a/libs/application/templates/driving-license/src/lib/constants.ts +++ b/libs/application/templates/driving-license/src/lib/constants.ts @@ -9,6 +9,9 @@ export const B_FULL = 'B-full' export const B_TEMP = 'B-temp' export const BE = 'BE' +export const otherLicenseCategories = ['C', 'C1', 'CE', 'D', 'D1', 'DE'] +export const codesRequiringHealthCertificate = ['400', '01.06'] + export type DrivingLicenseApplicationFor = | typeof B_FULL | typeof B_TEMP diff --git a/libs/application/templates/driving-license/src/lib/types.ts b/libs/application/templates/driving-license/src/lib/types.ts index 9d6a0822279e..92efcfc15176 100644 --- a/libs/application/templates/driving-license/src/lib/types.ts +++ b/libs/application/templates/driving-license/src/lib/types.ts @@ -13,8 +13,13 @@ export type DrivingLicenseCategory = { validToCode: number } +export interface Remark { + code: string + description: string +} + export type DrivingLicense = { currentLicense: string | null - remarks?: string[] + remarks?: Remark[] categories: DrivingLicenseCategory[] } diff --git a/libs/clients/driving-license/src/lib/drivingLicenseApi.service.ts b/libs/clients/driving-license/src/lib/drivingLicenseApi.service.ts index 7b1c2f9c3273..93b40dfc8436 100644 --- a/libs/clients/driving-license/src/lib/drivingLicenseApi.service.ts +++ b/libs/clients/driving-license/src/lib/drivingLicenseApi.service.ts @@ -14,6 +14,7 @@ import { RemarkCode, DrivingLicenseV4V5Dto, Jurisdiction, + Remark, } from './drivingLicenseApi.types' import { handleCreateResponse } from './utils/handleCreateResponse' import { PracticePermitDto, DriverLicenseWithoutImagesDto } from '../v5' @@ -104,10 +105,11 @@ export class DrivingLicenseApi { if (license?.comments) { const remarks = await this.getRemarksCodeTable() - const licenseRemarks: string[] = license.comments.map( - (remark) => + const licenseRemarks: Remark[] = license.comments.map((remark) => ({ + code: remark.nr ?? '', + description: remarks?.find((r) => r.index === remark?.nr?.toString())?.name ?? '', - ) + })) return { ...DrivingLicenseApi.normalizeDrivingLicenseDTO(license), remarks: licenseRemarks, @@ -136,17 +138,31 @@ export class DrivingLicenseApi { apiVersion: v5.DRIVING_LICENSE_API_VERSION_V5, apiVersion2: v5.DRIVING_LICENSE_API_VERSION_V5, }) - const licenseRemarks: string[] = licenseRaw.comments + const licenseRemarks: Remark[] = licenseRaw.comments .filter((remark) => remark.id === licenseRaw.id && !!remark.nr) - .map((remark) => remark.nr || '') - const filteredRemarks: string[] = remarks + .map((remark) => ({ + code: remark.comment ?? '', + description: remark.nr || '', + })) + + const filteredRemarks: Remark[] = remarks .filter( (remark) => !!remark.heiti && - licenseRemarks.includes(remark.nr || ('' && !remark.athugasemd)), + licenseRemarks.some((lremark) => + lremark.description.includes( + remark.nr || ('' && !remark.athugasemd), + ), + ), ) - .map((remark) => remark.heiti || '') - return { ...license, remarks: filteredRemarks } + .map((remark) => ({ + code: remark.nr || '', + description: remark.heiti || '', + })) + return { + ...license, + remarks: filteredRemarks, + } } return license diff --git a/libs/clients/driving-license/src/lib/drivingLicenseApi.types.ts b/libs/clients/driving-license/src/lib/drivingLicenseApi.types.ts index fa097fa8a40f..2c5a9d6616ab 100644 --- a/libs/clients/driving-license/src/lib/drivingLicenseApi.types.ts +++ b/libs/clients/driving-license/src/lib/drivingLicenseApi.types.ts @@ -28,13 +28,17 @@ export interface Disqualification { from?: Date | null } +export interface Remark { + code: string + description: string +} export interface DriversLicense { id: number name: string issued?: Date | null expires?: Date | null categories: DriversLicenseCategory[] - remarks?: string[] + remarks?: Remark[] disqualification?: Disqualification | null birthCountry?: string | null publishPlaceName?: string | null diff --git a/libs/email-service/src/tools/adapter.service.ts b/libs/email-service/src/tools/adapter.service.ts index adf06d94dcf7..70a4757720a1 100644 --- a/libs/email-service/src/tools/adapter.service.ts +++ b/libs/email-service/src/tools/adapter.service.ts @@ -78,7 +78,7 @@ export class AdapterService { throw new Error(`This component doesn't exist ${component}`) } - if (component === 'image') { + if (component === 'image' || component === 'imagewithlink') { const image = item as ImageComponent const path = image.context.src const filename = basename(path) diff --git a/libs/email-service/src/tools/design/foundation.hbs b/libs/email-service/src/tools/design/foundation.hbs index e89a3dec5139..45a3b40cd96e 100644 --- a/libs/email-service/src/tools/design/foundation.hbs +++ b/libs/email-service/src/tools/design/foundation.hbs @@ -1,3 +1,393 @@ diff --git a/libs/email-service/src/tools/design/image.hbs b/libs/email-service/src/tools/design/image.hbs index 67aa6c95a764..cc1b89d83052 100644 --- a/libs/email-service/src/tools/design/image.hbs +++ b/libs/email-service/src/tools/design/image.hbs @@ -1,5 +1,3 @@ {{#> center}} -
- {{{alt}}} -
+ {{{alt}}} {{/center}} diff --git a/libs/email-service/src/tools/design/imagewithlink.hbs b/libs/email-service/src/tools/design/imagewithlink.hbs new file mode 100644 index 000000000000..5ed87b6f0399 --- /dev/null +++ b/libs/email-service/src/tools/design/imagewithlink.hbs @@ -0,0 +1,5 @@ + + {{#> center}} + {{{alt}}} + {{/center}} + diff --git a/libs/email-service/src/tools/design/styles.hbs b/libs/email-service/src/tools/design/styles.hbs index 4627f4aa17c0..aa6c589b72dd 100644 --- a/libs/email-service/src/tools/design/styles.hbs +++ b/libs/email-service/src/tools/design/styles.hbs @@ -26,11 +26,6 @@ } } - .segment { - padding-left: 40px; - padding-right: 40px; - } - @media only screen and (max-width: 620px) { .segment { padding-left: 20px !important; @@ -42,8 +37,7 @@ * Section partial */ .section { - margin-bottom: 40px; - padding-bottom: 40px; + margin: 40px 20px; background: #ffffff; } @@ -295,21 +289,18 @@ display: flex; align-items: center; text-align: center; + justify-content: center; align-content: center; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; height: 32px; margin: 48px 0 24px 0; width: fit-content; - padding-left: 8px; - padding-right: 8px; - border-radius: 8px; font-family: ‘IBM Plex Sans’, Helvetica, Arial, sans-serif; font-weight: 600; font-size: 14px; line-height: 1.5; color: #0061FF; - background-color: #F2F7FF; } @media only screen and (max-width: 620px) { diff --git a/libs/email-service/src/types/index.ts b/libs/email-service/src/types/index.ts index 8695d27b4354..b20413f87e55 100644 --- a/libs/email-service/src/types/index.ts +++ b/libs/email-service/src/types/index.ts @@ -80,6 +80,16 @@ interface TagComponent { } } +interface ImageWithLinkComponent { + component: 'ImageWithLink' + context: { + src: string + alt: string + href: string + height?: string + } +} + export type Body = | ImageComponent | HeadingComponent @@ -90,6 +100,7 @@ export type Body = | SpacerComponent | TextWithLinkComponent | TagComponent + | ImageWithLinkComponent export interface Template { title: string diff --git a/libs/island-ui/core/src/lib/Navigation/Navigation.tsx b/libs/island-ui/core/src/lib/Navigation/Navigation.tsx index 3799a27ea05f..f2b91183a978 100644 --- a/libs/island-ui/core/src/lib/Navigation/Navigation.tsx +++ b/libs/island-ui/core/src/lib/Navigation/Navigation.tsx @@ -151,7 +151,6 @@ export const Navigation: FC> = ({ title = 'Efnisyfirlit', titleLink, titleIcon, - activeItemTitle, label, colorScheme = 'blue', @@ -168,7 +167,28 @@ export const Navigation: FC> = ({ mobileNavigationButtonCloseLabel = 'Close', }) => { const [mobileMenuOpen, setMobileMenuOpen] = useState(false) - const [activeAccordions, setActiveAccordions] = useState>([]) + + const [activeAccordions, setActiveAccordions] = useState>( + () => { + const initialActivePathIndex = items?.findIndex( + (item) => item.active && item.accordion, + ) + + if (initialActivePathIndex > 0) { + //first level only + return [ + `1-${items?.findIndex( + (item) => + item.active && + item.accordion && + item.items?.some((child) => child.active), + )}`, + ] + } + + return [] + }, + ) const color = colorSchemeColors[colorScheme]['color'] const activeColor = colorSchemeColors[colorScheme]['activeColor'] diff --git a/libs/island-ui/core/src/lib/RadioButton/RadioButton.css.ts b/libs/island-ui/core/src/lib/RadioButton/RadioButton.css.ts index ac0e95a6081d..0c27e6a3cf0b 100644 --- a/libs/island-ui/core/src/lib/RadioButton/RadioButton.css.ts +++ b/libs/island-ui/core/src/lib/RadioButton/RadioButton.css.ts @@ -10,6 +10,7 @@ const radioButtonCheckSize = 12 export const checkMarkWidth = theme.spacing[2] export const container = style({ + display: 'flex', position: 'relative', }) diff --git a/libs/judicial-system/types/src/lib/case.ts b/libs/judicial-system/types/src/lib/case.ts index ce77ee069b5b..9d57c7b87679 100644 --- a/libs/judicial-system/types/src/lib/case.ts +++ b/libs/judicial-system/types/src/lib/case.ts @@ -241,6 +241,7 @@ export enum CaseIndictmentRulingDecision { FINE = 'FINE', DISMISSAL = 'DISMISSAL', CANCELLATION = 'CANCELLATION', + MERGE = 'MERGE', } export enum IndictmentCaseReviewDecision { diff --git a/libs/nest/pagination/src/lib/paginate.ts b/libs/nest/pagination/src/lib/paginate.ts index 368deefebcba..e289af92c053 100644 --- a/libs/nest/pagination/src/lib/paginate.ts +++ b/libs/nest/pagination/src/lib/paginate.ts @@ -179,8 +179,26 @@ export async function paginate({ const [instances, totalCount, cursorCount] = await Promise.all([ Model.findAll(paginationQueryOptions), - Model.count(totalCountQueryOptions), - Model.count(cursorCountQueryOptions), + Model.count(totalCountQueryOptions).then( + // If the query does relation aggregations, then the count will be an array. + // Since we are only interested in the list length, not related elements, we + // can just take the length of the array. + + (count: Array | number) => { + if (Array.isArray(count)) { + return count.length + } + return count + }, + ), + Model.count(cursorCountQueryOptions).then( + (count: Array | number) => { + if (Array.isArray(count)) { + return count.length + } + return count + }, + ), ]) if (before) { diff --git a/libs/service-portal/core/src/components/ActionCard/ActionCard.tsx b/libs/service-portal/core/src/components/ActionCard/ActionCard.tsx index 6efbc9cca915..b2b5bb7f109d 100644 --- a/libs/service-portal/core/src/components/ActionCard/ActionCard.tsx +++ b/libs/service-portal/core/src/components/ActionCard/ActionCard.tsx @@ -142,7 +142,7 @@ export const ActionCard: React.FC> = ({ marginRight={[2, 3]} borderRadius="circle" > - action-card + ) } @@ -158,7 +158,7 @@ export const ActionCard: React.FC> = ({ background={image.active ? 'white' : 'blue100'} className={cn(styles.avatar, styles.image)} > - action-card + ) } @@ -339,13 +339,13 @@ export const ActionCard: React.FC> = ({ {/* Checking image type so the image is placed correctly */} {image?.type !== 'logo' && renderImage()} - {heading && ( - + + {heading && ( > = ({ {heading} - - {secondaryTag && ( - - {!date && !eyebrow && renderTag(secondaryTag)} - - )} - {!date && !eyebrow && renderTag(tag)} - - - )} - {text && {text}} + )} + {!heading && text && ( + {text} + )} + + {secondaryTag && ( + + {!date && !eyebrow && renderTag(secondaryTag)} + + )} + {!date && !eyebrow && renderTag(tag)} + + + {heading && text && {text}} {subText && {subText}} = ({ [`${imageClass}`]: imageClass, })} src={img} - alt="document" + alt="" /> )} diff --git a/libs/service-portal/finance/src/components/DocumentScreen/DocumentScreen.tsx b/libs/service-portal/finance/src/components/DocumentScreen/DocumentScreen.tsx index 1798fe30c1cc..3f7b71e85cb2 100644 --- a/libs/service-portal/finance/src/components/DocumentScreen/DocumentScreen.tsx +++ b/libs/service-portal/finance/src/components/DocumentScreen/DocumentScreen.tsx @@ -236,6 +236,7 @@ const DocumentScreen = ({ title={formatMessage(m.noData)} message={formatMessage(m.noTransactionFound)} imgSrc="./assets/images/sofa.svg" + imgAlt="" /> )} {billsDataArray.length > 0 ? ( diff --git a/libs/service-portal/finance/src/financeRedirects.tsx b/libs/service-portal/finance/src/financeRedirects.tsx index 06bd837a1ca5..d86650a82665 100644 --- a/libs/service-portal/finance/src/financeRedirects.tsx +++ b/libs/service-portal/finance/src/financeRedirects.tsx @@ -10,6 +10,14 @@ export const redirects: PortalRoute[] = [ enabled: true, element: , }, + { + name: m.financeTransactions, + path: FinancePaths.FinanceTransactions, + enabled: true, + element: ( + + ), + }, { name: m.financeBills, path: FinancePaths.FinanceBills, diff --git a/libs/service-portal/finance/src/lib/navigation.ts b/libs/service-portal/finance/src/lib/navigation.ts index 1b1755bb5ed7..3d2a821fddaf 100644 --- a/libs/service-portal/finance/src/lib/navigation.ts +++ b/libs/service-portal/finance/src/lib/navigation.ts @@ -24,7 +24,7 @@ export const financeNavigation: PortalNavigationItem = { { name: m.financeTransactionsCategories, description: m.financeTransactionsDescription, - path: FinancePaths.FinanceTransactions, + path: FinancePaths.FinanceTransactionCategories, }, { name: m.financeTransactionPeriods, diff --git a/libs/service-portal/finance/src/lib/paths.ts b/libs/service-portal/finance/src/lib/paths.ts index f7774e56d9da..ec5f1eb5c1d3 100644 --- a/libs/service-portal/finance/src/lib/paths.ts +++ b/libs/service-portal/finance/src/lib/paths.ts @@ -2,6 +2,7 @@ export enum FinancePaths { FinanceRoot = '/fjarmal', FinanceStatus = '/fjarmal/stada', FinanceTransactions = '/fjarmal/faerslur', + FinanceTransactionCategories = '/fjarmal/faerslur/flokkar', FinanceTransactionPeriods = '/fjarmal/faerslur/timabil', FinanceEmployeeClaims = '/fjarmal/laungreidendakrofur', FinanceLocalTax = '/fjarmal/utsvar', diff --git a/libs/service-portal/finance/src/module.tsx b/libs/service-portal/finance/src/module.tsx index d7fca17b5dea..a766755042d0 100644 --- a/libs/service-portal/finance/src/module.tsx +++ b/libs/service-portal/finance/src/module.tsx @@ -9,7 +9,9 @@ import { redirects } from './financeRedirects' const FinanceStatus = lazy(() => import('./screens/FinanceStatus')) const FinanceBills = lazy(() => import('./screens/FinanceBills')) -const FinanceTransactions = lazy(() => import('./screens/FinanceTransactions')) +const FinanceTransactionsCategories = lazy(() => + import('./screens/FinanceTransactions'), +) const FinanceTransactionPeriods = lazy(() => import('./screens/FinanceTransactionPeriods'), ) @@ -48,9 +50,9 @@ export const financeModule: PortalModule = { element: , }, { - name: m.financeTransactions, - path: FinancePaths.FinanceTransactions, - element: , + name: m.financeTransactionsCategories, + path: FinancePaths.FinanceTransactionCategories, + element: , enabled: userInfo.scopes.includes(ApiScope.financeOverview), dynamic: true, loader: financeRoutesLoader({ userInfo, ...rest }), diff --git a/libs/service-portal/finance/src/screens/FinanceLoans/FinanceLoans.tsx b/libs/service-portal/finance/src/screens/FinanceLoans/FinanceLoans.tsx index e196f8a56f2d..b8a470a2eb1c 100644 --- a/libs/service-portal/finance/src/screens/FinanceLoans/FinanceLoans.tsx +++ b/libs/service-portal/finance/src/screens/FinanceLoans/FinanceLoans.tsx @@ -40,6 +40,7 @@ const FinanceLoans = () => { title={formatMessage(m.noData)} message={formatMessage(m.noTransactionFound)} imgSrc="./assets/images/sofa.svg" + imgAlt="" /> )} {loanOverviewData?.hmsLoansHistory?.length ? ( diff --git a/libs/service-portal/finance/src/screens/FinanceTransactions/FinanceTransactions.tsx b/libs/service-portal/finance/src/screens/FinanceTransactions/FinanceTransactions.tsx index 4bf42e43932a..bd06f3d1b4cd 100644 --- a/libs/service-portal/finance/src/screens/FinanceTransactions/FinanceTransactions.tsx +++ b/libs/service-portal/finance/src/screens/FinanceTransactions/FinanceTransactions.tsx @@ -263,6 +263,7 @@ const FinanceTransactions = () => { title={formatMessage(m.noData)} message={formatMessage(m.noTransactionFound)} imgSrc="./assets/images/sofa.svg" + imgAlt="" /> )} {recordsDataArray.length > 0 ? ( diff --git a/libs/service-portal/information/src/components/PersonalInformation/Forms/ProfileForm/components/DropModal.tsx b/libs/service-portal/information/src/components/PersonalInformation/Forms/ProfileForm/components/DropModal.tsx index 1d5472796687..96f3da126101 100644 --- a/libs/service-portal/information/src/components/PersonalInformation/Forms/ProfileForm/components/DropModal.tsx +++ b/libs/service-portal/information/src/components/PersonalInformation/Forms/ProfileForm/components/DropModal.tsx @@ -57,7 +57,7 @@ export const DropModal: FC> = ({ Skrautmynd diff --git a/libs/service-portal/licenses/src/components/LicenseIcon/LicenseIcon.tsx b/libs/service-portal/licenses/src/components/LicenseIcon/LicenseIcon.tsx index abaab9dec2e2..7dc703feba4a 100644 --- a/libs/service-portal/licenses/src/components/LicenseIcon/LicenseIcon.tsx +++ b/libs/service-portal/licenses/src/components/LicenseIcon/LicenseIcon.tsx @@ -5,12 +5,7 @@ interface LogoSvgProps { } const LicenseIcon = ({ category }: LogoSvgProps) => { - return ( - {`driving-license-icon-${category}`} - ) + return } export default LicenseIcon diff --git a/libs/service-portal/licenses/src/components/SingleLicenseCard/SingleLicenseCard.tsx b/libs/service-portal/licenses/src/components/SingleLicenseCard/SingleLicenseCard.tsx index ba67f2959de2..77cb8de17245 100644 --- a/libs/service-portal/licenses/src/components/SingleLicenseCard/SingleLicenseCard.tsx +++ b/libs/service-portal/licenses/src/components/SingleLicenseCard/SingleLicenseCard.tsx @@ -63,7 +63,7 @@ export const SingleLicenseCard = ({ dataTestId={dataTestId} > - {title} + { )} - {openSignedLists && openSignedLists.length > 0 && ( diff --git a/libs/shared/connected/src/lib/generalPetition/GeneralPetitionLists/useGetPetitionLists.ts b/libs/shared/connected/src/lib/generalPetition/GeneralPetitionLists/useGetPetitionLists.ts index 6e476f71c4ae..39105b147f1a 100644 --- a/libs/shared/connected/src/lib/generalPetition/GeneralPetitionLists/useGetPetitionLists.ts +++ b/libs/shared/connected/src/lib/generalPetition/GeneralPetitionLists/useGetPetitionLists.ts @@ -28,6 +28,7 @@ const GetGeneralPetitionLists = gql` description closedDate openedDate + endorsementCounter adminLock meta owner diff --git a/scripts/ci/00_prepare-base-tags.sh b/scripts/ci/00_prepare-base-tags.sh index 4231289a1658..bfc83b8a63bd 100755 --- a/scripts/ci/00_prepare-base-tags.sh +++ b/scripts/ci/00_prepare-base-tags.sh @@ -24,7 +24,7 @@ if [[ "$BUILD_REF" != "$LAST_GOOD_BUILD_SHA" ]]; then git log -1 "$BUILD_REF" fi LAST_GOOD_BUILD_DOCKER_BRANCH_TAG=$(echo "${LAST_GOOD_BUILD_BRANCH}" | tr "/." "-" ) -export LAST_GOOD_BUILD_DOCKER_TAG=${LAST_GOOD_BUILD_DOCKER_BRANCH_TAG:0:45}_${LAST_GOOD_BUILD_SHA:0:7}_${LAST_GOOD_BUILD_RUN_NUMBER} +export LAST_GOOD_BUILD_DOCKER_TAG=${LAST_GOOD_BUILD_DOCKER_BRANCH_TAG:0:45}_${LAST_GOOD_BUILD_SHA}_${LAST_GOOD_BUILD_RUN_NUMBER} if [[ "$BUILD_REF" == "null" || "$BUILD_REF" == "" ]]; then curl -X POST -H 'Content-type: application/json' --data "{\"text\":\"Change detection failed for $HTML_URL\"}" "$ISSUE_REPORTING_SLACK_WEBHOOK_URL" exit 1 diff --git a/scripts/ci/30_test.sh b/scripts/ci/30_test.sh index 58a6e75ac860..1d760b6e021d 100755 --- a/scripts/ci/30_test.sh +++ b/scripts/ci/30_test.sh @@ -9,11 +9,23 @@ set -euxo pipefail : "${DD_SERVICE:=${APP:-"unit-test"}}" : "${DD_API_KEY:=''}" : "${NODE_OPTIONS:=}" +: "${FLAKY_TEST_RETRIES:=3}" # Default to big old-space, and more options for testing, but allow overriding NODE_OPTIONS="--max-old-space-size=8193 --unhandled-rejections=warn --require=dd-trace/ci/init ${NODE_OPTIONS:-}" EXTRA_OPTS="" +FLAKY_TESTS=( + "services-auth-delegation-api" + "services-auth-personal-representative" +) +if [[ " ${FLAKY_TESTS[*]} " == *" ${APP} "* ]]; then + IS_FLAKY_TEST=true +else + IS_FLAKY_TEST=false +fi + + projects_uncollectible_coverage=( "application-templates-no-debt-certificate" "api-domains-email-signup" @@ -34,9 +46,12 @@ export DD_CIVISIBILITY_AGENTLESS_ENABLED \ NODE_OPTIONS \ SERVERSIDE_FEATURES_ON=\"\" # disable server-side features -yarn run test \ - "${APP}" \ - ${EXTRA_OPTS} \ - --verbose \ - --no-watchman \ - "$@" +FLAKY_TEST_RETRIES=$(if [[ "$IS_FLAKY_TEST" == true ]]; then echo "$FLAKY_TEST_RETRIES"; else echo 1; fi) + +for ((i=1; i<=FLAKY_TEST_RETRIES; i++)); do + echo "Running test ${APP} (attempt: ${i}/${FLAKY_TEST_RETRIES})" + if yarn run test "${APP}" ${EXTRA_OPTS} --verbose --no-watchman "$@"; then + exit 0 + fi +done +exit 1 \ No newline at end of file diff --git a/scripts/ci/_chunk.js b/scripts/ci/_chunk.js index f44c4cd93142..a9d0269538e3 100644 --- a/scripts/ci/_chunk.js +++ b/scripts/ci/_chunk.js @@ -5,6 +5,8 @@ const projects = process.argv[2].split(',').map((s) => s.trim()) ?? [] const problematicProjects = [ 'judicial-system-backend', 'application-system-api', + 'services-auth-delegation-api', + 'services-auth-personal-representative', ] function groupbByPrefix(arr) { diff --git a/scripts/ci/cache/__config.mjs b/scripts/ci/cache/__config.mjs index 6552ec71b40f..608ed52f7696 100644 --- a/scripts/ci/cache/__config.mjs +++ b/scripts/ci/cache/__config.mjs @@ -33,7 +33,7 @@ export const ENABLED_MODULES = (process.env[ENV_ENABLED_CACHE] || '') return a }, {}) -export const cypressPath = '/github/home/.cache/Cypress' +export const cypressPath = '/github/home/.cypress-cache' export const cacheSuccess = JSON.parse(process.env[ENV_CACHE_SUCCESS] ?? '{}') export const initCache = process.env[ENV_INIT_CACHE] === 'true' @@ -147,7 +147,7 @@ export const caches = [ } const pkg = await getPackageJSON() const cypressVersion = pkg?.devDependencies?.cypress - return `cypress-cache-${HASH_VERSION}-${getPlatformString()}-${cypressVersion}` + return `cypress-cache-${HASH_VERSION}-${getPlatformString()}-${cypressVersion}-2` }, name: 'Cache Cypress', id: 'cypress', @@ -167,6 +167,7 @@ export const caches = [ const cypressVersion = pkg?.devDependencies?.cypress await runCommand('npx cypress install', ROOT, { CYPRESS_INSTALL_BINARY: cypressVersion, + CYPRESS_CACHE_FOLDER: cypressPath, }) }, path: cypressPath || '',