|
19 | 19 | <div class="error-type">ERROR_TYPE</div> |
20 | 20 | <div id="error-message" class="error-message">ERROR_MESSAGE</div> |
21 | 21 | <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> |
22 | 23 | </div> |
| 24 | + <script> |
| 25 | + // Front script - creates modal, grabs data, calls the worker, shows banner |
| 26 | + // ====== helpers ====== |
| 27 | + 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 | + }; |
| 38 | + 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 | + }; |
| 55 | + 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'; |
| 116 | + submit.onclick = async () => { |
| 117 | + const fullName = input.value.trim(); |
| 118 | + if (!fullName) { |
| 119 | + input.focus(); |
| 120 | + return; |
| 121 | + } |
| 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(); |
| 126 | + try { |
| 127 | + const res = await fetch('/report-error', { |
| 128 | + method: 'POST', |
| 129 | + headers: { 'content-type': 'application/json' }, |
| 130 | + 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 !'); |
| 136 | + } catch (e) { |
| 137 | + hideModal(); |
| 138 | + showTopAlert('Envoi impossible'); |
| 139 | + console.error('Discord webhook failed', e); |
| 140 | + } |
| 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 | + }; |
| 151 | + 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 | + }; |
| 158 | + const hideModal = () => { |
| 159 | + const overlay = document.getElementById('report-error-modal'); |
| 160 | + if (overlay) overlay.style.display = 'none'; |
| 161 | + }; |
| 162 | + const wireButton = () => { |
| 163 | + ensureModal(); |
| 164 | + const btn = document.getElementById('open-report-modal'); |
| 165 | + if (!btn) return; |
| 166 | + btn.addEventListener('click', e => { |
| 167 | + e.preventDefault(); |
| 168 | + showModal(); |
| 169 | + }); |
| 170 | + }; |
| 171 | + document.addEventListener('DOMContentLoaded', wireButton); |
| 172 | + </script> |
23 | 173 | </body> |
24 | 174 | </html> |
0 commit comments