From 49199489cb6634a126828c969dfef0e4f16f7480 Mon Sep 17 00:00:00 2001 From: unakb Date: Wed, 23 Oct 2024 14:37:53 +0000 Subject: [PATCH 1/2] feat(j-s): Judge confirms defender and civil claimant defense choices (#16480) * feat(j-s): Allow judge to confirm defender and civil claimant choices * Update SelectDefender.tsx * cleanup * fix(j-s): Cleanup * chore: nx format:write update dirty files * feat(j-s): Add case files shared with defender front end handling * fix(j-s): Control access with new flags * fix(j-s): Feedback * Update SelectDefender.tsx * fix(j-s): Tests * Update createDefendant.dto.ts * Update limitedAccessViewCaseFileGuard.spec.ts * fix(j-s): Internal update defendant dto separate from other update * fix(j-s): Feedback * Update subpoena.service.ts * fix(j-s): Case list filter for defenders * Update cases.filter.spec.ts --------- Co-authored-by: andes-it Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../dto/updateCivilClaimant.input.ts | 5 + .../defendant/dto/updateDefendant.input.ts | 10 ++ .../defendant/models/civilClaimant.model.ts | 3 + .../defendant/models/defendant.model.ts | 6 + .../20241015110312-update-defendant.js | 46 +++++++ .../20241018105400-update-civil-claimant.js | 26 ++++ .../app/modules/case/filters/case.filter.ts | 9 +- .../app/modules/case/filters/cases.filter.ts | 4 +- .../case/filters/test/cases.filter.spec.ts | 4 +- .../filters/test/defenceUserFilter.spec.ts | 52 ++++++- .../src/app/modules/case/guards/rolesRules.ts | 9 +- .../modules/defendant/defendant.service.ts | 14 +- .../dto/internalUpdateDefendant.dto.ts | 47 +++++++ .../defendant/dto/updateCivilClaimant.dto.ts | 5 + .../defendant/dto/updateDefendant.dto.ts | 10 ++ .../defendant/internalDefendant.controller.ts | 4 +- .../defendant/models/civilClaimant.model.ts | 13 +- .../defendant/models/defendant.model.ts | 28 +++- .../modules/file/guards/caseFileCategory.ts | 9 +- .../limitedAccessViewCaseFileGuard.spec.ts | 10 +- .../subpoena/dto/updateSubpoena.dto.ts | 7 +- .../app/modules/subpoena/subpoena.service.ts | 11 ++ .../src/app/modules/cases/case.service.ts | 5 + .../src/components/FormProvider/case.graphql | 3 + .../Advocates/Advocates.strings.ts | 99 ++++++++++++++ .../Advocates/SelectCivilClaimantAdvocate.tsx | 107 +++++++++++---- .../Indictments/Advocates/SelectDefender.tsx | 128 +++++++++++++++++- .../judicial-system/web/src/utils/validate.ts | 2 + .../xrd-api/src/app/app.service.ts | 23 +++- 29 files changed, 645 insertions(+), 54 deletions(-) create mode 100644 apps/judicial-system/backend/migrations/20241015110312-update-defendant.js create mode 100644 apps/judicial-system/backend/migrations/20241018105400-update-civil-claimant.js create mode 100644 apps/judicial-system/backend/src/app/modules/defendant/dto/internalUpdateDefendant.dto.ts diff --git a/apps/judicial-system/api/src/app/modules/defendant/dto/updateCivilClaimant.input.ts b/apps/judicial-system/api/src/app/modules/defendant/dto/updateCivilClaimant.input.ts index 5c00a5bfe44c..7e13fa8ebccd 100644 --- a/apps/judicial-system/api/src/app/modules/defendant/dto/updateCivilClaimant.input.ts +++ b/apps/judicial-system/api/src/app/modules/defendant/dto/updateCivilClaimant.input.ts @@ -61,4 +61,9 @@ export class UpdateCivilClaimantInput { @IsOptional() @Field(() => Boolean, { nullable: true }) readonly caseFilesSharedWithSpokesperson?: boolean + + @Allow() + @IsOptional() + @Field(() => Boolean, { nullable: true }) + readonly isSpokespersonConfirmed?: boolean } diff --git a/apps/judicial-system/api/src/app/modules/defendant/dto/updateDefendant.input.ts b/apps/judicial-system/api/src/app/modules/defendant/dto/updateDefendant.input.ts index 09637837e462..ca43a4658f72 100644 --- a/apps/judicial-system/api/src/app/modules/defendant/dto/updateDefendant.input.ts +++ b/apps/judicial-system/api/src/app/modules/defendant/dto/updateDefendant.input.ts @@ -94,4 +94,14 @@ export class UpdateDefendantInput { @IsOptional() @Field(() => SubpoenaType, { nullable: true }) readonly subpoenaType?: SubpoenaType + + @Allow() + @IsOptional() + @Field(() => Boolean, { nullable: true }) + readonly isDefenderChoiceConfirmed?: boolean + + @Allow() + @IsOptional() + @Field(() => Boolean, { nullable: true }) + readonly caseFilesSharedWithDefender?: boolean } diff --git a/apps/judicial-system/api/src/app/modules/defendant/models/civilClaimant.model.ts b/apps/judicial-system/api/src/app/modules/defendant/models/civilClaimant.model.ts index da90d7d1834f..a2298adf5abd 100644 --- a/apps/judicial-system/api/src/app/modules/defendant/models/civilClaimant.model.ts +++ b/apps/judicial-system/api/src/app/modules/defendant/models/civilClaimant.model.ts @@ -43,4 +43,7 @@ export class CivilClaimant { @Field(() => Boolean, { nullable: true }) readonly caseFilesSharedWithSpokesperson?: boolean + + @Field(() => Boolean, { nullable: true }) + readonly isSpokespersonConfirmed?: boolean } 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 f1e16bf6a8aa..5b509b4dba2b 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 @@ -89,4 +89,10 @@ export class Defendant { @Field(() => [Subpoena], { nullable: true }) readonly subpoenas?: Subpoena[] + + @Field(() => Boolean, { nullable: true }) + readonly isDefenderChoiceConfirmed?: boolean + + @Field(() => Boolean, { nullable: true }) + readonly caseFilesSharedWithDefender?: boolean } diff --git a/apps/judicial-system/backend/migrations/20241015110312-update-defendant.js b/apps/judicial-system/backend/migrations/20241015110312-update-defendant.js new file mode 100644 index 000000000000..684e85916d13 --- /dev/null +++ b/apps/judicial-system/backend/migrations/20241015110312-update-defendant.js @@ -0,0 +1,46 @@ +module.exports = { + up: (queryInterface, Sequelize) => { + return queryInterface.sequelize.transaction((t) => + Promise.all([ + queryInterface.addColumn( + 'defendant', + 'is_defender_choice_confirmed', + { + type: Sequelize.BOOLEAN, + allowNull: true, + }, + { transaction: t }, + ), + queryInterface.addColumn( + 'defendant', + 'case_files_shared_with_defender', + { + type: Sequelize.BOOLEAN, + allowNull: true, + }, + { transaction: t }, + ), + ]), + ) + }, + down: (queryInterface) => { + return queryInterface.sequelize.transaction((t) => + Promise.all([ + queryInterface.removeColumn( + 'defendant', + 'is_defender_choice_confirmed', + { + transaction: t, + }, + ), + queryInterface.removeColumn( + 'defendant', + 'case_files_shared_with_defender', + { + transaction: t, + }, + ), + ]), + ) + }, +} diff --git a/apps/judicial-system/backend/migrations/20241018105400-update-civil-claimant.js b/apps/judicial-system/backend/migrations/20241018105400-update-civil-claimant.js new file mode 100644 index 000000000000..e8cc6f8f09a4 --- /dev/null +++ b/apps/judicial-system/backend/migrations/20241018105400-update-civil-claimant.js @@ -0,0 +1,26 @@ +module.exports = { + up: (queryInterface, Sequelize) => { + return queryInterface.sequelize.transaction((t) => + queryInterface.addColumn( + 'civil_claimant', + 'is_spokesperson_confirmed', + { + type: Sequelize.BOOLEAN, + allowNull: true, + }, + { transaction: t }, + ), + ) + }, + down: (queryInterface) => { + return queryInterface.sequelize.transaction((t) => + queryInterface.removeColumn( + 'civil_claimant', + 'is_spokesperson_confirmed', + { + transaction: t, + }, + ), + ) + }, +} diff --git a/apps/judicial-system/backend/src/app/modules/case/filters/case.filter.ts b/apps/judicial-system/backend/src/app/modules/case/filters/case.filter.ts index 110e22c09bdf..e38b445c792b 100644 --- a/apps/judicial-system/backend/src/app/modules/case/filters/case.filter.ts +++ b/apps/judicial-system/backend/src/app/modules/case/filters/case.filter.ts @@ -386,13 +386,18 @@ const canDefenceUserAccessIndictmentCase = ( } // Check case defender assignment - if (Defendant.isDefenderOfDefendant(user.nationalId, theCase.defendants)) { + if ( + Defendant.isConfirmedDefenderOfDefendant( + user.nationalId, + theCase.defendants, + ) + ) { return true } // Check case spokesperson assignment if ( - CivilClaimant.isSpokespersonOfCivilClaimant( + CivilClaimant.isConfirmedSpokespersonOfCivilClaimant( user.nationalId, theCase.civilClaimants, ) && diff --git a/apps/judicial-system/backend/src/app/modules/case/filters/cases.filter.ts b/apps/judicial-system/backend/src/app/modules/case/filters/cases.filter.ts index 847292fbddbe..5e9d9ffc0dd3 100644 --- a/apps/judicial-system/backend/src/app/modules/case/filters/cases.filter.ts +++ b/apps/judicial-system/backend/src/app/modules/case/filters/cases.filter.ts @@ -283,7 +283,7 @@ const getDefenceUserCasesQueryFilter = (user: User): WhereOptions => { [Op.in]: Sequelize.literal(` (SELECT case_id FROM defendant - WHERE defender_national_id in ('${normalizedNationalId}', '${formattedNationalId}')) + WHERE defender_national_id in ('${normalizedNationalId}', '${formattedNationalId}') and is_defender_choice_confirmed = true) `), }, }, @@ -292,7 +292,7 @@ const getDefenceUserCasesQueryFilter = (user: User): WhereOptions => { [Op.in]: Sequelize.literal(` (SELECT case_id FROM civil_claimant - WHERE has_spokesperson = true AND spokesperson_national_id in ('${normalizedNationalId}', '${formattedNationalId}')) + WHERE has_spokesperson = true AND spokesperson_national_id in ('${normalizedNationalId}', '${formattedNationalId}') and is_spokesperson_confirmed = true) `), }, }, diff --git a/apps/judicial-system/backend/src/app/modules/case/filters/test/cases.filter.spec.ts b/apps/judicial-system/backend/src/app/modules/case/filters/test/cases.filter.spec.ts index 2a0af1e49d7d..e1da15d9cf19 100644 --- a/apps/judicial-system/backend/src/app/modules/case/filters/test/cases.filter.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/filters/test/cases.filter.spec.ts @@ -459,7 +459,7 @@ describe('getCasesQueryFilter', () => { [Op.in]: Sequelize.literal(` (SELECT case_id FROM defendant - WHERE defender_national_id in ('${user.nationalId}', '${user.nationalId}')) + WHERE defender_national_id in ('${user.nationalId}', '${user.nationalId}') and is_defender_choice_confirmed = true) `), }, }, @@ -468,7 +468,7 @@ describe('getCasesQueryFilter', () => { [Op.in]: Sequelize.literal(` (SELECT case_id FROM civil_claimant - WHERE has_spokesperson = true AND spokesperson_national_id in ('${user.nationalId}', '${user.nationalId}')) + WHERE has_spokesperson = true AND spokesperson_national_id in ('${user.nationalId}', '${user.nationalId}') and is_spokesperson_confirmed = true) `), }, }, diff --git a/apps/judicial-system/backend/src/app/modules/case/filters/test/defenceUserFilter.spec.ts b/apps/judicial-system/backend/src/app/modules/case/filters/test/defenceUserFilter.spec.ts index 4ada3f02f7c1..b37f9babaca9 100644 --- a/apps/judicial-system/backend/src/app/modules/case/filters/test/defenceUserFilter.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/filters/test/defenceUserFilter.spec.ts @@ -203,7 +203,31 @@ describe.each(defenceRoles)('defence user %s', (role) => { const theCase = { type, state, - defendants: [{}, { defenderNationalId: user.nationalId }, {}], + defendants: [ + {}, + { + defenderNationalId: user.nationalId, + }, + {}, + ], + dateLogs: [{ dateType: DateType.ARRAIGNMENT_DATE, date: new Date() }], + } as Case + + verifyNoAccess(theCase, user) + }) + + describe('confirmed defender assigned to case', () => { + const theCase = { + type, + state, + defendants: [ + {}, + { + defenderNationalId: user.nationalId, + isDefenderChoiceConfirmed: true, + }, + {}, + ], dateLogs: [{ dateType: DateType.ARRAIGNMENT_DATE, date: new Date() }], } as Case @@ -263,13 +287,35 @@ describe.each(defenceRoles)('defence user %s', (role) => { verifyNoAccess(theCase, user) }) - describe('spokesperson assigned to case', () => { + describe('non confirmed spokesperson assigned to case', () => { + const theCase = { + type, + state, + civilClaimants: [ + {}, + { + hasSpokesperson: true, + spokespersonNationalId: user.nationalId, + }, + {}, + ], + dateLogs: [{ dateType: DateType.ARRAIGNMENT_DATE, date: new Date() }], + } as Case + + verifyNoAccess(theCase, user) + }) + + describe('confirmed spokesperson assigned to case', () => { const theCase = { type, state, civilClaimants: [ {}, - { hasSpokesperson: true, spokespersonNationalId: user.nationalId }, + { + hasSpokesperson: true, + spokespersonNationalId: user.nationalId, + isSpokespersonConfirmed: true, + }, {}, ], dateLogs: [{ dateType: DateType.ARRAIGNMENT_DATE, date: new Date() }], diff --git a/apps/judicial-system/backend/src/app/modules/case/guards/rolesRules.ts b/apps/judicial-system/backend/src/app/modules/case/guards/rolesRules.ts index 6835cdae1f85..2b95c2f46a57 100644 --- a/apps/judicial-system/backend/src/app/modules/case/guards/rolesRules.ts +++ b/apps/judicial-system/backend/src/app/modules/case/guards/rolesRules.ts @@ -289,12 +289,17 @@ export const defenderGeneratedPdfRule: RolesRule = { } // Allow if the user is a defender of a defendant of the case - if (Defendant.isDefenderOfDefendant(user.nationalId, theCase.defendants)) { + if ( + Defendant.isConfirmedDefenderOfDefendantWithCaseFileAccess( + user.nationalId, + theCase.defendants, + ) + ) { return true } if ( - CivilClaimant.isSpokespersonOfCivilClaimantWithCaseFileAccess( + CivilClaimant.isConfirmedSpokespersonOfCivilClaimantWithCaseFileAccess( user.nationalId, theCase.civilClaimants, ) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/defendant.service.ts b/apps/judicial-system/backend/src/app/modules/defendant/defendant.service.ts index 5436816f44a8..7c38ff9f11b4 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/defendant.service.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/defendant.service.ts @@ -21,12 +21,14 @@ import type { User } from '@island.is/judicial-system/types' import { CaseState, CaseType, + isDistrictCourtUser, NotificationType, } from '@island.is/judicial-system/types' import { Case } from '../case/models/case.model' import { CourtService } from '../court' import { CreateDefendantDto } from './dto/createDefendant.dto' +import { InternalUpdateDefendantDto } from './dto/internalUpdateDefendant.dto' import { UpdateDefendantDto } from './dto/updateDefendant.dto' import { Defendant } from './models/defendant.model' import { DeliverResponse } from './models/deliver.response' @@ -199,8 +201,18 @@ export class DefendantService { async updateByNationalId( caseId: string, defendantNationalId: string, - update: UpdateDefendantDto, + update: InternalUpdateDefendantDto, ): Promise { + // The reason we have a separate dto for this is because requests that end here + // are initiated by outside API's which should not be able to edit other fields + // Defendant updated originating from the judicial system should use the UpdateDefendantDto + // and go through the update method above using the defendantId. + // This is also why we set the isDefenderChoiceConfirmed to false here - the judge needs to confirm all changes. + update = { + ...update, + isDefenderChoiceConfirmed: false, + } as UpdateDefendantDto + const [numberOfAffectedRows, defendants] = await this.defendantModel.update( update, { diff --git a/apps/judicial-system/backend/src/app/modules/defendant/dto/internalUpdateDefendant.dto.ts b/apps/judicial-system/backend/src/app/modules/defendant/dto/internalUpdateDefendant.dto.ts new file mode 100644 index 000000000000..8cfac3f40ade --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/defendant/dto/internalUpdateDefendant.dto.ts @@ -0,0 +1,47 @@ +import { IsEnum, IsOptional, IsString } from 'class-validator' + +import { ApiPropertyOptional } from '@nestjs/swagger' + +import { DefenderChoice } from '@island.is/judicial-system/types' + +export class InternalUpdateDefendantDto { + @IsOptional() + @IsString() + @ApiPropertyOptional({ type: String }) + readonly defenderName?: string + + @IsOptional() + @IsString() + @ApiPropertyOptional({ type: String }) + readonly defenderNationalId?: string + + @IsOptional() + @IsString() + @ApiPropertyOptional({ type: String }) + readonly defenderEmail?: string + + @IsOptional() + @IsString() + @ApiPropertyOptional({ type: String }) + readonly defenderPhoneNumber?: string + + @IsOptional() + @IsEnum(DefenderChoice) + @ApiPropertyOptional({ enum: DefenderChoice }) + readonly defenderChoice?: DefenderChoice + + @IsOptional() + @IsEnum(DefenderChoice) + @ApiPropertyOptional({ enum: DefenderChoice }) + readonly requestedDefenderChoice?: DefenderChoice + + @IsOptional() + @IsString() + @ApiPropertyOptional({ type: String }) + readonly requestedDefenderNationalId?: string + + @IsOptional() + @IsString() + @ApiPropertyOptional({ type: String }) + readonly requestedDefenderName?: string +} diff --git a/apps/judicial-system/backend/src/app/modules/defendant/dto/updateCivilClaimant.dto.ts b/apps/judicial-system/backend/src/app/modules/defendant/dto/updateCivilClaimant.dto.ts index 4ad6cb1ce29c..3f85ec624a7f 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/dto/updateCivilClaimant.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/dto/updateCivilClaimant.dto.ts @@ -52,4 +52,9 @@ export class UpdateCivilClaimantDto { @IsBoolean() @ApiPropertyOptional({ type: Boolean }) readonly caseFilesSharedWithSpokesperson?: boolean + + @IsOptional() + @IsBoolean() + @ApiPropertyOptional({ type: Boolean }) + readonly isSpokespersonConfirmed?: boolean } 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 5c51ae0d2813..47848c5c084f 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 @@ -108,4 +108,14 @@ export class UpdateDefendantDto { @IsString() @ApiPropertyOptional({ type: String }) readonly requestedDefenderName?: string + + @IsOptional() + @IsBoolean() + @ApiPropertyOptional({ type: Boolean }) + readonly isDefenderChoiceConfirmed?: boolean + + @IsOptional() + @IsBoolean() + @ApiPropertyOptional({ type: Boolean }) + readonly caseFilesSharedWithDefender?: boolean } 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 367ac2df85dc..f7dc724065a6 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/internalDefendant.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/internalDefendant.controller.ts @@ -20,7 +20,7 @@ import { import { Case, CaseExistsGuard, CurrentCase } from '../case' import { DeliverDefendantToCourtDto } from './dto/deliverDefendantToCourt.dto' -import { UpdateDefendantDto } from './dto/updateDefendant.dto' +import { InternalUpdateDefendantDto } from './dto/internalUpdateDefendant.dto' import { CurrentDefendant } from './guards/defendant.decorator' import { DefendantExistsGuard } from './guards/defendantExists.guard' import { Defendant } from './models/defendant.model' @@ -71,7 +71,7 @@ export class InternalDefendantController { @Param('caseId') caseId: string, @Param('defendantNationalId') defendantNationalId: string, @CurrentCase() theCase: Case, - @Body() updatedDefendantChoice: UpdateDefendantDto, + @Body() updatedDefendantChoice: InternalUpdateDefendantDto, ): Promise { this.logger.debug(`Updating defendant info for ${caseId}`) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/models/civilClaimant.model.ts b/apps/judicial-system/backend/src/app/modules/defendant/models/civilClaimant.model.ts index ce59dd77b472..0191d42734f4 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/models/civilClaimant.model.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/models/civilClaimant.model.ts @@ -20,7 +20,7 @@ import { Case } from '../../case/models/case.model' timestamps: false, }) export class CivilClaimant extends Model { - static isSpokespersonOfCivilClaimant( + static isConfirmedSpokespersonOfCivilClaimant( spokespersonNationalId: string, civilClaimants?: CivilClaimant[], ) { @@ -28,13 +28,14 @@ export class CivilClaimant extends Model { (civilClaimant) => civilClaimant.hasSpokesperson && civilClaimant.spokespersonNationalId && + civilClaimant.isSpokespersonConfirmed && normalizeAndFormatNationalId(spokespersonNationalId).includes( civilClaimant.spokespersonNationalId, ), ) } - static isSpokespersonOfCivilClaimantWithCaseFileAccess( + static isConfirmedSpokespersonOfCivilClaimantWithCaseFileAccess( spokespersonNationalId: string, civilClaimants?: CivilClaimant[], ) { @@ -42,6 +43,7 @@ export class CivilClaimant extends Model { (civilClaimant) => civilClaimant.hasSpokesperson && civilClaimant.spokespersonNationalId && + civilClaimant.isSpokespersonConfirmed && normalizeAndFormatNationalId(spokespersonNationalId).includes( civilClaimant.spokespersonNationalId, ) && @@ -141,4 +143,11 @@ export class CivilClaimant extends Model { }) @ApiPropertyOptional({ type: Boolean }) caseFilesSharedWithSpokesperson?: boolean + + @Column({ + type: DataType.BOOLEAN, + allowNull: true, + }) + @ApiPropertyOptional({ type: Boolean }) + isSpokespersonConfirmed?: boolean } 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 d36b4a8ad6d8..6c9f299641c4 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 @@ -29,7 +29,7 @@ import { Subpoena } from '../../subpoena/models/subpoena.model' timestamps: true, }) export class Defendant extends Model { - static isDefenderOfDefendant( + static isConfirmedDefenderOfDefendant( defenderNationalId: string, defendants?: Defendant[], ) { @@ -38,7 +38,23 @@ export class Defendant extends Model { defendant.defenderNationalId && normalizeAndFormatNationalId(defenderNationalId).includes( defendant.defenderNationalId, - ), + ) && + defendant.isDefenderChoiceConfirmed, + ) + } + + static isConfirmedDefenderOfDefendantWithCaseFileAccess( + defenderNationalId: string, + defendants?: Defendant[], + ) { + return defendants?.some( + (defendant) => + defendant.defenderNationalId && + normalizeAndFormatNationalId(defenderNationalId).includes( + defendant.defenderNationalId, + ) && + defendant.isDefenderChoiceConfirmed && + defendant.caseFilesSharedWithDefender, ) } @@ -167,4 +183,12 @@ export class Defendant extends Model { @Column({ type: DataType.STRING, allowNull: true }) @ApiPropertyOptional({ type: String }) requestedDefenderName?: string + + @Column({ type: DataType.BOOLEAN, allowNull: true }) + @ApiPropertyOptional({ type: Boolean }) + isDefenderChoiceConfirmed?: boolean + + @Column({ type: DataType.BOOLEAN, allowNull: true }) + @ApiPropertyOptional({ type: Boolean }) + caseFilesSharedWithDefender?: boolean } diff --git a/apps/judicial-system/backend/src/app/modules/file/guards/caseFileCategory.ts b/apps/judicial-system/backend/src/app/modules/file/guards/caseFileCategory.ts index f9ab8226bf93..0c873991e68e 100644 --- a/apps/judicial-system/backend/src/app/modules/file/guards/caseFileCategory.ts +++ b/apps/judicial-system/backend/src/app/modules/file/guards/caseFileCategory.ts @@ -64,14 +64,19 @@ const canDefenceUserViewCaseFileOfIndictmentCase = ( defendants?: Defendant[], civilClaimants?: CivilClaimant[], ) => { - if (Defendant.isDefenderOfDefendant(nationalId, defendants)) { + if ( + Defendant.isConfirmedDefenderOfDefendantWithCaseFileAccess( + nationalId, + defendants, + ) + ) { return defenderCaseFileCategoriesForIndictmentCases.includes( caseFileCategory, ) } if ( - CivilClaimant.isSpokespersonOfCivilClaimantWithCaseFileAccess( + CivilClaimant.isConfirmedSpokespersonOfCivilClaimantWithCaseFileAccess( nationalId, civilClaimants, ) diff --git a/apps/judicial-system/backend/src/app/modules/file/guards/test/limitedAccessViewCaseFileGuard.spec.ts b/apps/judicial-system/backend/src/app/modules/file/guards/test/limitedAccessViewCaseFileGuard.spec.ts index 998d9a7807d3..2c83715462f4 100644 --- a/apps/judicial-system/backend/src/app/modules/file/guards/test/limitedAccessViewCaseFileGuard.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/file/guards/test/limitedAccessViewCaseFileGuard.spec.ts @@ -178,7 +178,13 @@ describe('Limited Access View Case File Guard', () => { case: { type, state, - defendants: [{ defenderNationalId: nationalId }], + defendants: [ + { + defenderNationalId: nationalId, + isDefenderChoiceConfirmed: true, + caseFilesSharedWithDefender: true, + }, + ], }, caseFile: { category }, })) @@ -206,6 +212,7 @@ describe('Limited Access View Case File Guard', () => { hasSpokesperson: true, spokespersonNationalId: nationalId, caseFilesSharedWithSpokesperson: true, + isSpokespersonConfirmed: true, }, ], }, @@ -268,7 +275,6 @@ describe('Limited Access View Case File Guard', () => { { hasSpokesperson: true, spokespersonNationalId: nationalId, - caseFilesSharedWithSpokesperson: true, }, ], }, diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts b/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts index 5132ae3fb5d2..80603c97865d 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts @@ -1,4 +1,4 @@ -import { IsEnum, IsOptional, IsString } from 'class-validator' +import { IsBoolean, IsEnum, IsOptional, IsString } from 'class-validator' import { ApiPropertyOptional } from '@nestjs/swagger' @@ -64,4 +64,9 @@ export class UpdateSubpoenaDto { @IsString() @ApiPropertyOptional({ type: String }) readonly requestedDefenderName?: string + + @IsOptional() + @IsBoolean() + @ApiPropertyOptional({ type: Boolean }) + readonly isDefenderChoiceConfirmed?: boolean } diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts index 26175ace9bb1..4b0b3e129d2c 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts @@ -18,6 +18,7 @@ import { MessageService, MessageType } from '@island.is/judicial-system/message' import { CaseFileCategory, DefenderChoice, + isDistrictCourtUser, isFailedServiceStatus, isSuccessfulServiceStatus, isTrafficViolationCase, @@ -133,6 +134,15 @@ export class SubpoenaService { }) let defenderAffectedRows = 0 + // If there is a change in the defender choice after the judge has confirmed the choice, + // we need to set the isDefenderChoiceConfirmed to false + const isChangingDefenderChoice = + (update.defenderChoice && + subpoena.defendant?.defenderChoice !== update.defenderChoice) || + (update.defenderNationalId && + subpoena.defendant?.defenderNationalId !== update.defenderNationalId && + subpoena.defendant?.isDefenderChoiceConfirmed) + if ( defenderChoice || defenderNationalId || @@ -148,6 +158,7 @@ export class SubpoenaService { requestedDefenderChoice, requestedDefenderNationalId, requestedDefenderName, + isDefenderChoiceConfirmed: isChangingDefenderChoice ? false : undefined, } const [defenderUpdateAffectedRows] = await this.defendantModel.update( diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts index 03ea716e60d8..6f4cec3631b3 100644 --- a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts +++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts @@ -137,6 +137,11 @@ export class CaseService { } const defenderChoice = { + defenderChoice: defenderAssignment.defenderChoice, + defenderNationalId: defenderAssignment.defenderNationalId, + defenderName: chosenLawyer?.Name, + defenderEmail: chosenLawyer?.Email, + defenderPhoneNumber: chosenLawyer?.Phone, requestedDefenderChoice: defenderAssignment.defenderChoice, requestedDefenderNationalId: defenderAssignment.defenderNationalId, requestedDefenderName: chosenLawyer?.Name, diff --git a/apps/judicial-system/web/src/components/FormProvider/case.graphql b/apps/judicial-system/web/src/components/FormProvider/case.graphql index a8afe466ec62..5f2d3f3ec668 100644 --- a/apps/judicial-system/web/src/components/FormProvider/case.graphql +++ b/apps/judicial-system/web/src/components/FormProvider/case.graphql @@ -25,6 +25,8 @@ query Case($input: CaseQueryInput!) { requestedDefenderChoice requestedDefenderNationalId requestedDefenderName + isDefenderChoiceConfirmed + caseFilesSharedWithDefender serviceRequirement verdictViewDate verdictAppealDeadline @@ -340,6 +342,7 @@ query Case($input: CaseQueryInput!) { spokespersonEmail spokespersonPhoneNumber caseFilesSharedWithSpokesperson + isSpokespersonConfirmed } civilDemands hasCivilClaims diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Advocates/Advocates.strings.ts b/apps/judicial-system/web/src/routes/Court/Indictments/Advocates/Advocates.strings.ts index ac7bfc63dc14..0ee84e48c080 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Advocates/Advocates.strings.ts +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Advocates/Advocates.strings.ts @@ -25,6 +25,32 @@ export const strings = defineMessages({ description: 'Notaður sem texti fyrir takka þegar ákærðu óska ekki eftir verjanda í dómaraflæði í ákærum. ', }, + confirmDefenderChoice: { + id: 'judicial.system.core:court_indictments.advocates.confirm_defender_choice', + defaultMessage: 'Staðfesta val á verjanda', + description: + 'Notaður sem texti fyrir takka til að staðfesta val á verjanda í dómaraflæði í ákærum.', + }, + changeDefenderChoice: { + id: 'judicial.system.core:court_indictments.advocates.change_defender_choice', + defaultMessage: 'Breyta vali á verjanda', + description: + 'Notaður sem texti fyrir takka til að breyta vali á verjanda í dómaraflæði í ákærum.', + }, + confirmSpokespersonChoice: { + id: 'judicial.system.core:court_indictments.advocates.confirm_spokesperson_choice', + defaultMessage: + 'Staðfesta {spokespersonIsLawyer, select, true {lögmann} other {réttargæslumann}}', + description: + 'Notaður sem texti fyrir takka til að staðfesta val á talsmanni í dómaraflæði í ákærum.', + }, + changeSpokespersonChoice: { + id: 'judicial.system.core:court_indictments.advocates.change_spokesperson_choice', + defaultMessage: + 'Breyta {spokespersonIsLawyer, select, true {lögmanni} other {réttargæslumanni}}', + description: + 'Notaður sem texti fyrir takka til að breyta vali á talsmanni í dómaraflæði í ákærum.', + }, civilClaimants: { id: 'judicial.system.core:court_indictments.advocates.civil_claimants', defaultMessage: 'Kröfuhafar', @@ -87,4 +113,77 @@ export const strings = defineMessages({ description: 'Notaður sem texti þegar ákærði hefur valið verjanda í dómaraflæði í ákærum.', }, + + confirmDefenderChoiceModalTitle: { + id: 'judicial.system.core:court_indictments.advocates.confirm_defender_choice_modal_title', + defaultMessage: + '{isDefenderChoiceConfirmed, select, true {Breyta} other {Staðfesta}}', + description: + 'Notaður sem titill á staðfesta eða breyta val á verjanda modal í dómaraflæði í ákærum.', + }, + confirmDefenderChoiceModalText: { + id: 'judicial.system.core:court_indictments.advocates.confirm_defender_choice_modal_text', + defaultMessage: + 'Valinn verjandi, {defenderName}, mun fá aðgang að máli í Réttarvörslugátt. ', + description: + 'Notaður sem texti í staðfesta val á verjanda modal í dómaraflæði í ákærum.', + }, + confirmDefenderWaivedModalText: { + id: 'judicial.system.core:court_indictments.advocates.confirm_defender_waived_modal_text', + defaultMessage: 'Ákærða verður ekki skipaður verjandi.', + description: + 'Notaður sem texti í staðfesta val á verjanda modal í dómaraflæði í ákærum.', + }, + confirmDefenderDelayModalText: { + id: 'judicial.system.core:court_indictments.advocates.confirm_defender_delay_modal_text', + defaultMessage: + 'Ákærð fær frest fram að þingfestingu til þess að tilnefna verjanda.', + description: + 'Notaður sem texti í staðfesta val á verjanda modal í dómaraflæði í ákærum.', + }, + changeDefenderChoiceModalText: { + id: 'judicial.system.core:court_indictments.advocates.change_defender_choice_modal_text', + defaultMessage: 'Ertu viss um að þú viljir breyta vali á verjanda?', + description: + 'Notaður sem texti í breyta vali á verjanda modal í dómaraflæði í ákærum.', + }, + + confirmSpokespersonModalTitle: { + id: 'judicial.system.core:court_indictments.advocates.confirm_spokesperson_modal_title', + defaultMessage: + '{isSpokespersonConfirmed, select, true {Breyta} other {Staðfesta}} val á {spokespersonIsLawyer, select, true {lögmanni} other {réttargæslumanni}}', + description: + 'Notaður sem titill á staðfesta val á lögmanni eða réttargæslumanni modal í dómaraflæði í ákærum.', + }, + confirmSpokespersonModalText: { + id: 'judicial.system.core:court_indictments.advocates.confirm_spokesperson_modal_text', + defaultMessage: + 'Ertu viss um að þú viljir {isSpokespersonConfirmed, select, true {breyta vali} other {staðfesta val}} á {spokespersonIsLawyer, select, true {lögmanni} other {réttargæslumanni}}?', + description: + 'Notaður sem texti í staðfesta val á lögmanni eða réttargæslumanni modal í dómaraflæði í ákærum.', + }, + confirmModalPrimaryButtonText: { + id: 'judicial.system.core:court_indictments.advocates.modal_primary_button_text', + defaultMessage: 'Staðfesta', + description: + 'Notaður sem texti á takka til að staðfesta val á lögmanni eða réttargæslumanni í dómaraflæði í ákærum.', + }, + confirmModalSecondaryButtonText: { + id: 'judicial.system.core:court_indictments.advocates.modal_secondary_button_text', + defaultMessage: 'Hætta við', + description: + 'Notaður sem texti á takka til að hætta við val á lögmanni eða réttargæslumanni í dómaraflæði í ákærum.', + }, + shareFilesWithDefender: { + id: 'judicial.system.core:court_indictments.advocates.share_files_with_defender', + defaultMessage: 'Deila gögnum með verjanda', + description: 'Notaður sem texti á deila kröfum með verjanda takka.', + }, + shareFilesWithDefenderTooltip: { + id: 'judicial.system.core:court_indictments.advocates.share_files_with_defender_tooltip', + defaultMessage: + 'Ef hakað er í þennan reit fær lögmaður aðgang að gögnum málsins', + description: + 'Notaður sem texti í tooltip á deila kröfum með verjanda takka.', + }, }) diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Advocates/SelectCivilClaimantAdvocate.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Advocates/SelectCivilClaimantAdvocate.tsx index 7047b7724dfd..29263aa60383 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Advocates/SelectCivilClaimantAdvocate.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Advocates/SelectCivilClaimantAdvocate.tsx @@ -1,4 +1,4 @@ -import { FC, useContext } from 'react' +import { FC, useContext, useState } from 'react' import { useIntl } from 'react-intl' import { @@ -14,6 +14,7 @@ import { BlueBox, FormContext, InputAdvocate, + Modal, } from '@island.is/judicial-system-web/src/components' import { CivilClaimant, @@ -28,10 +29,11 @@ interface Props { } const SelectCivilClaimantAdvocate: FC = ({ civilClaimant }) => { - const { setAndSendCivilClaimantToServer } = useCivilClaimants() const { workingCase, setWorkingCase } = useContext(FormContext) - const { formatMessage } = useIntl() + const { setAndSendCivilClaimantToServer } = useCivilClaimants() + + const [displayModal, setDisplayModal] = useState(false) const updateCivilClaimant = (update: UpdateCivilClaimantInput) => { setAndSendCivilClaimantToServer( @@ -65,6 +67,7 @@ const SelectCivilClaimantAdvocate: FC = ({ civilClaimant }) => { spokespersonIsLawyer: true, } as UpdateCivilClaimantInput) } + disabled={Boolean(civilClaimant.isSpokespersonConfirmed)} /> @@ -80,6 +83,7 @@ const SelectCivilClaimantAdvocate: FC = ({ civilClaimant }) => { spokespersonIsLawyer: false, } as UpdateCivilClaimantInput) } + disabled={Boolean(civilClaimant.isSpokespersonConfirmed)} /> @@ -93,7 +97,8 @@ const SelectCivilClaimantAdvocate: FC = ({ civilClaimant }) => { } disabled={ civilClaimant.spokespersonIsLawyer === null || - civilClaimant.spokespersonIsLawyer === undefined + civilClaimant.spokespersonIsLawyer === undefined || + civilClaimant.isSpokespersonConfirmed } isCivilClaim={true} /> @@ -132,30 +137,82 @@ const SelectCivilClaimantAdvocate: FC = ({ civilClaimant }) => { /> )} - + + )} + + + + + + {displayModal && ( + { updateCivilClaimant({ - hasSpokesperson: !civilClaimant.hasSpokesperson, - spokespersonEmail: null, - spokespersonPhoneNumber: null, - spokespersonName: null, - spokespersonIsLawyer: null, - spokespersonNationalId: null, - caseFilesSharedWithSpokesperson: null, + isSpokespersonConfirmed: !civilClaimant.isSpokespersonConfirmed, } as UpdateCivilClaimantInput) + setDisplayModal(false) }} - > - {civilClaimant.hasSpokesperson - ? formatMessage(strings.removeCivilClaimantAdvocate, { - defenderIsLawyer: civilClaimant.spokespersonIsLawyer, - }) - : formatMessage(strings.addCivilClaimantAdvocate)} - - + secondaryButtonText={formatMessage( + strings.confirmModalSecondaryButtonText, + )} + onSecondaryButtonClick={() => setDisplayModal(false)} + /> + )} ) } diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Advocates/SelectDefender.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Advocates/SelectDefender.tsx index 15b455791e48..e12abf66a507 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Advocates/SelectDefender.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Advocates/SelectDefender.tsx @@ -1,7 +1,7 @@ import { ChangeEvent, FC, useCallback, useContext, useState } from 'react' import { useIntl } from 'react-intl' -import { Box, Checkbox, Text } from '@island.is/island-ui/core' +import { Box, Button, Checkbox, Text } from '@island.is/island-ui/core' import { capitalize } from '@island.is/judicial-system/formatters' import { core } from '@island.is/judicial-system-web/messages' import { @@ -9,6 +9,7 @@ import { DefenderNotFound, FormContext, InputAdvocate, + Modal, } from '@island.is/judicial-system-web/src/components' import { Defendant, @@ -27,6 +28,8 @@ const SelectDefender: FC = ({ defendant }) => { const { formatMessage } = useIntl() const { setAndSendDefendantToServer } = useDefendants() + const [displayModal, setDisplayModal] = useState(false) + const [defenderNotFound, setDefenderNotFound] = useState(false) const gender = defendant.gender || 'NONE' @@ -54,7 +57,7 @@ const SelectDefender: FC = ({ defendant }) => { defenderChoice: defendantWaivesRightToCounsel === true ? DefenderChoice.WAIVE - : undefined, + : DefenderChoice.DELAY, } setAndSendDefendantToServer(updateDefendantInput, setWorkingCase) @@ -62,6 +65,56 @@ const SelectDefender: FC = ({ defendant }) => { [setWorkingCase, setAndSendDefendantToServer], ) + const toggleDefenderChoiceConfirmed = useCallback( + ( + caseId: string, + defendant: Defendant, + isDefenderChoiceConfirmed: boolean, + ) => { + const { defenderChoice, defenderName } = defendant + + const isDelaying = + !defenderName && + (!defenderChoice || defenderChoice === DefenderChoice.CHOOSE) + const isChoosing = + defenderName && + (!defenderChoice || defenderChoice === DefenderChoice.DELAY) + + const defenderChoiceUpdate = isDelaying + ? DefenderChoice.DELAY + : isChoosing + ? DefenderChoice.CHOOSE + : defenderChoice + + const updateDefendantInput = { + caseId, + defendantId: defendant.id, + isDefenderChoiceConfirmed, + defenderChoice: defenderChoiceUpdate, + } + + setAndSendDefendantToServer(updateDefendantInput, setWorkingCase) + setDisplayModal(false) + }, + [setWorkingCase, setAndSendDefendantToServer, setDisplayModal], + ) + + const toggleCaseFilesSharedWithDefender = useCallback( + ( + caseId: string, + defendant: Defendant, + caseFilesSharedWithDefender: boolean, + ) => { + const updateDefendantInput = { + caseId: caseId, + defendantId: defendant.id, + caseFilesSharedWithDefender, + } + setAndSendDefendantToServer(updateDefendantInput, setWorkingCase) + }, + [setWorkingCase, setAndSendDefendantToServer], + ) + return ( {defenderNotFound && !workingCase.defendantWaivesRightToCounsel && ( @@ -94,14 +147,83 @@ const SelectDefender: FC = ({ defendant }) => { }} filled large + disabled={defendant.isDefenderChoiceConfirmed === true} /> + + { + toggleCaseFilesSharedWithDefender( + workingCase.id, + defendant, + !defendant.caseFilesSharedWithDefender, + ) + }} + tooltip={formatMessage(strings.shareFilesWithDefenderTooltip)} + backgroundColor="white" + large + filled + /> + + + + + {displayModal && ( + + toggleDefenderChoiceConfirmed( + workingCase.id, + defendant, + !defendant.isDefenderChoiceConfirmed, + ) + } + secondaryButtonText={formatMessage( + strings.confirmModalSecondaryButtonText, + )} + onSecondaryButtonClick={() => setDisplayModal(false)} + /> + )} ) } diff --git a/apps/judicial-system/web/src/utils/validate.ts b/apps/judicial-system/web/src/utils/validate.ts index b2162dc1c6da..e510a2739436 100644 --- a/apps/judicial-system/web/src/utils/validate.ts +++ b/apps/judicial-system/web/src/utils/validate.ts @@ -469,6 +469,8 @@ export const isDefenderStepValid = (workingCase: Case): boolean => { workingCase.defendants?.every((defendant) => { return ( defendant.defenderChoice === DefenderChoice.WAIVE || + defendant.defenderChoice === DefenderChoice.DELAY || + !defendant.defenderChoice || validate([ [defendant.defenderName, ['empty']], [defendant.defenderEmail, ['email-format']], diff --git a/apps/judicial-system/xrd-api/src/app/app.service.ts b/apps/judicial-system/xrd-api/src/app/app.service.ts index cafa8a8287c6..57648e9254f7 100644 --- a/apps/judicial-system/xrd-api/src/app/app.service.ts +++ b/apps/judicial-system/xrd-api/src/app/app.service.ts @@ -96,7 +96,15 @@ export class AppService { subpoenaId: string, updateSubpoena: UpdateSubpoenaDto, ): Promise { - let defenderName = undefined + let defenderInfo: { + defenderName: string | undefined + defenderEmail: string | undefined + defenderPhoneNumber: string | undefined + } = { + defenderName: undefined, + defenderEmail: undefined, + defenderPhoneNumber: undefined, + } if ( updateSubpoena.defenderChoice === DefenderChoice.CHOOSE && @@ -113,7 +121,11 @@ export class AppService { updateSubpoena.defenderNationalId, ) - defenderName = chosenLawyer.Name + defenderInfo = { + defenderName: chosenLawyer.Name, + defenderEmail: chosenLawyer.Email, + defenderPhoneNumber: chosenLawyer.Phone, + } } catch (reason) { // TODO: Reconsider throwing - what happens if registry is down? this.logger.error( @@ -141,9 +153,14 @@ export class AppService { comment: updateSubpoena.comment, servedBy: updateSubpoena.servedBy, serviceDate: updateSubpoena.servedAt, + defenderChoice: updateSubpoena.defenderChoice, + defenderNationalId: updateSubpoena.defenderNationalId, + defenderName: defenderInfo.defenderName, + defenderEmail: defenderInfo.defenderEmail, + defenderPhoneNumber: defenderInfo.defenderPhoneNumber, requestedDefenderChoice: updateSubpoena.defenderChoice, requestedDefenderNationalId: updateSubpoena.defenderNationalId, - requestedDefenderName: defenderName, + requestedDefenderName: defenderInfo.defenderName, } try { From f2f1f1292dad725acbc18b468f9f76aa5a6c7859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A6ring=20Gunnar=20Stein=C3=BE=C3=B3rsson?= Date: Wed, 23 Oct 2024 17:58:34 +0000 Subject: [PATCH 2/2] chore(portals-admin): Move Allow specific national Id to be superUser controlled (#16386) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../components/PermissionAccessControl.tsx | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/libs/portals/admin/ids-admin/src/screens/Permission/components/PermissionAccessControl.tsx b/libs/portals/admin/ids-admin/src/screens/Permission/components/PermissionAccessControl.tsx index 1478d12fcd63..c169b659a8dd 100644 --- a/libs/portals/admin/ids-admin/src/screens/Permission/components/PermissionAccessControl.tsx +++ b/libs/portals/admin/ids-admin/src/screens/Permission/components/PermissionAccessControl.tsx @@ -9,6 +9,7 @@ import { m } from '../../../lib/messages' import { PermissionFormTypes } from '../EditPermission.schema' import { useEnvironmentState } from '../../../hooks/useEnvironmentState' import { checkEnvironmentsSync } from '../../../utils/checkEnvironmentsSync' +import { useSuperAdmin } from '../../../hooks/useSuperAdmin' const commonProps: Pick = { @@ -20,6 +21,7 @@ const commonProps: Pick = export const PermissionAccessControl = () => { const { formatMessage } = useLocale() const { selectedPermission, permission } = usePermission() + const { isSuperAdmin } = useSuperAdmin() const { isAccessControlled, grantToAuthenticatedUser } = selectedPermission const [inputValues, setInputValues] = useEnvironmentState<{ @@ -40,19 +42,21 @@ export const PermissionAccessControl = () => { ])} > - { - setInputValues({ - ...inputValues, - isAccessControlled: e.target.checked, - }) - }} - {...commonProps} - /> + {isSuperAdmin && ( + { + setInputValues({ + ...inputValues, + isAccessControlled: e.target.checked, + }) + }} + {...commonProps} + /> + )}