Skip to content

Commit

Permalink
Merge branch 'main' into fix/web-rss-event-feed-single-digit-dates
Browse files Browse the repository at this point in the history
  • Loading branch information
kodiakhq[bot] authored Dec 19, 2024
2 parents 7016af8 + 72a8ce9 commit cce06cc
Show file tree
Hide file tree
Showing 37 changed files with 971 additions and 247 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ export class Defendant {
@Field(() => String, { nullable: true })
readonly sentToPrisonAdminDate?: string

@Field(() => String, { nullable: true })
readonly openedByPrisonAdminDate?: string

@Field(() => PunishmentType, { nullable: true })
readonly punishmentType?: PunishmentType
}
10 changes: 10 additions & 0 deletions apps/judicial-system/backend/src/app/modules/case/case.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,16 @@ export const caseListInclude: Includeable[] = [
as: 'defendants',
required: false,
order: [['created', 'ASC']],
include: [
{
model: DefendantEventLog,
as: 'eventLogs',
required: false,
where: { eventType: defendantEventTypes },
order: [['created', 'DESC']],
separate: true,
},
],
separate: true,
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
NestInterceptor,
} from '@nestjs/common'

import { DefendantEventType } from '@island.is/judicial-system/types'

import { Defendant, DefendantEventLog } from '../../defendant'
import { Case } from '../models/case.model'
import { CaseString } from '../models/caseString.model'
Expand All @@ -15,8 +17,15 @@ export const transformDefendants = (defendants?: Defendant[]) => {
return defendants?.map((defendant) => ({
...defendant.toJSON(),
sentToPrisonAdminDate: defendant.isSentToPrisonAdmin
? DefendantEventLog.sentToPrisonAdminDate(defendant.eventLogs)?.created
? DefendantEventLog.getDefendantEventLogTypeDate({
defendantEventLogs: defendant.eventLogs,
eventType: DefendantEventType.SENT_TO_PRISON_ADMIN,
})
: undefined,
openedByPrisonAdminDate: DefendantEventLog.getDefendantEventLogTypeDate({
defendantEventLogs: defendant.eventLogs,
eventType: DefendantEventType.OPENED_BY_PRISON_ADMIN,
}),
}))
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import {
CallHandler,
ExecutionContext,
Injectable,
NestInterceptor,
} from '@nestjs/common'

import {
DefendantEventType,
isIndictmentCase,
isPrisonAdminUser,
User,
} from '@island.is/judicial-system/types'

import { DefendantEventLog, DefendantService } from '../../defendant'
import { Case } from '../models/case.model'

const hasValidOpenByPrisonAdminEvent = (
defendantEventLogs: DefendantEventLog[],
) => {
const sentToPrisonAdminDate = DefendantEventLog.getDefendantEventLogTypeDate({
defendantEventLogs,
eventType: DefendantEventType.SENT_TO_PRISON_ADMIN,
})
const openedByPrisonAdminDate =
DefendantEventLog.getDefendantEventLogTypeDate({
defendantEventLogs,
eventType: DefendantEventType.OPENED_BY_PRISON_ADMIN,
})
return (
sentToPrisonAdminDate &&
openedByPrisonAdminDate &&
sentToPrisonAdminDate <= openedByPrisonAdminDate
)
}

@Injectable()
export class DefendantIndictmentAccessedInterceptor implements NestInterceptor {
constructor(private readonly defendantService: DefendantService) {}

intercept(context: ExecutionContext, next: CallHandler) {
const request = context.switchToHttp().getRequest()
const user: User = request.user
const theCase: Case = request.case

if (isIndictmentCase(theCase.type) && isPrisonAdminUser(user)) {
const defendantsIndictmentNotOpened = theCase.defendants?.filter(
({ isSentToPrisonAdmin, eventLogs = [] }) =>
isSentToPrisonAdmin && !hasValidOpenByPrisonAdminEvent(eventLogs),
)

// create new events for all defendants that prison admin has not accessed according to defendant event logs
defendantsIndictmentNotOpened?.forEach((defendant) =>
this.defendantService.createDefendantEvent({
caseId: theCase.id,
defendantId: defendant.id,
eventType: DefendantEventType.OPENED_BY_PRISON_ADMIN,
}),
)
}
return next.handle()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,16 @@ import type { User as TUser } from '@island.is/judicial-system/types'
import {
CaseState,
CaseType,
DefendantEventType,
indictmentCases,
investigationCases,
restrictionCases,
UserRole,
} from '@island.is/judicial-system/types'

import { nowFactory } from '../../factories'
import { defenderRule, prisonSystemStaffRule } from '../../guards'
import { DefendantService } from '../defendant'
import { EventService } from '../event'
import { User } from '../user'
import { TransitionCaseDto } from './dto/transitionCase.dto'
Expand All @@ -57,6 +60,7 @@ import {
} from './guards/rolesRules'
import { CaseInterceptor } from './interceptors/case.interceptor'
import { CompletedAppealAccessedInterceptor } from './interceptors/completedAppealAccessed.interceptor'
import { DefendantIndictmentAccessedInterceptor } from './interceptors/defendantIndictmentAccessed.interceptor'
import { LimitedAccessCaseFileInterceptor } from './interceptors/limitedAccessCaseFile.interceptor'
import { Case } from './models/case.model'
import { transitionCase } from './state/case.state'
Expand All @@ -73,6 +77,7 @@ export class LimitedAccessCaseController {
private readonly limitedAccessCaseService: LimitedAccessCaseService,
private readonly eventService: EventService,
private readonly pdfService: PdfService,
private readonly defendantService: DefendantService,
@Inject(LOGGER_PROVIDER) private readonly logger: Logger,
) {}

Expand All @@ -84,6 +89,7 @@ export class LimitedAccessCaseController {
)
@RolesRules(prisonSystemStaffRule, defenderRule)
@UseInterceptors(
DefendantIndictmentAccessedInterceptor,
CompletedAppealAccessedInterceptor,
LimitedAccessCaseFileInterceptor,
CaseInterceptor,
Expand All @@ -100,7 +106,7 @@ export class LimitedAccessCaseController {
): Promise<Case> {
this.logger.debug(`Getting limitedAccess case ${caseId} by id`)

if (!theCase.openedByDefender) {
if (user.role === UserRole.DEFENDER && !theCase.openedByDefender) {
const updated = await this.limitedAccessCaseService.update(
theCase,
{ openedByDefender: nowFactory() },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { uuid } from 'uuidv4'

import type { User } from '@island.is/judicial-system/types'
import { type User, UserRole } from '@island.is/judicial-system/types'

import { createTestingCaseModule } from '../createTestingCaseModule'

Expand All @@ -14,14 +14,18 @@ interface Then {
error: Error
}

type GivenWhenThen = (caseId: string, theCase: Case) => Promise<Then>
type GivenWhenThen = (
caseId: string,
theCase: Case,
user?: User,
) => Promise<Then>

describe('LimitedAccessCaseController - Get by id', () => {
let givenWhenThen: GivenWhenThen
const openedBeforeDate = randomDate()
const openedNowDate = randomDate()
const caseId = uuid()
const user = { id: uuid() } as User
const defaultUser = { id: uuid() } as User

let mockCaseModel: typeof Case

Expand All @@ -42,7 +46,11 @@ describe('LimitedAccessCaseController - Get by id', () => {
const mockFindOne = mockCaseModel.findOne as jest.Mock
mockFindOne.mockResolvedValue(updatedCase)

givenWhenThen = async (caseId: string, theCase: Case) => {
givenWhenThen = async (
caseId: string,
theCase: Case,
user = defaultUser,
) => {
const then = {} as Then

try {
Expand Down Expand Up @@ -79,11 +87,11 @@ describe('LimitedAccessCaseController - Get by id', () => {

describe('case exists and has not been opened by defender before', () => {
const theCase = { id: caseId } as Case

const user = { ...defaultUser, role: UserRole.DEFENDER } as User
let then: Then

beforeEach(async () => {
then = await givenWhenThen(caseId, theCase)
then = await givenWhenThen(caseId, theCase, user)
})

it('should update openedByDefender and return case', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,22 @@ export class DefendantService {
return updatedDefendant
}

async createDefendantEvent({
caseId,
defendantId,
eventType,
}: {
caseId: string
defendantId: string
eventType: DefendantEventType
}): Promise<void> {
await this.defendantEventLogModel.create({
caseId,
defendantId,
eventType,
})
}

async updateIndictmentCaseDefendant(
theCase: Case,
defendant: Defendant,
Expand All @@ -284,7 +300,7 @@ export class DefendantService {
)

if (update.isSentToPrisonAdmin) {
this.defendantEventLogModel.create({
this.createDefendantEvent({
caseId: theCase.id,
defendantId: defendant.id,
eventType: DefendantEventType.SENT_TO_PRISON_ADMIN,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,17 @@ import { Defendant } from './defendant.model'
timestamps: true,
})
export class DefendantEventLog extends Model {
static sentToPrisonAdminDate(defendantEventLogs?: DefendantEventLog[]) {
// gets the latest log date of a given type, since the defendant event logs are sorted
static getDefendantEventLogTypeDate({
defendantEventLogs,
eventType,
}: {
defendantEventLogs?: DefendantEventLog[]
eventType: DefendantEventType
}) {
return defendantEventLogs?.find(
(defendantEventLog) =>
defendantEventLog.eventType === DefendantEventType.SENT_TO_PRISON_ADMIN,
)
(defendantEventLog) => defendantEventLog.eventType === eventType,
)?.created
}

@Column({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ query Case($input: CaseQueryInput!) {
subpoenaType
isSentToPrisonAdmin
sentToPrisonAdminDate
openedByPrisonAdminDate
punishmentType
subpoenas {
id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ query LimitedAccessCase($input: CaseQueryInput!) {
subpoenaType
isSentToPrisonAdmin
sentToPrisonAdminDate
openedByPrisonAdminDate
punishmentType
subpoenas {
id
Expand Down
16 changes: 16 additions & 0 deletions apps/judicial-system/web/src/components/Tags/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,19 @@ export const getIndictmentRulingDecisionTag = (
return { color: 'darkerBlue', text: strings.complete }
}
}

export const getPrisonCaseStateTag = (
prisonCaseState: CaseState,
): {
color: TagVariant
text: { id: string; defaultMessage: string; description: string }
} => {
switch (prisonCaseState) {
case CaseState.NEW:
return { color: 'purple', text: strings.new }
case CaseState.RECEIVED:
return { color: 'blue', text: strings.received }
default:
return { color: 'darkerBlue', text: strings.complete }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ export const DefenderCasesTable: FC<Props> = ({
) {
return entry.defendants[0].name ?? ''
}
if (column === 'courtDate') {
return entry.courtDate
}
return entry.created
}
const { sortedData, requestSort, getClassNamesFor, isActiveColumn } = useSort(
Expand Down Expand Up @@ -129,9 +132,13 @@ export const DefenderCasesTable: FC<Props> = ({
</th>
) : (
<th>
<Text fontWeight="regular">
{formatMessage(tables.hearingArrangementDate)}
</Text>
<SortButton
title={formatMessage(tables.hearingArrangementDate)}
onClick={() => requestSort('courtDate')}
sortAsc={getClassNamesFor('courtDate') === 'ascending'}
sortDes={getClassNamesFor('courtDate') === 'descending'}
isActive={isActiveColumn('courtDate')}
/>
</th>
)}
<th></th>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ export const strings = defineMessages({
indictmentCompletedTitle: {
id: 'judicial.system.core:indictment_overview.indictment_completed_title',
defaultMessage: 'Dómsuppkvaðning {date}',
description: 'Titill á yfirliti ákæru fyrir fangelsi',
description: 'Undirtitill á yfirliti ákæru fyrir fangelsi',
},
indictmentReceivedTitle: {
id: 'judicial.system.core:indictment_overview.indictment_received_title',
defaultMessage: 'Móttekið {date}',
description: 'Undirtitill á yfirliti ákæru fyrir fangelsi',
},
infoCardDefendantsTitle: {
id: 'judicial.system.core:indictment_overview.info_card_defendants_title',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@ const IndictmentOverview = () => {
})}
</Text>
)}
{defendant?.openedByPrisonAdminDate && (
<Text variant="h4" as="h3">
{formatMessage(strings.indictmentReceivedTitle, {
date: formatDate(defendant.openedByPrisonAdminDate, 'PPP'),
})}
</Text>
)}
</Box>
<Box marginBottom={5}>
<InfoCardClosedIndictment displayVerdictViewDate />
Expand Down
Loading

0 comments on commit cce06cc

Please sign in to comment.