spaxel/dashboard/css/sleep.css
jedarden 636f3efba2 feat: complete anomaly detection & security mode dashboard UI
- Add anomaly.css and sleep.css to dashboard includes
- Add sleep.js for sleep quality monitoring
- Implement analytics API handler (flow, dwell, corridors)
- Add tracks API and tests for time-based data queries
- Add sleep monitor tests
- AnomalyDetector initialized and running in main()
- Anomaly events broadcast via WebSocket to dashboard
- Security mode arm/disarm persists across restarts (learning_state table)
- Learning progress tracking and display
- Alert banner with acknowledge functionality
- All API endpoints wired: /api/anomalies, /api/security/*, /api/analytics/*

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 05:59:54 -04:00

313 lines
6.4 KiB
CSS

/* Sleep Quality Monitoring UI */
/* ── Morning Summary Card ────────────────────────────────────────────────── */
.sleep-summary-card {
position: fixed;
top: 16px;
right: 16px;
z-index: 1000;
background: var(--bg-card, #1e1e2e);
border: 1px solid var(--border-color, #333);
border-radius: 12px;
padding: 16px;
max-width: 380px;
width: 100%;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
animation: sleep-slide-in 0.3s ease-out;
color: var(--text-color, #e0e0e0);
}
@keyframes sleep-slide-in {
from { transform: translateY(-20px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
.sleep-summary-card.hidden {
display: none;
}
.sleep-summary-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid var(--border-color, #333);
}
.sleep-summary-icon {
color: #a78bfa;
display: flex;
align-items: center;
}
.sleep-summary-title {
font-weight: 600;
font-size: 14px;
flex: 1;
}
.sleep-summary-dismiss {
background: none;
border: none;
color: var(--text-muted, #888);
font-size: 18px;
cursor: pointer;
padding: 2px 6px;
line-height: 1;
border-radius: 4px;
}
.sleep-summary-dismiss:hover {
background: var(--bg-hover, #333);
color: var(--text-color, #e0e0e0);
}
.sleep-summary-body {
display: flex;
flex-direction: column;
gap: 8px;
}
.sleep-summary-body > div {
font-size: 13px;
line-height: 1.5;
}
.sleep-summary-duration {
font-weight: 600;
font-size: 18px;
margin-bottom: 4px;
}
.sleep-efficiency-dot {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 4px;
vertical-align: middle;
}
.sleep-efficiency-dot.green { background: #4ade80; }
.sleep-efficiency-dot.amber { background: #fbbf24; }
.sleep-efficiency-dot.red { background: #f87171; }
.sleep-anomaly-warning {
color: #fbbf24;
font-weight: 500;
}
.sleep-summary-details-btn {
margin-top: 8px;
padding: 6px 12px;
background: var(--bg-hover, #2a2a3e);
border: 1px solid var(--border-color, #444);
border-radius: 6px;
color: var(--text-color, #e0e0e0);
font-size: 12px;
cursor: pointer;
text-align: center;
}
.sleep-summary-details-btn:hover {
background: var(--bg-active, #3a3a4e);
}
.sleep-summary-details-btn.hidden {
display: none;
}
.sleep-summary-anomaly.hidden {
display: none;
}
/* ── Sleep Panel ──────────────────────────────────────────────────────────── */
.sleep-panel {
position: fixed;
top: 0;
right: 0;
width: 360px;
height: 100%;
background: var(--bg-panel, #1a1a2e);
border-left: 1px solid var(--border-color, #333);
z-index: 900;
display: flex;
flex-direction: column;
box-shadow: -4px 0 24px rgba(0, 0, 0, 0.3);
overflow: hidden;
color: var(--text-color, #e0e0e0);
}
.sleep-panel.hidden {
display: none;
}
.sleep-panel-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px;
border-bottom: 1px solid var(--border-color, #333);
flex-shrink: 0;
}
.sleep-panel-header h3 {
margin: 0;
font-size: 16px;
font-weight: 600;
}
.sleep-panel-close {
background: none;
border: none;
color: var(--text-muted, #888);
font-size: 20px;
cursor: pointer;
padding: 2px 6px;
border-radius: 4px;
}
.sleep-panel-close:hover {
background: var(--bg-hover, #333);
color: var(--text-color, #e0e0e0);
}
.sleep-panel-content {
flex: 1;
overflow-y: auto;
padding: 16px;
}
.sleep-panel-section {
margin-bottom: 24px;
}
.sleep-panel-section h4 {
font-size: 13px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--text-muted, #888);
margin: 0 0 12px 0;
}
/* ── Trends ───────────────────────────────────────────────────────────────── */
.sleep-trends-container {
display: flex;
flex-direction: column;
gap: 12px;
}
.sleep-trend-row {
display: flex;
align-items: center;
gap: 8px;
}
.sleep-trend-label {
font-size: 12px;
color: var(--text-muted, #888);
min-width: 100px;
flex-shrink: 0;
}
.sleep-sparkline {
flex: 1;
height: 30px;
}
.sleep-sparkline-svg {
width: 100%;
height: 100%;
}
.sleep-trend-value {
font-size: 12px;
font-weight: 600;
min-width: 60px;
text-align: right;
flex-shrink: 0;
}
.sleep-week-comparison {
font-size: 12px;
color: var(--text-muted, #888);
margin-top: 8px;
padding: 8px;
background: var(--bg-card, #1e1e2e);
border-radius: 6px;
line-height: 1.5;
}
/* ── Breathing Stats ──────────────────────────────────────────────────────── */
.sleep-breathing-stats {
display: flex;
gap: 16px;
}
.sleep-stat {
flex: 1;
background: var(--bg-card, #1e1e2e);
border-radius: 8px;
padding: 12px;
text-align: center;
}
.sleep-stat-label {
display: block;
font-size: 11px;
color: var(--text-muted, #888);
margin-bottom: 4px;
}
.sleep-stat-value {
display: block;
font-size: 20px;
font-weight: 700;
color: #4a9eff;
}
/* ── History ──────────────────────────────────────────────────────────────── */
.sleep-history {
display: flex;
flex-direction: column;
gap: 4px;
}
.sleep-history-row {
display: flex;
align-items: center;
gap: 12px;
padding: 8px 10px;
background: var(--bg-card, #1e1e2e);
border-radius: 6px;
font-size: 13px;
}
.sleep-history-date {
min-width: 90px;
color: var(--text-muted, #888);
}
.sleep-history-duration {
flex: 1;
font-weight: 500;
}
.sleep-history-breathing {
color: #4a9eff;
font-size: 12px;
}
.sleep-history-empty {
text-align: center;
color: var(--text-muted, #888);
font-size: 13px;
padding: 24px 0;
}