feat: Begins thermal balance point

This commit is contained in:
2025-03-04 09:19:09 -05:00
parent 19a51598e4
commit d22beb9375
7 changed files with 247 additions and 3 deletions

View File

@@ -0,0 +1,143 @@
import Elementary
import ElementaryHTMX
import Routes
import Styleguide
struct HeatingBalancePointForm: HTML, Sendable {
let mode: HeatingBalancePoint.Mode
let heatLossMode: HeatingBalancePoint.HeatLoss.Mode
let response: HeatingBalancePoint.Response?
init(
mode: HeatingBalancePoint.Mode?,
heatLossMode: HeatingBalancePoint.HeatLoss.Mode? = nil,
response: HeatingBalancePoint.Response? = nil
) {
self.mode = mode ?? .thermal
self.heatLossMode = heatLossMode ?? .estimated
self.response = response
}
var content: some HTML {
FormHeader(label: "Balance Point - \(mode.label)", svg: .scale)
// TODO: Toggle button
form(
.hx.post(route: .heatingBalancePoint(.index)),
.hx.target("#result")
) {
div(.class("space-y-6")) {
switch mode {
case .thermal:
ThermalFields(heatLossMode: heatLossMode)
case .economic:
div {}
}
div {
SubmitButton(label: "Calculate Balance Point")
}
}
}
div(.id("result")) {
if let response {
HeatingBalancePointResponse(response: response)
}
}
}
struct ThermalFields: HTML, Sendable {
let heatLossMode: HeatingBalancePoint.HeatLoss.Mode
var content: some HTML {
LabeledContent(label: "System Size (Tons)") {
Input(id: "systemSize", placeholder: "System size")
.attributes(.type(.number), .min("1"), .step("0.5"), .autofocus, .required)
}
div {
div(.class("mb-4")) {
h4(.class("text-lg font-bold")) { "Capacities" }
p(.class("text-sm")) {
"Entering known capacities gives better results, otherwise capacities will be estimated."
}
}
div(.class("grid grid-cols-1 lg:grid-cols-2 gap-4")) {
LabeledContent(label: "Capacity @ 47° (BTU/h)") {
Input(id: "capacityAt47", placeholder: "Capacity @ 47° (optional)")
.attributes(.type(.number), .min("1"), .step("0.5"))
}
LabeledContent(label: "Capacity @ 17° (BTU/h)") {
Input(id: "capacityAt17", placeholder: "Capacity @ 17° (optional)")
.attributes(.type(.number), .min("1"), .step("0.5"))
}
}
}
HeatLossFields(mode: heatLossMode)
}
}
struct HeatLossFields: HTML, Sendable {
let mode: HeatingBalancePoint.HeatLoss.Mode
var content: some HTML {
div(.id("heatLossFields")) {
div(.class("flex flex-wrap justify-between")) {
h4(.class("text-lg font-bold")) { "Heat Loss - \(mode.label)" }
Toggle(
isOn: mode == .estimated,
onLabel: HeatingBalancePoint.HeatLoss.Mode.estimated.label,
onAttributes: [
.hx.get(route: .heatingBalancePoint(.heatLossFields(mode: .estimated))),
.hx.target("#heatLossFields")
],
offLabel: HeatingBalancePoint.HeatLoss.Mode.known.label,
offAttributes: [
.hx.get(route: .heatingBalancePoint(.heatLossFields(mode: .known))),
.hx.target("#heatLossFields")
]
)
.attributes(.class("mb-6"))
}
switch mode {
case .known:
knownFields
case .estimated:
simplifiedFields
}
}
}
private var simplifiedFields: some HTML {
LabeledContent(label: "Building Size (ft²)") {
Input(id: "simplifiedHeatLoss", placeholder: "Square feet")
.attributes(.type(.number), .min("1"), .step("0.5"), .required)
}
}
private var knownFields: some HTML {
LabeledContent(label: "Heat Loss (BTU/h)") {
Input(id: "knownHeatLoss", placeholder: "Heat loss")
.attributes(.type(.number), .min("1"), .step("0.5"), .required)
}
}
}
}
struct HeatingBalancePointResponse: HTML, Sendable {
let response: HeatingBalancePoint.Response
}
private extension HeatingBalancePoint.Mode {
var label: String { rawValue.capitalized }
}
private extension HeatingBalancePoint.HeatLoss.Mode {
var label: String { rawValue.capitalized }
}