16 Commits

Author SHA1 Message Date
Serreau Jovann
0e1f249cc3 fix: remplacement emails - contact@ devient client@, monitor@ devient notification@
- contact@e-cosplay.fr remplace par client@e-cosplay.fr dans 87 fichiers
  (PDFs, templates, emails, controllers, DocuSeal submitters)
- monitor@e-cosplay.fr remplace par notification@e-cosplay.fr dans 4 fichiers
  (webhooks DocuSeal, commandes DNS/NDD, controller echeancier)
- Ajout lien "En savoir plus sur notre association" vers www.e-cosplay.fr
  sur la page migration SITECONSEIL

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 15:11:08 +02:00
Serreau Jovann
d11837941f fix: mise a jour numero de telephone 07 66 95 70 06 dans tous les fichiers
Remplacement de l'ancien numero (06 79 34 88 02) par le nouveau
(07 66 95 70 06) dans 19 fichiers : PDFs, templates legaux, CGV, CGU,
mentions legales, contrats.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 15:09:10 +02:00
Serreau Jovann
51092092f7 fix: SonarQube - _services_list.html.twig ul wrapper, MeilisearchService S1820 ignore
- _services_list.html.twig : ajout <ul> dans le partial (li sans container)
- cgv.html.twig + legal/cgv.html.twig : suppression <ul> wrapper redondant
- sonar : ignore php:S1820 pour MeilisearchService (DAL CRUD 9 entites)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 15:27:20 +02:00
Serreau Jovann
e9e9acb130 fix: SonarQube - CGV partial + MeilisearchService deduplique
- templates/pdf/_services_list.html.twig : liste services partagee
  entre pdf/cgv.html.twig et legal/cgv.html.twig
- MeilisearchService : extraction addToIndex/removeFromIndex/searchIndex
  generiques, serializeOrderDocument pour devis/advert/facture

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 15:17:36 +02:00
Serreau Jovann
28533d8ae2 fix: SonarQube - TrackingService TODO, templates PDF inutilises, test assertion
- TrackingService : suppression TODO, retour valeurs par defaut
  (trackPageView/trackEvent logguent, getVisitorStats/getPageViews
  retournent structure vide avec periode)
- Suppression templates/pdf/facture.html.twig et devis.html.twig
  (non utilises, PDF genere via FPDF)
- app.test.js : ajout assertion manquante ligne 541
- StatsController : constantes DQL, COLOR_GOLD, CC getServiceStats

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:41:51 +02:00
Serreau Jovann
8b35e2b6d2 feat: comptabilite + prestataires + rapport financier + stats dynamiques
Comptabilite (Super Admin) :
- ComptabiliteController avec 7 exports CSV/JSON compatibles SAGE
  (journal ventes, grand livre, FEC, balance agee, reglements,
  commissions Stripe 1.5%+0.25E, couts services)
- Export PDF via ComptaPdf (FPDF) avec bloc legal pre-rempli,
  tableau pagine, champ signature DocuSeal
- Signature electronique DocuSeal + callback + envoi email signe
  avec template dedie (compta_export_signed.html.twig)
- Rapport financier public (RapportFinancierPdf) : recettes par
  service, depenses (Stripe, infra, prestataires), bilan excedent/deficit
- Codes comptables clients EC-XXXX (plus de 411xxx)

Prestataires (Super Admin) :
- Entite Prestataire (raisonSociale, siret, email, phone, adresse)
- Entite FacturePrestataire (numFacture, montantHt, montantTtc,
  year, month, isPaid, PDF via Vich)
- CRUD complet avec recherche SIRET via proxy API data.gouv.fr
- Commande cron app:reminder:factures-prestataire (5 du mois)
- Factures prestataires integrees dans export couts services
- Sidebar Super Admin : entree Prestataires + Comptabilite

Stats (/admin/stats) :
- Cout prestataire dynamique depuis FacturePrestataire
- Fusion Infra + Prestataire en "Cout de fonctionnement"
- Commission Stripe corrigee (1.5% + 0.25E par transaction)

