mirror of
https://github.com/danbulant/Portfolio
synced 2026-07-04 10:30:50 +00:00
feat: autofill birthdate and sex from CZ personalIdNumber
This commit is contained in:
parent
7d7a2b5faa
commit
1370881eee
4 changed files with 56 additions and 77 deletions
|
|
@ -48,25 +48,26 @@ export const isPersonalIdNumberWithBirthdateValid = (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const deriveBirthdateFromPersonalId = (
|
export const parseBirthdateSexFromPersonalId = (
|
||||||
personalIdNumber: string
|
personalIdNumber: string
|
||||||
): [birthdate: string, sex: 'MUŽ' | 'ŽENA'] => {
|
): [birthdate: string, sex: 'Muž' | 'Žena'] => {
|
||||||
const year = Number(personalIdNumber.slice(0, 2));
|
const yearPadded = Number(personalIdNumber.slice(0, 2));
|
||||||
|
const year = yearPadded < 24 ? yearPadded + 2000 : yearPadded + 1900;
|
||||||
const idMonth = Number(personalIdNumber.slice(2, 4));
|
const idMonth = Number(personalIdNumber.slice(2, 4));
|
||||||
let month;
|
let month;
|
||||||
let sex: 'MUŽ' | 'ŽENA';
|
let sex: 'Muž' | 'Žena';
|
||||||
if (idMonth > 12 && idMonth <= 32) {
|
if (idMonth > 12 && idMonth <= 32) {
|
||||||
month = idMonth - 20;
|
month = idMonth - 20;
|
||||||
sex = 'MUŽ';
|
sex = 'Muž';
|
||||||
} else if (idMonth > 50 && idMonth <= 52) {
|
} else if (idMonth > 50 && idMonth <= 52) {
|
||||||
month = idMonth - 50;
|
month = idMonth - 50;
|
||||||
sex = 'ŽENA';
|
sex = 'Žena';
|
||||||
} else if (idMonth > 70 && idMonth <= 82) {
|
} else if (idMonth > 70 && idMonth <= 82) {
|
||||||
month = idMonth - 70;
|
month = idMonth - 70;
|
||||||
sex = 'ŽENA';
|
sex = 'Žena';
|
||||||
} else {
|
} else {
|
||||||
month = idMonth;
|
month = idMonth;
|
||||||
sex = 'MUŽ';
|
sex = 'Muž';
|
||||||
}
|
}
|
||||||
const day = Number(personalIdNumber.slice(4, 6));
|
const day = Number(personalIdNumber.slice(4, 6));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,4 +8,14 @@ export const pushErrorText = (text: string) => {
|
||||||
'--toastBarBackground': '#7f1d1d'
|
'--toastBarBackground': '#7f1d1d'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const pushSuccessText = (text: string) => {
|
||||||
|
toast.push(text, {
|
||||||
|
theme: {
|
||||||
|
'--toastColor': 'mintcream',
|
||||||
|
'--toastBackground': '#047857',
|
||||||
|
'--toastBarBackground': '#064e3b'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -23,11 +23,14 @@
|
||||||
import GradesTable from '$lib/components/grades/GradesTable.svelte';
|
import GradesTable from '$lib/components/grades/GradesTable.svelte';
|
||||||
import SchoolSelect from '$lib/components/select/SchoolSelect/SchoolSelect.svelte';
|
import SchoolSelect from '$lib/components/select/SchoolSelect/SchoolSelect.svelte';
|
||||||
import PersonalIdConfirmCheckBox from '$lib/components/checkbox/PersonalIdConfirmCheckBox.svelte';
|
import PersonalIdConfirmCheckBox from '$lib/components/checkbox/PersonalIdConfirmCheckBox.svelte';
|
||||||
import { isPersonalIdNumberWithBirthdateValid } from '$lib/utils/personalIdFormat';
|
import {
|
||||||
|
parseBirthdateSexFromPersonalId,
|
||||||
|
isPersonalIdNumberWithBirthdateValid
|
||||||
|
} from '$lib/utils/personalIdFormat';
|
||||||
import PersonalIdErrorModal from '$lib/components/modal/PersonalIdErrorModal.svelte';
|
import PersonalIdErrorModal from '$lib/components/modal/PersonalIdErrorModal.svelte';
|
||||||
import LinkErrorModal from '$lib/components/modal/LinkErrorModal.svelte';
|
import LinkErrorModal from '$lib/components/modal/LinkErrorModal.svelte';
|
||||||
import type { Writable } from 'svelte/store';
|
import type { Writable } from 'svelte/store';
|
||||||
import { pushErrorText } from '$lib/utils/toast';
|
import { pushErrorText, pushSuccessText } from '$lib/utils/toast';
|
||||||
|
|
||||||
import schoolList from '$lib/assets/list/school.json';
|
import schoolList from '$lib/assets/list/school.json';
|
||||||
import countriesList from '$lib/assets/list/countries.json';
|
import countriesList from '$lib/assets/list/countries.json';
|
||||||
|
|
@ -73,7 +76,7 @@
|
||||||
city: '',
|
city: '',
|
||||||
zip: '',
|
zip: '',
|
||||||
citizenship: '',
|
citizenship: '',
|
||||||
personalIdNumber: '',
|
personalIdNumber: 'TODO: remove this',
|
||||||
schoolName: '',
|
schoolName: '',
|
||||||
healthInsurance: '',
|
healthInsurance: '',
|
||||||
grades: [],
|
grades: [],
|
||||||
|
|
@ -132,7 +135,7 @@
|
||||||
city: yup.string().required(),
|
city: yup.string().required(),
|
||||||
zip: yup.string().required(),
|
zip: yup.string().required(),
|
||||||
citizenship: yup.string().required(),
|
citizenship: yup.string().required(),
|
||||||
personalIdNumber: yup.string().required(),
|
personalIdNumber: yup.string(),
|
||||||
schoolName: yup.string().required(),
|
schoolName: yup.string().required(),
|
||||||
healthInsurance: yup.number().required(),
|
healthInsurance: yup.number().required(),
|
||||||
grades: yup
|
grades: yup
|
||||||
|
|
@ -243,25 +246,6 @@
|
||||||
personalIdModal: false,
|
personalIdModal: false,
|
||||||
linkErrorModal: false
|
linkErrorModal: false
|
||||||
};
|
};
|
||||||
const validatePersonalId = () => {
|
|
||||||
if ($form.candidate.citizenship === 'Česká republika') {
|
|
||||||
if (
|
|
||||||
!isPersonalIdNumberWithBirthdateValid(
|
|
||||||
$form.candidate.personalIdNumber,
|
|
||||||
$form.candidate.birthdate
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
toast.push('Rodné číslo neodpovídá oficiální specifikaci či datumu narození', {
|
|
||||||
theme: {
|
|
||||||
'--toastColor': 'mintcream',
|
|
||||||
'--toastBackground': '#b91c1c',
|
|
||||||
'--toastBarBackground': '#7f1d1d'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
throw new Error('Rodné číslo neodpovídá datumu narození');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onSubmit = async (values: CandidateData) => {
|
const onSubmit = async (values: CandidateData) => {
|
||||||
if (pageIndex === pageCount) {
|
if (pageIndex === pageCount) {
|
||||||
|
|
@ -359,7 +343,7 @@
|
||||||
$typedErrors['candidate']['healthInsurance'] ||
|
$typedErrors['candidate']['healthInsurance'] ||
|
||||||
$typedErrors['candidate']['birthdate'] ||
|
$typedErrors['candidate']['birthdate'] ||
|
||||||
$typedErrors['candidate']['birthplace'] ||
|
$typedErrors['candidate']['birthplace'] ||
|
||||||
$typedErrors['candidate']['personalIdNumber'] ||
|
$typedErrors['candidate']['birthSurname'] ||
|
||||||
$typedErrors['candidate']['testLanguage']
|
$typedErrors['candidate']['testLanguage']
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -409,11 +393,19 @@
|
||||||
return '+' + telephone.match(/[0-9]{1,3}/g)!.join(' ');
|
return '+' + telephone.match(/[0-9]{1,3}/g)!.join(' ');
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO
|
$: if ($form.candidate.citizenship === 'Česká republika') {
|
||||||
/* $form.candidate.personalIdNumber = data.whoami.personalIdNumber;
|
if ($form.candidate.birthdate === '' && $form.candidate.sex === '') {
|
||||||
const [birthdate, sex] = deriveBirthdateFromPersonalId(data.whoami.personalIdNumber);
|
let [birthdate, sex] = parseBirthdateSexFromPersonalId(data.whoami.personalIdNumber);
|
||||||
$form.candidate.birthdate = birthdate;
|
$form.candidate.birthdate = birthdate;
|
||||||
$form.candidate.sex = sex; */
|
$form.candidate.sex = sex;
|
||||||
|
pushSuccessText(
|
||||||
|
`Datum narození a pohlaví bylo vyplněno automaticky podle Vašeho rodného čísla (${data.whoami.personalIdNumber}).`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$form.candidate.birthdate = '';
|
||||||
|
$form.candidate.sex = '';
|
||||||
|
}
|
||||||
|
|
||||||
if (details !== undefined) {
|
if (details !== undefined) {
|
||||||
details.candidate.birthdate = details.candidate.birthdate.split('-').reverse().join('.');
|
details.candidate.birthdate = details.candidate.birthdate.split('-').reverse().join('.');
|
||||||
|
|
@ -525,24 +517,14 @@
|
||||||
</p>
|
</p>
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<div class="field flex">
|
<span class="field">
|
||||||
<span class="w-[50%]">
|
<NameField
|
||||||
<NameField
|
error={$typedErrors['candidate']['name'] || $typedErrors['candidate']['surname']}
|
||||||
error={$typedErrors['candidate']['name'] ||
|
bind:valueName={$form.candidate.name}
|
||||||
$typedErrors['candidate']['surname']}
|
bind:valueSurname={$form.candidate.surname}
|
||||||
bind:valueName={$form.candidate.name}
|
placeholder={$LL.input.nameSurname()}
|
||||||
bind:valueSurname={$form.candidate.surname}
|
/>
|
||||||
placeholder={$LL.input.nameSurname()}
|
</span>
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
<span class="ml-2 w-[50%]">
|
|
||||||
<TextField
|
|
||||||
error={$typedErrors['candidate']['birthSurname']}
|
|
||||||
bind:value={$form.candidate.birthSurname}
|
|
||||||
placeholder={`${$LL.input.birthSurname()} (${$LL.input.optional()})`}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<span class="field ml-2">
|
<span class="field ml-2">
|
||||||
<TelephoneField
|
<TelephoneField
|
||||||
bind:error={$typedErrors['candidate']['telephone']}
|
bind:error={$typedErrors['candidate']['telephone']}
|
||||||
|
|
@ -636,19 +618,11 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field flex items-center justify-center">
|
<div class="field flex items-center justify-center">
|
||||||
{#if $form.candidate.citizenship === 'Česká republika' || !$form.candidate.citizenship}
|
<TextField
|
||||||
<IdField
|
error={$typedErrors['candidate']['birthSurname']}
|
||||||
error={$typedErrors['candidate']['personalIdNumber']}
|
bind:value={$form.candidate.birthSurname}
|
||||||
bind:value={$form.candidate.personalIdNumber}
|
placeholder={`${$LL.input.birthSurname()} (${$LL.input.optional()})`}
|
||||||
placeholder={$LL.input.personalIdentificationNumber()}
|
/>
|
||||||
/>
|
|
||||||
{:else}
|
|
||||||
<TextField
|
|
||||||
error={$typedErrors['candidate']['personalIdNumber']}
|
|
||||||
bind:value={$form.candidate.personalIdNumber}
|
|
||||||
placeholder={$LL.input.personalIdentificationNumber()}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
<div class="ml-2">
|
<div class="ml-2">
|
||||||
<SelectField
|
<SelectField
|
||||||
error={$typedErrors['candidate']['sex']}
|
error={$typedErrors['candidate']['sex']}
|
||||||
|
|
@ -805,9 +779,6 @@
|
||||||
<Submit
|
<Submit
|
||||||
enterAllowed={pageIndex !== 7}
|
enterAllowed={pageIndex !== 7}
|
||||||
on:click={async (e) => {
|
on:click={async (e) => {
|
||||||
if (pageIndex === 4) {
|
|
||||||
validatePersonalId();
|
|
||||||
}
|
|
||||||
await handleSubmit(e);
|
await handleSubmit(e);
|
||||||
if (isPageInvalid(pageIndex)) return;
|
if (isPageInvalid(pageIndex)) return;
|
||||||
if (pageIndex !== pageCount) {
|
if (pageIndex !== pageCount) {
|
||||||
|
|
@ -826,9 +797,6 @@
|
||||||
<button
|
<button
|
||||||
class:dotActive={i === pageIndex}
|
class:dotActive={i === pageIndex}
|
||||||
on:click={async (e) => {
|
on:click={async (e) => {
|
||||||
if (pageIndex === 4 && i > pageIndex) {
|
|
||||||
validatePersonalId();
|
|
||||||
}
|
|
||||||
pageIndex -= pageIndex === pageCount ? 1 : 0;
|
pageIndex -= pageIndex === pageCount ? 1 : 0;
|
||||||
await handleSubmit(e);
|
await handleSubmit(e);
|
||||||
pagesFilled = pagesFilled.map((_, i) => !isPageInvalid(i));
|
pagesFilled = pagesFilled.map((_, i) => !isPageInvalid(i));
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ const cs: BaseTranslation = {
|
||||||
},
|
},
|
||||||
eighth: {
|
eighth: {
|
||||||
title: 'Poslední krok',
|
title: 'Poslední krok',
|
||||||
description: 'Přidejte prosím přepis Vaších známek z posledních dvou let studia'
|
description: 'Přidejte prosím přepis Vaších známek z posledních dvou let studia. Známky z druhého pololetí 9. třídy nevyplňujte, pokud vysvědčení ještě nemáte.'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -140,7 +140,7 @@ const cs: BaseTranslation = {
|
||||||
fullAddress: 'Adresa',
|
fullAddress: 'Adresa',
|
||||||
address: 'Ulice a č. p.',
|
address: 'Ulice a č. p.',
|
||||||
zipCode: 'PSČ',
|
zipCode: 'PSČ',
|
||||||
city: 'Město',
|
city: 'Okres',
|
||||||
birthPlace: 'Místo narození',
|
birthPlace: 'Místo narození',
|
||||||
birthDate: 'Datum narození',
|
birthDate: 'Datum narození',
|
||||||
sex: 'Pohlaví',
|
sex: 'Pohlaví',
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue