From 3f5e16fa7ee63879e744928669924030a0b06717 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Thu, 2 Feb 2023 17:55:59 +0100 Subject: [PATCH 01/13] chore: add svelte-tel-input dependency --- frontend/package.json | 1 + frontend/pnpm-lock.yaml | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/frontend/package.json b/frontend/package.json index 73a5d99..97a8e9a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -44,6 +44,7 @@ "isomorphic-dompurify": "^0.26.0", "just-debounce-it": "^3.2.0", "svelte-forms-lib": "^2.0.1", + "svelte-tel-input": "^1.1.2", "svelte-tippy": "^1.3.2", "swiper": "^8.4.6", "tippy.js": "^6.3.7", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 9cfed88..5cc4fcc 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -25,6 +25,9 @@ dependencies: svelte-forms-lib: specifier: ^2.0.1 version: 2.0.1 + svelte-tel-input: + specifier: ^1.1.2 + version: 1.1.2 svelte-tippy: specifier: ^1.3.2 version: 1.3.2 @@ -1677,6 +1680,10 @@ packages: type-check: 0.4.0 dev: true + /libphonenumber-js@1.10.19: + resolution: {integrity: sha512-MDZ1zLIkfSDZV5xBta3nuvbEOlsnKCPe4z5r3hyup/AXveevkl9A1eSWmLhd2FX4k7pJDe4MrLeQsux0HI/VWg==} + dev: false + /locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -2307,6 +2314,14 @@ packages: typescript: 4.9.4 dev: true + /svelte-tel-input@1.1.2: + resolution: {integrity: sha512-KJxV/4h2JJ6b2Gh6WfXwfukXcW02SHai65jZAfV6DuT3Mu0zhfZ7Lu4iyi9PAOaLQzEVQ3UpwBEBXH3B7YCIog==} + engines: {node: '>= 16', npm: '>= 7', pnpm: '>= 7', yarn: '>=2'} + dependencies: + libphonenumber-js: 1.10.19 + svelte: 3.55.1 + dev: false + /svelte-tippy@1.3.2: resolution: {integrity: sha512-41f+85hwhKBRqX0UNYrgFsi34Kk/KDvUkIZXYANxkWoA2NTVTCZbUC2J8hRNZ4TRVxObTshoZRjK2co5+i6LMw==} dependencies: @@ -2326,7 +2341,6 @@ packages: /svelte@3.55.1: resolution: {integrity: sha512-S+87/P0Ve67HxKkEV23iCdAh/SX1xiSfjF1HOglno/YTbSTW7RniICMCofWGdJJbdjw3S+0PfFb1JtGfTXE0oQ==} engines: {node: '>= 8'} - dev: true /swiper@8.4.6: resolution: {integrity: sha512-HACW035vBz2T6Kfut23EAzXhcDpgR8doX+wjq0ZUvJgS5SQApGrV885DAPLBFnmPUISsAhNSVxPKDxqroFvXvQ==} From bde8e8da99d77c8ee9ebc35da446549a02187e12 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Thu, 2 Feb 2023 18:03:19 +0100 Subject: [PATCH 02/13] feat: telephone input for every country --- .../textfield/TelephoneField.svelte | 117 +++++++++++++----- .../(authenticated)/register/+page.svelte | 64 ++++++---- 2 files changed, 129 insertions(+), 52 deletions(-) diff --git a/frontend/src/lib/components/textfield/TelephoneField.svelte b/frontend/src/lib/components/textfield/TelephoneField.svelte index b8eed07..89d87c7 100644 --- a/frontend/src/lib/components/textfield/TelephoneField.svelte +++ b/frontend/src/lib/components/textfield/TelephoneField.svelte @@ -1,39 +1,100 @@ - -
- + +
+ + + +
- +
+ select { + @apply h-full pl-3 pr-3 border-1 w-2/5 rounded; + @apply hover:border-sspsBlue rounded-lg border border-2 bg-[#f8fafb] p-3 text-xl shadow-lg outline-none transition-colors duration-300; + } + .inputWrapper { + @apply w-full relative; + } + .inputWrapper span { + @apply absolute right-0 top-1 bottom-0 my-auto flex bg-transparent p-3; + } + .wrapper :global(.basic-tel-input) { + /* height: 32px; + padding-left: 12px; + padding-right: 12px; + border-radius: 6px; + border: 1px solid; + outline: none; + width: 100%; */ + /* @apply h-full pl-3 pr-3 border-1 w-full rounded; */ + @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; + } + + .wrapper :global(.invalid) { + /* border-color: red; */ + @apply border-red-700; + } + \ No newline at end of file diff --git a/frontend/src/routes/(candidate)/(authenticated)/register/+page.svelte b/frontend/src/routes/(candidate)/(authenticated)/register/+page.svelte index 7e0382d..af56e7b 100644 --- a/frontend/src/routes/(candidate)/(authenticated)/register/+page.svelte +++ b/frontend/src/routes/(candidate)/(authenticated)/register/+page.svelte @@ -18,7 +18,7 @@ import { SvelteToast, toast } from '@zerodevx/svelte-toast'; import { createForm } from 'svelte-forms-lib'; - import type { Writable } from 'svelte/store'; + import { writable, type Writable } from 'svelte/store'; import * as yup from 'yup'; import type { CandidateData } from '$lib/stores/candidate'; import AccountLinkCheckBox from '$lib/components/checkbox/AccountLinkCheckBox.svelte'; @@ -46,7 +46,19 @@ export let data: PageData; let details = data.candidate; let baseCandidateDetails = data.whoami; - + let componentErrors = writable( { + candidate: { + telephone: false, + }, + parents: [ + { + telephone: false, + }, + { + telephone: false, + } + ] + }); let personalIdBirthdateMatch = true; const formInitialValues = { gdpr: false, @@ -106,8 +118,7 @@ email: yup.string().email().required(), telephone: yup .string() - .required() - .matches(/^\+\d{1,3} \d{3} \d{3} \d{3}$/), + .required(), // already validated by the 'TelephoneField' component birthplace: yup.string().required(), birthdate: yup .string() @@ -286,7 +297,7 @@ onSubmit: async (values: CandidateData) => onSubmit(values) }); - + $: console.log($componentErrors['candidate']['telephone']) const isPageInvalid = (index: number): boolean => { switch (index) { case 0: @@ -311,7 +322,8 @@ $typedErrors['candidate']['name'] || $typedErrors['candidate']['surname'] || $typedErrors['candidate']['email'] || - $typedErrors['candidate']['telephone'] || + // $typedErrors['candidate']['telephone'] || + $componentErrors['candidate']['telephone'] || $typedErrors['candidate']['city'] || $typedErrors['candidate']['street'] || $typedErrors['candidate']['houseNumber'] || @@ -341,7 +353,8 @@ $typedErrors['parents'][0]['name'] || $typedErrors['parents'][0]['surname'] || $typedErrors['parents'][0]['email'] || - $typedErrors['parents'][0]['telephone'] + // $typedErrors['parents'][0]['telephone'] + $componentErrors['parents'][0]['telephone'] ) { return true; } @@ -351,7 +364,8 @@ $typedErrors['parents'][1]['name'] || $typedErrors['parents'][1]['surname'] || $typedErrors['parents'][1]['email'] || - $typedErrors['parents'][1]['telephone'] + // $typedErrors['parents'][1]['telephone'] + $componentErrors['parents'][1]['telephone'] ) { return true; } @@ -511,6 +525,14 @@ /> + + + +
- - +
- - - +
@@ -675,7 +691,7 @@ @@ -704,7 +720,7 @@ From 6911c7f6ac8409b50c62dfbce98dfab399684fb3 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Thu, 2 Feb 2023 18:11:32 +0100 Subject: [PATCH 03/13] feat: tel input placeholder --- .../src/lib/components/textfield/TelephoneField.svelte | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/frontend/src/lib/components/textfield/TelephoneField.svelte b/frontend/src/lib/components/textfield/TelephoneField.svelte index 89d87c7..788a30c 100644 --- a/frontend/src/lib/components/textfield/TelephoneField.svelte +++ b/frontend/src/lib/components/textfield/TelephoneField.svelte @@ -63,8 +63,11 @@ {/each}
- - + +
@@ -78,7 +81,7 @@ .inputWrapper { @apply w-full relative; } - .inputWrapper span { + .tel-icon { @apply absolute right-0 top-1 bottom-0 my-auto flex bg-transparent p-3; } .wrapper :global(.basic-tel-input) { From c8ced5f79d7625da4ad6f69c6a47b303145edc1e Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Thu, 2 Feb 2023 18:13:43 +0100 Subject: [PATCH 04/13] fix: city input margin --- frontend/src/routes/(candidate)/auth/login/+page.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/routes/(candidate)/auth/login/+page.svelte b/frontend/src/routes/(candidate)/auth/login/+page.svelte index 6920fa8..19d9be2 100644 --- a/frontend/src/routes/(candidate)/auth/login/+page.svelte +++ b/frontend/src/routes/(candidate)/auth/login/+page.svelte @@ -21,7 +21,7 @@

{$LL.candidate.auth.login.title()}

-

+

{$LL.candidate.auth.login.description()}

From 911a5f6ee2e1d48a1b2c67b983c3f1dd003e3c76 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Thu, 2 Feb 2023 18:34:33 +0100 Subject: [PATCH 05/13] fix: parent telephone validation --- .../(authenticated)/register/+page.svelte | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/frontend/src/routes/(candidate)/(authenticated)/register/+page.svelte b/frontend/src/routes/(candidate)/(authenticated)/register/+page.svelte index af56e7b..111a235 100644 --- a/frontend/src/routes/(candidate)/(authenticated)/register/+page.svelte +++ b/frontend/src/routes/(candidate)/(authenticated)/register/+page.svelte @@ -185,12 +185,7 @@ } return _val !== ''; }), - telephone: yup.string().test((_val, context) => { - if (context.path.includes('parents[1]') && _val === '') { - return true; - } - return _val?.match(/^\+\d{1,3} \d{3} \d{3} \d{3}$/) !== null; - }) + telephone: yup.string() }) ) }); @@ -222,6 +217,9 @@ }; const onSubmit = async (values: CandidateData) => { + console.log("submit button clicked"); + console.log(pagesFilled.map((_, i) => !isPageInvalid(i))); + if (pageIndex === 3) { if (values.candidate.citizenship === 'Česká republika') { if ( @@ -297,7 +295,6 @@ onSubmit: async (values: CandidateData) => onSubmit(values) }); - $: console.log($componentErrors['candidate']['telephone']) const isPageInvalid = (index: number): boolean => { switch (index) { case 0: @@ -541,7 +538,7 @@ placeholder={$LL.input.email()} /> - + { await handleSubmit(e); + console.log(pagesFilled.map((_, i) => !isPageInvalid(i))); if (isPageInvalid(pageIndex)) return; if (pageIndex === pageCount) { } else { From 915708f2e8395b3552d5d80707435d2e70d3c57f Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Thu, 2 Feb 2023 18:45:34 +0100 Subject: [PATCH 06/13] fix: personalId validation --- .../(authenticated)/register/+page.svelte | 57 +++++++++++-------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/frontend/src/routes/(candidate)/(authenticated)/register/+page.svelte b/frontend/src/routes/(candidate)/(authenticated)/register/+page.svelte index 111a235..d02da74 100644 --- a/frontend/src/routes/(candidate)/(authenticated)/register/+page.svelte +++ b/frontend/src/routes/(candidate)/(authenticated)/register/+page.svelte @@ -49,6 +49,7 @@ let componentErrors = writable( { candidate: { telephone: false, + personalIdMatch: false, }, parents: [ { @@ -59,7 +60,7 @@ } ] }); - let personalIdBirthdateMatch = true; + const formInitialValues = { gdpr: false, personalIdOk: false, @@ -215,32 +216,32 @@ 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' + } + }); + $componentErrors['candidate']['personalIdMatch'] = true; + throw new Error('Rodné číslo neodpovídá datumu narození'); + } + } + $componentErrors['candidate']['personalIdMatch'] = false; + } const onSubmit = async (values: CandidateData) => { console.log("submit button clicked"); console.log(pagesFilled.map((_, i) => !isPageInvalid(i))); - 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 @@ -340,7 +341,7 @@ $typedErrors['candidate']['birthplace'] || $typedErrors['candidate']['personalIdNumber'] || $typedErrors['candidate']['testLanguage'] || - !personalIdBirthdateMatch + $componentErrors['candidate']['personalIdMatch'] ) { return true; } @@ -759,11 +760,14 @@
{ + 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) { - } else { + if (pageIndex !== pageCount) { pagesFilled[pageIndex] = true; pageIndex++; } @@ -779,6 +783,9 @@