Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(j-s): Public defender staff overview after review #14848

Merged
merged 19 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,9 @@ export class UpdateDefendantInput {
@IsOptional()
@Field(() => ServiceRequirement, { nullable: true })
readonly serviceRequirement?: ServiceRequirement

@Allow()
@IsOptional()
@Field(() => String, { nullable: true })
readonly verdictViewDate?: string
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,7 @@ export class Defendant {

@Field(() => ServiceRequirement, { nullable: true })
readonly serviceRequirement?: ServiceRequirement

@Field(() => String, { nullable: true })
readonly verdictViewDate?: string
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict'

module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.sequelize.transaction((t) =>
queryInterface.addColumn(
'defendant',
'verdict_view_date',
{ type: Sequelize.DATE, allowNull: true },
{ transaction: t },
),
)
},

down: (queryInterface) => {
return queryInterface.sequelize.transaction((t) =>
queryInterface.removeColumn('defendant', 'verdict_view_date', {
transaction: t,
}),
)
},
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
districtCourtRegistrarRule,
prosecutorRepresentativeRule,
prosecutorRule,
publicProsecutorStaffRule,
} from '../../guards'
import { Case, CaseExistsGuard, CaseWriteGuard, CurrentCase } from '../case'
import { CreateDefendantDto } from './dto/createDefendant.dto'
Expand Down Expand Up @@ -71,6 +72,7 @@ export class DefendantController {
districtCourtJudgeRule,
districtCourtRegistrarRule,
districtCourtAssistantRule,
publicProsecutorStaffRule,
)
@Patch(':defendantId')
@ApiOkResponse({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,9 @@ export class UpdateDefendantDto {
@IsEnum(ServiceRequirement)
@ApiPropertyOptional({ enum: ServiceRequirement })
readonly serviceRequirement?: ServiceRequirement

@IsOptional()
@IsString()
@ApiPropertyOptional()
readonly verdictViewDate?: string
}
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,11 @@ export class Defendant extends Model {
})
@ApiProperty({ enum: ServiceRequirement })
serviceRequirement?: ServiceRequirement

@Column({
type: DataType.STRING,
allowNull: true,
})
@ApiProperty()
verdictViewDate?: string
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
districtCourtRegistrarRule,
prosecutorRepresentativeRule,
prosecutorRule,
publicProsecutorStaffRule,
} from '../../../../guards'
import { DefendantController } from '../../defendant.controller'

Expand All @@ -19,11 +20,12 @@ describe('DefendantController - Update rules', () => {
})

it('should give permission to roles', () => {
expect(rules).toHaveLength(5)
expect(rules).toHaveLength(6)
expect(rules).toContain(prosecutorRule)
expect(rules).toContain(prosecutorRepresentativeRule)
expect(rules).toContain(districtCourtJudgeRule)
expect(rules).toContain(districtCourtRegistrarRule)
expect(rules).toContain(districtCourtAssistantRule)
expect(rules).toContain(publicProsecutorStaffRule)
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ query Case($input: CaseQueryInput!) {
defendantWaivesRightToCounsel
defendantPlea
serviceRequirement
verdictViewDate
unakb marked this conversation as resolved.
Show resolved Hide resolved
}
defenderName
defenderNationalId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ query LimitedAccessCase($input: CaseQueryInput!) {
defenderEmail
defenderPhoneNumber
defendantWaivesRightToCounsel
verdictViewDate
}
defenderName
defenderNationalId
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { style, styleVariants } from '@vanilla-extract/css'

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

const baseGridRow = style({
display: 'grid',
gridGap: theme.spacing[1],
marginBottom: theme.spacing[2],
})

export const gridRow = styleVariants({
withButton: [baseGridRow, { gridTemplateColumns: '5fr 1fr' }],
withoutButton: [baseGridRow, { gridTemplateColumns: '1fr' }],
})

export const infoCardDefendant = style({
display: 'flex',
flexDirection: 'column',

'@media': {
[`screen and (min-width: ${theme.breakpoints.lg}px)`]: {
display: 'block',
},
},
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { defineMessages } from 'react-intl'

export const strings = defineMessages({
appealExpirationDate: {
id: 'judicial.system.core:info_card.defendant_info.appeal_expiration_date',
defaultMessage: 'Áfrýjunarfrestur dómfellda er til {appealExpirationDate}',
description: 'Notað til að birta áfrýjunarfrest dómfellda í ákæru.',
},
appealDateExpired: {
id: 'judicial.system.core:info_card.defendant_info.appeal_date_expired',
defaultMessage: 'Áfrýjunarfrestur dómfellda var til {appealExpirationDate}',
description:
'Notað til að láta vita að áfrýjunarfrestur í ákæru er útrunninn.',
},
appealDateNotBegun: {
id: 'judicial.system.core:info_card.defendant_info.appeal_date_not_begun',
defaultMessage: 'Áfrýjunarfrestur dómfellda er ekki hafinn',
description:
'Notaður til að láta vita að áfrýjunarfrestur dómfellda er ekki hafinn.',
},
defender: {
id: 'judicial.system.core:info_card.defendant_info.defender',
defaultMessage: 'Verjandi',
description: 'Notað til að birta titil á verjanda í ákæru.',
},
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import React, { FC, PropsWithChildren } from 'react'
import { useIntl } from 'react-intl'

import {
Box,
Button,
IconMapIcon,
LinkV2,
Text,
} from '@island.is/island-ui/core'
import { formatDate, formatDOB } from '@island.is/judicial-system/formatters'
import { Defendant } from '@island.is/judicial-system-web/src/graphql/schema'

import { strings } from './DefendantInfo.strings'
import { link } from '../../MarkdownWrapper/MarkdownWrapper.css'
import * as styles from './DefendantInfo.css'

export type DefendantInfoActionButton = {
text: string
onClick: (defendant: Defendant) => void
icon?: IconMapIcon
isDisabled: (defendant: Defendant) => boolean
}

interface DefendantInfoProps {
defendant: Defendant
displayDefenderInfo: boolean
defendantInfoActionButton?: DefendantInfoActionButton
}

export const DefendantInfo: FC<PropsWithChildren<DefendantInfoProps>> = (
props,
) => {
const { defendant, displayDefenderInfo, defendantInfoActionButton } = props
const { formatMessage } = useIntl()

const getAppealExpirationInfo = (viewDate?: string) => {
if (!viewDate) {
return formatMessage(strings.appealDateNotBegun)
}

const today = new Date()
const expiryDate = new Date(viewDate)
expiryDate.setDate(expiryDate.getDate() + 28)

const message =
today < expiryDate
? strings.appealExpirationDate
: strings.appealDateExpired

return formatMessage(message, {
appealExpirationDate: formatDate(expiryDate, 'P'),
})
}

return (
<div
key={defendant.id}
className={
defendantInfoActionButton
? styles.gridRow.withButton
: styles.gridRow.withoutButton
}
>
<div className={styles.infoCardDefendant}>
<span>
<Text as="span" fontWeight="semiBold">
{defendant.name}
{defendant.nationalId &&
`, ${formatDOB(defendant.nationalId, defendant.noNationalId)}`}
</Text>
<Text as="span" fontWeight="light">
{defendant.citizenship && `, (${defendant.citizenship})`}
{defendant.address && `, ${defendant.address}`}
</Text>
</span>

<div>
<Text as="span">
{getAppealExpirationInfo(defendant.verdictViewDate ?? '')}
</Text>
</div>

{defendant.defenderName && displayDefenderInfo && (
<Box
display="flex"
key={defendant.defenderName}
role="paragraph"
marginBottom={1}
>
<Text as="span">{`${formatMessage(strings.defender)}: ${
defendant.defenderName
}`}</Text>
{defendant.defenderEmail && (
<>
<Text as="span" whiteSpace="pre">{`, `}</Text>
<LinkV2
href={`mailto:${defendant.defenderEmail}`}
key={defendant.defenderEmail}
className={link}
>
<Text as="span">{defendant.defenderEmail}</Text>
</LinkV2>
</>
)}
</Box>
)}
</div>

{defendantInfoActionButton && (
<Box>
<Button
variant="text"
size="small"
onClick={() => defendantInfoActionButton.onClick(defendant)}
icon={defendantInfoActionButton.icon}
iconType="outline"
disabled={defendantInfoActionButton.isDisabled(defendant)}
>
{defendantInfoActionButton.text}
</Button>
</Box>
)}
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,3 @@ export const infoCardAdditionalSectionContainer = style({
},
},
})

export const infoCardDefendant = style({
display: 'flex',
flexDirection: 'column',

'@media': {
[`screen and (min-width: ${theme.breakpoints.lg}px)`]: {
display: 'block',
},
},
})
44 changes: 18 additions & 26 deletions apps/judicial-system/web/src/components/InfoCard/InfoCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ import React from 'react'
import { useIntl } from 'react-intl'

import { Box, Icon, IconMapIcon, LinkV2, Text } from '@island.is/island-ui/core'
import { formatDOB } from '@island.is/judicial-system/formatters'
import {
Defendant,
SessionArrangements,
} from '@island.is/judicial-system-web/src/graphql/schema'

import {
DefendantInfo,
DefendantInfoActionButton,
} from './DefendantInfo/DefendantInfo'
import { strings } from './InfoCard.strings'
import { link } from '../MarkdownWrapper/MarkdownWrapper.css'
import * as styles from './InfoCard.css'
Expand All @@ -31,7 +34,11 @@ interface DataSection {
interface Props {
courtOfAppealData?: Array<{ title: string; value?: React.ReactNode }>
data: Array<{ title: string; value?: React.ReactNode }>
defendants?: { title: string; items: Defendant[] }
defendants?: {
title: string
items: Defendant[]
defendantInfoActionButton?: DefendantInfoActionButton
}
unakb marked this conversation as resolved.
Show resolved Hide resolved
defenders?: Defender[]
icon?: IconMapIcon
additionalDataSections?: DataSection[]
Expand Down Expand Up @@ -108,35 +115,20 @@ const InfoCard: React.FC<Props> = (props) => {
<Box
className={(defendants || defenders) && styles.infoCardTitleContainer}
marginBottom={(defendants || defenders) && [2, 2, 3, 3]}
paddingBottom={(defendants || defenders) && [2, 2, 3, 3]}
paddingBottom={(defendants || defenders) && [2, 2, 0, 0]}
>
{defendants && (
<>
<Text variant="h4">{defendants.title}</Text>
<Box marginBottom={defenders ? [2, 2, 3, 3] : 0}>
<Box marginBottom={defenders ? [2, 2, 3, 3] : 0} marginTop={1}>
{defendants.items.map((defendant) => (
<Text key={defendant.id}>
<span className={styles.infoCardDefendant}>
<Text
as="span"
fontWeight="semiBold"
>{`${defendant.name}, `}</Text>
<Text as="span" fontWeight="semiBold">
{defendant.nationalId
? `${formatDOB(
defendant.nationalId,
defendant.noNationalId,
)}, `
: ''}
</Text>
<Text as="span">
{defendant.citizenship && ` (${defendant.citizenship}), `}
</Text>
{defendant.address && (
<Text as="span">{`${defendant.address}`}</Text>
)}
</span>
</Text>
<DefendantInfo
defendant={defendant}
displayDefenderInfo={!defenders}
defendantInfoActionButton={
defendants.defendantInfoActionButton
}
/>
))}
</Box>
</>
Expand Down
Loading
Loading