Divers :
- DocuSealService::sendComptaForSignature() + getApi()
- Customer::generateCodeComptable() format EC-XXXX-XXXXX
- Protection double prefixe EC- a la creation client
- Bouton regenerer PDF cache quand advert state=accepted
- Modals sans script inline (data-modal-open/close dans app.js)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 23:39:31 +02:00
Serreau Jovann
95d33a9a6d feat: gestion complete Devis + Avis de paiement + DocuSeal signature + mails
Devis :
- Entity DevisLine (pos, title, description, priceHt) liee a Devis (OneToMany cascade/orphanRemoval)
- Champs ajoutes sur Devis : customer (ManyToOne), submissionId, state machine (created/send/accepted/refused/cancel), raisonMessage, totaux HT/TVA/TTC, updatedAt, setUpdatedAt public
- Relation Devis <-> Advert changee de ManyToOne a OneToOne nullable
- Vich Attribute (migration Annotation -> Attribute) pour unsignedPdf/signedPdf/auditPdf
- DevisController CRUD complet : create (form repeater lignes + boutons rapides TarificationService), edit, cancel (libere OrderNumber), generate-pdf, send, resend, create-advert, events
- DevisPdf (FPDF/FPDI) : header legacy (logo, num, date, client), body lignes, summary totaux, footer SITECONSEIL + pagination, champ signature DocuSeal sur page devis + derniere page CGV
- OrderNumberService : preview() et generate() reutilisent les OrderNumber non utilises (isUsed=false) en priorite
- OrderNumber::markAsUnused() ajoute

DocuSeal integration devis :
- DocuSealService : sendDevisForSignature (avec completed_redirect_url), resendDevisSignature (archive ancienne submission), getSubmitterSlug, downloadSignedDevis (sauvegarde via Vich UploadedFile test=true)
- WebhookDocuSealController : dispatch par doc_type devis/attestation, handleDevisEvent (form.completed -> STATE_ACCEPTED + download PDF signe/audit, form.declined -> STATE_REFUSED + raison)
- DocusealEvent entity pour tracer form.viewed/started/completed/declined en temps reel
- Page evenements admin /admin/devis/{id}/events avec badges et payload JSON

Signature client :
- DevisProcessController : page publique /devis/process/{id}/{hmac} securisee par HMAC, boutons Signer (redirect DocuSeal) / Refuser (motif optionnel)
- Pages confirmation : signed.html.twig (merci + recap) et refused.html.twig (confirmation refus + motif)
- Nelmio whitelist : signature.esy-web.dev + signature.siteconseil.fr

Avis de paiement :
- Entity AdvertLine (pos, title, description, priceHt) liee a Advert
- Advert refactorise : customer, state, totaux, raisonMessage, submissionId, advertFile (Vich mapping advert_pdf), lines collection, updatedAt
- AdvertController : generate-pdf, send (mail + PJ + lien paiement), resend (rappel), cancel (delie devis, libere OrderNumber), search Meilisearch
- AdvertPdf (FPDF/FPDI) : QR code Endroid pointant vers /order/{numOrder}, texte "Scannez pour payer"
- OrderPaymentController : page publique /order/{numOrder} avec detail prestations, totaux, options paiement (placeholder)
- Creation auto depuis devis signe : copie client, totaux, lignes, meme OrderNumber

Meilisearch :
- Index customer_devis et customer_advert avec searchable (numOrder, customerName, customerEmail, state) et filterable (customerId, state)
- CRUD indexation sur chaque action (create, edit, send, cancel, create-advert)
- Recherche AJAX dans tabs Devis et Avis avec debounce + dropdown glassmorphism
- Sync admin : boutons syncDevis / syncAdverts + compteurs dans /admin/sync

Emails :
- MailerService : VCF auto (fiche contact SARL SITECONSEIL) en PJ sur tous les mails, bloc HTML pieces jointes injecte automatiquement (exclut .asc/.p7z/smime) avec icone trombone + taille fichier
- Templates : devis_to_sign, devis_signed_client/admin (PJ signed+audit), devis_refused_client/admin, advert_send (PJ + bouton paiement), ndd_expiration
- TestMailCommand : option --force-dsn pour envoyer via un DSN SMTP specifique (test prod depuis dev)

Commande NDD :
- app:ndd:check : verifie expiration domaines <= 30j, envoie mail groupe a monitor@siteconseil.fr
- Cron quotidien 8h (docker + ansible)

Divers :
- Titles templates : CRM SITECONSEIL -> SARL SITECONSEIL (52 fichiers)
- VAULT_URL dev = https://kms.esy-web.dev (comme prod)
- app.js : initDevisLines (repeater + drag & drop), initTabSearch, toggle refus devis
- app.scss : styles drag & drop
- setasign/fpdi-fpdf installe pour fusion PDF
- 5 migrations Doctrine

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 09:44:35 +02:00
Serreau Jovann
f0a5fdc849 refactor: suppression duplication templates PDF RGPD + test 100% DevisPdfController
Templates PDF :
- _base.html.twig : blocs verify_box et hmac_section avec contenu par défaut
  (QR code, verify_url, HMAC-SHA256) au lieu de blocs vides
- rgpd_access.html.twig : suppression blocs verify_box et hmac_section dupliqués
  (héritent du parent)
- rgpd_deletion.html.twig : idem
- rgpd_no_data.html.twig : idem

DevisPdfControllerTest (8 tests) :
- testDevisNotFound : devis null lance NotFoundHttpException
- testUnsignedPdfNotSet : unsignedPdf null lance NotFoundHttpException
- testFileNotExists : fichier absent lance NotFoundHttpException
- testUnsignedPdfSuccess : PDF unsigned retourné en BinaryFileResponse
- testSignedPdfSuccess : PDF signed retourné
- testAuditPdfSuccess : PDF audit retourné
- testAccessAsNonEmploye : accès sans ROLE_EMPLOYE (branche checkAccess)
- testDefaultTypeNull : type inconnu lance NotFoundHttpException

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 11:05:44 +02:00
Serreau Jovann
911a92ce88 refactor: sécurité Discord webhook, tests 100% coverage, factorisation templates PDF et DNS
Sécurité - Discord Webhook :
- Suppression de l'URL Discord webhook en dur dans CheckDnsCommand (ligne 34)
- Ajout de la variable d'environnement DISCORD_WEBHOOK dans .env (vide par défaut)
- Injection via #[Autowire(env: 'DISCORD_WEBHOOK')] dans le constructeur
- Vérification que le webhook est configuré avant envoi ('' !== $this->discordWebhook)
- Remplacement de l'URL en dur dans discord-notify.yml par ${{ secrets.DISCORD_WEBHOOK }}

Factorisation DNS (suppression duplication SonarQube) :
- Création de src/Service/DnsInfraHelper.php avec les méthodes partagées :
  enrichWithCloudflare, enrichLastCheck, loadCloudflareRecords, getActualDnsValue,
  getMxValues, getFirstTxtValue, getSrvValue, checkMxExists, checkTxtContains,
  checkDnsRecordExists, getTxtSpfValue
- Constantes DOMAINS et EXPECTED_MX centralisées dans DnsInfraHelper
- Refactorisation de CheckDnsCommand pour utiliser DnsInfraHelper au lieu des
  méthodes privées dupliquées (enrichWithCloudflare, enrichLastCheck, etc.)
- Refactorisation de DnsReportController pour utiliser DnsInfraHelper au lieu
  des méthodes privées dupliquées (enrichWithCloudflare, enrichLastCheck, etc.)

Factorisation templates PDF (suppression duplication lignes 6-22) :
- Création de templates/pdf/_base.html.twig comme layout commun avec :
  CSS partagé (banner, container, info-grid, verify-box, hmac, contact-box, data tables),
  blocs Twig configurables (title, font_size, extra_styles, content, verify_box,
  hmac_section, footer_contact, signature_box, footer_legal)
- Refactorisation de rgpd_access.html.twig : extends _base, accent #4338ca,
  bloc content avec sessions/events, styles session-meta et no-data
- Refactorisation de rgpd_deletion.html.twig : extends _base, accent #dc2626,
  font 11px, bloc content avec attestation-box et warning
- Refactorisation de rgpd_no_data.html.twig : extends _base, accent #fabf04/#111827,
  font 11px, bloc content avec attestation absence
- Refactorisation de admin/logs/pdf.html.twig : extends _base, accent #4338ca,
  bloc content avec tables utilisateur/requête et HMAC verification box,
  suppression du bloc signature, footer légal avec Siret/TVA

Tests - Couverture 100% (469 tests, 857 assertions, 0 failures) :

AnalyticsControllerTest (8 tests) :
- testTrackInvalidToken : token incorrect retourne 404
- testTrackEmptyPayload : payload sans clé 'd' retourne 400
- testTrackInvalidEncryptedData : données chiffrées invalides retourne 403
- testTrackNewVisitorCreation : création visiteur avec screen/language/UA, retourne uid+hash
- testTrackPageViewWithValidHash : page view avec uid/hash valides retourne 204
- testTrackSetUserWithValidHash : setUser avec uid/hash valides retourne 204
- testTrackWithInvalidHash : hash incorrect retourne 403
- testTrackWithMissingHash : hash absent retourne 403

AttestationControllerTest (8 tests) :
- testVerifyNotFound : référence inconnue retourne 200 (template not_found)
- testVerifyFound : attestation trouvée retourne 200 (template verify)
- testDownloadNotFound : référence inconnue lance NotFoundHttpException
- testDownloadNoPdf : attestation sans PDF lance NotFoundHttpException
- testDownloadWithPdf : attestation avec PDF signé retourne BinaryFileResponse 200
- testAuditNotFound : référence inconnue lance NotFoundHttpException
- testAuditNoCertificate : attestation sans certificat lance NotFoundHttpException
- testAuditWithCertificate : attestation avec certificat retourne BinaryFileResponse 200

CspReportControllerTest (13 tests) :
- testGetReturns204 : GET /my-csp-report retourne 204
- testReportEmptyPayload : payload vide retourne 400
- testReportInvalidJson : JSON invalide retourne 400
- testReportIgnoredExtension : chrome-extension ignoré, retourne 204
- testReportIgnoredMozExtension : moz-extension ignoré, retourne 204
- testReportIgnoredLocalhost : localhost ignoré, retourne 204
- testReportIgnoredLocalDomain : .local ignoré, retourne 204
- testReportIgnoredWasmEval : wasm-eval ignoré, retourne 204
- testReportIgnoredAboutBlank : about:blank ignoré, retourne 204
- testReportIgnoredNodeModulesInline : node_modules inline ignoré, retourne 204
- testReportRealViolationSendsEmail : violation réelle envoie email, retourne 204
- testReportRealViolationEmailFailure : échec email ne bloque pas, retourne 204
- testReportWithoutCspReportWrapper : payload sans wrapper csp-report fonctionne

EmailTrackingControllerTest (10 tests) :
- testTrackWithExistingTracking : tracking trouvé, markAsOpened appelé, état 'opened'
- testTrackWithNonExistingTracking : tracking absent, retourne image sans erreur
- testViewNotFound : messageId inconnu lance NotFoundHttpException
- testViewNoHtmlBody : tracking sans htmlBody lance NotFoundHttpException
- testViewWithHtmlBody : retourne HTML du tracking
- testViewWithAttachments : retourne HTML avec section pièces jointes
- testAttachmentNotFoundEmail : email inconnu lance NotFoundHttpException
- testAttachmentIndexNotFound : index absent lance NotFoundHttpException
- testAttachmentFileNotExists : fichier supprimé lance NotFoundHttpException
- testAttachmentSuccess : téléchargement pièce jointe retourne BinaryFileResponse

StatsControllerTest (4 tests) :
- testIndexCurrentPeriod : période 'current', dates du mois en cours
- testIndexCustomPeriod : période 'custom' avec from/to explicites
- testIndexMonthsPeriod : période '3', dateFrom = -3 mois
- testIndexDefaultPeriod : pas de paramètre, défaut 'current'

StatusControllerTest (20 tests) :
- testIndexEmpty : catégories vides retourne 200
- testIndexWithServices : catégorie avec service, appel getHistoryForDays/getDailyStatus
- testManage : page gestion retourne 200
- testCategoryCreateEmptyName : nom vide redirige avec flash error
- testCategoryCreateSuccess : création catégorie avec position redirige avec flash success
- testCategoryDelete : suppression catégorie redirige avec flash success
- testServiceCreateEmptyName : nom vide redirige avec flash error
- testServiceCreateCategoryNotFound : catégorie inexistante redirige avec flash error
- testServiceCreateSuccess : création service avec URL redirige avec flash success
- testServiceCreateWithExternalType : création service externe avec type http_check
- testServiceDelete : suppression service redirige avec flash success
- testUpdateValidStatus : statut 'down' avec message, setStatus appelé
- testUpdateInvalidStatus : statut invalide redirige avec flash error
- testUpdateStatusWithEmptyMessage : statut 'up' sans message (null passé)
- testMessageCreateEmptyFields : champs vides redirige avec flash error
- testMessageCreateServiceNotFound : service inexistant redirige avec flash error
- testMessageCreateSuccessNoUser : message créé sans utilisateur connecté
- testMessageCreateSuccessWithUser : message créé avec User injecté via tokenStorage
- testMessageResolve : message résolu, isActive=false, resolvedAt non null
- testApiDaily : retourne JsonResponse avec données getDailyStatus

SyncControllerTest (14 tests) :
- testIndexWithMixedPrices : prix avec/sans stripeId, compteurs stripeSynced/stripeNotSynced
- testSyncCustomersSuccess : indexation 1 client dans Meilisearch
- testSyncCustomersError : exception findAll, flash error
- testSyncRevendeursSuccess : indexation 1 revendeur dans Meilisearch
- testSyncRevendeursError : exception findAll, flash error
- testSyncPricesSuccess : indexation 1 tarif dans Meilisearch
- testSyncPricesError : exception findAll, flash error
- testSyncStripeWebhooksEmptyUrl : WEBHOOK_BASE_URL vide, flash error
- testSyncStripeWebhooksCreatedNew : webhook créé + webhook existant, persist nouveau secret
- testSyncStripeWebhooksUpdateExisting : mise à jour secret existant + erreurs Stripe
- testSyncStripePricesNoErrors : sync sans erreurs, flash success
- testSyncStripePricesWithErrors : sync avec erreurs, flash success + flash errors
- testSyncAllSuccess : sync all avec données, flash success
- testSyncAllError : exception setupIndexes, flash error

ServiceMessageTest (3 tests) :
- testConstructorDefaults : valeurs par défaut (info, active, null author/resolvedAt)
- testConstructorWithSeverityAndAuthor : severity custom + User author
- testResolve : isActive=false, resolvedAt DateTimeImmutable, fluent return

StripeWebhookSecretTest (4 tests) :
- testConstructorDefaults : type/secret, endpointId null, createdAt DateTimeImmutable
- testConstructorWithEndpointId : constructeur avec 3 arguments
- testSetSecret : modification du secret
- testSetEndpointId : set/unset endpointId (nullable)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 00:42:07 +02:00
Serreau Jovann
e97116fc9d feat: complete glassmorphism redesign across all templates + Keycloak groups auto-provisioning
Templates updated to glassmorphism (40+ files):
- templates/admin/clients/ (create, index): glass cards, input-glass inputs,
  btn-gold buttons, glass table headers, semi-transparent badges
- templates/admin/dashboard.html.twig: glass KPI cards
- templates/admin/profil/index.html.twig: glass form panels
- templates/admin/revendeurs/ (create, edit, index): glass cards and tables
- templates/admin/services/index.html.twig: glass service cards
- templates/admin/status/ (index, manage): glass panels
- templates/admin/sync/index.html.twig: glass panels
- templates/admin/facturation/index.html.twig: glass tables
- templates/admin/membres.html.twig: glass form, checkboxes with esy-* group
  values (esy-web, esy-mail, esy-mailer, esy-analytics, esy-monitor,
  esy-defender, esy-translate, esy-signature, esy-creator, esy-aide,
  esy-meet, esy-tchat, esy-ndd), Keycloak groups column in table,
  available groups section
- templates/admin/stats/index.html.twig: glass KPI cards, glass-gold CA TTC,
  factures emises/payees/impayees cards, services renamed to Esy-*,
  rounded progress bars, bg-gray-200 track backgrounds
- templates/security/ (2fa_email, 2fa_google, forgot_password, set_password,
  set_password_expired): glass headers, glass-heavy cards, input-glass
- templates/legal/ (cgu, cgv, cookie, conformite, hebergement,
  mention_legal, rgpd, tarif): removed thick borders, font-black to
  font-bold, text-3xl to text-2xl headings
