Skip to content

Commit

Permalink
feat(j-s): Reviewer completes indictment review (#14762)
Browse files Browse the repository at this point in the history
* feat(j-s): Complete indictment review

* feat(j-s): continued complete indictment review work

* Update updateCase.input.ts

* fix(j-s): cleanup

* feat(j-s): Set decision

* fix(j-s): cleanup

* fix(j-s): cleanup

* feat(j-s): Hide case from prosecutor reviewer list when review is finished

* fix filter
  • Loading branch information
unakb authored May 13, 2024
1 parent 0e6e5bd commit 2887fbf
Show file tree
Hide file tree
Showing 18 changed files with 311 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
CaseAppealRulingDecision,
CaseAppealState,
CaseDecision,
CaseIndictmentRulingDecision,
CaseState,
CaseType,
} from '@island.is/judicial-system/types'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
CaseIndictmentRulingDecision,
CaseLegalProvisions,
CaseType,
IndictmentCaseReviewDecision,
RequestSharedWithDefender,
SessionArrangements,
UserRole,
Expand Down Expand Up @@ -377,4 +378,8 @@ export class UpdateCaseInput {
@Allow()
@Field({ nullable: true })
readonly indictmentReviewerId?: string

@Allow()
@Field(() => IndictmentCaseReviewDecision, { nullable: true })
readonly indictmentReviewDecision?: IndictmentCaseReviewDecision
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
CaseState,
CaseType,
CourtDocument,
IndictmentCaseReviewDecision,
RequestSharedWithDefender,
SessionArrangements,
UserRole,
Expand Down Expand Up @@ -48,6 +49,9 @@ registerEnumType(RequestSharedWithDefender, {
registerEnumType(CaseIndictmentRulingDecision, {
name: 'CaseIndictmentRulingDecision',
})
registerEnumType(IndictmentCaseReviewDecision, {
name: 'IndictmentCaseReviewDecision',
})

@ObjectType()
class DateLog {
Expand Down Expand Up @@ -420,6 +424,9 @@ export class Case {
@Field(() => User, { nullable: true })
readonly indictmentReviewer?: User

@Field(() => IndictmentCaseReviewDecision, { nullable: true })
readonly indictmentReviewDecision?: IndictmentCaseReviewDecision

@Field({ nullable: true })
readonly indictmentAppealDeadline?: string
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use strict'

module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.sequelize.transaction((t) =>
queryInterface.addColumn(
'case',
'indictment_review_decision',
{
type: Sequelize.STRING,
allowNull: true,
},
{ transaction: t },
),
)
},

down: (queryInterface) => {
return queryInterface.sequelize.transaction((t) =>
queryInterface.removeColumn('case', 'indictment_review_decision', {
transaction: t,
}),
)
},
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
CaseLegalProvisions,
CaseType,
CourtDocument,
IndictmentCaseReviewDecision,
RequestSharedWithDefender,
SessionArrangements,
UserRole,
Expand Down Expand Up @@ -472,4 +473,9 @@ export class UpdateCaseDto {
@IsUUID()
@ApiPropertyOptional()
readonly indictmentReviewerId?: string

@IsOptional()
@IsEnum(IndictmentCaseReviewDecision)
@ApiPropertyOptional({ enum: IndictmentCaseReviewDecision })
readonly indictmentReviewDecision?: IndictmentCaseReviewDecision
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ const canProsecutionUserAccessCase = (
if (
user.institution?.id !== theCase.prosecutorsOfficeId &&
(forUpdate ||
user.institution?.id !== theCase.sharedWithProsecutorsOfficeId)
user.institution?.id !== theCase.sharedWithProsecutorsOfficeId) &&
user.id !== theCase.indictmentReviewerId
) {
return false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,12 @@ const getProsecutionUserCasesQueryFilter = (user: User): WhereOptions => {
[Op.or]: [
{ prosecutors_office_id: user.institution?.id },
{ shared_with_prosecutors_office_id: user.institution?.id },
{ indictment_reviewer_id: user.id },
{
[Op.and]: [
{ indictment_reviewer_id: user.id },
{ indictment_review_decision: null },
],
},
],
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,17 @@ describe('getCasesQueryFilter', () => {
CaseState.COMPLETED,
],
},

{
[Op.or]: [
{ prosecutors_office_id: 'Prosecutors Office Id' },
{ shared_with_prosecutors_office_id: 'Prosecutors Office Id' },
{ indictment_reviewer_id: 'Prosecutor Id' },
{
[Op.and]: [
{ indictment_reviewer_id: 'Prosecutor Id' },
{ indictment_review_decision: null },
],
},
],
},
{
Expand Down Expand Up @@ -117,7 +123,12 @@ describe('getCasesQueryFilter', () => {
[Op.or]: [
{ prosecutors_office_id: 'Prosecutors Office Id' },
{ shared_with_prosecutors_office_id: 'Prosecutors Office Id' },
{ indictment_reviewer_id: 'Prosecutor Id' },
{
[Op.and]: [
{ indictment_reviewer_id: 'Prosecutor Id' },
{ indictment_review_decision: null },
],
},
],
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const prosecutorFields: (keyof UpdateCaseDto)[] = [
'prosecutorStatementDate',
'requestAppealRulingNotToBePublished',
'indictmentDeniedExplanation',
'indictmentReviewDecision',
]

const publicProsecutorFields: (keyof UpdateCaseDto)[] = ['indictmentReviewerId']
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
CaseState,
CaseType,
CourtDocument,
IndictmentCaseReviewDecision,
RequestSharedWithDefender,
SessionArrangements,
UserRole,
Expand Down Expand Up @@ -1252,4 +1253,15 @@ export class Case extends Model {
@BelongsTo(() => User, 'indictmentReviewerId')
@ApiPropertyOptional({ type: User })
indictmentReviewer?: User

/**********
* The review decision in indictment cases
**********/
@Column({
type: DataType.ENUM,
allowNull: true,
values: Object.values(IndictmentCaseReviewDecision),
})
@ApiPropertyOptional({ enum: IndictmentCaseReviewDecision })
indictmentReviewDecision?: IndictmentCaseReviewDecision
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,12 @@ const Conclusion: React.FC = () => {
} else if (selectedAction === 'COMPLETE') {
handleCompletion()
} else if (postponement?.postponedIndefinitely) {
const updateSuccss = await updateCase(workingCase.id, {
const updateSuccess = await updateCase(workingCase.id, {
courtDate: null,
postponedIndefinitelyExplanation: postponement.reason,
})

if (!updateSuccss) {
if (!updateSuccess) {
return
}
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { style } from '@vanilla-extract/css'

import { theme } from '@island.is/island-ui/theme'

export const gridRow = style({
display: 'grid',
gridTemplateColumns: '1.6fr 1fr',
gridGap: theme.spacing[1],
marginBottom: theme.spacing[1],
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { defineMessages } from 'react-intl'

export const strings = defineMessages({
title: {
id: 'judicial.system.core:public_prosecutor.indictments.review_decision.title',
defaultMessage: 'Ákvörðun um áfrýjun',
description: 'Notaður sem titill á ákvörðum um áfrýjun boxi fyrir ákæru.',
},
subtitle: {
id: 'judicial.system.core:public_prosecutor.indictments.review_decision.subtitle',
defaultMessage:
'Frestur til að áfrýja dómi rennur út {indictmentAppealDeadline}',
description:
'Notaður sem undirtitill á ákvörðum um áfrýjun boxi fyrir ákæru.',
},
appealToCourtOfAppeals: {
id: 'judicial.system.core:public_prosecutor.indictments.review_decision.appeal_to_court_of_appeals',
defaultMessage: 'Áfrýja héraðsdómi til Landsréttar',
description:
'Notaður sem texti fyrir "Áfrýja héraðsdómi til Landsréttar" radio takka.',
},
acceptDecision: {
id: 'judicial.system.core:public_prosecutor.indictments.review_decision.accept_decision',
defaultMessage: 'Una héraðsdómi',
description: 'Notaður sem texti fyrir "Una héraðsdómi" radio takka.',
},
reviewModalTitle: {
id: 'judicial.system.core:indictments_review.title',
defaultMessage: 'Staðfesta ákvörðun',
description: 'Notaður sem titill á yfirliti ákæru.',
},
reviewModalText: {
id: 'judicial.system.core:indictments_review.modal_text',

defaultMessage:
'Ertu viss um að þú viljir {reviewerDecision, select, ACCEPT {una héraðsdómi} APPEAL {áfrýja héraðsdómi til Landsréttar} other {halda áfram}}?',
description: 'Notaður sem texti í yfirlitsglugga um yfirlit ákæru.',
},
reviewModalPrimaryButtonText: {
id: 'judicial.system.core:indictments_review.modal_primary_button_text',
defaultMessage: 'Staðfesta',
description:
'Notaður sem texti á aðal takka í yfirlitsglugga um yfirlit ákæru.',
},
reviewModalSecondaryButtonText: {
id: 'judicial.system.core:indictments_review.modal_secondary_button_text',
defaultMessage: 'Hætta við',
description:
'Notaður sem texti á aukatakka í yfirlitsglugga um yfirlit ákæru.',
},
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { useContext, useState } from 'react'
import { useIntl } from 'react-intl'
import { useRouter } from 'next/router'

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 {
IndictmentCaseReviewDecision,
isPublicProsecutor,
} from '@island.is/judicial-system/types'
import {
BlueBox,
Modal,
SectionHeading,
UserContext,
} from '@island.is/judicial-system-web/src/components'
import { useCase } from '@island.is/judicial-system-web/src/utils/hooks'

import { strings } from './ReviewDecision.strings'
import * as styles from './ReviewDecision.css'

interface Props {
caseId: string
indictmentAppealDeadline?: string
modalVisible?: boolean
setModalVisible: React.Dispatch<React.SetStateAction<boolean>>
onSelect?: () => void
}

export const ReviewDecision: React.FC<Props> = (props) => {
const { user } = useContext(UserContext)
const router = useRouter()
const { formatMessage: fm } = useIntl()
const { updateCase } = useCase()

const {
caseId,
indictmentAppealDeadline,
modalVisible,
setModalVisible,
onSelect,
} = props

const [indictmentReviewDecision, setIndictmentReviewDecision] = useState<
IndictmentCaseReviewDecision | undefined
>(undefined)

const handleReviewDecision = async () => {
if (!indictmentReviewDecision) {
return
}
const updateSuccess = await updateCase(caseId, {
indictmentReviewDecision: indictmentReviewDecision,
})
if (updateSuccess) {
router.push(constants.CASES_ROUTE)
}
}

const options = [
{
label: fm(strings.appealToCourtOfAppeals),
value: IndictmentCaseReviewDecision.APPEAL,
},
{
label: fm(strings.acceptDecision),
value: IndictmentCaseReviewDecision.ACCEPT,
},
]

if (!isPublicProsecutor(user)) {
return null
}

return (
<Box marginBottom={5}>
<SectionHeading
title={fm(strings.title)}
description={
<Text variant="eyebrow">
{fm(strings.subtitle, {
indictmentAppealDeadline: formatDate(
indictmentAppealDeadline,
'P',
),
})}
</Text>
}
/>
<BlueBox>
<div className={styles.gridRow}>
{options.map((item, index) => {
return (
<RadioButton
name={`reviewOption-${index}`}
label={item.label}
value={item.value}
checked={indictmentReviewDecision === item.value}
onChange={() => {
onSelect && onSelect()
setIndictmentReviewDecision(item.value)
}}
backgroundColor="white"
large
/>
)
})}
</div>
</BlueBox>
{modalVisible && (
<Modal
title={fm(strings.reviewModalTitle)}
text={fm(strings.reviewModalText, {
reviewerDecision: indictmentReviewDecision,
})}
primaryButtonText={fm(strings.reviewModalPrimaryButtonText)}
secondaryButtonText={fm(strings.reviewModalSecondaryButtonText)}
onClose={() => setModalVisible(false)}
onPrimaryButtonClick={handleReviewDecision}
onSecondaryButtonClick={() => setModalVisible(false)}
/>
)}
</Box>
)
}
Loading

0 comments on commit 2887fbf

Please sign in to comment.