Group prices by hour with expandable quarter details and blue borders
Each hour shows as an open-by-default card with the average price and indented quarter-hour rows. Added @source directive to Tailwind v4 input so .clj files are scanned for utility classes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1 +1,2 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
@source "../../src/**/*.clj";
|
||||||
|
|||||||
@@ -14,13 +14,15 @@
|
|||||||
|
|
||||||
(def ^:private hour-fmt (DateTimeFormatter/ofPattern "HH:00"))
|
(def ^:private hour-fmt (DateTimeFormatter/ofPattern "HH:00"))
|
||||||
|
|
||||||
|
(defn- ->ldt [time-dk]
|
||||||
|
(cond
|
||||||
|
(instance? LocalDateTime time-dk) time-dk
|
||||||
|
(instance? java.sql.Timestamp time-dk)
|
||||||
|
(.toLocalDateTime ^java.sql.Timestamp time-dk)
|
||||||
|
:else (LocalDateTime/parse (str time-dk))))
|
||||||
|
|
||||||
(defn- format-hour [time-dk]
|
(defn- format-hour [time-dk]
|
||||||
(let [ldt (cond
|
(.format (->ldt time-dk) hour-fmt))
|
||||||
(instance? LocalDateTime time-dk) time-dk
|
|
||||||
(instance? java.sql.Timestamp time-dk)
|
|
||||||
(.toLocalDateTime ^java.sql.Timestamp time-dk)
|
|
||||||
:else (LocalDateTime/parse (str time-dk)))]
|
|
||||||
(.format ldt hour-fmt)))
|
|
||||||
|
|
||||||
(defn- dkk-mwh->ore-kwh
|
(defn- dkk-mwh->ore-kwh
|
||||||
"Convert DKK/MWh to øre/kWh (divide by 10)."
|
"Convert DKK/MWh to øre/kWh (divide by 10)."
|
||||||
@@ -28,24 +30,43 @@
|
|||||||
(when price-dkk
|
(when price-dkk
|
||||||
(/ (double price-dkk) 10.0)))
|
(/ (double price-dkk) 10.0)))
|
||||||
|
|
||||||
|
(defn- format-ore [price-dkk]
|
||||||
|
(if-let [ore (dkk-mwh->ore-kwh price-dkk)]
|
||||||
|
(format "%.1f" ore)
|
||||||
|
"-"))
|
||||||
|
|
||||||
|
(defn- group-by-hour [prices]
|
||||||
|
(->> prices
|
||||||
|
(group-by #(.getHour (->ldt (:time_dk %))))
|
||||||
|
(sort-by key)))
|
||||||
|
|
||||||
|
(defn- avg-ore [quarters]
|
||||||
|
(let [vals (keep :price_dkk quarters)]
|
||||||
|
(when (seq vals)
|
||||||
|
(/ (reduce + 0.0 (map double vals)) (count vals) 10.0))))
|
||||||
|
|
||||||
|
(defn- hour-block [hour quarters]
|
||||||
|
(let [sorted (sort-by #(.getMinute (->ldt (:time_dk %))) quarters)
|
||||||
|
avg (avg-ore quarters)]
|
||||||
|
[:details {:class "border-2 border-blue-400 rounded-lg mb-2" :open true}
|
||||||
|
[:summary {:class "flex justify-between py-2 px-3 cursor-pointer hover:bg-gray-50
|
||||||
|
font-medium text-gray-900"}
|
||||||
|
[:span (format "%02d:00" hour)]
|
||||||
|
[:span {:class "font-mono"}
|
||||||
|
(if avg (format "%.1f" avg) "-")]]
|
||||||
|
[:div {:class "border-t-2 border-blue-400 px-3 pb-2"}
|
||||||
|
(for [q sorted]
|
||||||
|
[:div {:class "flex justify-between py-1 ml-4 text-gray-500"}
|
||||||
|
[:span (format "%02d:%02d" hour (.getMinute (->ldt (:time_dk q))))]
|
||||||
|
[:span {:class "font-mono"} (format-ore (:price_dkk q))]])]]))
|
||||||
|
|
||||||
(defn- price-table [title prices]
|
(defn- price-table [title prices]
|
||||||
[:div {:class "mt-6"}
|
[:div {:class "mt-6"}
|
||||||
[:h2 {:class "text-lg font-semibold text-gray-900"} title]
|
[:h2 {:class "text-lg font-semibold text-gray-900 mb-2"} title]
|
||||||
(if (seq prices)
|
(if (seq prices)
|
||||||
[:table {:class "mt-2 w-full text-sm"}
|
[:div
|
||||||
[:thead
|
(for [[hour quarters] (group-by-hour prices)]
|
||||||
[:tr {:class "border-b border-gray-200 text-left text-gray-500"}
|
(hour-block hour quarters))]
|
||||||
[:th {:class "py-2 pr-4"} "Time"]
|
|
||||||
[:th {:class "py-2 text-right"} "øre/kWh"]]]
|
|
||||||
[:tbody
|
|
||||||
(for [p prices]
|
|
||||||
[:tr {:class "border-b border-gray-100"}
|
|
||||||
[:td {:class "py-2 pr-4 text-gray-700"}
|
|
||||||
(format-hour (:time_dk p))]
|
|
||||||
[:td {:class "py-2 text-right font-mono text-gray-900"}
|
|
||||||
(if-let [ore (dkk-mwh->ore-kwh (:price_dkk p))]
|
|
||||||
(format "%.1f" ore)
|
|
||||||
"-")]])]]
|
|
||||||
[:p {:class "mt-2 text-sm text-gray-400"} "Not available yet"])])
|
[:p {:class "mt-2 text-sm text-gray-400"} "Not available yet"])])
|
||||||
|
|
||||||
(defn- price-tables [query-fn]
|
(defn- price-tables [query-fn]
|
||||||
@@ -60,7 +81,7 @@
|
|||||||
[:h1 {:class "text-2xl font-bold text-gray-900"}
|
[:h1 {:class "text-2xl font-bold text-gray-900"}
|
||||||
"Electricity Prices"]
|
"Electricity Prices"]
|
||||||
[:p {:class "mt-1 text-sm text-gray-500"}
|
[:p {:class "mt-1 text-sm text-gray-500"}
|
||||||
"Day-ahead spot prices for DK1 in øre/kWh"]
|
"Day-ahead spot prices for DK1 (øre/kWh)"]
|
||||||
[:button {:class "mt-4 rounded-lg bg-blue-600 px-4 py-2 text-white font-medium
|
[:button {:class "mt-4 rounded-lg bg-blue-600 px-4 py-2 text-white font-medium
|
||||||
active:bg-blue-700 transition-colors"
|
active:bg-blue-700 transition-colors"
|
||||||
:hx-post "/prices/fetch"
|
:hx-post "/prices/fetch"
|
||||||
|
|||||||
Reference in New Issue
Block a user