mirror of
https://github.com/danbulant/Portfolio
synced 2026-05-20 04:48:32 +00:00
Merge pull request #193 from EETagent/frontend_prod_form
Frontend prod form
This commit is contained in:
commit
dce07ce2cb
22 changed files with 3115 additions and 131 deletions
|
|
@ -54,7 +54,6 @@ export const apiFetchSubmissionProgress = async (fetchSsr?: Fetch): Promise<Subm
|
|||
export const apiWhoami = async (fetchSsr?: Fetch): Promise<BaseCandidate> => {
|
||||
const apiFetch = fetchSsr || fetch;
|
||||
try {
|
||||
console.log(API_URL + '/candidate/whoami');
|
||||
const res = await apiFetch(API_URL + '/candidate/whoami', {
|
||||
method: 'GET',
|
||||
credentials: 'include'
|
||||
|
|
@ -100,7 +99,6 @@ export const apiFillDetails = async (data: CandidateData): Promise<CandidateData
|
|||
});
|
||||
}
|
||||
|
||||
console.log(data);
|
||||
try {
|
||||
const res = await axios.post(API_URL + '/candidate/details', data, { withCredentials: true });
|
||||
return res.data;
|
||||
|
|
|
|||
195
frontend/src/lib/assets/list/countries.json
Normal file
195
frontend/src/lib/assets/list/countries.json
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
[
|
||||
"Česká republika",
|
||||
"Slovenská republika",
|
||||
"Ukrajina",
|
||||
"Afghánistán",
|
||||
"Albánie",
|
||||
"Alžírsko",
|
||||
"Andorra",
|
||||
"Angola",
|
||||
"a Antigua Barbuda",
|
||||
"Argentina",
|
||||
"Arménie",
|
||||
"Austrálie",
|
||||
"Ázerbájdžán",
|
||||
"Bahamy",
|
||||
"Bahrajn",
|
||||
"Bangladéš",
|
||||
"Barbados",
|
||||
"Belgie",
|
||||
"Belize",
|
||||
"Bělorusko",
|
||||
"Benin",
|
||||
"Bhútán",
|
||||
"Bolívie",
|
||||
"Hercegovina a Bosna",
|
||||
"Botswana",
|
||||
"Brazílie",
|
||||
"Brunej",
|
||||
"Bulharsko",
|
||||
"Faso Burkina",
|
||||
"Burundi",
|
||||
"Čad",
|
||||
"Černá Hora",
|
||||
"Čína",
|
||||
"Dánsko",
|
||||
"Dominika",
|
||||
"republika Dominikánská",
|
||||
"Džibutsko",
|
||||
"Egypt",
|
||||
"Ekvádor",
|
||||
"Eritrea",
|
||||
"Estonsko",
|
||||
"Etiopie",
|
||||
"Fidži",
|
||||
"Filipíny",
|
||||
"Finsko",
|
||||
"Francie",
|
||||
"Gabon",
|
||||
"Gambie",
|
||||
"Ghana",
|
||||
"Grenada",
|
||||
"Gruzie",
|
||||
"Guatemala",
|
||||
"Guinea",
|
||||
"Guinea-Bissau",
|
||||
"Guyana",
|
||||
"Haiti",
|
||||
"Honduras",
|
||||
"Chile",
|
||||
"Chorvatsko",
|
||||
"Indie",
|
||||
"Indonésie",
|
||||
"Irák",
|
||||
"Írán",
|
||||
"Irsko",
|
||||
"Island",
|
||||
"Itálie",
|
||||
"Izrael",
|
||||
"Jamajka",
|
||||
"Japonsko",
|
||||
"Jemen",
|
||||
"Jižní Afrika",
|
||||
"Jižní Korea",
|
||||
"Jižní Súdán",
|
||||
"Jordánsko",
|
||||
"Kambodža",
|
||||
"Kamerun",
|
||||
"Kanada",
|
||||
"Kapverdy",
|
||||
"Katar",
|
||||
"Kazachstán",
|
||||
"Keňa",
|
||||
"Kiribati",
|
||||
"Kolumbie",
|
||||
"Komory",
|
||||
"Konžská republika",
|
||||
"Konžská demokratická republika",
|
||||
"Kostarika",
|
||||
"Kuba",
|
||||
"Kuvajt",
|
||||
"Kypr",
|
||||
"Kyrgyzstán",
|
||||
"Laos",
|
||||
"Lesotho",
|
||||
"Libanon",
|
||||
"Libérie",
|
||||
"Libye",
|
||||
"Lichtenštejnsko",
|
||||
"Litva",
|
||||
"Lotyšsko",
|
||||
"Lucembursko",
|
||||
"Madagaskar",
|
||||
"Maďarsko",
|
||||
"Malajsie",
|
||||
"Malawi",
|
||||
"Maledivy",
|
||||
"Mali",
|
||||
"Malta",
|
||||
"Maroko",
|
||||
"Marshallovy ostrovy",
|
||||
"Mauricius",
|
||||
"Mauritánie",
|
||||
"Mexiko",
|
||||
"Mikronésie",
|
||||
"Moldavsko",
|
||||
"Monako",
|
||||
"Mongolsko",
|
||||
"Mosambik",
|
||||
"Myanmar (Barma)",
|
||||
"Namibie",
|
||||
"Nauru",
|
||||
"Německo",
|
||||
"Nepál",
|
||||
"Niger",
|
||||
"Nigérie",
|
||||
"Nikaragua",
|
||||
"Nizozemsko",
|
||||
"Norsko",
|
||||
"Zéland Nový",
|
||||
"Omán",
|
||||
"Pákistán",
|
||||
"Palau",
|
||||
"Panama",
|
||||
"Papua Nová Guinea",
|
||||
"Paraguay",
|
||||
"Peru",
|
||||
"Pobřeží slonoviny",
|
||||
"Polsko",
|
||||
"Portugalsko",
|
||||
"Rakousko",
|
||||
"Rovníková Guinea",
|
||||
"Rumunsko",
|
||||
"Rusko",
|
||||
"Rwanda",
|
||||
"Řecko",
|
||||
"Salvador",
|
||||
"Samoa",
|
||||
"Marino San",
|
||||
"Saúdská Arábie",
|
||||
"Senegal",
|
||||
"Korea Severní",
|
||||
"Makedonie Severní",
|
||||
"Seychely",
|
||||
"Sierra Leone",
|
||||
"Singapur",
|
||||
"Slovinsko",
|
||||
"Somálsko",
|
||||
"arabské emiráty Spojené",
|
||||
"království Spojené",
|
||||
"americké státy Spojené",
|
||||
"Srbsko",
|
||||
"republika Středoafrická",
|
||||
"Súdán",
|
||||
"Surinam",
|
||||
"Lucie Svatá",
|
||||
"a Nevis Svatý Kryštof",
|
||||
"Princův Svatý a ostrov Tomáš",
|
||||
"Vincenc a Grenadiny Svatý",
|
||||
"Svazijsko",
|
||||
"Sýrie",
|
||||
"Šalomounovy ostrovy",
|
||||
"Španělsko",
|
||||
"Šrí",
|
||||
"Švédsko",
|
||||
"Švýcarsko",
|
||||
"Tádžikistán",
|
||||
"Tanzanie",
|
||||
"Thajsko",
|
||||
"Togo",
|
||||
"Tonga",
|
||||
"a Trinidad Tobago",
|
||||
"Tunisko",
|
||||
"Turecko",
|
||||
"Turkmenistán",
|
||||
"Tuvalu",
|
||||
"Uganda",
|
||||
"Uruguay",
|
||||
"Uzbekistán",
|
||||
"Vanuatu",
|
||||
"Venezuela",
|
||||
"Vietnam",
|
||||
"Timor Východní",
|
||||
"Zambie",
|
||||
"Zimbabwe"
|
||||
]
|
||||
1330
frontend/src/lib/assets/list/high_schools.json
Normal file
1330
frontend/src/lib/assets/list/high_schools.json
Normal file
File diff suppressed because it is too large
Load diff
1291
frontend/src/lib/assets/list/school.json
Normal file
1291
frontend/src/lib/assets/list/school.json
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
|
|
@ -1,6 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { apiGetCandidatePortfolio, apiResetCandidatePassword } from '$lib/@api/admin';
|
||||
import type { CandidateData } from '$lib/stores/candidate';
|
||||
import { SvelteToast, toast } from '@zerodevx/svelte-toast';
|
||||
|
||||
export let id: number;
|
||||
export let candidateData: CandidateData;
|
||||
|
|
@ -13,7 +14,13 @@
|
|||
const res = await apiResetCandidatePassword(id);
|
||||
alert('Nove heslo: ' + res.password);
|
||||
} catch {
|
||||
console.log('error');
|
||||
toast.push('Rodné číslo neodpovídá oficiální specifikaci či datumu narození', {
|
||||
theme: {
|
||||
'--toastColor': 'mintcream',
|
||||
'--toastBackground': '#b91c1c',
|
||||
'--toastBarBackground': '#7f1d1d'
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -27,11 +34,18 @@
|
|||
document.body.appendChild(link);
|
||||
link.click();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
toast.push('Rodné číslo neodpovídá oficiální specifikaci či datumu narození', {
|
||||
theme: {
|
||||
'--toastColor': 'mintcream',
|
||||
'--toastBackground': '#b91c1c',
|
||||
'--toastBarBackground': '#7f1d1d'
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<SvelteToast />
|
||||
<div class="flex h-screen w-full items-center justify-center">
|
||||
<div class="mr-8 max-w-sm">
|
||||
<div class="rounded-lg bg-white p-10 shadow-xl">
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
export let enterAllowed: boolean;
|
||||
export let value: string;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Enter') {
|
||||
if (enterAllowed && e.key === 'Enter') {
|
||||
dispatch('click');
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
import { baseCandidateData, candidateData } from '$lib/stores/candidate';
|
||||
import tippy, { sticky } from 'tippy.js';
|
||||
import { goto } from '$app/navigation';
|
||||
import { pushErrorText } from '$lib/utils/toast';
|
||||
|
||||
export let title: string;
|
||||
export let status: Status;
|
||||
|
|
@ -62,7 +63,7 @@
|
|||
document.body.appendChild(link);
|
||||
link.click();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
pushErrorText("Chyba při stahování portfolia");
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -31,12 +31,10 @@
|
|||
|
||||
$: if ($submissionProgress) {
|
||||
status = getStatus();
|
||||
// console.log('type' + fileType + ' status: ' + status);
|
||||
fileDropped = status === 'uploaded' || status === 'submitted';
|
||||
}
|
||||
|
||||
const getStatus = (): Status => {
|
||||
console.log($submissionProgress);
|
||||
switch ($submissionProgress.status) {
|
||||
case UploadStatus.None:
|
||||
return 'missing';
|
||||
|
|
@ -71,7 +69,6 @@
|
|||
};
|
||||
|
||||
const onFileDrop = (dropped: Files) => {
|
||||
console.log(dropped);
|
||||
if (dropped.accepted.length > 0) {
|
||||
fileDropped = true;
|
||||
const file = dropped.accepted[0];
|
||||
|
|
@ -79,7 +76,6 @@
|
|||
dispatch('filedrop', {
|
||||
file: file,
|
||||
callback: (progressEvent: AxiosProgressEvent) => {
|
||||
console.log(progressEvent.bytes);
|
||||
progress = progressEvent.progress!;
|
||||
bytesTotal = progressEvent.total ?? 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,15 @@
|
|||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let grade: Grade;
|
||||
const SEMESTERS: Semester[] = ['1/8', '2/8', '1/9', '2/9'];
|
||||
|
||||
const deleteRow = () => {
|
||||
dispatch('delete');
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex">
|
||||
|
|
@ -25,6 +32,23 @@
|
|||
<option value="5">5</option>
|
||||
</select>
|
||||
{/each}
|
||||
<!-- delete button with 'x' icon -->
|
||||
<button on:click={deleteRow} class="ml-0.5 h-6 w-6">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6 stroke-red-700"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
|
|
|
|||
|
|
@ -78,8 +78,8 @@
|
|||
};
|
||||
</script>
|
||||
|
||||
<div class="mx-auto mt-8 flex text-gray-400 lg:w-4/5">
|
||||
<span class="w-1/2 text-center">Známky</span>
|
||||
<div class="mx-auto mt-8 flex pr-6 text-gray-400 lg:w-4/5">
|
||||
<span class="w-1/2 text-center">Předmět</span>
|
||||
<span class="ml-0.5 w-1/6 text-center">1/8</span>
|
||||
<span class="ml-0.5 w-1/6 text-center">2/8</span>
|
||||
<span class="ml-0.5 w-1/6 text-center">1/9</span>
|
||||
|
|
@ -92,6 +92,10 @@
|
|||
on:keyup={convertGradeToGradeBackend}
|
||||
on:change={convertGradeToGradeBackend}
|
||||
bind:grade={gradesLocal[i]}
|
||||
on:delete={() => {
|
||||
grades = grades.filter((grade) => grade.subject !== gradesLocal[i].subject);
|
||||
gradesLocal = gradesLocal.filter((_, index) => index !== i);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,33 @@
|
|||
<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';
|
||||
import type { School as SchoolType, SchoolJson } from '$lib/stores/candidate';
|
||||
import SelectField from '../SelectField.svelte';
|
||||
import TextField from '$lib/components/textfield/TextField.svelte';
|
||||
|
||||
const schoolList: Array<string> = schoollistString.split(';');
|
||||
export let schoolNames: Array<string>;
|
||||
export let schoolList: Array<SchoolJson>;
|
||||
|
||||
let fields: Array<string> = [];
|
||||
let filteredSchools: Array<string> = [];
|
||||
|
||||
const filterSchools = () => {
|
||||
let storageArr: Array<string> = [];
|
||||
if (schoolNameInputValue) {
|
||||
schoolList.forEach((school) => {
|
||||
if (school.toLowerCase().startsWith(schoolNameInputValue.toLowerCase())) {
|
||||
schoolNames.forEach((school) => {
|
||||
if (
|
||||
school
|
||||
.toLowerCase()
|
||||
.normalize('NFD')
|
||||
.replace(/[\u0300-\u036f]/g, '')
|
||||
.includes(
|
||||
schoolNameInputValue
|
||||
.toLowerCase()
|
||||
.normalize('NFD')
|
||||
.replace(/[\u0300-\u036f]/g, '')
|
||||
)
|
||||
) {
|
||||
storageArr = [...storageArr, makeMatchBold(school)];
|
||||
}
|
||||
});
|
||||
|
|
@ -26,17 +40,30 @@
|
|||
|
||||
let schoolNameInputValue = '';
|
||||
let schoolFieldInputValue = '';
|
||||
let fieldFocusInputValue = '';
|
||||
|
||||
$: if (!schoolNameInputValue) {
|
||||
filteredSchools = [];
|
||||
hiLiteIndex = -1;
|
||||
}
|
||||
|
||||
const setFields = (schoolName: string) => {
|
||||
let school = schoolList.find((school) => school.n === schoolName);
|
||||
if (school) {
|
||||
fields = school.f;
|
||||
} else {
|
||||
fields = [];
|
||||
}
|
||||
};
|
||||
|
||||
$: setFields(schoolNameInputValue);
|
||||
|
||||
const setInputVal = (schoolName: string) => {
|
||||
schoolNameInputValue = removeBold(schoolName);
|
||||
filteredSchools = [];
|
||||
hiLiteIndex = -1;
|
||||
searchInput.focus();
|
||||
// setFields(schoolNameInputValue);
|
||||
};
|
||||
|
||||
const makeMatchBold = (str: string) => {
|
||||
|
|
@ -76,33 +103,67 @@
|
|||
export let selectedSchool: SchoolType;
|
||||
export let error: string = '';
|
||||
|
||||
schoolFieldInputValue = selectedSchool.field;
|
||||
if (selectedSchool.field.split(';').length > 1) {
|
||||
console.log(selectedSchool.field);
|
||||
schoolFieldInputValue = selectedSchool.field.split(';')[0];
|
||||
fieldFocusInputValue = selectedSchool.field.split(';')[1];
|
||||
} else {
|
||||
schoolFieldInputValue = selectedSchool.field;
|
||||
}
|
||||
schoolNameInputValue = selectedSchool.name;
|
||||
|
||||
$: selectedSchool.field = schoolFieldInputValue;
|
||||
$: selectedSchool.field = schoolFieldInputValue + (fieldFocusInputValue ? `;${fieldFocusInputValue}` : '');
|
||||
$: selectedSchool.name = schoolNameInputValue;
|
||||
|
||||
let isSSPS = false;
|
||||
$: isSSPS = schoolNameInputValue === 'Smíchovská střední průmyslová škola a gymnázium';
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={navigateList} />
|
||||
|
||||
<div class="autocomplete">
|
||||
<div class="flex">
|
||||
<div class="flex flex-col">
|
||||
<input
|
||||
class:error
|
||||
class="flex-1"
|
||||
class=""
|
||||
type="text"
|
||||
bind:this={searchInput}
|
||||
bind:value={schoolNameInputValue}
|
||||
on:input={filterSchools}
|
||||
placeholder={$LL.input.schoolName()}
|
||||
/>
|
||||
<input
|
||||
<div class="flex mt-2">
|
||||
<span class="w-1/2" class:w-full={isSSPS}>
|
||||
<SelectField
|
||||
on:focus={() => setFields(schoolNameInputValue)}
|
||||
bind:value={schoolFieldInputValue}
|
||||
options={fields}
|
||||
placeholder={$LL.input.fieldOfStudy()}
|
||||
/>
|
||||
</span>
|
||||
<span class="w-1/2 ml-2" class:hidden={isSSPS}>
|
||||
<TextField
|
||||
bind:value={fieldFocusInputValue}
|
||||
placeholder="Zaměření (jen některé školy)"
|
||||
helperText="Např. Kybernetická bezpečnost, protože obor nemá svůj vlastní kód"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<!-- <select
|
||||
on:focus={() => setFields(schoolNameInputValue)}
|
||||
>
|
||||
{#each fields as field}
|
||||
<option>{field}</option>
|
||||
{/each}
|
||||
</select> -->
|
||||
<!-- <input
|
||||
on:focus={() => setFields(schoolNameInputValue)}
|
||||
class:error
|
||||
class="ml-2 w-2/5"
|
||||
class="mt-4"
|
||||
type="text"
|
||||
bind:value={schoolFieldInputValue}
|
||||
placeholder={$LL.input.fieldOfStudy()}
|
||||
/>
|
||||
/> -->
|
||||
</div>
|
||||
{#if filteredSchools.length > 0}
|
||||
<ul bind:this={optionsList} class="schoolAutocompleteList">
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@
|
|||
if (number !== null && number !== undefined) {
|
||||
country = number.country!;
|
||||
}
|
||||
// console.log(country);
|
||||
}
|
||||
|
||||
// Validity
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import type { GradeBackend } from '$lib/components/grades/GradesTable.svelte';
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export interface SchoolJson {
|
||||
n: string;
|
||||
f: string[];
|
||||
}
|
||||
export interface School {
|
||||
name: string;
|
||||
field: string;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export const isPersonalIdNumberValid = (personalIdNumber: string): boolean => {
|
|||
}
|
||||
};
|
||||
|
||||
export const isPersonalIdNumberWithBirthdateValid = (
|
||||
export const isPersonalIdMatchingBirthdate = (
|
||||
personalIdNumber: string,
|
||||
birthdate: string
|
||||
): boolean => {
|
||||
|
|
@ -48,25 +48,26 @@ export const isPersonalIdNumberWithBirthdateValid = (
|
|||
}
|
||||
};
|
||||
|
||||
export const deriveBirthdateFromPersonalId = (
|
||||
export const parseBirthdateSexFromPersonalId = (
|
||||
personalIdNumber: string
|
||||
): [birthdate: string, sex: 'MUŽ' | 'ŽENA'] => {
|
||||
const year = Number(personalIdNumber.slice(0, 2));
|
||||
): [birthdate: string, sex: 'Muž' | 'Žena'] => {
|
||||
const yearPadded = Number(personalIdNumber.slice(0, 2));
|
||||
const year = yearPadded < 24 ? yearPadded + 2000 : yearPadded + 1900;
|
||||
const idMonth = Number(personalIdNumber.slice(2, 4));
|
||||
let month;
|
||||
let sex: 'MUŽ' | 'ŽENA';
|
||||
let sex: 'Muž' | 'Žena';
|
||||
if (idMonth > 12 && idMonth <= 32) {
|
||||
month = idMonth - 20;
|
||||
sex = 'MUŽ';
|
||||
sex = 'Muž';
|
||||
} else if (idMonth > 50 && idMonth <= 52) {
|
||||
month = idMonth - 50;
|
||||
sex = 'ŽENA';
|
||||
sex = 'Žena';
|
||||
} else if (idMonth > 70 && idMonth <= 82) {
|
||||
month = idMonth - 70;
|
||||
sex = 'ŽENA';
|
||||
sex = 'Žena';
|
||||
} else {
|
||||
month = idMonth;
|
||||
sex = 'MUŽ';
|
||||
sex = 'Muž';
|
||||
}
|
||||
const day = Number(personalIdNumber.slice(4, 6));
|
||||
|
||||
|
|
|
|||
21
frontend/src/lib/utils/toast.ts
Normal file
21
frontend/src/lib/utils/toast.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { toast } from "@zerodevx/svelte-toast";
|
||||
|
||||
export const pushErrorText = (text: string) => {
|
||||
toast.push(text, {
|
||||
theme: {
|
||||
'--toastColor': 'mintcream',
|
||||
'--toastBackground': '#b91c1c',
|
||||
'--toastBarBackground': '#7f1d1d'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const pushSuccessText = (text: string) => {
|
||||
toast.push(text, {
|
||||
theme: {
|
||||
'--toastColor': 'mintcream',
|
||||
'--toastBackground': '#047857',
|
||||
'--toastBarBackground': '#064e3b'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -11,6 +11,8 @@
|
|||
import bacgkround from '$lib/assets/background.jpg';
|
||||
import Logout from '$lib/components/icons/Logout.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { pushErrorText } from '$lib/utils/toast';
|
||||
import { SvelteToast } from '@zerodevx/svelte-toast';
|
||||
|
||||
export let data: PageServerData;
|
||||
|
||||
|
|
@ -20,7 +22,7 @@
|
|||
try {
|
||||
candidates = await apiListCandidates(undefined, activeFilter.filter);
|
||||
} catch {
|
||||
console.log('error');
|
||||
pushErrorText('Nepodařilo se načíst uchazeče');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -93,7 +95,7 @@
|
|||
link.setAttribute('download', 'UCHAZECI' + '.csv');
|
||||
link.click();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
pushErrorText('Nepodařilo se stáhnout CSV');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -110,6 +112,7 @@
|
|||
{/if}
|
||||
|
||||
<div>
|
||||
<SvelteToast />
|
||||
<header class="absolute h-14 w-full">
|
||||
<img class="h-12 w-full object-cover blur-sm filter" src={bacgkround} alt="Background" />
|
||||
</header>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
import { goto } from '$app/navigation';
|
||||
import Submit from '$lib/components/button/Submit.svelte';
|
||||
import PasswordField from '$lib/components/textfield/PasswordField.svelte';
|
||||
import { SvelteToast } from '@zerodevx/svelte-toast';
|
||||
import { pushErrorText } from '$lib/utils/toast';
|
||||
|
||||
let adminIdValue = '';
|
||||
let adminPasswordValue = '';
|
||||
|
|
@ -19,11 +21,12 @@
|
|||
await apiLogin({ adminId: Number(adminIdValue), password: adminPasswordValue });
|
||||
goto('/admin/dashboard');
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
pushErrorText('Neplatné heslo nebo ID!');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<SvelteToast />
|
||||
<SplitLayout backgroundImage={background} backgroundPosition="30%">
|
||||
<div class="form">
|
||||
<div
|
||||
|
|
@ -44,7 +47,7 @@
|
|||
</span>
|
||||
</div>
|
||||
<div class="mt-8 w-4/5 lg:w-3/5">
|
||||
<Submit value={$LL.input.submit()} on:click={login} />
|
||||
<Submit enterAllowed={true} value={$LL.input.submit()} on:click={login} />
|
||||
</div>
|
||||
</div>
|
||||
</SplitLayout>
|
||||
|
|
|
|||
|
|
@ -18,15 +18,26 @@
|
|||
import parsePhoneNumber from 'libphonenumber-js';
|
||||
import { createForm } from 'svelte-forms-lib';
|
||||
import * as yup from 'yup';
|
||||
import type { CandidateData } from '$lib/stores/candidate';
|
||||
import type { CandidateData, SchoolJson } 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';
|
||||
import {
|
||||
parseBirthdateSexFromPersonalId,
|
||||
isPersonalIdMatchingBirthdate
|
||||
} from '$lib/utils/personalIdFormat';
|
||||
import PersonalIdErrorModal from '$lib/components/modal/PersonalIdErrorModal.svelte';
|
||||
import LinkErrorModal from '$lib/components/modal/LinkErrorModal.svelte';
|
||||
import type { Writable } from 'svelte/store';
|
||||
import { pushErrorText, pushSuccessText } from '$lib/utils/toast';
|
||||
|
||||
// import schoolList from '$lib/assets/list/school.json';
|
||||
import schoolList from '$lib/assets/list/high_schools.json';
|
||||
import countriesList from '$lib/assets/list/countries.json';
|
||||
|
||||
// const schoolList = highSchoolList.map((school) => school['n']);
|
||||
const schoolNames = schoolList.map((school: SchoolJson) => school['n']);
|
||||
|
||||
let pageIndex = 0;
|
||||
let pagesFilled = [false, false, false, false, false, false, false, false];
|
||||
|
|
@ -69,7 +80,7 @@
|
|||
city: '',
|
||||
zip: '',
|
||||
citizenship: '',
|
||||
personalIdNumber: '',
|
||||
personalIdNumber: 'TODO: remove this',
|
||||
schoolName: '',
|
||||
healthInsurance: '',
|
||||
grades: [],
|
||||
|
|
@ -116,8 +127,21 @@
|
|||
birthdate: yup
|
||||
.string()
|
||||
.required()
|
||||
.matches(/^([0-3]?[0-9])\.(0?[1-9]|1[0-2])\.[0-9]{4}$/),
|
||||
birthSurname: yup.string().required(),
|
||||
.matches(/^([0-3]?[0-9])\.(0?[1-9]|1[0-2])\.[0-9]{4}$/)
|
||||
.test((_val) => {
|
||||
if ($form.candidate.citizenship !== 'Česká republika') return true;
|
||||
if (!_val) return false;
|
||||
if (isPersonalIdMatchingBirthdate(
|
||||
$form.candidate.personalIdNumber,
|
||||
_val
|
||||
)) {
|
||||
return true;
|
||||
} else {
|
||||
pushErrorText("Datum narození a rodné číslo se neshodují.")
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
birthSurname: yup.string(),
|
||||
sex: yup.string(),
|
||||
address: yup.string(),
|
||||
street: yup.string().required(),
|
||||
|
|
@ -128,7 +152,7 @@
|
|||
city: yup.string().required(),
|
||||
zip: yup.string().required(),
|
||||
citizenship: yup.string().required(),
|
||||
personalIdNumber: yup.string().required(),
|
||||
personalIdNumber: yup.string(),
|
||||
schoolName: yup.string().required(),
|
||||
healthInsurance: yup.number().required(),
|
||||
grades: yup
|
||||
|
|
@ -146,11 +170,34 @@
|
|||
)
|
||||
.required(),
|
||||
firstSchool: yup.object().shape({
|
||||
name: yup.string().required(),
|
||||
name: yup
|
||||
.string()
|
||||
.required()
|
||||
.test((_val) => {
|
||||
if (!_val) return false;
|
||||
if (schoolNames.includes(_val)) {
|
||||
return true;
|
||||
} else {
|
||||
pushErrorText('Vyberte prosím školu ze seznamu.');
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
field: yup.string().required()
|
||||
}),
|
||||
secondSchool: yup.object().shape({
|
||||
name: yup.string().required(),
|
||||
name: yup
|
||||
.string()
|
||||
.required()
|
||||
.test((_val) => {
|
||||
if (!_val) return false;
|
||||
if (!_val) return false;
|
||||
if (schoolNames.includes(_val)) {
|
||||
return true;
|
||||
} else {
|
||||
pushErrorText('Vyberte prosím školu ze seznamu.');
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
field: yup.string().required()
|
||||
}),
|
||||
testLanguage: yup.string().required()
|
||||
|
|
@ -216,32 +263,9 @@
|
|||
personalIdModal: false,
|
||||
linkErrorModal: false
|
||||
};
|
||||
const validatePersonalId = () => {
|
||||
if ($form.candidate.citizenship === 'Česká republika') {
|
||||
if (
|
||||
!isPersonalIdNumberWithBirthdateValid(
|
||||
$form.candidate.personalIdNumber,
|
||||
$form.candidate.birthdate
|
||||
)
|
||||
) {
|
||||
toast.push('Rodné číslo neodpovídá oficiální specifikaci či datumu narození', {
|
||||
theme: {
|
||||
'--toastColor': 'mintcream',
|
||||
'--toastBackground': '#b91c1c',
|
||||
'--toastBarBackground': '#7f1d1d'
|
||||
}
|
||||
});
|
||||
throw new Error('Rodné číslo neodpovídá datumu narození');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onSubmit = async (values: CandidateData) => {
|
||||
console.log('submit button clicked');
|
||||
console.log(pagesFilled.map((_, i) => !isPageInvalid(i)));
|
||||
|
||||
if (pageIndex === pageCount) {
|
||||
console.log('submitting');
|
||||
// clone values to oldValues
|
||||
let oldValues = JSON.parse(JSON.stringify(values));
|
||||
try {
|
||||
|
|
@ -336,7 +360,7 @@
|
|||
$typedErrors['candidate']['healthInsurance'] ||
|
||||
$typedErrors['candidate']['birthdate'] ||
|
||||
$typedErrors['candidate']['birthplace'] ||
|
||||
$typedErrors['candidate']['personalIdNumber'] ||
|
||||
$typedErrors['candidate']['birthSurname'] ||
|
||||
$typedErrors['candidate']['testLanguage']
|
||||
) {
|
||||
return true;
|
||||
|
|
@ -386,11 +410,23 @@
|
|||
return '+' + telephone.match(/[0-9]{1,3}/g)!.join(' ');
|
||||
};
|
||||
|
||||
// TODO
|
||||
/* $form.candidate.personalIdNumber = data.whoami.personalIdNumber;
|
||||
const [birthdate, sex] = deriveBirthdateFromPersonalId(data.whoami.personalIdNumber);
|
||||
$form.candidate.birthdate = birthdate;
|
||||
$form.candidate.sex = sex; */
|
||||
let lastCitizenshipSelected = $form.candidate.citizenship;
|
||||
$: if ($form.candidate.citizenship !== lastCitizenshipSelected) {
|
||||
lastCitizenshipSelected = $form.candidate.citizenship;
|
||||
$form.candidate.birthdate = '';
|
||||
$form.candidate.sex = '';
|
||||
|
||||
if ($form.candidate.citizenship === 'Česká republika') {
|
||||
let [birthdate, sex] = parseBirthdateSexFromPersonalId(data.whoami.personalIdNumber);
|
||||
$form.candidate.birthdate = birthdate;
|
||||
$form.candidate.sex = sex;
|
||||
if (pageIndex === 4) {
|
||||
pushSuccessText(
|
||||
`Datum narození a pohlaví bylo vyplněno automaticky podle Vašeho rodného čísla (${data.whoami.personalIdNumber}).`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (details !== undefined) {
|
||||
details.candidate.birthdate = details.candidate.birthdate.split('-').reverse().join('.');
|
||||
|
|
@ -442,7 +478,10 @@
|
|||
personalIdNumber={baseCandidateDetails.personalIdNumber}
|
||||
/>
|
||||
{:else if visibleModals.linkErrorModal}
|
||||
<LinkErrorModal applications={baseCandidateDetails.applications} on:close={(_) => (visibleModals.linkErrorModal = false)} />
|
||||
<LinkErrorModal
|
||||
applications={baseCandidateDetails.applications}
|
||||
on:close={(_) => (visibleModals.linkErrorModal = false)}
|
||||
/>
|
||||
{/if}
|
||||
<div class="form relative bg-center">
|
||||
<div class="bottom-5/24 absolute flex w-full flex-col md:h-auto">
|
||||
|
|
@ -499,24 +538,14 @@
|
|||
</p>
|
||||
<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>
|
||||
<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 ml-2">
|
||||
<TelephoneField
|
||||
bind:error={$typedErrors['candidate']['telephone']}
|
||||
|
|
@ -539,7 +568,7 @@
|
|||
bind:value={$form.candidate.city}
|
||||
type="text"
|
||||
placeholder={$LL.input.city()}
|
||||
helperText="Uveďte poštovní směrovací číslo. (např. 602 00)"
|
||||
helperText="Uveďte okres / MČ Prahy (např. Liberec nebo Praha 5)"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -553,16 +582,16 @@
|
|||
bind:valueName={$form.candidate.street}
|
||||
bind:valueSurname={$form.candidate.houseNumber}
|
||||
placeholder={$LL.input.address()}
|
||||
helperText="Uveďte ulici a číslo popisné (např. Preslova 72)."
|
||||
helperText="Uveďte ulici a číslo popisné (např. Preslova 72/25)."
|
||||
/>
|
||||
</span>
|
||||
<span class="ml-2 w-[33%]">
|
||||
<TextField
|
||||
error={$typedErrors['candidate']['zip']}
|
||||
bind:value={$form.candidate.zip}
|
||||
type="number"
|
||||
type="text"
|
||||
placeholder={$LL.input.zipCode()}
|
||||
helperText="Uveďte poštovní směrovací číslo. (např. 602 00)"
|
||||
helperText="Uveďte poštovní směrovací číslo. (např. 150 21)"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -579,7 +608,7 @@
|
|||
error={$typedErrors['candidate']['citizenship']}
|
||||
bind:value={$form.candidate.citizenship}
|
||||
placeholder={$LL.input.citizenship()}
|
||||
options={['Česká republika', 'Slovenská republika', 'Ukrajina', 'Jiné']}
|
||||
options={countriesList}
|
||||
/>
|
||||
</span>
|
||||
<span class="ml-2 w-[50%]">
|
||||
|
|
@ -597,7 +626,7 @@
|
|||
bind:value={$form.candidate.birthdate}
|
||||
type="text"
|
||||
placeholder={$LL.input.birthDate()}
|
||||
helperText="TODO: (Uveďte ve formátu DD.MM.RRRR)"
|
||||
helperText="Uveďte datum narození (např. 1. 1. 1970)"
|
||||
/>
|
||||
<div class="ml-2">
|
||||
<TextField
|
||||
|
|
@ -605,24 +634,16 @@
|
|||
bind:value={$form.candidate.birthplace}
|
||||
type="text"
|
||||
placeholder={$LL.input.birthPlace()}
|
||||
helperText="TODO: (Místo narození)"
|
||||
helperText="Uveďte místo narození (např. Liberec nebo Praha 5)"
|
||||
/>
|
||||
</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}
|
||||
<TextField
|
||||
error={$typedErrors['candidate']['birthSurname']}
|
||||
bind:value={$form.candidate.birthSurname}
|
||||
placeholder={`${$LL.input.birthSurname()} (${$LL.input.optional()})`}
|
||||
/>
|
||||
<div class="ml-2">
|
||||
<SelectField
|
||||
error={$typedErrors['candidate']['sex']}
|
||||
|
|
@ -640,6 +661,7 @@
|
|||
type="number"
|
||||
bind:value={$form.candidate.schoolName}
|
||||
placeholder={$LL.input.schoolIzo()}
|
||||
helperText="Uveďte IZO základní školy (např. 47608579)"
|
||||
/>
|
||||
{:else}
|
||||
<TextField
|
||||
|
|
@ -647,6 +669,7 @@
|
|||
type="text"
|
||||
bind:value={$form.candidate.schoolName}
|
||||
placeholder={$LL.input.schoolName()}
|
||||
helperText="Uveďte název základní školy (např. Masarykova základní škola, Praha 9 - Újezd nad Lesy, Polesná 1690)"
|
||||
/>
|
||||
{/if}
|
||||
</span>
|
||||
|
|
@ -657,6 +680,7 @@
|
|||
type="text"
|
||||
bind:value={$form.candidate.healthInsurance}
|
||||
placeholder={$LL.input.insuranceNumber()}
|
||||
helperText="Uveďte číslo zdravotní pojišťovny (např. 111)"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -719,20 +743,43 @@
|
|||
</span>
|
||||
</div>
|
||||
{:else if pageIndex === 7}
|
||||
<h1 class="title mt-8">{pageTexts[5]}</h1>
|
||||
<p class="description my-8 block text-center">
|
||||
<!-- <h1 class="title mt-8">{pageTexts[5]}</h1> -->
|
||||
<!-- <p class="description mt-8 block text-center">
|
||||
{$LL.candidate.register.seventh.description()}
|
||||
</p>
|
||||
</p> -->
|
||||
<div class="flex h-full flex-col justify-between">
|
||||
<span class="field">
|
||||
<h2 class="text-sspsBlueDark mb-6 text-3xl font-bold">
|
||||
První škola - termín JPZ: <span class="underline">13. 4. 2023</span>
|
||||
</h2>
|
||||
<SchoolSelect
|
||||
{schoolNames}
|
||||
{schoolList}
|
||||
error={$typedErrors['candidate']['firstSchool']['name'] ||
|
||||
$typedErrors['candidate']['firstSchool']['field']}
|
||||
bind:selectedSchool={$form.candidate.firstSchool}
|
||||
/>
|
||||
</span>
|
||||
<!--dotted line -->
|
||||
<svg class="mt-12 h-[10px] w-full" viewBox="0 0 800 5">
|
||||
<line
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="100%"
|
||||
y2="0"
|
||||
stroke="black"
|
||||
stroke-width="3"
|
||||
stroke-dasharray="10"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
<span class="field mt-10">
|
||||
<h2 class="text-sspsBlueDark mb-6 text-3xl font-bold">
|
||||
Druhá škola - termín JPZ: <span class="underline">14. 4. 2023</span>
|
||||
</h2>
|
||||
<SchoolSelect
|
||||
{schoolNames}
|
||||
{schoolList}
|
||||
error={$typedErrors['candidate']['secondSchool']['name'] ||
|
||||
$typedErrors['candidate']['secondSchool']['field']}
|
||||
bind:selectedSchool={$form.candidate.secondSchool}
|
||||
|
|
@ -753,13 +800,9 @@
|
|||
<div class="bottom-1/24 absolute w-full">
|
||||
<div class="field">
|
||||
<Submit
|
||||
enterAllowed={pageIndex !== 7}
|
||||
on:click={async (e) => {
|
||||
if (pageIndex === 4) {
|
||||
console.log('validating personal id');
|
||||
validatePersonalId();
|
||||
}
|
||||
await handleSubmit(e);
|
||||
console.log(pagesFilled.map((_, i) => !isPageInvalid(i)));
|
||||
if (isPageInvalid(pageIndex)) return;
|
||||
if (pageIndex !== pageCount) {
|
||||
pagesFilled[pageIndex] = true;
|
||||
|
|
@ -777,9 +820,6 @@
|
|||
<button
|
||||
class:dotActive={i === pageIndex}
|
||||
on:click={async (e) => {
|
||||
if (pageIndex === 4 && i > pageIndex) {
|
||||
validatePersonalId();
|
||||
}
|
||||
pageIndex -= pageIndex === pageCount ? 1 : 0;
|
||||
await handleSubmit(e);
|
||||
pagesFilled = pagesFilled.map((_, i) => !isPageInvalid(i));
|
||||
|
|
@ -799,7 +839,7 @@
|
|||
|
||||
<style lang="postcss">
|
||||
.field {
|
||||
@apply mt-4 w-full md:mt-8 lg:mx-auto lg:w-4/5;
|
||||
@apply lg:w-9/10 mt-4 w-full md:mt-8 lg:mx-auto 2xl:w-4/5;
|
||||
}
|
||||
.form {
|
||||
@apply flex flex-col;
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
/>
|
||||
</div>
|
||||
<div class="mt-8 w-4/5 lg:w-3/5">
|
||||
<Submit on:click={redirectToCode} value={$LL.input.submit()} />
|
||||
<Submit enterAllowed={true} on:click={redirectToCode} value={$LL.input.submit()} />
|
||||
</div>
|
||||
</div>
|
||||
</SplitLayout>
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@
|
|||
}
|
||||
|
||||
const submit = async () => {
|
||||
console.log('submitting: ', codeValueArray);
|
||||
try {
|
||||
await apiLogin({ applicationId, password: codeValueMobile });
|
||||
goto('/dashboard');
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ const cs: BaseTranslation = {
|
|||
},
|
||||
eighth: {
|
||||
title: 'Poslední krok',
|
||||
description: 'Přidejte prosím přepis Vaších známek z posledních dvou let studia'
|
||||
description: 'Přidejte prosím přepis Vaších známek z posledních dvou let studia. Známky z druhého pololetí 9. třídy nevyplňujte, pokud vysvědčení ještě nemáte.'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -140,7 +140,7 @@ const cs: BaseTranslation = {
|
|||
fullAddress: 'Adresa',
|
||||
address: 'Ulice a č. p.',
|
||||
zipCode: 'PSČ',
|
||||
city: 'Město',
|
||||
city: 'Okres',
|
||||
birthPlace: 'Místo narození',
|
||||
birthDate: 'Datum narození',
|
||||
sex: 'Pohlaví',
|
||||
|
|
|
|||
Loading…
Reference in a new issue