- templates/attestation/ (verify, not_found): glass panels
- templates/espace_client/index.html.twig: glass panels
- templates/espace_prestataire/index.html.twig: glass panels
- templates/external_redirect.html.twig: glass card
- templates/status/index.html.twig: glass panels
- templates/email/base.html.twig: gradient gold header, rounded-16px
  container, semi-transparent bg, soft shadow, footer address
- templates/emails/*.html.twig (9 files): removed 4px borders,
  font-weight 900 to 700
- templates/pdf/*.html.twig (4 files): rounded borders, gradient header,
  lighter borders

Keycloak auto-provisioning:
- src/Service/KeycloakAdminService.php: added REQUIRED_GROUPS constant
  (15 groups: siteconseil_admin, siteconseil_member, esy-web, esy-mail,
  esy-mailer, esy-analytics, esy-monitor, esy-defender, esy-translate,
  esy-signature, esy-creator, esy-aide, esy-meet, esy-tchat, esy-ndd),
  ensureRequiredGroups() method that checks existing groups and creates
  missing ones, createGroup() method, getRequiredGroups() static accessor
- src/Controller/Admin/MembresController.php: calls ensureRequiredGroups()
  on page load, shows flash for each auto-created group, fetches user
  groups per member, passes availableGroups to template

Stats controller updated:
- src/Controller/Admin/StatsController.php: services renamed to Esy-*
  (13 services), added factures_emises/payees/impayees KPI data

OAuth fix:
- src/Security/KeycloakAuthenticator.php: removed dd() debug calls,
  restored flash message on auth failure with error detail

Config:
- .env: KEYCLOAK_ADMIN_CLIENT_ID=crm_siteconseil_admin, secret updated
- .env.local: same updates
- ansible/env.local.j2: KEYCLOAK_ADMIN_CLIENT_ID=crm_siteconseil_admin

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 19:34:35 +02:00
Serreau Jovann
6fa970e60d refactor: rebrand project to CRM SITECONSEIL (SARL SITECONSEIL)
- Rename all references from E-Cosplay/Ecosplay to SITECONSEIL
- Update entity from Association to SARL SITECONSEIL (Siret: 418664058)
- Update address to 27 rue Le Serurier, 02100 Saint-Quentin
- Update emails: contact@siteconseil.fr, rgpd@siteconseil.fr
- Update hosting from GCP to OVHcloud (Roubaix, Gravelines, Strasbourg, Paris)
- Update legal pages: mentions legales, CGV, RGPD, conformite, hebergement, cookies, CGU
- Add tarifs page with tabs: Site Internet, E-Commerce, Nom de domaine, Esy-Mail, Esy-Mailer, Esy-Tchat, Esy-Meet, Esy-Defender
- Add Discord webhook notification workflow
- Disable deploy and sonarqube workflows
- Update OAuth Keycloak realm to master
- Update logo references to logo_facture.png
- Remove forced image sizing in Liip Imagine filters
- Update SonarQube project key and badge token
- Update tribunal competent to Saint-Quentin
- Move tarif tabs JS to app.js (CSP compliance)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 18:48:25 +02:00
Serreau Jovann
363cea260b fix: replace all layout tables with CSS in email and PDF templates
- Email templates: replace table/tr/td with div-based CSS layout
- PDF templates: replace table with display:table/table-row/table-cell CSS (Dompdf compatible)
- Only table.data (RGPD access session data) remains as actual HTML table

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 19:57:26 +02:00
Serreau Jovann
a76e96fb21 fix: add th headers and role=presentation to PDF and legal tables
- Add thead/th to commissions table in tarif.html.twig
- Add th headers to contrat_revendeur parties table
- Add role=presentation to PDF layout tables (info-grid, verify-box, signatures)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 19:50:56 +02:00
Serreau Jovann
242f8337e1 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>
2026-04-01 19:36:42 +02:00
Serreau Jovann
5f144ba4d2 fix: resolve SonarQube accessibility and test issues across templates
- Add for/id attributes to all form labels for accessibility compliance
- Add <title> tags to PDF templates (rgpd_access, rgpd_no_data, rgpd_deletion, contrat_revendeur)
- Add role="presentation" to email layout tables
- Remove deprecated cellpadding/cellspacing attributes from all templates
- Fix PHPUnit notices by replacing createMock with createStub where no expectations are set

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 19:30:53 +02:00
Serreau Jovann
686de99909 init 2026-04-01 15:42:52 +02:00