fix: replace deprecated HTML attributes and reduce JS nesting

- Replace email layout tables with CSS divs in base.html.twig
- Replace deprecated width/align attributes with CSS styles in PDF templates
- Add role="presentation" to email layout tables
- Convert td to th[scope=row] in profil and attestation verify tables
- Reduce function nesting in app.js by extracting renderHit/performSearch

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Serreau Jovann
2026-04-01 19:36:42 +02:00
parent 5f144ba4d2
commit 242f8337e1
10 changed files with 67 additions and 85 deletions

View File

@@ -4,27 +4,18 @@ import "./app.scss"
document.addEventListener('DOMContentLoaded', () => {
const memberCheckbox = document.querySelector('input[value="gp_member"]');
const adminCheckbox = document.querySelector('input[value="super_admin_asso"]');
const otherGroupCheckboxes = () =>
[...document.querySelectorAll('input[name="groups[]"]')].filter(cb => cb !== memberCheckbox);
if (memberCheckbox && adminCheckbox) {
memberCheckbox.addEventListener('change', () => {
if (memberCheckbox.checked) {
document.querySelectorAll('input[name="groups[]"]').forEach(cb => {
if (cb !== memberCheckbox) {
cb.checked = false;
}
});
}
if (memberCheckbox.checked) otherGroupCheckboxes().forEach(cb => { cb.checked = false; });
});
adminCheckbox.addEventListener('change', () => {
if (adminCheckbox.checked) {
memberCheckbox.checked = false;
document.querySelectorAll('input[name="groups[]"]').forEach(cb => {
if (cb !== memberCheckbox) {
cb.checked = true;
}
});
}
if (!adminCheckbox.checked) return;
memberCheckbox.checked = false;
otherGroupCheckboxes().forEach(cb => { cb.checked = true; });
});
}
@@ -105,6 +96,22 @@ document.addEventListener('DOMContentLoaded', () => {
});
// Search (customers & revendeurs)
const renderHit = (h, linkPrefix) =>
`<a href="${linkPrefix}${h.id}" class="block px-4 py-2 hover:bg-gray-50 border-b border-gray-100 text-xs">
<span class="font-bold">${h.fullName || h.raisonSociale || (h.firstName + ' ' + h.lastName)}</span>
${h.email ? `<span class="text-gray-400 ml-2">${h.email}</span>` : ''}
${h.codeRevendeur ? `<span class="ml-2 px-1 py-0.5 bg-gray-900 text-[#fabf04] text-[9px] font-bold">${h.codeRevendeur}</span>` : ''}
</a>`;
const performSearch = async (searchUrl, linkPrefix, results, q) => {
const resp = await fetch(`${searchUrl}?q=${encodeURIComponent(q)}`);
const hits = await resp.json();
results.innerHTML = hits.length === 0
? '<div class="px-4 py-3 text-xs text-gray-400">Aucun resultat.</div>'
: hits.map(h => renderHit(h, linkPrefix)).join('');
results.classList.remove('hidden');
};
const setupSearch = (inputId, resultsId, searchUrl, linkPrefix) => {
const input = document.getElementById(inputId);
const results = document.getElementById(resultsId);
@@ -119,22 +126,7 @@ document.addEventListener('DOMContentLoaded', () => {
results.innerHTML = '';
return;
}
debounce = setTimeout(async () => {
const resp = await fetch(`${searchUrl}?q=${encodeURIComponent(q)}`);
const hits = await resp.json();
if (hits.length === 0) {
results.innerHTML = '<div class="px-4 py-3 text-xs text-gray-400">Aucun resultat.</div>';
} else {
results.innerHTML = hits.map(h =>
`<a href="${linkPrefix}${h.id}" class="block px-4 py-2 hover:bg-gray-50 border-b border-gray-100 text-xs">
<span class="font-bold">${h.fullName || h.raisonSociale || (h.firstName + ' ' + h.lastName)}</span>
${h.email ? `<span class="text-gray-400 ml-2">${h.email}</span>` : ''}
${h.codeRevendeur ? `<span class="ml-2 px-1 py-0.5 bg-gray-900 text-[#fabf04] text-[9px] font-bold">${h.codeRevendeur}</span>` : ''}
</a>`
).join('');
}
results.classList.remove('hidden');
}, 300);
debounce = setTimeout(() => performSearch(searchUrl, linkPrefix, results, q), 300);
});
document.addEventListener('click', (e) => {

View File

@@ -42,19 +42,19 @@
<div class="border-2 border-gray-200 bg-gray-50 p-4">
<table class="w-full text-sm">
<tr class="border-b border-gray-200">
<td class="py-2 pr-4 font-black uppercase text-xs text-gray-400 w-1/3">Prenom</td>
<th scope="row" class="py-2 pr-4 font-black uppercase text-xs text-gray-400 w-1/3 text-left">Prenom</th>
<td class="py-2 font-bold">{{ app.user.firstName }}</td>
</tr>
<tr class="border-b border-gray-200">
<td class="py-2 pr-4 font-black uppercase text-xs text-gray-400">Nom</td>
<th scope="row" class="py-2 pr-4 font-black uppercase text-xs text-gray-400 text-left">Nom</th>
<td class="py-2 font-bold">{{ app.user.lastName }}</td>
</tr>
<tr class="border-b border-gray-200">
<td class="py-2 pr-4 font-black uppercase text-xs text-gray-400">Email</td>
<th scope="row" class="py-2 pr-4 font-black uppercase text-xs text-gray-400 text-left">Email</th>
<td class="py-2 font-bold font-mono text-xs">{{ app.user.email }}</td>
</tr>
<tr class="border-b border-gray-200">
<td class="py-2 pr-4 font-black uppercase text-xs text-gray-400">Connexion</td>
<th scope="row" class="py-2 pr-4 font-black uppercase text-xs text-gray-400 text-left">Connexion</th>
<td class="py-2">
{% if app.user.keycloakId %}
<span class="px-2 py-0.5 bg-green-100 text-green-800 font-black uppercase text-[10px]">Keycloak</span>
@@ -64,7 +64,7 @@
</td>
</tr>
<tr>
<td class="py-2 pr-4 font-black uppercase text-xs text-gray-400">Inscrit le</td>
<th scope="row" class="py-2 pr-4 font-black uppercase text-xs text-gray-400 text-left">Inscrit le</th>
<td class="py-2 text-xs text-gray-500">{{ app.user.createdAt|date('d/m/Y a H:i') }}</td>
</tr>
</table>

View File

@@ -34,11 +34,11 @@
<div class="p-6">
<table class="w-full text-sm">
<tr class="border-b border-gray-200">
<td class="py-3 pr-4 font-black uppercase text-xs text-gray-500 w-1/3">Reference</td>
<th scope="row" class="py-3 pr-4 font-black uppercase text-xs text-gray-500 w-1/3 text-left">Reference</th>
<td class="py-3 font-bold">{{ attestation.reference }}</td>
</tr>
<tr class="border-b border-gray-200">
<td class="py-3 pr-4 font-black uppercase text-xs text-gray-500">Type</td>
<th scope="row" class="py-3 pr-4 font-black uppercase text-xs text-gray-500 text-left">Type</th>
<td class="py-3 font-bold">
{% if attestation.type == 'access' %}
<span class="px-2 py-1 bg-indigo-100 text-indigo-800 text-xs font-black uppercase">Droit d'acces</span>
@@ -50,15 +50,15 @@
</td>
</tr>
<tr class="border-b border-gray-200">
<td class="py-3 pr-4 font-black uppercase text-xs text-gray-500">Date</td>
<th scope="row" class="py-3 pr-4 font-black uppercase text-xs text-gray-500 text-left">Date</th>
<td class="py-3 font-bold">{{ attestation.createdAt|date('d/m/Y a H:i:s') }}</td>
</tr>
<tr class="border-b border-gray-200">
<td class="py-3 pr-4 font-black uppercase text-xs text-gray-500">Adresse IP</td>
<th scope="row" class="py-3 pr-4 font-black uppercase text-xs text-gray-500 text-left">Adresse IP</th>
<td class="py-3 font-bold font-mono">{{ attestation.ip }}</td>
</tr>
<tr>
<td class="py-3 pr-4 font-black uppercase text-xs text-gray-500">Email</td>
<th scope="row" class="py-3 pr-4 font-black uppercase text-xs text-gray-500 text-left">Email</th>
<td class="py-3 font-bold">{{ attestation.email }}</td>
</tr>
</table>

View File

@@ -6,31 +6,21 @@
<title>{% block title %}CRM Ecosplay{% endblock %}</title>
</head>
<body style="margin: 0; padding: 0; background-color: #fbfbfb; font-family: Arial, Helvetica, sans-serif; color: #111827;">
<table role="presentation" width="100%" style="background-color: #fbfbfb; padding: 40px 0;">
<tr>
<td align="center">
<p style="font-size: 11px; color: #666; margin: 0 0 16px 0; text-align: center;">
Cet email ne s'affiche pas correctement ? <a href="__VIEW_URL__" style="color: #4338ca; text-decoration: underline;">Voir en ligne</a>
</p>
<table role="presentation" width="600" style="background-color: #ffffff; border: 4px solid #111827;">
<tr>
<td style="background-color: #fabf04; border-bottom: 4px solid #111827; padding: 20px; text-align: center;">
<img src="https://crm.e-cosplay.fr/logo.jpg" alt="CRM Ecosplay" width="120" style="display: block; margin: 0 auto;">
</td>
</tr>
<tr>
<td style="padding: 32px;">
{% block content %}{% endblock %}
</td>
</tr>
<tr>
<td style="background-color: #111827; color: #ffffff; padding: 16px; text-align: center; font-size: 11px; font-weight: bold; text-transform: uppercase; letter-spacing: 1px;">
&copy; {{ "now"|date("Y") }} Association E-Cosplay
</td>
</tr>
</table>
</td>
</tr>
</table>
<div style="background-color: #fbfbfb; padding: 40px 0; text-align: center;">
<p style="font-size: 11px; color: #666; margin: 0 0 16px 0; text-align: center;">
Cet email ne s'affiche pas correctement ? <a href="__VIEW_URL__" style="color: #4338ca; text-decoration: underline;">Voir en ligne</a>
</p>
<div style="max-width: 600px; margin: 0 auto; background-color: #ffffff; border: 4px solid #111827; text-align: left;">
<div style="background-color: #fabf04; border-bottom: 4px solid #111827; padding: 20px; text-align: center;">
<img src="https://crm.e-cosplay.fr/logo.jpg" alt="CRM Ecosplay" style="display: block; margin: 0 auto; width: 120px;">
</div>
<div style="padding: 32px;">
{% block content %}{% endblock %}
</div>
<div style="background-color: #111827; color: #ffffff; padding: 16px; text-align: center; font-size: 11px; font-weight: bold; text-transform: uppercase; letter-spacing: 1px;">
&copy; {{ "now"|date("Y") }} Association E-Cosplay
</div>
</div>
</div>
</body>
</html>

View File

@@ -8,7 +8,7 @@
<div style="background: #111827; color: #fff; padding: 20px; margin: 20px 0;">
<p style="font-size: 11px; text-transform: uppercase; letter-spacing: 1px; margin: 0 0 12px; opacity: 0.7;">Vos identifiants de connexion</p>
<table style="width: 100%; font-size: 14px;">
<table role="presentation" style="width: 100%; font-size: 14px;">
<tr>
<td style="color: #fabf04; font-weight: bold; padding: 4px 0; width: 120px;">Email</td>
<td style="color: #fff; padding: 4px 0;">{{ email }}</td>
@@ -45,7 +45,7 @@
</ul>
{% endif %}
<table style="margin: 24px 0;">
<table role="presentation" style="margin: 24px 0;">
<tr>
<td style="background: #fabf04; border: 2px solid #111827; padding: 12px 24px;">
<a href="{{ url('app_home') }}" style="color: #111827; font-weight: 900; text-transform: uppercase; font-size: 13px; text-decoration: none; letter-spacing: 1px;">Se connecter</a>

View File

@@ -13,7 +13,7 @@
<p style="font-size: 14px; line-height: 1.6; margin: 0 0 8px; font-weight: bold;">Pour acceder a votre espace, suivez ces etapes :</p>
<table style="width: 100%; margin: 16px 0;">
<table role="presentation" style="width: 100%; margin: 16px 0;">
<tr>
<td style="padding: 12px 16px; border-left: 4px solid #fabf04; background: #f9fafb;">
<p style="font-size: 12px; font-weight: 900; text-transform: uppercase; color: #fabf04; margin: 0 0 4px;">Etape 1</p>
@@ -36,7 +36,7 @@
</tr>
</table>
<table style="margin: 24px 0; width: 100%;">
<table role="presentation" style="margin: 24px 0; width: 100%;">
<tr>
<td style="background: #fabf04; border: 2px solid #111827; padding: 14px 24px; text-align: center;">
<a href="{{ setPasswordUrl }}" style="color: #111827; font-weight: 900; text-transform: uppercase; font-size: 14px; text-decoration: none; letter-spacing: 1px;">Definir mon mot de passe</a>
@@ -44,7 +44,7 @@
</tr>
</table>
<table style="margin: 0 0 24px; width: 100%;">
<table role="presentation" style="margin: 0 0 24px; width: 100%;">
<tr>
<td style="background: #fff; border: 2px solid #111827; padding: 12px 24px; text-align: center;">
<a href="{{ url('app_home') }}" style="color: #111827; font-weight: 900; text-transform: uppercase; font-size: 12px; text-decoration: none; letter-spacing: 1px;">Acceder a la page de connexion</a>
@@ -54,7 +54,7 @@
<div style="background: #f9fafb; padding: 16px; border: 1px solid #e5e7eb; margin: 16px 0;">
<p style="font-size: 12px; font-weight: bold; margin: 0 0 8px;">Informations de votre compte :</p>
<table style="font-size: 12px; width: 100%;">
<table role="presentation" style="font-size: 12px; width: 100%;">
<tr>
<td style="padding: 2px 0; color: #666; width: 120px;">Code revendeur</td>
<td style="padding: 2px 0; font-weight: bold; font-family: monospace;">{{ codeRevendeur }}</td>

View File

@@ -6,7 +6,7 @@
<h1 style="font-size: 20px; font-weight: bold; text-transform: uppercase; margin: 0 0 16px 0;">Attestation RGPD signee</h1>
<p style="font-size: 14px; line-height: 1.6; margin: 0 0 16px 0;">Bonjour,</p>
<p style="font-size: 14px; line-height: 1.6; margin: 0 0 16px 0;">Suite a votre demande, vous trouverez en piece jointe votre <strong>attestation d'{{ typeName }}</strong>, signee electroniquement par l'association E-Cosplay.</p>
<table style="margin: 16px 0; font-size: 13px; width: 100%;">
<table role="presentation" style="margin: 16px 0; font-size: 13px; width: 100%;">
<tr>
<td style="padding: 8px 12px; background: #f9fafb; border-left: 3px solid #fabf04; font-weight: bold;">Reference</td>
<td style="padding: 8px 12px; background: #f9fafb;">{{ attestation.reference }}</td>

View File

@@ -50,10 +50,10 @@
<div class="subtitle">RGPD &mdash; Article 15</div>
<table class="info-grid">
<tr>
<td class="info-cell" width="25%"><span class="info-label">Reference</span><span class="info-value">{{ attestation.reference }}</span></td>
<td class="info-cell" width="25%"><span class="info-label">Date</span><span class="info-value">{{ date|date('d/m/Y a H:i') }}</span></td>
<td class="info-cell" width="25%"><span class="info-label">Adresse IP</span><span class="info-value">{{ ip }}</span></td>
<td class="info-cell" width="25%"><span class="info-label">Sessions</span><span class="info-value">{{ data|length }}</span></td>
<td class="info-cell" style="width: 25%;"><span class="info-label">Reference</span><span class="info-value">{{ attestation.reference }}</span></td>
<td class="info-cell" style="width: 25%;"><span class="info-label">Date</span><span class="info-value">{{ date|date('d/m/Y a H:i') }}</span></td>
<td class="info-cell" style="width: 25%;"><span class="info-label">Adresse IP</span><span class="info-value">{{ ip }}</span></td>
<td class="info-cell" style="width: 25%;"><span class="info-label">Sessions</span><span class="info-value">{{ data|length }}</span></td>
</tr>
</table>
@@ -62,7 +62,7 @@
<div class="session-meta"><strong>ID :</strong> {{ entry.visitor.id }} &mdash; <strong>Visite :</strong> {{ entry.visitor.createdAt|date('d/m/Y H:i') }}</div>
{% if entry.events|length > 0 %}
<table class="data">
<thead><tr><th width="25%">Date</th><th width="20%">Type</th><th width="55%">Page</th></tr></thead>
<thead><tr><th style="width: 25%;">Date</th><th style="width: 20%;">Type</th><th style="width: 55%;">Page</th></tr></thead>
<tbody>
{% for event in entry.events %}
<tr><td>{{ event.createdAt|date('d/m/Y H:i:s') }}</td><td>{{ event.type }}</td><td>{{ event.page }}</td></tr>

View File

@@ -48,9 +48,9 @@
<div class="subtitle">RGPD &mdash; Article 17</div>
<table class="info-grid">
<tr>
<td class="info-cell" width="33%"><span class="info-label">Reference</span><span class="info-value">{{ attestation.reference }}</span></td>
<td class="info-cell" width="33%"><span class="info-label">Date</span><span class="info-value">{{ date|date('d/m/Y a H:i') }}</span></td>
<td class="info-cell" width="33%"><span class="info-label">Adresse IP</span><span class="info-value">{{ ip }}</span></td>
<td class="info-cell" style="width: 33%;"><span class="info-label">Reference</span><span class="info-value">{{ attestation.reference }}</span></td>
<td class="info-cell" style="width: 33%;"><span class="info-label">Date</span><span class="info-value">{{ date|date('d/m/Y a H:i') }}</span></td>
<td class="info-cell" style="width: 33%;"><span class="info-label">Adresse IP</span><span class="info-value">{{ ip }}</span></td>
</tr>
</table>
<div class="attestation-box">

View File

@@ -47,9 +47,9 @@
<div class="subtitle">RGPD &mdash; Article 15</div>
<table class="info-grid">
<tr>
<td class="info-cell" width="33%"><span class="info-label">Reference</span><span class="info-value">{{ attestation.reference }}</span></td>
<td class="info-cell" width="33%"><span class="info-label">Date</span><span class="info-value">{{ date|date('d/m/Y a H:i') }}</span></td>
<td class="info-cell" width="33%"><span class="info-label">Adresse IP</span><span class="info-value">{{ ip }}</span></td>
<td class="info-cell" style="width: 33%;"><span class="info-label">Reference</span><span class="info-value">{{ attestation.reference }}</span></td>
<td class="info-cell" style="width: 33%;"><span class="info-label">Date</span><span class="info-value">{{ date|date('d/m/Y a H:i') }}</span></td>
<td class="info-cell" style="width: 33%;"><span class="info-label">Adresse IP</span><span class="info-value">{{ ip }}</span></td>
</tr>
</table>
<div class="attestation-box">