Gestion des Thèmes
This commit is contained in:
1 parent
bab7a79b63
commit
be3c93897b
24 files changed
+1041
-141
No files matched your search
@@ -27,9 +27,11 @@ function ensureRow() {
|
||||
// colonne applique automatiquement son DEFAULT (5 Go / 500 documents,
|
||||
// cf. migration dans db/index.js) quand elle est omise d'un INSERT.
|
||||
}
|
||||
// parametres_environnement : normalement déjà créée avec sa ligne par défaut
|
||||
// par la migration dans db/index.js (INSERT OR IGNORE) — filet de sécurité ici.
|
||||
// parametres_environnement / parametres_theme : normalement déjà créées avec
|
||||
// leur ligne par défaut par la migration dans db/index.js (INSERT OR IGNORE)
|
||||
// — filet de sécurité ici.
|
||||
db.prepare('INSERT OR IGNORE INTO parametres_environnement (id) VALUES (1)').run();
|
||||
db.prepare('INSERT OR IGNORE INTO parametres_theme (id) VALUES (1)').run();
|
||||
}
|
||||
|
||||
// 1 Go = 1024^3 octets (convention déjà utilisée par fmtOctets côté frontend).
|
||||
@@ -40,10 +42,12 @@ router.get('/', (_req, res, next) => {
|
||||
ensureRow();
|
||||
const row = db.prepare('SELECT app_name, app_url, mcp_url, allow_registration, min_password_length, documents_quota_bytes, documents_quota_count FROM smtp_config WHERE id = 1').get();
|
||||
const envRow = db.prepare('SELECT environnement FROM parametres_environnement WHERE id = 1').get();
|
||||
const themeRow = db.prepare('SELECT color_theme FROM parametres_theme WHERE id = 1').get();
|
||||
res.json({
|
||||
appName: row.app_name || 'Crowdlending Tracker',
|
||||
appUrl: row.app_url || '',
|
||||
environnement: envRow?.environnement || 'developpement',
|
||||
colorTheme: themeRow?.color_theme || 'classique',
|
||||
mcpUrl: row.mcp_url || '',
|
||||
allowRegistration: row.allow_registration !== 0,
|
||||
minPasswordLength: row.min_password_length || 8,
|
||||
@@ -57,6 +61,7 @@ const PatchSchema = z.object({
|
||||
appName: z.string().min(1).max(100).optional(),
|
||||
appUrl: z.string().max(500).optional(),
|
||||
environnement: z.enum(['production', 'qualification', 'developpement']).optional(),
|
||||
colorTheme: z.enum(['classique', 'indigo_teal', 'nuit', 'nature', 'coucher_de_soleil', 'violet', 'frais', 'contraste', 'pastel', 'moderne']).optional(),
|
||||
mcpUrl: z.string().max(500).optional(),
|
||||
allowRegistration: z.boolean().optional(),
|
||||
minPasswordLength: z.number().int().min(6).max(64).optional(),
|
||||
@@ -93,6 +98,9 @@ router.patch('/', (req, res, next) => {
|
||||
if (body.environnement !== undefined) {
|
||||
db.prepare('UPDATE parametres_environnement SET environnement = ? WHERE id = 1').run(body.environnement);
|
||||
}
|
||||
if (body.colorTheme !== undefined) {
|
||||
db.prepare('UPDATE parametres_theme SET color_theme = ? WHERE id = 1').run(body.colorTheme);
|
||||
}
|
||||
|
||||
res.json({ ok: true });
|
||||
} catch (e) { next(e); }
|
||||
|
||||
@@ -10,7 +10,18 @@ const ALLOWED_KEYS = new Set([
|
||||
'chart_cashback',
|
||||
'pfo_assujetti',
|
||||
'objectifs_actifs',
|
||||
// Extensible : ajouter ici les futures prefs (theme, font_scale, langue, devise, display_mode…)
|
||||
'color_theme',
|
||||
// Extensible : ajouter ici les futures prefs (font_scale, langue, devise, display_mode…)
|
||||
]);
|
||||
|
||||
// Thème de couleur (Admin > Général, cf. project_theme_interface.md) — même
|
||||
// énumération que la colonne parametres_theme.color_theme côté admin.
|
||||
// Contrairement aux autres clés de ALLOWED_KEYS, la valeur est validée ici
|
||||
// (pas seulement la clé) car une valeur invalide casserait silencieusement
|
||||
// le rendu CSS ([data-color-theme] côté frontend n'a pas de fallback).
|
||||
const COLOR_THEME_VALUES = new Set([
|
||||
'classique', 'indigo_teal', 'nuit', 'nature', 'coucher_de_soleil',
|
||||
'violet', 'frais', 'contraste', 'pastel', 'moderne',
|
||||
]);
|
||||
|
||||
/**
|
||||
@@ -51,6 +62,10 @@ router.patch('/', (req, res) => {
|
||||
return res.status(400).json({ error: `Clés inconnues : ${unknown.join(', ')}` });
|
||||
}
|
||||
|
||||
if ('color_theme' in payload && !COLOR_THEME_VALUES.has(payload.color_theme)) {
|
||||
return res.status(400).json({ error: `Valeur invalide pour color_theme : ${payload.color_theme}` });
|
||||
}
|
||||
|
||||
const persist = db.transaction(() => {
|
||||
for (const [key, value] of Object.entries(payload)) {
|
||||
upsert.run(req.user.id, key, String(value));
|
||||
@@ -61,4 +76,21 @@ router.patch('/', (req, res) => {
|
||||
res.json({ ok: true });
|
||||
});
|
||||
|
||||
/**
|
||||
* DELETE /api/preferences/:key
|
||||
* Supprime la préférence de l'utilisateur connecté pour une clé donnée —
|
||||
* utile pour revenir à un comportement "par défaut" (ex. "Automatique"
|
||||
* pour le thème de couleur, cf. Settings > Apparence) plutôt que de
|
||||
* stocker une valeur sentinelle : une clé absente n'est simplement plus
|
||||
* retournée par GET (le frontend retombe alors sur sa valeur par défaut).
|
||||
*/
|
||||
router.delete('/:key', (req, res) => {
|
||||
const { key } = req.params;
|
||||
if (!ALLOWED_KEYS.has(key)) {
|
||||
return res.status(400).json({ error: `Clé inconnue : ${key}` });
|
||||
}
|
||||
db.prepare('DELETE FROM user_preferences WHERE user_id = ? AND key = ?').run(req.user.id, key);
|
||||
res.json({ ok: true });
|
||||
});
|
||||
|
||||
export default router;
|
||||
Reference in new issue
Block a user