mirror of
https://github.com/danbulant/Portfolio
synced 2026-06-17 13:31:12 +00:00
Merge pull request #174 from EETagent/frontend_school_order_selection
Frontend school order selection
This commit is contained in:
commit
95d74d192b
22 changed files with 759 additions and 284 deletions
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"baseLocale": "cs",
|
||||
"adapter": "svelte",
|
||||
"esmImports": true,
|
||||
"outputPath": "./src/translations",
|
||||
"$schema": "https://unpkg.com/typesafe-i18n@5.20.0/schema/typesafe-i18n.json"
|
||||
}
|
||||
"baseLocale": "cs",
|
||||
"adapter": "svelte",
|
||||
"esmImports": true,
|
||||
"outputPath": "./src/translations",
|
||||
"$schema": "https://unpkg.com/typesafe-i18n@5.20.0/schema/typesafe-i18n.json"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
export let linkOk: boolean = false;
|
||||
export let linkError: boolean = false;
|
||||
export let applications: Array<number>;
|
||||
|
||||
let title1 = $LL.components.checkbox.accountLinkCheckBox.multiple.title({
|
||||
first: applications[0],
|
||||
second: applications[1]
|
||||
|
|
@ -20,8 +19,6 @@
|
|||
title2 = $LL.components.checkbox.accountLinkCheckBox.single.title2();
|
||||
}
|
||||
|
||||
$: console.log(linkOk, linkError);
|
||||
|
||||
export let error: string = '';
|
||||
|
||||
const switchSelection = (id: number) => {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,9 @@
|
|||
|
||||
<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"><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>
|
||||
</label>
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -160,30 +160,33 @@
|
|||
<span class="font-bold">{$baseCandidateData.applications[1]}</span></span
|
||||
>
|
||||
{/if}
|
||||
<span>{$LL.input.address()}: <span class="font-bold">{$candidateData.candidate.address}</span></span>
|
||||
<span
|
||||
>{$LL.input.birthDate()}: <span class="font-bold">{$candidateData.candidate.birthdate}</span
|
||||
></span
|
||||
>{$LL.input.address()}:
|
||||
<span class="font-bold">{$candidateData.candidate.address}</span></span
|
||||
>
|
||||
<span
|
||||
>{$LL.input.birthPlace()}: <span class="font-bold">{$candidateData.candidate.birthplace}</span
|
||||
></span
|
||||
>{$LL.input.birthDate()}:
|
||||
<span class="font-bold">{$candidateData.candidate.birthdate}</span></span
|
||||
>
|
||||
<span
|
||||
>{$LL.input.personalIdentificationNumber()}: <span class="font-bold"
|
||||
>{$candidateData.candidate.personalIdNumber}</span
|
||||
></span
|
||||
>{$LL.input.birthPlace()}:
|
||||
<span class="font-bold">{$candidateData.candidate.birthplace}</span></span
|
||||
>
|
||||
<span
|
||||
>{$LL.input.schoolIzo()}: <span class="font-bold">{$candidateData.candidate.schoolName}</span
|
||||
></span
|
||||
>{$LL.input.personalIdentificationNumber()}:
|
||||
<span class="font-bold">{$candidateData.candidate.personalIdNumber}</span></span
|
||||
>
|
||||
<span
|
||||
>{$LL.input.insuranceNumber()}: <span class="font-bold"
|
||||
>{$candidateData.candidate.healthInsurance}</span
|
||||
></span
|
||||
>{$LL.input.schoolIzo()}:
|
||||
<span class="font-bold">{$candidateData.candidate.schoolName}</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
|
||||
|
|
@ -202,7 +205,9 @@
|
|||
>{parent.name + ' ' + parent.surname}</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>
|
||||
{/each}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -105,7 +105,8 @@
|
|||
{#if status === 'uploaded'}
|
||||
<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"
|
||||
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}
|
||||
<StatusNotificationDot {status} />
|
||||
|
|
@ -133,9 +134,16 @@
|
|||
>
|
||||
<div class="hidden items-center xl:block">
|
||||
{#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}
|
||||
<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>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
}
|
||||
return grades;
|
||||
};
|
||||
|
||||
|
||||
let gradesLocal: Array<Grade> =
|
||||
grades.length > 0
|
||||
? convertGradeBackendToGrade(grades)
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@
|
|||
.view {
|
||||
@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 md:h-screen md:w-[50vw];
|
||||
@apply md:h-screen md:w-[60vw];
|
||||
@apply md:my-auto;
|
||||
@apply bg-white;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -1,13 +1,19 @@
|
|||
import type { GradeBackend } from '$lib/components/grades/GradesTable.svelte';
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export interface School {
|
||||
name: string;
|
||||
field: string;
|
||||
}
|
||||
export interface CandidateData {
|
||||
candidate: {
|
||||
name: string;
|
||||
surname: string;
|
||||
birthSurname: string;
|
||||
birthplace: string;
|
||||
birthdate: string;
|
||||
address: string;
|
||||
letterAddress: string;
|
||||
telephone: string;
|
||||
citizenship: string;
|
||||
email: string;
|
||||
|
|
@ -16,6 +22,8 @@ export interface CandidateData {
|
|||
schoolName: string;
|
||||
healthInsurance: string;
|
||||
grades: Array<GradeBackend>;
|
||||
firstSchool: School;
|
||||
secondSchool: School;
|
||||
testLanguage: string;
|
||||
};
|
||||
parents: Array<{
|
||||
|
|
@ -66,9 +74,11 @@ export const candidateData = writable<CandidateData>({
|
|||
candidate: {
|
||||
name: '',
|
||||
surname: '',
|
||||
birthSurname: '',
|
||||
birthplace: '',
|
||||
birthdate: '',
|
||||
address: '',
|
||||
letterAddress: '',
|
||||
telephone: '',
|
||||
citizenship: '',
|
||||
email: '',
|
||||
|
|
@ -77,6 +87,8 @@ export const candidateData = writable<CandidateData>({
|
|||
schoolName: '',
|
||||
healthInsurance: '',
|
||||
grades: [],
|
||||
firstSchool: { name: '', field: '' },
|
||||
secondSchool: { name: '', field: '' },
|
||||
testLanguage: ''
|
||||
},
|
||||
parents: []
|
||||
|
|
|
|||
75
frontend/src/lib/utils/personalIdFormat.ts
Normal file
75
frontend/src/lib/utils/personalIdFormat.ts
Normal 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];
|
||||
};
|
||||
|
|
@ -10,9 +10,11 @@ export const load: PageServerLoad = async ({ fetch, params }) => {
|
|||
candidate: {
|
||||
name: '',
|
||||
surname: '',
|
||||
birthSurname: '',
|
||||
birthplace: '',
|
||||
birthdate: '',
|
||||
address: '',
|
||||
letterAddress: '',
|
||||
telephone: '',
|
||||
citizenship: '',
|
||||
email: '',
|
||||
|
|
@ -21,6 +23,8 @@ export const load: PageServerLoad = async ({ fetch, params }) => {
|
|||
schoolName: '',
|
||||
healthInsurance: '',
|
||||
grades: [],
|
||||
firstSchool: { name: '', field: '' },
|
||||
secondSchool: { name: '', field: '' },
|
||||
testLanguage: ''
|
||||
},
|
||||
parents: []
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
import Submit from '$lib/components/button/Submit.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 SplitLayout from '$lib/components/layout/SplitLayout.svelte';
|
||||
import SelectField from '$lib/components/select/SelectField.svelte';
|
||||
|
|
@ -24,10 +23,14 @@
|
|||
import type { CandidateData } from '$lib/stores/candidate';
|
||||
import AccountLinkCheckBox from '$lib/components/checkbox/AccountLinkCheckBox.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 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 = [
|
||||
$LL.candidate.register.second.title(),
|
||||
$LL.candidate.register.third.title(),
|
||||
|
|
@ -42,19 +45,24 @@
|
|||
let details = data.candidate;
|
||||
let baseCandidateDetails = data.whoami;
|
||||
|
||||
let personalIdBirthdateMatch = true;
|
||||
const formInitialValues = {
|
||||
gdpr: false,
|
||||
personalIdOk: false,
|
||||
personalIdErr: false,
|
||||
linkOk: false,
|
||||
linkError: false,
|
||||
candidate: {
|
||||
name: '',
|
||||
surname: '',
|
||||
birthSurname: '',
|
||||
email: '',
|
||||
telephone: '',
|
||||
birthplace: '',
|
||||
birthdate: '',
|
||||
sex: '',
|
||||
address: '',
|
||||
letterAddress: '',
|
||||
street: '',
|
||||
houseNumber: '',
|
||||
city: '',
|
||||
|
|
@ -64,6 +72,8 @@
|
|||
schoolName: '',
|
||||
healthInsurance: '',
|
||||
grades: [],
|
||||
firstSchool: { name: '', field: '' },
|
||||
secondSchool: { name: '', field: '' },
|
||||
testLanguage: ''
|
||||
},
|
||||
parents: [
|
||||
|
|
@ -84,6 +94,8 @@
|
|||
|
||||
const formValidationSchema = yup.object().shape({
|
||||
gdpr: yup.boolean().oneOf([true]),
|
||||
personalIdOk: yup.boolean().oneOf([true]),
|
||||
personalIdErr: yup.boolean().oneOf([false]),
|
||||
linkOk: yup.boolean().oneOf([true]),
|
||||
linkError: yup.boolean().oneOf([false]),
|
||||
candidate: yup.object().shape({
|
||||
|
|
@ -99,6 +111,7 @@
|
|||
.string()
|
||||
.required()
|
||||
.matches(/^([0-3]?[0-9])\.(0?[1-9]|1[0-2])\.[0-9]{4}$/),
|
||||
birthSurname: yup.string().required(),
|
||||
sex: yup.string(),
|
||||
address: yup.string(),
|
||||
street: yup.string().required(),
|
||||
|
|
@ -126,6 +139,14 @@
|
|||
.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()
|
||||
}),
|
||||
parents: yup.array().of(
|
||||
|
|
@ -167,7 +188,12 @@
|
|||
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>>
|
||||
? 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)
|
||||
$: typedErrors = errors as unknown as Writable<FormErrorType>;
|
||||
|
||||
// TODO: validate on admin dashboard, move somewhere
|
||||
// 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;
|
||||
}
|
||||
};
|
||||
|
||||
$: console.log($typedErrors);
|
||||
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) {
|
||||
console.log('submitting');
|
||||
// clone values to oldValues
|
||||
let oldValues = JSON.parse(JSON.stringify(values));
|
||||
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
|
||||
delete values.undefined;
|
||||
// convert birthdate from dd.mm.yyyy to yyyy-mm-dd
|
||||
|
|
@ -304,40 +284,51 @@
|
|||
const isPageInvalid = (index: number): boolean => {
|
||||
switch (index) {
|
||||
case 0:
|
||||
if ($typedErrors['linkOk'] || $typedErrors['linkError']) {
|
||||
if ($typedErrors['personalIdOk'] || $typedErrors['personalIdErr']) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if ($typedErrors['gdpr']) {
|
||||
if ($typedErrors['linkOk'] || $typedErrors['linkError']) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if ($typedErrors['gdpr']) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (
|
||||
$typedErrors['candidate']['name'] ||
|
||||
$typedErrors['candidate']['surname'] ||
|
||||
$typedErrors['candidate']['email'] ||
|
||||
$typedErrors['candidate']['telephone']
|
||||
$typedErrors['candidate']['telephone'] ||
|
||||
$typedErrors['candidate']['city'] ||
|
||||
$typedErrors['candidate']['street'] ||
|
||||
$typedErrors['candidate']['houseNumber'] ||
|
||||
$typedErrors['candidate']['zip']
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
case 4:
|
||||
if (
|
||||
$typedErrors['candidate']['birthplace'] ||
|
||||
$typedErrors['candidate']['citizenship'] ||
|
||||
$typedErrors['candidate']['personalIdNumber'] ||
|
||||
$typedErrors['candidate']['schoolName'] ||
|
||||
$typedErrors['candidate']['healthInsurance'] ||
|
||||
$typedErrors['candidate']['birthdate'] ||
|
||||
$typedErrors['candidate']['street'] ||
|
||||
$typedErrors['candidate']['houseNumber'] ||
|
||||
$typedErrors['candidate']['city'] ||
|
||||
$typedErrors['candidate']['zip']
|
||||
// $typedErrors['candidate']['address']
|
||||
$typedErrors['candidate']['birthplace'] ||
|
||||
$typedErrors['candidate']['personalIdNumber'] ||
|
||||
$typedErrors['candidate']['testLanguage'] ||
|
||||
!personalIdBirthdateMatch
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
if (
|
||||
$typedErrors['parents'][0]['name'] ||
|
||||
$typedErrors['parents'][0]['surname'] ||
|
||||
|
|
@ -347,7 +338,7 @@
|
|||
return true;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
if (
|
||||
$typedErrors['parents'][1]['name'] ||
|
||||
$typedErrors['parents'][1]['surname'] ||
|
||||
|
|
@ -357,17 +348,17 @@
|
|||
return true;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
case 7:
|
||||
if (
|
||||
$typedErrors['candidate']['citizenship'] ||
|
||||
$typedErrors['candidate']['personalIdNumber'] ||
|
||||
$typedErrors['candidate']['schoolName'] ||
|
||||
$typedErrors['candidate']['healthInsurance']
|
||||
$typedErrors['candidate']['firstSchool']['name'] ||
|
||||
$typedErrors['candidate']['firstSchool']['field'] ||
|
||||
$typedErrors['candidate']['secondSchool']['name'] ||
|
||||
$typedErrors['candidate']['secondSchool']['field']
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
case 8:
|
||||
if ($typedErrors['candidate']['grades'].length > 0) return true;
|
||||
break;
|
||||
default:
|
||||
|
|
@ -380,6 +371,11 @@
|
|||
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) {
|
||||
details.candidate.birthdate = details.candidate.birthdate.split('-').reverse().join('.');
|
||||
|
||||
|
|
@ -387,10 +383,13 @@
|
|||
details.parents.map(
|
||||
(x) => (x.telephone = x.telephone != '' ? formatTelephone(x.telephone) : '')
|
||||
);
|
||||
|
||||
form.set({
|
||||
gdpr: true,
|
||||
linkOk: true,
|
||||
linkError: false,
|
||||
personalIdOk: true,
|
||||
personalIdErr: false,
|
||||
candidate: {
|
||||
...details.candidate,
|
||||
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();
|
||||
}
|
||||
</script>
|
||||
|
||||
<SplitLayout>
|
||||
<SvelteToast />
|
||||
<div class="form relative">
|
||||
<div class="bottom-3/12 absolute flex w-full flex-col md:h-auto">
|
||||
<!-- TODO: Find different way how to display SchoolBadge -->
|
||||
{#if pageIndex !== 0 && pageIndex !== 7}
|
||||
<div class="<md:h-24 <md:w-24 mb-4 h-32 w-32 self-center">
|
||||
<SchoolBadge />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="form relative bg-center">
|
||||
<div class="bottom-5/24 absolute flex w-full flex-col md:h-auto">
|
||||
<div class="<md:hidden self-center">
|
||||
<SchoolBadge />
|
||||
</div>
|
||||
<form on:submit={handleSubmit} id="triggerForm" class="invisible hidden" />
|
||||
{#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}>
|
||||
<h1 class="title mt-8">{$LL.candidate.register.first.title()}</h1>
|
||||
<p class="description mt-8 block text-center">
|
||||
|
|
@ -445,7 +456,7 @@
|
|||
/>
|
||||
</div>
|
||||
</form>
|
||||
{:else if pageIndex === 1}
|
||||
{:else if pageIndex === 2}
|
||||
<form on:submit={handleSubmit}>
|
||||
<h1 class="title mt-8">{pageTexts[0]}</h1>
|
||||
<p class="description mt-8 block text-center">
|
||||
|
|
@ -456,89 +467,104 @@
|
|||
<GdprCheckBox bind:value={$form.gdpr} error={$typedErrors['gdpr']} />
|
||||
</div>
|
||||
</form>
|
||||
{:else if pageIndex === 2}
|
||||
{:else if pageIndex === 3}
|
||||
<form on:submit={handleSubmit}>
|
||||
<h1 class="title mt-8">{pageTexts[1]}</h1>
|
||||
<p class="description mt-8 block text-center">
|
||||
{$LL.candidate.register.third.description()}
|
||||
</p>
|
||||
<div class="flex flex-col">
|
||||
<span class="field">
|
||||
<NameField
|
||||
error={$typedErrors['candidate']['name'] || $typedErrors['candidate']['surname']}
|
||||
bind:valueName={$form.candidate.name}
|
||||
bind:valueSurname={$form.candidate.surname}
|
||||
placeholder={$LL.input.nameSurname()}
|
||||
/>
|
||||
</span>
|
||||
<span class="field">
|
||||
<EmailField
|
||||
error={$typedErrors['candidate']['email']}
|
||||
bind:value={$form.candidate.email}
|
||||
placeholder={$LL.input.email()}
|
||||
/>
|
||||
</span>
|
||||
<span class="field">
|
||||
<TelephoneField
|
||||
error={$typedErrors['candidate']['telephone']}
|
||||
bind:value={$form.candidate.telephone}
|
||||
placeholder={$LL.input.telephone()}
|
||||
/>
|
||||
</span>
|
||||
<div class="w-full">
|
||||
<div class="flex flex-col">
|
||||
<div class="field flex">
|
||||
<span class="w-[50%]">
|
||||
<NameField
|
||||
error={$typedErrors['candidate']['name'] ||
|
||||
$typedErrors['candidate']['surname']}
|
||||
bind:valueName={$form.candidate.name}
|
||||
bind:valueSurname={$form.candidate.surname}
|
||||
placeholder={$LL.input.nameSurname()}
|
||||
/>
|
||||
</span>
|
||||
<span class="ml-2 w-[50%]">
|
||||
<TextField
|
||||
error={$typedErrors['candidate']['birthSurname']}
|
||||
bind:value={$form.candidate.birthSurname}
|
||||
placeholder={$LL.input.birthSurname()}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="field flex">
|
||||
<span class="w-[50%]">
|
||||
<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>
|
||||
</form>
|
||||
{:else if pageIndex === 3}
|
||||
{:else if pageIndex === 4}
|
||||
<h1 class="title mt-8">{pageTexts[2]}</h1>
|
||||
<p class="description mt-8 block text-center">
|
||||
{$LL.candidate.register.fourth.description()}
|
||||
</p>
|
||||
<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)."
|
||||
<div class="field flex w-full">
|
||||
<span class="w-[50%]">
|
||||
<SelectField
|
||||
error={$typedErrors['candidate']['citizenship']}
|
||||
bind:value={$form.candidate.citizenship}
|
||||
placeholder={$LL.input.citizenship()}
|
||||
options={['Česká republika', 'Slovenská republika', 'Ukrajina', 'Jiné']}
|
||||
/>
|
||||
</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 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">
|
||||
<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">
|
||||
<TextField
|
||||
error={$typedErrors['candidate']['birthdate']}
|
||||
|
|
@ -547,6 +573,30 @@
|
|||
placeholder={$LL.input.birthDate()}
|
||||
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">
|
||||
<SelectField
|
||||
error={$typedErrors['candidate']['sex']}
|
||||
|
|
@ -556,7 +606,35 @@
|
|||
/>
|
||||
</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>
|
||||
<p class="description mt-8 block text-center">
|
||||
{$LL.candidate.register.fifth.description()}
|
||||
|
|
@ -585,7 +663,7 @@
|
|||
/>
|
||||
</span>
|
||||
</div>
|
||||
{:else if pageIndex === 5}
|
||||
{:else if pageIndex === 6}
|
||||
<h1 class="title mt-8">{pageTexts[4]}</h1>
|
||||
<p class="description mt-8 block text-center">
|
||||
{$LL.candidate.register.sixth.description()}
|
||||
|
|
@ -614,75 +692,28 @@
|
|||
/>
|
||||
</span>
|
||||
</div>
|
||||
{:else if pageIndex === 6}
|
||||
{:else if pageIndex === 7}
|
||||
<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()}
|
||||
</p>
|
||||
<div class="flex w-full flex-col">
|
||||
<div class="field flex w-full">
|
||||
<span class="w-[50%]">
|
||||
<SelectField
|
||||
error={$typedErrors['candidate']['citizenship']}
|
||||
bind:value={$form.candidate.citizenship}
|
||||
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()}
|
||||
<div class="flex h-full flex-col justify-between">
|
||||
<span class="field">
|
||||
<SchoolSelect
|
||||
error={$typedErrors['candidate']['firstSchool']['name'] ||
|
||||
$typedErrors['candidate']['firstSchool']['field']}
|
||||
bind:selectedSchool={$form.candidate.firstSchool}
|
||||
/>
|
||||
{:else}
|
||||
<TextField
|
||||
error={$typedErrors['candidate']['personalIdNumber']}
|
||||
bind:value={$form.candidate.personalIdNumber}
|
||||
placeholder={$LL.input.personalIdentificationNumber()}
|
||||
</span>
|
||||
<span class="field mt-10">
|
||||
<SchoolSelect
|
||||
error={$typedErrors['candidate']['secondSchool']['name'] ||
|
||||
$typedErrors['candidate']['secondSchool']['field']}
|
||||
bind:selectedSchool={$form.candidate.secondSchool}
|
||||
/>
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
{:else if pageIndex === 7}
|
||||
{:else if pageIndex === 8}
|
||||
<h1 class="title mt-8">{pageTexts[6]}</h1>
|
||||
<p class="description mt-8 block text-center">
|
||||
{$LL.candidate.register.eighth.description()}
|
||||
|
|
@ -693,7 +724,7 @@
|
|||
/>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="bottom-1/12 absolute w-full">
|
||||
<div class="bottom-1/24 absolute w-full">
|
||||
<div class="field">
|
||||
<Submit
|
||||
on:click={async (e) => {
|
||||
|
|
@ -711,7 +742,7 @@
|
|||
/>
|
||||
</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}
|
||||
<button
|
||||
class:dotActive={i === pageIndex}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,11 @@
|
|||
{$LL.candidate.auth.login.description()}
|
||||
</p>
|
||||
<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 class="mt-8 w-4/5 lg:w-3/5">
|
||||
<Submit on:click={redirectToCode} value={$LL.input.submit()} />
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import type { LayoutLoad } from './$types';
|
|||
export const load: LayoutLoad = async ({ url }) => {
|
||||
await loadAllLocalesAsync();
|
||||
setLocale('cs');
|
||||
|
||||
|
||||
return {
|
||||
url: url.pathname
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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.'
|
||||
},
|
||||
seventh: {
|
||||
title: 'Dokončení registrace',
|
||||
title: 'Přihlášky na školy',
|
||||
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: {
|
||||
title: 'Poslední krok',
|
||||
|
|
@ -100,7 +100,7 @@ const cs: BaseTranslation = {
|
|||
},
|
||||
missing: {
|
||||
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'
|
||||
}
|
||||
},
|
||||
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: {
|
||||
title: 'Souhlasím 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: {
|
||||
optional: 'nepovinné',
|
||||
nameSurname: 'Jméno a příjmení',
|
||||
birthSurname: 'Rodné příjmení',
|
||||
email: 'E-mail',
|
||||
telephone: 'Telefon',
|
||||
address: 'Ulice a č. p.',
|
||||
|
|
@ -147,6 +154,8 @@ const cs: BaseTranslation = {
|
|||
password: 'Heslo',
|
||||
submit: 'Odeslat',
|
||||
continue: 'Pokračovat',
|
||||
fieldOfStudy: 'Obor',
|
||||
selectedSchool: 'Vybraná škola',
|
||||
parent: {
|
||||
nameSurname: 'Jméno a příjmení zákonného zástupce',
|
||||
email: 'E-mail zákonného zástupce',
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import type { FormattersInitializer } from 'typesafe-i18n'
|
||||
import type { Locales, Formatters } from './i18n-types.js'
|
||||
import type { FormattersInitializer } from 'typesafe-i18n';
|
||||
import type { Locales, Formatters } from './i18n-types.js';
|
||||
|
||||
export const initFormatters: FormattersInitializer<Locales, Formatters> = (locale: Locales) => {
|
||||
|
||||
const formatters: Formatters = {
|
||||
// add your formatter functions here
|
||||
}
|
||||
};
|
||||
|
||||
return formatters
|
||||
}
|
||||
return formatters;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -129,11 +129,11 @@ type RootTranslation = {
|
|||
}
|
||||
seventh: {
|
||||
/**
|
||||
* Dokončení registrace
|
||||
* Přihlášky na školy
|
||||
*/
|
||||
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
|
||||
}
|
||||
|
|
@ -272,6 +272,26 @@ type RootTranslation = {
|
|||
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: {
|
||||
/**
|
||||
* Souhlasím se zpracováním osobních údajů
|
||||
|
|
@ -297,6 +317,10 @@ type RootTranslation = {
|
|||
* Jméno a příjmení
|
||||
*/
|
||||
nameSurname: string
|
||||
/**
|
||||
* Rodné příjmení
|
||||
*/
|
||||
birthSurname: string
|
||||
/**
|
||||
* E-mail
|
||||
*/
|
||||
|
|
@ -373,6 +397,14 @@ type RootTranslation = {
|
|||
* Pokračovat
|
||||
*/
|
||||
'continue': string
|
||||
/**
|
||||
* Obor
|
||||
*/
|
||||
fieldOfStudy: string
|
||||
/**
|
||||
* Vybraná škola
|
||||
*/
|
||||
selectedSchool: string
|
||||
parent: {
|
||||
/**
|
||||
* Jméno a příjmení zákonného zástupce
|
||||
|
|
@ -507,11 +539,11 @@ export type TranslationFunctions = {
|
|||
}
|
||||
seventh: {
|
||||
/**
|
||||
* Dokončení registrace
|
||||
* Přihlášky na školy
|
||||
*/
|
||||
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
|
||||
}
|
||||
|
|
@ -645,6 +677,24 @@ export type TranslationFunctions = {
|
|||
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: {
|
||||
/**
|
||||
* Souhlasím se zpracováním osobních údajů
|
||||
|
|
@ -670,6 +720,10 @@ export type TranslationFunctions = {
|
|||
* Jméno a příjmení
|
||||
*/
|
||||
nameSurname: () => LocalizedString
|
||||
/**
|
||||
* Rodné příjmení
|
||||
*/
|
||||
birthSurname: () => LocalizedString
|
||||
/**
|
||||
* E-mail
|
||||
*/
|
||||
|
|
@ -746,6 +800,14 @@ export type TranslationFunctions = {
|
|||
* Pokračovat
|
||||
*/
|
||||
'continue': () => LocalizedString
|
||||
/**
|
||||
* Obor
|
||||
*/
|
||||
fieldOfStudy: () => LocalizedString
|
||||
/**
|
||||
* Vybraná škola
|
||||
*/
|
||||
selectedSchool: () => LocalizedString
|
||||
parent: {
|
||||
/**
|
||||
* Jméno a příjmení zákonného zástupce
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import adapter from '@sveltejs/adapter-node';
|
||||
import preprocess from 'svelte-preprocess';
|
||||
import path from "path";
|
||||
import path from 'path';
|
||||
import { windi } from 'svelte-windicss-preprocess';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
|
|
@ -11,9 +11,8 @@ const config = {
|
|||
kit: {
|
||||
adapter: adapter({ out: 'build' }),
|
||||
alias: {
|
||||
$i18n: path.resolve('./src/translations'),
|
||||
$i18n: path.resolve('./src/translations')
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,6 @@
|
|||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"strict": true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue