|
11 | 11 | .error-message { font-size: 24px; margin: 20px 0; } |
12 | 12 | .gif { width: 700px; margin-bottom: 25px; } |
13 | 13 | .footer { color: #aaa; font-size: 14px; margin-top: 40px; } |
| 14 | + .report-btn { background: #ef4444; color: white; border: none; border-radius: 8px; padding: 10px 20px; font-size: 16px; cursor: pointer; margin-top: 20px; } |
| 15 | + .report-btn:hover { background: #dc2626; } |
14 | 16 | </style> |
15 | 17 | </head> |
16 | 18 | <body> |
|
19 | 21 | <div class="error-type">ERROR_TYPE</div> |
20 | 22 | <div id="error-message" class="error-message">ERROR_MESSAGE</div> |
21 | 23 | <img class="gif" src="ERROR_GIF" alt="Error illustration"> |
22 | | - <button id="open-report-modal" data-site="MySite" data-link="https://example.com">Signaler une erreur</button> |
| 24 | + <!-- New report button --> |
| 25 | + <button id="open-report-modal" class="report-btn" data-link="" data-site="">Signaler cette erreur</button> |
23 | 26 | </div> |
| 27 | + <!-- Embedded front-end script for modal and reporting --> |
24 | 28 | <script> |
25 | 29 | // Front script - creates modal, grabs data, calls the worker, shows banner |
26 | 30 | // ====== helpers ====== |
| 31 | + // Try to extract an error code from the page |
27 | 32 | const getErrorCodeFromPage = () => { |
28 | | - const idEl = document.querySelector('#error-code'); |
29 | | - if (idEl && idEl.textContent.trim()) return idEl.textContent.trim(); |
30 | | - const bodyAttr = document.body?.getAttribute('data-error-code'); |
31 | | - if (bodyAttr) return bodyAttr.trim(); |
32 | | - const meta = document.querySelector('meta[name="error-code"]'); |
33 | | - if (meta && meta.getAttribute('content')) return meta.getAttribute('content').trim(); |
34 | | - const p = new URLSearchParams(location.search).get('errorCode'); |
35 | | - if (p) return p.trim(); |
36 | | - return 'N/A'; |
37 | | - }; |
| 33 | + // #error-code element text |
| 34 | + const idEl = document.querySelector('#error-code') |
| 35 | + if (idEl && idEl.textContent.trim()) return idEl.textContent.trim() |
| 36 | + // body[data-error-code] |
| 37 | + const bodyAttr = document.body?.getAttribute('data-error-code') |
| 38 | + if (bodyAttr) return bodyAttr.trim() |
| 39 | + // <meta name="error-code" content="..."> |
| 40 | + const meta = document.querySelector('meta[name="error-code"]') |
| 41 | + if (meta && meta.getAttribute('content')) return meta.getAttribute('content').trim() |
| 42 | + // ?errorCode=XYZ |
| 43 | + const p = new URLSearchParams(location.search).get('errorCode') |
| 44 | + if (p) return p.trim() |
| 45 | + // fallback |
| 46 | + return 'N/A' |
| 47 | + } |
| 48 | + // Create a top-of-page success alert |
38 | 49 | const showTopAlert = msg => { |
39 | | - const bar = document.createElement('div'); |
40 | | - bar.textContent = msg; |
41 | | - bar.setAttribute('role', 'status'); |
42 | | - bar.style.position = 'fixed'; |
43 | | - bar.style.top = '0'; |
44 | | - bar.style.left = '0'; |
45 | | - bar.style.right = '0'; |
46 | | - bar.style.padding = '12px 16px'; |
47 | | - bar.style.textAlign = 'center'; |
48 | | - bar.style.fontWeight = '600'; |
49 | | - bar.style.background = '#16a34a'; |
50 | | - bar.style.color = 'white'; |
51 | | - bar.style.zIndex = '2147483647'; |
52 | | - document.body.appendChild(bar); |
53 | | - setTimeout(() => bar.remove(), 4000); |
54 | | - }; |
| 50 | + const bar = document.createElement('div') |
| 51 | + bar.textContent = msg |
| 52 | + bar.setAttribute('role', 'status') |
| 53 | + bar.style.position = 'fixed' |
| 54 | + bar.style.top = '0' |
| 55 | + bar.style.left = '0' |
| 56 | + bar.style.right = '0' |
| 57 | + bar.style.padding = '12px 16px' |
| 58 | + bar.style.textAlign = 'center' |
| 59 | + bar.style.fontWeight = '600' |
| 60 | + bar.style.background = '#16a34a' |
| 61 | + bar.style.color = 'white' |
| 62 | + bar.style.zIndex = '2147483647' |
| 63 | + document.body.appendChild(bar) |
| 64 | + setTimeout(() => bar.remove(), 4000) |
| 65 | + } |
| 66 | + // ====== modal ====== |
55 | 67 | const ensureModal = () => { |
56 | | - if (document.getElementById('report-error-modal')) return; |
57 | | - const overlay = document.createElement('div'); |
58 | | - overlay.id = 'report-error-modal'; |
59 | | - overlay.style.position = 'fixed'; |
60 | | - overlay.style.inset = '0'; |
61 | | - overlay.style.background = 'rgba(0,0,0,0.4)'; |
62 | | - overlay.style.display = 'none'; |
63 | | - overlay.style.alignItems = 'center'; |
64 | | - overlay.style.justifyContent = 'center'; |
65 | | - overlay.style.zIndex = '2147483646'; |
66 | | - const box = document.createElement('div'); |
67 | | - box.style.background = 'white'; |
68 | | - box.style.borderRadius = '12px'; |
69 | | - box.style.padding = '20px'; |
70 | | - box.style.width = 'min(92vw, 420px)'; |
71 | | - box.style.boxShadow = '0 10px 30px rgba(0,0,0,0.25)'; |
72 | | - box.style.fontFamily = 'system-ui, -apple-system, Segoe UI, Roboto, sans-serif'; |
73 | | - const title = document.createElement('h2'); |
74 | | - title.textContent = '🆘 Erreur signalée'; |
75 | | - title.style.margin = '0 0 12px 0'; |
76 | | - title.style.fontSize = '20px'; |
77 | | - const label = document.createElement('label'); |
78 | | - label.textContent = 'Nom / Prénom'; |
79 | | - label.style.display = 'block'; |
80 | | - label.style.fontSize = '14px'; |
81 | | - label.style.marginBottom = '6px'; |
82 | | - const input = document.createElement('input'); |
83 | | - input.type = 'text'; |
84 | | - input.placeholder = 'Ex: Jane Doe'; |
85 | | - input.required = true; |
86 | | - input.style.width = '100%'; |
87 | | - input.style.boxSizing = 'border-box'; |
88 | | - input.style.padding = '10px 12px'; |
89 | | - input.style.fontSize = '16px'; |
90 | | - input.style.border = '1px solid #d1d5db'; |
91 | | - input.style.borderRadius = '8px'; |
92 | | - input.id = 'reporter-fullname'; |
93 | | - const actions = document.createElement('div'); |
94 | | - actions.style.display = 'flex'; |
95 | | - actions.style.gap = '8px'; |
96 | | - actions.style.marginTop = '16px'; |
97 | | - const cancel = document.createElement('button'); |
98 | | - cancel.type = 'button'; |
99 | | - cancel.textContent = 'Annuler'; |
100 | | - cancel.style.flex = '1'; |
101 | | - cancel.style.padding = '10px 12px'; |
102 | | - cancel.style.border = '1px solid #d1d5db'; |
103 | | - cancel.style.borderRadius = '8px'; |
104 | | - cancel.style.background = 'white'; |
105 | | - cancel.onclick = () => hideModal(); |
106 | | - const submit = document.createElement('button'); |
107 | | - submit.type = 'button'; |
108 | | - submit.textContent = 'Signaler'; |
109 | | - submit.style.flex = '1'; |
110 | | - submit.style.padding = '10px 12px'; |
111 | | - submit.style.border = '0'; |
112 | | - submit.style.borderRadius = '8px'; |
113 | | - submit.style.background = '#ef4444'; |
114 | | - submit.style.color = 'white'; |
115 | | - submit.style.fontWeight = '600'; |
| 68 | + if (document.getElementById('report-error-modal')) return |
| 69 | + const overlay = document.createElement('div') |
| 70 | + overlay.id = 'report-error-modal' |
| 71 | + overlay.style.position = 'fixed' |
| 72 | + overlay.style.inset = '0' |
| 73 | + overlay.style.background = 'rgba(0,0,0,0.4)' |
| 74 | + overlay.style.display = 'none' |
| 75 | + overlay.style.alignItems = 'center' |
| 76 | + overlay.style.justifyContent = 'center' |
| 77 | + overlay.style.zIndex = '2147483646' |
| 78 | + const box = document.createElement('div') |
| 79 | + box.style.background = 'white' |
| 80 | + box.style.borderRadius = '12px' |
| 81 | + box.style.padding = '20px' |
| 82 | + box.style.width = 'min(92vw, 420px)' |
| 83 | + box.style.boxShadow = '0 10px 30px rgba(0,0,0,0.25)' |
| 84 | + box.style.fontFamily = 'system-ui, -apple-system, Segoe UI, Roboto, sans-serif' |
| 85 | + const title = document.createElement('h2') |
| 86 | + title.textContent = '🆘 Erreur signalée' |
| 87 | + title.style.margin = '0 0 12px 0' |
| 88 | + title.style.fontSize = '20px' |
| 89 | + const label = document.createElement('label') |
| 90 | + label.textContent = 'Nom / Prénom' |
| 91 | + label.style.display = 'block' |
| 92 | + label.style.fontSize = '14px' |
| 93 | + label.style.marginBottom = '6px' |
| 94 | + const input = document.createElement('input') |
| 95 | + input.type = 'text' |
| 96 | + input.placeholder = 'Ex: Jane Doe' |
| 97 | + input.required = true |
| 98 | + input.style.width = '100%' |
| 99 | + input.style.boxSizing = 'border-box' |
| 100 | + input.style.padding = '10px 12px' |
| 101 | + input.style.fontSize = '16px' |
| 102 | + input.style.border = '1px solid #d1d5db' |
| 103 | + input.style.borderRadius = '8px' |
| 104 | + input.id = 'reporter-fullname' |
| 105 | + const actions = document.createElement('div') |
| 106 | + actions.style.display = 'flex' |
| 107 | + actions.style.gap = '8px' |
| 108 | + actions.style.marginTop = '16px' |
| 109 | + const cancel = document.createElement('button') |
| 110 | + cancel.type = 'button' |
| 111 | + cancel.textContent = 'Annuler' |
| 112 | + cancel.style.flex = '1' |
| 113 | + cancel.style.padding = '10px 12px' |
| 114 | + cancel.style.border = '1px solid #d1d5db' |
| 115 | + cancel.style.borderRadius = '8px' |
| 116 | + cancel.style.background = 'white' |
| 117 | + cancel.onclick = () => hideModal() |
| 118 | + const submit = document.createElement('button') |
| 119 | + submit.type = 'button' |
| 120 | + submit.textContent = 'Signaler' |
| 121 | + submit.style.flex = '1' |
| 122 | + submit.style.padding = '10px 12px' |
| 123 | + submit.style.border = '0' |
| 124 | + submit.style.borderRadius = '8px' |
| 125 | + submit.style.background = '#ef4444' |
| 126 | + submit.style.color = 'white' |
| 127 | + submit.style.fontWeight = '600' |
116 | 128 | submit.onclick = async () => { |
117 | | - const fullName = input.value.trim(); |
| 129 | + const fullName = input.value.trim() |
118 | 130 | if (!fullName) { |
119 | | - input.focus(); |
120 | | - return; |
| 131 | + input.focus() |
| 132 | + return |
121 | 133 | } |
122 | | - const trigger = document.getElementById('open-report-modal'); |
123 | | - const redirectUrl = trigger?.getAttribute('data-link') || location.href; |
124 | | - const siteName = trigger?.getAttribute('data-site') || location.hostname; |
125 | | - const errorCode = getErrorCodeFromPage(); |
| 134 | + // Collect data |
| 135 | + const trigger = document.getElementById('open-report-modal') |
| 136 | + const redirectUrl = trigger?.getAttribute('data-link') || location.href |
| 137 | + const siteName = trigger?.getAttribute('data-site') || location.hostname |
| 138 | + const errorCode = getErrorCodeFromPage() |
| 139 | + // Call the worker endpoint |
126 | 140 | try { |
127 | 141 | const res = await fetch('/report-error', { |
128 | 142 | method: 'POST', |
129 | 143 | headers: { 'content-type': 'application/json' }, |
130 | 144 | body: JSON.stringify({ fullName, errorCode, siteName, redirectUrl }) |
131 | | - }); |
132 | | - const data = await res.json().catch(() => ({})); |
133 | | - if (!res.ok || !data.ok) throw new Error(data.error || 'send failed'); |
134 | | - hideModal(); |
135 | | - showTopAlert('Erreur signalée !'); |
| 145 | + }) |
| 146 | + const data = await res.json().catch(() => ({})) |
| 147 | + if (!res.ok || !data.ok) throw new Error(data.error || 'send failed') |
| 148 | + hideModal() |
| 149 | + showTopAlert('Erreur signalée !') |
136 | 150 | } catch (e) { |
137 | | - hideModal(); |
138 | | - showTopAlert('Envoi impossible'); |
139 | | - console.error('Discord webhook failed', e); |
| 151 | + hideModal() |
| 152 | + showTopAlert('Envoi impossible') |
| 153 | + console.error('Discord webhook failed', e) |
140 | 154 | } |
141 | | - }; |
142 | | - actions.appendChild(cancel); |
143 | | - actions.appendChild(submit); |
144 | | - box.appendChild(title); |
145 | | - box.appendChild(label); |
146 | | - box.appendChild(input); |
147 | | - box.appendChild(actions); |
148 | | - overlay.appendChild(box); |
149 | | - document.body.appendChild(overlay); |
150 | | - }; |
| 155 | + } |
| 156 | + actions.appendChild(cancel) |
| 157 | + actions.appendChild(submit) |
| 158 | + box.appendChild(title) |
| 159 | + box.appendChild(label) |
| 160 | + box.appendChild(input) |
| 161 | + box.appendChild(actions) |
| 162 | + overlay.appendChild(box) |
| 163 | + document.body.appendChild(overlay) |
| 164 | + } |
151 | 165 | const showModal = () => { |
152 | | - ensureModal(); |
153 | | - const overlay = document.getElementById('report-error-modal'); |
154 | | - const input = document.getElementById('reporter-fullname'); |
155 | | - overlay.style.display = 'flex'; |
156 | | - setTimeout(() => input?.focus(), 0); |
157 | | - }; |
| 166 | + ensureModal() |
| 167 | + const overlay = document.getElementById('report-error-modal') |
| 168 | + const input = document.getElementById('reporter-fullname') |
| 169 | + overlay.style.display = 'flex' |
| 170 | + setTimeout(() => input?.focus(), 0) |
| 171 | + } |
158 | 172 | const hideModal = () => { |
159 | | - const overlay = document.getElementById('report-error-modal'); |
160 | | - if (overlay) overlay.style.display = 'none'; |
161 | | - }; |
| 173 | + const overlay = document.getElementById('report-error-modal') |
| 174 | + if (overlay) overlay.style.display = 'none' |
| 175 | + } |
| 176 | + // ====== wire the button ====== |
162 | 177 | const wireButton = () => { |
163 | | - ensureModal(); |
164 | | - const btn = document.getElementById('open-report-modal'); |
165 | | - if (!btn) return; |
| 178 | + ensureModal() |
| 179 | + const btn = document.getElementById('open-report-modal') |
| 180 | + if (!btn) return |
166 | 181 | btn.addEventListener('click', e => { |
167 | | - e.preventDefault(); |
168 | | - showModal(); |
169 | | - }); |
170 | | - }; |
171 | | - document.addEventListener('DOMContentLoaded', wireButton); |
| 182 | + e.preventDefault() |
| 183 | + showModal() |
| 184 | + }) |
| 185 | + // Set dynamic attributes |
| 186 | + btn.setAttribute('data-link', location.href) |
| 187 | + btn.setAttribute('data-site', location.hostname) |
| 188 | + } |
| 189 | + document.addEventListener('DOMContentLoaded', wireButton) |
172 | 190 | </script> |
173 | 191 | </body> |
174 | 192 | </html> |
0 commit comments