diff --git a/apps/judicial-system/api/src/app/modules/backend/backend.service.ts b/apps/judicial-system/api/src/app/modules/backend/backend.service.ts index 8069fc52faac..12377fb8d096 100644 --- a/apps/judicial-system/api/src/app/modules/backend/backend.service.ts +++ b/apps/judicial-system/api/src/app/modules/backend/backend.service.ts @@ -355,6 +355,17 @@ export class BackendService extends DataSource<{ req: Request }> { ) } + limitedAccessUpdateDefendant( + caseId: string, + defendantId: string, + updateDefendant: unknown, + ): Promise { + return this.patch( + `case/${caseId}/limitedAccess/defendant/${defendantId}`, + updateDefendant, + ) + } + deleteDefendant( caseId: string, defendantId: string, diff --git a/apps/judicial-system/api/src/app/modules/defendant/defendant.module.ts b/apps/judicial-system/api/src/app/modules/defendant/defendant.module.ts index 245771d1f74e..069954e5695f 100644 --- a/apps/judicial-system/api/src/app/modules/defendant/defendant.module.ts +++ b/apps/judicial-system/api/src/app/modules/defendant/defendant.module.ts @@ -2,8 +2,13 @@ import { Module } from '@nestjs/common' import { CivilClaimantResolver } from './civilClaimant.resolver' import { DefendantResolver } from './defendant.resolver' +import { LimitedAccessDefendantResolver } from './limitedAccessDefendant.resolver' @Module({ - providers: [DefendantResolver, CivilClaimantResolver], + providers: [ + DefendantResolver, + CivilClaimantResolver, + LimitedAccessDefendantResolver, + ], }) export class DefendantModule {} 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 d25bad9175cb..185906750683 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 @@ -6,6 +6,7 @@ import { DefendantPlea, DefenderChoice, Gender, + PunishmentType, ServiceRequirement, SubpoenaType, } from '@island.is/judicial-system/types' @@ -114,4 +115,9 @@ export class UpdateDefendantInput { @IsOptional() @Field(() => Boolean, { nullable: true }) readonly isSentToPrisonAdmin?: boolean + + @Allow() + @IsOptional() + @Field(() => PunishmentType, { nullable: true }) + readonly punishmentType?: PunishmentType } diff --git a/apps/judicial-system/api/src/app/modules/defendant/limitedAccessDefendant.resolver.ts b/apps/judicial-system/api/src/app/modules/defendant/limitedAccessDefendant.resolver.ts new file mode 100644 index 000000000000..ab84737de74c --- /dev/null +++ b/apps/judicial-system/api/src/app/modules/defendant/limitedAccessDefendant.resolver.ts @@ -0,0 +1,54 @@ +import { Inject, UseGuards } from '@nestjs/common' +import { Args, Context, Mutation, Resolver } from '@nestjs/graphql' + +import type { Logger } from '@island.is/logging' +import { LOGGER_PROVIDER } from '@island.is/logging' + +import { + AuditedAction, + AuditTrailService, +} from '@island.is/judicial-system/audit-trail' +import { + CurrentGraphQlUser, + JwtGraphQlAuthGuard, +} from '@island.is/judicial-system/auth' +import type { User } from '@island.is/judicial-system/types' + +import { BackendService } from '../backend' +import { UpdateDefendantInput } from './dto/updateDefendant.input' +import { Defendant } from './models/defendant.model' + +@UseGuards(JwtGraphQlAuthGuard) +@Resolver() +export class LimitedAccessDefendantResolver { + constructor( + private readonly auditTrailService: AuditTrailService, + @Inject(LOGGER_PROVIDER) + private readonly logger: Logger, + ) {} + + @Mutation(() => Defendant, { nullable: true }) + limitedAccessUpdateDefendant( + @Args('input', { type: () => UpdateDefendantInput }) + input: UpdateDefendantInput, + @CurrentGraphQlUser() user: User, + @Context('dataSources') + { backendService }: { backendService: BackendService }, + ): Promise { + const { caseId, defendantId, ...updateDefendant } = input + this.logger.debug( + `Updating limitedAccess defendant ${defendantId} for case ${caseId}`, + ) + + return this.auditTrailService.audit( + user.id, + AuditedAction.UPDATE_DEFENDANT, + backendService.limitedAccessUpdateDefendant( + caseId, + defendantId, + updateDefendant, + ), + defendantId, + ) + } +} 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 8a692f3a9ade..589709247bd9 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 @@ -4,6 +4,7 @@ import { DefendantPlea, DefenderChoice, Gender, + PunishmentType, ServiceRequirement, SubpoenaType, } from '@island.is/judicial-system/types' @@ -15,6 +16,7 @@ registerEnumType(DefendantPlea, { name: 'DefendantPlea' }) registerEnumType(ServiceRequirement, { name: 'ServiceRequirement' }) registerEnumType(DefenderChoice, { name: 'DefenderChoice' }) registerEnumType(SubpoenaType, { name: 'SubpoenaType' }) +registerEnumType(PunishmentType, { name: 'PunishmentType' }) @ObjectType() export class Defendant { @@ -107,4 +109,7 @@ export class Defendant { @Field(() => String, { nullable: true }) readonly sentToPrisonAdminDate?: string + + @Field(() => PunishmentType, { nullable: true }) + readonly punishmentType?: PunishmentType } diff --git a/apps/judicial-system/backend/migrations/20241211095654-update-defendant.js b/apps/judicial-system/backend/migrations/20241211095654-update-defendant.js new file mode 100644 index 000000000000..f8d0249598cc --- /dev/null +++ b/apps/judicial-system/backend/migrations/20241211095654-update-defendant.js @@ -0,0 +1,26 @@ +'use strict' + +module.exports = { + async up(queryInterface, Sequelize) { + return queryInterface.sequelize.transaction((t) => + Promise.all([ + queryInterface.addColumn( + 'defendant', + 'punishment_type', + { + type: Sequelize.STRING, + allowNull: true, + }, + { transaction: t }, + ), + ]), + ) + }, + async down(queryInterface, Sequelize) { + return queryInterface.sequelize.transaction((t) => + queryInterface.removeColumn('defendant', 'punishment_type', { + transaction: t, + }), + ) + }, +} diff --git a/apps/judicial-system/backend/src/app/modules/defendant/defendant.module.ts b/apps/judicial-system/backend/src/app/modules/defendant/defendant.module.ts index 3aa77ff57b8f..3ac9e2543e23 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/defendant.module.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/defendant.module.ts @@ -13,6 +13,7 @@ import { CivilClaimantService } from './civilClaimant.service' import { DefendantController } from './defendant.controller' import { DefendantService } from './defendant.service' import { InternalDefendantController } from './internalDefendant.controller' +import { LimitedAccessDefendantController } from './limitedAccessDefendant.controller' @Module({ imports: [ @@ -25,6 +26,7 @@ import { InternalDefendantController } from './internalDefendant.controller' DefendantController, InternalDefendantController, CivilClaimantController, + LimitedAccessDefendantController, ], providers: [DefendantService, CivilClaimantService], exports: [DefendantService, CivilClaimantService], 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 611604fb8c5d..50cad98ba07e 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 @@ -15,6 +15,7 @@ import { DefendantPlea, DefenderChoice, Gender, + PunishmentType, ServiceRequirement, SubpoenaType, } from '@island.is/judicial-system/types' @@ -149,4 +150,9 @@ export class UpdateDefendantDto { @IsBoolean() @ApiPropertyOptional({ type: Boolean }) readonly isSentToPrisonAdmin?: boolean + + @IsOptional() + @IsEnum(PunishmentType) + @ApiPropertyOptional({ enum: PunishmentType }) + readonly punishmentType?: PunishmentType } diff --git a/apps/judicial-system/backend/src/app/modules/defendant/guards/rolesRules.ts b/apps/judicial-system/backend/src/app/modules/defendant/guards/rolesRules.ts new file mode 100644 index 000000000000..94577db1a405 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/defendant/guards/rolesRules.ts @@ -0,0 +1,13 @@ +import { RolesRule, RulesType } from '@island.is/judicial-system/auth' +import { UserRole } from '@island.is/judicial-system/types' + +import { UpdateDefendantDto } from '../dto/updateDefendant.dto' + +const limitedAccessFields: (keyof UpdateDefendantDto)[] = ['punishmentType'] + +// Allows prison staff to update a specific set of fields for defendant +export const prisonSystemStaffUpdateRule: RolesRule = { + role: UserRole.PRISON_SYSTEM_STAFF, + type: RulesType.FIELD, + dtoFields: limitedAccessFields, +} diff --git a/apps/judicial-system/backend/src/app/modules/defendant/limitedAccessDefendant.controller.ts b/apps/judicial-system/backend/src/app/modules/defendant/limitedAccessDefendant.controller.ts new file mode 100644 index 000000000000..f68b9dabf06c --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/defendant/limitedAccessDefendant.controller.ts @@ -0,0 +1,64 @@ +import { + Body, + Controller, + Inject, + Param, + Patch, + UseGuards, +} from '@nestjs/common' +import { ApiOkResponse, ApiTags } from '@nestjs/swagger' + +import type { Logger } from '@island.is/logging' +import { LOGGER_PROVIDER } from '@island.is/logging' + +import { + CurrentHttpUser, + JwtAuthGuard, + RolesGuard, + RolesRules, +} from '@island.is/judicial-system/auth' +import { type User } from '@island.is/judicial-system/types' + +import { Case, CaseExistsGuard, CurrentCase } from '../case' +import { UpdateDefendantDto } from './dto/updateDefendant.dto' +import { CurrentDefendant } from './guards/defendant.decorator' +import { DefendantExistsGuard } from './guards/defendantExists.guard' +import { prisonSystemStaffUpdateRule } from './guards/rolesRules' +import { Defendant } from './models/defendant.model' +import { DefendantService } from './defendant.service' + +@Controller('api/case/:caseId/limitedAccess/defendant') +@ApiTags('limited access defendant') +@UseGuards(JwtAuthGuard, RolesGuard) +export class LimitedAccessDefendantController { + constructor( + private readonly defendantService: DefendantService, + @Inject(LOGGER_PROVIDER) private readonly logger: Logger, + ) {} + + @UseGuards(CaseExistsGuard, DefendantExistsGuard) + @RolesRules(prisonSystemStaffUpdateRule) + @Patch(':defendantId') + @ApiOkResponse({ + type: Defendant, + description: 'Updates a defendant', + }) + updateDefendant( + @Param('caseId') caseId: string, + @Param('defendantId') defendantId: string, + @CurrentHttpUser() user: User, + @CurrentCase() theCase: Case, + @CurrentDefendant() defendant: Defendant, + @Body() updateDto: Pick, + ): Promise { + this.logger.debug( + `Updating limitedAccess defendant ${defendantId} of case ${caseId}`, + ) + return this.defendantService.updateRequestCaseDefendant( + theCase, + defendant, + updateDto, + user, + ) + } +} 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 f795a4980c13..8c3f12c78c73 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 @@ -17,6 +17,7 @@ import { DefendantPlea, DefenderChoice, Gender, + PunishmentType, ServiceRequirement, SubpoenaType, } from '@island.is/judicial-system/types' @@ -201,6 +202,14 @@ export class Defendant extends Model { @ApiPropertyOptional({ type: Boolean }) isSentToPrisonAdmin?: boolean + @Column({ + type: DataType.ENUM, + allowNull: true, + values: Object.values(PunishmentType), + }) + @ApiPropertyOptional({ enum: PunishmentType }) + punishmentType?: PunishmentType + @HasMany(() => DefendantEventLog, { foreignKey: 'defendantId' }) @ApiPropertyOptional({ type: () => DefendantEventLog, isArray: true }) eventLogs?: DefendantEventLog[] diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/createTestingDefendantModule.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/createTestingDefendantModule.ts index e6ef10b79545..fe83d08485fc 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/createTestingDefendantModule.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/test/createTestingDefendantModule.ts @@ -18,6 +18,7 @@ import { CivilClaimantService } from '../civilClaimant.service' import { DefendantController } from '../defendant.controller' import { DefendantService } from '../defendant.service' import { InternalDefendantController } from '../internalDefendant.controller' +import { LimitedAccessDefendantController } from '../limitedAccessDefendant.controller' import { CivilClaimant } from '../models/civilClaimant.model' import { Defendant } from '../models/defendant.model' import { DefendantEventLog } from '../models/defendantEventLog.model' @@ -32,6 +33,7 @@ export const createTestingDefendantModule = async () => { imports: [ConfigModule.forRoot({ load: [sharedAuthModuleConfig] })], controllers: [ DefendantController, + LimitedAccessDefendantController, InternalDefendantController, CivilClaimantController, ], @@ -108,6 +110,11 @@ export const createTestingDefendantModule = async () => { InternalDefendantController, ) + const limitedAccessDefendantController = + defendantModule.get( + LimitedAccessDefendantController, + ) + const civilClaimantModel = await defendantModule.resolve< typeof CivilClaimant >(getModelToken(CivilClaimant)) @@ -129,6 +136,7 @@ export const createTestingDefendantModule = async () => { defendantService, defendantController, internalDefendantController, + limitedAccessDefendantController, civilClaimantService, civilClaimantController, civilClaimantModel, diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/limitedAccessDefendantController/updateDefendant.spec.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/limitedAccessDefendantController/updateDefendant.spec.ts new file mode 100644 index 000000000000..60224b93822a --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/defendant/test/limitedAccessDefendantController/updateDefendant.spec.ts @@ -0,0 +1,96 @@ +import { uuid } from 'uuidv4' + +import { MessageService } from '@island.is/judicial-system/message' +import { + CaseType, + PunishmentType, + User, +} from '@island.is/judicial-system/types' + +import { createTestingDefendantModule } from '../createTestingDefendantModule' + +import { Case } from '../../../case' +import { UpdateDefendantDto } from '../../dto/updateDefendant.dto' +import { Defendant } from '../../models/defendant.model' + +interface Then { + result: Defendant + error: Error +} + +type GivenWhenThen = ( + defendantUpdate: UpdateDefendantDto, + type: CaseType, + courtCaseNumber?: string, +) => Promise + +describe('LimitedAccessDefendantController - UpdateDefendant', () => { + const user = { id: uuid() } as User + const caseId = uuid() + const defendantId = uuid() + const defendant = { + id: defendantId, + caseId, + nationalId: uuid(), + defenderEmail: uuid(), + } as Defendant + + let mockMessageService: MessageService + let mockDefendantModel: typeof Defendant + let givenWhenThen: GivenWhenThen + + beforeEach(async () => { + const { messageService, defendantModel, limitedAccessDefendantController } = + await createTestingDefendantModule() + + mockMessageService = messageService + mockDefendantModel = defendantModel + + const mockUpdate = mockDefendantModel.update as jest.Mock + mockUpdate.mockRejectedValue(new Error('Some error')) + + givenWhenThen = async ( + defendantUpdate: UpdateDefendantDto, + type: CaseType, + courtCaseNumber?: string, + ) => { + const then = {} as Then + + await limitedAccessDefendantController + .updateDefendant( + caseId, + defendantId, + user, + { id: caseId, courtCaseNumber, type } as Case, + defendant, + defendantUpdate, + ) + .then((result) => (then.result = result)) + .catch((error) => (then.error = error)) + + return then + } + }) + + describe('defendant limited updated', () => { + const defendantUpdate = { punishmentType: PunishmentType.IMPRISONMENT } + const updatedDefendant = { ...defendant, ...defendantUpdate } + let then: Then + + beforeEach(async () => { + const mockUpdate = mockDefendantModel.update as jest.Mock + mockUpdate.mockResolvedValueOnce([1, [updatedDefendant]]) + + then = await givenWhenThen(defendantUpdate, CaseType.INDICTMENT) + }) + + it('should update the defendant without queuing', () => { + expect(mockDefendantModel.update).toHaveBeenCalledWith(defendantUpdate, { + where: { id: defendantId, caseId }, + returning: true, + }) + expect(then.result).toBe(updatedDefendant) + expect(mockMessageService.sendMessagesToQueue).not.toHaveBeenCalled() + }) + }) +}) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/limitedAccessDefendantController/updateDefendantRolesRules.spec.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/limitedAccessDefendantController/updateDefendantRolesRules.spec.ts new file mode 100644 index 000000000000..d53607986ffc --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/defendant/test/limitedAccessDefendantController/updateDefendantRolesRules.spec.ts @@ -0,0 +1,19 @@ +import { prisonSystemStaffUpdateRule } from '../../guards/rolesRules' +import { LimitedAccessDefendantController } from '../../limitedAccessDefendant.controller' + +describe('LimitedAccessDefendantController - Update defendant roles rules', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let rules: any[] + + beforeEach(() => { + rules = Reflect.getMetadata( + 'roles-rules', + LimitedAccessDefendantController.prototype.updateDefendant, + ) + }) + + it('should give permission to roles', () => { + expect(rules).toHaveLength(1) + expect(rules).toContain(prisonSystemStaffUpdateRule) + }) +}) diff --git a/apps/judicial-system/web/src/components/FormProvider/case.graphql b/apps/judicial-system/web/src/components/FormProvider/case.graphql index 00c390081d4a..4aec6d983d4b 100644 --- a/apps/judicial-system/web/src/components/FormProvider/case.graphql +++ b/apps/judicial-system/web/src/components/FormProvider/case.graphql @@ -35,6 +35,7 @@ query Case($input: CaseQueryInput!) { subpoenaType isSentToPrisonAdmin sentToPrisonAdminDate + punishmentType subpoenas { id created @@ -343,6 +344,7 @@ query Case($input: CaseQueryInput!) { verdictAppealDate isVerdictAppealDeadlineExpired subpoenaType + punishmentType subpoenas { id created diff --git a/apps/judicial-system/web/src/components/FormProvider/limitedAccessCase.graphql b/apps/judicial-system/web/src/components/FormProvider/limitedAccessCase.graphql index b61738d433a9..dea05680c538 100644 --- a/apps/judicial-system/web/src/components/FormProvider/limitedAccessCase.graphql +++ b/apps/judicial-system/web/src/components/FormProvider/limitedAccessCase.graphql @@ -47,6 +47,7 @@ query LimitedAccessCase($input: CaseQueryInput!) { subpoenaType isSentToPrisonAdmin sentToPrisonAdminDate + punishmentType subpoenas { id created diff --git a/apps/judicial-system/web/src/routes/Prison/IndictmentOverview/IndictmentOverview.strings.ts b/apps/judicial-system/web/src/routes/Prison/IndictmentOverview/IndictmentOverview.strings.ts index 3762e47532c0..36e6d6b19fbd 100644 --- a/apps/judicial-system/web/src/routes/Prison/IndictmentOverview/IndictmentOverview.strings.ts +++ b/apps/judicial-system/web/src/routes/Prison/IndictmentOverview/IndictmentOverview.strings.ts @@ -26,4 +26,36 @@ export const strings = defineMessages({ defaultMessage: 'Dómur', description: 'Titill á Dómur hluta', }, + punishmentTypeTitle: { + id: 'judicial.system.core:indictment_overview.punishmentType_title', + defaultMessage: 'Refsitegund', + description: 'Titill á Refsitegund', + }, + imprisonment: { + id: 'judicial.system.core:indictment_overview.punishmentType.imprisonment', + defaultMessage: 'Óskilorðsbundið', + description: + 'Notaður sem texti í óskilorðsbundið valmöguleika í Refsitegund', + }, + probation: { + id: 'judicial.system.core:indictment_overview.punishmentType.probation', + defaultMessage: 'Skilorðsbundið', + description: + 'Notaður sem texti í skilorðsbundið valmöguleika í Refsitegund', + }, + fine: { + id: 'judicial.system.core:court.indictment_overview.punishmentType.fine', + defaultMessage: 'Sekt', + description: 'Notaður sem texti í sekt valmöguleika í Refsitegund', + }, + indictmentRulingDecisionFine: { + id: 'judicial.system.core:court.indictment_overview.punishmentType.indictmentRulingDecisionFine', + defaultMessage: 'Viðurlagaákvörðun', + description: 'Notaður sem texti í viðurlagaákvörðun í Refsitegund', + }, + signedFineInvitation: { + id: 'judicial.system.core:court.indictment_overview.punishmentType.signedFineInvitation', + defaultMessage: 'Áritað sektarboð', + description: 'Notaður sem texti í sektarboð í Refsitegund', + }, }) diff --git a/apps/judicial-system/web/src/routes/Prison/IndictmentOverview/IndictmentOverview.tsx b/apps/judicial-system/web/src/routes/Prison/IndictmentOverview/IndictmentOverview.tsx index fa2d92993205..f4fd4f61128c 100644 --- a/apps/judicial-system/web/src/routes/Prison/IndictmentOverview/IndictmentOverview.tsx +++ b/apps/judicial-system/web/src/routes/Prison/IndictmentOverview/IndictmentOverview.tsx @@ -1,11 +1,13 @@ -import { useContext } from 'react' +import { useContext, useState } from 'react' import { useIntl } from 'react-intl' -import { Box, Text } from '@island.is/island-ui/core' +import { Box, RadioButton, Text } from '@island.is/island-ui/core' import * as constants from '@island.is/judicial-system/consts' import { formatDate } from '@island.is/judicial-system/formatters' +import { PunishmentType } from '@island.is/judicial-system/types' import { core } from '@island.is/judicial-system-web/messages' import { + BlueBox, FormContentContainer, FormContext, FormFooter, @@ -16,18 +18,40 @@ import { RenderFiles, } from '@island.is/judicial-system-web/src/components' import { CaseFileCategory } from '@island.is/judicial-system-web/src/graphql/schema' -import { useFileList } from '@island.is/judicial-system-web/src/utils/hooks' +import { + useDefendants, + useFileList, +} from '@island.is/judicial-system-web/src/utils/hooks' import { strings } from './IndictmentOverview.strings' const IndictmentOverview = () => { const { workingCase } = useContext(FormContext) const { formatMessage } = useIntl() + const { limitedAccessUpdateDefendant } = useDefendants() const { onOpen } = useFileList({ caseId: workingCase.id, }) + const { defendants } = workingCase + const defendant = + defendants && defendants?.length > 0 ? defendants[0] : undefined + const [selectedPunishmentType, setPunishmentType] = useState() + + const onChange = (updatedPunishmentType: PunishmentType) => { + setPunishmentType(updatedPunishmentType) + defendant && + limitedAccessUpdateDefendant({ + caseId: workingCase.id, + defendantId: defendant.id, + punishmentType: updatedPunishmentType, + }) + } + + const hasSetPunishmentType = (punishmentType: PunishmentType) => + !selectedPunishmentType && defendant?.punishmentType === punishmentType + return ( @@ -56,7 +80,7 @@ const IndictmentOverview = () => { - + {formatMessage(strings.verdictTitle)} @@ -69,6 +93,97 @@ const IndictmentOverview = () => { } /> + + + {formatMessage(strings.punishmentTypeTitle)} + + + + { + onChange(PunishmentType.IMPRISONMENT) + }} + large + backgroundColor="white" + label={formatMessage(strings.imprisonment)} + /> + + + { + onChange(PunishmentType.PROBATION) + }} + large + backgroundColor="white" + label={formatMessage(strings.probation)} + /> + + + { + onChange(PunishmentType.FINE) + }} + large + backgroundColor="white" + label={formatMessage(strings.fine)} + /> + + + { + onChange(PunishmentType.INDICTMENT_RULING_DECISION_FINE) + }} + large + backgroundColor="white" + label={formatMessage(strings.indictmentRulingDecisionFine)} + /> + + + { + onChange(PunishmentType.SIGNED_FINE_INVITATION) + }} + large + backgroundColor="white" + label={formatMessage(strings.signedFineInvitation)} + /> + + + diff --git a/apps/judicial-system/web/src/utils/hooks/useDefendants/index.ts b/apps/judicial-system/web/src/utils/hooks/useDefendants/index.ts index 49da94987d47..fad7f5a7efae 100644 --- a/apps/judicial-system/web/src/utils/hooks/useDefendants/index.ts +++ b/apps/judicial-system/web/src/utils/hooks/useDefendants/index.ts @@ -12,6 +12,7 @@ import { TempCase as Case } from '@island.is/judicial-system-web/src/types' import { useCreateDefendantMutation } from './createDefendant.generated' import { useDeleteDefendantMutation } from './deleteDefendant.generated' +import { useLimitedAccessUpdateDefendantMutation } from './limitedAccessUpdateDefendant.generated' import { useUpdateDefendantMutation } from './updateDefendant.generated' const useDefendants = () => { @@ -19,10 +20,15 @@ const useDefendants = () => { const [createDefendantMutation, { loading: isCreatingDefendant }] = useCreateDefendantMutation() + const [deleteDefendantMutation] = useDeleteDefendantMutation() + const [updateDefendantMutation, { loading: isUpdatingDefendant }] = useUpdateDefendantMutation() + const [limitedAccessUpdateDefendantMutation] = + useLimitedAccessUpdateDefendantMutation() + const createDefendant = useCallback( async (defendant: CreateDefendantInput) => { try { @@ -78,6 +84,24 @@ const useDefendants = () => { [formatMessage, updateDefendantMutation], ) + const limitedAccessUpdateDefendant = useCallback( + async (updateDefendant: UpdateDefendantInput) => { + try { + const { data } = await limitedAccessUpdateDefendantMutation({ + variables: { + input: updateDefendant, + }, + }) + + return Boolean(data) + } catch (error) { + toast.error(formatMessage(errors.updateDefendant)) + return false + } + }, + [formatMessage, limitedAccessUpdateDefendantMutation], + ) + const updateDefendantState = useCallback( ( update: UpdateDefendantInput, @@ -119,6 +143,7 @@ const useDefendants = () => { createDefendant, deleteDefendant, updateDefendant, + limitedAccessUpdateDefendant, isUpdatingDefendant, updateDefendantState, setAndSendDefendantToServer, diff --git a/apps/judicial-system/web/src/utils/hooks/useDefendants/limitedAccessUpdateDefendant.graphql b/apps/judicial-system/web/src/utils/hooks/useDefendants/limitedAccessUpdateDefendant.graphql new file mode 100644 index 000000000000..a2fe721993a5 --- /dev/null +++ b/apps/judicial-system/web/src/utils/hooks/useDefendants/limitedAccessUpdateDefendant.graphql @@ -0,0 +1,5 @@ +mutation LimitedAccessUpdateDefendant($input: UpdateDefendantInput!) { + limitedAccessUpdateDefendant(input: $input) { + id + } +} diff --git a/libs/judicial-system/types/src/index.ts b/libs/judicial-system/types/src/index.ts index 82a11272ee06..a10d648c52a1 100644 --- a/libs/judicial-system/types/src/index.ts +++ b/libs/judicial-system/types/src/index.ts @@ -7,6 +7,7 @@ export { DefendantPlea, ServiceRequirement, ServiceStatus, + PunishmentType, isSuccessfulServiceStatus, isFailedServiceStatus, } from './lib/defendant' diff --git a/libs/judicial-system/types/src/lib/defendant.ts b/libs/judicial-system/types/src/lib/defendant.ts index 03b843eb6d46..2bd9c1be3953 100644 --- a/libs/judicial-system/types/src/lib/defendant.ts +++ b/libs/judicial-system/types/src/lib/defendant.ts @@ -36,6 +36,14 @@ export enum ServiceStatus { EXPIRED = 'EXPIRED', // If a subpoena expires } +export enum PunishmentType { + IMPRISONMENT = 'IMPRISONMENT', + PROBATION = 'PROBATION', + FINE = 'FINE', + INDICTMENT_RULING_DECISION_FINE = 'INDICTMENT_RULING_DECISION_FINE', + SIGNED_FINE_INVITATION = 'SIGNED_FINE_INVITATION', +} + export const successfulServiceStatus: string[] = [ ServiceStatus.ELECTRONICALLY, ServiceStatus.DEFENDER,