diff --git a/package-lock.json b/package-lock.json index fd56f013..e5e58807 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3111,6 +3111,11 @@ "randomfill": "^1.0.3" } }, + "crypto-js": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.3.0.tgz", + "integrity": "sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q==" + }, "css-blank-pseudo": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz", @@ -6185,6 +6190,11 @@ "yallist": "^2.1.2" } }, + "lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=" + }, "magic-string": { "version": "0.22.5", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz", @@ -9603,6 +9613,15 @@ "xmlchars": "^2.1.1" } }, + "secure-ls": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/secure-ls/-/secure-ls-1.2.6.tgz", + "integrity": "sha512-g8vUSKl6elSfyAUHodybnNkuZW+mUYEOWj4SZIDg+xoQ1dq5ddktBoOFrtxQBUl88ZyAJOtGWQ1PRaOxkTAuZQ==", + "requires": { + "crypto-js": "^3.1.6", + "lz-string": "^1.4.4" + } + }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", diff --git a/package.json b/package.json index 63835afa..fdf892d1 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,8 @@ "bootstrap": "^4.5.3", "pdf-lib": "^1.11.2", "qrcode": "^1.4.4", - "remove-accents": "^0.4.2" + "remove-accents": "^0.4.2", + "secure-ls": "^1.2.6" }, "browserslist": [ "last 5 versions" diff --git a/src/css/main.css b/src/css/main.css index 17fddff9..0a35892d 100644 --- a/src/css/main.css +++ b/src/css/main.css @@ -154,6 +154,10 @@ p { transform: translateY(-2px); } +#form-profile #formgroup-storedata { + user-select: none; +} + @media (prefers-color-scheme: dark) { #form-profile .form-radio-label .form-check-label { color: #ddd; @@ -352,6 +356,10 @@ input[type=number] { border-radius: 0.5em; } +.delete-data-link { + text-align: center; +} + .btn-attestation:hover { background-color: #3031C1; } @@ -581,7 +589,7 @@ input[type=number] { } } -#snackbar { +#snackbar, #snackbar-cleardata { min-width: 250px; color: #fff; text-align: center; @@ -598,7 +606,7 @@ input[type=number] { transition: all 0.5s ease-in-out; } -#snackbar.show { +#snackbar.show, #snackbar-cleardata.show { opacity: 1; } diff --git a/src/index.html b/src/index.html index a85589a2..16084f61 100644 --- a/src/index.html +++ b/src/index.html @@ -46,8 +46,8 @@

- En application du décret n°2020-1310 du 29 octobre 2020 prescrivant les mesures générales - nécessaires pour faire face à l'épidémie de Covid19 dans le cadre de l'état d'urgence sanitaire + En application du décret n°2020-1310 du 29 octobre 2020 prescrivant les mesures générales + nécessaires pour faire face à l'épidémie de Covid19 dans le cadre de l'état d'urgence sanitaire

@@ -59,16 +59,43 @@

id="alert-facebook" >

-
-
-

- -

+
+
+

+ +

L'attestation est téléchargée sur votre appareil.
+ +
+ + +
+ + Effacer les données du formulaire +
+ Les données du formulaire ont été effacées.
+
+

