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 099c3bf98b10..806cb2c44ee2 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 @@ -270,6 +270,25 @@ export class BackendService extends DataSource<{ req: Request }> { return this.post(`case/${id}/file`, createFile) } + createDefendantCaseFile( + id: string, + createFile: unknown, + defendantId: string, + ): Promise { + return this.post(`case/${id}/defendant/${defendantId}/file`, createFile) + } + + createCivilClaimantCaseFile( + id: string, + createFile: unknown, + civilClaimantId: string, + ): Promise { + return this.post( + `case/${id}/civilClaimant/${civilClaimantId}/file`, + createFile, + ) + } + getCaseFileSignedUrl( caseId: string, id: string, @@ -439,6 +458,28 @@ export class BackendService extends DataSource<{ req: Request }> { return this.post(`case/${id}/limitedAccess/file`, createFile) } + limitedAccessCreateDefendantCaseFile( + id: string, + createFile: unknown, + defendantId: string, + ): Promise { + return this.post( + `case/${id}/limitedAccess$/defendant/${defendantId}/file`, + createFile, + ) + } + + limitedAccessCreateCivilClaimantCaseFile( + id: string, + createFile: unknown, + civilClaimantId: string, + ): Promise { + return this.post( + `case/${id}/limitedAccess$/civilClaimant/${civilClaimantId}/file`, + createFile, + ) + } + limitedAccessGetCaseFileSignedUrl( caseId: string, id: string, diff --git a/apps/judicial-system/api/src/app/modules/file/dto/createCivilClaimantFile.input.ts b/apps/judicial-system/api/src/app/modules/file/dto/createCivilClaimantFile.input.ts new file mode 100644 index 000000000000..a66e171df826 --- /dev/null +++ b/apps/judicial-system/api/src/app/modules/file/dto/createCivilClaimantFile.input.ts @@ -0,0 +1,12 @@ +import { Allow } from 'class-validator' + +import { Field, ID, InputType } from '@nestjs/graphql' + +import { CreateFileInput } from './createFile.input' + +@InputType() +export class CreateCivilClaimantFileInput extends CreateFileInput { + @Allow() + @Field(() => ID) + readonly civilClaimantId!: string +} diff --git a/apps/judicial-system/api/src/app/modules/file/dto/createDefendantFile.input.ts b/apps/judicial-system/api/src/app/modules/file/dto/createDefendantFile.input.ts new file mode 100644 index 000000000000..540e5ce719c3 --- /dev/null +++ b/apps/judicial-system/api/src/app/modules/file/dto/createDefendantFile.input.ts @@ -0,0 +1,12 @@ +import { Allow } from 'class-validator' + +import { Field, ID, InputType } from '@nestjs/graphql' + +import { CreateFileInput } from './createFile.input' + +@InputType() +export class CreateDefendantFileInput extends CreateFileInput { + @Allow() + @Field(() => ID) + readonly defendantId!: string +} diff --git a/apps/judicial-system/api/src/app/modules/file/file.resolver.ts b/apps/judicial-system/api/src/app/modules/file/file.resolver.ts index 45990fbb7dc2..e59c2724125a 100644 --- a/apps/judicial-system/api/src/app/modules/file/file.resolver.ts +++ b/apps/judicial-system/api/src/app/modules/file/file.resolver.ts @@ -15,6 +15,8 @@ import { import type { User } from '@island.is/judicial-system/types' import { BackendService } from '../backend' +import { CreateCivilClaimantFileInput } from './dto/createCivilClaimantFile.input' +import { CreateDefendantFileInput } from './dto/createDefendantFile.input' import { CreateFileInput } from './dto/createFile.input' import { CreatePresignedPostInput } from './dto/createPresignedPost.input' import { DeleteFileInput } from './dto/deleteFile.input' @@ -77,6 +79,54 @@ export class FileResolver { ) } + @Mutation(() => CaseFile) + createDefendantFile( + @Args('input', { type: () => CreateDefendantFileInput }) + input: CreateDefendantFileInput, + @CurrentGraphQlUser() user: User, + @Context('dataSources') + { backendService }: { backendService: BackendService }, + ): Promise { + const { caseId, defendantId, ...createFile } = input + + this.logger.debug( + `Creating a file for case ${caseId} and defendant ${defendantId}`, + ) + + return this.auditTrailService.audit( + user.id, + AuditedAction.CREATE_FILE, + backendService.createDefendantCaseFile(caseId, createFile, defendantId), + (file) => file.id, + ) + } + + @Mutation(() => CaseFile) + createCivilClaimantFile( + @Args('input', { type: () => CreateCivilClaimantFileInput }) + input: CreateCivilClaimantFileInput, + @CurrentGraphQlUser() user: User, + @Context('dataSources') + { backendService }: { backendService: BackendService }, + ): Promise { + const { caseId, civilClaimantId, ...createFile } = input + + this.logger.debug( + `Creating a file for case ${caseId} and civil claimant ${civilClaimantId}`, + ) + + return this.auditTrailService.audit( + user.id, + AuditedAction.CREATE_FILE, + backendService.createCivilClaimantCaseFile( + caseId, + createFile, + civilClaimantId, + ), + (file) => file.id, + ) + } + @Query(() => SignedUrl, { nullable: true }) getSignedUrl( @Args('input', { type: () => GetSignedUrlInput }) diff --git a/apps/judicial-system/api/src/app/modules/file/limitedAccessFile.resolver.ts b/apps/judicial-system/api/src/app/modules/file/limitedAccessFile.resolver.ts index ecd4c88ec4bf..af599b9bff15 100644 --- a/apps/judicial-system/api/src/app/modules/file/limitedAccessFile.resolver.ts +++ b/apps/judicial-system/api/src/app/modules/file/limitedAccessFile.resolver.ts @@ -15,6 +15,8 @@ import { import type { User } from '@island.is/judicial-system/types' import { BackendService } from '../backend' +import { CreateCivilClaimantFileInput } from './dto/createCivilClaimantFile.input' +import { CreateDefendantFileInput } from './dto/createDefendantFile.input' import { CreateFileInput } from './dto/createFile.input' import { CreatePresignedPostInput } from './dto/createPresignedPost.input' import { DeleteFileInput } from './dto/deleteFile.input' @@ -76,6 +78,58 @@ export class LimitedAccessFileResolver { ) } + @Mutation(() => CaseFile) + limitedAccessCreateDefendantFile( + @Args('input', { type: () => CreateDefendantFileInput }) + input: CreateDefendantFileInput, + @CurrentGraphQlUser() user: User, + @Context('dataSources') + { backendService }: { backendService: BackendService }, + ): Promise { + const { caseId, defendantId, ...createFile } = input + + this.logger.debug( + `Creating a file for case ${caseId} and defendant ${defendantId}`, + ) + + return this.auditTrailService.audit( + user.id, + AuditedAction.CREATE_FILE, + backendService.limitedAccessCreateDefendantCaseFile( + caseId, + createFile, + defendantId, + ), + (file) => file.id, + ) + } + + @Mutation(() => CaseFile) + limitedAccessCreateCivilClaimantFile( + @Args('input', { type: () => CreateCivilClaimantFileInput }) + input: CreateCivilClaimantFileInput, + @CurrentGraphQlUser() user: User, + @Context('dataSources') + { backendService }: { backendService: BackendService }, + ): Promise { + const { caseId, civilClaimantId, ...createFile } = input + + this.logger.debug( + `Creating a file for case ${caseId} and civil claimant ${civilClaimantId}`, + ) + + return this.auditTrailService.audit( + user.id, + AuditedAction.CREATE_FILE, + backendService.limitedAccessCreateCivilClaimantCaseFile( + caseId, + createFile, + civilClaimantId, + ), + (file) => file.id, + ) + } + @Query(() => SignedUrl, { nullable: true }) limitedAccessGetSignedUrl( @Args('input', { type: () => GetSignedUrlInput }) diff --git a/apps/judicial-system/backend/migrations/20241122091513-update-case-file.js b/apps/judicial-system/backend/migrations/20241122091513-update-case-file.js new file mode 100644 index 000000000000..46ff67fc695a --- /dev/null +++ b/apps/judicial-system/backend/migrations/20241122091513-update-case-file.js @@ -0,0 +1,44 @@ +'use strict' + +module.exports = { + up: (queryInterface, Sequelize) => { + return queryInterface.sequelize.transaction(async (t) => { + await queryInterface.addColumn( + 'case_file', + 'defendant_id', + { + type: Sequelize.UUID, + references: { + model: 'defendant', + key: 'id', + }, + allowNull: true, + }, + { transaction: t }, + ) + await queryInterface.addColumn( + 'case_file', + 'civil_claimant_id', + { + type: Sequelize.UUID, + references: { + model: 'civil_claimant', + key: 'id', + }, + allowNull: true, + }, + { transaction: t }, + ) + }) + }, + down: (queryInterface) => { + return queryInterface.sequelize.transaction(async (t) => { + await queryInterface.removeColumn('case_file', 'civil_claimant_id', { + transaction: t, + }) + await queryInterface.removeColumn('case_file', 'defendant_id', { + transaction: t, + }) + }) + }, +} 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 9a671f3ba761..d59a3a2e5684 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 @@ -87,6 +87,7 @@ interface UpdateDateLog { date?: Date location?: string } + export interface UpdateCase extends Pick< Case, diff --git a/apps/judicial-system/backend/src/app/modules/file/file.controller.ts b/apps/judicial-system/backend/src/app/modules/file/file.controller.ts index 54626a9f5700..ca960a15f389 100644 --- a/apps/judicial-system/backend/src/app/modules/file/file.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/file/file.controller.ts @@ -49,11 +49,14 @@ import { CurrentCase, } from '../case' import { MergedCaseExistsGuard } from '../case/guards/mergedCaseExists.guard' +import { CivilClaimantExistsGuard, DefendantExistsGuard } from '../defendant' import { CreateFileDto } from './dto/createFile.dto' import { CreatePresignedPostDto } from './dto/createPresignedPost.dto' import { UpdateFilesDto } from './dto/updateFile.dto' import { CurrentCaseFile } from './guards/caseFile.decorator' import { CaseFileExistsGuard } from './guards/caseFileExists.guard' +import { CreateCivilClaimantCaseFileGuard } from './guards/createCivilClaimantCaseFile.guard' +import { CreateDefendantCaseFileGuard } from './guards/createDefendantCaseFile.guard' import { ViewCaseFileGuard } from './guards/viewCaseFile.guard' import { DeleteFileResponse } from './models/deleteFile.response' import { CaseFile } from './models/file.model' @@ -126,6 +129,68 @@ export class FileController { return this.fileService.createCaseFile(theCase, createFile, user) } + @UseGuards( + RolesGuard, + CaseExistsGuard, + DefendantExistsGuard, + CaseWriteGuard, + CreateDefendantCaseFileGuard, + ) + @RolesRules(publicProsecutorStaffRule) + @Post('defendant/:defendantId/file') + @ApiCreatedResponse({ + type: CaseFile, + description: 'Creates a new case file connected to a defendant', + }) + async createDefendantCaseFile( + @Param('caseId') caseId: string, + @Param('defendantId') defendantId: string, + @CurrentHttpUser() user: User, + @CurrentCase() theCase: Case, + @Body() createFile: CreateFileDto, + ): Promise { + this.logger.debug( + `Creating a file for case ${caseId} for defendant ${defendantId}`, + ) + + return this.fileService.createCaseFile( + theCase, + { ...createFile, defendantId }, + user, + ) + } + + @UseGuards( + RolesGuard, + CaseExistsGuard, + CivilClaimantExistsGuard, + CaseWriteGuard, + CreateCivilClaimantCaseFileGuard, + ) + @RolesRules() // This endpoint is not used by any role at the moment + @Post('civilClaimant/:civilClaimantId/file') + @ApiCreatedResponse({ + type: CaseFile, + description: 'Creates a new case file connected to a civil claimant', + }) + async createCivilClaimantCaseFile( + @Param('caseId') caseId: string, + @Param('civilClaimantId') civilClaimantId: string, + @CurrentHttpUser() user: User, + @CurrentCase() theCase: Case, + @Body() createFile: CreateFileDto, + ): Promise { + this.logger.debug( + `Creating a file for case ${caseId} for civil claimant ${civilClaimantId}`, + ) + + return this.fileService.createCaseFile( + theCase, + { ...createFile, civilClaimantId }, + user, + ) + } + @UseGuards( RolesGuard, CaseExistsGuard, diff --git a/apps/judicial-system/backend/src/app/modules/file/file.service.ts b/apps/judicial-system/backend/src/app/modules/file/file.service.ts index 3aa552b87baf..397f488a5c0c 100644 --- a/apps/judicial-system/backend/src/app/modules/file/file.service.ts +++ b/apps/judicial-system/backend/src/app/modules/file/file.service.ts @@ -43,6 +43,11 @@ import { SignedUrl } from './models/signedUrl.model' import { UploadFileToCourtResponse } from './models/uploadFileToCourt.response' import { fileModuleConfig } from './file.config' +interface CreateFile extends CreateFileDto { + defendantId?: string + civilClaimantId?: string +} + // File keys have the following format: // // // As uuid-s have length 36, the filename starts at position 82 in the key. @@ -337,7 +342,7 @@ export class FileService { async createCaseFile( theCase: Case, - createFile: CreateFileDto, + createFile: CreateFile, user: User, ): Promise { const { key } = createFile diff --git a/apps/judicial-system/backend/src/app/modules/file/guards/createCivilClaimantCaseFile.guard.ts b/apps/judicial-system/backend/src/app/modules/file/guards/createCivilClaimantCaseFile.guard.ts new file mode 100644 index 000000000000..eef72619f4d9 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/file/guards/createCivilClaimantCaseFile.guard.ts @@ -0,0 +1,25 @@ +import { + CanActivate, + ExecutionContext, + ForbiddenException, + Injectable, +} from '@nestjs/common' + +import { CaseFileCategory } from '@island.is/judicial-system/types' + +@Injectable() +export class CreateCivilClaimantCaseFileGuard implements CanActivate { + canActivate(context: ExecutionContext): boolean { + const request = context.switchToHttp().getRequest() + + const caseFileCategory: CaseFileCategory = request.body?.category + + if (caseFileCategory !== CaseFileCategory.CIVIL_CLAIM) { + throw new ForbiddenException( + `Forbidden for case file category ${caseFileCategory}`, + ) + } + + return true + } +} diff --git a/apps/judicial-system/backend/src/app/modules/file/guards/createDefendantCaseFile.guard.ts b/apps/judicial-system/backend/src/app/modules/file/guards/createDefendantCaseFile.guard.ts new file mode 100644 index 000000000000..de4aa6818b9d --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/file/guards/createDefendantCaseFile.guard.ts @@ -0,0 +1,25 @@ +import { + CanActivate, + ExecutionContext, + ForbiddenException, + Injectable, +} from '@nestjs/common' + +import { CaseFileCategory } from '@island.is/judicial-system/types' + +@Injectable() +export class CreateDefendantCaseFileGuard implements CanActivate { + canActivate(context: ExecutionContext): boolean { + const request = context.switchToHttp().getRequest() + + const caseFileCategory: CaseFileCategory = request.body?.category + + if (caseFileCategory !== CaseFileCategory.SENT_TO_PRISON_ADMIN_FILE) { + throw new ForbiddenException( + `Forbidden for case file category ${caseFileCategory}`, + ) + } + + return true + } +} diff --git a/apps/judicial-system/backend/src/app/modules/file/guards/limitedAccessWriteCaseFile.guard.ts b/apps/judicial-system/backend/src/app/modules/file/guards/limitedAccessWriteCaseFile.guard.ts index 42fb0abf30b7..ecb4f981ed0e 100644 --- a/apps/judicial-system/backend/src/app/modules/file/guards/limitedAccessWriteCaseFile.guard.ts +++ b/apps/judicial-system/backend/src/app/modules/file/guards/limitedAccessWriteCaseFile.guard.ts @@ -21,16 +21,17 @@ export class LimitedAccessWriteCaseFileGuard implements CanActivate { const request = context.switchToHttp().getRequest() const user: User = request.user + + if (!user) { + throw new InternalServerErrorException('Missing user') + } + const theCase: Case = request.case if (!theCase) { throw new InternalServerErrorException('Missing case') } - if (!user) { - throw new InternalServerErrorException('Missing user') - } - // The case file category is either in the request body (creating case file) // or in the case file (deleting case file) const caseFileCategory: CaseFileCategory = diff --git a/apps/judicial-system/backend/src/app/modules/file/guards/test/limitedAccessWriteCaseFileGuard.spec.ts b/apps/judicial-system/backend/src/app/modules/file/guards/test/limitedAccessWriteCaseFileGuard.spec.ts index 5b5854214b6f..712162f28265 100644 --- a/apps/judicial-system/backend/src/app/modules/file/guards/test/limitedAccessWriteCaseFileGuard.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/file/guards/test/limitedAccessWriteCaseFileGuard.spec.ts @@ -48,7 +48,7 @@ describe('LimitedAccess Write Case File Guard', () => { }) describe.each(allowedCaseFileCategories)( - 'a defender can view %s', + 'a defender can write %s', (category) => { describe('when creating a case file', () => { let then: Then @@ -93,7 +93,7 @@ describe('LimitedAccess Write Case File Guard', () => { (category) => !allowedCaseFileCategories.includes(category as CaseFileCategory), ), - )('a defender can not view %s', (category) => { + )('a defender can not write %s', (category) => { describe('when creating a case file', () => { let then: Then @@ -137,7 +137,7 @@ describe('LimitedAccess Write Case File Guard', () => { Object.keys(UserRole).filter((role) => role !== UserRole.DEFENDER), )('role %s', (role) => { describe.each(Object.keys(CaseFileCategory))( - 'can not view %s', + 'can not write %s', (category) => { describe('when creating a case file', () => { let then: Then @@ -225,7 +225,7 @@ describe('LimitedAccess Write Case File Guard', () => { }) }) - describe('a defender can view DEFENDANT_CASE_FILE in indictment cases', () => { + describe('a defender can write DEFENDANT_CASE_FILE in indictment cases', () => { describe('when creating a case file', () => { let then: Then @@ -265,7 +265,7 @@ describe('LimitedAccess Write Case File Guard', () => { describe.each( Object.keys(CaseType).filter((ct) => ct !== CaseType.INDICTMENT), - )('a defender can not view DEFENDANT_CASE_FILE in %s cases', (caseType) => { + )('a defender can not write DEFENDANT_CASE_FILE in %s cases', (caseType) => { describe('when creating a case file', () => { let then: Then diff --git a/apps/judicial-system/backend/src/app/modules/file/limitedAccessFile.controller.ts b/apps/judicial-system/backend/src/app/modules/file/limitedAccessFile.controller.ts index 3c1605da9c12..d6399c0a36a4 100644 --- a/apps/judicial-system/backend/src/app/modules/file/limitedAccessFile.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/file/limitedAccessFile.controller.ts @@ -37,10 +37,16 @@ import { LimitedAccessCaseExistsGuard, } from '../case' import { MergedCaseExistsGuard } from '../case/guards/mergedCaseExists.guard' +import { + CivilClaimant, + CivilClaimantExistsGuard, + CurrentCivilClaimant, +} from '../defendant' import { CreateFileDto } from './dto/createFile.dto' import { CreatePresignedPostDto } from './dto/createPresignedPost.dto' import { CurrentCaseFile } from './guards/caseFile.decorator' import { CaseFileExistsGuard } from './guards/caseFileExists.guard' +import { CreateCivilClaimantCaseFileGuard } from './guards/createCivilClaimantCaseFile.guard' import { LimitedAccessViewCaseFileGuard } from './guards/limitedAccessViewCaseFile.guard' import { LimitedAccessWriteCaseFileGuard } from './guards/limitedAccessWriteCaseFile.guard' import { DeleteFileResponse } from './models/deleteFile.response' @@ -108,6 +114,41 @@ export class LimitedAccessFileController { return this.fileService.createCaseFile(theCase, createFile, user) } + // This endpoint is not used by any role at the moment + // Before using the endpoint we should probably change + // the createCaseFile endpoint to createDefendantCaseFile and + // limit file creation to defendant's and spokesperson's clients + @UseGuards( + new CaseTypeGuard([...indictmentCases]), + CivilClaimantExistsGuard, + CaseWriteGuard, + LimitedAccessWriteCaseFileGuard, + CreateCivilClaimantCaseFileGuard, + ) + @RolesRules() + @Post('civilClaimant/:civilClaimantId/file') + @ApiCreatedResponse({ + type: CaseFile, + description: 'Creates a new case file for a civil claimant', + }) + createCivilClaimantCaseFile( + @Param('caseId') caseId: string, + @Param('civilClaimantId') civilClaimantId: string, + @CurrentHttpUser() user: User, + @CurrentCase() theCase: Case, + @Body() createFile: CreateFileDto, + ): Promise { + this.logger.debug( + `Creating a file for case ${caseId} and civil claimant ${civilClaimantId}`, + ) + + return this.fileService.createCaseFile( + theCase, + { ...createFile, civilClaimantId }, + user, + ) + } + @UseGuards( CaseReadGuard, MergedCaseExistsGuard, diff --git a/apps/judicial-system/backend/src/app/modules/file/models/file.model.ts b/apps/judicial-system/backend/src/app/modules/file/models/file.model.ts index b07a262aa8fa..9d3a68677b33 100644 --- a/apps/judicial-system/backend/src/app/modules/file/models/file.model.ts +++ b/apps/judicial-system/backend/src/app/modules/file/models/file.model.ts @@ -17,6 +17,7 @@ import { // TODO Find a way to import from an index file import { Case } from '../../case/models/case.model' +import { CivilClaimant, Defendant } from '../../defendant' @Table({ tableName: 'case_file', @@ -45,6 +46,16 @@ export class CaseFile extends Model { @ApiProperty({ type: String }) caseId!: string + @ForeignKey(() => Defendant) + @Column({ type: DataType.UUID, allowNull: true }) + @ApiProperty({ type: String }) + defendantId?: string + + @ForeignKey(() => CivilClaimant) + @Column({ type: DataType.UUID, allowNull: true }) + @ApiProperty({ type: String }) + civilClaimantId?: string + @Column({ type: DataType.STRING, allowNull: false }) @ApiProperty({ type: String }) name!: string diff --git a/apps/judicial-system/web/src/routes/PublicProsecutor/Indictments/SendToPrisonAdmin/SendToPrisonAdmin.tsx b/apps/judicial-system/web/src/routes/PublicProsecutor/Indictments/SendToPrisonAdmin/SendToPrisonAdmin.tsx index 84ba3f0e18ad..4b2962c7c8fc 100644 --- a/apps/judicial-system/web/src/routes/PublicProsecutor/Indictments/SendToPrisonAdmin/SendToPrisonAdmin.tsx +++ b/apps/judicial-system/web/src/routes/PublicProsecutor/Indictments/SendToPrisonAdmin/SendToPrisonAdmin.tsx @@ -2,9 +2,9 @@ import { FC, useCallback, useContext, useState } from 'react' import { useIntl } from 'react-intl' import { useParams, useRouter } from 'next/navigation' -import { Box, UploadFile } from '@island.is/island-ui/core' +import { Box, InputFileUpload, UploadFile } from '@island.is/island-ui/core' import { PUBLIC_PROSECUTOR_STAFF_INDICTMENT_OVERVIEW_ROUTE } from '@island.is/judicial-system/consts' -import { core } from '@island.is/judicial-system-web/messages' +import { core, errors } from '@island.is/judicial-system-web/messages' import { CourtCaseInfo, FormContentContainer, @@ -37,7 +37,10 @@ const SendToPrisonAdmin: FC = () => { const [uploadFileError, setUploadFileError] = useState() const router = useRouter() const { defendantId } = useParams<{ caseId: string; defendantId: string }>() - const { handleUpload, handleRemove } = useS3Upload(workingCase.id) + const { handleUpload, handleRemove } = useS3Upload( + workingCase.id, + defendantId, + ) const { updateDefendant, isUpdatingDefendant } = useDefendants() const { uploadFiles, removeUploadFile, addUploadFiles, updateUploadFile } = useUploadFiles() @@ -65,16 +68,15 @@ const SendToPrisonAdmin: FC = () => { isSentToPrisonAdmin: true, }) - // TODO: UNCOMMENT WHEN THIS FEATURE IS READY - // const uploadResult = await handleUpload( - // uploadFiles.filter((file) => file.percent === 0), - // updateUploadFile, - // ) + const uploadResult = await handleUpload( + uploadFiles.filter((file) => file.percent === 0), + updateUploadFile, + ) - // if (uploadResult !== 'ALL_SUCCEEDED') { - // setUploadFileError(formatMessage(errors.uploadFailed)) - // return - // } + if (uploadResult !== 'ALL_SUCCEEDED') { + setUploadFileError(formatMessage(errors.uploadFailed)) + return + } router.push( `${PUBLIC_PROSECUTOR_STAFF_INDICTMENT_OVERVIEW_ROUTE}/${workingCase.id}`, @@ -119,8 +121,6 @@ const SendToPrisonAdmin: FC = () => { description={formatMessage(strings.fileUploadDescription)} /> - {/* NOTE: This is temporarily disabled while we work on this - upload feature. @@ -134,7 +134,7 @@ const SendToPrisonAdmin: FC = () => { buttonLabel={formatMessage(core.uploadBoxButtonLabel)} onChange={handleFileUpload} onRemove={handleRemoveFile} - /> */} + /> diff --git a/apps/judicial-system/web/src/utils/hooks/useS3Upload/createCivilClaimantFile.graphql b/apps/judicial-system/web/src/utils/hooks/useS3Upload/createCivilClaimantFile.graphql new file mode 100644 index 000000000000..b45b6b8b1eb0 --- /dev/null +++ b/apps/judicial-system/web/src/utils/hooks/useS3Upload/createCivilClaimantFile.graphql @@ -0,0 +1,5 @@ +mutation CreateCivilClaimantFile($input: CreateCivilClaimantFileInput!) { + createCivilClaimantFile(input: $input) { + id + } +} diff --git a/apps/judicial-system/web/src/utils/hooks/useS3Upload/createDefendantFile.graphql b/apps/judicial-system/web/src/utils/hooks/useS3Upload/createDefendantFile.graphql new file mode 100644 index 000000000000..be964c4e573d --- /dev/null +++ b/apps/judicial-system/web/src/utils/hooks/useS3Upload/createDefendantFile.graphql @@ -0,0 +1,5 @@ +mutation CreateDefendantFile($input: CreateDefendantFileInput!) { + createDefendantFile(input: $input) { + id + } +} diff --git a/apps/judicial-system/web/src/utils/hooks/useS3Upload/limitedAccessCreateCivilClaimantFile.graphql b/apps/judicial-system/web/src/utils/hooks/useS3Upload/limitedAccessCreateCivilClaimantFile.graphql new file mode 100644 index 000000000000..b5aa0de564cb --- /dev/null +++ b/apps/judicial-system/web/src/utils/hooks/useS3Upload/limitedAccessCreateCivilClaimantFile.graphql @@ -0,0 +1,7 @@ +mutation LimitedAccessCreateCivilClaimantFile( + $input: CreateCivilClaimantFileInput! +) { + limitedAccessCreateCivilClaimantFile(input: $input) { + id + } +} diff --git a/apps/judicial-system/web/src/utils/hooks/useS3Upload/limitedAccessCreateDefendantFile.graphql b/apps/judicial-system/web/src/utils/hooks/useS3Upload/limitedAccessCreateDefendantFile.graphql new file mode 100644 index 000000000000..3da010ab1aa9 --- /dev/null +++ b/apps/judicial-system/web/src/utils/hooks/useS3Upload/limitedAccessCreateDefendantFile.graphql @@ -0,0 +1,5 @@ +mutation LimitedAccessCreateDefendantFile($input: CreateDefendantFileInput!) { + limitedAccessCreateDefendantFile(input: $input) { + id + } +} diff --git a/apps/judicial-system/web/src/utils/hooks/useS3Upload/limitedAccessCreateFile.graphql b/apps/judicial-system/web/src/utils/hooks/useS3Upload/limitedAccessCreateFile.graphql index 09f3b80fa2e3..a2ccee8e28ea 100644 --- a/apps/judicial-system/web/src/utils/hooks/useS3Upload/limitedAccessCreateFile.graphql +++ b/apps/judicial-system/web/src/utils/hooks/useS3Upload/limitedAccessCreateFile.graphql @@ -1,12 +1,5 @@ mutation LimitedAccessCreateFile($input: CreateFileInput!) { limitedAccessCreateFile(input: $input) { id - created - caseId - name - key - size - category - policeCaseNumber } } diff --git a/apps/judicial-system/web/src/utils/hooks/useS3Upload/useS3Upload.ts b/apps/judicial-system/web/src/utils/hooks/useS3Upload/useS3Upload.ts index d47a6ad001f6..28cfdb3f621e 100644 --- a/apps/judicial-system/web/src/utils/hooks/useS3Upload/useS3Upload.ts +++ b/apps/judicial-system/web/src/utils/hooks/useS3Upload/useS3Upload.ts @@ -7,9 +7,18 @@ import { UserContext } from '@island.is/judicial-system-web/src/components' import { CaseFile, CaseFileCategory, + CreateFileInput, PresignedPost, } from '@island.is/judicial-system-web/src/graphql/schema' +import { + CreateCivilClaimantFileMutation, + useCreateCivilClaimantFileMutation, +} from './createCivilClaimantFile.generated' +import { + CreateDefendantFileMutation, + useCreateDefendantFileMutation, +} from './createDefendantFile.generated' import { CreateFileMutation, useCreateFileMutation, @@ -22,6 +31,14 @@ import { DeleteFileMutation, useDeleteFileMutation, } from './deleteFile.generated' +import { + LimitedAccessCreateCivilClaimantFileMutation, + useLimitedAccessCreateCivilClaimantFileMutation, +} from './limitedAccessCreateCivilClaimantFile.generated' +import { + LimitedAccessCreateDefendantFileMutation, + useLimitedAccessCreateDefendantFileMutation, +} from './limitedAccessCreateDefendantFile.generated' import { LimitedAccessCreateFileMutation, useLimitedAccessCreateFileMutation, @@ -189,7 +206,11 @@ const uploadToS3 = ( return promise } -const useS3Upload = (caseId: string) => { +const useS3Upload = ( + caseId: string, + defendantId?: string, + civilClaimantId?: string, +) => { const { limitedAccess } = useContext(UserContext) const { formatMessage } = useIntl() @@ -198,6 +219,12 @@ const useS3Upload = (caseId: string) => { useLimitedAccessCreatePresignedPostMutation() const [createFile] = useCreateFileMutation() const [limitedAccessCreateFile] = useLimitedAccessCreateFileMutation() + const [createDefendantFile] = useCreateDefendantFileMutation() + const [limitedAccessCreateDefendantFile] = + useLimitedAccessCreateDefendantFileMutation() + const [createCivilClaimantFile] = useCreateCivilClaimantFileMutation() + const [limitedAccessCreateCivilClaimantFile] = + useLimitedAccessCreateCivilClaimantFileMutation() const [deleteFile] = useDeleteFileMutation() const [limitedAccessDeleteFile] = useLimitedAccessDeleteFileMutation() const [uploadPoliceCaseFile] = useUploadPoliceCaseFileMutation() @@ -239,37 +266,106 @@ const useS3Upload = (caseId: string) => { const addFileToCaseState = useCallback( async (file: TUploadFile) => { - const mutation = limitedAccess ? limitedAccessCreateFile : createFile + const addCaseFileToCaseState = async (input: CreateFileInput) => { + const mutation = limitedAccess ? limitedAccessCreateFile : createFile - const { data } = await mutation({ - variables: { - input: { - caseId, - type: file.type ?? '', - key: file.key ?? '', - size: file.size ?? 0, - category: file.category, - policeCaseNumber: file.policeCaseNumber, - chapter: file.chapter, - orderWithinChapter: file.orderWithinChapter, - displayDate: file.displayDate, - policeFileId: file.policeFileId, - userGeneratedFilename: file.userGeneratedFilename, - }, - }, - }) + const { data } = await mutation({ variables: { input } }) - const createdFile = limitedAccess - ? (data as LimitedAccessCreateFileMutation)?.limitedAccessCreateFile - : (data as CreateFileMutation)?.createFile + const createdFile = limitedAccess + ? (data as LimitedAccessCreateFileMutation)?.limitedAccessCreateFile + : (data as CreateFileMutation)?.createFile - if (!createdFile?.id) { - throw Error('Failed to add file to case') + if (!createdFile?.id) { + throw Error('Failed to add file to case') + } + + return createdFile.id } - return createdFile.id + const addDefendantFileToCaseState = async ( + input: CreateFileInput, + defendantId: string, + ) => { + const mutation = limitedAccess + ? limitedAccessCreateDefendantFile + : createDefendantFile + + const { data } = await mutation({ + variables: { input: { ...input, defendantId } }, + }) + + const createdFile = limitedAccess + ? (data as LimitedAccessCreateDefendantFileMutation) + ?.limitedAccessCreateDefendantFile + : (data as CreateDefendantFileMutation)?.createDefendantFile + + if (!createdFile?.id) { + throw Error('Failed to add file to case') + } + + return createdFile.id + } + + const addCivilClaimantFileToCaseState = async ( + input: CreateFileInput, + civilClaimantId: string, + ) => { + const mutation = limitedAccess + ? limitedAccessCreateCivilClaimantFile + : createCivilClaimantFile + + const { data } = await mutation({ + variables: { input: { ...input, civilClaimantId } }, + }) + + const createdFile = limitedAccess + ? (data as LimitedAccessCreateCivilClaimantFileMutation) + ?.limitedAccessCreateCivilClaimantFile + : (data as CreateCivilClaimantFileMutation)?.createCivilClaimantFile + + if (!createdFile?.id) { + throw Error('Failed to add file to case') + } + + return createdFile.id + } + + const baseInput = { + caseId, + type: file.type ?? '', + key: file.key ?? '', + size: file.size ?? 0, + category: file.category, + policeCaseNumber: file.policeCaseNumber, + chapter: file.chapter, + orderWithinChapter: file.orderWithinChapter, + displayDate: file.displayDate, + policeFileId: file.policeFileId, + userGeneratedFilename: file.userGeneratedFilename, + } + + if (defendantId) { + return addDefendantFileToCaseState(baseInput, defendantId) + } + + if (civilClaimantId) { + return addCivilClaimantFileToCaseState(baseInput, civilClaimantId) + } + + return addCaseFileToCaseState(baseInput) }, - [limitedAccess, limitedAccessCreateFile, createFile, caseId], + [ + caseId, + defendantId, + civilClaimantId, + limitedAccess, + limitedAccessCreateFile, + createFile, + limitedAccessCreateDefendantFile, + createDefendantFile, + limitedAccessCreateCivilClaimantFile, + createCivilClaimantFile, + ], ) const handleUpload = useCallback(