diff --git a/src/baby_monitor/routers/health.py b/src/baby_monitor/routers/health.py index 9bf7c88..aa510fc 100644 --- a/src/baby_monitor/routers/health.py +++ b/src/baby_monitor/routers/health.py @@ -1,6 +1,7 @@ """Health check router for monitoring endpoints.""" -from fastapi import APIRouter +import os +from fastapi import APIRouter, HTTPException router = APIRouter(tags=["health"]) @@ -20,11 +21,53 @@ def readiness_check() -> dict[str, str]: """ Readiness check endpoint for container orchestration. - Returns 200 if the service is ready to accept traffic. - This can include checks for database connectivity, external services, etc. + Returns 200 if the service is ready to accept traffic, else 503. """ - # Add dependency checks here if needed (database, cache, etc.) - # For now, simple readiness check + + # Determine if configured to use Redis + redis_uri = os.getenv("REDIS_URI") + + # If Redis is used, check connectivity + if redis_uri: + try: + import redis + + redis_client = redis.from_url(redis_uri) + redis_client.ping() + except Exception as exc: + raise HTTPException( + status_code=503, + detail={ + "status": "not ready", + "reason": "No connection to Redis", + "exception": str(exc), + }, + ) from exc + # Determine if configured to use PostgreSQL + postgres_uri = os.getenv("POSTGRES_URI") + + # If PostgreSQL is used, check connectivity + if postgres_uri: + try: + from sqlalchemy import create_engine + from sqlalchemy.orm import sessionmaker + from sqlalchemy import text + + engine = create_engine(postgres_uri) + SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + with SessionLocal() as db: + db.execute(text("SELECT 1")) + except Exception as exc: + raise HTTPException( + status_code=503, + detail={ + "status": "not ready", + "reason": "No connection to PostgreSQL", + "exception": str(exc), + }, + ) from exc + + # Unable to prove not ready, so assume ready return {"status": "ready"} @@ -60,9 +103,9 @@ def service_info() -> dict: # Check current configuration config = { "environment": os.getenv("ENVIRONMENT", "production"), - "data_dir": os.getenv("DATA_DIR", "/data"), - "redis_configured": bool(os.getenv("REDIS_URI")), } + if not features["postgresql"]: + config["data_dir"] = os.getenv("DATA_DIR", "/data") return { "service": "baby-monitor", diff --git a/src/baby_monitor/static/chart-theme.js b/src/baby_monitor/static/chart-theme.js index c87918c..99a263b 100644 --- a/src/baby_monitor/static/chart-theme.js +++ b/src/baby_monitor/static/chart-theme.js @@ -3,201 +3,205 @@ * Usage: Include this before your chart initialization code */ -(function() { - 'use strict'; - - // Helper to get effective theme - function getEffectiveTheme() { - return window.getEffectiveTheme ? window.getEffectiveTheme() : 'light'; - } - - // Get theme-appropriate colors for Chart.js - window.getChartColors = function() { - const isDark = getEffectiveTheme() === 'dark'; - - return { - // Grid lines - grid: isDark ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.05)', - gridMajor: isDark ? 'rgba(255, 255, 255, 0.15)' : 'rgba(0, 0, 0, 0.1)', - - // Text colors - text: isDark ? '#b0b0b0' : '#666666', - textPrimary: isDark ? '#e0e0e0' : '#333333', - - // Data series colors (optimized for background) - series: isDark ? [ - 'rgba(139, 158, 255, 0.8)', // Brighter purple - 'rgba(110, 197, 255, 0.8)', // Brighter blue - 'rgba(255, 179, 255, 0.8)', // Brighter pink - 'rgba(129, 199, 132, 0.8)', // Brighter green - 'rgba(255, 183, 77, 0.8)', // Brighter orange - 'rgba(156, 107, 184, 0.8)', // Brighter deep purple - 'rgba(77, 255, 255, 0.8)', // Brighter cyan - 'rgba(255, 138, 155, 0.8)', // Brighter red - ] : [ - 'rgba(102, 126, 234, 0.8)', // Purple - 'rgba(118, 75, 162, 0.8)', // Deep purple - 'rgba(255, 159, 64, 0.8)', // Orange - 'rgba(76, 175, 80, 0.8)', // Green - 'rgba(244, 67, 54, 0.8)', // Red - 'rgba(156, 39, 176, 0.8)', // Violet - 'rgba(102, 126, 234, 0.6)', // Light purple - 'rgba(118, 75, 162, 0.6)', // Light deep purple - ], - - // Solid data colors (for pie charts, etc.) - solidSeries: isDark ? [ - '#8b9eff', - '#6ec5ff', - '#ffb3ff', - '#81c784', - '#ffb74d', - '#9c6bb8', - '#4dffff', - '#ff8a9b', - ] : [ - '#667eea', - '#764ba2', - '#ff9f40', - '#4caf50', - '#f44336', - '#9c27b0', - '#667eea', - '#764ba2', - ], - - // Background colors - background: isDark ? '#2d2d2d' : '#f8f9fa', - }; +(function () { + "use strict"; + + // Helper to get effective theme + function getEffectiveTheme() { + return window.getEffectiveTheme ? window.getEffectiveTheme() : "light"; + } + + // Get theme-appropriate colors for Chart.js + window.getChartColors = function () { + const isDark = getEffectiveTheme() === "dark"; + + return { + // Grid lines + grid: isDark ? "rgba(255, 255, 255, 0.1)" : "rgba(0, 0, 0, 0.05)", + gridMajor: isDark ? "rgba(255, 255, 255, 0.15)" : "rgba(0, 0, 0, 0.1)", + + // Text colors + text: isDark ? "#b0b0b0" : "#666666", + textPrimary: isDark ? "#e0e0e0" : "#333333", + + // Data series colors (optimized for background) + series: isDark + ? [ + "rgba(139, 158, 255, 0.8)", // Brighter purple + "rgba(110, 197, 255, 0.8)", // Brighter blue + "rgba(255, 179, 255, 0.8)", // Brighter pink + "rgba(129, 199, 132, 0.8)", // Brighter green + "rgba(255, 183, 77, 0.8)", // Brighter orange + "rgba(156, 107, 184, 0.8)", // Brighter deep purple + "rgba(77, 255, 255, 0.8)", // Brighter cyan + "rgba(255, 138, 155, 0.8)", // Brighter red + ] + : [ + "rgba(102, 126, 234, 0.8)", // Purple + "rgba(118, 75, 162, 0.8)", // Deep purple + "rgba(255, 159, 64, 0.8)", // Orange + "rgba(76, 175, 80, 0.8)", // Green + "rgba(244, 67, 54, 0.8)", // Red + "rgba(156, 39, 176, 0.8)", // Violet + "rgba(102, 126, 234, 0.6)", // Light purple + "rgba(118, 75, 162, 0.6)", // Light deep purple + ], + + // Solid data colors (for pie charts, etc.) + solidSeries: isDark + ? [ + "#8b9eff", + "#6ec5ff", + "#ffb3ff", + "#81c784", + "#ffb74d", + "#9c6bb8", + "#4dffff", + "#ff8a9b", + ] + : [ + "#667eea", + "#764ba2", + "#ff9f40", + "#4caf50", + "#f44336", + "#9c27b0", + "#667eea", + "#764ba2", + ], + + // Background colors + background: isDark ? "#2d2d2d" : "#f8f9fa", }; - - // Get default Chart.js options with theme support - window.getChartOptions = function(customOptions = {}) { - const colors = window.getChartColors(); - - const defaults = { - responsive: true, - maintainAspectRatio: false, - plugins: { - legend: { - labels: { - color: colors.text, - font: { - size: 12 - } - } - }, - tooltip: { - backgroundColor: getEffectiveTheme() === 'dark' ? '#3a3a3a' : '#ffffff', - titleColor: colors.textPrimary, - bodyColor: colors.text, - borderColor: colors.grid, - borderWidth: 1, - } + }; + + // Get default Chart.js options with theme support + window.getChartOptions = function (customOptions = {}) { + const colors = window.getChartColors(); + + const defaults = { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + labels: { + color: colors.text, + font: { + size: 12, }, - scales: {} - }; - - // Deep merge customOptions with defaults - return mergeDeep(defaults, customOptions); + }, + }, + tooltip: { + backgroundColor: + getEffectiveTheme() === "dark" ? "#3a3a3a" : "#ffffff", + titleColor: colors.textPrimary, + bodyColor: colors.text, + borderColor: colors.grid, + borderWidth: 1, + }, + }, + scales: {}, }; - - // Get theme-appropriate scale options - window.getChartScaleOptions = function(axis = 'x', customOptions = {}) { - const colors = window.getChartColors(); - - const defaults = { - grid: { - color: colors.grid, - borderColor: colors.gridMajor, - }, - ticks: { - color: colors.text, - font: { - size: 12 - } - }, - title: { - color: colors.textPrimary, - font: { - size: 13, - weight: '600' - } - } - }; - - return mergeDeep(defaults, customOptions); + + // Deep merge customOptions with defaults + return mergeDeep(defaults, customOptions); + }; + + // Get theme-appropriate scale options + window.getChartScaleOptions = function (axis = "x", customOptions = {}) { + const colors = window.getChartColors(); + + const defaults = { + grid: { + color: colors.grid, + borderColor: colors.gridMajor, + }, + ticks: { + color: colors.text, + font: { + size: 12, + }, + }, + title: { + color: colors.textPrimary, + font: { + size: 13, + weight: "600", + }, + }, }; - - // Helper function to deep merge objects - function mergeDeep(target, source) { - const output = Object.assign({}, target); - if (isObject(target) && isObject(source)) { - Object.keys(source).forEach(key => { - if (isObject(source[key])) { - if (!(key in target)) { - Object.assign(output, { [key]: source[key] }); - } else { - output[key] = mergeDeep(target[key], source[key]); - } - } else { - Object.assign(output, { [key]: source[key] }); - } - }); + + return mergeDeep(defaults, customOptions); + }; + + // Helper function to deep merge objects + function mergeDeep(target, source) { + const output = Object.assign({}, target); + if (isObject(target) && isObject(source)) { + Object.keys(source).forEach((key) => { + if (isObject(source[key])) { + if (!(key in target)) { + Object.assign(output, { [key]: source[key] }); + } else { + output[key] = mergeDeep(target[key], source[key]); + } + } else { + Object.assign(output, { [key]: source[key] }); } - return output; + }); } - - function isObject(item) { - return item && typeof item === 'object' && !Array.isArray(item); + return output; + } + + function isObject(item) { + return item && typeof item === "object" && !Array.isArray(item); + } + + // Function to update all charts on theme change + window.updateChartsForTheme = function (chartInstances) { + if (!Array.isArray(chartInstances)) { + chartInstances = [chartInstances]; } - - // Function to update all charts on theme change - window.updateChartsForTheme = function(chartInstances) { - if (!Array.isArray(chartInstances)) { - chartInstances = [chartInstances]; - } - - const colors = window.getChartColors(); - - chartInstances.forEach(chart => { - if (!chart) return; - - // Update grid colors - if (chart.options.scales) { - Object.keys(chart.options.scales).forEach(axis => { - if (chart.options.scales[axis].grid) { - chart.options.scales[axis].grid.color = colors.grid; - chart.options.scales[axis].grid.borderColor = colors.gridMajor; - } - if (chart.options.scales[axis].ticks) { - chart.options.scales[axis].ticks.color = colors.text; - } - if (chart.options.scales[axis].title) { - chart.options.scales[axis].title.color = colors.textPrimary; - } - }); - } - - // Update legend colors - if (chart.options.plugins && chart.options.plugins.legend) { - if (chart.options.plugins.legend.labels) { - chart.options.plugins.legend.labels.color = colors.text; - } - } - - // Update tooltip colors - if (chart.options.plugins && chart.options.plugins.tooltip) { - chart.options.plugins.tooltip.backgroundColor = - getEffectiveTheme() === 'dark' ? '#3a3a3a' : '#ffffff'; - chart.options.plugins.tooltip.titleColor = colors.textPrimary; - chart.options.plugins.tooltip.bodyColor = colors.text; - chart.options.plugins.tooltip.borderColor = colors.grid; - } - - // Update the chart - chart.update(); + + const colors = window.getChartColors(); + + chartInstances.forEach((chart) => { + if (!chart) return; + + // Update grid colors + if (chart.options.scales) { + Object.keys(chart.options.scales).forEach((axis) => { + if (chart.options.scales[axis].grid) { + chart.options.scales[axis].grid.color = colors.grid; + chart.options.scales[axis].grid.borderColor = colors.gridMajor; + } + if (chart.options.scales[axis].ticks) { + chart.options.scales[axis].ticks.color = colors.text; + } + if (chart.options.scales[axis].title) { + chart.options.scales[axis].title.color = colors.textPrimary; + } }); - }; - + } + + // Update legend colors + if (chart.options.plugins && chart.options.plugins.legend) { + if (chart.options.plugins.legend.labels) { + chart.options.plugins.legend.labels.color = colors.text; + } + } + + // Update tooltip colors + if (chart.options.plugins && chart.options.plugins.tooltip) { + chart.options.plugins.tooltip.backgroundColor = + getEffectiveTheme() === "dark" ? "#3a3a3a" : "#ffffff"; + chart.options.plugins.tooltip.titleColor = colors.textPrimary; + chart.options.plugins.tooltip.bodyColor = colors.text; + chart.options.plugins.tooltip.borderColor = colors.grid; + } + + // Update the chart + chart.update(); + }); + }; })(); diff --git a/src/baby_monitor/static/dark-mode.css b/src/baby_monitor/static/dark-mode.css index b4e4ab5..0d7e24d 100644 --- a/src/baby_monitor/static/dark-mode.css +++ b/src/baby_monitor/static/dark-mode.css @@ -3,198 +3,130 @@ */ :root { - /* ===== LIGHT MODE (Default) ===== */ - - /* Background colors */ - --bg-primary: #f0f0f0; - --bg-secondary: #ffffff; - --bg-tertiary: #f8f9fa; - --bg-card: #ffffff; - - /* Text colors */ - --text-primary: #333333; - --text-secondary: #666666; - --text-muted: #999999; - --text-inverse: #ffffff; - - /* Border colors */ - --border-color: #dddddd; - --border-light: #e0e0e0; - --border-lighter: #eeeeee; - - /* Gradients - Brand */ - --gradient-purple: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - --gradient-blue: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); - --gradient-pink: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); - --gradient-green: linear-gradient(135deg, #56ab2f 0%, #a8e063 100%); - --gradient-orange: linear-gradient(135deg, #f2994a 0%, #f2c94c 100%); - - /* Action colors */ - --color-success: #4caf50; - --color-error: #dc3545; - --color-warning: #ff9800; - --color-info: #2196f3; - --color-danger: #f44336; - - /* Action gradients */ - --gradient-success: linear-gradient(135deg, #56ab2f 0%, #a8e063 100%); - --gradient-error: linear-gradient(135deg, #dc3545 0%, #c62828 100%); - --gradient-danger: linear-gradient(135deg, #f44336 0%, #d32f2f 100%); - - /* Interactive elements */ - --input-bg: #ffffff; - --input-border: #dddddd; - --input-focus-border: #667eea; - --input-text: #333333; - --input-placeholder: #999999; - - /* Shadows */ - --shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.1); - --shadow-md: 0 2px 10px rgba(0, 0, 0, 0.1); - --shadow-lg: 0 4px 20px rgba(0, 0, 0, 0.15); - --shadow-hover: 0 5px 15px rgba(0, 0, 0, 0.2); - - /* Modal/Overlay */ - --overlay-bg: rgba(0, 0, 0, 0.5); - - /* Chart colors (vibrant for light backgrounds) */ - --chart-grid: rgba(0, 0, 0, 0.1); - --chart-text: #666666; - --chart-color-1: #667eea; - --chart-color-2: #4facfe; - --chart-color-3: #f093fb; - --chart-color-4: #56ab2f; - --chart-color-5: #f2994a; - --chart-color-6: #764ba2; - --chart-color-7: #00f2fe; - --chart-color-8: #f5576c; + /* ===== LIGHT MODE (Default) ===== */ + + /* Background colors */ + --bg-primary: #f0f0f0; + --bg-secondary: #ffffff; + --bg-tertiary: #f8f9fa; + --bg-card: #ffffff; + + /* Text colors */ + --text-primary: #333333; + --text-secondary: #666666; + --text-muted: #999999; + --text-inverse: #ffffff; + + /* Border colors */ + --border-color: #dddddd; + --border-light: #e0e0e0; + --border-lighter: #eeeeee; + + /* Gradients - Brand */ + --gradient-purple: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + --gradient-blue: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); + --gradient-pink: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); + --gradient-green: linear-gradient(135deg, #56ab2f 0%, #a8e063 100%); + --gradient-orange: linear-gradient(135deg, #f2994a 0%, #f2c94c 100%); + + /* Action colors */ + --color-success: #4caf50; + --color-error: #dc3545; + --color-warning: #ff9800; + --color-info: #2196f3; + --color-danger: #f44336; + + /* Action gradients */ + --gradient-success: linear-gradient(135deg, #56ab2f 0%, #a8e063 100%); + --gradient-error: linear-gradient(135deg, #dc3545 0%, #c62828 100%); + --gradient-danger: linear-gradient(135deg, #f44336 0%, #d32f2f 100%); + + /* Interactive elements */ + --input-bg: #ffffff; + --input-border: #dddddd; + --input-focus-border: #667eea; + --input-text: #333333; + --input-placeholder: #999999; + + /* Shadows */ + --shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.1); + --shadow-md: 0 2px 10px rgba(0, 0, 0, 0.1); + --shadow-lg: 0 4px 20px rgba(0, 0, 0, 0.15); + --shadow-hover: 0 5px 15px rgba(0, 0, 0, 0.2); + + /* Modal/Overlay */ + --overlay-bg: rgba(0, 0, 0, 0.5); + + /* Chart colors (vibrant for light backgrounds) */ + --chart-grid: rgba(0, 0, 0, 0.1); + --chart-text: #666666; + --chart-color-1: #667eea; + --chart-color-2: #4facfe; + --chart-color-3: #f093fb; + --chart-color-4: #56ab2f; + --chart-color-5: #f2994a; + --chart-color-6: #764ba2; + --chart-color-7: #00f2fe; + --chart-color-8: #f5576c; } /* ===== DARK MODE ===== */ /* Applied via @media query (auto-detect) OR .dark-mode class (manual override) */ @media (prefers-color-scheme: dark) { - :root:not(.light-mode) { - /* Background colors - Dark theme */ - --bg-primary: #1a1a1a; - --bg-secondary: #2d2d2d; - --bg-tertiary: #3a3a3a; - --bg-card: #2d2d2d; - - /* Text colors - Light on dark */ - --text-primary: #e0e0e0; - --text-secondary: #b0b0b0; - --text-muted: #808080; - --text-inverse: #1a1a1a; - - /* Border colors - Lighter for visibility */ - --border-color: #444444; - --border-light: #4a4a4a; - --border-lighter: #505050; - - /* Gradients - Dimmed (~50% brightness reduction) */ - --gradient-purple: linear-gradient(135deg, #3d4d8c 0%, #452d61 100%); - --gradient-blue: linear-gradient(135deg, #2f667f 0%, #00919a 100%); - --gradient-pink: linear-gradient(135deg, #905997 0%, #924344 100%); - --gradient-green: linear-gradient(135deg, #33661c 0%, #64873c 100%); - --gradient-orange: linear-gradient(135deg, #915b2d 0%, #917a2e 100%); - - /* Action colors - Slightly brighter for visibility */ - --color-success: #66bb6a; - --color-error: #ef5350; - --color-warning: #ffa726; - --color-info: #42a5f5; - --color-danger: #ff5252; - - /* Action gradients - Dimmed */ - --gradient-success: linear-gradient(135deg, #33661c 0%, #64873c 100%); - --gradient-error: linear-gradient(135deg, #a32a2a 0%, #8b2020 100%); - --gradient-danger: linear-gradient(135deg, #a32929 0%, #8b1e1e 100%); - - /* Interactive elements */ - --input-bg: #2d2d2d; - --input-border: #444444; - --input-focus-border: #667eea; - --input-text: #e0e0e0; - --input-placeholder: #808080; - - /* Shadows - Lighter/softer for dark mode */ - --shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.3); - --shadow-md: 0 2px 10px rgba(0, 0, 0, 0.4); - --shadow-lg: 0 4px 20px rgba(0, 0, 0, 0.5); - --shadow-hover: 0 5px 15px rgba(0, 0, 0, 0.6); - - /* Modal/Overlay - Same opacity */ - --overlay-bg: rgba(0, 0, 0, 0.5); - - /* Chart colors - Brighter and more saturated for dark backgrounds */ - --chart-grid: rgba(255, 255, 255, 0.1); - --chart-text: #b0b0b0; - --chart-color-1: #8b9eff; - --chart-color-2: #6ec5ff; - --chart-color-3: #ffb3ff; - --chart-color-4: #81c784; - --chart-color-5: #ffb74d; - --chart-color-6: #9c6bb8; - --chart-color-7: #4dffff; - --chart-color-8: #ff8a9b; - } -} - -/* Manual override with .dark-mode class */ -body.dark-mode { + :root:not(.light-mode) { /* Background colors - Dark theme */ --bg-primary: #1a1a1a; --bg-secondary: #2d2d2d; --bg-tertiary: #3a3a3a; --bg-card: #2d2d2d; - + /* Text colors - Light on dark */ --text-primary: #e0e0e0; --text-secondary: #b0b0b0; --text-muted: #808080; --text-inverse: #1a1a1a; - + /* Border colors - Lighter for visibility */ --border-color: #444444; --border-light: #4a4a4a; --border-lighter: #505050; - + /* Gradients - Dimmed (~50% brightness reduction) */ --gradient-purple: linear-gradient(135deg, #3d4d8c 0%, #452d61 100%); --gradient-blue: linear-gradient(135deg, #2f667f 0%, #00919a 100%); --gradient-pink: linear-gradient(135deg, #905997 0%, #924344 100%); --gradient-green: linear-gradient(135deg, #33661c 0%, #64873c 100%); --gradient-orange: linear-gradient(135deg, #915b2d 0%, #917a2e 100%); - + /* Action colors - Slightly brighter for visibility */ --color-success: #66bb6a; --color-error: #ef5350; --color-warning: #ffa726; --color-info: #42a5f5; --color-danger: #ff5252; - + /* Action gradients - Dimmed */ --gradient-success: linear-gradient(135deg, #33661c 0%, #64873c 100%); --gradient-error: linear-gradient(135deg, #a32a2a 0%, #8b2020 100%); --gradient-danger: linear-gradient(135deg, #a32929 0%, #8b1e1e 100%); - + /* Interactive elements */ --input-bg: #2d2d2d; --input-border: #444444; --input-focus-border: #667eea; --input-text: #e0e0e0; --input-placeholder: #808080; - + /* Shadows - Lighter/softer for dark mode */ --shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.3); --shadow-md: 0 2px 10px rgba(0, 0, 0, 0.4); --shadow-lg: 0 4px 20px rgba(0, 0, 0, 0.5); --shadow-hover: 0 5px 15px rgba(0, 0, 0, 0.6); - + /* Modal/Overlay - Same opacity */ --overlay-bg: rgba(0, 0, 0, 0.5); - + /* Chart colors - Brighter and more saturated for dark backgrounds */ --chart-grid: rgba(255, 255, 255, 0.1); --chart-text: #b0b0b0; @@ -206,53 +138,121 @@ body.dark-mode { --chart-color-6: #9c6bb8; --chart-color-7: #4dffff; --chart-color-8: #ff8a9b; + } +} + +/* Manual override with .dark-mode class */ +body.dark-mode { + /* Background colors - Dark theme */ + --bg-primary: #1a1a1a; + --bg-secondary: #2d2d2d; + --bg-tertiary: #3a3a3a; + --bg-card: #2d2d2d; + + /* Text colors - Light on dark */ + --text-primary: #e0e0e0; + --text-secondary: #b0b0b0; + --text-muted: #808080; + --text-inverse: #1a1a1a; + + /* Border colors - Lighter for visibility */ + --border-color: #444444; + --border-light: #4a4a4a; + --border-lighter: #505050; + + /* Gradients - Dimmed (~50% brightness reduction) */ + --gradient-purple: linear-gradient(135deg, #3d4d8c 0%, #452d61 100%); + --gradient-blue: linear-gradient(135deg, #2f667f 0%, #00919a 100%); + --gradient-pink: linear-gradient(135deg, #905997 0%, #924344 100%); + --gradient-green: linear-gradient(135deg, #33661c 0%, #64873c 100%); + --gradient-orange: linear-gradient(135deg, #915b2d 0%, #917a2e 100%); + + /* Action colors - Slightly brighter for visibility */ + --color-success: #66bb6a; + --color-error: #ef5350; + --color-warning: #ffa726; + --color-info: #42a5f5; + --color-danger: #ff5252; + + /* Action gradients - Dimmed */ + --gradient-success: linear-gradient(135deg, #33661c 0%, #64873c 100%); + --gradient-error: linear-gradient(135deg, #a32a2a 0%, #8b2020 100%); + --gradient-danger: linear-gradient(135deg, #a32929 0%, #8b1e1e 100%); + + /* Interactive elements */ + --input-bg: #2d2d2d; + --input-border: #444444; + --input-focus-border: #667eea; + --input-text: #e0e0e0; + --input-placeholder: #808080; + + /* Shadows - Lighter/softer for dark mode */ + --shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.3); + --shadow-md: 0 2px 10px rgba(0, 0, 0, 0.4); + --shadow-lg: 0 4px 20px rgba(0, 0, 0, 0.5); + --shadow-hover: 0 5px 15px rgba(0, 0, 0, 0.6); + + /* Modal/Overlay - Same opacity */ + --overlay-bg: rgba(0, 0, 0, 0.5); + + /* Chart colors - Brighter and more saturated for dark backgrounds */ + --chart-grid: rgba(255, 255, 255, 0.1); + --chart-text: #b0b0b0; + --chart-color-1: #8b9eff; + --chart-color-2: #6ec5ff; + --chart-color-3: #ffb3ff; + --chart-color-4: #81c784; + --chart-color-5: #ffb74d; + --chart-color-6: #9c6bb8; + --chart-color-7: #4dffff; + --chart-color-8: #ff8a9b; } /* Manual override with .light-mode class (forces light even when browser prefers dark) */ body.light-mode { - /* Force light mode values - same as root defaults */ - --bg-primary: #f0f0f0; - --bg-secondary: #ffffff; - --bg-tertiary: #f8f9fa; - --bg-card: #ffffff; - --text-primary: #333333; - --text-secondary: #666666; - --text-muted: #999999; - --text-inverse: #ffffff; - --border-color: #dddddd; - --border-light: #e0e0e0; - --border-lighter: #eeeeee; - --gradient-purple: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - --gradient-blue: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); - --gradient-pink: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); - --gradient-green: linear-gradient(135deg, #56ab2f 0%, #a8e063 100%); - --gradient-orange: linear-gradient(135deg, #f2994a 0%, #f2c94c 100%); - --color-success: #4caf50; - --color-error: #dc3545; - --color-warning: #ff9800; - --color-info: #2196f3; - --color-danger: #f44336; - --gradient-success: linear-gradient(135deg, #56ab2f 0%, #a8e063 100%); - --gradient-error: linear-gradient(135deg, #dc3545 0%, #c62828 100%); - --gradient-danger: linear-gradient(135deg, #f44336 0%, #d32f2f 100%); - --input-bg: #ffffff; - --input-border: #dddddd; - --input-focus-border: #667eea; - --input-text: #333333; - --input-placeholder: #999999; - --shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.1); - --shadow-md: 0 2px 10px rgba(0, 0, 0, 0.1); - --shadow-lg: 0 4px 20px rgba(0, 0, 0, 0.15); - --shadow-hover: 0 5px 15px rgba(0, 0, 0, 0.2); - --overlay-bg: rgba(0, 0, 0, 0.5); - --chart-grid: rgba(0, 0, 0, 0.1); - --chart-text: #666666; - --chart-color-1: #667eea; - --chart-color-2: #4facfe; - --chart-color-3: #f093fb; - --chart-color-4: #56ab2f; - --chart-color-5: #f2994a; - --chart-color-6: #764ba2; - --chart-color-7: #00f2fe; - --chart-color-8: #f5576c; + /* Force light mode values - same as root defaults */ + --bg-primary: #f0f0f0; + --bg-secondary: #ffffff; + --bg-tertiary: #f8f9fa; + --bg-card: #ffffff; + --text-primary: #333333; + --text-secondary: #666666; + --text-muted: #999999; + --text-inverse: #ffffff; + --border-color: #dddddd; + --border-light: #e0e0e0; + --border-lighter: #eeeeee; + --gradient-purple: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + --gradient-blue: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); + --gradient-pink: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); + --gradient-green: linear-gradient(135deg, #56ab2f 0%, #a8e063 100%); + --gradient-orange: linear-gradient(135deg, #f2994a 0%, #f2c94c 100%); + --color-success: #4caf50; + --color-error: #dc3545; + --color-warning: #ff9800; + --color-info: #2196f3; + --color-danger: #f44336; + --gradient-success: linear-gradient(135deg, #56ab2f 0%, #a8e063 100%); + --gradient-error: linear-gradient(135deg, #dc3545 0%, #c62828 100%); + --gradient-danger: linear-gradient(135deg, #f44336 0%, #d32f2f 100%); + --input-bg: #ffffff; + --input-border: #dddddd; + --input-focus-border: #667eea; + --input-text: #333333; + --input-placeholder: #999999; + --shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.1); + --shadow-md: 0 2px 10px rgba(0, 0, 0, 0.1); + --shadow-lg: 0 4px 20px rgba(0, 0, 0, 0.15); + --shadow-hover: 0 5px 15px rgba(0, 0, 0, 0.2); + --overlay-bg: rgba(0, 0, 0, 0.5); + --chart-grid: rgba(0, 0, 0, 0.1); + --chart-text: #666666; + --chart-color-1: #667eea; + --chart-color-2: #4facfe; + --chart-color-3: #f093fb; + --chart-color-4: #56ab2f; + --chart-color-5: #f2994a; + --chart-color-6: #764ba2; + --chart-color-7: #00f2fe; + --chart-color-8: #f5576c; } diff --git a/src/baby_monitor/static/diapers.html b/src/baby_monitor/static/diapers.html index 2bb8d00..8beb7d5 100644 --- a/src/baby_monitor/static/diapers.html +++ b/src/baby_monitor/static/diapers.html @@ -857,7 +857,7 @@ }, }, scales: { - y: getChartScaleOptions('y', { + y: getChartScaleOptions("y", { beginAtZero: true, title: { display: true, @@ -1098,8 +1098,12 @@ loadDiaperChanges(); // Listen for theme changes and update charts - window.addEventListener('themechange', () => { - const charts = [barChartInstance, poopPieChartInstance, peePieChartInstance].filter(c => c); + window.addEventListener("themechange", () => { + const charts = [ + barChartInstance, + poopPieChartInstance, + peePieChartInstance, + ].filter((c) => c); updateChartsForTheme(charts); }); diff --git a/src/baby_monitor/static/feedings.html b/src/baby_monitor/static/feedings.html index c29b2f9..5a7bf8a 100644 --- a/src/baby_monitor/static/feedings.html +++ b/src/baby_monitor/static/feedings.html @@ -424,13 +424,13 @@ // Build child dropdown options const childOptions = allChildren - .map( - (child) => - ``, - ) - .join(""); + .map( + (child) => + ``, + ) + .join(""); - content.innerHTML = ` + content.innerHTML = `
${ allChildren.length === 1 @@ -486,13 +486,13 @@
`; - // Render charts - renderBarChart(barChartData); - renderPieChart(pieChartData); - renderDurationChart(durationChartData); + // Render charts + renderBarChart(barChartData); + renderPieChart(pieChartData); + renderDurationChart(durationChartData); - // Render feeding list - renderFeedingList(feedings); + // Render feeding list + renderFeedingList(feedings); } function renderFeedingList(feedings) { @@ -940,7 +940,7 @@ display: false, }, }, - y: getChartScaleOptions('y', { + y: getChartScaleOptions("y", { stacked: true, beginAtZero: true, title: { @@ -1127,7 +1127,7 @@ }, }, scales: { - y: getChartScaleOptions('y', { + y: getChartScaleOptions("y", { beginAtZero: true, title: { display: true, @@ -1144,7 +1144,7 @@ }, }, }), - x: getChartScaleOptions('x', { + x: getChartScaleOptions("x", { title: { display: true, text: "Duration (minutes)", @@ -1202,8 +1202,12 @@ loadFeedings(); // Listen for theme changes and update charts - window.addEventListener('themechange', () => { - const charts = [barChartInstance, pieChartInstance, durationChartInstance].filter(c => c); + window.addEventListener("themechange", () => { + const charts = [ + barChartInstance, + pieChartInstance, + durationChartInstance, + ].filter((c) => c); updateChartsForTheme(charts); }); diff --git a/src/baby_monitor/static/index.html b/src/baby_monitor/static/index.html index 1feeb5c..7328ae1 100644 --- a/src/baby_monitor/static/index.html +++ b/src/baby_monitor/static/index.html @@ -3,9 +3,11 @@ - Baby Monitor + Baby Monitor + - + +