diff --git a/apps/judicial-system/api/src/app/modules/case/interceptors/case.transformer.spec.ts b/apps/judicial-system/api/src/app/modules/case/interceptors/case.transformer.spec.ts index 5c76205ba8d7..f73423c0f6d4 100644 --- a/apps/judicial-system/api/src/app/modules/case/interceptors/case.transformer.spec.ts +++ b/apps/judicial-system/api/src/app/modules/case/interceptors/case.transformer.spec.ts @@ -731,6 +731,54 @@ describe('getIndictmentInfo', () => { indictmentVerdictAppealDeadlineExpired: false, }) }) + + it('should return correct indictment info when the indictment ruling decision is FINE and the appeal deadline is not expired', () => { + const rulingDate = new Date().toISOString() + const defendants = [ + { + serviceRequirement: ServiceRequirement.NOT_REQUIRED, + verdictViewDate: undefined, + } as Defendant, + ] + + const indictmentInfo = getIndictmentInfo( + CaseIndictmentRulingDecision.FINE, + rulingDate, + defendants, + ) + + expect(indictmentInfo).toEqual({ + indictmentAppealDeadline: new Date( + new Date(rulingDate).setDate(new Date(rulingDate).getDate() + 3), + ).toISOString(), + indictmentVerdictViewedByAll: true, + indictmentVerdictAppealDeadlineExpired: false, + }) + }) + + it('should return correct indictment info when the indictment ruling decision is FINE and the appeal deadline is expired', () => { + const rulingDate = '2024-05-26T21:51:19.156Z' + const defendants = [ + { + serviceRequirement: ServiceRequirement.NOT_REQUIRED, + verdictViewDate: undefined, + } as Defendant, + ] + + const indictmentInfo = getIndictmentInfo( + CaseIndictmentRulingDecision.FINE, + rulingDate, + defendants, + ) + + expect(indictmentInfo).toEqual({ + indictmentAppealDeadline: new Date( + new Date(rulingDate).setDate(new Date(rulingDate).getDate() + 3), + ).toISOString(), + indictmentVerdictViewedByAll: true, + indictmentVerdictAppealDeadlineExpired: true, + }) + }) }) describe('getIndictmentDefendantsInfo', () => { diff --git a/apps/judicial-system/api/src/app/modules/case/interceptors/case.transformer.ts b/apps/judicial-system/api/src/app/modules/case/interceptors/case.transformer.ts index bf8139e14014..f01909d284f1 100644 --- a/apps/judicial-system/api/src/app/modules/case/interceptors/case.transformer.ts +++ b/apps/judicial-system/api/src/app/modules/case/interceptors/case.transformer.ts @@ -2,6 +2,7 @@ import { CaseAppealDecision, CaseIndictmentRulingDecision, EventType, + FINE_APPEAL_WINDOW_DAYS, getIndictmentVerdictAppealDeadlineStatus, getStatementDeadline, isRequestCase, @@ -151,6 +152,8 @@ export const getIndictmentInfo = ( eventLog?: EventLog[], ): IndictmentInfo => { const indictmentInfo: IndictmentInfo = {} + const isFine = rulingDecision === CaseIndictmentRulingDecision.FINE + const isRuling = rulingDecision === CaseIndictmentRulingDecision.RULING if (!rulingDate) { return indictmentInfo @@ -158,13 +161,16 @@ export const getIndictmentInfo = ( const theRulingDate = new Date(rulingDate) indictmentInfo.indictmentAppealDeadline = new Date( - theRulingDate.getTime() + getDays(VERDICT_APPEAL_WINDOW_DAYS), + theRulingDate.getTime() + + getDays(isFine ? FINE_APPEAL_WINDOW_DAYS : VERDICT_APPEAL_WINDOW_DAYS), ).toISOString() const verdictInfo = defendants?.map<[boolean, Date | undefined]>( (defendant) => [ - rulingDecision === CaseIndictmentRulingDecision.RULING, - defendant.serviceRequirement === ServiceRequirement.NOT_REQUIRED + isRuling || isFine, + isFine + ? theRulingDate + : defendant.serviceRequirement === ServiceRequirement.NOT_REQUIRED ? new Date() : defendant.verdictViewDate ? new Date(defendant.verdictViewDate) @@ -173,7 +179,7 @@ export const getIndictmentInfo = ( ) const [indictmentVerdictViewedByAll, indictmentVerdictAppealDeadlineExpired] = - getIndictmentVerdictAppealDeadlineStatus(verdictInfo) + getIndictmentVerdictAppealDeadlineStatus(verdictInfo, isFine) indictmentInfo.indictmentVerdictViewedByAll = indictmentVerdictViewedByAll indictmentInfo.indictmentVerdictAppealDeadlineExpired = indictmentVerdictAppealDeadlineExpired @@ -189,6 +195,8 @@ export const getIndictmentDefendantsInfo = (theCase: Case) => { return theCase.defendants?.map((defendant) => { const serviceRequired = defendant.serviceRequirement === ServiceRequirement.REQUIRED + const isFine = + theCase.indictmentRulingDecision === CaseIndictmentRulingDecision.FINE const { verdictViewDate } = defendant @@ -196,7 +204,10 @@ export const getIndictmentDefendantsInfo = (theCase: Case) => { const verdictAppealDeadline = baseDate ? new Date( - new Date(baseDate).getTime() + getDays(VERDICT_APPEAL_WINDOW_DAYS), + new Date(baseDate).getTime() + + getDays( + isFine ? FINE_APPEAL_WINDOW_DAYS : VERDICT_APPEAL_WINDOW_DAYS, + ), ).toISOString() : undefined 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 e4200b4dd5ee..36dbfdc8671c 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 @@ -277,7 +277,9 @@ const canPrisonAdminUserAccessCase = ( // Check case indictment ruling decision access if ( - theCase.indictmentRulingDecision !== CaseIndictmentRulingDecision.RULING + theCase.indictmentRulingDecision !== + CaseIndictmentRulingDecision.RULING && + theCase.indictmentRulingDecision !== CaseIndictmentRulingDecision.FINE ) { return false } 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 34ae100c239d..bc5f2f9f3a5f 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 @@ -211,7 +211,12 @@ const getPrisonAdminUserCasesQueryFilter = (): WhereOptions => { { type: indictmentCases, state: CaseState.COMPLETED, - indictment_ruling_decision: CaseIndictmentRulingDecision.RULING, + indictment_ruling_decision: { + [Op.or]: [ + CaseIndictmentRulingDecision.RULING, + CaseIndictmentRulingDecision.FINE, + ], + }, indictment_review_decision: IndictmentCaseReviewDecision.ACCEPT, id: { [Op.in]: Sequelize.literal(` 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 cb6a69ebaf7c..4d6027e5d5b4 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 @@ -388,7 +388,12 @@ describe('getCasesQueryFilter', () => { { type: indictmentCases, state: CaseState.COMPLETED, - indictment_ruling_decision: CaseIndictmentRulingDecision.RULING, + indictment_ruling_decision: { + [Op.or]: [ + CaseIndictmentRulingDecision.RULING, + CaseIndictmentRulingDecision.FINE, + ], + }, indictment_review_decision: IndictmentCaseReviewDecision.ACCEPT, id: { [Op.in]: Sequelize.literal(` diff --git a/apps/judicial-system/backend/src/app/modules/case/filters/test/prisonAdminUserFilter.spec.ts b/apps/judicial-system/backend/src/app/modules/case/filters/test/prisonAdminUserFilter.spec.ts index 570de65e3f81..9d147a4ca169 100644 --- a/apps/judicial-system/backend/src/app/modules/case/filters/test/prisonAdminUserFilter.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/filters/test/prisonAdminUserFilter.spec.ts @@ -132,6 +132,7 @@ describe.each(prisonSystemRoles)('prison admin user %s', (role) => { (state) => { const accessibleCaseIndictmentRulingDecisions = [ CaseIndictmentRulingDecision.RULING, + CaseIndictmentRulingDecision.FINE, ] describe.each( diff --git a/apps/judicial-system/web/src/components/BlueBoxWithIcon/BlueBoxWithDate.spec.tsx b/apps/judicial-system/web/src/components/BlueBoxWithIcon/BlueBoxWithDate.spec.tsx new file mode 100644 index 000000000000..48ac69307bbc --- /dev/null +++ b/apps/judicial-system/web/src/components/BlueBoxWithIcon/BlueBoxWithDate.spec.tsx @@ -0,0 +1,80 @@ +import faker from 'faker' +import { render, screen } from '@testing-library/react' + +import { + CaseIndictmentRulingDecision, + CaseType, + Defendant, +} from '../../graphql/schema' +import { mockCase } from '../../utils/mocks' +import { + ApolloProviderWrapper, + FormContextWrapper, + IntlProviderWrapper, +} from '../../utils/testHelpers' +import BlueBoxWithDate from './BlueBoxWithDate' + +jest.mock('next/router', () => ({ + useRouter() { + return { + pathname: '', + query: { + id: 'test_id', + }, + } + }, +})) + +describe('BlueBoxWithDate', () => { + const name = faker.name.firstName() + const rulingDate = new Date().toISOString() + + const mockDefendant: Defendant = { + name, + id: faker.datatype.uuid(), + } + + it('renders correctly when ruling decision is FINE', () => { + render( + + + + + + + , + ) + + expect(screen.getByText('Viðurlagaákvörðun')).toBeInTheDocument() + expect(screen.getByText(name)).toBeInTheDocument() + }) + + it('renders correctly when ruling decision is RULING', () => { + render( + + + + + + + , + ) + + expect(screen.getByText('Birting dóms')).toBeInTheDocument() + expect(screen.getByText(name)).toBeInTheDocument() + }) +}) diff --git a/apps/judicial-system/web/src/components/BlueBoxWithIcon/BlueBoxWithDate.strings.ts b/apps/judicial-system/web/src/components/BlueBoxWithIcon/BlueBoxWithDate.strings.ts index a68972df8229..a3f57cbebd4c 100644 --- a/apps/judicial-system/web/src/components/BlueBoxWithIcon/BlueBoxWithDate.strings.ts +++ b/apps/judicial-system/web/src/components/BlueBoxWithIcon/BlueBoxWithDate.strings.ts @@ -70,4 +70,17 @@ export const strings = defineMessages({ description: 'Notaður sem texti í valmynd fyrir aðgerðina að senda mál til fullnustu', }, + indictmentRulingDecisionFine: { + id: 'judicial.system.core:blue_box_with_date.indictment_ruling_decision_fine', + defaultMessage: 'Viðurlagaákvörðun', + description: + 'Notaður sem titill í svæði þar sem kærufrestur viðurlagaákvörðunar er tekinn fram', + }, + fineAppealDeadline: { + id: 'judicial.system.core:blue_box_with_date.fine_appeal_deadline', + defaultMessage: + 'Kærufrestur Ríkissaksóknara {appealDeadlineIsInThePast, select, true {var} other {er}} til {appealDeadline}', + description: + 'Notaður sem titill í svæði þar sem kærufrestur viðurlagaákvörðunar er tekinn fram', + }, }) diff --git a/apps/judicial-system/web/src/components/BlueBoxWithIcon/BlueBoxWithDate.tsx b/apps/judicial-system/web/src/components/BlueBoxWithIcon/BlueBoxWithDate.tsx index 6a404390e8c9..50ecd41a0c44 100644 --- a/apps/judicial-system/web/src/components/BlueBoxWithIcon/BlueBoxWithDate.tsx +++ b/apps/judicial-system/web/src/components/BlueBoxWithIcon/BlueBoxWithDate.tsx @@ -18,8 +18,8 @@ import { VERDICT_APPEAL_WINDOW_DAYS } from '@island.is/judicial-system/types' import { errors } from '@island.is/judicial-system-web/messages' import { + CaseIndictmentRulingDecision, Defendant, - IndictmentCaseReviewDecision, ServiceRequirement, } from '../../graphql/schema' import { formatDateForServer, useDefendants } from '../../utils/hooks' @@ -32,12 +32,11 @@ import * as styles from './BlueBoxWithIcon.css' interface Props { defendant: Defendant - indictmentReviewDecision?: IndictmentCaseReviewDecision | null icon?: IconMapIcon } const BlueBoxWithDate: FC = (props) => { - const { defendant, indictmentReviewDecision, icon } = props + const { defendant, icon } = props const { formatMessage } = useIntl() const [dates, setDates] = useState<{ verdictViewDate?: Date @@ -52,6 +51,9 @@ const BlueBoxWithDate: FC = (props) => { const { workingCase, setWorkingCase } = useContext(FormContext) const router = useRouter() + const isFine = + workingCase.indictmentRulingDecision === CaseIndictmentRulingDecision.FINE + const serviceRequired = defendant.serviceRequirement === ServiceRequirement.REQUIRED @@ -133,34 +135,45 @@ const BlueBoxWithDate: FC = (props) => { const textItems = useMemo(() => { const texts = [] - if (serviceRequired) { + if (isFine) { texts.push( - formatMessage(strings.defendantVerdictViewedDate, { - date: formatDate(dates.verdictViewDate ?? defendant.verdictViewDate), + formatMessage(strings.fineAppealDeadline, { + appealDeadlineIsInThePast: defendant.isVerdictAppealDeadlineExpired, + appealDeadline: formatDate(defendant.verdictAppealDeadline), }), ) - } - - texts.push( - formatMessage(appealExpirationInfo.message, { - appealExpirationDate: appealExpirationInfo.date, - }), - ) + } else { + if (serviceRequired) { + texts.push( + formatMessage(strings.defendantVerdictViewedDate, { + date: formatDate( + dates.verdictViewDate ?? defendant.verdictViewDate, + ), + }), + ) + } - if (defendant.verdictAppealDate) { texts.push( - formatMessage(strings.defendantAppealDate, { - date: formatDate(defendant.verdictAppealDate), + formatMessage(appealExpirationInfo.message, { + appealExpirationDate: appealExpirationInfo.date, }), ) - } - if (defendant.sentToPrisonAdminDate && defendant.isSentToPrisonAdmin) { - texts.push( - formatMessage(strings.sendToPrisonAdminDate, { - date: formatDate(defendant.sentToPrisonAdminDate), - }), - ) + if (defendant.verdictAppealDate) { + texts.push( + formatMessage(strings.defendantAppealDate, { + date: formatDate(defendant.verdictAppealDate), + }), + ) + } + + if (defendant.sentToPrisonAdminDate && defendant.isSentToPrisonAdmin) { + texts.push( + formatMessage(strings.sendToPrisonAdminDate, { + date: formatDate(defendant.sentToPrisonAdminDate), + }), + ) + } } return texts @@ -169,10 +182,13 @@ const BlueBoxWithDate: FC = (props) => { appealExpirationInfo.message, dates.verdictViewDate, defendant.isSentToPrisonAdmin, + defendant.isVerdictAppealDeadlineExpired, defendant.sentToPrisonAdminDate, defendant.verdictAppealDate, + defendant.verdictAppealDeadline, defendant.verdictViewDate, formatMessage, + isFine, serviceRequired, ]) @@ -205,7 +221,9 @@ const BlueBoxWithDate: FC = (props) => { @@ -217,7 +235,7 @@ const BlueBoxWithDate: FC = (props) => { - {(!serviceRequired || defendant.verdictViewDate) && + {(!serviceRequired || defendant.verdictViewDate || isFine) && textItems.map((text, index) => ( = (props) => { {defendant.verdictAppealDate || - defendant.isVerdictAppealDeadlineExpired ? null : !serviceRequired || - defendant.verdictViewDate ? ( + defendant.isVerdictAppealDeadlineExpired || + isFine ? null : !serviceRequired || defendant.verdictViewDate ? ( = (props) => { variant="text" onClick={handleSendToPrisonAdmin} size="small" - disabled={!indictmentReviewDecision || !defendant.verdictViewDate} + disabled={ + !workingCase.indictmentReviewDecision || + (!isFine && !defendant.verdictViewDate) + } > {formatMessage(strings.sendToPrisonAdmin)} diff --git a/apps/judicial-system/web/src/components/InfoCard/useInfoCardItems.strings.ts b/apps/judicial-system/web/src/components/InfoCard/useInfoCardItems.strings.ts index 57689f0fa174..cbe3bdb2b903 100644 --- a/apps/judicial-system/web/src/components/InfoCard/useInfoCardItems.strings.ts +++ b/apps/judicial-system/web/src/components/InfoCard/useInfoCardItems.strings.ts @@ -28,14 +28,16 @@ export const strings = defineMessages({ description: 'Notaður sem titill á "ákvörðun" hluta af yfirliti ákæru.', }, reviewTagAppealed: { - id: 'judicial.system.core:info_card_indictment.review_tag_appealed_v1', - defaultMessage: 'Áfrýja dómi', + id: 'judicial.system.core:info_card_indictment.review_tag_appealed_v2', + defaultMessage: + 'Áfrýja {isFine, select, true {viðurlagaákvörðun} other {dómi}}', description: 'Notað sem texti á tagg fyrir "Áfrýjun" tillögu í yfirliti ákæru.', }, reviewTagAccepted: { - id: 'judicial.system.core:info_card_indictment.review_tag_completed_v1', - defaultMessage: 'Una dómi', + id: 'judicial.system.core:info_card_indictment.review_tag_completed_v2', + defaultMessage: + 'Una {isFine, select, true {viðurlagaákvörðun} other {dómi}}', description: 'Notað sem texti á tagg fyrir "Una" tillögu í yfirliti ákæru.', }, indictmentReviewedDateTitle: { diff --git a/apps/judicial-system/web/src/components/InfoCard/useInfoCardItems.tsx b/apps/judicial-system/web/src/components/InfoCard/useInfoCardItems.tsx index 1a0c7ccf70c3..a2112beb8c66 100644 --- a/apps/judicial-system/web/src/components/InfoCard/useInfoCardItems.tsx +++ b/apps/judicial-system/web/src/components/InfoCard/useInfoCardItems.tsx @@ -15,6 +15,7 @@ import { core } from '@island.is/judicial-system-web/messages' import { requestCourtDate } from '@island.is/judicial-system-web/messages' import { Case, + CaseIndictmentRulingDecision, CaseType, IndictmentCaseReviewDecision, } from '@island.is/judicial-system-web/src/graphql/schema' @@ -279,6 +280,11 @@ const useInfoCardItems = () => { IndictmentCaseReviewDecision.ACCEPT ? strings.reviewTagAccepted : strings.reviewTagAppealed, + { + isFine: + workingCase.indictmentRulingDecision === + CaseIndictmentRulingDecision.FINE, + }, ), ], } diff --git a/apps/judicial-system/web/src/components/index.ts b/apps/judicial-system/web/src/components/index.ts index adf54284e0c6..5dc0a2ff8240 100644 --- a/apps/judicial-system/web/src/components/index.ts +++ b/apps/judicial-system/web/src/components/index.ts @@ -1,6 +1,7 @@ export { CourtCaseInfo, ProsecutorCaseInfo } from './CaseInfo/CaseInfo' export { default as AccordionListItem } from './AccordionListItem/AccordionListItem' export { default as BlueBox } from './BlueBox/BlueBox' +export { default as BlueBoxWithDate } from './BlueBoxWithIcon/BlueBoxWithDate' export { default as CaseDates } from './CaseDates/CaseDates' export { default as CaseFile } from './CaseFile/CaseFile' export { default as CaseFileList } from './CaseFileList/CaseFileList' diff --git a/apps/judicial-system/web/src/routes/PublicProsecutor/Indictments/Overview/Overview.strings.ts b/apps/judicial-system/web/src/routes/PublicProsecutor/Indictments/Overview/Overview.strings.ts index bda2b5b7b75f..3418534bf794 100644 --- a/apps/judicial-system/web/src/routes/PublicProsecutor/Indictments/Overview/Overview.strings.ts +++ b/apps/judicial-system/web/src/routes/PublicProsecutor/Indictments/Overview/Overview.strings.ts @@ -22,9 +22,9 @@ export const strings = defineMessages({ description: 'Notaður sem titill á yfirliti ákæru.', }, reviewerSubtitle: { - id: 'judicial.system.core:public_prosecutor.indictments.overview.reviewer_subtitle', + id: 'judicial.system.core:public_prosecutor.indictments.overview.reviewer_subtitle_v2', defaultMessage: - 'Frestur til að áfrýja dómi rennur út {indictmentAppealDeadline}', + 'Frestur til að {isFine, select, true {kæra viðurlagaákvörðun} other {áfrýja dómi}} {appealDeadlineIsInThePast, select, true {rann} other {rennur}} út {indictmentAppealDeadline}', description: 'Notaður sem undirtitill á yfirliti ákæru.', }, reviewerAssignedModalTitle: { diff --git a/apps/judicial-system/web/src/routes/PublicProsecutor/Indictments/Overview/Overview.tsx b/apps/judicial-system/web/src/routes/PublicProsecutor/Indictments/Overview/Overview.tsx index 6ac0b378751e..36abf477db77 100644 --- a/apps/judicial-system/web/src/routes/PublicProsecutor/Indictments/Overview/Overview.tsx +++ b/apps/judicial-system/web/src/routes/PublicProsecutor/Indictments/Overview/Overview.tsx @@ -8,6 +8,7 @@ import { formatDate } from '@island.is/judicial-system/formatters' import { core, titles } from '@island.is/judicial-system-web/messages' import { BlueBox, + BlueBoxWithDate, CourtCaseInfo, FormContentContainer, FormContext, @@ -23,8 +24,8 @@ import { // useIndictmentsLawsBroken, NOTE: Temporarily hidden while list of laws broken is not complete UserContext, } from '@island.is/judicial-system-web/src/components' -import BlueBoxWithDate from '@island.is/judicial-system-web/src/components/BlueBoxWithIcon/BlueBoxWithDate' import { useProsecutorSelectionUsersQuery } from '@island.is/judicial-system-web/src/components/ProsecutorSelection/prosecutorSelectionUsers.generated' +import { CaseIndictmentRulingDecision } from '@island.is/judicial-system-web/src/graphql/schema' import { useCase } from '@island.is/judicial-system-web/src/utils/hooks' import { strings } from './Overview.strings' @@ -102,11 +103,7 @@ export const Overview = () => { {workingCase.defendants?.map((defendant) => ( - + ))} @@ -130,9 +127,14 @@ export const Overview = () => { description={ {fm(strings.reviewerSubtitle, { + isFine: + workingCase.indictmentRulingDecision === + CaseIndictmentRulingDecision.FINE, indictmentAppealDeadline: formatDate( workingCase.indictmentAppealDeadline, ), + appealDeadlineIsInThePast: + workingCase.indictmentVerdictAppealDeadlineExpired, })} } diff --git a/apps/judicial-system/web/src/routes/PublicProsecutor/Tables/CasesReviewed.strings.ts b/apps/judicial-system/web/src/routes/PublicProsecutor/Tables/CasesReviewed.strings.ts index 85cb3bb24b15..130b6c5b87da 100644 --- a/apps/judicial-system/web/src/routes/PublicProsecutor/Tables/CasesReviewed.strings.ts +++ b/apps/judicial-system/web/src/routes/PublicProsecutor/Tables/CasesReviewed.strings.ts @@ -18,6 +18,12 @@ export const strings = defineMessages({ description: 'Notað sem texti á tagg fyrir "Unun" tillögu í yfirlesin mál málalista', }, + reviewTagFineAppealed: { + id: 'judicial.system.core:public_prosecutor.tables.cases_reviewed.review_tag_fine_appealed', + defaultMessage: 'Kært', + description: + 'Notað sem texti á tagg fyrir "Kært" tillögu í yfirlesin mál málalista', + }, infoContainerMessage: { id: 'judicial.system.core:public_prosecutor.tables.cases_reviewed.info_container_message', defaultMessage: 'Engin yfirlesin mál.', diff --git a/apps/judicial-system/web/src/routes/PublicProsecutor/Tables/CasesReviewed.tsx b/apps/judicial-system/web/src/routes/PublicProsecutor/Tables/CasesReviewed.tsx index e6a4b98ab6c2..3462a41a096e 100644 --- a/apps/judicial-system/web/src/routes/PublicProsecutor/Tables/CasesReviewed.tsx +++ b/apps/judicial-system/web/src/routes/PublicProsecutor/Tables/CasesReviewed.tsx @@ -4,6 +4,7 @@ import { AnimatePresence } from 'framer-motion' import { Tag, Text } from '@island.is/island-ui/core' import { capitalize } from '@island.is/judicial-system/formatters' +import { CaseIndictmentRulingDecision } from '@island.is/judicial-system/types' import { core, tables } from '@island.is/judicial-system-web/messages' import { SectionHeading } from '@island.is/judicial-system-web/src/components' import { useContextMenu } from '@island.is/judicial-system-web/src/components/ContextMenu/ContextMenu' @@ -31,13 +32,19 @@ const CasesReviewed: FC = ({ loading, cases }) => { const { formatMessage } = useIntl() const { openCaseInNewTabMenuItem } = useContextMenu() - const decisionMapping = { - [IndictmentCaseReviewDecision.ACCEPT]: formatMessage( - strings.reviewTagAccepted, - ), - [IndictmentCaseReviewDecision.APPEAL]: formatMessage( - strings.reviewTagAppealed, - ), + const indictmentReviewDecisionMapping = ( + reviewDecision: IndictmentCaseReviewDecision, + isFine: boolean, + ) => { + if (reviewDecision === IndictmentCaseReviewDecision.ACCEPT) { + return formatMessage(strings.reviewTagAccepted) + } else if (reviewDecision === IndictmentCaseReviewDecision.APPEAL) { + return formatMessage( + isFine ? strings.reviewTagFineAppealed : strings.reviewTagAppealed, + ) + } else { + return null + } } const getVerdictViewTag = (row: CaseListEntry) => { @@ -49,7 +56,9 @@ const CasesReviewed: FC = ({ loading, cases }) => { row.defendants.some((defendant) => defendant.isSentToPrisonAdmin), ) - if (someDefendantIsSentToPrisonAdmin) { + if (row.indictmentRulingDecision === CaseIndictmentRulingDecision.FINE) { + return null + } else if (someDefendantIsSentToPrisonAdmin) { variant = 'red' message = strings.tagVerdictViewSentToPrisonAdmin } else if (!row.indictmentVerdictViewedByAll) { @@ -112,7 +121,11 @@ const CasesReviewed: FC = ({ loading, cases }) => { cell: (row) => ( {row.indictmentReviewDecision && - decisionMapping[row.indictmentReviewDecision]} + indictmentReviewDecisionMapping( + row.indictmentReviewDecision, + row.indictmentRulingDecision === + CaseIndictmentRulingDecision.FINE, + )} ), }, diff --git a/apps/judicial-system/web/src/routes/PublicProsecutor/components/ReviewDecision/ReviewDecision.css.ts b/apps/judicial-system/web/src/routes/PublicProsecutor/components/ReviewDecision/ReviewDecision.css.ts index f3b85cfcbefe..f0211d02ca05 100644 --- a/apps/judicial-system/web/src/routes/PublicProsecutor/components/ReviewDecision/ReviewDecision.css.ts +++ b/apps/judicial-system/web/src/routes/PublicProsecutor/components/ReviewDecision/ReviewDecision.css.ts @@ -4,7 +4,12 @@ import { theme } from '@island.is/island-ui/theme' export const gridRow = style({ display: 'grid', - gridTemplateColumns: '1.6fr 1fr', + gridTemplateColumns: '1fr auto', gridGap: theme.spacing[1], - marginBottom: theme.spacing[1], + + '@media': { + [`screen and (max-width: ${theme.breakpoints.lg}px)`]: { + gridTemplateColumns: '1fr', + }, + }, }) diff --git a/apps/judicial-system/web/src/routes/PublicProsecutor/components/ReviewDecision/ReviewDecision.strings.ts b/apps/judicial-system/web/src/routes/PublicProsecutor/components/ReviewDecision/ReviewDecision.strings.ts index 796a7372f889..c9250aa3fd48 100644 --- a/apps/judicial-system/web/src/routes/PublicProsecutor/components/ReviewDecision/ReviewDecision.strings.ts +++ b/apps/judicial-system/web/src/routes/PublicProsecutor/components/ReviewDecision/ReviewDecision.strings.ts @@ -2,14 +2,14 @@ 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', + id: 'judicial.system.core:public_prosecutor.indictments.review_decision.title_v1', + defaultMessage: 'Ákvörðun um {isFine, select, true {kæru} other {á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', + id: 'judicial.system.core:public_prosecutor.indictments.review_decision.subtitle_v1', defaultMessage: - 'Frestur til að áfrýja dómi rennur út {indictmentAppealDeadline}', + 'Frestur til að {isFine, select, true {kæra viðurlagaákvörðun} other {áfrýja dómi}} {appealDeadlineIsInThePast, select, true {rann} other {rennur}} út {indictmentAppealDeadline}', description: 'Notaður sem undirtitill á ákvörðum um áfrýjun boxi fyrir ákæru.', }, @@ -24,6 +24,18 @@ export const strings = defineMessages({ defaultMessage: 'Una héraðsdómi', description: 'Notaður sem texti fyrir "Una héraðsdómi" radio takka.', }, + appealFineToCourtOfAppeals: { + id: 'judicial.system.core:public_prosecutor.indictments.review_decision.appeal_fine_to_court_of_appeals', + defaultMessage: 'Kæra viðurlagaákvörðun til Landsréttar', + description: + 'Notaður sem texti fyrir "Kæra viðurlagaákvörðun til Landsréttar" radio takka.', + }, + acceptFineDecision: { + id: 'judicial.system.core:public_prosecutor.indictments.review_decision.accept_fine_decision', + defaultMessage: 'Una viðurlagaákvörðun', + description: + 'Notaður sem texti fyrir "Kæra viðurlagaákvörðun" radio takka.', + }, reviewModalTitle: { id: 'judicial.system.core:indictments_review.title', defaultMessage: 'Staðfesta ákvörðun', @@ -31,11 +43,16 @@ export const strings = defineMessages({ }, 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.', }, + reviewModalTextFine: { + id: 'judicial.system.core:indictments_review.modal_text_fine', + defaultMessage: + 'Ertu viss um að þú viljir {reviewerDecision, select, ACCEPT {una viðurlagaákvörðun} APPEAL {kæra viðurlagaákvörðun 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', diff --git a/apps/judicial-system/web/src/routes/PublicProsecutor/components/ReviewDecision/ReviewDecision.tsx b/apps/judicial-system/web/src/routes/PublicProsecutor/components/ReviewDecision/ReviewDecision.tsx index ff25436bea93..119153159b66 100644 --- a/apps/judicial-system/web/src/routes/PublicProsecutor/components/ReviewDecision/ReviewDecision.tsx +++ b/apps/judicial-system/web/src/routes/PublicProsecutor/components/ReviewDecision/ReviewDecision.tsx @@ -21,25 +21,28 @@ import * as styles from './ReviewDecision.css' interface Props { caseId: string indictmentAppealDeadline?: string + indictmentAppealDeadlineIsInThePast?: boolean modalVisible?: boolean setModalVisible: Dispatch> + isFine: boolean onSelect?: () => void } export const ReviewDecision: FC = (props) => { - const { user } = useContext(UserContext) - const router = useRouter() - const { formatMessage: fm } = useIntl() - const { updateCase } = useCase() - const { caseId, indictmentAppealDeadline, + indictmentAppealDeadlineIsInThePast, modalVisible, setModalVisible, + isFine, onSelect, } = props + const { user } = useContext(UserContext) + const router = useRouter() + const { formatMessage: fm } = useIntl() + const { updateCase } = useCase() const [indictmentReviewDecision, setIndictmentReviewDecision] = useState< IndictmentCaseReviewDecision | undefined >(undefined) @@ -58,11 +61,15 @@ export const ReviewDecision: FC = (props) => { const options = [ { - label: fm(strings.appealToCourtOfAppeals), + label: fm( + isFine + ? strings.appealFineToCourtOfAppeals + : strings.appealToCourtOfAppeals, + ), value: IndictmentCaseReviewDecision.APPEAL, }, { - label: fm(strings.acceptDecision), + label: fm(isFine ? strings.acceptFineDecision : strings.acceptDecision), value: IndictmentCaseReviewDecision.ACCEPT, }, ] @@ -74,11 +81,13 @@ export const ReviewDecision: FC = (props) => { return ( {fm(strings.subtitle, { + isFine, indictmentAppealDeadline: formatDate(indictmentAppealDeadline), + appealDeadlineIsInThePast: indictmentAppealDeadlineIsInThePast, })} } @@ -107,9 +116,12 @@ export const ReviewDecision: FC = (props) => { {modalVisible && ( setModalVisible(false)} diff --git a/apps/judicial-system/web/src/routes/Shared/IndictmentOverview/IndictmentOverview.tsx b/apps/judicial-system/web/src/routes/Shared/IndictmentOverview/IndictmentOverview.tsx index 83b140c09ed3..d5d2a465c780 100644 --- a/apps/judicial-system/web/src/routes/Shared/IndictmentOverview/IndictmentOverview.tsx +++ b/apps/judicial-system/web/src/routes/Shared/IndictmentOverview/IndictmentOverview.tsx @@ -285,8 +285,15 @@ const IndictmentOverview: FC = () => { indictmentAppealDeadline={ workingCase.indictmentAppealDeadline ?? '' } + indictmentAppealDeadlineIsInThePast={ + workingCase.indictmentVerdictAppealDeadlineExpired ?? false + } modalVisible={modalVisible} setModalVisible={setModalVisible} + isFine={ + workingCase.indictmentRulingDecision === + CaseIndictmentRulingDecision.FINE + } onSelect={() => setIsReviewDecisionSelected(true)} /> )} diff --git a/libs/judicial-system/types/src/index.ts b/libs/judicial-system/types/src/index.ts index 2a7cf9901626..140f6c9bc92a 100644 --- a/libs/judicial-system/types/src/index.ts +++ b/libs/judicial-system/types/src/index.ts @@ -102,6 +102,7 @@ export { export { getIndictmentVerdictAppealDeadlineStatus, VERDICT_APPEAL_WINDOW_DAYS, + FINE_APPEAL_WINDOW_DAYS, } from './lib/indictmentCase' export type { diff --git a/libs/judicial-system/types/src/lib/indictmentCase.ts b/libs/judicial-system/types/src/lib/indictmentCase.ts index d751d60616b5..f823acbdf4b4 100644 --- a/libs/judicial-system/types/src/lib/indictmentCase.ts +++ b/libs/judicial-system/types/src/lib/indictmentCase.ts @@ -1,6 +1,7 @@ const DAYS_TO_MILLISECONDS = 24 * 60 * 60 * 1000 export const VERDICT_APPEAL_WINDOW_DAYS = 28 -const MILLISECONDS_TO_EXPIRY = VERDICT_APPEAL_WINDOW_DAYS * DAYS_TO_MILLISECONDS +export const FINE_APPEAL_WINDOW_DAYS = 3 +const getDays = (days: number) => days * DAYS_TO_MILLISECONDS /* This function takes an array of verdict info tuples: @@ -12,6 +13,7 @@ const MILLISECONDS_TO_EXPIRY = VERDICT_APPEAL_WINDOW_DAYS * DAYS_TO_MILLISECONDS */ export const getIndictmentVerdictAppealDeadlineStatus = ( verdictInfo?: [boolean, Date | undefined][], + isFine?: boolean, ): [boolean, boolean] => { if ( !verdictInfo || @@ -34,5 +36,10 @@ export const getIndictmentVerdictAppealDeadlineStatus = ( new Date(0), ) - return [true, Date.now() > newestViewDate.getTime() + MILLISECONDS_TO_EXPIRY] + return [ + true, + Date.now() > + newestViewDate.getTime() + + getDays(isFine ? FINE_APPEAL_WINDOW_DAYS : VERDICT_APPEAL_WINDOW_DAYS), + ] }