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.
175 lines
8.9 KiB
HTML
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}}
|