Files
crm_ecosplay/assets/app.js
Serreau Jovann f8155c9454 fix: update Keycloak group names for SITECONSEIL OAuth authentication
Problem: OAuth login failed because the authenticator was checking for
old Keycloak group names (super_admin_asso, gp_member) that no longer
exist in the master realm.

Changes:
- src/Security/KeycloakAuthenticator.php:106: resolveRoles() now checks
  for 'siteconseil_admin' instead of 'super_admin_asso' to grant ROLE_ROOT
- src/Controller/Admin/MembresController.php:140: member creation role
  resolution updated from 'super_admin_asso' to 'siteconseil_admin'
- templates/admin/membres.html.twig: checkbox values updated from
  'gp_member' to 'siteconseil_member' and 'super_admin_asso' to
  'siteconseil_admin' in the member management form
- assets/app.js:5-6: JS mutual exclusion logic updated to use new
  group values 'siteconseil_member' and 'siteconseil_admin'
- tests/Security/KeycloakAuthenticatorTest.php:79: test data updated
  from 'super_admin_asso' to 'siteconseil_admin'

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 19:01:21 +02:00

162 lines
6.6 KiB
JavaScript

import "./app.scss"
// Membre / Super Admin : mutuellement exclusif
document.addEventListener('DOMContentLoaded', () => {
const memberCheckbox = document.querySelector('input[value="siteconseil_member"]');
const adminCheckbox = document.querySelector('input[value="siteconseil_admin"]');
const otherGroupCheckboxes = () =>
[...document.querySelectorAll('input[name="groups[]"]')].filter(cb => cb !== memberCheckbox);
if (memberCheckbox && adminCheckbox) {
memberCheckbox.addEventListener('change', () => {
if (memberCheckbox.checked) otherGroupCheckboxes().forEach(cb => { cb.checked = false; });
});
adminCheckbox.addEventListener('change', () => {
if (!adminCheckbox.checked) return;
memberCheckbox.checked = false;
otherGroupCheckboxes().forEach(cb => { cb.checked = true; });
});
}
// Stats period selector
const periodSelect = document.getElementById('stats-period-select');
const customRange = document.getElementById('stats-custom-range');
if (periodSelect && customRange) {
periodSelect.addEventListener('change', () => {
customRange.classList.toggle('hidden', periodSelect.value !== 'custom');
});
}
// data-confirm
document.querySelectorAll('form[data-confirm]').forEach(form => {
form.addEventListener('submit', (e) => {
if (!confirm(form.dataset.confirm)) {
e.preventDefault();
}
});
});
// Sidebar dropdown toggle
document.querySelectorAll('.sidebar-dropdown-btn').forEach(btn => {
btn.addEventListener('click', () => {
const menu = btn.nextElementSibling;
const arrow = btn.querySelector('.sidebar-dropdown-arrow');
menu?.classList.toggle('hidden');
arrow?.classList.toggle('rotate-180');
});
});
// Mobile sidebar toggle
const sidebarToggle = document.getElementById('admin-sidebar-toggle');
const sidebar = document.getElementById('admin-sidebar');
const overlay = document.getElementById('admin-overlay');
if (sidebarToggle && sidebar && overlay) {
sidebarToggle.addEventListener('click', () => {
sidebar.classList.toggle('open');
});
overlay.addEventListener('click', () => {
sidebar.classList.remove('open');
});
}
// Mobile menu toggle (public)
const mobileMenuBtn = document.getElementById('mobile-menu-btn');
const mobileMenu = document.getElementById('mobile-menu');
const menuIconOpen = document.getElementById('menu-icon-open');
const menuIconClose = document.getElementById('menu-icon-close');
if (mobileMenuBtn && mobileMenu) {
mobileMenuBtn.addEventListener('click', () => {
mobileMenu.classList.toggle('hidden');
menuIconOpen?.classList.toggle('hidden');
menuIconClose?.classList.toggle('hidden');
});
}
// Cookie banner
const cookieBanner = document.getElementById('cookie-banner');
const cookieAccept = document.getElementById('cookie-accept');
const cookieRefuse = document.getElementById('cookie-refuse');
if (cookieBanner && !localStorage.getItem('cookie_consent')) {
cookieBanner.classList.remove('hidden');
}
cookieAccept?.addEventListener('click', () => {
localStorage.setItem('cookie_consent', 'accepted');
cookieBanner?.classList.add('hidden');
});
cookieRefuse?.addEventListener('click', () => {
localStorage.setItem('cookie_consent', 'refused');
cookieBanner?.classList.add('hidden');
});
// 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);
if (!input || !results) return;
let debounce;
input.addEventListener('input', () => {
clearTimeout(debounce);
const q = input.value.trim();
if (q.length < 2) {
results.classList.add('hidden');
results.innerHTML = '';
return;
}
debounce = setTimeout(() => performSearch(searchUrl, linkPrefix, results, q), 300);
});
document.addEventListener('click', (e) => {
if (!results.contains(e.target) && e.target !== input) {
results.classList.add('hidden');
}
});
};
setupSearch('search-customers', 'search-results', '/admin/clients/search', '/admin/clients/');
setupSearch('search-revendeurs', 'search-results-revendeurs', '/admin/revendeurs/search', '/admin/revendeurs/');
// Tarif tabs
const tarifTabs = document.getElementById('tarif-tabs');
if (tarifTabs) {
const active = 'px-6 py-3 font-bold uppercase text-sm tracking-wider glass-dark text-white border border-white/20 border-b-0 cursor-pointer';
const inactive = 'px-6 py-3 font-bold uppercase text-sm tracking-wider glass text-gray-700 border border-gray-200 border-b-0 cursor-pointer hover:bg-white/80 transition-all';
tarifTabs.querySelectorAll('[data-tab]').forEach(btn => {
btn.addEventListener('click', () => {
const tab = btn.dataset.tab;
tarifTabs.querySelectorAll('[data-tab]').forEach(b => {
b.className = b.dataset.tab === tab ? active : inactive;
});
document.querySelectorAll('[id^="content-"]').forEach(el => {
if (el.closest('#tarif-tabs')) return;
el.classList.toggle('hidden', el.id !== 'content-' + tab);
});
});
});
}
});