Bonjour,
Je cherche a générer un pdf à partir d'un formulaire. J'ai bien avancé en m'inspirant du site de l'attestation de déplacement, mais étant novice en js j'ai du louper quelque chose.
Je n'ai pas d'erreur dans la console mais lorsque je clic sur le bouton mon pdf ne ce générer pas.
J'ai mis mon projet sur GitHub pour plus de facilité
https://github.com/JefNewTech/Generateur_conge.git
sinon voici le
Code HTML : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159 <!DOCTYPE html> <html lang="fr"> <head> <meta charset="utf-8"> <link rel="stylesheet" href="./conge.css"> <link rel="apple-touch-icon" sizes="180x180" href="./favicons/apple-icon-180x180.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="mask-icon" href="./favicons/safari-pinned-tab.svg" color="#21bf73"> </head> <body> <form> <h2>Générateur de feuille de congé.</h2> <form id="form-conge" action="#"> <p><i>Complétez le formulaire. Les champs marqué par </i><em>*</em> sont <em>obligatoires</em></p> <fieldset> <legend>Contact</legend> <label for="Nom et Prénom">Nom et prénom <em>*</em></label> <input id="field-nom" name="name" placeholder="Dupont Jean" autofocus="" required=""> </fieldset> <fieldset> <legend>Date du(des) congé(s) </legend> <label for="Début">Date de début <em>*</em></label> <input id="field-debut" name="debut" type="text" required=""> <p> <label for="Fin">Date de fin </label> <input id="field-fin" name="fin" type="text"> </fieldset> <fieldset> <legend>Choisissez un motif de congé</legend> <div class="form-check"> <input class="form-check-input" type="radio" name="field-reason" id="radio-VaAn" value="VaAn" checked> <label class="form-check-label" for="radio-VaAn">Vacances Annuelles</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" name="field-reason" id="radio-ReHs" value="ReHs"> <label class="form-check-label" for="radio-ReHs">Récupération d'heures supplémentaires</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" name="field-reason" id="radio-CoCi" value="CoCi"> <label class="form-check-label" for="radio-CoCi">Congé de Circonstance</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" name="field-reason" id="radio-CoFmp" value="CoFmp"> <label class="form-check-label" for="radio-CoFmp">Congé pour force majeur payé ( 4jours)</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" name="field-reason" id="radio-CoFmNp" value="CoFmNp"> <label class="form-check-label" for="radio-CoFmNp">Congé pour force majeur non payé ( 6jours)</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" name="field-reason" id="radio-COMiC" value="COMiC"> <label class="form-check-label" for="radio-COMiC">Congé pour motif imperieux (Contractuel)</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" name="field-reason" id="radio-COMIS" value="COMIS"> <label class="form-check-label" for="radio-COMIS">Congé pour motif imperieux (Statutaire)</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" name="field-reason" id="radio-CoSy" value="CoSy"> <label class="form-check-label" for="radio-CoSy">Congé Syndical</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" name="field-reason" id="radio-CoPa" value="CoPa"> <label class="form-check-label" for="radio-CoPa">Congé de Paternité</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" name="field-reason" id="radio-DisCm" value="DisCm"> <label class="form-check-label" for="radio-DisCm">Dispense de Service Convocation médical</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" name="field-reason" id="radio-DisDo" value="DisDo"> <label class="form-check-label" for="radio-DisDo">Dispense de Service Don de moelle, Organe</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" name="field-reason" id="radio-DisDp" value="DisDp"> <label class="form-check-label" for="radio-DisDp">Dispense de Service Don de plaquettes</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" name="field-reason" id="radio-DisDs" value="DisDs"> <label class="form-check-label" for="radio-DisDs">Dispense de Service Don de Sang</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" name="field-reason" id="radio-DisRm" value="DisRm"> <label class="form-check-label" for="radio-DisRm">Dispense de Service RDV médical Service</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" name="field-reason" id="radio-EnGr" value="EnGr"> <label class="form-check-label" for="radio-EnGr">En Grève</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" name="field-reason" id="radio-FoPr" value="FoPr"> <label class="form-check-label" for="radio-FoPr">Formation professionnelle </label> </div> <div class="form-check"> <input class="form-check-input" type="radio" name="field-reason" id="radio-JoVe" value="JoVe"> <label class="form-check-label" for="radio-JoVe">Jour(s) Vert(s)</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" name="field-reason" id="radio-Mala" value="Mala"> <label class="form-check-label" for="radio-Mala">Maladie</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" name="field-reason" id="radio-MiEx" value="MiEx"> <label class="form-check-label" for="radio-MiEx">Mission extérieure</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" name="field-reason" id="radio-MiTme" value="MiTme"> <label class="form-check-label" for="radio-MiTme">Mi-Temps médical</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" name="field-reason" id="radio-none" value=""> <label class="form-check-label" for="radio-none">Aucun (remplissage manuel)</label> </div> </fieldset> <fieldset> <legend>Signature</legend> <div class="form-group"> <label>Dessinez votre signature dans la case ci-dessous.</label> <canvas id="field-signature" width=360 height=240 aria-describedby="help-signature"></canvas> <button id="reset-signature" type="button" class="btn btn-link btn-sm float-right">Effacer la signature</button> <br><br> </div> </fieldset> <p><input type="button" id="generate-btn" value="Générer la feuille de congé"></p> </form> </form> <script src="https://cdn.jsdelivr.net/npm/signature_pad@2.3.2/dist/signature_pad.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/pdf-lib@1.4.1/dist/pdf-lib.js"></script> <script src="conge.js"></script> </body> </html>
Et le Js :
Merci pour vos lumières
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226 const { PDFDocument, StandardFonts } = PDFLib const $ = (...args) => document.querySelector(...args) const $$ = (...args) => document.querySelectorAll(...args) function getProfile() { const fields = {} for (const field of $$('#form-conge input')) { if (field.id == 'field-debut') { const debut = field.value.split('-') fields[field.id.substring('field-'.length)] = '${debut[2]}/${debut[1]}' } else { fields[field.id.substring('field-'.length)] = field.value } } return fields } async function generatePdf(profile, reason) { const pdfBase = 'Conge.pdf' const creationInstant = new Date() const creationDate = creationInstant.toLocaleDateString('fr-FR') const { name, debut, fin } = profile const existingPdfBytes = await fetch(pdfBase).then((res) => res.arrayBuffer()) const pdfDoc = await PDFDocument.load(existingPdfBytes) const page = pdfDoc.getPages()[0] const font = await pdfDoc.embedFont(StandardFonts.Helvetica) const drawText = (text, x, y, size = 11) => { page.drawText(text, { x, y, size, font }) } drawText(profile.name, 180, 235, 12) if (profile.fin !== '') { drawText(`Du ${profile.debut}`, 25, 180) drawText(`au ${profile.fin}`, 120, 180) } else { drawText(`Le ${profile.debut}`, 25, 180) } switch (reason) { case 'VaAn': drawText('de vacance annuelle', 25, 200, 15) break case 'ReHs': drawText('de récupération d heures supplémentaires', 25, 200, 15) break case 'CoCi': drawText('d un congé de Circonstance', 25, 200, 15) break case 'CoFmp': drawText('d un congé pour force majeur payé ( 4jours)', 25, 200, 15) break case 'CoFmNp': drawText('d un congé pour force majeur non payé ( 6jours)', 25, 200, 15) break case 'COMiC': drawText('d un congé pour motif imperieux (Contractuel)', 25, 200, 15) break case 'COMIS': drawText('d un congé pour motif imperieux (Statutaire)', 25, 200, 15) break case 'CoPa': drawText('d un congé Syndical', 25, 200, 15) break case 'DisCm': drawText('d une dispense de Service Convocation médical', 25, 200, 15) break case 'DisDo': drawText('d une dispense de Service Don de moelle, Organe', 25, 200, 15) break case 'DisDp': drawText('d une dispense de Service Don de plaquettes', 25, 200, 15) break case 'DisDs': drawText('d une dispense de Service Don de Sang', 25, 200, 15) break case 'DisRm': drawText('d une dispense de Service RDV médical Service', 25, 200, 15) break case 'EnGr': drawText('d un congé pour greve', 25, 200, 15) break case 'FoPr': drawText('d un congé pour formation professionnelle', 25, 200, 15) break case 'JoVe': drawText('d un jour vert', 25, 200, 15) break case 'Mala': drawText('d un congé pour maladie', 25, 200, 15) break case 'MiEx': drawText('d un congé pour mission exterieur', 25, 200, 15) break case 'MiTime': drawText('d un congé pour mi-temps médical', 25, 200, 15) break } if (reason !== '') { const date = [ String((new Date).getDate()).padStart(2, '0'), String((new Date).getMonth() + 1).padStart(2, '0'), String((new Date).getFullYear()), ].join('/') drawText(date, 405, 354) } const pdfBytes = await pdfDoc.save() return new Blob([pdfBytes], { type: 'application/pdf' }) } function downloadBlob(blob, fileName) { const link = document.createElement('a') var url = URL.createObjectURL(blob) link.href = url link.download = fileName link.click() } function getReason() { const val = $('input[name="field-reason"]:checked').value return val } const snackbar = $('#snackbar') $('#generate-btn').addEventListener('click', async (event) => { event.preventDefault() const invalid = validateAriaFields() if (invalid) return const reason = getReason() const pdfBlob = await generatePdf(getProfile(), reason) const creationInstant = new Date() const creationDate = creationInstant.toLocaleDateString('fr-CA') const creationHour = creationInstant .toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' }) .replace(':', '-') downloadBlob(pdfBlob, `Feuille-${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') const validitySpan = input.parentNode.parentNode.querySelector('.validity') if (input.placeholder && exempleElt) { input.addEventListener('input', (event) => { if (input.value) { exempleElt.innerHTML = 'ex. : ' + input.placeholder validitySpan.removeAttribute('hidden') } else { exempleElt.innerHTML = '' } }) } }) const conditions = { '#field-nom': { condition: 'length', }, '#field-debut': { condition: 'pattern', pattern: /\d{4}-\d{2}-\d{2}/g, }, '#field-fin': { condition: 'pattern', pattern: /\d{4}-\d{2}-\d{2}/g, }, } function validateAriaFields() { return Object.keys(conditions).map(field => { if (conditions[field].condition === 'pattern') { const pattern = conditions[field].pattern if ($(field).value.match(pattern)) { $(field).setAttribute('aria-invalid', 'false') return 0 } else { $(field).setAttribute('aria-invalid', 'true') $(field).focus() return 1 } } if (conditions[field].condition === 'length') { if ($(field).value.length > 0) { $(field).setAttribute('aria-invalid', 'false') return 0 } else { $(field).setAttribute('aria-invalid', 'true') $(field).focus() return 1 } } }).some(x => x === 1) }
Partager