Compare commits

..

2 Commits

Author SHA1 Message Date
LAB-MI 6a5f269560 Automated Release v1.0.1
Please find changes in CHANGELOG.md file
2020-04-16 12:57:25 +00:00
LAB-MI 86b15a4d6e Automated Release v1.0.0
Please find changes in CHANGELOG.md file
2020-04-16 08:15:53 +00:00
25 changed files with 1267 additions and 292 deletions

View File

@ -9,6 +9,7 @@ module.exports = {
"no-console": process.env.NODE_ENV === "production" ? "error" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
"comma-dangle": [2, "always-multiline"],
"no-var": 2,
},
overrides: [
{

View File

@ -1,45 +1,5 @@
# Changelog
## [1.0.13] - 2020-04-06
## [1.0.0] - 2020-04-15
### Added
- first public release
## [1.1.0] - 2020-04-08
### Added
- (#21, #23, #25, #26, #27, #28, #29) :wheelchair: improve A11Y support
- adds github links in footer
- (#19) clear localstorage
- prevent sending personal data
- 💚 Publish html as workflow asset and code in public repository
- Add sitemap and tell robots.txt to scan the sitemap
- Add Changelog
### Changes
- (#18) 📝 Update header for reasons section
- (#3) ✏ Replace codiv by covid in package.json, README
- changes start target
- (#1) 🐛 Autocomplete birthday field only on keyup, not when deleting
- (#1) 🎨 Improve regex control pattern for birhtday
- :recycle: Move check update code to dedicated file
- :recycle: Move dom utils in dedicated file
- :sparkles: Notify user about newer version
- :sparkles: Add cache with service workers
- :recycle: Cleanup directory structure
- :heavy_plus_sign: Avoid using env specific syntax in npm scripts
- changes PWA display
## [1.1.1] - 2020-04-09
### Fixed
- :wrench: Fix parcel-plugin-sw-cache configuration
### Added
- :pencil: add CONTRIBUTORS
### Changes
- :pencil: change LICENCE ( #16 )
## [1.1.2] - 2020-04-10
### Added
- ♿ Increase contrast ratio hover link color (#29)
- ♿ Add landmarks roles to header, main and footer sections (#43)
- ♿ Add title to open stores link and add more explicit text (#44, #45)
- ♿ Expose aria-invalid field assistance technologies (#24)
- ✨ Add timestamp to generated pdf (#9)
- 💄 Don't use autocompletion for release date and time (#31)
- 💄 Add underline on hover links
### Fixes
- Fix Typo site.webmanifest (#52)

View File

@ -1,20 +1,13 @@
Ce générateur d'attestation de déplacement dérogatoire a été mis en place dans le cadre du confinement lié à la pandémie du virus COVID-19 de 2020.
Ce service repose sur l'utilisation initale du projet covid-19-certificate <https://github.com/nesk/covid-19-certificate> de Johann Pardanaud <https://github.com/nesk>.
Ce générateur d'attestation de déplacement international dérogatoire a été mis en place dans le cadre du confinement lié à la pandémie du virus COVID-19 de 2020.
Ce service repose sur l'utilisation initale du projet covid-19-certificate <https://github.com/nesk/covid-19-certificate> de Johann Pardanaud <https://github.com/nesk> et de l'attestation de déplacement nationale dérogatoire <https://github.com/LAB-MI/deplacement-covid-19> du ministère de l'intérieur.
Il a été enrichi par l'incubateur du ministère de l'intérieur : le LAB-MI <https://beta.interieur.gouv.fr>.
La liste qui suit mentionne les différents participants ayant contribué à rendre ce service utile à la fois à la population et aux forces de l'ordre :
Johann Pardanaud (https://github.com/nesk)
La liste qui suit mentionne les différents participants ayant contribué à rendre ce service utile à la fois à la population et aux forces de l'ordre :
Philippe Bron (https://github.com/PhilippeBron)
Cristian (https://github.com/cristianpb)
Stanislas Ormières (https://github.com/laruiss)
Caroline Robillard (https://github.com/Carolinedanslesnuages)
Joel Pagniez (https://github.com/JoelPagniez)
Sophie GUERLAIS
Victor Journé (https://github.com/victorjourne)
Philippe (https://github.com/pli01)
Matthieu Bacconnier (https://github.com/Neamar)
Hugo Cartigny (https://github.com/BlueskyFR)
Sébastien Touzé (https://github.com/SebastienTouze)
John Livingston (https://github.com/JohnXLivingston)
David Libeau (https://github.com/DavidLibeau)
Arnaud Delafosse (https://github.com/ArnaudDelafosse)

View File

@ -1,4 +1,4 @@
# Générateur de certificat de déplacement
# Générateur: ATTESTATION DE DÉPLACEMENT INTERNATIONAL DÉROGATOIRE VERS LA FRANCE MÉTROPOLITAINE
## Développer

59
doc-tools/conception.md Normal file
View File

@ -0,0 +1,59 @@
# Attestation de déplacement international dérogatoire vers la France métropolitaine
## Fichiers pdf des attestations papier :
- version française : 07-04-20-Attestation-etranger-metropole-FR.pdf
- version anglaise : Attestation_deplacement_International_EN-1.pdf
## Analyse de la structure du document
### Champs ajoutés :
- nationalité
- typologie de nationalités : pays tiers ; eu ou assimilés ; française
### Champs supprimés :
- lieu de naissance
- date de sortie
- heure de sortie
### Remarques :
- Les motifs sont identiques pour les types tiers et eu.
- Pas de motif pour les français.
- Un seul motif possible défini en fonction du type de nationalité
## Structure des champs du QR Code
- **Cree le:** creationDate a creationHour;\n
- **Nom:** `firstname`;\n
- **Prenom:** `lastname`;\n
- **Naissance:** `birthday` (`nationality`);\n
- **Adresse:** `address` `zipcode` `town` `country`;\n
- **Sortie:** N/A;\n
- **Motifs:** `national`-`reason` (`reaseon` à vide pour les français)
### Correspondances formulaire/QR Code
Afin de pouvoir être sotckées dans le QR Code, à chaque choix du formulaire est associé un alias.
#### types de nationalités
| libellé formulaire | alias |
|:-----|:-----|
| Ressortissants de pays tiers | tiers |
| Ressortissants de lUnion européenne et assimilés | eu |
| Ressortissants de nationalité française | fr |
#### types de motifs
| libellé formulaire | alias |
|:-----|:-----|
| Personnes ayant leur résidence principale en France | residence |
| Personnes transitant par la France pour rejoindre leur résidence | transit |
| Professionnels de santé aux fins de lutter contre le Covid-19 | prof._sante |
| Transporteurs de marchandises | marchandises |
| Equipages et personnels exploitant des vols | equipage |
| Personnels des missions diplomatiques et consulaires | diplomatique |
| Travailleurs frontaliers | frontalier |

52
doc-tools/grid.html Normal file
View File

@ -0,0 +1,52 @@
<html>
<head>
<meta charset="utf-8" />
<script src="https://unpkg.com/pdf-lib@1.4.1"></script>
<script src="https://unpkg.com/downloadjs@1.4.7"></script>
</head>
<body>
<label for="file">Sélectionner le pdf</label>
<input type="file" id="file" name="file" onchange="generatePdf()">
</body>
<script>
const { PDFDocument, StandardFonts, rgb } = PDFLib
async function generatePdf () {
var pdfBase = document.getElementById("file").files[0];
var buffer = await pdfBase.arrayBuffer();
const pdfDoc = await PDFDocument.load(buffer)
const page1 = pdfDoc.getPages()[0]
const font = await pdfDoc.embedFont(StandardFonts.Helvetica)
const drawText = (text, x, y, size = 11) => {
page1.drawText(text, { x, y, size, font })
}
var x;
var y;
for (x = 25; x < 1000; x += 25) {
for (y = 25; y < 1000; y += 25) {
page1.drawText('.', { x: x, y: y, size: 11, font: font, color: rgb(0.95, 0.1, 0.1) })
page1.drawText(`${x}`, { x: x+3, y: y, size: 7, font: font, color: rgb(0, 0, 0) })
page1.drawText(`${y}`, { x: x+3, y: y-6, size: 7, font: font, color: rgb(0, 0, 0) })
}
}
pdfDoc.addPage()
const pdfBytes = await pdfDoc.save()
// Trigger the browser to download the PDF document
download(pdfBytes, "grid.pdf", "application/pdf");
}
</script>
</html>

2
package-lock.json generated
View File

@ -1,5 +1,5 @@
{
"name": "deplacement-covid-19",
"name": "deplacement-vers-france-covid-19",
"version": "0.0.1",
"lockfileVersion": 1,
"requires": true,

View File

@ -1,15 +1,16 @@
{
"name": "deplacement-covid-19",
"name": "deplacement-vers-france-covid-19",
"version": "0.0.1",
"description": "Générateur d'attestation de déplacement dérogatoire'",
"description": "Générateur d'attestation de déplacement international dérogatoire vers la France",
"main": "certificate.js",
"scripts": {
"lint": "eslint ./*.js",
"start": "cross-env VERSION=${VERSION:-localversion} parcel --public-url ${PUBLIC_URL:-/deplacement-covid-19} ./src/index.html",
"lint": "eslint src/*.js",
"format": "npm run lint -- --fix",
"start": "cross-env VERSION=${VERSION:-localversion} parcel --public-url ${PUBLIC_URL:-/deplacement-vers-france-covid-19} ./src/index.html",
"clean:dist": "rimraf dist",
"prebuild": "run-s lint clean:dist",
"build": "parcel build --public-url ${PUBLIC_URL:-/deplacement-covid-19} ./src/index.html ./src/robots.txt ./src/sitemap.xml",
"postbuild": "PUBLIC_URL=${PUBLIC_URL:-/deplacement-covid-19} react-snap",
"build": "parcel build --public-url ${PUBLIC_URL:-/deplacement-vers-france-covid-19} ./src/index.html ./src/index-en.html ./src/robots.txt ./src/sitemap.xml",
"postbuild": "PUBLIC_URL=${PUBLIC_URL:-/deplacement-vers-france-covid-19} react-snap",
"preserve": "npm run build",
"serve": "serve dist",
"serve:dist": "serve dist"
@ -19,7 +20,7 @@
"url": "git+https://github.com/lab-mi/deplacement-covid-19"
},
"keywords": [],
"author": "",
"author": "Ministère de l'Intérieur",
"license": "MIT",
"bugs": {
"url": "https://github.com/lab-mi/deplacement-covid-19/issues"
@ -55,7 +56,8 @@
"qrcode": "^1.4.4"
},
"browserslist": [
"last 5 versions"
"last 5 versions",
"ios_saf >= 7"
],
"reactSnap": {
"source": "dist",
@ -73,6 +75,9 @@
"strategy": "default",
"clearDist": false,
"templatedURLs": {
"./": ["index.html"]
} }
"./": [
"index.html"
]
}
}
}

Binary file not shown.

Binary file not shown.

13
src/Flag_of_France.svg Normal file
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 900 600" style="enable-background:new 0 0 900 600;" xml:space="preserve">
<style type="text/css">
.st0{fill:#ED2939;}
.st1{fill:#FFFFFF;}
.st2{fill:#002395;}
</style>
<rect y="0" class="st0" width="900" height="600"/>
<rect y="0" class="st1" width="600" height="600"/>
<rect y="0" class="st2" width="300" height="600"/>
</svg>

After

Width:  |  Height:  |  Size: 606 B

328
src/certificate-en.js Normal file
View File

@ -0,0 +1,328 @@
import 'bootstrap/dist/css/bootstrap.min.css'
import './main.css'
import { PDFDocument, StandardFonts } from 'pdf-lib'
import QRCode from 'qrcode'
import { library, dom } from '@fortawesome/fontawesome-svg-core'
import { faEye, faFilePdf } from '@fortawesome/free-solid-svg-icons'
import './check-updates'
import { $, $$ } from './dom-utils'
import pdfBase from './Attestation_deplacement_International_EN-1.pdf'
library.add(faEye, faFilePdf)
dom.watch()
$('#radio-language-fr').addEventListener('click', async event => {
window.location.href = `${process.env.PUBLIC_URL === '/' ? '' : process.env.PUBLIC_URL}/index.html`
})
const generateQR = async text => {
try {
const opts = {
errorCorrectionLevel: 'M',
type: 'image/png',
quality: 0.92,
margin: 1,
}
return await QRCode.toDataURL(text, opts)
} catch (err) {
console.error(err)
}
}
function saveProfile () {
for (const field of $$('#form-profile input')) {
localStorage.setItem(field.id.substring('field-'.length), field.value)
}
}
function getProfile () {
const fields = {}
for (let i = 0; i < localStorage.length; i++) {
const name = localStorage.key(i)
fields[name] = localStorage.getItem(name)
}
return fields
}
function idealFontSize (font, text, maxWidth, minSize, defaultSize) {
let currentSize = defaultSize
let textWidth = font.widthOfTextAtSize(text, defaultSize)
while (textWidth > maxWidth && currentSize > minSize) {
textWidth = font.widthOfTextAtSize(text, --currentSize)
}
return (textWidth > maxWidth) ? null : currentSize
}
async function generatePdf (profile, typeNationality, reasons) {
const creationDate = new Date().toLocaleDateString('fr-FR')
const creationHour = new Date().toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' }).replace(':', 'h')
const { lastname, firstname, birthday, nationality, address, zipcode, town, country } = profile
const data = [
`Cree le: ${creationDate} a ${creationHour}`,
`Nom: ${lastname}`,
`Prenom: ${firstname}`,
`Naissance: ${birthday} (${nationality})`,
`Adresse: ${address} ${zipcode} ${town} ${country}`,
'Sortie: N/A',
`Motifs: ${typeNationality}-${reasons}`,
].join(';\n ')
const existingPdfBytes = await fetch(pdfBase).then(res => res.arrayBuffer())
const pdfDoc = await PDFDocument.load(existingPdfBytes)
const page1 = pdfDoc.getPages()[0]
const font = await pdfDoc.embedFont(StandardFonts.Helvetica)
const drawText = (text, x, y, size = 11) => {
page1.drawText(text, { x, y, size, font })
}
drawText(`${firstname} ${lastname}`, 125, 590)
drawText(birthday, 125, 567)
drawText(nationality, 125, 545)
drawText(`${address} ${zipcode}`, 127, 527)
drawText(`${town}, ${country}`, 127, 505)
if (typeNationality === 'tiers') {
if (reasons.includes('residence')) {
drawText('x', 49, 449, 19)
}
if (reasons.includes('transit')) {
drawText('x', 49, 412, 19)
}
if (reasons.includes('prof._sante')) {
drawText('x', 49, 385, 19)
}
if (reasons.includes('marchandises')) {
drawText('x', 49, 370, 19)
}
if (reasons.includes('equipage')) {
drawText('x', 49, 354, 19)
}
if (reasons.includes('diplomatique')) {
drawText('x', 49, 339, 19)
}
if (reasons.includes('frontalier')) {
drawText('x', 49, 313, 19)
}
}
if (typeNationality === 'eu') {
if (reasons.includes('resident')) {
drawText('x', 49, 276, 19)
}
if (reasons.includes('transit')) {
drawText('x', 49, 263, 19)
}
if (reasons.includes('prof._sante')) {
drawText('x', 49, 250, 19)
}
if (reasons.includes('marchandises')) {
drawText('x', 49, 238, 19)
}
if (reasons.includes('equipage')) {
drawText('x', 49, 225, 19)
}
if (reasons.includes('diplomatique')) {
drawText('x', 49, 212, 19)
}
if (reasons.includes('frontalier')) {
drawText('x', 49, 189, 19)
}
}
if (typeNationality === 'fr') {
drawText('x', 49, 162, 19)
}
let locationSize = idealFontSize(font, profile.town, 83, 7, 11)
if (!locationSize) {
alert('Le nom de la ville risque de ne pas être affiché correctement en raison de sa longueur. ' +
'Essayez d\'utiliser des abréviations ("Saint" en "St." par exemple) quand cela est possible.')
locationSize = 7
}
// Fait à :
drawText(profile.town, 395, 142, locationSize)
// Le
drawText(`${new Date().toLocaleDateString('fr-FR', { month: 'numeric', day: 'numeric' })}`, 488, 142)
const generatedQR = await generateQR(data)
const qrImage = await pdfDoc.embedPng(generatedQR)
page1.drawImage(qrImage, {
x: 450,
y: 572,
width: 100,
height: 100,
})
pdfDoc.addPage()
const page2 = pdfDoc.getPages()[1]
page2.drawImage(qrImage, {
x: 50,
y: page2.getHeight() - 350,
width: 300,
height: 300,
})
const pdfBytes = await pdfDoc.save()
return new Blob([pdfBytes], { type: 'application/pdf' })
}
function downloadBlob (blob, fileName) {
const link = document.createElement('a')
const url = URL.createObjectURL(blob)
link.href = url
link.download = fileName
document.body.appendChild(link)
link.click()
}
function getAndSaveReasons () {
const values = $$('input[name="field-reason"]:checked')
.map(x => x.value)
.join('-')
localStorage.setItem('reasons', values)
return values
}
function getAndSaveTypeNationality () {
const typeNationality = $$('input[name="field-type-nationality"]:checked')
.map(x => x.value)
.join('-')
localStorage.setItem('typeNationality', typeNationality)
return typeNationality
}
// see: https://stackoverflow.com/a/32348687/1513045
function isFacebookBrowser () {
const ua = navigator.userAgent || navigator.vendor || window.opera
return ua.includes('FBAN') || ua.includes('FBAV')
}
if (isFacebookBrowser()) {
$('#alert-facebook').value = 'ATTENTION !! Vous utilisez actuellement le navigateur Facebook, ce générateur ne fonctionne pas correctement au sein de ce navigateur ! Merci d\'ouvrir Chrome sur Android ou bien Safari sur iOS.'
$('#alert-facebook').classList.remove('d-none')
}
function addSlash () {
$('#field-birthday').value = $('#field-birthday').value.replace(/^(\d{2})$/g, '$1/')
$('#field-birthday').value = $('#field-birthday').value.replace(/^(\d{2})\/(\d{2})$/g, '$1/$2/')
$('#field-birthday').value = $('#field-birthday').value.replace(/\/\//g, '/')
}
$('#field-birthday').onkeyup = function () {
const key = event.keyCode || event.charCode
if (key === 8 || key === 46) {
return false
} else {
addSlash()
return false
}
}
const snackbar = $('#snackbar')
$('#generate-btn').addEventListener('click', async event => {
event.preventDefault()
saveProfile()
const typeNationality = getAndSaveTypeNationality()
let reasons
if (typeNationality === 'fr') {
reasons = 'N/A'
} else {
reasons = getAndSaveReasons()
}
const pdfBlob = await generatePdf(getProfile(), typeNationality, reasons)
localStorage.clear()
const creationDate = new Date().toLocaleDateString('fr-CA')
const creationHour = new Date().toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' }).replace(':', '-')
downloadBlob(pdfBlob, `attestation-${creationDate}_${creationHour}.pdf`)
snackbar.classList.remove('d-none')
setTimeout(() => snackbar.classList.add('show'), 100)
setTimeout(function () {
snackbar.classList.remove('show')
setTimeout(() => snackbar.classList.add('d-none'), 500)
}, 6000)
})
$$('input').forEach(input => {
const exempleElt = input.parentNode.parentNode.querySelector('.exemple')
if (input.placeholder && exempleElt) {
input.addEventListener('input', (event) => {
if (input.value) {
exempleElt.innerHTML = 'ex.&nbsp;: ' + input.placeholder
} else {
exempleElt.innerHTML = ''
}
})
}
})
const conditions = {
'#field-firstname': {
condition: 'length',
},
'#field-lastname': {
condition: 'length',
},
'#field-birthday': {
condition: 'pattern',
pattern: /^([0][1-9]|[1-2][0-9]|30|31)\/([0][1-9]|10|11|12)\/(19[0-9][0-9]|20[0-1][0-9]|2020)/g,
},
'#field-nationality': {
condition: 'length',
},
'#field-address': {
condition: 'length',
},
'#field-town': {
condition: 'length',
},
'#field-zipcode': {
condition: 'lenght',
},
'#field-country': {
condition: 'length',
},
}
Object.keys(conditions).forEach(field => {
$(field).addEventListener('input', () => {
if (conditions[field].condition === 'pattern') {
const pattern = conditions[field].pattern
if ($(field).value.match(pattern)) {
$(field).setAttribute('aria-invalid', 'false')
} else {
$(field).setAttribute('aria-invalid', 'true')
}
}
if (conditions[field].condition === 'length') {
if ($(field).value.length > 0) {
$(field).setAttribute('aria-invalid', 'false')
} else {
$(field).setAttribute('aria-invalid', 'true')
}
}
})
})
function addVersion () {
document.getElementById('version').innerHTML = `${new Date().getFullYear()} - ${process.env.VERSION}`
}
addVersion()

View File

@ -9,17 +9,19 @@ import { faEye, faFilePdf } from '@fortawesome/free-solid-svg-icons'
import './check-updates'
import { $, $$ } from './dom-utils'
import pdfBase from './certificate.pdf'
import pdfBase from './07-04-20-Attestation-etranger-metropole-FR.pdf'
library.add(faEye, faFilePdf)
dom.watch()
var year, month, day
$('#radio-language-en').addEventListener('click', async event => {
window.location.href = `${process.env.PUBLIC_URL === '/' ? '' : process.env.PUBLIC_URL}/index-en.html`
})
const generateQR = async text => {
try {
var opts = {
const opts = {
errorCorrectionLevel: 'M',
type: 'image/png',
quality: 0.92,
@ -31,39 +33,9 @@ const generateQR = async text => {
}
}
function pad (str) {
return String(str).padStart(2, '0')
}
function setDateNow (date) {
year = date.getFullYear()
month = pad(date.getMonth() + 1) // Les mois commencent à 0
day = pad(date.getDate())
}
document.addEventListener('DOMContentLoaded', setReleaseDateTime)
function setReleaseDateTime () {
const loadedDate = new Date()
setDateNow(loadedDate)
const releaseDateInput = document.querySelector('#field-datesortie')
releaseDateInput.value = `${year}-${month}-${day}`
const hour = pad(loadedDate.getHours())
const minute = pad(loadedDate.getMinutes())
const releaseTimeInput = document.querySelector('#field-heuresortie')
releaseTimeInput.value = `${hour}:${minute}`
}
function saveProfile () {
for (const field of $$('#form-profile input')) {
if (field.id === 'field-datesortie') {
var dateSortie = field.value.split('-')
localStorage.setItem(field.id.substring('field-'.length), `${dateSortie[2]}/${dateSortie[1]}/${dateSortie[0]}`)
} else {
localStorage.setItem(field.id.substring('field-'.length), field.value)
}
localStorage.setItem(field.id.substring('field-'.length), field.value)
}
}
@ -87,23 +59,21 @@ function idealFontSize (font, text, maxWidth, minSize, defaultSize) {
return (textWidth > maxWidth) ? null : currentSize
}
async function generatePdf (profile, reasons) {
async function generatePdf (profile, typeNationality, reasons) {
const creationDate = new Date().toLocaleDateString('fr-FR')
const creationHour = new Date().toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' }).replace(':', 'h')
const { lastname, firstname, birthday, lieunaissance, address, zipcode, town, datesortie, heuresortie } = profile
const releaseHours = String(heuresortie).substring(0, 2)
const releaseMinutes = String(heuresortie).substring(3, 5)
const { lastname, firstname, birthday, nationality, address, zipcode, town, country } = profile
const data = [
`Cree le: ${creationDate} a ${creationHour}`,
`Nom: ${lastname}`,
`Prenom: ${firstname}`,
`Naissance: ${birthday} a ${lieunaissance}`,
`Adresse: ${address} ${zipcode} ${town}`,
`Sortie: ${datesortie} a ${releaseHours}h${releaseMinutes}`,
`Motifs: ${reasons}`,
].join('; ')
`Naissance: ${birthday} (${nationality})`,
`Adresse: ${address} ${zipcode} ${town} ${country}`,
'Sortie: N/A',
`Motifs: ${typeNationality}-${reasons}`,
].join(';\n ')
const existingPdfBytes = await fetch(pdfBase).then(res => res.arrayBuffer())
@ -115,31 +85,61 @@ async function generatePdf (profile, reasons) {
page1.drawText(text, { x, y, size, font })
}
drawText(`${firstname} ${lastname}`, 123, 686)
drawText(birthday, 123, 661)
drawText(lieunaissance, 92, 638)
drawText(`${address} ${zipcode} ${town}`, 134, 613)
drawText(`${firstname} ${lastname}`, 125, 590)
drawText(birthday, 125, 567)
drawText(nationality, 125, 545)
drawText(`${address} ${zipcode}`, 127, 527)
drawText(`${town}, ${country}`, 127, 505)
if (reasons.includes('travail')) {
drawText('x', 76, 527, 19)
if (typeNationality === 'tiers') {
if (reasons.includes('residence')) {
drawText('x', 49, 449, 19)
}
if (reasons.includes('transit')) {
drawText('x', 49, 412, 19)
}
if (reasons.includes('prof._sante')) {
drawText('x', 49, 385, 19)
}
if (reasons.includes('marchandises')) {
drawText('x', 49, 370, 19)
}
if (reasons.includes('equipage')) {
drawText('x', 49, 354, 19)
}
if (reasons.includes('diplomatique')) {
drawText('x', 49, 328, 19)
}
if (reasons.includes('frontalier')) {
drawText('x', 49, 302, 19)
}
}
if (reasons.includes('courses')) {
drawText('x', 76, 478, 19)
if (typeNationality === 'eu') {
if (reasons.includes('resident')) {
drawText('x', 49, 265, 19)
}
if (reasons.includes('transit')) {
drawText('x', 49, 249, 19)
}
if (reasons.includes('prof._sante')) {
drawText('x', 49, 234, 19)
}
if (reasons.includes('marchandises')) {
drawText('x', 49, 218, 19)
}
if (reasons.includes('equipage')) {
drawText('x', 49, 202, 19)
}
if (reasons.includes('diplomatique')) {
drawText('x', 49, 177, 19)
}
if (reasons.includes('frontalier')) {
drawText('x', 49, 150, 19)
}
}
if (reasons.includes('sante')) {
drawText('x', 76, 436, 19)
}
if (reasons.includes('famille')) {
drawText('x', 76, 400, 19)
}
if (reasons.includes('sport')) {
drawText('x', 76, 345, 19)
}
if (reasons.includes('judiciaire')) {
drawText('x', 76, 298, 19)
}
if (reasons.includes('missions')) {
drawText('x', 76, 260, 19)
if (typeNationality === 'fr') {
drawText('x', 49, 127, 19)
}
let locationSize = idealFontSize(font, profile.town, 83, 7, 11)
@ -149,26 +149,18 @@ async function generatePdf (profile, reasons) {
locationSize = 7
}
drawText(profile.town, 111, 226, locationSize)
if (reasons !== '') {
// Date sortie
drawText(`${profile.datesortie}`, 92, 200)
drawText(releaseHours, 200, 201)
drawText(releaseMinutes, 220, 201)
}
// Date création
drawText('Date de création:', 464, 150, 7)
drawText(`${creationDate} à ${creationHour}`, 455, 144, 7)
// Fait à :
drawText(profile.town, 388, 107, locationSize)
// Le
drawText(`${new Date().toLocaleDateString('fr-FR', { month: 'numeric', day: 'numeric' })}`, 488, 107)
const generatedQR = await generateQR(data)
const qrImage = await pdfDoc.embedPng(generatedQR)
page1.drawImage(qrImage, {
x: page1.getWidth() - 170,
y: 155,
x: 450,
y: 572,
width: 100,
height: 100,
})
@ -189,7 +181,7 @@ async function generatePdf (profile, reasons) {
function downloadBlob (blob, fileName) {
const link = document.createElement('a')
var url = URL.createObjectURL(blob)
const url = URL.createObjectURL(blob)
link.href = url
link.download = fileName
document.body.appendChild(link)
@ -204,6 +196,15 @@ function getAndSaveReasons () {
return values
}
function getAndSaveTypeNationality () {
const typeNationality = $$('input[name="field-type-nationality"]:checked')
.map(x => x.value)
.join('-')
localStorage.setItem('typeNationality', typeNationality)
return typeNationality
}
// see: https://stackoverflow.com/a/32348687/1513045
function isFacebookBrowser () {
const ua = navigator.userAgent || navigator.vendor || window.opera
@ -237,8 +238,15 @@ $('#generate-btn').addEventListener('click', async event => {
event.preventDefault()
saveProfile()
const reasons = getAndSaveReasons()
const pdfBlob = await generatePdf(getProfile(), reasons)
const typeNationality = getAndSaveTypeNationality()
let reasons
if (typeNationality === 'fr') {
reasons = 'N/A'
} else {
reasons = getAndSaveReasons()
}
const pdfBlob = await generatePdf(getProfile(), typeNationality, reasons)
localStorage.clear()
const creationDate = new Date().toLocaleDateString('fr-CA')
const creationHour = new Date().toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' }).replace(':', '-')
@ -275,9 +283,9 @@ const conditions = {
},
'#field-birthday': {
condition: 'pattern',
pattern: /^([0][1-9]|[1-2][0-9]|30|31)\/([0][1-9]|10|11|12)\/(19[0-9][0-9]|20[0-1][0-9]|2020)/g
pattern: /^([0][1-9]|[1-2][0-9]|30|31)\/([0][1-9]|10|11|12)\/(19[0-9][0-9]|20[0-1][0-9]|2020)/g,
},
'#field-lieunaissance': {
'#field-nationality': {
condition: 'length',
},
'#field-address': {
@ -287,34 +295,28 @@ const conditions = {
condition: 'length',
},
'#field-zipcode': {
condition: 'pattern',
pattern: /\d{5}/g
condition: 'lenght',
},
'#field-datesortie': {
condition: 'pattern',
pattern: /\d{4}-\d{2}-\d{2}/g
'#field-country': {
condition: 'length',
},
'#field-heuresortie': {
condition: 'pattern',
pattern: /\d{2}:\d{2}/g
}
}
Object.keys(conditions).forEach(field => {
$(field).addEventListener('input', () => {
if (conditions[field].condition == 'pattern') {
const pattern = conditions[field].pattern;
if (conditions[field].condition === 'pattern') {
const pattern = conditions[field].pattern
if ($(field).value.match(pattern)) {
$(field).setAttribute('aria-invalid', "false");
$(field).setAttribute('aria-invalid', 'false')
} else {
$(field).setAttribute('aria-invalid', "true");
$(field).setAttribute('aria-invalid', 'true')
}
}
if (conditions[field].condition == 'length') {
if (conditions[field].condition === 'length') {
if ($(field).value.length > 0) {
$(field).setAttribute('aria-invalid', "false");
$(field).setAttribute('aria-invalid', 'false')
} else {
$(field).setAttribute('aria-invalid', "true");
$(field).setAttribute('aria-invalid', 'true')
}
}
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -14,26 +14,6 @@
"src": "android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "apple-touch-icon.png",
"sizes": "180x180",
"type": "image/png"
},
{
"src": "apple-touch-icon-precomposed.png",
"sizes": "180x180",
"type": "image/png"
},
{
"src": "apple-touch-icon-120x120.png",
"sizes": "120x120",
"type": "image/png"
},
{
"src": "apple-touch-icon-120x120-precomposed.png",
"sizes": "120x120",
"type": "image/png"
}
],
"orientation": "portrait-primary",

70
src/flag EN.svg Normal file
View File

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="flag_EN" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 207 133.5" style="enable-background:new 0 0 207 133.5;" xml:space="preserve">
<style type="text/css">
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#EDEDED;}
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#CE0100;}
.st2{fill-rule:evenodd;clip-rule:evenodd;fill:#1E359B;}
.st3{fill:#EDEDED;}
.st4{fill:#CC0B1B;}
.st5{fill:#2C4DB5;}
</style>
<g>
<g>
<rect x="9.7" y="38.3" class="st0" width="138.6" height="92.4"/>
<polygon class="st1" points="148.3,76.8 148.3,92.3 85.8,92.3 85.8,130.7 72.2,130.7 72.2,92.3 9.7,92.3 9.7,76.8 72.2,76.8
72.2,38.3 85.8,38.3 85.8,76.8 "/>
<path class="st2" d="M22.5,38.3h46.2v30.5L22.5,38.3z M89.3,38.3h46.2L89.3,68.8V38.3z M148.3,47.8l-37.6,24.8h37.6V47.8z
M110.7,96.5h37.6v24.8L110.7,96.5z M135.6,130.7l-46.2-30.5v30.5H135.6z M9.7,96.5h37.6L9.7,121.3V96.5z M68.7,100.3l-46.2,30.5
h46.2V100.3z M47.3,72.6L9.7,47.8v24.8H47.3z"/>
<g>
<polygon class="st1" points="52.3,72.6 9.7,44.5 9.7,38.8 61,72.6 "/>
<polygon class="st1" points="68.7,97.2 17.8,130.7 9.7,130.7 9.7,130.4 61.1,96.5 68.7,96.5 "/>
<polygon class="st1" points="148.3,39 97.4,72.6 89.3,72.6 89.3,72.1 140.7,38.3 148.3,38.3 "/>
<polygon class="st1" points="148.3,130 97.4,96.5 106.1,96.5 148.3,124.3 "/>
</g>
</g>
</g>
<g>
<rect x="64" y="4.4" class="st3" width="138.6" height="92.4"/>
<path class="st4" d="M64,89.7h138.6v7.1H64V89.7z M64,4.4h138.6v7.1H64V4.4z M64,18.6h138.6v7.1H64V18.6z M64,32.9h138.6V40H64
V32.9z M64,47.1h138.6v7.1H64V47.1z M64,61.3h138.6v7.1H64V61.3z M64,75.5h138.6v7.1H64V75.5z"/>
<rect x="64" y="4.4" class="st5" width="70.2" height="49.8"/>
<path class="st3" d="M128.9,18.4l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2l-0.6-2l1.7-1.2H128.9z M71.1,9.7l1.7-1.2h-2.1
l-0.6-2l-0.6,2h-2.1L69,9.7l-0.6,2l1.7-1.2l1.7,1.2L71.1,9.7z M82.7,9.7l1.7-1.2h-2.1l-0.6-2l-0.6,2H79l1.7,1.2l-0.6,2l1.7-1.2
l1.7,1.2L82.7,9.7z M94.3,9.7L96,8.5H94l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L94.3,9.7z M106,9.7l1.7-1.2h-2.1
l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L106,9.7z M77.2,44.4l1.7-1.2h-2.1l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2
l1.7,1.2L77.2,44.4z M77,34.7l1.7-1.2h-2.1l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L77,34.7z M77,25l1.7-1.2h-2.1
l-0.6-2l-0.6,2h-2.1L75,25l-0.6,2l1.7-1.2l1.7,1.2L77,25z M117.6,9.7l1.7-1.2h-2.1l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2
l1.7,1.2L117.6,9.7z M129.3,9.7l1.7-1.2h-2.1l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L129.3,9.7z M88.7,25l1.7-1.2h-2.1
l-0.6-2l-0.6,2h-2.1l1.7,1.2L86,27l1.7-1.2l1.7,1.2L88.7,25z M88.6,34.7l1.7-1.2h-2.1l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2
l1.7,1.2L88.6,34.7z M88.8,44.4l1.7-1.2h-2.1l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L88.8,44.4z M100.5,44.4l1.7-1.2
h-2.1l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L100.5,44.4z M100.3,34.7l1.7-1.2h-2.1l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2
l1.7-1.2l1.7,1.2L100.3,34.7z M100.3,25l1.7-1.2h-2.1l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L100.3,25z M76.9,14.7
l1.7-1.2h-2.1l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L76.9,14.7z M88.5,14.7l1.7-1.2h-2.1l-0.6-2l-0.6,2h-2.1l1.7,1.2
l-0.6,2l1.7-1.2l1.7,1.2L88.5,14.7z M112,25l1.7-1.2h-2.1l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L112,25z M111.9,34.7
l1.7-1.2h-2.1l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L111.9,34.7z M112.1,44.4l1.7-1.2h-2.1l-0.6-2l-0.6,2h-2.1
l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L112.1,44.4z M123.8,44.4l1.7-1.2h-2.1l-0.6-2l-0.6,2H120l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2
L123.8,44.4z M123.5,34.7l1.7-1.2h-2.1l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L123.5,34.7z M123.6,25l1.7-1.2h-2.1
l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L123.6,25z M100.2,14.7l1.7-1.2h-2.1l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2
l1.7-1.2l1.7,1.2L100.2,14.7z M111.8,14.7l1.7-1.2h-2.1l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L111.8,14.7z
M123.5,14.7l1.7-1.2h-2.1l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L123.5,14.7z M71.4,49.4l1.7-1.2H71l-0.6-2l-0.6,2
h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L71.4,49.4z M71.1,39.7l1.7-1.2h-2.1l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2
L71.1,39.7z M71.2,30l1.7-1.2h-2.1l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L71.2,30z M82.8,30l1.7-1.2h-2.1l-0.6-2
l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L82.8,30z M82.8,39.7l1.7-1.2h-2.1l-0.6-2l-0.6,2H79l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2
L82.8,39.7z M83,49.4l1.7-1.2h-2.1l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L83,49.4z M94.7,49.4l1.7-1.2h-2.1l-0.6-2
l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L94.7,49.4z M94.4,39.7l1.7-1.2H94l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2
L94.4,39.7z M94.5,30l1.7-1.2h-2.1l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L94.5,30z M71.1,19.7l1.7-1.2h-2.1l-0.6-2
l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L71.1,19.7z M82.7,19.7l1.7-1.2h-2.1l-0.6-2l-0.6,2H79l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2
L82.7,19.7z M106.1,30l1.7-1.2h-2.1l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L106.1,30z M106.1,39.7l1.7-1.2h-2.1l-0.6-2
l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L106.1,39.7z M106.3,49.4l1.7-1.2h-2.1l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2
l1.7,1.2L106.3,49.4z M117.9,49.4l1.7-1.2h-2.1l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L117.9,49.4z M129.6,49.4
l1.7-1.2h-2.1l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L129.6,49.4z M117.7,39.7l1.7-1.2h-2.1l-0.6-2l-0.6,2H114l1.7,1.2
l-0.6,2l1.7-1.2l1.7,1.2L117.7,39.7z M129.4,39.7l1.7-1.2H129l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L129.4,39.7z
M117.8,30l1.7-1.2h-2.1l-0.6-2l-0.6,2H114l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L117.8,30z M129.4,30l1.7-1.2H129l-0.6-2l-0.6,2h-2.1
l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L129.4,30z M94.3,19.7l1.7-1.2H94l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L94.3,19.7z
M106,19.7l1.7-1.2h-2.1l-0.6-2l-0.6,2h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L106,19.7z M117.6,19.7l1.7-1.2h-2.1l-0.6-2l-0.6,2
h-2.1l1.7,1.2l-0.6,2l1.7-1.2l1.7,1.2L117.6,19.7z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

378
src/index-en.html Normal file
View File

@ -0,0 +1,378 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="msapplication-TileColor" content="#603cba">
<meta name="msapplication-config" content="./favicons/browserconfig.xml">
<meta name="theme-color" content="#ffffff">
<meta name="title" content="international travel certificate to mainland France - COVID-19">
<meta name="description" content="Ce service officiel génère une version numérique de lattestation déplacement covid-19 à présenter aux forces de sécurité lors dun contrôle.">
<meta name="keywords" content="covid19, covid-19, certificate, travel, international, official, government">
<meta name="robots" content="index, follow">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="language" content="English">
<meta property="og:title" content="Generator of international travel certificate to mainland France - COVID-19" />
<meta property="og:locale" content="fr_FR" />
<meta property="og:description" content="Ce service officiel génère une version numérique de lattestation déplacement covid-19 à présenter aux forces de sécurité lors dun contrôle." />
<link rel="canonical" href="https://media.interieur.gouv.fr/deplacement-vers-france-covid-19/" />
<meta property="og:url" content="https://media.interieur.gouv.fr/deplacement-vers-france-covid-19/" />
<meta property="og:site_name" content="Generator of international travel certificate to mainland France - COVID-19" />
<script type='application/ld+json'>{"@context":"http://www.schema.org","@type":"GovernmentOrganization","name":"Générateur d'attestation de déplacement international dérogatoire vers la France métropolitaine - COVID-19","description":"Ce service officiel génère une version numérique de lattestation déplacement covid-19 à présenter aux forces de sécurité lors dun contrôle.","address":{"@type":"PostalAddress","addressCountry":"France"}}</script>
<link rel="apple-touch-icon" sizes="180x180" href="./favicons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="./favicons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="./favicons/favicon-16x16.png">
<link rel="manifest" href="./favicons/site.webmanifest">
<link rel="mask-icon" href="./favicons/safari-pinned-tab.svg" color="#21bf73">
<title>COVID-19 Generator of international travel certificate to mainland France</title>
</head>
<body>
<header role="banner" class="wrapper">
<ul class="flex-justify">
<img class="logo" src="/MIN_Interieur_RVB.svg" alt="Ministère de l'intérieur. Liberté, égalité, fraternité.">
<fieldset class="form-language">
<legend class="legend-language">Select your language</legend>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-language" id="radio-language-en" value="en" checked>
<label class="form-check-label" for="radio-language-en">ENGLISH</label>
<img class="flags" src="flag EN.svg">
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-language" id="radio-language-fr" value="fr">
<label class="form-check-label" for="radio-language-fr">FRANCAIS</label>
<img class="flags" src="Flag_of_France.svg">
</div>
</fieldset>
</ul>
<div>
<h1 class="flex flex-wrap">
<span class="covid-title">
COVID-19
</span>
<span class="covid-subtitle">
Generator of international travel certificate to mainland France
</span>
</h1>
<p class="text-alert">
Filled in data is exclusively stored on your mobile phone or computer.
No information is collected by the Ministry of the Interior.
The generated pdf certificate has a QR Code.
This visual barcode makes it possible to read the information on your certificate at the time it was entered.
It is readable with all generic QR code readers.
</p>
<p>
This certificate must be presented to transportation companies, before boarding, by passengers travelling to
mainland France. It must also be presented to border control authorities, for any type of border:
<ul>
<li>European external borders of France (air, maritime, land including railway connections).</li>
<li>European internal borders of France.</li>
</ul>
</p>
</div>
</header>
<main role="main">
<p class="alert alert-danger d-none" role="alert" id="alert-facebook"></p>
<div class="wrapper">
<form id="form-profile" accept-charset="UTF-8">
<h2 class="titre-2">Fill online your digital certificate&nbsp;:</h2>
<p class="text-alert">All fields are mandatory.</p>
<div class="form-group">
<label for="field-firstname" id="field-firstname-label">First name</label>
<div class="input-group align-items-center">
<input
type="text"
class="form-control"
id="field-firstname"
name="firstname"
autocomplete="given-name"
placeholder="Jean"
required
aria-invalid="true"
>
<span class="validity" aria-hidden="true"></span>
</div>
<p class="exemple"></p>
</div>
<div class="form-group">
<label for="field-lastname" id="field-lastname-label">Surname</label>
<div class="input-group align-items-center">
<input
type="text"
class="form-control"
id="field-lastname"
name="lastname"
autocomplete="family-name"
placeholder="Dupont"
aria-invalid="true"
required
>
<span class="validity" aria-hidden="true"></span>
</div>
<p class="exemple"></p>
</div>
<div class="form-group">
<label for="field-birthday" id="field-birthday-label">Date of birth (dd/mm/yyyy)</label>
<div class="input-group align-items-center">
<input
type="text"
pattern="^([0][1-9]|[1-2][0-9]|30|31)\/([0][1-9]|10|11|12)\/(19[0-9][0-9]|20[0-1][0-9]|2020)"
inputmode="numeric"
class="form-control"
id="field-birthday"
name="birthday"
aria-invalid="true"
autocomplete="bday"
placeholder="01/01/1970"
maxlength="10"
required
>
<span class="validity" aria-hidden="true"></span>
</div>
<p class="exemple"></p>
</div>
<div class="form-group">
<label for="field-nationality" id="field-nationality-label">Nationality</label>
<div class="input-group align-items-center">
<input
type="text"
class="form-control"
id="field-nationality"
name="nationality"
autocomplete="country-name"
aria-invalid="true"
placeholder="Belgian"
required
>
<span class="validity" aria-hidden="true"></span>
</div>
<p class="exemple"></p>
</div>
<div class="form-group">
<label for="field-address" id="field-address-label">Address</label>
<div class="input-group align-items-center">
<input
type="text"
class="form-control"
id="field-address"
name="address"
aria-invalid="true"
autocomplete="address-line1"
placeholder="999 avenue de Belgique"
required
>
<span class="validity" aria-hidden="true"></span>
</div>
<p class="exemple"></p>
</div>
<div class="form-group">
<label for="field-town" id="field-town-label">City</label>
<div class="input-group align-items-center">
<input
type="text"
class="form-control"
id="field-town"
name="town"
autocomplete="address-level1"
aria-invalid="true"
placeholder="Brussels"
required
>
<span class="validity" aria-hidden="true"></span>
</div>
<p class="exemple"></p>
</div>
<div class="form-group">
<label for="field-zipcode" id="field-zipcode-label" >Zip code</label>
<div class="input-group align-items-center">
<input
type="text"
class="form-control"
id="field-zipcode"
name="zipcode"
aria-invalid="true"
autocomplete="postal-code"
placeholder="1000"
required
>
<span class="validity" aria-hidden="true"></span>
</div>
<p class="exemple"></p>
</div>
<div class="form-group">
<label for="field-country" id="field-country-label">Country</label>
<div class="input-group align-items-center">
<input
type="text"
class="form-control"
id="field-country"
name="country"
autocomplete="country-name"
aria-invalid="true"
placeholder="Belgium"
required
>
<span class="validity" aria-hidden="true"></span>
</div>
<p class="exemple"></p>
</div>
<fieldset class="control">
<legend class="titre-3">You are : </legend>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-type-nationality" id="radio-tiers" value="tiers">
<label class="form-nationality-label" for="radio-tiers">Third country nationals.</label>
<fieldset class='conditional'>
<legend class="titre-4">Choose the reason :</legend>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-residence-tiers" value="residence">
<label class="form-check-label" for="radio-residence-tiers">Individuals having their primary residence in France or in the European Union or assimilated countries<a href="#footnote1">[1]</a>, who are holders of a French or European residence permit or valid long-stay visa, accompanied by their spouse and
children.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-transit-tiers" value="transit">
<label class="form-check-label" for="radio-transit-tiers">Individuals in transit to reach their country of origin who are holders of a travel document to their country of origin and remaining in the international area with no intention to enter the national territory.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-prof._sante-tiers" value="prof._sante">
<label class="form-check-label" for="radio-prof._sante-tiers">Healthcare workers supporting the fight against Covid-19.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-marchandises-tiers" value="marchandises">
<label class="form-check-label" for="radio-marchandises-tiers">Goods carriers, including seamen.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-equipage-tiers" value="equipage">
<label class="form-check-label" for="radio-equipage-tiers">Flight and cargo crews, or travelling as a passenger to their departure base.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-diplomatique-tiers" value="diplomatique">
<label class="form-check-label" for="radio-diplomatique-tiers">Diplomatic mission staff, or international organisations staff working in headquarters or offices located in France, who are holders of a special residence permit or a type D promae visa.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-frontalier-tiers" value="frontalier">
<label class="form-check-label" for="radio-frontalier-tiers">Cross-border workers at internal land borders.</label>
</div>
</fieldset>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-type-nationality" id="radio-eu" value="eu">
<label class="form-nationality-label" for="radio-eu">European Union or assimilated countries nationals<a href="#footnote2">[2]</a>.</label>
<fieldset class='conditional'>
<legend class="titre-4">Choose the reason :</legend>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-residence-eu" value="residence">
<label class="form-check-label" for="radio-residence-eu">Individuals having their primary residence in France, accompanied by their spouse and children.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-transit-eu" value="transit">
<label class="form-check-label" for="radio-transit-eu">Individuals transiting through France to reach their residence, accompanied by their spouse and children.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-prof._sante-eu" value="prof._sante">
<label class="form-check-label" for="radio-prof._sante-eu">Healthcare workers supporting the fight against Covid-19.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-marchandises-eu" value="marchandises">
<label class="form-check-label" for="radio-marchandises-eu">Goods carriers, including seamen.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-equipage-eu" value="equipage">
<label class="form-check-label" for="radio-equipage-eu">Flight and cargo crews, or travelling as a passenger to their departure base.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-diplomatique-eu" value="diplomatique">
<label class="form-check-label" for="radio-diplomatique-eu">Diplomatic mission staff, or international organisations staff working in headquarters or offices located in France,
who are hold ers of a special residence permit or a type D promae visa.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-frontalier" value="frontalier">
<label class="form-check-label" for="radio-frontalier">Cross-border workers at internal land borders.</label>
</div>
</fieldset>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-type-nationality" id="radio-fr" value="fr">
<label class="form-nationality-label" for="radio-fr">French nationals, accompanied by their spouse and children.</label>
</div>
</fieldset>
<p class="text-center mt-5">
<button type="button" id="generate-btn" class="btn btn-primary btn-attestation"><span ><i class="fa fa-file-pdf inline-block mr-1"></i> Generate my certificate</span></button>
</p>
<div class="bg-primary d-none" id="snackbar">
The certificate is downloaded on your terminal.
</div>
</form>
</div>
<div class="">
<p id="footnotes">
<span id="footnote1">[1] United Kingdom, Iceland, Liechtenstein, Norway, Andorra, Monaco, Switzerland, San Marino, Holy See.</span><br>
<span id="footnote2">[2] European Union, United Kingdom, Iceland, Liechtenstein, Norway, Andorra, Monaco, Switzerland, San Marino and Holy See nationals (2004/38/CE directive).</span>
</p>
<p class="github">
The source code of this service is available for consultation on <a href="https://github.com/LAB-MI/deplacement-covid-19" class="github-link">GitHub</a>.
</p>
<p class="label-mi">
Ministry of the Interior - DNUM - SDIT
</p>
<img class="center" src="/logo_dnum.svg" alt="logo dnum">
</div>
</main>
<footer role="contentinfo" class="main-footer">
<div class="footer-links">
<a href="./confidentialite.html" title="Confidentialité - nouvelle page" target="_blank" class="footer-line footer-link">Privacy policy</a>
<a href="https://www.interieur.gouv.fr/Infos-du-site/Mentions-legales" title="Mentions légales - nouvelle page" target="_blank" class="footer-line footer-link">Legal notices</a>
<a href="https://www.gouvernement.fr/info-coronavirus" title="Informations du gouvernement sur le Covid-19 - nouvelle page" target="_blank" class="footer-line footer-link">Government information on COVID-19</a>
<div class="footer-line" >For more information, call <a class="num-08" href="tel:0800130000">0 800 130 000</a></div>
<p class="footer-line" id="version"></p>
</div>
</footer>
<div class="alert alert-info d-none" id="update-alert">
A new version is available. Click on the button to get it.
<p class="text-right">
<button id="reload-btn" class="btn btn-info">Update</button>
</p>
</div>
<script src="./certificate-en.js"></script>
</body>
</html>

View File

@ -6,19 +6,19 @@
<meta name="msapplication-TileColor" content="#603cba">
<meta name="msapplication-config" content="./favicons/browserconfig.xml">
<meta name="theme-color" content="#ffffff">
<meta name="title" content="Générateur d'attestation de déplacement dérogatoire - COVID-19">
<meta name="title" content="Générateur d'attestation de déplacement international dérogatoire vers la France métropolitaine - COVID-19">
<meta name="description" content="Ce service officiel génère une version numérique de lattestation déplacement covid-19 à présenter aux forces de sécurité lors dun contrôle.">
<meta name="keywords" content="covid19, covid-19, attestation, déplacement, officielle, gouvernement">
<meta name="keywords" content="covid19, covid-19, attestation, déplacement, international, officielle, gouvernement">
<meta name="robots" content="index, follow">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="language" content="French">
<meta property="og:title" content="Générateur d'attestation de déplacement dérogatoire - COVID-19" />
<meta property="og:title" content="Générateur d'attestation de déplacement international dérogatoire vers la France métropolitaine - COVID-19" />
<meta property="og:locale" content="fr_FR" />
<meta property="og:description" content="Ce service officiel génère une version numérique de lattestation déplacement covid-19 à présenter aux forces de sécurité lors dun contrôle." />
<link rel="canonical" href="https://media.interieur.gouv.fr/deplacement-covid-19/" />
<meta property="og:url" content="https://media.interieur.gouv.fr/deplacement-covid-19/" />
<meta property="og:site_name" content="Générateur d'attestation de déplacement dérogatoire - COVID-19" />
<script type='application/ld+json'>{"@context":"http://www.schema.org","@type":"GovernmentOrganization","name":"Générateur d'attestation de déplacement dérogatoire - COVID-19","description":"Ce service officiel génère une version numérique de lattestation déplacement covid-19 à présenter aux forces de sécurité lors dun contrôle.","address":{"@type":"PostalAddress","addressCountry":"France"}}</script>
<link rel="canonical" href="https://media.interieur.gouv.fr/deplacement-vers-france-covid-19/" />
<meta property="og:url" content="https://media.interieur.gouv.fr/deplacement-vers-france-covid-19/" />
<meta property="og:site_name" content="Générateur d'attestation de déplacement international dérogatoire vers la France métropolitaine - COVID-19" />
<script type='application/ld+json'>{"@context":"http://www.schema.org","@type":"GovernmentOrganization","name":"Générateur d'attestation de déplacement international dérogatoire vers la France métropolitaine - COVID-19","description":"Ce service officiel génère une version numérique de lattestation déplacement covid-19 à présenter aux forces de sécurité lors dun contrôle.","address":{"@type":"PostalAddress","addressCountry":"France"}}</script>
<link rel="apple-touch-icon" sizes="180x180" href="./favicons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="./favicons/favicon-32x32.png">
@ -26,30 +26,50 @@
<link rel="manifest" href="./favicons/site.webmanifest">
<link rel="mask-icon" href="./favicons/safari-pinned-tab.svg" color="#21bf73">
<title>COVID-19 Générateur d'attestation de déplacement dérogatoire</title>
<title>COVID-19 Générateur d'attestation de déplacement international dérogatoire vers la France métropolitaine</title>
</head>
<body>
<header role="banner" class="wrapper">
<img class="logo" src="/MIN_Interieur_RVB.svg" alt="Ministère de l'intérieur. Liberté, égalité, fraternité.">
<ul class="flex-justify">
<img class="logo" src="/MIN_Interieur_RVB.svg" alt="Ministère de l'intérieur. Liberté, égalité, fraternité.">
<fieldset class="form-language">
<legend class="legend-language">Choisissez votre langue</legend>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-language" id="radio-language-en" value="en">
<label class="form-check-label" for="radio-language-en">ENGLISH</label>
<img class="flags" src="flag EN.svg">
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-language" id="radio-language-fr" value="fr" checked>
<label class="form-check-label" for="radio-language-fr">FRANCAIS</label>
<img class="flags" src="Flag_of_France.svg">
</div>
</fieldset>
</ul>
<div>
<h1 class="flex flex-wrap">
<span class="covid-title">
COVID-19
</span>
<span class="covid-subtitle">
Générateur d'attestation de&nbsp;déplacement&nbsp;dérogatoire
Générateur d'attestation de déplacement&nbsp;international&nbsp;dérogatoire vers la France&nbsp;métropolitaine
</span>
</h1>
<p class="text-alert important">
Cette application est <a href="#source">dérivée de l'application développée par le Ministère de l'Intérieur</a>.
Elle est hébergé sur un serveur qui n'a <b>aucun lien avec les pouvoirs publics</b> et ne stocke aucune information (contrairement aux pratiques habituelles, les requêtes ne sont pas stockées dans les journaux du serveur).
Elle est mise à votre disposition sans aucune garantie de bon fonctionnement !
</p>
<p class="text-alert">
Les données saisies sont stockées exclusivement sur votre téléphone ou votre ordinateur. Aucune information n'est collectée par le Ministère de l'Intérieur.
L'attestation pdf générée contient un QR Code. Ce code-barres graphique permet de lire les informations portées dans votre attestation au moment de leur saisie.
Il peut être déchiffré à l'aide de tout type de lecteur de QR code générique.
</p>
<p>
Cette attestation est à présenter aux compagnies de transport, avant lutilisation du titre de transport, par les
passagers qui souhaitent voyager à destination de la France métropolitaine. Elle sera aussi présentée aux autorités en
charge du contrôle frontières, pour tout type de frontière :
<ul>
<li>Aux frontières extérieures de la France (liaisons aériennes, maritimes, terrestres, dont les liaisons ferroviaires).</li>
<li>Aux frontières intérieures de la France.</li>
</ul>
</p>
</div>
</header>
<main role="main">
@ -72,7 +92,6 @@
autocomplete="given-name"
placeholder="Jean"
required
autofocus
aria-invalid="true"
>
<span class="validity" aria-hidden="true"></span>
@ -92,7 +111,6 @@
placeholder="Dupont"
aria-invalid="true"
required
autofocus
>
<span class="validity" aria-hidden="true"></span>
</div>
@ -121,15 +139,16 @@
</div>
<div class="form-group">
<label for="field-lieunaissance" id="field-lieunaissance-label">Lieu de naissance</label>
<label for="field-nationality" id="field-nationality-label">Nationalité</label>
<div class="input-group align-items-center">
<input
type="text"
class="form-control"
id="field-lieunaissance"
name="lieunaissance"
id="field-nationality"
name="nationality"
autocomplete="country-name"
aria-invalid="true"
placeholder="Lyon"
placeholder="Belge"
required
>
<span class="validity" aria-hidden="true"></span>
@ -147,7 +166,7 @@
name="address"
aria-invalid="true"
autocomplete="address-line1"
placeholder="999 avenue de france"
placeholder="999 avenue de Belgique"
required
>
<span class="validity" aria-hidden="true"></span>
@ -165,7 +184,7 @@
name="town"
autocomplete="address-level1"
aria-invalid="true"
placeholder="Paris"
placeholder="Bruxelles"
required
>
<span class="validity" aria-hidden="true"></span>
@ -178,18 +197,12 @@
<div class="input-group align-items-center">
<input
type="text"
inputmode="numeric"
pattern="[0-9]{5}"
min="00000"
max="99999"
class="form-control"
id="field-zipcode"
name="zipcode"
aria-invalid="true"
autocomplete="postal-code"
minlength="4"
maxlength="5"
placeholder="75001"
placeholder="1000"
required
>
<span class="validity" aria-hidden="true"></span>
@ -197,59 +210,125 @@
<p class="exemple"></p>
</div>
<fieldset>
<legend class="title-3">Choisissez le ou les motif(s) de sortie</legend>
<div class="form-group">
<label for="field-country" id="field-country-label">Pays</label>
<div class="input-group align-items-center">
<input
type="text"
class="form-control"
id="field-country"
name="country"
autocomplete="country-name"
aria-invalid="true"
placeholder="Belgique"
required
>
<span class="validity" aria-hidden="true"></span>
</div>
<p class="exemple"></p>
</div>
<fieldset class="control">
<legend class="titre-3">Vous êtes : </legend>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="field-reason" id="checkbox-travail" value="travail">
<label class="form-check-label" for="checkbox-travail">Déplacements entre le domicile et le lieu dexercice de lactivité professionnelle, lorsqu'ils sont indispensables à l'exercice dactivités ne pouvant être organisées sous forme de télétravail ou déplacements professionnels ne pouvant être différés.</label>
<input class="form-check-input" type="radio" name="field-type-nationality" id="radio-tiers" value="tiers">
<label class="form-nationality-label" for="radio-tiers">Ressortissant de pays tiers.</label>
<fieldset class='conditional'>
<legend class="titre-4">Choisissez le motif :</legend>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-residence-tiers" value="residence">
<label class="form-check-label" for="radio-residence-tiers">Personnes ayant leur résidence principale en France ou dans lUnion européenne et pays assimilés<a href="#footnote1">[1]</a>, titulaires dun titre de séjour ou dun visa de long séjour français ou européen en cours de validité, ainsi que leurs conjoints et leurs enfants.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-transit-tiers" value="transit">
<label class="form-check-label" for="radio-transit-tiers">Personnes en transit pour rejoindre leur pays dorigine, présentant le titre de voyage vers leur pays dorigine et
restant en zone internationale sans entrer sur le territoire national.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-prof._sante-tiers" value="prof._sante">
<label class="form-check-label" for="radio-prof._sante-tiers">Professionnels de santé aux fins de lutter contre le Covid-19.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-marchandises-tiers" value="marchandises">
<label class="form-check-label" for="radio-marchandises-tiers">Transporteurs de marchandises, dont les marins.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-equipage-tiers" value="equipage">
<label class="form-check-label" for="radio-equipage-tiers">Équipages et personnels exploitant des vols passagers et cargo, ou voyageant comme passagers pour se positionner sur leur base de départ.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-diplomatique-tiers" value="diplomatique">
<label class="form-check-label" for="radio-diplomatique-tiers">Personnels des missions diplomatiques et consulaires, ainsi que des organisations internationales ayant leur siège
ou un bureau en France, titulaires dun titre de séjour spécial ou dun visa D promae.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-frontalier-tiers" value="frontalier">
<label class="form-check-label" for="radio-frontalier-tiers">Travailleurs frontaliers aux frontières intérieures terrestres.</label>
</div>
</fieldset>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="field-reason" id="checkbox-courses" value="courses">
<label class="form-check-label" for="checkbox-courses">Déplacements pour effectuer des achats de fournitures nécessaires à lactivité professionnelle et des achats de première nécessité dans des établissements dont les activités demeurent autorisées (<a href="https://www.service-public.fr/particuliers/actualites/A13921" class="stores-link" title="Liste des commerces et établissements qui restent ouverts - nouvelle page" target="_blank">liste des commerces et établissements qui restent ouverts</a>).</label>
<input class="form-check-input" type="radio" name="field-type-nationality" id="radio-eu" value="eu">
<label class="form-nationality-label" for="radio-eu">Ressortissant de lUnion européenne et assimilés<a href="#footnote2">[2]</a>.</label>
<fieldset class='conditional'>
<legend class="titre-4">Choisissez le motif</legend>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-residence-eu" value="residence">
<label class="form-check-label" for="radio-residence-eu">Personnes ayant leur résidence principale en France, ainsi que leurs conjoints et leurs enfants.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-transit-eu" value="transit">
<label class="form-check-label" for="radio-transit-eu">Personnes transitant par la France pour rejoindre leur résidence, ainsi que leurs conjoints et leurs enfants.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-prof._sante-eu" value="prof._sante">
<label class="form-check-label" for="radio-prof._sante-eu">Professionnels de santé aux fins de lutter contre le Covid-19.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-marchandises-eu" value="marchandises">
<label class="form-check-label" for="radio-marchandises-eu">Transporteurs de marchandises, dont les marins.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-equipage-eu" value="equipage">
<label class="form-check-label" for="radio-equipage-eu">Équipages et personnels exploitant des vols passagers et cargo, ou voyageant comme passagers pour se positionner sur leur base de départ.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-diplomatique-eu" value="diplomatique">
<label class="form-check-label" for="radio-diplomatique-eu">Personnels des missions diplomatiques et consulaires, ainsi que des organisations internationales ayant leur siège
ou un bureau en France, titulaires dun titre de séjour spécial ou dun visa D promae.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="field-reason" id="radio-frontalier" value="frontalier">
<label class="form-check-label" for="radio-frontalier">Travailleurs frontaliers aux frontières intérieures terrestres.</label>
</div>
</fieldset>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="field-reason" id="checkbox-sante" value="sante">
<label class="form-check-label" for="checkbox-sante">Consultations et soins ne pouvant être assurés à distance et ne pouvant être différés ; consultations et soins des patients atteints d'une affection de longue durée.</label>
<input class="form-check-input" type="radio" name="field-type-nationality" id="radio-fr" value="fr">
<label class="form-nationality-label" for="radio-fr">Ressortissant de nationalité française, ainsi que votre conjoint et enfants.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="field-reason" id="checkbox-famille" value="famille">
<label class="form-check-label" for="checkbox-famille">Déplacements pour motif familial impérieux, pour lassistance aux personnes vulnérables ou la garde denfants.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="field-reason" id="checkbox-sport" value="sport">
<label class="form-check-label" for="checkbox-sport">Déplacements brefs, dans la limite d'une heure quotidienne et dans un rayon maximal d'un kilomètre autour du domicile, liés soit à l'activité physique individuelle des personnes, à l'exclusion de toute pratique sportive collective et de toute proximité avec d'autres personnes, soit à la promenade avec les seules personnes regroupées dans un même domicile, soit aux besoins des animaux de compagnie.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="field-reason" id="checkbox-judiciaire" value="judiciaire">
<label class="form-check-label" for="checkbox-judiciaire">Convocation judiciaire ou administrative.</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="field-reason" id="checkbox-missions" value="missions">
<label class="form-check-label" for="checkbox-missions">Participation à des missions dintérêt général sur demande de lautorité administrative.</label>
</div>
</fieldset>
<div class="form-group">
<label for="field-datesortie">Date de sortie</label>
<div class="input-group align-items-center">
<input type="date" class="form-control" id="field-datesortie" name="datesortie" autocomplete="off" placeholder="JJ/MM/YYYY" aria-invalid="true" required>
<span class="validity" aria-hidden="true"></span>
</div>
</div>
<div class="form-group">
<label for="field-heuresortie">Heure de sortie</label>
<div class="input-group align-items-center">
<input type="time" class="form-control" id="field-heuresortie" name="heure" autocomplete="off" aria-invalid="true" required>
<span class="validity" aria-hidden="true"></span>
</div>
</div>
<p class="text-center mt-5">
<button type="button" id="generate-btn" class="btn btn-primary btn-attestation"><span ><i class="fa fa-file-pdf inline-block mr-1"></i> Générer mon attestation</span></button>
</p>
@ -258,10 +337,17 @@
L'attestation est téléchargée sur votre appareil.
</div>
</form>
</div>
<div class="">
<p class="github" id="source">
Le code source de ce service est dérivé de celui qui a été développé par le ministère de l'intérieur. La version d'origine est consultable sur <a href="https://github.com/LAB-MI/deplacement-covid-19" class="github-link">Github (Microsoft)</a>. La version dérivée est consultable sur <a href="https://gitea.dyomedea.com/vdv/deplacement-covid-19" class="github-link">Gitea (Open Source)</a>. L'application est hébergée dans un container Docker dont le source est également <a href="https://gitea.dyomedea.com/vdv/docker-deplacement-covid-19" class="github-link">public</a>.
<p id="footnotes">
<span id="footnote1">[1] Royaume-Uni, Islande, Liechtenstein, Norvège, Andorre, Monaco, Suisse, Saint-Marin, Saint Siège.</span><br>
<span id="footnote2">[2] Ressortissants de lUnion européenne et ressortissants britanniques, ainsi que les ressortissants islandais, liechtensteinois, norvégiens, andorrans, monégasques, suisses, saint-marinais, citoyens du Saint Siège (directive 2004/38/CE).</span>
</p>
<p class="github">
Le code source de ce service est consultable sur <a href="https://github.com/LAB-MI/deplacement-covid-19" class="github-link">Github</a>.
</p>
<p class="label-mi">
Ministère de l'Intérieur - DNUM - SDIT

View File

@ -46,6 +46,14 @@ svg {
height: 1em;
}
ul.flex-justify {
display: -webkit-flex; /* Safari */
-webkit-align-items: center; /* Safari 7.0+ */
justify-content:space-between;
padding: 0;
margin: 0;
}
h1.flex.flex-wrap {
display: flex;
flex-wrap: wrap;
@ -83,6 +91,11 @@ a {
transform: translateY(-2px);
}
#form-profile .form-nationality-label {
font-weight: 800;
transform: translateY(-2px);
}
#form-generate .form-check {
margin: 10px;
}
@ -155,6 +168,27 @@ input:valid+span:after {
width: 50%;
}
fieldset .form-language {
display: flex;
align-items: right;
}
.legend-language {
text-align: left;
font-size: 1rem;
color: #000000;
}
.form-language .form-check-label {
font-size: 1rem;
font-weight: bold;
color: #000000;
}
.flags {
width: 20px;
height: 15px;
}
.covid-title {
display: flex;
@ -185,11 +219,10 @@ input:valid+span:after {
.text-alert{
text-align: left;
font-style: italic;
color: #000000;
}
.btn-attestation {
padding: 0.9em;
font-size: 1.2em;
@ -204,7 +237,7 @@ input:valid+span:after {
}
.github {
font-size: 1em;
font-size: 0.7em;
text-align: center;
color: #000000;
@ -402,6 +435,21 @@ input:valid+span:after {
transform: translateX(-50%);
}
.important {
font-style: italic;
.control:not(:checked) ~ .conditional,
#radio-fr:not(:checked) ~ .conditional,
#radio-eu:not(:checked) ~ .conditional,
#radio-tiers:not(:checked) ~ .conditional {
clip: rect(0 0 0 0);
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
}
[id^="footnote"] {
margin: 30px auto;
max-width: 400px;
font-size: 0.8em;
color: #000000;
}

View File

@ -1 +1 @@
Sitemap: https://media.interieur.gouv.fr/deplacement-covid-19/sitemap.xml
Sitemap: https://media.interieur.gouv.fr/deplacement-vers-france-covid-19/sitemap.xml

View File

@ -8,17 +8,17 @@
<url>
<loc>https://media.interieur.gouv.fr/deplacement-covid-19/</loc>
<loc>https://media.interieur.gouv.fr/deplacement-vers-france-covid-19/</loc>
<lastmod>2020-04-06T04:22:03+00:00</lastmod>
<priority>1.00</priority>
</url>
<url>
<loc>https://media.interieur.gouv.fr/deplacement-covid-19/confidentialite.html</loc>
<loc>https://media.interieur.gouv.fr/deplacement-vers-france-covid-19/confidentialite.html</loc>
<lastmod>2020-04-06T04:22:03+00:00</lastmod>
<priority>0.80</priority>
</url>
<url>
<loc>https://media.interieur.gouv.fr/deplacement-covid-19/index.html</loc>
<loc>https://media.interieur.gouv.fr/deplacement-vers-france-covid-19/index.html</loc>
<lastmod>2020-04-06T04:22:03+00:00</lastmod>
<priority>0.64</priority>
</url>