gigafibre-fsm/apps/ops/src/composables/useTheme.js
louispaulb 512c4a5f1b feat(ops): dispatch auto complet + perf Boîte/rapports + fix session
Planificateur « Suggérer » : 4 stratégies (smart / meilleurs d'abord / équilibré /
juste ce qu'il faut), compétences+niveaux par tech (édition inline), niveau requis
par compétence + par job, carte des tournées (1 couleur/tech, domicile→arrêts,
sélecteur de jour), fenêtre de dispatch auj.+demain (dates sélectionnées), règle
week-end + placeholder « en attente du quart », clustering + lasso + filtre-date
sur la carte, accès rapide « À assigner » (badge).

Boîte : liste /conversations allégée (45 Mo → ~1 Mo, 17×) + messages chargés à
l'ouverture. Rapports : cache SWR sur revenue-explorer (22×). Session : keep-alive
+ timeout fetch global + authFetch durci → fin des rechargements manuels.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 08:49:35 -04:00

82 lines
4.5 KiB
JavaScript

// Éditeur de thème : regroupe les couleurs PRINCIPALES de l'app (variables --ops-*) en un seul endroit,
// avec presets (défaut indigo actuel + palette de marque Gigafibre extraite de gigafibre.ca), aperçu live et persistance.
// But : réduire/uniformiser les couleurs. Chaque token pilote une variable CSS --ops-* (+ miroir Quasar --q-* pour que
// les composants Quasar suivent). N.B. les ~2000 hex EN DUR dans les pages ne suivent PAS encore (migration séparée).
import { ref } from 'vue'
export const THEME_GROUPS = [
{ title: 'Marque', hint: "L'accent : boutons, liens, sélections, mises en valeur.", tokens: [
{ var: '--ops-accent', label: 'Accent (couleur de marque)', quasar: '--q-primary' },
] },
{ title: 'Sombre', hint: 'Barre latérale + tableau Dispatch.', tokens: [
{ var: '--ops-sidebar-bg', label: 'Fond sombre', mirror: '--ops-primary' },
] },
{ title: 'Surfaces', hint: 'Fonds, cartes, bordures.', tokens: [
{ var: '--ops-bg', label: 'Fond de page', mirror: '--ops-bg-light' },
{ var: '--ops-surface', label: 'Cartes / surfaces' },
{ var: '--ops-border', label: 'Bordures' },
{ var: '--ops-bg-hover', label: 'Survol / sélection' },
] },
{ title: 'Texte', tokens: [
{ var: '--ops-text', label: 'Texte principal' },
{ var: '--ops-text-muted', label: 'Texte discret' },
] },
{ title: 'États sémantiques', hint: 'Rôles unifiés (badges, boutons, icônes, notifications) — migrés depuis amber/orange/red/blue.', tokens: [
{ var: '--ops-success', label: 'Succès — payé, actif, complété', quasar: '--q-positive' },
{ var: '--ops-warning', label: 'Attention — en attente, priorité moyenne', quasar: '--q-warning' },
{ var: '--ops-danger', label: 'Erreur — destructif, en retard, urgent', quasar: '--q-negative' },
{ var: '--ops-info', label: 'Info — liens externes, en cours', quasar: '--q-info' },
] },
]
// Palettes. `default` = MODERNE (inspirée des refs zentry/marque : vert + near-black + off-white + teinte verte + gris),
// alignée sur css/app.scss. `vif` = vert lime éclatant (#22c11f de la ref — superbe mais texte blanc moins contrasté sur boutons).
// `indigo` = ancienne palette (legacy).
export const PRESETS = {
default: {
'--ops-accent': '#16a34a', '--ops-sidebar-bg': '#0d0d0d',
'--ops-bg': '#f4f5f2', '--ops-surface': '#ffffff', '--ops-border': '#e5e7e2', '--ops-bg-hover': '#dcf5d3',
'--ops-text': '#0d0d0d', '--ops-text-muted': '#6b7280',
'--ops-success': '#16a34a', '--ops-warning': '#f59e0b', '--ops-danger': '#ef4444', '--ops-info': '#2563eb',
},
vif: {
'--ops-accent': '#22c11f', '--ops-sidebar-bg': '#0d0d0d',
'--ops-bg': '#f4f5f2', '--ops-surface': '#ffffff', '--ops-border': '#e5e7e2', '--ops-bg-hover': '#cceecc',
'--ops-text': '#0d0d0d', '--ops-text-muted': '#6b7280',
'--ops-success': '#16a34a', '--ops-warning': '#f59e0b', '--ops-danger': '#ef4444', '--ops-info': '#2563eb',
},
indigo: {
'--ops-accent': '#6366f1', '--ops-sidebar-bg': '#111422',
'--ops-bg': '#f8fafc', '--ops-surface': '#ffffff', '--ops-border': '#e2e8f0', '--ops-bg-hover': '#eef2ff',
'--ops-text': '#1e293b', '--ops-text-muted': '#64748b',
'--ops-success': '#10b981', '--ops-warning': '#f59e0b', '--ops-danger': '#ef4444', '--ops-info': '#3b82f6',
},
}
const KEY = 'ops_theme_v1'
export const themeVars = ref({})
export function allTokens () { return THEME_GROUPS.flatMap(g => g.tokens) }
// Applique un jeu de couleurs en direct : pose les variables --ops-* (+ miroirs Quasar/alias) sur :root.
export function applyTheme (vars) {
const root = document.documentElement
for (const t of allTokens()) {
const v = vars[t.var]; if (!v) continue
root.style.setProperty(t.var, v)
if (t.mirror) root.style.setProperty(t.mirror, v)
if (t.quasar) root.style.setProperty(t.quasar, v)
}
themeVars.value = { ...vars }
}
// Valeurs effectives courantes (override appliqué OU valeur CSS calculée OU défaut).
export function currentValues () {
const cs = getComputedStyle(document.documentElement); const out = {}
for (const t of allTokens()) out[t.var] = (themeVars.value[t.var] || cs.getPropertyValue(t.var).trim() || PRESETS.default[t.var])
return out
}
export function saveTheme (vars) { try { localStorage.setItem(KEY, JSON.stringify(vars)) } catch (e) {} applyTheme(vars) }
export function resetTheme () { try { localStorage.removeItem(KEY) } catch (e) {} applyTheme(PRESETS.default) }
export function loadSavedTheme () { try { const s = localStorage.getItem(KEY); if (s) applyTheme(JSON.parse(s)) } catch (e) {} }