feat: Updates forms to use LabeledInput, style updates.

This commit is contained in:
2026-01-13 10:15:06 -05:00
parent 432533c940
commit df600a5471
12 changed files with 204 additions and 167 deletions

View File

@@ -5225,9 +5225,6 @@
.m-1 { .m-1 {
margin: calc(var(--spacing) * 1); margin: calc(var(--spacing) * 1);
} }
.m-4 {
margin: calc(var(--spacing) * 4);
}
.m-6 { .m-6 {
margin: calc(var(--spacing) * 6); margin: calc(var(--spacing) * 6);
} }
@@ -5371,12 +5368,6 @@
.-my-2 { .-my-2 {
margin-block: calc(var(--spacing) * -2); margin-block: calc(var(--spacing) * -2);
} }
.-my-4 {
margin-block: calc(var(--spacing) * -4);
}
.my-1 {
margin-block: calc(var(--spacing) * 1);
}
.my-1\.5 { .my-1\.5 {
margin-block: calc(var(--spacing) * 1.5); margin-block: calc(var(--spacing) * 1.5);
} }
@@ -5589,11 +5580,14 @@
border-width: var(--border, 1px) 0 var(--border, 1px) var(--border, 1px); border-width: var(--border, 1px) 0 var(--border, 1px) var(--border, 1px);
} }
} }
.me-2 {
margin-inline-end: calc(var(--spacing) * 2);
}
.me-4 { .me-4 {
margin-inline-end: calc(var(--spacing) * 4); margin-inline-end: calc(var(--spacing) * 4);
} }
.me-6 { .me-8 {
margin-inline-end: calc(var(--spacing) * 6); margin-inline-end: calc(var(--spacing) * 8);
} }
.modal-action { .modal-action {
@layer daisyui.l1.l2.l3 { @layer daisyui.l1.l2.l3 {
@@ -5634,12 +5628,6 @@
} }
} }
} }
.mt-1 {
margin-top: calc(var(--spacing) * 1);
}
.mt-2 {
margin-top: calc(var(--spacing) * 2);
}
.mt-4 { .mt-4 {
margin-top: calc(var(--spacing) * 4); margin-top: calc(var(--spacing) * 4);
} }
@@ -6592,6 +6580,9 @@
.w-full { .w-full {
width: 100%; width: 100%;
} }
.max-w-\[300px\] {
max-width: 300px;
}
.flex-1 { .flex-1 {
flex: 1; flex: 1;
} }
@@ -6797,9 +6788,6 @@
.flex-col { .flex-col {
flex-direction: column; flex-direction: column;
} }
.flex-row {
flex-direction: row;
}
.flex-wrap { .flex-wrap {
flex-wrap: wrap; flex-wrap: wrap;
} }
@@ -7269,6 +7257,12 @@
border-color: currentColor; border-color: currentColor;
} }
} }
.border-base-300 {
border-color: var(--color-base-300);
}
.border-error {
border-color: var(--color-error);
}
.border-gray-200 { .border-gray-200 {
border-color: var(--color-gray-200); border-color: var(--color-gray-200);
} }
@@ -7852,21 +7846,12 @@
.px-4 { .px-4 {
padding-inline: calc(var(--spacing) * 4); padding-inline: calc(var(--spacing) * 4);
} }
.py-1 {
padding-block: calc(var(--spacing) * 1);
}
.py-1\.5 { .py-1\.5 {
padding-block: calc(var(--spacing) * 1.5); padding-block: calc(var(--spacing) * 1.5);
} }
.py-2 { .py-2 {
padding-block: calc(var(--spacing) * 2); padding-block: calc(var(--spacing) * 2);
} }
.py-4 {
padding-block: calc(var(--spacing) * 4);
}
.py-6 {
padding-block: calc(var(--spacing) * 6);
}
.ps-2 { .ps-2 {
padding-inline-start: calc(var(--spacing) * 2); padding-inline-start: calc(var(--spacing) * 2);
} }
@@ -7884,6 +7869,9 @@
.pe-2 { .pe-2 {
padding-inline-end: calc(var(--spacing) * 2); padding-inline-end: calc(var(--spacing) * 2);
} }
.pt-4 {
padding-top: calc(var(--spacing) * 4);
}
.pt-6 { .pt-6 {
padding-top: calc(var(--spacing) * 6); padding-top: calc(var(--spacing) * 6);
} }
@@ -7941,14 +7929,6 @@
font-size: var(--text-3xl); font-size: var(--text-3xl);
line-height: var(--tw-leading, var(--text-3xl--line-height)); line-height: var(--tw-leading, var(--text-3xl--line-height));
} }
.text-4xl {
font-size: var(--text-4xl);
line-height: var(--tw-leading, var(--text-4xl--line-height));
}
.text-base {
font-size: var(--text-base);
line-height: var(--tw-leading, var(--text-base--line-height));
}
.text-lg { .text-lg {
font-size: var(--text-lg); font-size: var(--text-lg);
line-height: var(--tw-leading, var(--text-lg--line-height)); line-height: var(--tw-leading, var(--text-lg--line-height));
@@ -8518,6 +8498,9 @@
.text-base-100 { .text-base-100 {
color: var(--color-base-100); color: var(--color-base-100);
} }
.text-base-300 {
color: var(--color-base-300);
}
.text-base-content { .text-base-content {
color: var(--color-base-content); color: var(--color-base-content);
} }

View File

@@ -13,7 +13,7 @@ public struct Label: HTML, Sendable {
} }
public var body: some HTML<HTMLTag.span> { public var body: some HTML<HTMLTag.span> {
span(.class("text-lg text-secondary font-bold")) { span(.class("text-lg label font-bold")) {
title title
} }
} }

View File

@@ -211,10 +211,8 @@ extension SiteRoute.View.ProjectRoute.EquipmentInfoRoute {
return await equipmentView(on: request, projectID: projectID) return await equipmentView(on: request, projectID: projectID)
case .submit(let form): case .submit(let form):
return await ResultView { return await equipmentView(on: request, projectID: projectID) {
try await database.equipment.create(form) _ = try await database.equipment.create(form)
} onSuccess: { equipment in
EquipmentInfoView(equipmentInfo: equipment, projectID: projectID)
} }
case .update(let id, let updates): case .update(let id, let updates):
@@ -576,7 +574,8 @@ extension SiteRoute.View.ProjectRoute.DuctSizingRoute {
) async -> AnySendableHTML { ) async -> AnySendableHTML {
@Dependency(\.database) var database @Dependency(\.database) var database
return await ResultView { return await request.view {
await ResultView {
try await catching() try await catching()
return ( return (
try await database.projects.getCompletedSteps(projectID), try await database.projects.getCompletedSteps(projectID),
@@ -589,6 +588,7 @@ extension SiteRoute.View.ProjectRoute.DuctSizingRoute {
} }
} }
} }
}
extension SiteRoute.View.UserRoute { extension SiteRoute.View.UserRoute {

View File

@@ -42,19 +42,27 @@ struct ComponentLossForm: HTML, Sendable {
input(.class("hidden"), .name("projectID"), .value("\(projectID)")) input(.class("hidden"), .name("projectID"), .value("\(projectID)"))
div { LabeledInput(
label(.for("name")) { "Name" } "Name",
Input(id: "name", placeholder: "Name") .name("name"),
.attributes(.type(.text), .required, .autofocus, .value(componentLoss?.name)) .type(.text),
} .value(componentLoss?.name),
div { .placeholder("Name"),
label(.for("value")) { "Value" } .required,
Input(id: "value", placeholder: "Pressure loss") .autofocus
.attributes( )
.type(.number), .min("0.03"), .max("1.0"), .step("0.01"), .required,
.value(componentLoss?.value) LabeledInput(
"Value",
.name("value"),
.type(.number),
.value(componentLoss?.value),
.placeholder("0.2"),
.min("0.03"),
.max("1.0"),
.step("0.01"),
.required
) )
}
SubmitButton() SubmitButton()
.attributes(.class("btn-block")) .attributes(.class("btn-block"))

View File

@@ -24,6 +24,7 @@ struct ComponentPressureLossesView: HTML, Sendable {
} }
.attributes(.class("px-4")) .attributes(.class("px-4"))
div(.class("overflow-x-auto")) {
table(.class("table table-zebra")) { table(.class("table table-zebra")) {
thead { thead {
tr(.class("text-xl font-bold")) { tr(.class("text-xl font-bold")) {
@@ -34,7 +35,7 @@ struct ComponentPressureLossesView: HTML, Sendable {
Tooltip("Add Component Loss") { Tooltip("Add Component Loss") {
PlusButton() PlusButton()
.attributes( .attributes(
.class("btn-ghost text-2xl"), .class("btn-ghost text-2xl me-2"),
.showModal(id: ComponentLossForm.id()) .showModal(id: ComponentLossForm.id())
) )
} }
@@ -50,6 +51,7 @@ struct ComponentPressureLossesView: HTML, Sendable {
} }
} }
}
ComponentLossForm(dismiss: true, projectID: projectID, componentLoss: nil) ComponentLossForm(dismiss: true, projectID: projectID, componentLoss: nil)
} }

View File

@@ -13,7 +13,7 @@ struct DuctSizingView: HTML, Sendable {
let rooms: [DuctSizing.RoomContainer] let rooms: [DuctSizing.RoomContainer]
var body: some HTML { var body: some HTML {
div { div(.class("space-y-4")) {
PageTitle { "Duct Sizes" } PageTitle { "Duct Sizes" }
if rooms.count == 0 { if rooms.count == 0 {
p(.class("text-error italic")) { p(.class("text-error italic")) {

View File

@@ -80,8 +80,16 @@ struct RectangularSizeForm: HTML, Sendable {
input(.class("hidden"), .name("register"), .value(register)) input(.class("hidden"), .name("register"), .value(register))
input(.class("hidden"), .name("id"), .value(rectangularSizeID)) input(.class("hidden"), .name("id"), .value(rectangularSizeID))
Input(id: "height", placeholder: "Height") LabeledInput(
.attributes(.type(.number), .min("0"), .value(height), .required, .autofocus) "Height",
.name("height"),
.type(.number),
.value(height),
.placeholder("8"),
.min("0"),
.required,
.autofocus
)
SubmitButton() SubmitButton()
.attributes(.class("btn-block")) .attributes(.class("btn-block"))

View File

@@ -47,7 +47,7 @@ struct EffectiveLengthForm: HTML, Sendable {
dismiss: dismiss dismiss: dismiss
) { ) {
h1(.class("text-2xl font-bold")) { "Effective Length" } h1(.class("text-2xl font-bold")) { "Effective Length" }
div(.id("formStep_\(id)")) { div(.id("formStep_\(id)"), .class("mt-4")) {
StepOne(projectID: projectID, effectiveLength: effectiveLength) StepOne(projectID: projectID, effectiveLength: effectiveLength)
} }
} }
@@ -74,8 +74,15 @@ struct EffectiveLengthForm: HTML, Sendable {
if let id = effectiveLength?.id { if let id = effectiveLength?.id {
input(.class("hidden"), .name("id"), .value("\(id)")) input(.class("hidden"), .name("id"), .value("\(id)"))
} }
Input(id: "name", placeholder: "Name")
.attributes(.type(.text), .required, .autofocus, .value(effectiveLength?.name)) LabeledInput(
"Name",
.name("name"),
.type(.text),
.value(effectiveLength?.name),
.required,
.autofocus
)
GroupTypeSelect(projectID: projectID, selected: effectiveLength?.type ?? .supply) GroupTypeSelect(projectID: projectID, selected: effectiveLength?.type ?? .supply)
@@ -193,14 +200,6 @@ struct EffectiveLengthForm: HTML, Sendable {
} }
} }
div(.class("grid grid-cols-5 gap-2")) {
Label("Group")
Label("Letter")
Label("Length")
Label("Quantity")
.attributes(.class("col-span-2"))
}
div(.id("groups"), .class("space-y-4")) { div(.id("groups"), .class("space-y-4")) {
if let effectiveLength { if let effectiveLength {
for group in effectiveLength.groups { for group in effectiveLength.groups {
@@ -228,12 +227,16 @@ struct StraightLengthField: HTML, Sendable {
var body: some HTML<HTMLTag.div> { var body: some HTML<HTMLTag.div> {
Row { Row {
Input( LabeledInput(
name: "straightLengths", "Length",
placeholder: "Length" .name("straightLengths"),
.type(.number),
.value(value),
.placeholder("10"),
.min("0"),
.autofocus,
.required
) )
.attributes(.type(.number), .min("0"), .autofocus, .required, .value(value))
TrashButton() TrashButton()
.attributes(.data("remove", value: "true")) .attributes(.data("remove", value: "true"))
} }
@@ -252,18 +255,43 @@ struct GroupField: HTML, Sendable {
} }
var body: some HTML { var body: some HTML {
div(.class("grid grid-cols-5 gap-2")) { div(.class("grid grid-cols-3 gap-2 p-2 border rounded-lg shadow-sm")) {
GroupSelect(style: style) GroupSelect(style: style)
Input(name: "group[letter]", placeholder: "Letter")
.attributes(.type(.text), .autofocus, .required, .value(group?.letter)) LabeledInput(
Input(name: "group[length]", placeholder: "Length") "Letter",
.attributes(.type(.number), .min("0"), .required, .value(group?.value)) .name("group[letter]"),
Input(name: "group[quantity]", placeholder: "Quantity") .type(.text),
.attributes(.type(.number), .min("1"), .value("1"), .required, .value(group?.quantity ?? 1)) .value(group?.letter),
div(.class("flex justify-end")) { .placeholder("a"),
.required
)
LabeledInput(
"Length",
.name("group[length]"),
.type(.number),
.value(group?.value),
.placeholder("10"),
.min("0"),
.required
)
LabeledInput(
"Quantity",
.name("group[quantity]"),
.type(.number),
.value(group?.quantity ?? 1),
.min("1"),
.required
)
.attributes(.class("col-span-2"))
TrashButton() TrashButton()
.attributes(.data("remove", value: "true"), .class("mx-2")) .attributes(
} .data("remove", value: "true"),
.class("me-2 btn-block")
)
} }
.attributes(.class("space-x-2"), .hx.ext("remove")) .attributes(.class("space-x-2"), .hx.ext("remove"))
} }
@@ -274,15 +302,18 @@ struct GroupSelect: HTML, Sendable {
let style: EffectiveLength.EffectiveLengthType let style: EffectiveLength.EffectiveLengthType
var body: some HTML { var body: some HTML {
label(.class("select")) {
span(.class("label")) { "Group" }
select( select(
.name("group[group]"), .name("group[group]"),
.class("select") .autofocus
) { ) {
for value in style.selectOptions { for value in style.selectOptions {
option(.value("\(value)")) { "\(value)" } option(.value("\(value)")) { "\(value)" }
} }
} }
} }
}
} }
@@ -291,8 +322,10 @@ struct GroupTypeSelect: HTML, Sendable {
let projectID: Project.ID let projectID: Project.ID
let selected: EffectiveLength.EffectiveLengthType let selected: EffectiveLength.EffectiveLengthType
var body: some HTML<HTMLTag.select> { var body: some HTML<HTMLTag.label> {
select(.class("select"), .name("type"), .id("type")) { label(.class("select")) {
span(.class("label")) { "Type" }
select(.name("type"), .id("type")) {
for value in EffectiveLength.EffectiveLengthType.allCases { for value in EffectiveLength.EffectiveLengthType.allCases {
option( option(
.value("\(value.rawValue)"), .value("\(value.rawValue)"),
@@ -302,6 +335,7 @@ struct GroupTypeSelect: HTML, Sendable {
} }
} }
} }
}
extension EffectiveLength.EffectiveLengthType { extension EffectiveLength.EffectiveLengthType {

View File

@@ -3,13 +3,10 @@ import ElementaryHTMX
import ManualDCore import ManualDCore
import Styleguide import Styleguide
// TODO: Group into grids of supply / return.
struct EffectiveLengthsView: HTML, Sendable { struct EffectiveLengthsView: HTML, Sendable {
@Environment(ProjectViewValue.$projectID) var projectID @Environment(ProjectViewValue.$projectID) var projectID
// let projectID: Project.ID
let effectiveLengths: [EffectiveLength] let effectiveLengths: [EffectiveLength]
var supplies: [EffectiveLength] { var supplies: [EffectiveLength] {
@@ -41,9 +38,8 @@ struct EffectiveLengthsView: HTML, Sendable {
.attributes(.class("hidden"), when: supplies.count == 0) .attributes(.class("hidden"), when: supplies.count == 0)
div(.class("grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4")) { div(.class("grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4")) {
for (n, row) in supplies.enumerated() { for row in supplies {
EffectiveLengthView(effectiveLength: row) EffectiveLengthView(effectiveLength: row)
.attributes(.class(n == 0 ? "border-primary" : "border-gray-200"))
} }
} }
} }
@@ -52,9 +48,8 @@ struct EffectiveLengthsView: HTML, Sendable {
h2(.class("text-xl font-bold pb-4")) { "Returns" } h2(.class("text-xl font-bold pb-4")) { "Returns" }
.attributes(.class("hidden"), when: returns.count == 0) .attributes(.class("hidden"), when: returns.count == 0)
div(.class("grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 space-x-4 space-y-4")) { div(.class("grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 space-x-4 space-y-4")) {
for (n, row) in returns.enumerated() { for row in returns {
EffectiveLengthView(effectiveLength: row) EffectiveLengthView(effectiveLength: row)
.attributes(.class(n == 0 ? "border-secondary" : "border-gray-200"))
} }
} }
} }
@@ -84,44 +79,41 @@ struct EffectiveLengthsView: HTML, Sendable {
.class("card h-full bg-base-100 shadow-sm border rounded-lg"), .class("card h-full bg-base-100 shadow-sm border rounded-lg"),
.id(id) .id(id)
) { ) {
div(.class("card-body")) { div(.class("card-body text-lg")) {
Row { Row {
h2 { effectiveLength.name } h2 { effectiveLength.name }
div( div(
.class("space-x-4") .class("space-x-4")
) { ) {
span(.class("text-sm italic")) { span(.class("text-primary text-sm italic")) {
"Total" "Total"
} }
.attributes(.class("text-primary"), when: effectiveLength.type == .supply)
.attributes(.class("text-secondary"), when: effectiveLength.type == .return)
Number(self.effectiveLength.totalEquivalentLength, digits: 0) Number(self.effectiveLength.totalEquivalentLength, digits: 0)
.attributes(.class("badge badge-outline text-lg")) .attributes(.class("badge badge-outline badge-primary text-lg"))
.attributes(
.class("badge-primary"), when: effectiveLength.type == .supply
)
.attributes(
.class("badge-secondary"), when: effectiveLength.type == .return
)
} }
} }
.attributes(.class("card-title pb-6")) .attributes(.class("card-title pb-6"))
Label("Straight Lengths") Row {
Label { "Straight Lengths" }
ul {
for length in effectiveLength.straightLengths { for length in effectiveLength.straightLengths {
div(.class("flex justify-end")) { li {
Number(length) Number(length)
} }
} }
}
}
.attributes(.class("pb-6"))
Row { Row {
Label("Groups") span { "Groups" }
Label("Equivalent Length") span { "Equivalent Length" }
Label("Quantity") span { "Quantity" }
} }
.attributes(.class("border-b border-gray-200")) .attributes(.class("label font-bold border-b border-label"))
for group in effectiveLength.groups { for group in effectiveLength.groups {
Row { Row {

View File

@@ -29,7 +29,7 @@ struct EquipmentInfoView: HTML, Sendable {
table(.class("table table-zebra")) { table(.class("table table-zebra")) {
tbody(.class("text-lg")) { tbody(.class("text-lg")) {
tr { tr {
td { span { "Static Pressure" } } td { Label { "Static Pressure" } }
td { td {
div(.class("flex justify-end")) { div(.class("flex justify-end")) {
Number(equipmentInfo.staticPressure) Number(equipmentInfo.staticPressure)
@@ -37,7 +37,7 @@ struct EquipmentInfoView: HTML, Sendable {
} }
} }
tr { tr {
td { span { "Heating CFM" } } td { Label { "Heating CFM" } }
td { td {
div(.class("flex justify-end")) { div(.class("flex justify-end")) {
Number(equipmentInfo.heatingCFM) Number(equipmentInfo.heatingCFM)
@@ -45,7 +45,7 @@ struct EquipmentInfoView: HTML, Sendable {
} }
} }
tr { tr {
td { span { "Cooling CFM" } } td { Label { "Cooling CFM" } }
td { td {
div(.class("flex justify-end")) { div(.class("flex justify-end")) {
Number(equipmentInfo.coolingCFM) Number(equipmentInfo.coolingCFM)

View File

@@ -21,7 +21,7 @@ struct ProjectDetail: HTML, Sendable {
table(.class("table table-zebra text-lg")) { table(.class("table table-zebra text-lg")) {
tbody { tbody {
tr { tr {
td { "Name" } td(.class("label font-bold")) { "Name" }
td { td {
div(.class("flex justify-end")) { div(.class("flex justify-end")) {
project.name project.name
@@ -29,7 +29,7 @@ struct ProjectDetail: HTML, Sendable {
} }
} }
tr { tr {
td { "Street Address" } td(.class("label font-bold")) { "Street Address" }
td { td {
div(.class("flex justify-end")) { div(.class("flex justify-end")) {
project.streetAddress project.streetAddress
@@ -37,7 +37,7 @@ struct ProjectDetail: HTML, Sendable {
} }
} }
tr { tr {
td { "City" } td(.class("label font-bold")) { "City" }
td { td {
div(.class("flex justify-end")) { div(.class("flex justify-end")) {
project.city project.city
@@ -45,7 +45,7 @@ struct ProjectDetail: HTML, Sendable {
} }
} }
tr { tr {
td { "State" } td(.class("label font-bold")) { "State" }
td { td {
div(.class("flex justify-end")) { div(.class("flex justify-end")) {
project.state project.state
@@ -53,7 +53,7 @@ struct ProjectDetail: HTML, Sendable {
} }
} }
tr { tr {
td { "Zip" } td(.class("label font-bold")) { "Zip" }
td { td {
div(.class("flex justify-end")) { div(.class("flex justify-end")) {
project.zipCode project.zipCode

View File

@@ -29,36 +29,39 @@ struct RoomsView: HTML, Sendable {
) { ) {
LabeledContent { LabeledContent {
div(.class("flex justify-end items-end space-x-4")) { div(.class("flex justify-end items-end space-x-4")) {
// SVG(.squarePen) Label {
span(.class("font-bold")) {
"Sensible Heat Ratio" "Sensible Heat Ratio"
} }
.attributes(.class("me-8"), when: sensibleHeatRatio == nil)
} }
} content: { } content: {
if let sensibleHeatRatio { if let sensibleHeatRatio {
Badge(number: sensibleHeatRatio) Badge(number: sensibleHeatRatio)
} else {
SVG(.squarePen)
} }
} }
} }
.attributes(.class("border rounded-lg border-error"), when: sensibleHeatRatio == nil)
} }
} }
} }
div(.class("flex flex-wrap justify-between mt-6")) { div(.class("flex flex-wrap justify-between mt-6")) {
div(.class("flex items-end space-x-4")) { div(.class("flex items-end space-x-4")) {
span(.class("font-bold")) { "Heating Total" } Label { "Heating Total" }
Badge(number: rooms.heatingTotal, digits: 0) Badge(number: rooms.heatingTotal, digits: 0)
.attributes(.class("badge-error")) .attributes(.class("badge-error"))
} }
div(.class("flex items-end space-x-4")) { div(.class("flex items-end space-x-4")) {
span(.class("font-bold")) { "Cooling Total" } Label { "Cooling Total" }
Badge(number: rooms.coolingTotal, digits: 0) Badge(number: rooms.coolingTotal, digits: 0)
.attributes(.class("badge-success")) .attributes(.class("badge-success"))
} }
div(.class("flex justify-end items-end space-x-4 me-4")) { div(.class("flex justify-end items-end space-x-4 me-4")) {
span(.class("font-bold")) { "Cooling Sensible" } Label { "Cooling Sensible" }
Badge(number: rooms.coolingSensible(shr: sensibleHeatRatio), digits: 0) Badge(number: rooms.coolingSensible(shr: sensibleHeatRatio), digits: 0)
.attributes(.class("badge-info")) .attributes(.class("badge-info"))
} }
@@ -95,7 +98,7 @@ struct RoomsView: HTML, Sendable {
} }
} }
th { th {
div(.class("flex justify-end")) { div(.class("flex justify-end me-2")) {
Tooltip("Add Room") { Tooltip("Add Room") {
PlusButton() PlusButton()
.attributes( .attributes(
@@ -201,6 +204,12 @@ struct RoomsView: HTML, Sendable {
let projectID: Project.ID let projectID: Project.ID
let sensibleHeatRatio: Double? let sensibleHeatRatio: Double?
var route: String {
SiteRoute.View.router
.path(for: .project(.detail(projectID, .rooms(.index))))
.appendingPath("update-shr")
}
var body: some HTML { var body: some HTML {
ModalForm(id: Self.id, dismiss: true) { ModalForm(id: Self.id, dismiss: true) {
h1(.class("text-xl font-bold mb-6")) { h1(.class("text-xl font-bold mb-6")) {
@@ -208,19 +217,20 @@ struct RoomsView: HTML, Sendable {
} }
form( form(
.class("grid grid-cols-1 gap-4"), .class("grid grid-cols-1 gap-4"),
.hx.patch("/projects/\(projectID)/rooms/update-shr"), .hx.patch(route),
.hx.target("body"), .hx.target("body"),
.hx.swap(.outerHTML) .hx.swap(.outerHTML)
) { ) {
input(.class("hidden"), .name("projectID"), .value("\(projectID)")) input(.class("hidden"), .name("projectID"), .value("\(projectID)"))
LabeledInput( LabeledInput(
"SHR", "SHR",
.name("sensibleHeatRatio"),
.type(.number), .type(.number),
.value(sensibleHeatRatio),
.placeholder("0.83"), .placeholder("0.83"),
.min("0"), .min("0"),
.max("1"), .max("1"),
.step("0.01"), .step("0.01"),
.value(sensibleHeatRatio),
.autofocus .autofocus
) )
SubmitButton() SubmitButton()