From 155de270dc159e456162be604533b27107ac5e9b Mon Sep 17 00:00:00 2001 From: gilles Date: Sun, 22 Feb 2026 20:12:22 +0100 Subject: [PATCH] =?UTF-8?q?feat(settings):=20sliders=20taille=20texte/menu?= =?UTF-8?q?/ic=C3=B4nes/miniatures=20+=20CSS=20vars=20globales?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/App.vue | 17 ++- frontend/src/views/ReglagesView.vue | 192 +++++++++++++++++++++++++++- 2 files changed, 199 insertions(+), 10 deletions(-) diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 155936d..db01285 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -28,8 +28,8 @@ class="flex items-center gap-3 text-text-muted hover:text-text py-2 px-3 rounded-lg text-sm transition-colors group" active-class="bg-bg-soft text-green font-medium" > - {{ l.icon }} - {{ l.label }} + {{ l.icon }} + {{ l.label }}
@@ -38,7 +38,7 @@ -
+
@@ -123,11 +123,22 @@ function startDebugPolling() { }, 10000) } +function applyUiSizesFromSettings(data: Record) { + const defaults: Record = { ui_font_size: 14, ui_menu_font_size: 13, ui_menu_icon_size: 18, ui_thumb_size: 96 } + const root = document.documentElement + for (const [key, def] of Object.entries(defaults)) { + const val = Number(data[key]) || def + const prop = '--' + key.replace(/_/g, '-') + root.style.setProperty(prop, `${val}px`) + } +} + async function loadDebugModeFromApi() { try { const data = await settingsApi.get() debugMode.value = toBool(data.debug_mode) localStorage.setItem('debug_mode', debugMode.value ? '1' : '0') + applyUiSizesFromSettings(data) } catch { // On garde la valeur locale. } diff --git a/frontend/src/views/ReglagesView.vue b/frontend/src/views/ReglagesView.vue index 313ff50..da8f4da 100644 --- a/frontend/src/views/ReglagesView.vue +++ b/frontend/src/views/ReglagesView.vue @@ -2,6 +2,38 @@

Réglages

+
+

Interface

+

Ajustez les tailles d'affichage. Les changements sont appliqués instantanément.

+ +
+
+ + + {{ uiSizes[s.key] }}{{ s.unit }} +
+
+ +
+ + + {{ uiSavedMsg }} +
+
+

Général

Options globales de l'application.

@@ -35,14 +67,49 @@
+
+

Test API backend

+

+ Ouvre la documentation interactive de l'API et un test rapide de santé. +

+

Base API détectée: {{ apiBaseUrl }}

+
+ + + +
+
+
-

Idées utiles (prochaine étape)

-
    -
  • • Sauvegarde/restauration JSON de la base métier
  • -
  • • Rotation/nettoyage des médias anciens
  • -
  • • Choix des unités météo (°C, mm, km/h)
  • -
  • • Paramètres de seuils alertes (gel, pluie, vent)
  • -
+

Sauvegarde des données

+

+ Exporte un ZIP téléchargeable contenant la base SQLite, les images/vidéos uploadées et les fichiers texte utiles. +

+
+ + {{ backupMsg }} +
@@ -56,6 +123,90 @@ const debugMode = ref(false) const saving = ref(false) const savedMsg = ref('') const refreshingMeteo = ref(false) +const downloadingBackup = ref(false) +const backupMsg = ref('') +const apiBaseUrl = detectApiBaseUrl() + +// --- UI Size settings --- +const UI_DEFAULTS: Record = { + ui_font_size: 14, + ui_menu_font_size: 13, + ui_menu_icon_size: 18, + ui_thumb_size: 96, +} + +const uiSizeSettings = [ + { key: 'ui_font_size', label: 'Taille texte', min: 12, max: 20, step: 1, unit: 'px' }, + { key: 'ui_menu_font_size', label: 'Texte menu latéral', min: 11, max: 18, step: 1, unit: 'px' }, + { key: 'ui_menu_icon_size', label: 'Icônes menu', min: 14, max: 28, step: 1, unit: 'px' }, + { key: 'ui_thumb_size', label: 'Miniatures images/vidéo', min: 60, max: 200, step: 4, unit: 'px' }, +] + +const uiSizes = ref>({ ...UI_DEFAULTS }) +const savingUi = ref(false) +const uiSavedMsg = ref('') + +function applyUiSizes() { + const root = document.documentElement + root.style.setProperty('--ui-font-size', `${uiSizes.value.ui_font_size}px`) + root.style.setProperty('--ui-menu-font-size', `${uiSizes.value.ui_menu_font_size}px`) + root.style.setProperty('--ui-menu-icon-size', `${uiSizes.value.ui_menu_icon_size}px`) + root.style.setProperty('--ui-thumb-size', `${uiSizes.value.ui_thumb_size}px`) + window.dispatchEvent(new CustomEvent('ui-sizes-updated', { detail: { ...uiSizes.value } })) +} + +async function saveUiSettings() { + savingUi.value = true + uiSavedMsg.value = '' + try { + const payload: Record = {} + for (const [k, v] of Object.entries(uiSizes.value)) payload[k] = String(v) + await settingsApi.update(payload) + applyUiSizes() + uiSavedMsg.value = 'Enregistré' + setTimeout(() => { uiSavedMsg.value = '' }, 1800) + } finally { + savingUi.value = false + } +} + +function resetUiSettings() { + uiSizes.value = { ...UI_DEFAULTS } + applyUiSizes() +} + +function detectApiBaseUrl(): string { + const envBase = String((import.meta as any).env?.VITE_API_URL || '').trim() + if (envBase) { + if (envBase.startsWith('http://') || envBase.startsWith('https://')) { + return envBase.replace(/\/$/, '') + } + if (envBase.startsWith('/')) { + return window.location.origin + } + } + if (window.location.port === '8060') { + return window.location.origin + } + return `${window.location.protocol}//${window.location.hostname}:8060` +} + +function openInNewTab(path: string) { + const url = `${apiBaseUrl}${path}` + window.open(url, '_blank', 'noopener,noreferrer') +} + +function openApiDocs() { + openInNewTab('/docs') +} + +function openApiRedoc() { + openInNewTab('/redoc') +} + +function openApiHealth() { + openInNewTab('/api/health') +} function toBool(value: unknown): boolean { if (typeof value === 'boolean') return value @@ -73,6 +224,11 @@ async function loadSettings() { const data = await settingsApi.get() debugMode.value = toBool(data.debug_mode) notifyDebugChanged(debugMode.value) + for (const s of uiSizeSettings) { + const v = data[s.key] + if (v != null) uiSizes.value[s.key] = Number(v) || UI_DEFAULTS[s.key] + } + applyUiSizes() } catch { // Laisse la valeur locale si l'API n'est pas disponible. } @@ -100,6 +256,28 @@ async function refreshMeteo() { } } +async function downloadBackup() { + downloadingBackup.value = true + backupMsg.value = '' + try { + const { blob, filename } = await settingsApi.downloadBackup() + const url = window.URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = filename + document.body.appendChild(a) + a.click() + document.body.removeChild(a) + window.URL.revokeObjectURL(url) + backupMsg.value = 'Téléchargement lancé.' + } catch { + backupMsg.value = 'Erreur lors de la sauvegarde.' + } finally { + downloadingBackup.value = false + window.setTimeout(() => { backupMsg.value = '' }, 2200) + } +} + onMounted(() => { void loadSettings() })