diff --git a/src/js/form-util.js b/src/js/form-util.js index 4ce61869..d052d439 100644 --- a/src/js/form-util.js +++ b/src/js/form-util.js @@ -4,6 +4,11 @@ import { $, $$, downloadBlob } from './dom-utils' import { addSlash, getFormattedDate } from './util' import pdfBase from '../certificate.pdf' import { generatePdf } from './pdf-util' +import SecureLS from 'secure-ls' + +const secureLS = new SecureLS({ encodingType: 'aes' }) +const clearDataSnackbar = $('#snackbar-cleardata') +const storeDataInput = $('#field-storedata') const conditions = { '#field-firstname': { @@ -55,6 +60,46 @@ function validateAriaFields () { .includes(true) } +function updateSecureLS (formInputs, reasonInputs) { + if (wantDataToBeStored() === true) { + secureLS.set('profile', getProfile(formInputs)) + secureLS.set('reason', getReasonsObject(reasonInputs)) + } else { + clearSecureLS() + } +} + +function clearSecureLS () { + secureLS.clear() +} + +function clearForm () { + const formProfile = $('#form-profile') + formProfile.reset() + storeDataInput.checked = false +} + +function setCurrentDate (releaseDateInput, releaseTimeInput) { + const currentDate = new Date() + + releaseDateInput.value = getFormattedDate(currentDate) + releaseTimeInput.value = currentDate.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' }) +} + +function showSnackbar (snackbarToShow, showDuration = 6000) { + snackbarToShow.classList.remove('d-none') + setTimeout(() => snackbarToShow.classList.add('show'), 100) + + setTimeout(function () { + snackbarToShow.classList.remove('show') + setTimeout(() => snackbarToShow.classList.add('d-none'), 500) + }, showDuration) +} + +export function wantDataToBeStored () { + return storeDataInput.checked +} + export function setReleaseDateTime (releaseDateInput) { const loadedDate = new Date() releaseDateInput.value = getFormattedDate(loadedDate) @@ -90,8 +135,39 @@ export function getReasons (reasonInputs) { return reasons } -export function prepareInputs (formInputs, reasonInputs, reasonFieldset, reasonAlert, snackbar) { +export function getReasonsObject (reasonInputs) { + return reasonInputs + .filter((reason) => reason.checked) + .reduce((map, reason) => { + map[reason.value] = reason.checked + return map + }, {}) +} + +export function prepareInputs (formInputs, reasonInputs, reasonFieldset, reasonAlert, snackbar, releaseDateInput, releaseTimeInput) { + const lsProfile = secureLS.get('profile') + const lsReason = secureLS.get('reason') + const currentDate = new Date() + const formattedDate = getFormattedDate(currentDate) + const formattedTime = currentDate.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' }) + + // Continue to store data if already stored + storeDataInput.checked = lsReason || lsProfile + formInputs.forEach((input) => { + switch (input.name) { + case 'datesortie': + input.value = formattedDate + break + case 'heuresortie': + input.value = formattedTime + break + case 'field-reason': + if (lsReason) input.checked = lsReason[input.value] + break + default: + if (lsProfile) input.value = lsProfile[input.name] + } const exempleElt = input.parentNode.parentNode.querySelector('.exemple') const validitySpan = input.parentNode.parentNode.querySelector('.validity') if (input.placeholder && exempleElt) { @@ -123,6 +199,13 @@ export function prepareInputs (formInputs, reasonInputs, reasonFieldset, reasonA }) }) + $('#cleardata').addEventListener('click', () => { + clearSecureLS() + clearForm() + setCurrentDate(releaseDateInput, releaseTimeInput) + showSnackbar(clearDataSnackbar, 1200) + }) + $('#generate-btn').addEventListener('click', async (event) => { event.preventDefault() @@ -139,6 +222,8 @@ export function prepareInputs (formInputs, reasonInputs, reasonFieldset, reasonA return } + updateSecureLS(formInputs, reasonInputs) + const pdfBlob = await generatePdf(getProfile(formInputs), reasons, pdfBase) const creationInstant = new Date() @@ -166,6 +251,7 @@ export function prepareForm () { const reasonFieldset = $('#reason-fieldset') const reasonAlert = reasonFieldset.querySelector('.msg-alert') const releaseDateInput = $('#field-datesortie') + const releaseTimeInput = $('#field-heuresortie') setReleaseDateTime(releaseDateInput) - prepareInputs(formInputs, reasonInputs, reasonFieldset, reasonAlert, snackbar) + prepareInputs(formInputs, reasonInputs, reasonFieldset, reasonAlert, snackbar, releaseDateInput, releaseTimeInput) } diff --git a/src/js/form.js b/src/js/form.js index e6725ea6..085b9de9 100644 --- a/src/js/form.js +++ b/src/js/form.js @@ -11,12 +11,6 @@ const createTitle = () => { const p = createElement('p', { className: 'msg-info', innerHTML: 'Tous les champs sont obligatoires.' }) return [h2, p] } -// createElement('div', { className: 'form-group' }) - -const getCurrentTime = () => { - const date = new Date() - return date.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' }) -} const createFormGroup = ({ autocomplete = false, @@ -60,10 +54,6 @@ const createFormGroup = ({ const input = createElement('input', inputAttrs) - if (name === 'heuresortie') { - input.value = getCurrentTime() - } - const validityAttrs = { className: 'validity', }