Replace CLI + SQLite architecture with a Go web server + vanilla JS frontend using IndexedDB for all client-side data storage. - Remove: cli, store, report, static packages - Add: compute engine (BuildDashboard), server package, web UI - Add: setup page with CRUD for profiles, rooms, devices, occupants, AC - Add: dashboard with SVG temperature timeline, risk analysis, care checklist - Add: i18n support (English/German) with server-side Go templates - Add: LLM provider selection UI with client-side API key storage - Add: per-room indoor temperature, edit buttons, language-aware AI summary
110 lines
4.8 KiB
HTML
110 lines
4.8 KiB
HTML
{{define "content"}}
|
|
<div id="dashboard">
|
|
<!-- No data state -->
|
|
<div id="no-data" class="hidden">
|
|
<div class="text-center py-16">
|
|
<div class="text-6xl mb-4">🌡️</div>
|
|
<h1 class="text-2xl font-bold mb-2">{{t "dashboard.title"}}</h1>
|
|
<p class="text-gray-500 dark:text-gray-400 mb-6">{{t "dashboard.noData"}}</p>
|
|
<div class="flex gap-4 justify-center">
|
|
<a href="/guide" class="px-4 py-2 bg-orange-600 text-white rounded-lg hover:bg-orange-700 transition">{{t "dashboard.goToGuide"}}</a>
|
|
<a href="/setup" class="px-4 py-2 bg-gray-200 dark:bg-gray-700 rounded-lg hover:bg-gray-300 dark:hover:bg-gray-600 transition">{{t "dashboard.goToSetup"}}</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- No forecast state -->
|
|
<div id="no-forecast" class="hidden">
|
|
<div class="text-center py-16">
|
|
<h1 class="text-2xl font-bold mb-2">{{t "dashboard.title"}}</h1>
|
|
<p class="text-gray-500 dark:text-gray-400 mb-6">{{t "dashboard.fetchForecastFirst"}}</p>
|
|
<a href="/setup#forecast" class="px-4 py-2 bg-orange-600 text-white rounded-lg hover:bg-orange-700 transition">{{t "dashboard.goToSetup"}}</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Loading state -->
|
|
<div id="loading" class="hidden">
|
|
<div class="text-center py-16">
|
|
<div class="inline-block animate-spin rounded-full h-8 w-8 border-2 border-orange-600 border-t-transparent mb-4"></div>
|
|
<p class="text-gray-500 dark:text-gray-400">{{t "dashboard.computing"}}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Error state -->
|
|
<div id="error-state" class="hidden">
|
|
<div class="text-center py-16">
|
|
<p class="text-red-600 dark:text-red-400 mb-4">{{t "dashboard.error"}}</p>
|
|
<button onclick="loadDashboard()" class="px-4 py-2 bg-orange-600 text-white rounded-lg hover:bg-orange-700 transition">↻</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Data display -->
|
|
<div id="data-display" class="hidden space-y-6">
|
|
<div class="flex items-center justify-between">
|
|
<h1 class="text-2xl font-bold">{{t "dashboard.title"}}</h1>
|
|
<span id="profile-name" class="text-sm text-gray-500 dark:text-gray-400"></span>
|
|
</div>
|
|
|
|
<!-- Warnings -->
|
|
<div id="warnings-section" class="hidden space-y-2"></div>
|
|
|
|
<!-- Summary cards -->
|
|
<div class="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
|
<div id="risk-card" class="rounded-xl p-4 text-center">
|
|
<div class="text-sm text-gray-500 dark:text-gray-400 mb-1">{{t "dashboard.riskLevel"}}</div>
|
|
<div id="risk-level" class="text-2xl font-bold"></div>
|
|
</div>
|
|
<div class="bg-white dark:bg-gray-800 rounded-xl p-4 text-center shadow-sm">
|
|
<div class="text-sm text-gray-500 dark:text-gray-400 mb-1">{{t "dashboard.peakTemp"}}</div>
|
|
<div id="peak-temp" class="text-2xl font-bold"></div>
|
|
</div>
|
|
<div class="bg-white dark:bg-gray-800 rounded-xl p-4 text-center shadow-sm">
|
|
<div class="text-sm text-gray-500 dark:text-gray-400 mb-1">{{t "dashboard.minNightTemp"}}</div>
|
|
<div id="min-night-temp" class="text-2xl font-bold"></div>
|
|
<div id="poor-night-cool" class="hidden text-xs text-orange-600 dark:text-orange-400 mt-1">{{t "dashboard.poorNightCool"}}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Risk windows -->
|
|
<div id="risk-windows-section" class="hidden">
|
|
<h2 class="text-lg font-semibold mb-3">{{t "dashboard.riskWindows"}}</h2>
|
|
<div id="risk-windows" class="space-y-2"></div>
|
|
</div>
|
|
|
|
<!-- Timeline -->
|
|
<div>
|
|
<h2 class="text-lg font-semibold mb-3">{{t "dashboard.timeline"}}</h2>
|
|
<div id="timeline-chart" class="bg-white dark:bg-gray-800 rounded-xl p-4 shadow-sm"></div>
|
|
<div id="timeline-strip" class="flex gap-0.5 mt-2"></div>
|
|
<div id="timeline-tooltip" class="hidden absolute z-50 bg-gray-800 text-white text-xs rounded-lg p-3 shadow-lg max-w-xs pointer-events-none"></div>
|
|
</div>
|
|
|
|
<!-- Room budgets -->
|
|
<div id="budgets-section" class="hidden">
|
|
<h2 class="text-lg font-semibold mb-3">{{t "dashboard.roomBudgets"}}</h2>
|
|
<div id="room-budgets" class="grid grid-cols-1 md:grid-cols-2 gap-4"></div>
|
|
</div>
|
|
|
|
<!-- Care checklist -->
|
|
<div id="care-section" class="hidden">
|
|
<h2 class="text-lg font-semibold mb-3">{{t "dashboard.careChecklist"}}</h2>
|
|
<ul id="care-checklist" class="space-y-1"></ul>
|
|
</div>
|
|
|
|
<!-- LLM Summary -->
|
|
<div id="llm-section">
|
|
<h2 class="text-lg font-semibold mb-3">{{t "dashboard.llmSummary"}}</h2>
|
|
<div id="llm-summary" class="bg-white dark:bg-gray-800 rounded-xl p-4 shadow-sm">
|
|
<div class="animate-pulse h-4 bg-gray-200 dark:bg-gray-700 rounded w-3/4 mb-2"></div>
|
|
<div class="animate-pulse h-4 bg-gray-200 dark:bg-gray-700 rounded w-1/2"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{define "scripts"}}
|
|
<script src="/assets/js/db.js"></script>
|
|
<script src="/assets/js/dashboard.js"></script>
|
|
{{end}}
|