Files
HeatGuard/web/templates/dashboard.html
vikingowl 5e6696aa42 feat: add AI-powered actions endpoint and timeline annotations
Add LLM actions endpoint that generates hour-specific heat
management recommendations. Replace static action engine with
AI-driven approach. Add cool mode logic (ventilate/ac/overloaded),
indoor temperature tracking, and timeline legend with annotations.
2026-02-10 03:54:09 +01:00

175 lines
8.9 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-5">
<!-- Header -->
<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>
<!-- Two-column: Main content + Sidebar -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-5">
<!-- Left column: cards, risk windows, timeline, actions -->
<div class="lg:col-span-2 space-y-5">
<!-- Summary cards -->
<div class="grid grid-cols-1 sm:grid-cols-3 gap-3">
<div id="risk-card" class="rounded-xl p-4 text-center transition-all duration-200 hover:shadow-md">
<div id="risk-icon" class="mb-1"></div>
<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 border-l-4 border-orange-400 transition-all duration-200 hover:shadow-md">
<div class="mb-1"><svg class="w-5 h-5 mx-auto text-orange-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2v6m0 0a6 6 0 106 6V8a6 6 0 10-6 0z"/></svg></div>
<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 border-l-4 border-blue-400 transition-all duration-200 hover:shadow-md">
<div class="mb-1"><svg class="w-5 h-5 mx-auto text-blue-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg></div>
<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-2">{{t "dashboard.riskWindows"}}</h2>
<div id="risk-windows" class="space-y-2"></div>
</div>
<!-- Timeline heatmap -->
<div class="relative">
<h2 class="text-lg font-semibold mb-2">{{t "dashboard.timeline"}}</h2>
<div id="timeline-chart" class="bg-white dark:bg-gray-800 rounded-xl p-4 shadow-sm"></div>
<div id="cooling-legend" class="flex flex-col gap-1 text-xs text-gray-400 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>
<!-- AI Actions -->
<div id="actions-section" class="hidden">
<div class="flex items-center gap-2 mb-3">
<h2 class="text-lg font-semibold">{{t "dashboard.actions"}}</h2>
<span id="actions-badge" class="hidden text-xs px-2 py-0.5 rounded-full bg-purple-100 text-purple-700 dark:bg-purple-900 dark:text-purple-300">AI</span>
</div>
<div id="actions-list" class="space-y-4"></div>
</div>
<!-- Actions loading skeleton (shown while AI is loading) -->
<div id="actions-loading" class="hidden">
<div class="flex items-center gap-2 mb-3">
<h2 class="text-lg font-semibold">{{t "dashboard.actions"}}</h2>
<div class="inline-block animate-spin rounded-full h-4 w-4 border-2 border-purple-500 border-t-transparent"></div>
</div>
<div class="space-y-3">
<div class="bg-white dark:bg-gray-800 rounded-lg p-3 shadow-sm animate-pulse"><div class="h-4 bg-gray-200 dark:bg-gray-700 rounded w-2/3 mb-2"></div><div class="h-3 bg-gray-200 dark:bg-gray-700 rounded w-1/2"></div></div>
<div class="bg-white dark:bg-gray-800 rounded-lg p-3 shadow-sm animate-pulse"><div class="h-4 bg-gray-200 dark:bg-gray-700 rounded w-3/4 mb-2"></div><div class="h-3 bg-gray-200 dark:bg-gray-700 rounded w-1/3"></div></div>
<div class="bg-white dark:bg-gray-800 rounded-lg p-3 shadow-sm animate-pulse"><div class="h-4 bg-gray-200 dark:bg-gray-700 rounded w-1/2 mb-2"></div><div class="h-3 bg-gray-200 dark:bg-gray-700 rounded w-2/5"></div></div>
</div>
</div>
</div>
<!-- Right column: Budgets + Summary + Care -->
<div class="space-y-5">
<!-- Room budgets -->
<div id="budgets-section" class="hidden">
<h2 class="text-lg font-semibold mb-2">{{t "dashboard.roomBudgets"}}</h2>
<div id="room-budgets" class="space-y-3"></div>
</div>
<!-- LLM Summary -->
<div id="llm-section">
<h2 class="text-lg font-semibold mb-2">{{t "dashboard.llmSummary"}}</h2>
<div id="llm-summary" class="bg-white dark:bg-gray-800 rounded-xl p-4 shadow-sm border-l-4 border-purple-400 opacity-0 transition-opacity duration-500">
<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>
<!-- Care checklist -->
<div id="care-section" class="hidden">
<h2 class="text-lg font-semibold mb-2">{{t "dashboard.careChecklist"}}</h2>
<ul id="care-checklist" class="space-y-1"></ul>
</div>
</div>
</div>
</div>
</div>
{{end}}
{{define "scripts"}}
<script>
window.HG.t = {
warnings: "{{t "dashboard.warnings"}}",
noActions: "{{t "dashboard.noActions"}}",
effort: "{{t "dashboard.effort"}}",
impact: "{{t "dashboard.impact"}}",
aiDisclaimer: "{{t "dashboard.aiDisclaimer"}}",
actions: "{{t "dashboard.actions"}}",
internalGains: "{{t "dashboard.internalGains"}}",
solarGain: "{{t "dashboard.solarGain"}}",
ventGain: "{{t "dashboard.ventGain"}}",
totalGain: "{{t "dashboard.totalGain"}}",
acCapacity: "{{t "dashboard.acCapacity"}}",
headroom: "{{t "dashboard.headroom"}}",
coolVentilate: "{{t "dashboard.coolVentilate"}}",
coolAC: "{{t "dashboard.coolAC"}}",
coolOverloaded: "{{t "dashboard.coolOverloaded"}}",
aiActions: "{{t "dashboard.aiActions"}}",
legendTemp: "{{t "dashboard.legendTemp"}}",
legendCooling: "{{t "dashboard.legendCooling"}}",
legendAI: "{{t "dashboard.legendAI"}}",
category: {
shading: "{{t "dashboard.category.shading"}}",
ventilation: "{{t "dashboard.category.ventilation"}}",
internal_gains: "{{t "dashboard.category.internal_gains"}}",
ac_strategy: "{{t "dashboard.category.ac_strategy"}}",
hydration: "{{t "dashboard.category.hydration"}}",
care: "{{t "dashboard.category.care"}}",
},
};
</script>
<script src="/assets/js/db.js"></script>
<script src="/assets/js/dashboard.js"></script>
{{end}}