mirror of
https://github.com/danbulant/Portfolio
synced 2026-06-24 17:11:49 +00:00
Merge pull request #109 from EETagent/feature_edit_details
(frontend) SSR Edit Details
This commit is contained in:
commit
5c10d79be4
9 changed files with 397 additions and 235 deletions
|
|
@ -7,7 +7,8 @@
|
||||||
import StatusNotificationBig from './StatusNotificationBig.svelte';
|
import StatusNotificationBig from './StatusNotificationBig.svelte';
|
||||||
import InfoButton from './InfoButton.svelte';
|
import InfoButton from './InfoButton.svelte';
|
||||||
import { candidateData } from '$lib/stores/candidate';
|
import { candidateData } from '$lib/stores/candidate';
|
||||||
import tippy from 'tippy.js';
|
import tippy, {sticky} from 'tippy.js';
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
|
||||||
export let title: string;
|
export let title: string;
|
||||||
export let status: Status;
|
export let status: Status;
|
||||||
|
|
@ -58,6 +59,10 @@
|
||||||
console.log(e);
|
console.log(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const editDetails = async () => {
|
||||||
|
goto('/register?edit=true')
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="card flex flex-col">
|
<div class="card flex flex-col">
|
||||||
|
|
@ -86,52 +91,65 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if showDetails}
|
{#if showDetails}
|
||||||
<div class="overflow-scroll">
|
<div class="overflow-scroll flex justify-between">
|
||||||
<div
|
<div>
|
||||||
|
<div
|
||||||
|
use:tippy={{
|
||||||
|
content: '<span>Vámi vyplněné osobní údaje</span>',
|
||||||
|
allowHTML: true,
|
||||||
|
placement: 'top',
|
||||||
|
showOnCreate: false,
|
||||||
|
delay: 0
|
||||||
|
}}
|
||||||
|
class="mt-4 flex flex-col justify-between leading-10"
|
||||||
|
>
|
||||||
|
<span>Adresa: <span class="font-bold">{$candidateData.candidate.address}</span></span>
|
||||||
|
<span
|
||||||
|
>Datum narození: <span class="font-bold">{$candidateData.candidate.birthdate}</span
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
>Místo narození: <span class="font-bold">{$candidateData.candidate.birthplace}</span
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
>Rodné číslo: <span class="font-bold">{$candidateData.candidate.personalIdNumber}</span
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
<span>Telefon: <span class="font-bold">{$candidateData.candidate.telephone}</span></span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
use:tippy={{
|
||||||
|
content: '<span>Vámi vyplněné osobní údaje</span>',
|
||||||
|
allowHTML: true,
|
||||||
|
placement: 'top',
|
||||||
|
showOnCreate: false,
|
||||||
|
delay: 0
|
||||||
|
}}
|
||||||
|
class="mt-4 flex flex-col leading-10"
|
||||||
|
>
|
||||||
|
{#each $candidateData.parents as parent}
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<span class="text-sspsBlue text-xl font-bold"
|
||||||
|
>{parent.name + ' ' + parent.surname}</span
|
||||||
|
>
|
||||||
|
<span>Email: <span class="font-bold">{parent.email}</span></span>
|
||||||
|
<span>Telefon: <span class="font-bold">{parent.telephone}</span></span>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
use:tippy={{
|
use:tippy={{
|
||||||
content: '<span>Vámi vyplněné osobní údaje</span>',
|
content: 'Upravit osobní údaje',
|
||||||
allowHTML: true,
|
|
||||||
placement: 'top',
|
placement: 'top',
|
||||||
showOnCreate: false,
|
showOnCreate: false,
|
||||||
delay: 0
|
sticky: true,
|
||||||
|
plugins: [sticky]
|
||||||
}}
|
}}
|
||||||
class="mt-4 flex flex-col justify-between leading-10"
|
on:click={(_) => editDetails()} on:keydown={(_) => editDetails()} class="mt-4 hover:cursor-pointer">
|
||||||
>
|
<svg class="w-10 h-10 stroke-sspsBlue" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path></svg>
|
||||||
<span>Adresa: <span class="font-bold">{$candidateData.candidate.address}</span></span>
|
</span>
|
||||||
<span
|
|
||||||
>Datum narození: <span class="font-bold">{$candidateData.candidate.birthdate}</span
|
|
||||||
></span
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
>Místo narození: <span class="font-bold">{$candidateData.candidate.birthplace}</span
|
|
||||||
></span
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
>Rodné číslo: <span class="font-bold">{$candidateData.candidate.personalIdNumber}</span
|
|
||||||
></span
|
|
||||||
>
|
|
||||||
<span>Telefon: <span class="font-bold">{$candidateData.candidate.telephone}</span></span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
use:tippy={{
|
|
||||||
content: '<span>Vámi vyplněné osobní údaje</span>',
|
|
||||||
allowHTML: true,
|
|
||||||
placement: 'top',
|
|
||||||
showOnCreate: false,
|
|
||||||
delay: 0
|
|
||||||
}}
|
|
||||||
class="mt-4 flex flex-col leading-10"
|
|
||||||
>
|
|
||||||
{#each $candidateData.parents as parent}
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<span class="text-sspsBlue text-xl font-bold"
|
|
||||||
>{parent.name + ' ' + parent.surname}</span
|
|
||||||
>
|
|
||||||
<span>Email: <span class="font-bold">{parent.email}</span></span>
|
|
||||||
<span>Telefon: <span class="font-bold">{parent.telephone}</span></span>
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -164,7 +164,7 @@
|
||||||
class="drag group"
|
class="drag group"
|
||||||
on:mouseenter={dashAnimationStart}
|
on:mouseenter={dashAnimationStart}
|
||||||
on:mouseleave={dashAnimationStop}
|
on:mouseleave={dashAnimationStop}
|
||||||
style={`background-image: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' rx='9' ry='9' stroke-opacity='50%' stroke='%23406280' stroke-width='4' stroke-dasharray='10' stroke-dashoffset='${dashAnimationProgress}' stroke-linecap='square'/%3e%3c/svg%3e");`}
|
style={`background-image: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' fill='none' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' rx='9' ry='9' stroke-opacity='50%' stroke='%23406280' stroke-width='4' stroke-dasharray='10' stroke-dashoffset='${dashAnimationProgress}' stroke-linecap='square'/%3e%3c/svg%3e");`}
|
||||||
>
|
>
|
||||||
{#if error}
|
{#if error}
|
||||||
<span class="font-semibold text-red-600">{error}</span>
|
<span class="font-semibold text-red-600">{error}</span>
|
||||||
|
|
|
||||||
|
|
@ -28,11 +28,11 @@
|
||||||
<style lang="postcss">
|
<style lang="postcss">
|
||||||
.bgImage {
|
.bgImage {
|
||||||
@apply -z-20;
|
@apply -z-20;
|
||||||
@apply min-w-screen absolute min-h-screen md:min-w-[50vw];
|
@apply min-w-screen absolute min-h-full md:min-w-[50vw];
|
||||||
@apply bg-cover bg-no-repeat;
|
@apply bg-cover bg-no-repeat;
|
||||||
}
|
}
|
||||||
.bgOverlay {
|
.bgOverlay {
|
||||||
@apply min-w-screen absolute -z-10 min-h-screen md:min-w-[50vw];
|
@apply min-w-screen absolute -z-10 min-h-full md:min-w-[50vw];
|
||||||
background: linear-gradient(45deg, rgba(18, 48, 75, 1), rgba(119, 173, 224, 0.443));
|
background: linear-gradient(45deg, rgba(18, 48, 75, 1), rgba(119, 173, 224, 0.443));
|
||||||
@apply bg-cover;
|
@apply bg-cover;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,23 +2,23 @@ import { writable } from 'svelte/store';
|
||||||
|
|
||||||
export interface CandidateData {
|
export interface CandidateData {
|
||||||
candidate: {
|
candidate: {
|
||||||
name?: string;
|
name: string;
|
||||||
surname?: string;
|
surname: string;
|
||||||
birthplace?: string;
|
birthplace: string;
|
||||||
birthdate?: string;
|
birthdate: string;
|
||||||
address?: string;
|
address: string;
|
||||||
telephone?: string;
|
telephone: string;
|
||||||
citizenship?: string;
|
citizenship: string;
|
||||||
email?: string;
|
email: string;
|
||||||
sex?: string;
|
sex: string;
|
||||||
study?: string;
|
study: string;
|
||||||
personalIdNumber?: string;
|
personalIdNumber: string;
|
||||||
};
|
};
|
||||||
parents: Array<{
|
parents: Array<{
|
||||||
name?: string;
|
name: string;
|
||||||
surname?: string;
|
surname: string;
|
||||||
telephone?: string;
|
telephone: string;
|
||||||
email?: string;
|
email: string;
|
||||||
}>;
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -44,6 +44,18 @@ export interface CreateCandidateLogin extends CreateCandidate {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const candidateData = writable<CandidateData>({
|
export const candidateData = writable<CandidateData>({
|
||||||
candidate: {},
|
candidate: {
|
||||||
|
name: '',
|
||||||
|
surname: '',
|
||||||
|
birthplace: '',
|
||||||
|
birthdate: '',
|
||||||
|
address: '',
|
||||||
|
telephone: '',
|
||||||
|
citizenship: '',
|
||||||
|
email: '',
|
||||||
|
sex: '',
|
||||||
|
study: '',
|
||||||
|
personalIdNumber: ''
|
||||||
|
},
|
||||||
parents: []
|
parents: []
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,19 @@ export const load: PageServerLoad = async ({ fetch, params }) => {
|
||||||
const codeNumber = Number(code);
|
const codeNumber = Number(code);
|
||||||
|
|
||||||
let candidateData: CandidateData = {
|
let candidateData: CandidateData = {
|
||||||
candidate: {},
|
candidate: {
|
||||||
|
name: '',
|
||||||
|
surname: '',
|
||||||
|
birthplace: '',
|
||||||
|
birthdate: '',
|
||||||
|
address: '',
|
||||||
|
telephone: '',
|
||||||
|
citizenship: '',
|
||||||
|
email: '',
|
||||||
|
sex: '',
|
||||||
|
study: '',
|
||||||
|
personalIdNumber: ''
|
||||||
|
},
|
||||||
parents: []
|
parents: []
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
<div class="dashboard dashboardMobile">
|
<div class="dashboard dashboardMobile">
|
||||||
<div class="name my-10 mx-auto w-[90%]">
|
<div class="name md:my-10 mx-auto w-[90%]">
|
||||||
<DashboardInfoCard
|
<DashboardInfoCard
|
||||||
status={getUploadStatus($submissionProgress.status)}
|
status={getUploadStatus($submissionProgress.status)}
|
||||||
title={$candidateData.candidate.name + ' ' + $candidateData.candidate.surname ?? ''}
|
title={$candidateData.candidate.name + ' ' + $candidateData.candidate.surname ?? ''}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { apiFetchDetails, apiFetchSubmissionProgress } from '$lib/@api/candidate';
|
||||||
|
import type { CandidateData } from '$lib/stores/candidate';
|
||||||
|
import type { PageServerLoad } from './$types';
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async ({ fetch }) => {
|
||||||
|
const details: CandidateData | undefined = await apiFetchDetails(fetch).catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
return undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
candidate: details,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -13,15 +13,28 @@
|
||||||
import NameField from '$lib/components/textfield/NameField.svelte';
|
import NameField from '$lib/components/textfield/NameField.svelte';
|
||||||
import TelephoneField from '$lib/components/textfield/TelephoneField.svelte';
|
import TelephoneField from '$lib/components/textfield/TelephoneField.svelte';
|
||||||
import TextField from '$lib/components/textfield/TextField.svelte';
|
import TextField from '$lib/components/textfield/TextField.svelte';
|
||||||
import type { CandidateData } from '$lib/stores/candidate';
|
import type { PageData } from './$types';
|
||||||
|
|
||||||
import { createForm } from 'svelte-forms-lib';
|
import { createForm } from 'svelte-forms-lib';
|
||||||
import type { Writable } from 'svelte/store';
|
import type { Writable } from 'svelte/store';
|
||||||
import * as yup from 'yup';
|
import * as yup from 'yup';
|
||||||
|
import type { CandidateData } from '$lib/stores/candidate';
|
||||||
|
|
||||||
const pageCount = 5;
|
const pageCount = 5;
|
||||||
let pageIndex = 0;
|
let pageIndex = 0;
|
||||||
let pagesFilled = [false, false, false, false, false];
|
let pagesFilled = [false, false, false, false, false];
|
||||||
|
let pageTexts = [
|
||||||
|
'Zpracování osobních údajů',
|
||||||
|
'Registrace',
|
||||||
|
'Něco o Vás',
|
||||||
|
'Kontakt na zákonného zástupce',
|
||||||
|
'Kontakt na druhého zákonného zástupce',
|
||||||
|
'Poslední krok'
|
||||||
|
];
|
||||||
|
|
||||||
|
export let data: PageData;
|
||||||
|
let details = data.candidate;
|
||||||
|
|
||||||
|
|
||||||
const formInitialValues = {
|
const formInitialValues = {
|
||||||
gdpr: false,
|
gdpr: false,
|
||||||
|
|
@ -58,7 +71,7 @@
|
||||||
gdpr: yup.boolean().oneOf([true]),
|
gdpr: yup.boolean().oneOf([true]),
|
||||||
candidate: yup.object().shape({
|
candidate: yup.object().shape({
|
||||||
name: yup.string().required(),
|
name: yup.string().required(),
|
||||||
surname: yup.string(),
|
surname: yup.string().required(),
|
||||||
email: yup.string().email().required(),
|
email: yup.string().email().required(),
|
||||||
telephone: yup
|
telephone: yup
|
||||||
.string()
|
.string()
|
||||||
|
|
@ -68,7 +81,7 @@
|
||||||
birthdate: yup
|
birthdate: yup
|
||||||
.string()
|
.string()
|
||||||
.required()
|
.required()
|
||||||
.matches(/^([0-3]?[0-9])\.([1-9]|1[0-2])\.[0-9]{4}$/),
|
.matches(/^([0-3]?[0-9])\.(0?[1-9]|1[0-2])\.[0-9]{4}$/),
|
||||||
sex: yup.string(),
|
sex: yup.string(),
|
||||||
address: yup.string().required(),
|
address: yup.string().required(),
|
||||||
citizenship: yup.string().required(),
|
citizenship: yup.string().required(),
|
||||||
|
|
@ -93,13 +106,13 @@
|
||||||
.string()
|
.string()
|
||||||
.email()
|
.email()
|
||||||
.test((_val, context) => {
|
.test((_val, context) => {
|
||||||
if (context.path.includes('parents[1]')) {
|
if (context.path.includes('parents[1]') && _val === '') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return _val !== '';
|
return _val !== '';
|
||||||
}),
|
}),
|
||||||
telephone: yup.string().test((_val, context) => {
|
telephone: yup.string().test((_val, context) => {
|
||||||
if (context.path.includes('parents[1]')) {
|
if (context.path.includes('parents[1]') && _val === '') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return _val?.match(/^\+\d{1,3} \d{3} \d{3} \d{3}$/) !== null;
|
return _val?.match(/^\+\d{1,3} \d{3} \d{3} \d{3}$/) !== null;
|
||||||
|
|
@ -108,45 +121,6 @@
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
const { form, errors, handleSubmit, handleChange } = createForm({
|
|
||||||
initialValues: formInitialValues,
|
|
||||||
validationSchema: formValidationSchema,
|
|
||||||
|
|
||||||
onSubmit: async (values: CandidateData) => {
|
|
||||||
console.log('page count: ' + pageIndex);
|
|
||||||
console.log(values.candidate);
|
|
||||||
console.log(values.parents);
|
|
||||||
console.log(values);
|
|
||||||
if (pageIndex === pageCount) {
|
|
||||||
// clone values to oldValues
|
|
||||||
let oldValues = JSON.parse(JSON.stringify(values));
|
|
||||||
try {
|
|
||||||
console.log('submit');
|
|
||||||
// @ts-ignore // love javascript
|
|
||||||
delete values.undefined;
|
|
||||||
// convert birthdate from dd.mm.yyyy to yyyy-mm-dd
|
|
||||||
let birthdate_formttted = values.candidate
|
|
||||||
.birthdate!.split('.')
|
|
||||||
.map((x) => x.padStart(2, '0'))
|
|
||||||
.reverse()
|
|
||||||
.join('-');
|
|
||||||
|
|
||||||
values.candidate.birthdate = birthdate_formttted;
|
|
||||||
|
|
||||||
values.parents.filter(
|
|
||||||
(x) => x.name !== '' && x.surname !== '' && x.email !== '' && x.telephone !== ''
|
|
||||||
);
|
|
||||||
|
|
||||||
await apiFillDetails(values);
|
|
||||||
goto('/dashboard');
|
|
||||||
} catch (e) {
|
|
||||||
values = oldValues;
|
|
||||||
console.error('error while submitting data: ' + e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
type FormErrorType = {
|
type FormErrorType = {
|
||||||
[K in keyof typeof formInitialValues]: typeof formInitialValues[K] extends Record<
|
[K in keyof typeof formInitialValues]: typeof formInitialValues[K] extends Record<
|
||||||
string,
|
string,
|
||||||
|
|
@ -163,8 +137,100 @@
|
||||||
// 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>;
|
||||||
|
|
||||||
const isPageInvalid = (): boolean => {
|
// TODO: validate on admin dashboard, move somewhere
|
||||||
switch (pageIndex) {
|
// 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) => {
|
||||||
|
if (pageIndex === pageCount) {
|
||||||
|
// 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
|
||||||
|
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
|
||||||
|
let birthdate_formttted = values.candidate
|
||||||
|
.birthdate!.split('.')
|
||||||
|
.map((x) => x.padStart(2, '0'))
|
||||||
|
.reverse()
|
||||||
|
.join('-');
|
||||||
|
|
||||||
|
values.candidate.birthdate = birthdate_formttted;
|
||||||
|
|
||||||
|
values.parents = values.parents.filter(
|
||||||
|
(x) => x.name !== '' && x.surname !== '' && x.email !== '' && x.telephone !== ''
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
await apiFillDetails(values);
|
||||||
|
goto('/dashboard');
|
||||||
|
} catch (e) {
|
||||||
|
values = oldValues;
|
||||||
|
console.error('error while submitting data: ' + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { form, errors, handleSubmit, handleChange } = createForm({
|
||||||
|
initialValues: formInitialValues,
|
||||||
|
validationSchema: formValidationSchema,
|
||||||
|
|
||||||
|
onSubmit: async (values: CandidateData) => onSubmit(values)
|
||||||
|
});
|
||||||
|
|
||||||
|
const isPageInvalid = (index: number): boolean => {
|
||||||
|
switch (index) {
|
||||||
case 0:
|
case 0:
|
||||||
if ($typedErrors['gdpr']) {
|
if ($typedErrors['gdpr']) {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -182,9 +248,9 @@
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
if (
|
if (
|
||||||
/* $typedErrors.birthdurname || */ $typedErrors['candidate']['birthplace'] ||
|
$typedErrors['candidate']['birthplace'] ||
|
||||||
$typedErrors['candidate']['birthdate'] ||
|
$typedErrors['candidate']['birthdate'] ||
|
||||||
$typedErrors['candidate']['address'] /* || $typedErrors.sex */
|
$typedErrors['candidate']['address']
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -213,8 +279,7 @@
|
||||||
if (
|
if (
|
||||||
$typedErrors['candidate']['citizenship'] ||
|
$typedErrors['candidate']['citizenship'] ||
|
||||||
$typedErrors['candidate']['personalIdNumber'] ||
|
$typedErrors['candidate']['personalIdNumber'] ||
|
||||||
$typedErrors['candidate']['study'] //||
|
$typedErrors['candidate']['study']
|
||||||
// $typedErrors.applicationId
|
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -224,21 +289,62 @@
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const formatTelephone = (telephone: string) => {
|
||||||
|
return '+' + telephone
|
||||||
|
.match(/[0-9]{1,3}/g)!
|
||||||
|
.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (details !== undefined) {
|
||||||
|
details.candidate.birthdate = details.candidate.birthdate
|
||||||
|
.split('-')
|
||||||
|
.reverse()
|
||||||
|
.join('.');
|
||||||
|
|
||||||
|
details.candidate.telephone = formatTelephone(details.candidate.telephone);
|
||||||
|
details.parents.map((x) => x.telephone = x.telephone != '' ? formatTelephone(x.telephone) : '');
|
||||||
|
form.set({
|
||||||
|
gdpr: true,
|
||||||
|
candidate: {
|
||||||
|
...details.candidate
|
||||||
|
},
|
||||||
|
parents: [
|
||||||
|
{
|
||||||
|
...details.parents[0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...details.parents[1] ?? {
|
||||||
|
name: '',
|
||||||
|
surname: '',
|
||||||
|
email: '',
|
||||||
|
telephone: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
pageIndex = 1; // skip gdpr page
|
||||||
|
pageTexts[1] = 'Úprava osobních údajů'
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SplitLayout>
|
<SplitLayout>
|
||||||
<div class="form">
|
<div class="form relative">
|
||||||
<div class="h-24 w-24 md:h-auto md:w-auto">
|
<div class="overflow-scroll h-[65%] md:h-auto absolute bottom-3/12 flex flex-col w-full">
|
||||||
<SchoolBadge />
|
<div class="h-32 w-32 <md:hidden self-center mb-4">
|
||||||
</div>
|
<SchoolBadge />
|
||||||
|
</div>
|
||||||
|
<form on:submit={handleSubmit} id="triggerForm" class="invisible hidden"></form>
|
||||||
{#if pageIndex === 0}
|
{#if pageIndex === 0}
|
||||||
<form on:submit={handleSubmit}>
|
<form on:submit={handleSubmit}>
|
||||||
<h1 class="text-sspsBlue mt-8 text-4xl font-semibold">Váš souhlas</h1>
|
<h1 class="title mt-8">{pageTexts[0]}</h1>
|
||||||
<p class="text-sspsGray mt-8 block text-center font-light">
|
<p class="description mt-8 block text-center">
|
||||||
V rámci portálu pro přijímací řízení zpracováváme mnoho osobních údajů. Proto je nutný Váš
|
V rámci portálu pro přijímací řízení zpracováváme mnoho osobních údajů. Proto je nutný Váš
|
||||||
souhlas s jejich zpracováním.
|
souhlas s jejich zpracováním. O bezpečnosti zpracování Vašich osobních údajů si můžete přečíst
|
||||||
|
<a href="/bezpecnost" class="text-sspsBlue underline"> zde</a>.
|
||||||
</p>
|
</p>
|
||||||
<div class="mt-8 w-full">
|
<div class="field">
|
||||||
<GdprCheckBox
|
<GdprCheckBox
|
||||||
on:change={handleChange}
|
on:change={handleChange}
|
||||||
bind:value={$form.gdpr}
|
bind:value={$form.gdpr}
|
||||||
|
|
@ -248,13 +354,13 @@
|
||||||
</form>
|
</form>
|
||||||
{:else if pageIndex === 1}
|
{:else if pageIndex === 1}
|
||||||
<form on:submit={handleSubmit}>
|
<form on:submit={handleSubmit}>
|
||||||
<h1 class="text-sspsBlue mt-8 text-4xl font-semibold">Registrace</h1>
|
<h1 class="title mt-8">{pageTexts[1]}</h1>
|
||||||
<p class="text-sspsGray mt-8 block text-center font-light">
|
<p class="description mt-8 block text-center">
|
||||||
V rámci usnadnění přijímacího řízení jsme připravili online formulář, který vám pomůže s
|
V rámci usnadnění přijímacího řízení jsme připravili online formulář, který Vám pomůže s
|
||||||
vyplněním potřebných údajů.
|
vyplněním potřebných údajů.
|
||||||
</p>
|
</p>
|
||||||
<div class="flex w-full items-center justify-center md:flex-col">
|
<div class="flex flex-col">
|
||||||
<span class="mt-8 w-full">
|
<span class="field">
|
||||||
<NameField
|
<NameField
|
||||||
error={$typedErrors['candidate']['name']}
|
error={$typedErrors['candidate']['name']}
|
||||||
on:change={handleChange}
|
on:change={handleChange}
|
||||||
|
|
@ -263,7 +369,7 @@
|
||||||
placeholder="Jméno a příjmení"
|
placeholder="Jméno a příjmení"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span class="mt-8 ml-2 w-full md:ml-0">
|
<span class="field">
|
||||||
<EmailField
|
<EmailField
|
||||||
error={$typedErrors['candidate']['email']}
|
error={$typedErrors['candidate']['email']}
|
||||||
on:change={handleChange}
|
on:change={handleChange}
|
||||||
|
|
@ -271,24 +377,24 @@
|
||||||
placeholder="E-mail"
|
placeholder="E-mail"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
<span class="field">
|
||||||
<div class="mt-8 w-full">
|
<TelephoneField
|
||||||
<TelephoneField
|
error={$typedErrors['candidate']['telephone']}
|
||||||
error={$typedErrors['candidate']['telephone']}
|
on:change={handleChange}
|
||||||
on:change={handleChange}
|
bind:value={$form.candidate.telephone}
|
||||||
bind:value={$form.candidate.telephone}
|
placeholder="Telefon"
|
||||||
placeholder="Telefon"
|
/>
|
||||||
/>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{:else if pageIndex === 2}
|
{:else if pageIndex === 2}
|
||||||
<h1 class="text-sspsBlue mt-8 text-4xl font-semibold">Něco o tobě</h1>
|
<h1 class="title mt-8">{pageTexts[2]}</h1>
|
||||||
<p class="text-sspsGray mt-8 block text-center font-light">
|
<p class="description mt-8 block text-center">
|
||||||
Pro registraci je potřeba vyplnit několik údajů o tobě. Tyto údaje budou použity pro
|
Pro registraci je potřeba vyplnit několik údajů o Vás. Tyto údaje budou použity pro
|
||||||
přijímací řízení. Všechny údaje jsou důležité a bez nich se registrace nezdaří.
|
přijímací řízení. Všechny údaje jsou důležité a bez nich se registrace nezdaří.
|
||||||
</p>
|
</p>
|
||||||
<div class="flex w-full flex-row md:flex-col">
|
<div class="flex w-full flex-col">
|
||||||
<span class="mt-8 w-full">
|
<span class="field">
|
||||||
<TextField
|
<TextField
|
||||||
error={$typedErrors['candidate']['address']}
|
error={$typedErrors['candidate']['address']}
|
||||||
on:change={handleChange}
|
on:change={handleChange}
|
||||||
|
|
@ -298,7 +404,7 @@
|
||||||
helperText="Uveďte ulici, č.p., město, PSČ"
|
helperText="Uveďte ulici, č.p., město, PSČ"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span class="mt-8 ml-2 w-full md:ml-0">
|
<span class="field">
|
||||||
<TextField
|
<TextField
|
||||||
error={$typedErrors['candidate']['birthplace']}
|
error={$typedErrors['candidate']['birthplace']}
|
||||||
on:change={handleChange}
|
on:change={handleChange}
|
||||||
|
|
@ -315,7 +421,7 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-8 flex w-full items-center">
|
<div class="field flex items-center">
|
||||||
<TextField
|
<TextField
|
||||||
error={$typedErrors['candidate']['birthdate']}
|
error={$typedErrors['candidate']['birthdate']}
|
||||||
on:change={handleChange}
|
on:change={handleChange}
|
||||||
|
|
@ -335,80 +441,76 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{:else if pageIndex === 3}
|
{:else if pageIndex === 3}
|
||||||
<h1 class="text-sspsBlue mt-8 text-4xl font-semibold">Už jen kousek!</h1>
|
<h1 class="title mt-8">{pageTexts[3]}</h1>
|
||||||
<p class="text-sspsGray mt-8 block text-center font-light">
|
<p class="description mt-8 block text-center">
|
||||||
Sběr dat o zákonném zástupci je klíčový pro získání důležitých kontaktů a informací.
|
Sběr dat o zákonném zástupci je klíčový pro získání důležitých kontaktů a informací.
|
||||||
</p>
|
</p>
|
||||||
<div class="flex w-full flex-col">
|
<div class="flex w-full flex-col">
|
||||||
<span class="mt-8 w-full">
|
<span class="field">
|
||||||
<NameField
|
<NameField
|
||||||
error={$typedErrors['parents'][0]['name'] || $typedErrors['parents'][0]['surname']}
|
error={$typedErrors['parents'][0]['name'] || $typedErrors['parents'][0]['surname']}
|
||||||
on:change={handleChange}
|
on:change={handleChange}
|
||||||
bind:valueName={$form.parents[0].name}
|
bind:valueName={$form.parents[0].name}
|
||||||
bind:valueSurname={$form.parents[0].surname}
|
bind:valueSurname={$form.parents[0].surname}
|
||||||
placeholder="Jméno a příjmení zákonného zástupce"
|
placeholder="Jméno a příjmení zákonného zástupce"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<div class="mt-8 flex flex-row items-center md:flex-col">
|
<span class="field">
|
||||||
<span class="w-full">
|
<EmailField
|
||||||
<EmailField
|
error={$typedErrors['parents'][0]['email']}
|
||||||
error={$typedErrors['parents'][0]['email']}
|
on:change={handleChange}
|
||||||
on:change={handleChange}
|
bind:value={$form.parents[0].email}
|
||||||
bind:value={$form.parents[0].email}
|
placeholder="E-mail zákonného zástupce"
|
||||||
placeholder="E-mail zákonného zástupce"
|
/>
|
||||||
/>
|
</span>
|
||||||
</span>
|
<span class="field">
|
||||||
<span class="ml-2 w-full md:ml-0 md:mt-8">
|
<TelephoneField
|
||||||
<TelephoneField
|
error={$typedErrors['parents'][0]['telephone']}
|
||||||
error={$typedErrors['parents'][0]['telephone']}
|
on:change={handleChange}
|
||||||
on:change={handleChange}
|
bind:value={$form.parents[0].telephone}
|
||||||
bind:value={$form.parents[0].telephone}
|
placeholder="Telefon zákonného zástupce"
|
||||||
placeholder="Telefon zákonného zástupce"
|
/>
|
||||||
/>
|
</span>
|
||||||
</span>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{:else if pageIndex === 4}
|
{:else if pageIndex === 4}
|
||||||
<h1 class="text-sspsBlue mt-8 text-4xl font-semibold">Dobrovolné!</h1>
|
<h1 class="title mt-8">{pageTexts[4]}</h1>
|
||||||
<p class="text-sspsGray mt-8 block text-center font-light">
|
<p class="description mt-8 block text-center">
|
||||||
V případě, že máte druhého zákonného zástupce (např. otec a matka), můžete jej zde zadat.
|
Zde můžete zadat údaje o druhém zákonném zástupci. Škole tím umožníte lépe komunikovat.
|
||||||
</p>
|
</p>
|
||||||
<div class="flex w-full flex-col">
|
<div class="flex w-full flex-col">
|
||||||
<span class="mt-8 w-full">
|
<span class="field">
|
||||||
<NameField
|
<NameField
|
||||||
error={$typedErrors['parents'][1]['name'] || $typedErrors['parents'][1]['surname']}
|
error={$typedErrors['parents'][1]['name'] || $typedErrors['parents'][1]['surname']}
|
||||||
on:change={handleChange}
|
on:change={handleChange}
|
||||||
bind:valueName={$form.parents[1].name}
|
bind:valueName={$form.parents[1].name}
|
||||||
bind:valueSurname={$form.parents[1].surname}
|
bind:valueSurname={$form.parents[1].surname}
|
||||||
placeholder="Jméno a příjmení zákonného zástupce"
|
placeholder="Jméno a příjmení zákonného zástupce (nepovinné)"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<div class="mt-8 flex flex-row items-center md:flex-col">
|
<span class="field">
|
||||||
<span class="w-full">
|
<EmailField
|
||||||
<EmailField
|
error={$typedErrors['parents'][1]['email']}
|
||||||
error={$typedErrors['parents'][1]['email']}
|
on:change={handleChange}
|
||||||
on:change={handleChange}
|
bind:value={$form.parents[1].email}
|
||||||
bind:value={$form.parents[1].email}
|
placeholder="E-mail zákonného zástupce (nepovinné)"
|
||||||
placeholder="E-mail zákonného zástupce"
|
/>
|
||||||
/>
|
</span>
|
||||||
|
<span class="field">
|
||||||
|
<TelephoneField
|
||||||
|
error={$typedErrors['parents'][1]['telephone']}
|
||||||
|
on:change={handleChange}
|
||||||
|
bind:value={$form.parents[1].telephone}
|
||||||
|
placeholder="Telefon zákonného zástupce (nepovinné)"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span class="ml-2 w-full md:ml-0 md:mt-8">
|
|
||||||
<TelephoneField
|
|
||||||
error={$typedErrors['parents'][1]['telephone']}
|
|
||||||
on:change={handleChange}
|
|
||||||
bind:value={$form.parents[1].telephone}
|
|
||||||
placeholder="Telefon zákonného zástupce"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{:else if pageIndex === 5}
|
{:else if pageIndex === 5}
|
||||||
<h1 class="text-sspsBlue mt-8 text-4xl font-semibold">Poslední krok</h1>
|
<h1 class="title mt-8">{pageTexts[5]}</h1>
|
||||||
<p class="text-sspsGray mt-8 block text-center font-light">
|
<p class="description mt-8 block text-center">
|
||||||
Zadejte prosím své občanství, rodné číslo a obor na který se hlásíte.
|
Zadejte prosím své občanství, rodné číslo a obor na který se hlásíte.
|
||||||
</p>
|
</p>
|
||||||
<div class="flex w-full flex-row md:flex-col">
|
<div class="flex w-full flex-row md:flex-col">
|
||||||
<span class="mt-8 w-full">
|
<span class="field">
|
||||||
<SelectField
|
<SelectField
|
||||||
error={$typedErrors['candidate']['citizenship']}
|
error={$typedErrors['candidate']['citizenship']}
|
||||||
on:change={handleChange}
|
on:change={handleChange}
|
||||||
|
|
@ -417,11 +519,11 @@
|
||||||
options={['Česká republika', 'Slovenská republika', 'Ukrajina', 'Jiné']}
|
options={['Česká republika', 'Slovenská republika', 'Ukrajina', 'Jiné']}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span class="mt-8 ml-2 w-full md:ml-0">
|
<span class="field ml-2 md:ml-0">
|
||||||
<TextField on:change={handleChange} type="text" placeholder="Evidenční číslo přihlášky" />
|
<TextField on:change={handleChange} type="text" placeholder="Evidenční číslo přihlášky" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-8 flex w-full items-center justify-center">
|
<div class="field flex items-center justify-center">
|
||||||
{#if $form.candidate.citizenship === 'Česká republika' || !$form.candidate.citizenship}
|
{#if $form.candidate.citizenship === 'Česká republika' || !$form.candidate.citizenship}
|
||||||
<IdField
|
<IdField
|
||||||
error={$typedErrors['candidate']['personalIdNumber']}
|
error={$typedErrors['candidate']['personalIdNumber']}
|
||||||
|
|
@ -448,53 +550,51 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="mt-8 w-full">
|
|
||||||
<Submit
|
|
||||||
on:click={async (e) => {
|
|
||||||
await handleSubmit(e);
|
|
||||||
console.log('clicked ' + isPageInvalid());
|
|
||||||
if (isPageInvalid()) return;
|
|
||||||
if (pageIndex === pageCount) {
|
|
||||||
} else {
|
|
||||||
pagesFilled[pageIndex] = true;
|
|
||||||
pageIndex++;
|
|
||||||
}
|
|
||||||
// @ts-ignore
|
|
||||||
errors.set(formInitialValues);
|
|
||||||
}}
|
|
||||||
value={pageIndex === pageCount ? 'Odeslat' : 'Pokračovat'}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="controls w-full absolute bottom-1/12">
|
||||||
<div class="mt-8 flex flex-row justify-center">
|
<div class="field">
|
||||||
{#each Array(pageCount + 1) as _, i}
|
<Submit
|
||||||
<button
|
|
||||||
class:dotActive={i === pageIndex}
|
|
||||||
on:click={async (e) => {
|
on:click={async (e) => {
|
||||||
const progress = pagesFilled.slice(0, i).every((item) => item === true);
|
await handleSubmit(e);
|
||||||
if (progress) {
|
if (isPageInvalid(pageIndex)) return;
|
||||||
if (i > pageIndex) {
|
if (pageIndex === pageCount) {
|
||||||
// if next page is clicked, validate current page
|
} else {
|
||||||
await handleSubmit(e);
|
pagesFilled[pageIndex] = true;
|
||||||
if (isPageInvalid()) return;
|
pageIndex++;
|
||||||
pagesFilled[i] = true;
|
}
|
||||||
pageIndex++;
|
// @ts-ignore
|
||||||
} else {
|
errors.set(formInitialValues);
|
||||||
|
}}
|
||||||
|
value={pageIndex === pageCount ? 'Odeslat' : 'Pokračovat'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-4 md:mt-8 flex flex-row justify-center">
|
||||||
|
{#each Array(pageCount + 1) as _, i}
|
||||||
|
<button
|
||||||
|
class:dotActive={i === pageIndex}
|
||||||
|
on:click={async (e) => {
|
||||||
|
pageIndex -= pageIndex === pageCount ? 1 : 0;
|
||||||
|
await handleSubmit(e);
|
||||||
|
pagesFilled = pagesFilled.map((_, i) => !isPageInvalid(i));
|
||||||
|
|
||||||
|
const progress = pagesFilled.slice(0, i).every((item) => item === true);
|
||||||
|
if (progress) {
|
||||||
pageIndex = i;
|
pageIndex = i;
|
||||||
}
|
}
|
||||||
// @ts-ignore
|
}}
|
||||||
errors.set(formInitialValues);
|
class="dot"
|
||||||
}
|
/>
|
||||||
}}
|
{/each}
|
||||||
class="dot"
|
</div>
|
||||||
/>
|
|
||||||
{/each}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</SplitLayout>
|
</SplitLayout>
|
||||||
|
|
||||||
<style lang="postcss">
|
<style lang="postcss">
|
||||||
|
.field {
|
||||||
|
@apply mt-4 md:mt-8 w-full;
|
||||||
|
}
|
||||||
.form {
|
.form {
|
||||||
@apply flex flex-col;
|
@apply flex flex-col;
|
||||||
@apply mx-auto h-full w-[90%];
|
@apply mx-auto h-full w-[90%];
|
||||||
|
|
@ -513,4 +613,10 @@
|
||||||
.dotActive {
|
.dotActive {
|
||||||
@apply bg-sspsBlue;
|
@apply bg-sspsBlue;
|
||||||
}
|
}
|
||||||
|
.description {
|
||||||
|
@apply text-gray-500;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
@apply text-sspsBlue text-4xl font-semibold text-center;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@
|
||||||
input {
|
input {
|
||||||
@apply text-sspsBlue text-center font-semibold;
|
@apply text-sspsBlue text-center font-semibold;
|
||||||
@apply transition-colors duration-300;
|
@apply transition-colors duration-300;
|
||||||
@apply focus:border-sspsBlue hover:border-sspsBlue rounded-xl border border-2 bg-[#f8fafb] p-3 caret-transparent shadow-lg outline-none;
|
@apply focus:border-sspsBlue hover:border-sspsBlue rounded-xl border border-2 bg-[#f8fafb] p-3 md:caret-transparent shadow-lg outline-none;
|
||||||
}
|
}
|
||||||
.separater {
|
.separater {
|
||||||
@apply bg-sspsBlue mr-2 hidden h-2 w-8 md:block;
|
@apply bg-sspsBlue mr-2 hidden h-2 w-8 md:block;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue