0 ? 'number-field-errors' : undefined
+ "
class="vd-number-field flex-grow-0 mr-2 mr-sm-4"
@keydown="focusKeyField"
@maska="changeNumberValue"
@blur="validateNumberValue(maskaNumberValue.unmasked)"
@focus="
- () => {
- numberErrors = []
- }
- "
+ () => {
+ numberErrors = [];
+ }
+ "
>
-
{{ numberErrors.join(' ') }}
@@ -350,23 +350,23 @@ export default defineComponent({
:label="keyLabel"
:hint="locales.keyHint"
persistent-hint
- :hide-details="false"
+ hide-details
:color="maskaKeyValue.completed ? 'success' : 'primary'"
:base-color="maskaKeyValue.completed ? 'success' : ''"
:error="keyErrors.length > 0"
:aria-invalid="keyErrors.length > 0"
:aria-errormessage="
- keyErrors.length > 0 ? 'key-field-errors' : undefined
- "
+ keyErrors.length > 0 ? 'key-field-errors' : undefined
+ "
class="vd-key-field flex-grow-0 mr-2 mr-sm-4"
@keyup.delete="focusNumberField"
@maska="changeKeyValue"
@blur="validateKeyValue(maskaKeyValue.unmasked)"
@focus="
- () => {
- keyErrors = []
- }
- "
+ () => {
+ keyErrors = [];
+ }
+ "
>
.v-input__control > .v-input__slot > .v-text-field__slot
- ) {
+ .v-text-field > .v-input__control > .v-input__slot > .v-text-field__slot
+ ) {
width: min-content !important;
}
diff --git a/packages/synapse-bridge/src/patterns/NirField/locales.ts b/packages/synapse-bridge/src/patterns/NirField/locales.ts
index 77736a8ed4..d41b79d564 100644
--- a/packages/synapse-bridge/src/patterns/NirField/locales.ts
+++ b/packages/synapse-bridge/src/patterns/NirField/locales.ts
@@ -1,13 +1,12 @@
export const locales = {
- numberLabel: 'Numéro de sécurité sociale',
- numberHint: '13 caractères',
+ errorRequiredNumber: "Le numéro de sécurité sociale est requis.",
+ errorLengthNumber: (length: number) => `Le numéro de sécurité sociale doit contenir ${length} caractères.`,
+ errorInvalidFormat: "Le format du numéro de sécurité sociale est invalide.",
+ errorRequiredKey: "La clé du numéro de sécurité sociale est requise.",
+ errorLengthKey: (length: number) => `La clé du numéro de sécurité sociale doit contenir ${length} caractères.`,
+ errorInvalidKey: "La clé du numéro de sécurité sociale est invalide.",
+ numberLabel: "Numéro de sécurité sociale",
+ numberHint: "13 caractères",
keyLabel: 'Clé',
- keyHint: '2 chiffres',
- errorRequiredNumber: 'Le numéro de sécurité sociale est obligatoire',
- errorRequiredKey: 'La clé de validation est obligatoire',
- errorLengthNumber: (length: number): string =>
- `La longueur du numéro de sécurité sociale doit être de ${length} caractères.`,
- errorLengthKey: (length: number): string =>
- `La longueur de la clé doit être de ${length} caractères.`,
- errorCorsican: 'Le chiffre précédant A ou B doit être 1 ou 2.',
-} as const
+ keyHint: '2 chiffres'
+} as const ;
diff --git a/packages/synapse-bridge/src/patterns/NirField/nirValidations.ts b/packages/synapse-bridge/src/patterns/NirField/nirValidations.ts
new file mode 100644
index 0000000000..49e01d698f
--- /dev/null
+++ b/packages/synapse-bridge/src/patterns/NirField/nirValidations.ts
@@ -0,0 +1,48 @@
+export const NUMBER_LENGTH = 13;
+export const KEY_LENGTH = 2;
+
+export function checkNIR(nir: string): boolean {
+ nir = nir.replace(/\s+/g, '').toUpperCase();
+
+ const nirRegex = new RegExp(
+ '^' +
+ '(?[12])' +
+ '(?\\d{2})' +
+ '(?0[1-9]|1[0-2]|[2-9][0-9])' +
+ '(?\\d{2}|2A|2B)' +
+ '(?\\d{3})' +
+ '(?\\d{3})' +
+ '(?9[0-7]|[0-8]\\d)?' +
+ '$',
+ 'i'
+ );
+
+ return nirRegex.test(nir);
+}
+
+export function computeNIRKey(nir: string): string {
+ nir = nir.replace(/\s+/g, '').toUpperCase();
+
+ let nirNumberPart = nir.substring(0, 13);
+
+ nirNumberPart = nirNumberPart.replace('2A', '19').replace('2B', '18');
+
+ const nirNumber = BigInt(nirNumberPart);
+
+ const key = 97n - (nirNumber % 97n);
+
+ return key.toString().padStart(2, '0');
+}
+
+export function isNIRKeyValid(nir: string): boolean {
+ nir = nir.replace(/\s+/g, '').toUpperCase();
+
+ if (nir.length !== 15) {
+ return true;
+ }
+
+ const providedKey = nir.substring(13, 15);
+ const computedKey = computeNIRKey(nir);
+
+ return providedKey === computedKey;
+}
diff --git a/packages/synapse-bridge/src/patterns/NirField/tests/NirField.spec.ts b/packages/synapse-bridge/src/patterns/NirField/tests/NirField.spec.ts
index d185297955..0adbd6d939 100644
--- a/packages/synapse-bridge/src/patterns/NirField/tests/NirField.spec.ts
+++ b/packages/synapse-bridge/src/patterns/NirField/tests/NirField.spec.ts
@@ -8,9 +8,9 @@ import { defineComponent } from 'vue'
import { VForm } from 'vuetify/lib/components/index.mjs'
describe('NirField', () => {
- const nir = '195122B120005'
- const key = '29'
- const formattedNir = '1 95 12 2B 120 005'
+ const nir = '2940375120005'
+ const key = '91'
+ const formattedNir = '2 94 03 75 120 005'
it('renders correctly', () => {
const wrapper = mount(NirField, {
@@ -219,7 +219,7 @@ describe('NirField', () => {
await input.trigger('blur')
expect(wrapper.find('#number-field-errors').text()).toBe(
- locales.errorLengthNumber(13)
+ locales.errorLengthNumber(13) + ' ' + locales.errorInvalidFormat
)
})
@@ -238,7 +238,7 @@ describe('NirField', () => {
await numberField.trigger('blur')
expect(wrapper.find('#number-field-errors').text()).toBe(
- locales.errorLengthNumber(13)
+ locales.errorLengthNumber(13) + ' ' + locales.errorInvalidFormat
)
})
@@ -275,7 +275,7 @@ describe('NirField', () => {
await wrapper.setProps({ modelValue: '12345' })
expect(wrapper.find('#number-field-errors').text()).toBe(
- locales.errorLengthNumber(13)
+ locales.errorLengthNumber(13) + ' ' + locales.errorInvalidFormat
)
})
diff --git a/packages/synapse-bridge/src/patterns/NirField/tests/__snapshots__/NirField.spec.ts.snap b/packages/synapse-bridge/src/patterns/NirField/tests/__snapshots__/NirField.spec.ts.snap
index 8bb1482c10..b47de9cb41 100644
--- a/packages/synapse-bridge/src/patterns/NirField/tests/__snapshots__/NirField.spec.ts.snap
+++ b/packages/synapse-bridge/src/patterns/NirField/tests/__snapshots__/NirField.spec.ts.snap
@@ -44,12 +44,7 @@ exports[`NirField > renders correctly 1`] = `
-
+
@@ -91,12 +86,7 @@ exports[`NirField > renders correctly 1`] = `
-
+
@@ -156,12 +146,7 @@ exports[`NirField > renders correctly with 13 characters 1`] = `
-
+
@@ -222,12 +207,7 @@ exports[`NirField > renders correctly with a tooltip 1`] = `
-
+
@@ -269,12 +249,7 @@ exports[`NirField > renders correctly with a tooltip 1`] = `
-
+
@@ -338,12 +313,7 @@ exports[`NirField > renders correctly with outlined prop 1`] = `
-
+
@@ -388,12 +358,7 @@ exports[`NirField > renders correctly with outlined prop 1`] = `
-
+
diff --git a/packages/synapse-bridge/src/patterns/NirField/tests/nitValidation.spec.ts b/packages/synapse-bridge/src/patterns/NirField/tests/nitValidation.spec.ts
new file mode 100644
index 0000000000..078c81edb5
--- /dev/null
+++ b/packages/synapse-bridge/src/patterns/NirField/tests/nitValidation.spec.ts
@@ -0,0 +1,36 @@
+import { checkNIR, computeNIRKey, isNIRKeyValid } from '../nirValidations';
+import { describe, it, expect } from 'vitest'
+
+describe('NIR Validations', () => {
+ it('returns true for valid NIR', () => {
+ expect(checkNIR('1234567890123')).toBe(true);
+ });
+
+ it('returns false for invalid NIR', () => {
+ expect(checkNIR('3234567890123')).toBe(false);
+ });
+
+ it('returns false for NIR with invalid length', () => {
+ expect(checkNIR('123456789012')).toBe(false);
+ });
+
+ it('computes correct NIR key', () => {
+ expect(computeNIRKey('2940375120005')).toBe('91');
+ });
+
+ it('returns true for valid NIR key', () => {
+ expect(isNIRKeyValid('294037512000591')).toBe(true);
+ });
+
+ it('returns false for invalid NIR key', () => {
+ expect(isNIRKeyValid('123456789012398')).toBe(false);
+ });
+
+ it('returns true for NIR without key', () => {
+ expect(isNIRKeyValid('1234567890123')).toBe(true);
+ });
+
+ it('returns true for NIR with invalid length', () => {
+ expect(isNIRKeyValid('123456789012')).toBe(true);
+ });
+});
diff --git a/packages/synapse-bridge/src/rules/required/index.ts b/packages/synapse-bridge/src/rules/required/index.ts
index 485edb05cd..4f5a38ea45 100644
--- a/packages/synapse-bridge/src/rules/required/index.ts
+++ b/packages/synapse-bridge/src/rules/required/index.ts
@@ -16,7 +16,6 @@ export function requiredFn(
} else {
valid = Boolean(typeof value === 'string' ? value.trim() : value)
}
-
return valid || ruleMessage(errorMessages, 'default')
}
}