Repository: island-is/island.is
Pull request #18792: feat(car-recycling): Prevent register lower mileage than is already registered at Samgöngustofa
Author: login: birkirkristmunds
Created: 2025-04-15T08:58:03Z | Merged: 2025-04-16T14:11:52Z

[TS-1006](https://dit-iceland.atlassian.net/browse/TS-1006)


## What

Prevent register lower mileage than is already registered at Samgöngustofa

Specify why you need to achieve this

## Screenshots / Gifs

Attach Screenshots / Gifs to help reviewers understand the scope of the pull request

## Checklist:

- [x] I have performed a self-review of my own code
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [x] Formatting passes locally with my changes
- [x] I have rebased against main before asking for a review


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Added display and validation for the latest recorded vehicle mileage during deregistration, including an informational alert and context-aware error messages.
  - Enhanced vehicle queries and mutations to support mileage, plate count, and plate lost status updates.
  - Vehicle lists and details now show the latest mileage information.
  - Improved layout consistency in vehicle details form with grouped controls and spacing adjustments.
  - Confirm button now disables when the form is invalid or loading.

- **Bug Fixes**
  - Improved form validation to ensure current mileage cannot be entered lower than the previously recorded mileage.

- **Internationalization**
  - Updated English and Icelandic translations to provide detailed labels, validation messages, and informational text for vehicle mileage fields.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/apps/skilavottord/web/components/CarDetailsBox2/CarDetailsBox2.tsx b/apps/skilavottord/web/components/CarDetailsBox2/CarDetailsBox2.tsx
index 0b80532213d7..77daa839eee3 100644
--- a/apps/skilavottord/web/components/CarDetailsBox2/CarDetailsBox2.tsx
+++ b/apps/skilavottord/web/components/CarDetailsBox2/CarDetailsBox2.tsx
@@ -23,7 +23,7 @@ import {
   PlateInfo,
   UseStatus,
 } from '@island.is/skilavottord-web/utils/consts'
-import { Controller } from 'react-hook-form'
+import { Controller, useFormContext } from 'react-hook-form'
 
 interface BoxProps {
   vehicleId: string
@@ -61,9 +61,13 @@ export const CarDetailsBox2: FC<React.PropsWithChildren<BoxProps>> = ({
   const [missingPlates, setMissingPlates] = useState(false)
   const [lostPlate, setLostPlate] = useState(false)
 
+  const {
+    formState: { errors },
+  } = useFormContext()
+
   return (
     <GridContainer>
-      <Stack space={3}>
+      <Stack space={2}>
         <GridRow>
           <GridColumn span={['12/12', '12/12', '12/12', '6/12']}>
             <Box
@@ -86,13 +90,38 @@ export const CarDetailsBox2: FC<React.PropsWithChildren<BoxProps>> = ({
           >
             <InputController
               id="mileage"
-              label={t.currentMileage}
+              label={t.currentMileage.label}
               name="mileage"
               type="number"
               defaultValue={mileage?.toString()}
+              rules={{
+                validate: (value) => {
+                  const newMileage = Number(value)
+                  const registeredMileage = Number(mileage)
+
+                  // Ensure mileage is defined and newMileage is greater than or equal to registeredMileage
+                  if (mileage !== undefined) {
+                    return (
+                      newMileage >= registeredMileage ||
+                      t.currentMileage.rules.validate
+                    )
+                  }
+                  return true
+                },
+              }}
+              error={
+                typeof errors?.mileage?.message === 'string'
+                  ? errors.mileage.message
+                  : undefined
+              }
             />
           </GridColumn>
         </GridRow>
+        <GridRow>
+          <GridColumn span="12/12">
+            <AlertMessage type="info" message={t.currentMileage.info} />
+          </GridColumn>
+        </GridRow>
         <GridRow>
           <GridColumn span="12/12" paddingTop={[3, 3, 3, 3]}>
             <Text variant="h5">{t.numberplate.sectionTitle}</Text>
@@ -126,73 +155,74 @@ export const CarDetailsBox2: FC<React.PropsWithChildren<BoxProps>> = ({
           </Box>
         ) : (
           <Box>
-            {(outInStatus === OutInUsage.IN ||
-              (outInStatus === OutInUsage.OUT &&
-                useStatus === UseStatus.OUT_TICKET)) && (
-              <GridRow>
-                <GridColumn span="12/12">
-                  <SelectController
-                    label={t.numberplate.count}
-                    id="plateCount"
-                    name="plateCount"
-                    options={[
-                      { label: '0', value: 0 },
-                      { label: '1', value: 1 },
-                      { label: '2', value: 2 },
-                    ]}
-                    onSelect={(option) => {
-                      onPlateCountChange(option?.value)
-
-                      if (option?.value === 2) {
-                        setMissingPlates(false)
-                        setLostPlate(false)
-                      } else {
-                        setMissingPlates(true)
-                      }
-                    }}
-                    defaultValue={plateCount}
-                  />
-                </GridColumn>
-              </GridRow>
-            )}
-            {missingPlates && (
-              <GridRow>
-                <GridColumn span="12/12">
-                  <Controller
-                    name="plateLost"
-                    render={({ field: { onChange, value, name } }) => {
-                      return (
-                        <Checkbox
-                          large
-                          name={name}
-                          label={t.numberplate.lost}
-                          onChange={() => {
-                            if (!lostPlate) {
-                              onChange(PlateInfo.PLATE_LOST)
-                            } else {
-                              onChange()
-                            }
+            <Stack space={2}>
+              {(outInStatus === OutInUsage.IN ||
+                (outInStatus === OutInUsage.OUT &&
+                  useStatus === UseStatus.OUT_TICKET)) && (
+                <GridRow>
+                  <GridColumn span="12/12">
+                    <SelectController
+                      label={t.numberplate.count}
+                      id="plateCount"
+                      name="plateCount"
+                      options={[
+                        { label: '0', value: 0 },
+                        { label: '1', value: 1 },
+                        { label: '2', value: 2 },
+                      ]}
+                      onSelect={(option) => {
+                        onPlateCountChange(option?.value)
 
-                            setLostPlate(!lostPlate)
-                          }}
-                        />
-                      )
-                    }}
-                  />
-                </GridColumn>
-              </GridRow>
-            )}
+                        if (option?.value === 2) {
+                          setMissingPlates(false)
+                          setLostPlate(false)
+                        } else {
+                          setMissingPlates(true)
+                        }
+                      }}
+                      defaultValue={plateCount}
+                    />
+                  </GridColumn>
+                </GridRow>
+              )}
+              {missingPlates && (
+                <GridRow>
+                  <GridColumn span="12/12">
+                    <Controller
+                      name="plateLost"
+                      render={({ field: { onChange, name } }) => {
+                        return (
+                          <Checkbox
+                            large
+                            name={name}
+                            label={t.numberplate.lost}
+                            onChange={() => {
+                              if (!lostPlate) {
+                                onChange(PlateInfo.PLATE_LOST)
+                              } else {
+                                onChange()
+                              }
 
-            {lostPlate && (
-              <GridRow>
-                <GridColumn span="12/12">
-                  <AlertMessage
-                    type="info"
-                    message={t.numberplate.missingInfo}
-                  />
-                </GridColumn>
-              </GridRow>
-            )}
+                              setLostPlate(!lostPlate)
+                            }}
+                          />
+                        )
+                      }}
+                    />
+                  </GridColumn>
+                </GridRow>
+              )}
+              {lostPlate && (
+                <GridRow>
+                  <GridColumn span="12/12">
+                    <AlertMessage
+                      type="info"
+                      message={t.numberplate.missingInfo}
+                    />
+                  </GridColumn>
+                </GridRow>
+              )}
+            </Stack>
           </Box>
         )}
       </Stack>
diff --git a/apps/skilavottord/web/graphql/queries.ts b/apps/skilavottord/web/graphql/queries.ts
index 41094621e92f..08029620add3 100644
--- a/apps/skilavottord/web/graphql/queries.ts
+++ b/apps/skilavottord/web/graphql/queries.ts
@@ -156,3 +156,64 @@ export const SkilavottordRecyclingPartnersQuery = gql`
     }
   }
 `
+export const SkilavottordVehicleReadyToDeregisteredQuery = gql`
+  query skilavottordVehicleReadyToDeregisteredQuery($permno: String!) {
+    skilavottordVehicleReadyToDeregistered(permno: $permno) {
+      vehicleId
+      vehicleType
+      newregDate
+      vinNumber
+      mileage
+      recyclingRequests {
+        nameOfRequestor
+      }
+    }
+  }
+`
+
+export const SkilavottordTrafficQuery = gql`
+  query skilavottordTrafficQuery($permno: String!) {
+    skilavottordTraffic(permno: $permno) {
+      permno
+      outInStatus
+      useStatus
+      useStatusName
+    }
+  }
+`
+
+export const SkilavottordRecyclingRequestMutation = gql`
+  mutation skilavottordRecyclingRequestMutation(
+    $permno: String!
+    $requestType: RecyclingRequestTypes!
+  ) {
+    createSkilavottordRecyclingRequest(
+      permno: $permno
+      requestType: $requestType
+    ) {
+      ... on RequestErrors {
+        message
+        operation
+      }
+      ... on RequestStatus {
+        status
+      }
+    }
+  }
+`
+
+export const UpdateSkilavottordVehicleInfoMutation = gql`
+  mutation updateSkilavottordVehicleInfo(
+    $permno: String!
+    $mileage: Float!
+    $plateCount: Float!
+    $plateLost: Boolean!
+  ) {
+    updateSkilavottordVehicleInfo(
+      permno: $permno
+      mileage: $mileage
+      plateCount: $plateCount
+      plateLost: $plateLost
+    )
+  }
+`
diff --git a/apps/skilavottord/web/i18n/locales/en.json b/apps/skilavottord/web/i18n/locales/en.json
index 5120fe9aa525..d8bf779a0c57 100644
--- a/apps/skilavottord/web/i18n/locales/en.json
+++ b/apps/skilavottord/web/i18n/locales/en.json
@@ -329,7 +329,13 @@
         "primaryButton": "Try again",
         "secondaryButton": "Cancel"
       },
-      "currentMileage": "Current mileage",
+      "currentMileage": {
+        "label": "Current mileage",
+        "rules": {
+          "validate": "Current mileage is lower then registered mileage"
+        },
+        "info": "Recycling companies are required to register the current mileage of the car when it is handed over for recycling."
+      },
       "numberplate": {
         "sectionTitle": "Number plates information",
         "alert": {
diff --git a/apps/skilavottord/web/i18n/locales/is.json b/apps/skilavottord/web/i18n/locales/is.json
index 0291a6db3cfe..9f8d84896caa 100644
--- a/apps/skilavottord/web/i18n/locales/is.json
+++ b/apps/skilavottord/web/i18n/locales/is.json
@@ -329,7 +329,13 @@
         "primaryButton": "Reyna aftur",
         "secondaryButton": "Hætta við"
       },
-      "currentMileage": "Núverandi kílómetrastaða",
+      "currentMileage": {
+        "label": "Núverandi kílómetrastaða",
+        "rules": {
+          "validate": "Núverandi km staða er lægri en skráð km staða"
+        },
+        "info": "Móttökuaðila ber að skrá núverandi kílómetrastöðu við afskráningu"
+      },
       "numberplate": {
         "sectionTitle": "Upplýsingar um númeraplötur",
         "alert": {
diff --git a/apps/skilavottord/web/i18n/locales/translation.d.ts b/apps/skilavottord/web/i18n/locales/translation.d.ts
index 3eceb20874c3..1cd50a52c131 100644
--- a/apps/skilavottord/web/i18n/locales/translation.d.ts
+++ b/apps/skilavottord/web/i18n/locales/translation.d.ts
@@ -275,10 +275,20 @@ export interface Deregister {
   buttons: DeregisterButtons
   success: string
   error: CompletedError
-  currentMileage: string
+  currentMileage: Milage
   numberplate: NumberPlate
 }
 
+export interface Milage {
+  label: string
+  rules: MilageRules
+  info: string
+}
+
+export interface MilageRules {
+  validate: string
+}
+
 export interface NumberPlate {
   sectionTitle: string
   alert: DeregisteredMessages
diff --git a/apps/skilavottord/web/screens/DeregisterVehicle/Confirm/Confirm.tsx b/apps/skilavottord/web/screens/DeregisterVehicle/Confirm/Confirm.tsx
index c102ea78bf04..6e13ffc267c4 100644
--- a/apps/skilavottord/web/screens/DeregisterVehicle/Confirm/Confirm.tsx
+++ b/apps/skilavottord/web/screens/DeregisterVehicle/Confirm/Confirm.tsx
@@ -1,5 +1,4 @@
 import { useMutation, useQuery } from '@apollo/client'
-import gql from 'graphql-tag'
 import { useRouter } from 'next/router'
 import React, { FC, useContext, useEffect, useState } from 'react'
 
@@ -24,6 +23,12 @@ import {
   ProcessPageLayout,
 } from '@island.is/skilavottord-web/components'
 import { UserContext } from '@island.is/skilavottord-web/context'
+import {
+  SkilavottordRecyclingRequestMutation,
+  SkilavottordTrafficQuery,
+  SkilavottordVehicleReadyToDeregisteredQuery,
+  UpdateSkilavottordVehicleInfoMutation,
+} from '@island.is/skilavottord-web/graphql/queries'
 import {
   Mutation,
   Query,
@@ -37,68 +42,6 @@ import { OutInUsage, UseStatus } from '@island.is/skilavottord-web/utils/consts'
 import { getYear } from '@island.is/skilavottord-web/utils/dateUtils'
 import { FormProvider, useForm } from 'react-hook-form'
 
-const SkilavottordVehicleReadyToDeregisteredQuery = gql`
-  query skilavottordVehicleReadyToDeregisteredQuery($permno: String!) {
-    skilavottordVehicleReadyToDeregistered(permno: $permno) {
-      vehicleId
-      vehicleType
-      newregDate
-      vinNumber
-      mileage
-      recyclingRequests {
-        nameOfRequestor
-      }
-    }
-  }
-`
-
-const SkilavottordTrafficQuery = gql`
-  query skilavottordTrafficQuery($permno: String!) {
-    skilavottordTraffic(permno: $permno) {
-      permno
-      outInStatus
-      useStatus
-      useStatusName
-    }
-  }
-`
-
-const SkilavottordRecyclingRequestMutation = gql`
-  mutation skilavottordRecyclingRequestMutation(
-    $permno: String!
-    $requestType: RecyclingRequestTypes!
-  ) {
-    createSkilavottordRecyclingRequest(
-      permno: $permno
-      requestType: $requestType
-    ) {
-      ... on RequestErrors {
-        message
-        operation
-      }
-      ... on RequestStatus {
-        status
-      }
-    }
-  }
-`
-
-const UpdateSkilavottordVehicleInfoMutation = gql`
-  mutation updateSkilavottordVehicleInfo(
-    $permno: String!
-    $mileage: Float!
-    $plateCount: Float!
-    $plateLost: Boolean!
-  ) {
-    updateSkilavottordVehicleInfo(
-      permno: $permno
-      mileage: $mileage
-      plateCount: $plateCount
-      plateLost: $plateLost
-    )
-  }
-`
-
 const Confirm: FC<React.PropsWithChildren<unknown>> = () => {
   const [plateCount, setPlateCount] = useState<number>(2)
 
@@ -350,7 +293,10 @@ const Confirm: FC<React.PropsWithChildren<unknown>> = () => {
             </Button>
           </Hidden>
           {vehicle && (
-            <Button onClick={handleConfirm} disabled={loadingTraffic}>
+            <Button
+              onClick={handleConfirm}
+              disabled={loadingTraffic || !methods.formState.isValid}
+            >
               {t.buttons.confirm}
             </Button>
           )}
diff --git a/libs/api/domains/vehicles/src/lib/models/usersVehicles.model.ts b/libs/api/domains/vehicles/src/lib/models/usersVehicles.model.ts
index 594e021951e8..bbc2afb368ab 100644
--- a/libs/api/domains/vehicles/src/lib/models/usersVehicles.model.ts
+++ b/libs/api/domains/vehicles/src/lib/models/usersVehicles.model.ts
@@ -251,6 +251,9 @@ export class VehicleListed {
 
   @Field(() => [VehicleMileageDetail], { nullable: true })
   mileageRegistrationHistory?: Array<VehicleMileageDetail>
+
+  @Field({ nullable: true })
+  latestMileage?: number
 }
 
 @ObjectType()
diff --git a/libs/application/template-api-modules/src/lib/modules/templates/car-recycling/car-recycling.service.ts b/libs/application/template-api-modules/src/lib/modules/templates/car-recycling/car-recycling.service.ts
index 81c4634a2976..3bcc9322980f 100644
--- a/libs/application/template-api-modules/src/lib/modules/templates/car-recycling/car-recycling.service.ts
+++ b/libs/application/template-api-modules/src/lib/modules/templates/car-recycling/car-recycling.service.ts
@@ -44,10 +44,16 @@ export class CarRecyclingService extends BaseTemplateApiService {
       let mileage = 0
       let modelYear = null
 
+      // If mileage is provided, convert it to a number and remove the dot
       if (vehicle.mileage) {
         mileage = +vehicle.mileage.trim().replace(/\./g, '')
       }
 
+      // If no mileage is provided, use the latest mileage
+      if (mileage === 0) {
+        mileage = vehicle.latestMileage || 0
+      }
+
       // Support the newRegistrationDate, for now, to keep backwards compatibility
       if (vehicle.newRegistrationDate) {
         modelYear = new Date(vehicle.newRegistrationDate)
diff --git a/libs/application/templates/car-recycling/src/graphql/queries.ts b/libs/application/templates/car-recycling/src/graphql/queries.ts
index 1d0ec4b420a4..c8ee237c0657 100644
--- a/libs/application/templates/car-recycling/src/graphql/queries.ts
+++ b/libs/application/templates/car-recycling/src/graphql/queries.ts
@@ -11,6 +11,7 @@ export const GetUsersVehiclesV2Query = gql`
         requiresMileageRegistration
         canRegisterMileage
         role
+        latestMileage
       }
       paging {
         pageNumber
diff --git a/libs/application/templates/car-recycling/src/lib/dataSchema.ts b/libs/application/templates/car-recycling/src/lib/dataSchema.ts
index f8f086cd6488..a6004aa89028 100644
--- a/libs/application/templates/car-recycling/src/lib/dataSchema.ts
+++ b/libs/application/templates/car-recycling/src/lib/dataSchema.ts
@@ -1,5 +1,6 @@
 import { z } from 'zod'
 import { FuelCodes } from '../shared'
+import { errorMessages } from './messages'
 
 const Vehicles = z
   .object({
@@ -9,6 +10,7 @@ const Vehicles = z
     mileage: z.string().optional(),
     fuelCode: z.string().optional(), // Not used any more, kept here to keep backwards compatibility
     requiresMileageRegistration: z.boolean().optional(),
+    latestMileage: z.number().optional().nullable(),
   })
   .refine(
     ({ requiresMileageRegistration, fuelCode, mileage }) =>
@@ -16,7 +18,24 @@ const Vehicles = z
       Object.values(FuelCodes).includes(fuelCode as FuelCodes)
         ? Boolean(mileage && mileage !== '' && mileage !== '0 ')
         : true,
-    { path: ['mileage'] },
+    { path: ['mileage'], params: errorMessages.mileageMissing },
+  )
+  .refine(
+    ({ mileage, latestMileage }) => {
+      // Mileage is an string and we need to convert it to a number and remove the dot
+      const computedMileage = +(mileage ?? '0').replace('.', '')
+
+      // If the owner decided to enter mileage then we need to check if the mileage is greater than or equal than last mileage registration
+      if (computedMileage > 0 && latestMileage) {
+        return computedMileage >= latestMileage
+      }
+
+      return true
+    },
+    {
+      path: ['mileage'],
+      params: errorMessages.mileageLowerThanLast,
+    },
   )
 
 export const DataSchema = z.object({
diff --git a/libs/application/templates/car-recycling/src/lib/messages.ts b/libs/application/templates/car-recycling/src/lib/messages.ts
index f9c8bc5137bc..c965cc772270 100644
--- a/libs/application/templates/car-recycling/src/lib/messages.ts
+++ b/libs/application/templates/car-recycling/src/lib/messages.ts
@@ -301,6 +301,11 @@ export const errorMessages = defineMessages({
     defaultMessage: 'Það verður að setja inn kílómetrastöðu',
     description: 'Mileage as a mandatory',
   },
+  mileageLowerThanLast: {
+    id: 'rf.cr.application:error.mileage.lower.than.last',
+    defaultMessage: 'Kílómetrastaðan verður að vera hærri en síðasta skráning',
+    description: 'Mileage must be greater than last registration',
+  },
 })
 
 export const inReviewFormMessages = defineMessages({
diff --git a/libs/application/templates/car-recycling/src/shared/types.ts b/libs/application/templates/car-recycling/src/shared/types.ts
index 738ed4db1201..cf22e4f11330 100644
--- a/libs/application/templates/car-recycling/src/shared/types.ts
+++ b/libs/application/templates/car-recycling/src/shared/types.ts
@@ -7,4 +7,5 @@ export interface VehicleDto extends VehicleMiniDto {
   colorName?: string
   requiresMileageRegistration?: boolean
   modelYear?: number
+  latestMileage?: number
 }


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>libs/application/template-api-modules/src/lib/modules/templates/car-recycling/car-recycling.service.ts (1)</summary><blockquote>

`52-55`: **Appropriate fallback to latest mileage when no value is provided.**

The implementation properly falls back to the `latestMileage` value when the user-provided mileage is zero or empty. This supports the requirement to prevent registering lower mileage values than what's already recorded at the Transport Authority.


However, consider adding a warning log when this fallback is triggered to help with debugging potential issues in the future:

```diff
if (mileage === 0) {
  mileage = vehicle.latestMileage || 0
+ this.logger.warn(`car-recycling: No mileage provided for vehicle ${vehicle.permno}, falling back to latestMileage: ${vehicle.latestMileage || 0}`)
}
```

</blockquote></details>
<details>
<summary>apps/skilavottord/web/i18n/locales/en.json (1)</summary><blockquote>

`332-338`: **Consistent localization structure across languages.**

The English localization follows the same structured format as the Icelandic version, ensuring consistent user experience across languages. One small spelling issue to note:

The validation message uses "lower then registered mileage" where it should be "lower than registered mileage".

```diff
-          "validate": "Current mileage is lower then registered mileage"
+          "validate": "Current mileage is lower than registered mileage"
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 91aa50b720c50b6003ddbbfa2926bf40825cf4ac and fe77d38a7073bf8059675d57a82416b7dd25b670.

</details>

<details>
<summary>📒 Files selected for processing (12)</summary>

* `apps/skilavottord/web/components/CarDetailsBox2/CarDetailsBox2.tsx` (4 hunks)
* `apps/skilavottord/web/graphql/queries.ts` (1 hunks)
* `apps/skilavottord/web/i18n/locales/en.json` (1 hunks)
* `apps/skilavottord/web/i18n/locales/is.json` (1 hunks)
* `apps/skilavottord/web/i18n/locales/translation.d.ts` (2 hunks)
* `apps/skilavottord/web/screens/DeregisterVehicle/Confirm/Confirm.tsx` (2 hunks)
* `libs/api/domains/vehicles/src/lib/models/usersVehicles.model.ts` (1 hunks)
* `libs/application/template-api-modules/src/lib/modules/templates/car-recycling/car-recycling.service.ts` (1 hunks)
* `libs/application/templates/car-recycling/src/graphql/queries.ts` (1 hunks)
* `libs/application/templates/car-recycling/src/lib/dataSchema.ts` (2 hunks)
* `libs/application/templates/car-recycling/src/lib/messages.ts` (1 hunks)
* `libs/application/templates/car-recycling/src/shared/types.ts` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>📓 Path-based instructions (2)</summary>

<details>
<summary>`libs/**/*`: "Confirm that the code adheres to the following:
- Reusability of components and hooks across different NextJS apps.
- TypeScript usage for defining props and exportin...</summary>

> `libs/**/*`: "Confirm that the code adheres to the following:
> - Reusability of components and hooks across different NextJS apps.
> - TypeScript usage for defining props and exporting types.
> - Effective tree-shaking and bundling practices."
> 

- `libs/api/domains/vehicles/src/lib/models/usersVehicles.model.ts`
- `libs/application/templates/car-recycling/src/lib/dataSchema.ts`
- `libs/application/templates/car-recycling/src/graphql/queries.ts`
- `libs/application/template-api-modules/src/lib/modules/templates/car-recycling/car-recycling.service.ts`
- `libs/application/templates/car-recycling/src/lib/messages.ts`
- `libs/application/templates/car-recycling/src/shared/types.ts`

</details>
<details>
<summary>`apps/**/*`: "Confirm that the code adheres to the following:
- NextJS best practices, including file structure, API routes, and static generation methods.
- Efficient state manage...</summary>

> `apps/**/*`: "Confirm that the code adheres to the following:
> - NextJS best practices, including file structure, API routes, and static generation methods.
> - Efficient state management and server-side rendering techniques.
> - Optimal use of TypeScript for component and utility type safety."
> 

- `apps/skilavottord/web/i18n/locales/is.json`
- `apps/skilavottord/web/screens/DeregisterVehicle/Confirm/Confirm.tsx`
- `apps/skilavottord/web/i18n/locales/translation.d.ts`
- `apps/skilavottord/web/components/CarDetailsBox2/CarDetailsBox2.tsx`
- `apps/skilavottord/web/i18n/locales/en.json`
- `apps/skilavottord/web/graphql/queries.ts`

</details>

</details><details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>libs/application/templates/car-recycling/src/lib/dataSchema.ts (1)</summary><blockquote>

<details>
<summary>libs/application/templates/car-recycling/src/lib/messages.ts (1)</summary>

* `errorMessages` (262-309)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (12)</summary>

* GitHub Check: linting-workspace
* GitHub Check: build (system-e2e)
* GitHub Check: build (skilavottord-web)
* GitHub Check: build (application-system-api)
* GitHub Check: tests (skilavottord-web)
* GitHub Check: build (api)
* GitHub Check: linting (skilavottord-web,api-domains-vehicles,api,application-template-api-modules,application-s...
* GitHub Check: build (application-system-form)
* GitHub Check: tests (application-system-api)
* GitHub Check: formatting
* GitHub Check: tests (application-system-form,application-template-api-modules,application-template-loader,appli...
* GitHub Check: tests (api,api-domains-application,api-domains-vehicles)

</details>

<details>
<summary>🔇 Additional comments (23)</summary><blockquote>

<details>
<summary>libs/application/templates/car-recycling/src/shared/types.ts (1)</summary>

`10-10`: **Appropriate addition of `latestMileage` field.**

The `latestMileage` field is correctly defined as an optional number property in the `VehicleDto` interface. This enables tracking the most recently registered mileage value from the Transport Authority to prevent registering lower values.

</details>
<details>
<summary>libs/api/domains/vehicles/src/lib/models/usersVehicles.model.ts (1)</summary>

`254-256`: **GraphQL model properly extended with `latestMileage` field.**

The `latestMileage` field is correctly added to the `VehicleListed` GraphQL object type with appropriate nullable configuration. This aligns with the corresponding changes in the data interface and supports the new validation requirements.

</details>
<details>
<summary>libs/application/templates/car-recycling/src/graphql/queries.ts (1)</summary>

`14-14`: **Query updated to fetch the `latestMileage` data.**

The GraphQL query has been updated to include the `latestMileage` field in the selection set, which is necessary to retrieve this information from the backend. This change supports the mileage validation feature being implemented.

</details>
<details>
<summary>libs/application/template-api-modules/src/lib/modules/templates/car-recycling/car-recycling.service.ts (1)</summary>

`47-51`: **Good clarification in code comment.**

The updated comment accurately describes what the code is doing - converting a string mileage value to a number and removing dots. This improves code readability.

</details>
<details>
<summary>libs/application/templates/car-recycling/src/lib/dataSchema.ts (4)</summary>

`3-3`: **Good addition of error messages import.**

This import is necessary for utilizing the structured error messages in the validation refinements. Good practice to keep validation messages separate for better maintainability.

---

`13-13`: **Properly typed new field for tracking latest mileage.**

The `latestMileage` field is correctly defined as optional and nullable, following TypeScript best practices. This field enables the comparison between current mileage input and previously recorded values at Samgöngustofa.

---

`21-21`: **Improved validation error message handling.**

Updating the refinement to include structured error message parameters enhances the user experience by providing more informative feedback.

---

`23-39`: **Well-implemented mileage validation logic.**

This refinement effectively fulfills the PR objective of preventing users from registering a lower mileage than what's already recorded at Samgöngustofa.

The implementation:
1. Properly converts string mileage to numeric format
2. Checks current mileage only when relevant (>0 and latestMileage exists)
3. Provides appropriate error messaging when validation fails

The code is clean, well-commented, and follows best practices for schema validation in Zod.

</details>
<details>
<summary>apps/skilavottord/web/i18n/locales/is.json (1)</summary>

`332-338`: **Good localization structure for mileage validation.**

Converting the current mileage entry from a simple string to a structured object with label, validation rules, and contextual information improves user experience. This change properly supports the form validation logic implemented in the backend.

The Icelandic validation message correctly informs users that the current mileage must be higher than what's already registered, and the informational text explains the requirement for recycling companies.

</details>
<details>
<summary>libs/application/templates/car-recycling/src/lib/messages.ts (1)</summary>

`304-308`: **Good addition of validation message for mileage comparison.**

This message properly supports the new validation logic in the Vehicles schema. The message is clear and the ID follows the established naming convention in the file. The description accurately explains the purpose of the message.

This change completes the implementation of the mileage validation feature by providing the necessary user feedback when validation fails.

</details>
<details>
<summary>apps/skilavottord/web/screens/DeregisterVehicle/Confirm/Confirm.tsx (2)</summary>

`26-31`: **Good refactoring: Centralizing GraphQL queries and mutations.**

Moving these GraphQL operations from inline definitions to imports from a centralized module improves code organization and maintainability. This approach makes it easier to reuse queries across components and simplifies future modifications.

---

`296-299`: **Enhanced form validation for the confirm button.**

Adding the form validation check (`!methods.formState.isValid`) to the disabled state ensures users cannot submit invalid data. This is crucial for enforcing the new mileage validation rules that prevent users from registering a lower mileage than what's already on record.

</details>
<details>
<summary>apps/skilavottord/web/i18n/locales/translation.d.ts (2)</summary>

`278-278`: **Type enhancement: Structured validation messages for mileage.**

Converting `currentMileage` from a string to a structured interface improves type safety and enables more sophisticated validation messaging.

---

`282-290`: **Well-designed type interfaces for mileage validation.**

The introduction of `Milage` and `MilageRules` interfaces provides proper typing for structured validation messages, improving code reliability and developer experience. This supports the feature's requirement to prevent lower mileage registration than what's recorded at Samgöngustofa.


Note: There's a minor typo in the interface name - "Milage" should be "Mileage", but since this is consistent across the codebase, it's better to address in a separate refactoring PR.

</details>
<details>
<summary>apps/skilavottord/web/components/CarDetailsBox2/CarDetailsBox2.tsx (5)</summary>

`26-26`: **Enhanced form integration with react-hook-form.**

Adding the `useFormContext` import enables access to form state and validation, which is necessary for implementing the mileage validation feature.

---

`64-66`: **Proper form state access for validation error handling.**

Using `useFormContext` to access form errors follows React Hook Form best practices and enables displaying validation errors to users.

---

`92-117`: **Robust mileage validation implementation.**

The validation logic effectively prevents users from entering a mileage value lower than what's already registered, addressing the core requirement of the ticket TS-1006. The error message is properly retrieved from i18n resources, ensuring consistent UX across different languages.


The validation approach is well-structured with:
1. Clear comparison between new and registered mileage
2. Proper error handling with internationalized messages
3. Null/undefined checks to prevent runtime errors

---

`120-124`: **Informative user guidance for mileage input.**

Adding an information alert below the mileage field provides users with context about why this validation exists, improving the overall user experience. This follows best practices for form design by providing guidance at the point of interaction.

---

`158-225`: **Improved UI organization with Stack component.**

Wrapping related form elements with the Stack component creates better visual grouping and spacing, enhancing the UI. This follows Island.is design system best practices for composing UI components.

</details>
<details>
<summary>apps/skilavottord/web/graphql/queries.ts (4)</summary>

`159-172`: **Well-structured GraphQL query for vehicle data.**

The `SkilavottordVehicleReadyToDeregisteredQuery` properly fetches all necessary vehicle information, including the mileage field which is crucial for the validation feature. The query is well-named and focused on a specific use case.

---

`174-183`: **Effective traffic data query for status checking.**

The `SkilavottordTrafficQuery` retrieves essential traffic status information that helps determine the vehicle's current state in the system. This supports the overall deregistration workflow.

---

`185-203`: **Well-designed mutation with proper error handling.**

The `SkilavottordRecyclingRequestMutation` includes fragment spreads to handle different response types (errors vs. success status), which demonstrates good GraphQL practices. The mutation is focused on a single responsibility: creating recycling requests.

---

`205-219`: **Comprehensive vehicle update mutation.**

The `UpdateSkilavottordVehicleInfoMutation` properly includes all required fields for updating vehicle information, including mileage, plate count, and plate status. This mutation is essential for persisting the validated mileage data to prevent rollback.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-15T09:11:16Z
user: login: coderabbitai[bot]
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-15T09:11:19Z
user: login: thorkellmani
body: One question
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-15T15:02:18Z
user: login: birkirkristmunds
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-15T15:42:20Z
user: login: thorkellmani
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-15T16:17:41Z
user: login: thorkellmani
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-15T16:17:57Z
user: login: veronikasif
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-16T12:18:50Z
user: login: birkirkristmunds
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-16T12:22:22Z

Review comments:
diff_hunk: @@ -251,6 +251,9 @@ export class VehicleListed {
 
   @Field(() => [VehicleMileageDetail], { nullable: true })
   mileageRegistrationHistory?: Array<VehicleMileageDetail>
+
+  @Field({ nullable: true })
+  latestMileage?: number
path: libs/api/domains/vehicles/src/lib/models/usersVehicles.model.ts
user: login: thorkellmani
body: This is the last mileage registered right? There is a property above called `lastMileageRegistration` which contains the last mileage submitted by the user, so might suffice without adding a new property.
created_at: 2025-04-15T15:01:52Z
author_association: MEMBER
diff_hunk: @@ -251,6 +251,9 @@ export class VehicleListed {
 
   @Field(() => [VehicleMileageDetail], { nullable: true })
   mileageRegistrationHistory?: Array<VehicleMileageDetail>
+
+  @Field({ nullable: true })
+  latestMileage?: number
path: libs/api/domains/vehicles/src/lib/models/usersVehicles.model.ts
user: login: birkirkristmunds
body: Yes it is.

I was trying to use that one but it is always returning null value, so therefore I added the new one.

<img width="359" alt="image" src="https://github.com/user-attachments/assets/13fa145b-ea29-494f-ad37-f439bd07495d" />

created_at: 2025-04-15T15:42:20Z
author_association: MEMBER
diff_hunk: @@ -251,6 +251,9 @@ export class VehicleListed {
 
   @Field(() => [VehicleMileageDetail], { nullable: true })
   mileageRegistrationHistory?: Array<VehicleMileageDetail>
+
+  @Field({ nullable: true })
+  latestMileage?: number
path: libs/api/domains/vehicles/src/lib/models/usersVehicles.model.ts
user: login: thorkellmani
body: Ah yeah, that property is only available in the  `vehiclesListV3` query.
created_at: 2025-04-15T16:17:41Z
author_association: MEMBER
diff_hunk: @@ -1,4 +1,4 @@
-// To parse this data:
+validate // To parse this data:
path: apps/skilavottord/web/i18n/locales/translation.d.ts
user: login: veronikasif
body: Is this correct? 🤔 
created_at: 2025-04-16T11:34:45Z
author_association: MEMBER
diff_hunk: @@ -1,4 +1,4 @@
-// To parse this data:
+validate // To parse this data:
path: apps/skilavottord/web/i18n/locales/translation.d.ts
user: login: birkirkristmunds
body: what? no....  very strange ...
created_at: 2025-04-16T12:22:22Z
author_association: MEMBER

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-04-15T08:58:11Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

The changes introduce enhanced mileage validation and error handling across the car recycling and deregistration workflows. GraphQL queries and mutations are expanded to support new vehicle data fields, including `latestMileage`. The UI components and form schemas are updated to utilize structured validation messages and provide contextual information to users regarding mileage input requirements. The data schema and TypeScript interfaces are refined to accommodate the new `latestMileage` field and enforce that entered mileage cannot be less than the last recorded value. Internationalization files and types are updated to support these new validation and informational messages.

## Changes

| File(s)                                                                                                 | Change Summary |
|---------------------------------------------------------------------------------------------------------|----------------|
| `apps/skilavottord/web/components/CarDetailsBox2/CarDetailsBox2.tsx`                                   | Integrated `useFormContext` for error handling, added validation for mileage input, displayed info alert, and improved layout structure. |
| `apps/skilavottord/web/graphql/queries.ts`                                                             | Added four new GraphQL operations for vehicle deregistration, traffic status, recycling requests, and vehicle info updates. |
| `apps/skilavottord/web/i18n/locales/en.json`,<br>`apps/skilavottord/web/i18n/locales/is.json`          | Changed "currentMileage" from a string to an object with label, validation rule, and info fields for structured messaging. |
| `apps/skilavottord/web/i18n/locales/translation.d.ts`                                                  | Updated `Deregister` interface: changed `currentMileage` to a structured `Milage` object with validation and info fields. |
| `apps/skilavottord/web/screens/DeregisterVehicle/Confirm/Confirm.tsx`                                  | Replaced inline GraphQL queries/mutations with imports; updated confirm button to check form validity before enabling. |
| `libs/api/domains/vehicles/src/lib/models/usersVehicles.model.ts`                                      | Added nullable `latestMileage` field to the `VehicleListed` class. |
| `libs/application/template-api-modules/src/lib/modules/templates/car-recycling/car-recycling.service.ts`| Enhanced mileage handling in `createVehicle` to fallback to `latestMileage` if mileage is zero or missing. |
| `libs/application/templates/car-recycling/src/graphql/queries.ts`                                      | Included `latestMileage` in the `GetUsersVehiclesV2Query` vehicle list selection set. |
| `libs/application/templates/car-recycling/src/lib/dataSchema.ts`                                       | Added `latestMileage` to schema, improved mileage validation to ensure it is not less than the last recorded value. |
| `libs/application/templates/car-recycling/src/lib/messages.ts`                                         | Added new error message `mileageLowerThanLast` for mileage validation. |
| `libs/application/templates/car-recycling/src/shared/types.ts`                                         | Added optional `latestMileage` property to `VehicleDto` interface. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant WebApp
    participant GraphQLAPI
    participant DB

    User->>WebApp: Enter vehicle mileage in deregistration form
    WebApp->>WebApp: Validate mileage (must be >= latestMileage)
    alt Mileage invalid
        WebApp->>User: Show validation error and info alert
    else Mileage valid
        User->>WebApp: Submit form
        WebApp->>GraphQLAPI: UpdateSkilavottordVehicleInfoMutation
        GraphQLAPI->>DB: Update vehicle info
        DB-->>GraphQLAPI: Confirmation
        GraphQLAPI-->>WebApp: Success response
        WebApp->>User: Show success/next step
    end
```

## Suggested labels

`deploy-feature`

## Suggested reviewers

- thordurhhh
- disaerna
- baering

<!-- walkthrough_end -->

<!-- announcements_start -->

> [!TIP]
> <details>
> <summary>⚡💬 Agentic Chat (Pro Plan, General Availability)</summary>
> 
> - We're introducing multi-step agentic chat in review comments and issue comments, within and outside of PR's. This feature enhances review and issue discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments and add commits to existing pull requests.
> 
> </details>

<!-- announcements_end -->

---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between fe77d38a7073bf8059675d57a82416b7dd25b670 and cd1c9d007d9c8a37e3b881752d04e7dee3c4ce2c.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `apps/skilavottord/web/i18n/locales/translation.d.ts` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* apps/skilavottord/web/i18n/locales/translation.d.ts

</details>

</details>
<!-- internal state start -->


<!-- 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= -->

<!-- internal state end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=island-is/island.is&utm_content=18792):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: nx-cloud[bot]
created_at: 2025-04-15T09:01:20Z
body: 
View your [CI Pipeline Execution ↗](https://cloud.nx.app/cipes/67ffa16c7962132f8b9c0d09?utm_source=pull-request&utm_medium=comment) for commit cd1c9d007d9c8a37e3b881752d04e7dee3c4ce2c.

| Command | Status | Duration | Result |
|---------|--------|----------:|--------|
| `nx run-many --projects application-system-api -...` | ✅ Succeeded | 11m 38s | [View ↗](https://cloud.nx.app/runs/LA1r7GAt7z?utm_source=pull-request&utm_medium=comment) |
| `nx run-many --projects application-system-form,...` | ✅ Succeeded | 6m 21s | [View ↗](https://cloud.nx.app/runs/ZgbDTa000p?utm_source=pull-request&utm_medium=comment) |
| `nx run-many --target=build --projects=api --par...` | ✅ Succeeded | 3m 57s | [View ↗](https://cloud.nx.app/runs/DXCe5pK4Um?utm_source=pull-request&utm_medium=comment) |
| `nx run-many --target=build --projects=skilavott...` | ✅ Succeeded | 3m 15s | [View ↗](https://cloud.nx.app/runs/ntB7Y9DMZx?utm_source=pull-request&utm_medium=comment) |
| `nx run-many --target=build --projects=applicati...` | ✅ Succeeded | 2m 47s | [View ↗](https://cloud.nx.app/runs/699ZDTPmMj?utm_source=pull-request&utm_medium=comment) |
| `nx run-many --target=build --projects=applicati...` | ✅ Succeeded | 1m 40s | [View ↗](https://cloud.nx.app/runs/BBVatsbshH?utm_source=pull-request&utm_medium=comment) |
| `nx run-many -t lint --output-style=static --pro...` | ✅ Succeeded | 2s | [View ↗](https://cloud.nx.app/runs/Yt5PbhYx2u?utm_source=pull-request&utm_medium=comment) |
| `nx run-many --projects api,api-domains-applicat...` | ✅ Succeeded | 7s | [View ↗](https://cloud.nx.app/runs/JvX77EKUZ4?utm_source=pull-request&utm_medium=comment) |
| `Additional runs (6)` | ✅ Succeeded | ... | [View ↗](https://cloud.nx.app/cipes/67ffa16c7962132f8b9c0d09?utm_source=pull-request&utm_medium=comment) |

---

☁️ [Nx Cloud](https://cloud.nx.app?utm_source=pull-request&utm_medium=comment) last updated this comment at `2025-04-16 12:49:08` UTC

<!-- NX_CLOUD_APP_COMMENT_END -->
author_association: NONE
user: login: datadog-island-is[bot]
created_at: 2025-04-15T09:15:08Z
body: 
# Datadog Report

All test runs `74185aa` [:link:](https://app.datadoghq.eu/ci/test-runs?query=%40git.repository.id_v2%3A%22github.com%2Fisland-is%2Fisland.is%22+%40git.commit.sha%3A74185aa59491865ecb7b2db2148d2eab29ce0179+%40git.branch%3A%22feat%2Fcar-recycling-not-allowed-to-register-lower-mileage%22&_user_click_origin_=github)

:white_check_mark: **4 Total Test Services**: 0 Failed, 4 Passed



<details>
<summary>Test Services</summary>

| Service Name | Failed | Known Flaky | New Flaky | Passed | Skipped | Total Time | Test Service View |
| ------------ | ------ | ----------- | --------- | ------ | ------- | --------- | ----------------- |
| [**`api`**](https://app.datadoghq.eu/ci/test-commit/github.com%2Fisland-is%2Fisland.is/api/feat%2Fcar-recycling-not-allowed-to-register-lower-mileage/74185aa59491865ecb7b2db2148d2eab29ce0179?env=ci&index=citest&_user_click_origin_=github) | 0 | 0 | 0 | 4 | 0 | 2.28s |  [Link](https://app.datadoghq.eu/ci/test-branch/github.com%2Fisland-is%2Fisland.is/api/feat%2Fcar-recycling-not-allowed-to-register-lower-mileage?env=ci&index=citest&_user_click_origin_=github) |
| [**`application-system-api`**](https://app.datadoghq.eu/ci/test-commit/github.com%2Fisland-is%2Fisland.is/application-system-api/feat%2Fcar-recycling-not-allowed-to-register-lower-mileage/74185aa59491865ecb7b2db2148d2eab29ce0179?env=ci&index=citest&_user_click_origin_=github) | 0 | 0 | 0 | 46 | 0 | 11m 34.51s |  [Link](https://app.datadoghq.eu/ci/test-branch/github.com%2Fisland-is%2Fisland.is/application-system-api/feat%2Fcar-recycling-not-allowed-to-register-lower-mileage?env=ci&index=citest&_user_click_origin_=github) |
| [**`application-template-api-modules`**](https://app.datadoghq.eu/ci/test-commit/github.com%2Fisland-is%2Fisland.is/application-template-api-modules/feat%2Fcar-recycling-not-allowed-to-register-lower-mileage/74185aa59491865ecb7b2db2148d2eab29ce0179?env=ci&index=citest&_user_click_origin_=github) | 0 | 0 | 0 | 32 | 0 | 6m 9.62s |  [Link](https://app.datadoghq.eu/ci/test-branch/github.com%2Fisland-is%2Fisland.is/application-template-api-modules/feat%2Fcar-recycling-not-allowed-to-register-lower-mileage?env=ci&index=citest&_user_click_origin_=github) |
| [**`application-ui-shell`**](https://app.datadoghq.eu/ci/test-commit/github.com%2Fisland-is%2Fisland.is/application-ui-shell/feat%2Fcar-recycling-not-allowed-to-register-lower-mileage/74185aa59491865ecb7b2db2148d2eab29ce0179?env=ci&index=citest&_user_click_origin_=github) | 0 | 0 | 0 | 74 | 0 | 1m 25.9s |  [Link](https://app.datadoghq.eu/ci/test-branch/github.com%2Fisland-is%2Fisland.is/application-ui-shell/feat%2Fcar-recycling-not-allowed-to-register-lower-mileage?env=ci&index=citest&_user_click_origin_=github) |
</details>



author_association: NONE