Merge pull request #174 from EETagent/frontend_school_order_selection

Frontend school order selection
This commit is contained in:
Vojtěch Jungmann 2023-01-26 23:27:05 +01:00 committed by GitHub
commit 95d74d192b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 759 additions and 284 deletions

View file

@ -1,7 +1,7 @@
{ {
"baseLocale": "cs", "baseLocale": "cs",
"adapter": "svelte", "adapter": "svelte",
"esmImports": true, "esmImports": true,
"outputPath": "./src/translations", "outputPath": "./src/translations",
"$schema": "https://unpkg.com/typesafe-i18n@5.20.0/schema/typesafe-i18n.json" "$schema": "https://unpkg.com/typesafe-i18n@5.20.0/schema/typesafe-i18n.json"
} }

View file

@ -4,7 +4,6 @@
export let linkOk: boolean = false; export let linkOk: boolean = false;
export let linkError: boolean = false; export let linkError: boolean = false;
export let applications: Array<number>; export let applications: Array<number>;
let title1 = $LL.components.checkbox.accountLinkCheckBox.multiple.title({ let title1 = $LL.components.checkbox.accountLinkCheckBox.multiple.title({
first: applications[0], first: applications[0],
second: applications[1] second: applications[1]
@ -20,8 +19,6 @@
title2 = $LL.components.checkbox.accountLinkCheckBox.single.title2(); title2 = $LL.components.checkbox.accountLinkCheckBox.single.title2();
} }
$: console.log(linkOk, linkError);
export let error: string = ''; export let error: string = '';
const switchSelection = (id: number) => { const switchSelection = (id: number) => {

View file

@ -13,7 +13,9 @@
<div class="w-full text-lg font-semibold">{$LL.components.checkbox.gdprCheckBox.title()}</div> <div class="w-full text-lg font-semibold">{$LL.components.checkbox.gdprCheckBox.title()}</div>
<div class="w-full text-sm">{$LL.components.checkbox.gdprCheckBox.description()}</div> <div class="w-full text-sm">{$LL.components.checkbox.gdprCheckBox.description()}</div>
<div class="w-full text-sm"><a class="underline" href="/gdpr">{$LL.components.checkbox.gdprCheckBox.here()}</a></div> <div class="w-full text-sm">
<a class="underline" href="/gdpr">{$LL.components.checkbox.gdprCheckBox.here()}</a>
</div>
</div> </div>
</label> </label>

View file

@ -0,0 +1,82 @@
<script lang="ts">
import LL from '$i18n/i18n-svelte';
export let personalIdOk: boolean = false;
export let personalIdErr: boolean = false;
export let personalIdNumber: string;
let titleOk = $LL.components.checkbox.personalIdConfirmCheckBox.titleOk({
personalId: personalIdNumber
});
let titleErr = $LL.components.checkbox.personalIdConfirmCheckBox.titleErr({
personalId: personalIdNumber
});
export let error: string = '';
const switchSelection = (id: number) => {
if (id === 0) {
personalIdOk = true;
personalIdErr = false;
} else {
personalIdOk = false;
personalIdErr = true;
}
};
</script>
<div>
<input
on:click={(_) => switchSelection(0)}
class:error
on:change
type="checkbox"
id="linkOk"
checked={personalIdOk}
class="peer hidden"
/>
<label for="linkOk" class="peer-checked:border-sspsBlue peer-checked:text-gray-600" class:error>
<div class="block">
<span class="text-2xl">📜</span>
<div class="w-full text-lg font-semibold">
{titleOk}
</div>
<div class="w-full text-sm">{$LL.components.checkbox.personalIdConfirmCheckBox.ok()}</div>
</div>
</label>
</div>
<div class="mt-2">
<input
on:click={(_) => switchSelection(1)}
on:change
type="checkbox"
id="linkError"
checked={personalIdErr}
class="peer hidden"
/>
<label for="linkError" class="peer-checked:border-sspsBlue peer-checked:text-gray-600">
<div class="block">
<span class="text-2xl">📜</span>
<div class="w-full text-lg font-semibold">
{titleErr}
</div>
<div class="w-full text-sm">
{$LL.components.checkbox.personalIdConfirmCheckBox.whatHappened()}
</div>
</div>
</label>
</div>
<style lang="postcss">
label {
@apply inline-flex w-full items-center justify-between;
@apply cursor-pointer;
@apply bg-white p-5 text-gray-500;
@apply hover:bg-gray-50 hover:text-gray-600;
@apply rounded-lg border-2 border-gray-200;
}
.error {
@apply border-red-700;
}
</style>

View file

@ -160,30 +160,33 @@
<span class="font-bold">{$baseCandidateData.applications[1]}</span></span <span class="font-bold">{$baseCandidateData.applications[1]}</span></span
> >
{/if} {/if}
<span>{$LL.input.address()}: <span class="font-bold">{$candidateData.candidate.address}</span></span>
<span <span
>{$LL.input.birthDate()}: <span class="font-bold">{$candidateData.candidate.birthdate}</span >{$LL.input.address()}:
></span <span class="font-bold">{$candidateData.candidate.address}</span></span
> >
<span <span
>{$LL.input.birthPlace()}: <span class="font-bold">{$candidateData.candidate.birthplace}</span >{$LL.input.birthDate()}:
></span <span class="font-bold">{$candidateData.candidate.birthdate}</span></span
> >
<span <span
>{$LL.input.personalIdentificationNumber()}: <span class="font-bold" >{$LL.input.birthPlace()}:
>{$candidateData.candidate.personalIdNumber}</span <span class="font-bold">{$candidateData.candidate.birthplace}</span></span
></span
> >
<span <span
>{$LL.input.schoolIzo()}: <span class="font-bold">{$candidateData.candidate.schoolName}</span >{$LL.input.personalIdentificationNumber()}:
></span <span class="font-bold">{$candidateData.candidate.personalIdNumber}</span></span
> >
<span <span
>{$LL.input.insuranceNumber()}: <span class="font-bold" >{$LL.input.schoolIzo()}:
>{$candidateData.candidate.healthInsurance}</span <span class="font-bold">{$candidateData.candidate.schoolName}</span></span
></span
> >
<span>{$LL.input.telephone()}: <span class="font-bold">{$candidateData.candidate.telephone}</span></span <span
>{$LL.input.insuranceNumber()}:
<span class="font-bold">{$candidateData.candidate.healthInsurance}</span></span
>
<span
>{$LL.input.telephone()}:
<span class="font-bold">{$candidateData.candidate.telephone}</span></span
> >
</div> </div>
<div <div
@ -202,7 +205,9 @@
>{parent.name + ' ' + parent.surname}</span >{parent.name + ' ' + parent.surname}</span
> >
<span>{$LL.input.email()}: <span class="font-bold">{parent.email}</span></span> <span>{$LL.input.email()}: <span class="font-bold">{parent.email}</span></span>
<span>{$LL.input.telephone()}: <span class="font-bold">{parent.telephone}</span></span> <span
>{$LL.input.telephone()}: <span class="font-bold">{parent.telephone}</span></span
>
</div> </div>
{/each} {/each}
</div> </div>

View file

@ -105,7 +105,8 @@
{#if status === 'uploaded'} {#if status === 'uploaded'}
<button <button
class="mr-3 rounded-xl bg-[#ef8b46] py-0.5 px-2 text-white shadow-md transition-all duration-300 hover:bg-orange-400" class="mr-3 rounded-xl bg-[#ef8b46] py-0.5 px-2 text-white shadow-md transition-all duration-300 hover:bg-orange-400"
on:click={debounce(() => dispatch('delete'), 150)}>{$LL.components.dashboard.dashboardUploadCard.delete()}</button on:click={debounce(() => dispatch('delete'), 150)}
>{$LL.components.dashboard.dashboardUploadCard.delete()}</button
> >
{/if} {/if}
<StatusNotificationDot {status} /> <StatusNotificationDot {status} />
@ -133,9 +134,16 @@
> >
<div class="hidden items-center xl:block"> <div class="hidden items-center xl:block">
{#if bytesTotal === 0 || Math.round(progress * 100) === 100} {#if bytesTotal === 0 || Math.round(progress * 100) === 100}
<h2 class="text-xl font-bold">{status === 'submitted' ? $LL.components.dashboard.dashboardUploadCard.sent() : $LL.components.dashboard.dashboardUploadCard.uploaded()}</h2> <h2 class="text-xl font-bold">
{status === 'submitted'
? $LL.components.dashboard.dashboardUploadCard.sent()
: $LL.components.dashboard.dashboardUploadCard.uploaded()}
</h2>
{:else} {:else}
<h2 class="text-xl">{$LL.components.dashboard.dashboardUploadCard.uploaded()} {((bytesTotal / 1_000_000) * progress).toFixed(1)} MB</h2> <h2 class="text-xl">
{$LL.components.dashboard.dashboardUploadCard.uploaded()}
{((bytesTotal / 1_000_000) * progress).toFixed(1)} MB
</h2>
<h2 class="self-center text-xl">z {(bytesTotal / 1_000_000).toFixed(1)} MB</h2> <h2 class="self-center text-xl">z {(bytesTotal / 1_000_000).toFixed(1)} MB</h2>
{/if} {/if}
</div> </div>

View file

@ -29,7 +29,7 @@
} }
return grades; return grades;
}; };
let gradesLocal: Array<Grade> = let gradesLocal: Array<Grade> =
grades.length > 0 grades.length > 0
? convertGradeBackendToGrade(grades) ? convertGradeBackendToGrade(grades)

View file

@ -50,7 +50,7 @@
.view { .view {
@apply z-10; @apply z-10;
@apply absolute top-0 right-0 bottom-0 left-0 m-auto md:top-auto md:bottom-auto md:left-auto md:m-0; @apply absolute top-0 right-0 bottom-0 left-0 m-auto md:top-auto md:bottom-auto md:left-auto md:m-0;
@apply md:h-screen md:w-[50vw]; @apply md:h-screen md:w-[60vw];
@apply md:my-auto; @apply md:my-auto;
@apply bg-white; @apply bg-white;
} }

View file

@ -0,0 +1,41 @@
<script lang="ts">
export let itemLabel: string;
export let highlighted: boolean;
</script>
<li class="autocomplete-items" class:autocomplete-active={highlighted} on:click on:keydown={null}>
{@html itemLabel}
</li>
<style>
li.autocomplete-items {
list-style: none;
border-bottom: 1px solid #d4d4d4;
z-index: 99;
/*position the autocomplete items to be the same width as the container:*/
top: 100%;
left: 0;
right: 0;
padding: 10px;
cursor: pointer;
background-color: #fff;
}
li.autocomplete-items:hover {
/*when hovering an item:*/
background-color: #81921f;
color: white;
}
li.autocomplete-items:active {
/*when navigating through the items using the arrow keys:*/
background-color: DodgerBlue !important;
color: #ffffff;
}
.autocomplete-active {
/*when navigating through the items using the arrow keys:*/
background-color: DodgerBlue !important;
color: #ffffff;
}
</style>

View file

@ -0,0 +1,145 @@
<script lang="ts">
import LL from '$i18n/i18n-svelte';
import schoollistString from '$lib/assets/schoollist.txt?raw';
import School from './School.svelte';
import type { School as SchoolType } from '$lib/stores/candidate';
const schoolList: Array<string> = schoollistString.split(';');
let filteredSchools: Array<string> = [];
const filterSchools = () => {
let storageArr: Array<string> = [];
if (schoolNameInputValue) {
schoolList.forEach((school) => {
if (school.toLowerCase().startsWith(schoolNameInputValue.toLowerCase())) {
storageArr = [...storageArr, makeMatchBold(school)];
}
});
}
filteredSchools = storageArr;
};
let searchInput: HTMLInputElement;
let optionsList: HTMLUListElement;
let schoolNameInputValue = '';
let schoolFieldInputValue = '';
$: if (!schoolNameInputValue) {
filteredSchools = [];
hiLiteIndex = -1;
}
const setInputVal = (schoolName: string) => {
schoolNameInputValue = removeBold(schoolName);
filteredSchools = [];
hiLiteIndex = -1;
searchInput.focus();
};
const makeMatchBold = (str: string) => {
let matched = str.substring(0, schoolNameInputValue.length);
let makeBold = `<strong>${matched}</strong>`;
let boldedMatch = str.replace(matched, makeBold);
return boldedMatch;
};
const removeBold = (str: string) => {
return str.replace(/<(.)*?>/g, '');
};
let hiLiteIndex: number = 0;
const navigateList = (e: KeyboardEvent) => {
if (e.key === 'ArrowDown') {
if (hiLiteIndex < filteredSchools.length - 1) {
hiLiteIndex++;
// scroll optionsList
let option = optionsList.children[hiLiteIndex];
if (option) {
option.scrollIntoView({ block: 'nearest' });
}
}
} else if (e.key === 'ArrowUp') {
if (hiLiteIndex > 0) {
hiLiteIndex--;
}
} else if (e.key === 'Enter') {
if (hiLiteIndex > -1) {
setInputVal(filteredSchools[hiLiteIndex]);
}
}
};
export let selectedSchool: SchoolType;
export let error: string = '';
schoolFieldInputValue = selectedSchool.field;
schoolNameInputValue = selectedSchool.name;
$: selectedSchool.field = schoolFieldInputValue;
$: selectedSchool.name = schoolNameInputValue;
</script>
<svelte:window on:keydown={navigateList} />
<div class="autocomplete">
<div class="flex">
<input
class:error
class="flex-1"
type="text"
bind:this={searchInput}
bind:value={schoolNameInputValue}
on:input={filterSchools}
placeholder={$LL.input.schoolName()}
/>
<input
class:error
class="ml-2 w-2/5"
type="text"
bind:value={schoolFieldInputValue}
placeholder={$LL.input.fieldOfStudy()}
/>
</div>
{#if filteredSchools.length > 0}
<ul bind:this={optionsList} class="schoolAutocompleteList">
{#each filteredSchools as country, i}
<School
itemLabel={country}
highlighted={i === hiLiteIndex}
on:click={() => setInputVal(country)}
/>
{/each}
</ul>
{/if}
</div>
<style lang="postcss">
div,
input {
@apply w-full;
}
div {
@apply relative flex items-center justify-center;
}
input {
@apply hover:border-sspsBlue w-full rounded-lg border border-2 bg-[#f8fafb] p-3 text-xl shadow-lg outline-none transition-colors duration-300;
}
.error {
@apply border-red-700;
}
.autocomplete {
@apply relative;
}
.schoolAutocompleteList {
@apply absolute top-20 z-50;
@apply w-full;
@apply max-h-72 overflow-scroll;
}
</style>

View file

@ -1,13 +1,19 @@
import type { GradeBackend } from '$lib/components/grades/GradesTable.svelte'; import type { GradeBackend } from '$lib/components/grades/GradesTable.svelte';
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
export interface School {
name: string;
field: string;
}
export interface CandidateData { export interface CandidateData {
candidate: { candidate: {
name: string; name: string;
surname: string; surname: string;
birthSurname: string;
birthplace: string; birthplace: string;
birthdate: string; birthdate: string;
address: string; address: string;
letterAddress: string;
telephone: string; telephone: string;
citizenship: string; citizenship: string;
email: string; email: string;
@ -16,6 +22,8 @@ export interface CandidateData {
schoolName: string; schoolName: string;
healthInsurance: string; healthInsurance: string;
grades: Array<GradeBackend>; grades: Array<GradeBackend>;
firstSchool: School;
secondSchool: School;
testLanguage: string; testLanguage: string;
}; };
parents: Array<{ parents: Array<{
@ -66,9 +74,11 @@ export const candidateData = writable<CandidateData>({
candidate: { candidate: {
name: '', name: '',
surname: '', surname: '',
birthSurname: '',
birthplace: '', birthplace: '',
birthdate: '', birthdate: '',
address: '', address: '',
letterAddress: '',
telephone: '', telephone: '',
citizenship: '', citizenship: '',
email: '', email: '',
@ -77,6 +87,8 @@ export const candidateData = writable<CandidateData>({
schoolName: '', schoolName: '',
healthInsurance: '', healthInsurance: '',
grades: [], grades: [],
firstSchool: { name: '', field: '' },
secondSchool: { name: '', field: '' },
testLanguage: '' testLanguage: ''
}, },
parents: [] parents: []

View file

@ -0,0 +1,75 @@
// TODO: nefunguje pro lidi nar. pred 1.1.1954 :D
export const isPersonalIdNumberValid = (personalIdNumber: string): boolean => {
const idFmt = personalIdNumber.split('/').join('');
const lastDigitCheck =
Number(idFmt.slice(0, 9)) % 11 === Number(idFmt.at(-1)) ||
Number(idFmt.slice(0, 9)) % 11 === 10; // an edge case that could occur
const divisibleBy11 = Number(idFmt) % 11 === 0;
if (lastDigitCheck && divisibleBy11) {
return true;
} else {
return false;
}
};
export const isPersonalIdNumberWithBirthdateValid = (
personalIdNumber: string,
birthdate: string
): boolean => {
const dateFmt = birthdate
.split('.')
.map((x) => x.padStart(2, '0'))
.reverse()
.join('')
.slice(2);
const idFmt = personalIdNumber.split('/').join('');
const divisionValid = isPersonalIdNumberValid(personalIdNumber);
const idMonth = Number(idFmt.slice(2, 4));
const dateMonth = Number(dateFmt.slice(2, 4));
const monthValid =
idMonth === dateMonth ||
idMonth === dateMonth + 50 ||
idMonth === dateMonth + 20 ||
idMonth === dateMonth + 70;
if (
idFmt.slice(0, 2) === dateFmt.slice(0, 2) &&
monthValid &&
idFmt.slice(4, 6) === dateFmt.slice(4, 6) &&
divisionValid
) {
return true;
} else {
return false;
}
};
export const deriveBirthdateFromPersonalId = (
personalIdNumber: string
): [birthdate: string, sex: 'MUŽ' | 'ŽENA'] => {
const year = Number(personalIdNumber.slice(0, 2));
const idMonth = Number(personalIdNumber.slice(2, 4));
let month;
let sex: 'MUŽ' | 'ŽENA';
if (idMonth > 12 && idMonth <= 32) {
month = idMonth - 20;
sex = 'MUŽ';
} else if (idMonth > 50 && idMonth <= 52) {
month = idMonth - 50;
sex = 'ŽENA';
} else if (idMonth > 70 && idMonth <= 82) {
month = idMonth - 70;
sex = 'ŽENA';
} else {
month = idMonth;
sex = 'MUŽ';
}
const day = Number(personalIdNumber.slice(4, 6));
const birthdate = `${day}.${month}.${year}`;
return [birthdate, sex];
};

View file

@ -10,9 +10,11 @@ export const load: PageServerLoad = async ({ fetch, params }) => {
candidate: { candidate: {
name: '', name: '',
surname: '', surname: '',
birthSurname: '',
birthplace: '', birthplace: '',
birthdate: '', birthdate: '',
address: '', address: '',
letterAddress: '',
telephone: '', telephone: '',
citizenship: '', citizenship: '',
email: '', email: '',
@ -21,6 +23,8 @@ export const load: PageServerLoad = async ({ fetch, params }) => {
schoolName: '', schoolName: '',
healthInsurance: '', healthInsurance: '',
grades: [], grades: [],
firstSchool: { name: '', field: '' },
secondSchool: { name: '', field: '' },
testLanguage: '' testLanguage: ''
}, },
parents: [] parents: []

View file

@ -6,7 +6,6 @@
import Submit from '$lib/components/button/Submit.svelte'; import Submit from '$lib/components/button/Submit.svelte';
import GdprCheckBox from '$lib/components/checkbox/GdprCheckBox.svelte'; import GdprCheckBox from '$lib/components/checkbox/GdprCheckBox.svelte';
import Home from '$lib/components/icons/Home.svelte';
import SchoolBadge from '$lib/components/icons/SchoolBadge.svelte'; import SchoolBadge from '$lib/components/icons/SchoolBadge.svelte';
import SplitLayout from '$lib/components/layout/SplitLayout.svelte'; import SplitLayout from '$lib/components/layout/SplitLayout.svelte';
import SelectField from '$lib/components/select/SelectField.svelte'; import SelectField from '$lib/components/select/SelectField.svelte';
@ -24,10 +23,14 @@
import type { CandidateData } from '$lib/stores/candidate'; import type { CandidateData } from '$lib/stores/candidate';
import AccountLinkCheckBox from '$lib/components/checkbox/AccountLinkCheckBox.svelte'; import AccountLinkCheckBox from '$lib/components/checkbox/AccountLinkCheckBox.svelte';
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 PersonalIdConfirmCheckBox from '$lib/components/checkbox/PersonalIdConfirmCheckBox.svelte';
import { isPersonalIdNumberWithBirthdateValid } from '$lib/utils/personalIdFormat';
const pageCount = 7;
let pageIndex = 0; let pageIndex = 0;
let pagesFilled = [false, false, false, false, false, false, false]; let pagesFilled = [false, false, false, false, false, false, false, false];
let editModePageIndex = 3;
const pageCount = pagesFilled.length;
let pageTexts = [ let pageTexts = [
$LL.candidate.register.second.title(), $LL.candidate.register.second.title(),
$LL.candidate.register.third.title(), $LL.candidate.register.third.title(),
@ -42,19 +45,24 @@
let details = data.candidate; let details = data.candidate;
let baseCandidateDetails = data.whoami; let baseCandidateDetails = data.whoami;
let personalIdBirthdateMatch = true;
const formInitialValues = { const formInitialValues = {
gdpr: false, gdpr: false,
personalIdOk: false,
personalIdErr: false,
linkOk: false, linkOk: false,
linkError: false, linkError: false,
candidate: { candidate: {
name: '', name: '',
surname: '', surname: '',
birthSurname: '',
email: '', email: '',
telephone: '', telephone: '',
birthplace: '', birthplace: '',
birthdate: '', birthdate: '',
sex: '', sex: '',
address: '', address: '',
letterAddress: '',
street: '', street: '',
houseNumber: '', houseNumber: '',
city: '', city: '',
@ -64,6 +72,8 @@
schoolName: '', schoolName: '',
healthInsurance: '', healthInsurance: '',
grades: [], grades: [],
firstSchool: { name: '', field: '' },
secondSchool: { name: '', field: '' },
testLanguage: '' testLanguage: ''
}, },
parents: [ parents: [
@ -84,6 +94,8 @@
const formValidationSchema = yup.object().shape({ const formValidationSchema = yup.object().shape({
gdpr: yup.boolean().oneOf([true]), gdpr: yup.boolean().oneOf([true]),
personalIdOk: yup.boolean().oneOf([true]),
personalIdErr: yup.boolean().oneOf([false]),
linkOk: yup.boolean().oneOf([true]), linkOk: yup.boolean().oneOf([true]),
linkError: yup.boolean().oneOf([false]), linkError: yup.boolean().oneOf([false]),
candidate: yup.object().shape({ candidate: yup.object().shape({
@ -99,6 +111,7 @@
.string() .string()
.required() .required()
.matches(/^([0-3]?[0-9])\.(0?[1-9]|1[0-2])\.[0-9]{4}$/), .matches(/^([0-3]?[0-9])\.(0?[1-9]|1[0-2])\.[0-9]{4}$/),
birthSurname: yup.string().required(),
sex: yup.string(), sex: yup.string(),
address: yup.string(), address: yup.string(),
street: yup.string().required(), street: yup.string().required(),
@ -126,6 +139,14 @@
.required() .required()
) )
.required(), .required(),
firstSchool: yup.object().shape({
name: yup.string().required(),
field: yup.string().required()
}),
secondSchool: yup.object().shape({
name: yup.string().required(),
field: yup.string().required()
}),
testLanguage: yup.string().required() testLanguage: yup.string().required()
}), }),
parents: yup.array().of( parents: yup.array().of(
@ -167,7 +188,12 @@
unknown unknown
> >
? { ? {
[K2 in keyof (typeof formInitialValues)[K]]: string; [K2 in keyof (typeof formInitialValues)[K]]: (typeof formInitialValues)[K][K2] extends Record<
string,
unknown
>
? { [K4 in keyof (typeof formInitialValues)[K][K2]]: string }
: string;
} }
: (typeof formInitialValues)[K] extends Array<Record<string, unknown>> : (typeof formInitialValues)[K] extends Array<Record<string, unknown>>
? Array<{ [K3 in keyof (typeof formInitialValues)[K][number]]: string }> ? Array<{ [K3 in keyof (typeof formInitialValues)[K][number]]: string }>
@ -177,80 +203,34 @@
// TODO: https://github.com/tjinauyeung/svelte-forms-lib/issues/171!! (Zatím tenhle mega typ) // TODO: https://github.com/tjinauyeung/svelte-forms-lib/issues/171!! (Zatím tenhle mega typ)
$: typedErrors = errors as unknown as Writable<FormErrorType>; $: typedErrors = errors as unknown as Writable<FormErrorType>;
// TODO: validate on admin dashboard, move somewhere $: console.log($typedErrors);
// TODO: nefunguje pro lidi nar. pred 1.1.1954 :D
const isPersonalIdNumberValid = (personalIdNumber: string): boolean => {
const idFmt = personalIdNumber.split('/').join('');
const lastDigitCheck =
Number(idFmt.slice(0, 9)) % 11 === Number(idFmt.at(-1)) ||
Number(idFmt.slice(0, 9)) % 11 === 10; // an edge case that could occur
const divisibleBy11 = Number(idFmt) % 11 === 0;
if (lastDigitCheck && divisibleBy11) {
return true;
} else {
return false;
}
};
const isPersonalIdNumberWithBirthdateValid = (
personalIdNumber: string,
birthdate: string
): boolean => {
const dateFmt = birthdate
.split('.')
.map((x) => x.padStart(2, '0'))
.reverse()
.join('')
.slice(2);
const idFmt = personalIdNumber.split('/').join('');
const divisionValid = isPersonalIdNumberValid(personalIdNumber);
const idMonth = Number(idFmt.slice(2, 4));
const dateMonth = Number(dateFmt.slice(2, 4));
const monthValid =
idMonth === dateMonth ||
idMonth === dateMonth + 50 ||
idMonth === dateMonth + 20 ||
idMonth === dateMonth + 70;
if (
idFmt.slice(0, 2) === dateFmt.slice(0, 2) &&
monthValid &&
idFmt.slice(4, 6) === dateFmt.slice(4, 6) &&
divisionValid
) {
return true;
} else {
return false;
}
};
const onSubmit = async (values: CandidateData) => { const onSubmit = async (values: CandidateData) => {
if (pageIndex === 3) {
if (values.candidate.citizenship === 'Česká republika') {
if (
!isPersonalIdNumberWithBirthdateValid(
values.candidate.personalIdNumber,
values.candidate.birthdate
)
) {
toast.push('Rodné číslo neodpovídá oficiální specifikaci či datumu narození', {
theme: {
'--toastColor': 'mintcream',
'--toastBackground': '#b91c1c',
'--toastBarBackground': '#7f1d1d'
}
});
personalIdBirthdateMatch = false;
throw new Error('Rodné číslo neodpovídá datumu narození');
}
}
personalIdBirthdateMatch = true;
}
if (pageIndex === pageCount) { if (pageIndex === pageCount) {
console.log('submitting');
// clone values to oldValues // clone values to oldValues
let oldValues = JSON.parse(JSON.stringify(values)); let oldValues = JSON.parse(JSON.stringify(values));
try { try {
if (values.candidate.citizenship === 'Česká republika') {
if (
!isPersonalIdNumberWithBirthdateValid(
values.candidate.personalIdNumber,
values.candidate.birthdate
)
) {
// alert('Rodné číslo neodpovídá oficiální specifikaci či datumu narození'); // TODO: alerts
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í');
}
}
// @ts-ignore // love javascript // @ts-ignore // love javascript
delete values.undefined; delete values.undefined;
// convert birthdate from dd.mm.yyyy to yyyy-mm-dd // convert birthdate from dd.mm.yyyy to yyyy-mm-dd
@ -304,40 +284,51 @@
const isPageInvalid = (index: number): boolean => { const isPageInvalid = (index: number): boolean => {
switch (index) { switch (index) {
case 0: case 0:
if ($typedErrors['linkOk'] || $typedErrors['linkError']) { if ($typedErrors['personalIdOk'] || $typedErrors['personalIdErr']) {
return true; return true;
} }
break; break;
case 1: case 1:
if ($typedErrors['gdpr']) { if ($typedErrors['linkOk'] || $typedErrors['linkError']) {
return true; return true;
} }
break; break;
case 2: case 2:
if ($typedErrors['gdpr']) {
return true;
}
break;
case 3:
if ( if (
$typedErrors['candidate']['name'] || $typedErrors['candidate']['name'] ||
$typedErrors['candidate']['surname'] || $typedErrors['candidate']['surname'] ||
$typedErrors['candidate']['email'] || $typedErrors['candidate']['email'] ||
$typedErrors['candidate']['telephone'] $typedErrors['candidate']['telephone'] ||
$typedErrors['candidate']['city'] ||
$typedErrors['candidate']['street'] ||
$typedErrors['candidate']['houseNumber'] ||
$typedErrors['candidate']['zip']
) { ) {
return true; return true;
} }
break; break;
case 3: case 4:
if ( if (
$typedErrors['candidate']['birthplace'] || $typedErrors['candidate']['citizenship'] ||
$typedErrors['candidate']['personalIdNumber'] ||
$typedErrors['candidate']['schoolName'] ||
$typedErrors['candidate']['healthInsurance'] ||
$typedErrors['candidate']['birthdate'] || $typedErrors['candidate']['birthdate'] ||
$typedErrors['candidate']['street'] || $typedErrors['candidate']['birthplace'] ||
$typedErrors['candidate']['houseNumber'] || $typedErrors['candidate']['personalIdNumber'] ||
$typedErrors['candidate']['city'] || $typedErrors['candidate']['testLanguage'] ||
$typedErrors['candidate']['zip'] !personalIdBirthdateMatch
// $typedErrors['candidate']['address']
) { ) {
return true; return true;
} }
break; break;
case 4: case 5:
if ( if (
$typedErrors['parents'][0]['name'] || $typedErrors['parents'][0]['name'] ||
$typedErrors['parents'][0]['surname'] || $typedErrors['parents'][0]['surname'] ||
@ -347,7 +338,7 @@
return true; return true;
} }
break; break;
case 5: case 6:
if ( if (
$typedErrors['parents'][1]['name'] || $typedErrors['parents'][1]['name'] ||
$typedErrors['parents'][1]['surname'] || $typedErrors['parents'][1]['surname'] ||
@ -357,17 +348,17 @@
return true; return true;
} }
break; break;
case 6: case 7:
if ( if (
$typedErrors['candidate']['citizenship'] || $typedErrors['candidate']['firstSchool']['name'] ||
$typedErrors['candidate']['personalIdNumber'] || $typedErrors['candidate']['firstSchool']['field'] ||
$typedErrors['candidate']['schoolName'] || $typedErrors['candidate']['secondSchool']['name'] ||
$typedErrors['candidate']['healthInsurance'] $typedErrors['candidate']['secondSchool']['field']
) { ) {
return true; return true;
} }
break; break;
case 7: case 8:
if ($typedErrors['candidate']['grades'].length > 0) return true; if ($typedErrors['candidate']['grades'].length > 0) return true;
break; break;
default: default:
@ -380,6 +371,11 @@
return '+' + telephone.match(/[0-9]{1,3}/g)!.join(' '); return '+' + telephone.match(/[0-9]{1,3}/g)!.join(' ');
}; };
/* $form.candidate.personalIdNumber = data.whoami.personalIdNumber;
const [birthdate, sex] = deriveBirthdateFromPersonalId(data.whoami.personalIdNumber);
$form.candidate.birthdate = birthdate;
$form.candidate.sex = 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('.');
@ -387,10 +383,13 @@
details.parents.map( details.parents.map(
(x) => (x.telephone = x.telephone != '' ? formatTelephone(x.telephone) : '') (x) => (x.telephone = x.telephone != '' ? formatTelephone(x.telephone) : '')
); );
form.set({ form.set({
gdpr: true, gdpr: true,
linkOk: true, linkOk: true,
linkError: false, linkError: false,
personalIdOk: true,
personalIdErr: false,
candidate: { candidate: {
...details.candidate, ...details.candidate,
street: details.candidate.address.split(',')[0].split(' ')[0], street: details.candidate.address.split(',')[0].split(' ')[0],
@ -414,23 +413,35 @@
} }
] ]
}); });
pageIndex = 2; // skip gdpr page pageIndex = editModePageIndex; // skip gdpr page
pageTexts[2] = $LL.candidate.register.fourth.titleEdit(); pageTexts[2] = $LL.candidate.register.fourth.titleEdit();
} }
</script> </script>
<SplitLayout> <SplitLayout>
<SvelteToast /> <SvelteToast />
<div class="form relative"> <div class="form relative bg-center">
<div class="bottom-3/12 absolute flex w-full flex-col md:h-auto"> <div class="bottom-5/24 absolute flex w-full flex-col md:h-auto">
<!-- TODO: Find different way how to display SchoolBadge --> <div class="<md:hidden self-center">
{#if pageIndex !== 0 && pageIndex !== 7} <SchoolBadge />
<div class="<md:h-24 <md:w-24 mb-4 h-32 w-32 self-center"> </div>
<SchoolBadge />
</div>
{/if}
<form on:submit={handleSubmit} id="triggerForm" class="invisible hidden" /> <form on:submit={handleSubmit} id="triggerForm" class="invisible hidden" />
{#if pageIndex === 0} {#if pageIndex === 0}
<form on:submit={handleSubmit}>
<h1 class="title mt-8">{$LL.candidate.register.first.title()}</h1>
<p class="description mt-8 block text-center">
{$LL.candidate.register.first.description()}
</p>
<div class="field">
<PersonalIdConfirmCheckBox
personalIdNumber={baseCandidateDetails.personalIdNumber}
bind:personalIdOk={$form.personalIdOk}
bind:personalIdErr={$form.personalIdErr}
error={$typedErrors['personalIdOk']}
/>
</div>
</form>
{:else if pageIndex === 1}
<form on:submit={handleSubmit}> <form on:submit={handleSubmit}>
<h1 class="title mt-8">{$LL.candidate.register.first.title()}</h1> <h1 class="title mt-8">{$LL.candidate.register.first.title()}</h1>
<p class="description mt-8 block text-center"> <p class="description mt-8 block text-center">
@ -445,7 +456,7 @@
/> />
</div> </div>
</form> </form>
{:else if pageIndex === 1} {:else if pageIndex === 2}
<form on:submit={handleSubmit}> <form on:submit={handleSubmit}>
<h1 class="title mt-8">{pageTexts[0]}</h1> <h1 class="title mt-8">{pageTexts[0]}</h1>
<p class="description mt-8 block text-center"> <p class="description mt-8 block text-center">
@ -456,89 +467,104 @@
<GdprCheckBox bind:value={$form.gdpr} error={$typedErrors['gdpr']} /> <GdprCheckBox bind:value={$form.gdpr} error={$typedErrors['gdpr']} />
</div> </div>
</form> </form>
{:else if pageIndex === 2} {:else if pageIndex === 3}
<form on:submit={handleSubmit}> <form on:submit={handleSubmit}>
<h1 class="title mt-8">{pageTexts[1]}</h1> <h1 class="title mt-8">{pageTexts[1]}</h1>
<p class="description mt-8 block text-center"> <p class="description mt-8 block text-center">
{$LL.candidate.register.third.description()} {$LL.candidate.register.third.description()}
</p> </p>
<div class="flex flex-col"> <div class="w-full">
<span class="field"> <div class="flex flex-col">
<NameField <div class="field flex">
error={$typedErrors['candidate']['name'] || $typedErrors['candidate']['surname']} <span class="w-[50%]">
bind:valueName={$form.candidate.name} <NameField
bind:valueSurname={$form.candidate.surname} error={$typedErrors['candidate']['name'] ||
placeholder={$LL.input.nameSurname()} $typedErrors['candidate']['surname']}
/> bind:valueName={$form.candidate.name}
</span> bind:valueSurname={$form.candidate.surname}
<span class="field"> placeholder={$LL.input.nameSurname()}
<EmailField />
error={$typedErrors['candidate']['email']} </span>
bind:value={$form.candidate.email} <span class="ml-2 w-[50%]">
placeholder={$LL.input.email()} <TextField
/> error={$typedErrors['candidate']['birthSurname']}
</span> bind:value={$form.candidate.birthSurname}
<span class="field"> placeholder={$LL.input.birthSurname()}
<TelephoneField />
error={$typedErrors['candidate']['telephone']} </span>
bind:value={$form.candidate.telephone} </div>
placeholder={$LL.input.telephone()} <div class="field flex">
/> <span class="w-[50%]">
</span> <EmailField
error={$typedErrors['candidate']['email']}
bind:value={$form.candidate.email}
placeholder={$LL.input.email()}
/>
</span>
<span class="ml-2 w-[50%]">
<TelephoneField
error={$typedErrors['candidate']['telephone']}
bind:value={$form.candidate.telephone}
placeholder={$LL.input.telephone()}
/>
</span>
</div>
<span class="field">
<TextField
error={$typedErrors['candidate']['city']}
bind:value={$form.candidate.city}
type="text"
placeholder={$LL.input.city()}
helperText="Uveďte poštovní směrovací číslo. (např. 602 00)"
/>
</span>
</div>
<div class="field flex">
<span class="w-[66%]">
<NameField
error={$typedErrors['candidate']['street'] ||
$typedErrors['candidate']['houseNumber']}
bind:valueName={$form.candidate.street}
bind:valueSurname={$form.candidate.houseNumber}
placeholder={$LL.input.address()}
helperText="Uveďte ulici a číslo popisné (např. Preslova 72)."
/>
</span>
<span class="ml-2 w-[33%]">
<TextField
error={$typedErrors['candidate']['zip']}
bind:value={$form.candidate.zip}
type="number"
placeholder={$LL.input.zipCode()}
helperText="Uveďte poštovní směrovací číslo. (např. 602 00)"
/>
</span>
</div>
</div> </div>
</form> </form>
{:else if pageIndex === 3} {:else if pageIndex === 4}
<h1 class="title mt-8">{pageTexts[2]}</h1> <h1 class="title mt-8">{pageTexts[2]}</h1>
<p class="description mt-8 block text-center"> <p class="description mt-8 block text-center">
{$LL.candidate.register.fourth.description()} {$LL.candidate.register.fourth.description()}
</p> </p>
<div class="field flex"> <div class="field flex w-full">
<span class="w-[66%]"> <span class="w-[50%]">
<NameField <SelectField
error={$typedErrors['candidate']['street'] || error={$typedErrors['candidate']['citizenship']}
$typedErrors['candidate']['houseNumber']} bind:value={$form.candidate.citizenship}
bind:valueName={$form.candidate.street} placeholder={$LL.input.citizenship()}
bind:valueSurname={$form.candidate.houseNumber} options={['Česká republika', 'Slovenská republika', 'Ukrajina', 'Jiné']}
placeholder={$LL.input.address()}
helperText="Uveďte ulici a číslo popisné (např. Preslova 72)."
/> />
</span> </span>
<span class="ml-2 w-[33%]"> <span class="ml-2 w-[50%]">
<TextField <SelectField
error={$typedErrors['candidate']['zip']} error={$typedErrors['candidate']['testLanguage']}
bind:value={$form.candidate.zip} bind:value={$form.candidate.testLanguage}
type="number" placeholder={$LL.input.testLanguage()}
placeholder={$LL.input.zipCode()} options={['Čeština', 'Angličtina']}
helperText="Uveďte poštovní směrovací číslo. (např. 602 00)"
/> />
</span> </span>
</div> </div>
<div class="field flex">
<span>
<TextField
error={$typedErrors['candidate']['city']}
bind:value={$form.candidate.city}
type="text"
placeholder={$LL.input.city()}
helperText="Uveďte město"
/>
</span>
<span class="ml-2">
<TextField
error={$typedErrors['candidate']['birthplace']}
bind:value={$form.candidate.birthplace}
type="text"
placeholder={$LL.input.birthPlace()}
helperText="Uveďte město"
icon
>
<div slot="icon" class="text-sspsBlue flex items-center justify-center">
<Home />
</div>
</TextField>
</span>
</div>
<div class="field flex items-center"> <div class="field flex items-center">
<TextField <TextField
error={$typedErrors['candidate']['birthdate']} error={$typedErrors['candidate']['birthdate']}
@ -547,6 +573,30 @@
placeholder={$LL.input.birthDate()} placeholder={$LL.input.birthDate()}
helperText="TODO: (Uveďte ve formátu DD.MM.RRRR)" helperText="TODO: (Uveďte ve formátu DD.MM.RRRR)"
/> />
<div class="ml-2">
<TextField
error={$typedErrors['candidate']['birthplace']}
bind:value={$form.candidate.birthplace}
type="text"
placeholder={$LL.input.birthPlace()}
helperText="TODO: (Místo narození)"
/>
</div>
</div>
<div class="field flex items-center justify-center">
{#if $form.candidate.citizenship === 'Česká republika' || !$form.candidate.citizenship}
<IdField
error={$typedErrors['candidate']['personalIdNumber']}
bind:value={$form.candidate.personalIdNumber}
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']}
@ -556,7 +606,35 @@
/> />
</div> </div>
</div> </div>
{:else if pageIndex === 4} <div class="field flex flex-row">
<span>
{#if $form.candidate.citizenship === 'Česká republika' || !$form.candidate.citizenship}
<TextField
error={$typedErrors['candidate']['schoolName']}
type="number"
bind:value={$form.candidate.schoolName}
placeholder={$LL.input.schoolIzo()}
/>
{:else}
<TextField
error={$typedErrors['candidate']['schoolName']}
type="text"
bind:value={$form.candidate.schoolName}
placeholder={$LL.input.schoolName()}
/>
{/if}
</span>
<span class="ml-2">
<TextField
error={$typedErrors['candidate']['healthInsurance']}
type="text"
bind:value={$form.candidate.healthInsurance}
placeholder={$LL.input.insuranceNumber()}
/>
</span>
</div>
{:else if pageIndex === 5}
<h1 class="title mt-8">{pageTexts[3]}</h1> <h1 class="title mt-8">{pageTexts[3]}</h1>
<p class="description mt-8 block text-center"> <p class="description mt-8 block text-center">
{$LL.candidate.register.fifth.description()} {$LL.candidate.register.fifth.description()}
@ -585,7 +663,7 @@
/> />
</span> </span>
</div> </div>
{:else if pageIndex === 5} {:else if pageIndex === 6}
<h1 class="title mt-8">{pageTexts[4]}</h1> <h1 class="title mt-8">{pageTexts[4]}</h1>
<p class="description mt-8 block text-center"> <p class="description mt-8 block text-center">
{$LL.candidate.register.sixth.description()} {$LL.candidate.register.sixth.description()}
@ -614,75 +692,28 @@
/> />
</span> </span>
</div> </div>
{:else if pageIndex === 6} {:else if pageIndex === 7}
<h1 class="title mt-8">{pageTexts[5]}</h1> <h1 class="title mt-8">{pageTexts[5]}</h1>
<p class="description mt-8 block text-center"> <p class="description my-8 block text-center">
{$LL.candidate.register.seventh.description()} {$LL.candidate.register.seventh.description()}
</p> </p>
<div class="flex w-full flex-col"> <div class="flex h-full flex-col justify-between">
<div class="field flex w-full"> <span class="field">
<span class="w-[50%]"> <SchoolSelect
<SelectField error={$typedErrors['candidate']['firstSchool']['name'] ||
error={$typedErrors['candidate']['citizenship']} $typedErrors['candidate']['firstSchool']['field']}
bind:value={$form.candidate.citizenship} bind:selectedSchool={$form.candidate.firstSchool}
placeholder={$LL.input.citizenship()}
options={['Česká republika', 'Slovenská republika', 'Ukrajina', 'Jiné']}
/>
</span>
<span class="ml-2 w-[50%]">
<SelectField
error={$typedErrors['candidate']['testLanguage']}
bind:value={$form.candidate.testLanguage}
placeholder={$LL.input.testLanguage()}
options={['Čeština', 'Angličtina']}
/>
</span>
</div>
<div class="field flex flex-row">
<span>
{#if $form.candidate.citizenship === 'Česká republika' || !$form.candidate.citizenship}
<TextField
error={$typedErrors['candidate']['schoolName']}
type="number"
bind:value={$form.candidate.schoolName}
placeholder={$LL.input.schoolIzo()}
/>
{:else}
<TextField
error={$typedErrors['candidate']['schoolName']}
type="text"
bind:value={$form.candidate.schoolName}
placeholder={$LL.input.schoolName()}
/>
{/if}
</span>
<span class="ml-2">
<TextField
error={$typedErrors['candidate']['healthInsurance']}
type="text"
bind:value={$form.candidate.healthInsurance}
placeholder={$LL.input.insuranceNumber()}
/>
</span>
</div>
</div>
<div class="field flex items-center justify-center">
{#if $form.candidate.citizenship === 'Česká republika' || !$form.candidate.citizenship}
<IdField
error={$typedErrors['candidate']['personalIdNumber']}
bind:value={$form.candidate.personalIdNumber}
placeholder={$LL.input.personalIdentificationNumber()}
/> />
{:else} </span>
<TextField <span class="field mt-10">
error={$typedErrors['candidate']['personalIdNumber']} <SchoolSelect
bind:value={$form.candidate.personalIdNumber} error={$typedErrors['candidate']['secondSchool']['name'] ||
placeholder={$LL.input.personalIdentificationNumber()} $typedErrors['candidate']['secondSchool']['field']}
bind:selectedSchool={$form.candidate.secondSchool}
/> />
{/if} </span>
</div> </div>
{:else if pageIndex === 7} {:else if pageIndex === 8}
<h1 class="title mt-8">{pageTexts[6]}</h1> <h1 class="title mt-8">{pageTexts[6]}</h1>
<p class="description mt-8 block text-center"> <p class="description mt-8 block text-center">
{$LL.candidate.register.eighth.description()} {$LL.candidate.register.eighth.description()}
@ -693,7 +724,7 @@
/> />
{/if} {/if}
</div> </div>
<div class="bottom-1/12 absolute w-full"> <div class="bottom-1/24 absolute w-full">
<div class="field"> <div class="field">
<Submit <Submit
on:click={async (e) => { on:click={async (e) => {
@ -711,7 +742,7 @@
/> />
</div> </div>
<div class="mt-4 flex flex-row justify-center md:mt-8"> <div class="mt-4 flex flex-row justify-center md:mt-6">
{#each Array(pageCount + 1) as _, i} {#each Array(pageCount + 1) as _, i}
<button <button
class:dotActive={i === pageIndex} class:dotActive={i === pageIndex}

View file

@ -25,7 +25,11 @@
{$LL.candidate.auth.login.description()} {$LL.candidate.auth.login.description()}
</p> </p>
<div class="w-4/5 lg:w-3/5"> <div class="w-4/5 lg:w-3/5">
<TextField bind:value={applicationValue} placeholder={$LL.input.evidenceNumber()} type="number" /> <TextField
bind:value={applicationValue}
placeholder={$LL.input.evidenceNumber()}
type="number"
/>
</div> </div>
<div class="mt-8 w-4/5 lg:w-3/5"> <div class="mt-8 w-4/5 lg:w-3/5">
<Submit on:click={redirectToCode} value={$LL.input.submit()} /> <Submit on:click={redirectToCode} value={$LL.input.submit()} />

View file

@ -5,7 +5,7 @@ import type { LayoutLoad } from './$types';
export const load: LayoutLoad = async ({ url }) => { export const load: LayoutLoad = async ({ url }) => {
await loadAllLocalesAsync(); await loadAllLocalesAsync();
setLocale('cs'); setLocale('cs');
return { return {
url: url.pathname url: url.pathname
}; };

View file

@ -58,9 +58,9 @@ const cs: BaseTranslation = {
'Zde můžete zadat údaje o druhém zákonném zástupci. Škole tím umožníte lépe komunikovat.' 'Zde můžete zadat údaje o druhém zákonném zástupci. Škole tím umožníte lépe komunikovat.'
}, },
seventh: { seventh: {
title: 'Dokončení registrace', title: 'Přihlášky na školy',
description: description:
'Zadejte prosím své občanství, rodné číslo, či jeho alternativu Vaší země a obor na který se hlásíte.' 'Zde můžete vyplnit přihlášky na školy v pořadí Vašeho výběru. V případě, že jste podali přihlášku na více škol, vyplňte přihlášky na školy v pořadí Vašeho výběru.'
}, },
eighth: { eighth: {
title: 'Poslední krok', title: 'Poslední krok',
@ -100,7 +100,7 @@ const cs: BaseTranslation = {
}, },
missing: { missing: {
title: 'Soubory nebyly nahrány!', title: 'Soubory nebyly nahrány!',
description: 'Nahrajte včechny soubory prosím', description: 'Nahrajte včechny soubory prosím'
} }
} }
}, },
@ -118,6 +118,12 @@ const cs: BaseTranslation = {
title2: 'Ne, přihlášku na SSPŠaG jsem podával více přihlášek' title2: 'Ne, přihlášku na SSPŠaG jsem podával více přihlášek'
} }
}, },
personalIdConfirmCheckBox: {
ok: 'Vše je v pořádku',
whatHappened: 'Co se děje?',
titleOk: 'Potvrzuji, že moje rodné číslo je {personalId}',
titleErr: 'Ne, moje rodné číslo není {personalId}'
},
gdprCheckBox: { gdprCheckBox: {
title: 'Souhlasím se zpracováním osobních údajů', title: 'Souhlasím se zpracováním osobních údajů',
description: 'Kliknutím vyjaďřujete souhlas se zpracováním osobních údajů', description: 'Kliknutím vyjaďřujete souhlas se zpracováním osobních údajů',
@ -128,6 +134,7 @@ const cs: BaseTranslation = {
input: { input: {
optional: 'nepovinné', optional: 'nepovinné',
nameSurname: 'Jméno a příjmení', nameSurname: 'Jméno a příjmení',
birthSurname: 'Rodné příjmení',
email: 'E-mail', email: 'E-mail',
telephone: 'Telefon', telephone: 'Telefon',
address: 'Ulice a č. p.', address: 'Ulice a č. p.',
@ -147,6 +154,8 @@ const cs: BaseTranslation = {
password: 'Heslo', password: 'Heslo',
submit: 'Odeslat', submit: 'Odeslat',
continue: 'Pokračovat', continue: 'Pokračovat',
fieldOfStudy: 'Obor',
selectedSchool: 'Vybraná škola',
parent: { parent: {
nameSurname: 'Jméno a příjmení zákonného zástupce', nameSurname: 'Jméno a příjmení zákonného zástupce',
email: 'E-mail zákonného zástupce', email: 'E-mail zákonného zástupce',

View file

@ -1,11 +1,10 @@
import type { FormattersInitializer } from 'typesafe-i18n' import type { FormattersInitializer } from 'typesafe-i18n';
import type { Locales, Formatters } from './i18n-types.js' import type { Locales, Formatters } from './i18n-types.js';
export const initFormatters: FormattersInitializer<Locales, Formatters> = (locale: Locales) => { export const initFormatters: FormattersInitializer<Locales, Formatters> = (locale: Locales) => {
const formatters: Formatters = { const formatters: Formatters = {
// add your formatter functions here // add your formatter functions here
} };
return formatters return formatters;
} };

View file

@ -129,11 +129,11 @@ type RootTranslation = {
} }
seventh: { seventh: {
/** /**
* Dokončení registrace * Přihlášky na školy
*/ */
title: string title: string
/** /**
* Zadejte prosím své občanství, rodné číslo, či jeho alternativu Vaší země a obor na který se hlásíte. * Zde můžete vyplnit přihlášky na školy v pořadí Vašeho výběru. V případě, že jste podali přihlášku na více škol, vyplňte přihlášky na školy v pořadí Vašeho výběru.
*/ */
description: string description: string
} }
@ -272,6 +272,26 @@ type RootTranslation = {
title2: string title2: string
} }
} }
personalIdConfirmCheckBox: {
/**
* Vše je v pořádku
*/
ok: string
/**
* Co se děje?
*/
whatHappened: string
/**
* Potvrzuji, že moje rodné číslo je {personalId}
* @param {unknown} personalId
*/
titleOk: RequiredParams<'personalId'>
/**
* Ne, moje rodné číslo není {personalId}
* @param {unknown} personalId
*/
titleErr: RequiredParams<'personalId'>
}
gdprCheckBox: { gdprCheckBox: {
/** /**
* Souhlasím se zpracováním osobních údajů * Souhlasím se zpracováním osobních údajů
@ -297,6 +317,10 @@ type RootTranslation = {
* Jméno a příjmení * Jméno a příjmení
*/ */
nameSurname: string nameSurname: string
/**
* Rodné příjmení
*/
birthSurname: string
/** /**
* E-mail * E-mail
*/ */
@ -373,6 +397,14 @@ type RootTranslation = {
* Pokračovat * Pokračovat
*/ */
'continue': string 'continue': string
/**
* Obor
*/
fieldOfStudy: string
/**
* Vybraná škola
*/
selectedSchool: string
parent: { parent: {
/** /**
* Jméno a příjmení zákonného zástupce * Jméno a příjmení zákonného zástupce
@ -507,11 +539,11 @@ export type TranslationFunctions = {
} }
seventh: { seventh: {
/** /**
* Dokončení registrace * Přihlášky na školy
*/ */
title: () => LocalizedString title: () => LocalizedString
/** /**
* Zadejte prosím své občanství, rodné číslo, či jeho alternativu Vaší země a obor na který se hlásíte. * Zde můžete vyplnit přihlášky na školy v pořadí Vašeho výběru. V případě, že jste podali přihlášku na více škol, vyplňte přihlášky na školy v pořadí Vašeho výběru.
*/ */
description: () => LocalizedString description: () => LocalizedString
} }
@ -645,6 +677,24 @@ export type TranslationFunctions = {
title2: () => LocalizedString title2: () => LocalizedString
} }
} }
personalIdConfirmCheckBox: {
/**
* Vše je v pořádku
*/
ok: () => LocalizedString
/**
* Co se děje?
*/
whatHappened: () => LocalizedString
/**
* Potvrzuji, že moje rodné číslo je {personalId}
*/
titleOk: (arg: { personalId: unknown }) => LocalizedString
/**
* Ne, moje rodné číslo není {personalId}
*/
titleErr: (arg: { personalId: unknown }) => LocalizedString
}
gdprCheckBox: { gdprCheckBox: {
/** /**
* Souhlasím se zpracováním osobních údajů * Souhlasím se zpracováním osobních údajů
@ -670,6 +720,10 @@ export type TranslationFunctions = {
* Jméno a příjmení * Jméno a příjmení
*/ */
nameSurname: () => LocalizedString nameSurname: () => LocalizedString
/**
* Rodné příjmení
*/
birthSurname: () => LocalizedString
/** /**
* E-mail * E-mail
*/ */
@ -746,6 +800,14 @@ export type TranslationFunctions = {
* Pokračovat * Pokračovat
*/ */
'continue': () => LocalizedString 'continue': () => LocalizedString
/**
* Obor
*/
fieldOfStudy: () => LocalizedString
/**
* Vybraná škola
*/
selectedSchool: () => LocalizedString
parent: { parent: {
/** /**
* Jméno a příjmení zákonného zástupce * Jméno a příjmení zákonného zástupce

View file

@ -1,6 +1,6 @@
import adapter from '@sveltejs/adapter-node'; import adapter from '@sveltejs/adapter-node';
import preprocess from 'svelte-preprocess'; import preprocess from 'svelte-preprocess';
import path from "path"; import path from 'path';
import { windi } from 'svelte-windicss-preprocess'; import { windi } from 'svelte-windicss-preprocess';
/** @type {import('@sveltejs/kit').Config} */ /** @type {import('@sveltejs/kit').Config} */
@ -11,9 +11,8 @@ const config = {
kit: { kit: {
adapter: adapter({ out: 'build' }), adapter: adapter({ out: 'build' }),
alias: { alias: {
$i18n: path.resolve('./src/translations'), $i18n: path.resolve('./src/translations')
} }
} }
}; };

View file

@ -9,6 +9,6 @@
"resolveJsonModule": true, "resolveJsonModule": true,
"skipLibCheck": true, "skipLibCheck": true,
"sourceMap": true, "sourceMap": true,
"strict": true, "strict": true
} }
} }