Skip to content

Commit

Permalink
feat(j-s): Speeding (#17589)
Browse files Browse the repository at this point in the history
* Add option SPEEDING

* Add legal arguments

* Add input fields

* Add recordedSpeed and speedLimit to db

* Enable seedLimit update

* Add validation

* Update indictment description

* Clear recorded speed and speed limit when removing speedig

* Add error handling

* Fix comment

* Fix lint

* Remove css

* Make Icon position abs

* Rec speed and speed limit is now number

* Remove island-ui change

* Improve validation

* Use Number.isNaN instead of isNaN
  • Loading branch information
oddsson authored and RunarVestmann committed Jan 29, 2025
1 parent 7b4b93e commit fa02386
Show file tree
Hide file tree
Showing 14 changed files with 280 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import { Allow, IsArray, IsEnum, IsOptional } from 'class-validator'
import {
Allow,
IsArray,
IsEnum,
IsNumber,
IsOptional,
Min,
} from 'class-validator'
import { GraphQLJSONObject } from 'graphql-type-json'

import { Field, ID, InputType } from '@nestjs/graphql'
import { Field, ID, InputType, Int } from '@nestjs/graphql'

import type { SubstanceMap } from '@island.is/judicial-system/types'
import {
Expand Down Expand Up @@ -63,4 +70,18 @@ export class UpdateIndictmentCountInput {
@IsEnum(IndictmentSubtype, { each: true })
@Field(() => [IndictmentSubtype], { nullable: true })
readonly indictmentCountSubtypes?: IndictmentSubtype[]

@Allow()
@IsOptional()
@IsNumber()
@Min(0)
@Field(() => Int, { nullable: true })
readonly recordedSpeed?: number

@Allow()
@IsOptional()
@IsNumber()
@Min(0)
@Field(() => Int, { nullable: true })
readonly speedLimit?: number
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { GraphQLJSONObject } from 'graphql-type-json'

import { Field, ID, ObjectType, registerEnumType } from '@nestjs/graphql'
import { Field, ID, Int, ObjectType, registerEnumType } from '@nestjs/graphql'

import type { SubstanceMap } from '@island.is/judicial-system/types'
import {
Expand Down Expand Up @@ -48,4 +48,10 @@ export class IndictmentCount {

@Field(() => [IndictmentSubtype], { nullable: true })
readonly indictmentCountSubtypes?: IndictmentSubtype[]

@Field(() => Int, { nullable: true })
readonly recordedSpeed?: number

@Field(() => Int, { nullable: true })
readonly speedLimit?: number
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
module.exports = {
async up(queryInterface, Sequelize) {
return queryInterface.sequelize.transaction((t) =>
Promise.all([
queryInterface.addColumn(
'indictment_count',
'recorded_speed',
{
type: Sequelize.INTEGER,
allowNull: true,
},
{ transaction: t },
),
queryInterface.addColumn(
'indictment_count',
'speed_limit',
{
type: Sequelize.INTEGER,
allowNull: true,
},
{ transaction: t },
),
]),
)
},
async down(queryInterface) {
return queryInterface.sequelize.transaction((t) =>
Promise.all([
queryInterface.removeColumn('indictment_count', 'recorded_speed', {
transaction: t,
}),
queryInterface.removeColumn('indictment_count', 'speed_limit', {
transaction: t,
}),
]),
)
},
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import {
IsArray,
IsEnum,
IsNumber,
IsObject,
IsOptional,
IsString,
MaxLength,
Min,
} from 'class-validator'

import { ApiPropertyOptional } from '@nestjs/swagger'
Expand Down Expand Up @@ -59,4 +61,16 @@ export class UpdateIndictmentCountDto {
@IsEnum(IndictmentSubtype, { each: true })
@ApiPropertyOptional({ enum: IndictmentSubtype, isArray: true })
readonly indictmentCountSubtypes?: IndictmentSubtype[]

@IsOptional()
@IsNumber()
@Min(0)
@ApiPropertyOptional({ type: Number })
readonly recordedSpeed?: number

@IsOptional()
@IsNumber()
@Min(0)
@ApiPropertyOptional({ type: Number })
readonly speedLimit?: number
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,12 @@ export class IndictmentCount extends Model {
})
@ApiPropertyOptional({ enum: IndictmentSubtype, isArray: true })
indictmentCountSubtypes?: IndictmentSubtype[]

@Column({ type: DataType.INTEGER, allowNull: true })
@ApiPropertyOptional({ type: Number })
recordedSpeed?: number

@Column({ type: DataType.INTEGER, allowNull: true })
@ApiPropertyOptional({ type: Number })
speedLimit?: number
}
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ query Case($input: CaseQueryInput!) {
incidentDescription
legalArguments
indictmentCountSubtypes
recordedSpeed
speedLimit
}
requestDriversLicenseSuspension
appealState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,24 @@ export const indictmentCount = defineMessages({
description:
'Notaður sem skýritexti á "vínandamagn" svæði á ákæruliða skrefi í ákærum.',
},
speedingTitle: {
id: 'judicial.system.core:indictments_indictment.indictment_count.speeding',
defaultMessage: 'Hraði',
description:
'Notaður sem titill á "Hraði" svæði á ákæruliða skrefi í ákærum.',
},
recordedSpeedLabel: {
id: 'judicial.system.core:indictments_indictment.indictment_count.recorded_speed_label',
defaultMessage: 'Mældur hraði (km/klst)',
description:
'Notaður sem titill á "Mældur hraði" innsláttarsvæði á ákæruliða skrefi í ákærum.',
},
speedLimitLabel: {
id: 'judicial.system.core:indictments_indictment.indictment_count.speed_limit_label',
defaultMessage: 'Leyfilegur hámarkshraði (km/klst)',
description:
'Notaður sem titill á "Leyfilegur hámarkshraði" innsláttarsvæði á ákæruliða skrefi í ákærum.',
},
lawsBrokenTitle: {
id: 'judicial.system.core:indictments_indictment.indictment_count.laws_broken_title',
defaultMessage: 'Lagaákvæði',
Expand All @@ -118,10 +136,15 @@ export const indictmentCount = defineMessages({
defaultMessage: '{paragraph}. mgr. {article}. gr. umfl.',
description: 'Notaður sem texti í lagaákvæði taggi.',
},
lawsBrokenTagArticleOnly: {
id: 'judicial.system.core:indictments_indictment.indictment_count.laws_broken_tag_article_only',
defaultMessage: '{article}. gr. umfl.',
description: 'Notaður sem texti í lagaákvæði taggi.',
},
incidentDescriptionAutofill: {
id: 'judicial.system.core:indictments_indictment.indictment_count.incident_description_auto_fill',
id: 'judicial.system.core:indictments_indictment.indictment_count.incident_description_auto_fill_v1',
defaultMessage:
'fyrir umferðarlagabrot með því að hafa, {incidentDate}, ekið bifreiðinni {vehicleRegistrationNumber} {reason} um {incidentLocation}, þar sem lögregla stöðvaði aksturinn.',
'fyrir umferðarlagabrot með því að hafa, {incidentDate}, ekið bifreiðinni {vehicleRegistrationNumber} {reason} um {incidentLocation}, {isSpeeding, select, true {á {recordedSpeed} km hraða á klukkustund, að teknu tilliti til vikmarka, þar sem leyfður hámarkshraði var {speedLimit} km/klst} other {þar sem lögregla stöðvaði aksturinn}}.',
description:
'Notaður sem skýritexti á "atvikalýsing" svæði á ákæruliða skrefi í umferðalagabrots ákærum.',
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ const offensesCompare = (
return 0
}

/**
* Indicates what laws are broken for each offence. The first number is
* the paragraph and the second is the article, i.e. [49, 2] means paragraph
* 49, article 2. If article is set to 0, that means that an article is
* not specified.
*/
const offenseLawsMap: Record<
IndictmentCountOffense | 'DRUNK_DRIVING_MINOR' | 'DRUNK_DRIVING_MAJOR',
[number, number][]
Expand All @@ -98,6 +104,7 @@ const offenseLawsMap: Record<
[48, 1],
[48, 2],
],
[IndictmentCountOffense.SPEEDING]: [[37, 0]],
}

const generalLaws: [number, number][] = [[95, 1]]
Expand Down Expand Up @@ -267,11 +274,15 @@ export const getLegalArguments = (
}

let articles = `${lawsBroken[0][1]}.`
const noArticle = articles === '0.'

for (let i = 1; i < lawsBroken.length; i++) {
let useSbr = true

if (lawsBroken[i][0] !== lawsBroken[i - 1][0]) {
articles = `${articles} mgr. ${lawsBroken[i - 1][0]}. gr.`
articles = `${noArticle ? '' : `${articles} mgr. `}${
lawsBroken[i - 1][0]
}. gr.`
useSbr = i > andIndex
}

Expand Down Expand Up @@ -334,11 +345,21 @@ export const getIncidentDescription = (
formatMessage,
)

const isSpeeding = indictmentCount.offenses?.includes(
IndictmentCountOffense.SPEEDING,
)

const recordedSpeed = indictmentCount.recordedSpeed ?? '[Mældur hraði]'
const speedLimit = indictmentCount.speedLimit ?? '[Leyfilegur hraði]'

return formatMessage(strings.incidentDescriptionAutofill, {
incidentDate,
vehicleRegistrationNumber: vehicleRegistration,
reason,
incidentLocation,
isSpeeding,
recordedSpeed,
speedLimit,
})
}

Expand Down Expand Up @@ -380,6 +401,10 @@ export const IndictmentCount: FC<Props> = ({
useState<string>('')
const [legalArgumentsErrorMessage, setLegalArgumentsErrorMessage] =
useState<string>('')
const [recordedSpeedErrorMessage, setRecordedSpeedErrorMessage] =
useState<string>('')
const [speedLimitErrorMessage, setSpeedLimitErrorMessage] =
useState<string>('')

const subtypes: IndictmentSubtype[] = indictmentCount.policeCaseNumber
? workingCase.indictmentSubtypes[indictmentCount.policeCaseNumber]
Expand Down Expand Up @@ -685,6 +710,10 @@ export const IndictmentCount: FC<Props> = ({
handleIndictmentCountChanges({
offenses,
substances: indictmentCount.substances,
...(offense === IndictmentCountOffense.SPEEDING && {
recordedSpeed: null,
speedLimit: null,
}),
})
}}
>
Expand Down Expand Up @@ -767,6 +796,105 @@ export const IndictmentCount: FC<Props> = ({
</InputMask>
</Box>
)}
{indictmentCount.offenses?.includes(
IndictmentCountOffense.SPEEDING,
) && (
<Box marginBottom={2}>
<SectionHeading
title={formatMessage(strings.speedingTitle)}
heading="h4"
marginBottom={2}
/>
<Box marginBottom={1}>
<InputMask
mask={'999'}
maskPlaceholder={null}
value={indictmentCount.recordedSpeed?.toString() ?? ''}
onChange={(event) => {
const recordedSpeed = parseInt(event.target.value)

removeErrorMessageIfValid(
['empty'],
event.target.value,
recordedSpeedErrorMessage,
setRecordedSpeedErrorMessage,
)

updateIndictmentCountState(
indictmentCount.id,
{ recordedSpeed },
setWorkingCase,
)
}}
onBlur={(event) => {
const recordedSpeed = parseInt(event.target.value)

if (Number.isNaN(recordedSpeed)) {
setRecordedSpeedErrorMessage('Reitur má ekki vera tómur')
return
}

handleIndictmentCountChanges({
recordedSpeed,
})
}}
>
<Input
name="recordedSpeed"
autoComplete="off"
label={formatMessage(strings.recordedSpeedLabel)}
placeholder="0"
required
errorMessage={recordedSpeedErrorMessage}
hasError={recordedSpeedErrorMessage !== ''}
/>
</InputMask>
</Box>
<InputMask
mask={'999'}
maskPlaceholder={null}
value={indictmentCount.speedLimit?.toString() ?? ''}
onChange={(event) => {
const speedLimit = parseInt(event.target.value)

removeErrorMessageIfValid(
['empty'],
event.target.value,
speedLimitErrorMessage,
setSpeedLimitErrorMessage,
)

updateIndictmentCountState(
indictmentCount.id,
{ speedLimit },
setWorkingCase,
)
}}
onBlur={(event) => {
const speedLimit = parseInt(event.target.value)

if (Number.isNaN(speedLimit)) {
setSpeedLimitErrorMessage('Reitur má ekki vera tómur')
return
}

handleIndictmentCountChanges({
speedLimit,
})
}}
>
<Input
name="speedLimit"
autoComplete="off"
label={formatMessage(strings.speedLimitLabel)}
placeholder="0"
required
errorMessage={speedLimitErrorMessage}
hasError={speedLimitErrorMessage !== ''}
/>
</InputMask>
</Box>
)}
{indictmentCount.offenses
?.filter(
(offenseType) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,9 @@ export const indictmentCountEnum = defineMessages({
defaultMessage: 'Lyfjaakstur',
description: 'Notaður sem titill á subtype fyrir "lyfjaakstur" brot.',
},
SPEEDING: {
id: 'judicial.system.core:indictments_indictment.indictment_count_enum.speeding',
defaultMessage: 'Hraðakstur',
description: 'Notaður sem titill á subtype fyrir "hraðakstur" brot.',
},
})
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,6 @@ export const Substance: FC<Props> = ({
}}
errorMessage={substanceAmountMissingErrorMessage}
hasError={substanceAmountMissingErrorMessage !== ''}
></Input>
/>
)
}
Loading

0 comments on commit fa02386

Please sign in to comment.