feat: rewrite to stateless web app with IndexedDB frontend
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
This commit is contained in:
326
web/templates/setup.html
Normal file
326
web/templates/setup.html
Normal file
@@ -0,0 +1,326 @@
|
||||
{{define "content"}}
|
||||
<div id="setup">
|
||||
<h1 class="text-2xl font-bold mb-6">{{t "setup.title"}}</h1>
|
||||
|
||||
<!-- Tabs -->
|
||||
<div class="flex gap-1 mb-6 overflow-x-auto border-b border-gray-200 dark:border-gray-700">
|
||||
<button class="tab-btn px-3 py-2 text-sm font-medium whitespace-nowrap border-b-2 border-orange-600 text-orange-600 dark:text-orange-400 dark:border-orange-400" data-tab="profiles">{{t "setup.profiles.title"}}</button>
|
||||
<button class="tab-btn px-3 py-2 text-sm font-medium whitespace-nowrap border-b-2 border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400" data-tab="rooms">{{t "setup.rooms.title"}}</button>
|
||||
<button class="tab-btn px-3 py-2 text-sm font-medium whitespace-nowrap border-b-2 border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400" data-tab="devices">{{t "setup.devices.title"}}</button>
|
||||
<button class="tab-btn px-3 py-2 text-sm font-medium whitespace-nowrap border-b-2 border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400" data-tab="occupants">{{t "setup.occupants.title"}}</button>
|
||||
<button class="tab-btn px-3 py-2 text-sm font-medium whitespace-nowrap border-b-2 border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400" data-tab="ac">{{t "setup.ac.title"}}</button>
|
||||
<button class="tab-btn px-3 py-2 text-sm font-medium whitespace-nowrap border-b-2 border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400" data-tab="toggles">{{t "setup.toggles.title"}}</button>
|
||||
<button class="tab-btn px-3 py-2 text-sm font-medium whitespace-nowrap border-b-2 border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400" data-tab="forecast">{{t "setup.forecast.title"}}</button>
|
||||
<button class="tab-btn px-3 py-2 text-sm font-medium whitespace-nowrap border-b-2 border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400" data-tab="llm">{{t "setup.llm.title"}}</button>
|
||||
</div>
|
||||
|
||||
<!-- Toast -->
|
||||
<div id="toast" class="hidden fixed bottom-4 right-4 px-4 py-2 rounded-lg shadow-lg text-sm z-50 transition-opacity"></div>
|
||||
|
||||
<!-- Profiles tab -->
|
||||
<section id="tab-profiles" class="tab-panel">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-4">{{t "setup.profiles.help"}}</p>
|
||||
<form id="profile-form" class="bg-white dark:bg-gray-800 rounded-xl p-4 shadow-sm space-y-3 mb-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.profiles.name.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.profiles.name.tooltip"}}">?</span></label>
|
||||
<input type="text" name="name" required class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.profiles.timezone.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.profiles.timezone.tooltip"}}">?</span></label>
|
||||
<input type="text" name="timezone" value="Europe/Berlin" class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.profiles.latitude.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.profiles.latitude.tooltip"}}">?</span></label>
|
||||
<input type="number" name="latitude" step="0.0001" required class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.profiles.longitude.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.profiles.longitude.tooltip"}}">?</span></label>
|
||||
<input type="number" name="longitude" step="0.0001" required class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button type="button" id="geolocate-btn" class="px-3 py-1.5 text-sm bg-gray-100 dark:bg-gray-700 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition">
|
||||
📍 {{t "setup.profiles.geolocate.button"}}
|
||||
</button>
|
||||
<button type="submit" class="px-4 py-1.5 text-sm bg-orange-600 text-white rounded-lg hover:bg-orange-700 transition">{{t "setup.profiles.add"}}</button>
|
||||
</div>
|
||||
<input type="hidden" name="id" value="">
|
||||
</form>
|
||||
<div id="profiles-list" class="space-y-2">
|
||||
<p class="text-sm text-gray-400 dark:text-gray-500">{{t "setup.profiles.noItems"}}</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Rooms tab -->
|
||||
<section id="tab-rooms" class="tab-panel hidden">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-4">{{t "setup.rooms.help"}}</p>
|
||||
<form id="room-form" class="bg-white dark:bg-gray-800 rounded-xl p-4 shadow-sm space-y-3 mb-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.rooms.name.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.rooms.name.tooltip"}}">?</span></label>
|
||||
<input type="text" name="name" required class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.rooms.area.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.rooms.area.tooltip"}}">?</span></label>
|
||||
<input type="number" name="areaSqm" step="0.1" required class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.rooms.ceilingHeight.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.rooms.ceilingHeight.tooltip"}}">?</span></label>
|
||||
<input type="number" name="ceilingHeightM" step="0.1" value="2.5" class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.rooms.floor.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.rooms.floor.tooltip"}}">?</span></label>
|
||||
<input type="number" name="floor" value="0" class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.rooms.orientation.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.rooms.orientation.tooltip"}}">?</span></label>
|
||||
<select name="orientation" class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
<option value="N">{{t "setup.rooms.orientation.options.N"}}</option><option value="NE">{{t "setup.rooms.orientation.options.NE"}}</option><option value="E">{{t "setup.rooms.orientation.options.E"}}</option>
|
||||
<option value="SE">{{t "setup.rooms.orientation.options.SE"}}</option><option value="S" selected>{{t "setup.rooms.orientation.options.S"}}</option><option value="SW">{{t "setup.rooms.orientation.options.SW"}}</option>
|
||||
<option value="W">{{t "setup.rooms.orientation.options.W"}}</option><option value="NW">{{t "setup.rooms.orientation.options.NW"}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.rooms.shadingType.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.rooms.shadingType.tooltip"}}">?</span></label>
|
||||
<select name="shadingType" class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
<option value="none">{{t "setup.rooms.shadingType.options.none"}}</option><option value="blinds">{{t "setup.rooms.shadingType.options.blinds"}}</option>
|
||||
<option value="shutters">{{t "setup.rooms.shadingType.options.shutters"}}</option><option value="awning">{{t "setup.rooms.shadingType.options.awning"}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.rooms.shadingFactor.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.rooms.shadingFactor.tooltip"}}">?</span></label>
|
||||
<input type="number" name="shadingFactor" step="0.1" min="0" max="1" value="1.0" class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.rooms.ventilationAch.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.rooms.ventilationAch.tooltip"}}">?</span></label>
|
||||
<input type="number" name="ventilationAch" step="0.1" value="0.5" class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.rooms.windowFraction.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.rooms.windowFraction.tooltip"}}">?</span></label>
|
||||
<input type="number" name="windowFraction" step="0.01" min="0" max="1" value="0.15" class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.rooms.shgc.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.rooms.shgc.tooltip"}}">?</span></label>
|
||||
<input type="number" name="shgc" step="0.1" min="0" max="1" value="0.6" class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.rooms.insulation.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.rooms.insulation.tooltip"}}">?</span></label>
|
||||
<select name="insulation" class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
<option value="poor">{{t "setup.rooms.insulation.options.poor"}}</option><option value="average" selected>{{t "setup.rooms.insulation.options.average"}}</option>
|
||||
<option value="good">{{t "setup.rooms.insulation.options.good"}}</option><option value="excellent">{{t "setup.rooms.insulation.options.excellent"}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.rooms.indoorTemp.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.rooms.indoorTemp.tooltip"}}">?</span></label>
|
||||
<input type="number" name="indoorTempC" step="0.5" min="15" max="35" value="25" class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button type="submit" class="px-4 py-1.5 text-sm bg-orange-600 text-white rounded-lg hover:bg-orange-700 transition">{{t "setup.rooms.add"}}</button>
|
||||
</div>
|
||||
<input type="hidden" name="id" value="">
|
||||
<input type="hidden" name="profileId" value="">
|
||||
</form>
|
||||
<div id="rooms-list" class="space-y-2">
|
||||
<p class="text-sm text-gray-400 dark:text-gray-500">{{t "setup.rooms.noItems"}}</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Devices tab -->
|
||||
<section id="tab-devices" class="tab-panel hidden">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-4">{{t "setup.devices.help"}}</p>
|
||||
<form id="device-form" class="bg-white dark:bg-gray-800 rounded-xl p-4 shadow-sm space-y-3 mb-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.devices.room.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.devices.room.tooltip"}}">?</span></label>
|
||||
<select name="roomId" required class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm" id="device-room-select"></select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.devices.name.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.devices.name.tooltip"}}">?</span></label>
|
||||
<input type="text" name="name" required class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.devices.type.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.devices.type.tooltip"}}">?</span></label>
|
||||
<input type="text" name="deviceType" value="electronics" class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.devices.wattsIdle.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.devices.wattsIdle.tooltip"}}">?</span></label>
|
||||
<input type="number" name="wattsIdle" step="1" value="0" class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.devices.wattsTypical.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.devices.wattsTypical.tooltip"}}">?</span></label>
|
||||
<input type="number" name="wattsTypical" step="1" required class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.devices.wattsPeak.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.devices.wattsPeak.tooltip"}}">?</span></label>
|
||||
<input type="number" name="wattsPeak" step="1" value="0" class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.devices.dutyCycle.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.devices.dutyCycle.tooltip"}}">?</span></label>
|
||||
<input type="number" name="dutyCycle" step="0.1" min="0" max="1" value="1.0" class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="px-4 py-1.5 text-sm bg-orange-600 text-white rounded-lg hover:bg-orange-700 transition">{{t "setup.devices.add"}}</button>
|
||||
<input type="hidden" name="id" value="">
|
||||
</form>
|
||||
<div id="devices-list" class="space-y-2">
|
||||
<p class="text-sm text-gray-400 dark:text-gray-500">{{t "setup.devices.noItems"}}</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Occupants tab -->
|
||||
<section id="tab-occupants" class="tab-panel hidden">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-4">{{t "setup.occupants.help"}}</p>
|
||||
<form id="occupant-form" class="bg-white dark:bg-gray-800 rounded-xl p-4 shadow-sm space-y-3 mb-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3">
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.occupants.room.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.occupants.room.tooltip"}}">?</span></label>
|
||||
<select name="roomId" required class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm" id="occupant-room-select"></select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.occupants.count.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.occupants.count.tooltip"}}">?</span></label>
|
||||
<input type="number" name="count" min="1" value="1" required class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.occupants.activity.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.occupants.activity.tooltip"}}">?</span></label>
|
||||
<select name="activityLevel" class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
<option value="sleeping">{{t "setup.occupants.activity.options.sleeping"}}</option><option value="sedentary" selected>{{t "setup.occupants.activity.options.sedentary"}}</option>
|
||||
<option value="light">{{t "setup.occupants.activity.options.light"}}</option><option value="moderate">{{t "setup.occupants.activity.options.moderate"}}</option><option value="heavy">{{t "setup.occupants.activity.options.heavy"}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex items-end pb-1">
|
||||
<label class="flex items-center gap-2 text-sm">
|
||||
<input type="checkbox" name="vulnerable" class="rounded">
|
||||
{{t "setup.occupants.vulnerable.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.occupants.vulnerable.tooltip"}}">?</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="px-4 py-1.5 text-sm bg-orange-600 text-white rounded-lg hover:bg-orange-700 transition">{{t "setup.occupants.add"}}</button>
|
||||
<input type="hidden" name="id" value="">
|
||||
</form>
|
||||
<div id="occupants-list" class="space-y-2">
|
||||
<p class="text-sm text-gray-400 dark:text-gray-500">{{t "setup.occupants.noItems"}}</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- AC Units tab -->
|
||||
<section id="tab-ac" class="tab-panel hidden">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-4">{{t "setup.ac.help"}}</p>
|
||||
<form id="ac-form" class="bg-white dark:bg-gray-800 rounded-xl p-4 shadow-sm space-y-3 mb-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.ac.name.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.ac.name.tooltip"}}">?</span></label>
|
||||
<input type="text" name="name" required class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.ac.type.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.ac.type.tooltip"}}">?</span></label>
|
||||
<select name="acType" class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
<option value="portable">{{t "setup.ac.type.options.portable"}}</option><option value="window">{{t "setup.ac.type.options.window"}}</option>
|
||||
<option value="split">{{t "setup.ac.type.options.split"}}</option><option value="central">{{t "setup.ac.type.options.central"}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.ac.capacity.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.ac.capacity.tooltip"}}">?</span></label>
|
||||
<input type="number" name="capacityBtu" step="100" required class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.ac.eer.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.ac.eer.tooltip"}}">?</span></label>
|
||||
<input type="number" name="efficiencyEer" step="0.1" value="10" class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
</div>
|
||||
<div class="flex items-end pb-1">
|
||||
<label class="flex items-center gap-2 text-sm">
|
||||
<input type="checkbox" name="hasDehumidify" class="rounded">
|
||||
{{t "setup.ac.dehumidify.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.ac.dehumidify.tooltip"}}">?</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.ac.rooms.label"}} <span class="tooltip-trigger" data-tooltip="{{t "setup.ac.rooms.tooltip"}}">?</span></label>
|
||||
<div id="ac-room-checkboxes" class="flex flex-wrap gap-2"></div>
|
||||
</div>
|
||||
<button type="submit" class="px-4 py-1.5 text-sm bg-orange-600 text-white rounded-lg hover:bg-orange-700 transition">{{t "setup.ac.add"}}</button>
|
||||
<input type="hidden" name="id" value="">
|
||||
</form>
|
||||
<div id="ac-list" class="space-y-2">
|
||||
<p class="text-sm text-gray-400 dark:text-gray-500">{{t "setup.ac.noItems"}}</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Toggles tab -->
|
||||
<section id="tab-toggles" class="tab-panel hidden">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-4">{{t "setup.toggles.help"}}</p>
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl p-4 shadow-sm space-y-3">
|
||||
<label class="flex items-center gap-3 cursor-pointer">
|
||||
<input type="checkbox" id="toggle-gaming" class="toggle-switch rounded" data-toggle="gaming">
|
||||
<span class="text-sm">{{t "setup.toggles.gaming"}}</span>
|
||||
</label>
|
||||
<label class="flex items-center gap-3 cursor-pointer">
|
||||
<input type="checkbox" id="toggle-cooking" class="toggle-switch rounded" data-toggle="cooking">
|
||||
<span class="text-sm">{{t "setup.toggles.cooking"}}</span>
|
||||
</label>
|
||||
<label class="flex items-center gap-3 cursor-pointer">
|
||||
<input type="checkbox" id="toggle-laundry" class="toggle-switch rounded" data-toggle="laundry">
|
||||
<span class="text-sm">{{t "setup.toggles.laundry"}}</span>
|
||||
</label>
|
||||
<label class="flex items-center gap-3 cursor-pointer">
|
||||
<input type="checkbox" id="toggle-guests" class="toggle-switch rounded" data-toggle="guests">
|
||||
<span class="text-sm">{{t "setup.toggles.guests"}}</span>
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Forecast tab -->
|
||||
<section id="tab-forecast" class="tab-panel hidden">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-4">{{t "setup.forecast.help"}}</p>
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl p-4 shadow-sm space-y-3">
|
||||
<div class="flex items-center gap-4">
|
||||
<button id="fetch-forecast-btn" class="px-4 py-2 bg-orange-600 text-white rounded-lg hover:bg-orange-700 transition text-sm">
|
||||
{{t "setup.forecast.fetch"}}
|
||||
</button>
|
||||
<span id="forecast-status" class="text-sm text-gray-500 dark:text-gray-400">
|
||||
{{t "setup.forecast.lastFetched"}}: <span id="last-fetched">{{t "setup.forecast.never"}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div id="forecast-spinner" class="hidden text-sm text-gray-500 dark:text-gray-400">
|
||||
<span class="inline-block animate-spin mr-2">↻</span> {{t "setup.forecast.fetching"}}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- LLM tab -->
|
||||
<section id="tab-llm" class="tab-panel hidden">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-4">{{t "setup.llm.help"}}</p>
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl p-4 shadow-sm space-y-4">
|
||||
<div id="llm-server-info" class="text-sm text-gray-400 hidden"></div>
|
||||
<form id="llm-form" class="space-y-3">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.llm.provider"}}</label>
|
||||
<select name="llmProvider" id="llm-provider-select" class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
<option value="">-- {{t "setup.llm.provider"}} --</option>
|
||||
<option value="anthropic">{{t "setup.llm.providerOptions.anthropic"}}</option>
|
||||
<option value="openai">{{t "setup.llm.providerOptions.openai"}}</option>
|
||||
<option value="gemini">{{t "setup.llm.providerOptions.gemini"}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.llm.apiKey"}}</label>
|
||||
<input type="password" name="llmApiKey" id="llm-api-key" placeholder="{{t "setup.llm.apiKeyPlaceholder"}}" class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">{{t "setup.llm.model"}}</label>
|
||||
<input type="text" name="llmModel" id="llm-model" placeholder="{{t "setup.llm.modelPlaceholder"}}" class="w-full px-3 py-1.5 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-sm">
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="px-4 py-1.5 text-sm bg-orange-600 text-white rounded-lg hover:bg-orange-700 transition">{{t "setup.llm.save"}}</button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{define "scripts"}}
|
||||
<script src="/assets/js/db.js"></script>
|
||||
<script src="/assets/js/setup.js"></script>
|
||||
{{end}}
|
||||
Reference in New Issue
Block a user