feat: Updates forms to use LabeledInput, style updates.
This commit is contained in:
@@ -42,19 +42,27 @@ struct ComponentLossForm: HTML, Sendable {
|
||||
|
||||
input(.class("hidden"), .name("projectID"), .value("\(projectID)"))
|
||||
|
||||
div {
|
||||
label(.for("name")) { "Name" }
|
||||
Input(id: "name", placeholder: "Name")
|
||||
.attributes(.type(.text), .required, .autofocus, .value(componentLoss?.name))
|
||||
}
|
||||
div {
|
||||
label(.for("value")) { "Value" }
|
||||
Input(id: "value", placeholder: "Pressure loss")
|
||||
.attributes(
|
||||
.type(.number), .min("0.03"), .max("1.0"), .step("0.01"), .required,
|
||||
.value(componentLoss?.value)
|
||||
)
|
||||
}
|
||||
LabeledInput(
|
||||
"Name",
|
||||
.name("name"),
|
||||
.type(.text),
|
||||
.value(componentLoss?.name),
|
||||
.placeholder("Name"),
|
||||
.required,
|
||||
.autofocus
|
||||
)
|
||||
|
||||
LabeledInput(
|
||||
"Value",
|
||||
.name("value"),
|
||||
.type(.number),
|
||||
.value(componentLoss?.value),
|
||||
.placeholder("0.2"),
|
||||
.min("0.03"),
|
||||
.max("1.0"),
|
||||
.step("0.01"),
|
||||
.required
|
||||
)
|
||||
|
||||
SubmitButton()
|
||||
.attributes(.class("btn-block"))
|
||||
|
||||
@@ -24,30 +24,32 @@ struct ComponentPressureLossesView: HTML, Sendable {
|
||||
}
|
||||
.attributes(.class("px-4"))
|
||||
|
||||
table(.class("table table-zebra")) {
|
||||
thead {
|
||||
tr(.class("text-xl font-bold")) {
|
||||
th { "Name" }
|
||||
th { "Value" }
|
||||
th {
|
||||
div(.class("flex justify-end mx-auto")) {
|
||||
Tooltip("Add Component Loss") {
|
||||
PlusButton()
|
||||
.attributes(
|
||||
.class("btn-ghost text-2xl"),
|
||||
.showModal(id: ComponentLossForm.id())
|
||||
)
|
||||
div(.class("overflow-x-auto")) {
|
||||
table(.class("table table-zebra")) {
|
||||
thead {
|
||||
tr(.class("text-xl font-bold")) {
|
||||
th { "Name" }
|
||||
th { "Value" }
|
||||
th {
|
||||
div(.class("flex justify-end mx-auto")) {
|
||||
Tooltip("Add Component Loss") {
|
||||
PlusButton()
|
||||
.attributes(
|
||||
.class("btn-ghost text-2xl me-2"),
|
||||
.showModal(id: ComponentLossForm.id())
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tbody {
|
||||
for row in componentPressureLosses {
|
||||
TableRow(row: row)
|
||||
tbody {
|
||||
for row in componentPressureLosses {
|
||||
TableRow(row: row)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
ComponentLossForm(dismiss: true, projectID: projectID, componentLoss: nil)
|
||||
|
||||
@@ -13,7 +13,7 @@ struct DuctSizingView: HTML, Sendable {
|
||||
let rooms: [DuctSizing.RoomContainer]
|
||||
|
||||
var body: some HTML {
|
||||
div {
|
||||
div(.class("space-y-4")) {
|
||||
PageTitle { "Duct Sizes" }
|
||||
if rooms.count == 0 {
|
||||
p(.class("text-error italic")) {
|
||||
|
||||
@@ -80,8 +80,16 @@ struct RectangularSizeForm: HTML, Sendable {
|
||||
input(.class("hidden"), .name("register"), .value(register))
|
||||
input(.class("hidden"), .name("id"), .value(rectangularSizeID))
|
||||
|
||||
Input(id: "height", placeholder: "Height")
|
||||
.attributes(.type(.number), .min("0"), .value(height), .required, .autofocus)
|
||||
LabeledInput(
|
||||
"Height",
|
||||
.name("height"),
|
||||
.type(.number),
|
||||
.value(height),
|
||||
.placeholder("8"),
|
||||
.min("0"),
|
||||
.required,
|
||||
.autofocus
|
||||
)
|
||||
|
||||
SubmitButton()
|
||||
.attributes(.class("btn-block"))
|
||||
|
||||
@@ -47,7 +47,7 @@ struct EffectiveLengthForm: HTML, Sendable {
|
||||
dismiss: dismiss
|
||||
) {
|
||||
h1(.class("text-2xl font-bold")) { "Effective Length" }
|
||||
div(.id("formStep_\(id)")) {
|
||||
div(.id("formStep_\(id)"), .class("mt-4")) {
|
||||
StepOne(projectID: projectID, effectiveLength: effectiveLength)
|
||||
}
|
||||
}
|
||||
@@ -74,8 +74,15 @@ struct EffectiveLengthForm: HTML, Sendable {
|
||||
if let id = effectiveLength?.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)
|
||||
|
||||
@@ -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")) {
|
||||
if let effectiveLength {
|
||||
for group in effectiveLength.groups {
|
||||
@@ -228,12 +227,16 @@ struct StraightLengthField: HTML, Sendable {
|
||||
|
||||
var body: some HTML<HTMLTag.div> {
|
||||
Row {
|
||||
Input(
|
||||
name: "straightLengths",
|
||||
placeholder: "Length"
|
||||
LabeledInput(
|
||||
"Length",
|
||||
.name("straightLengths"),
|
||||
.type(.number),
|
||||
.value(value),
|
||||
.placeholder("10"),
|
||||
.min("0"),
|
||||
.autofocus,
|
||||
.required
|
||||
)
|
||||
.attributes(.type(.number), .min("0"), .autofocus, .required, .value(value))
|
||||
|
||||
TrashButton()
|
||||
.attributes(.data("remove", value: "true"))
|
||||
}
|
||||
@@ -252,18 +255,43 @@ struct GroupField: HTML, Sendable {
|
||||
}
|
||||
|
||||
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)
|
||||
Input(name: "group[letter]", placeholder: "Letter")
|
||||
.attributes(.type(.text), .autofocus, .required, .value(group?.letter))
|
||||
Input(name: "group[length]", placeholder: "Length")
|
||||
.attributes(.type(.number), .min("0"), .required, .value(group?.value))
|
||||
Input(name: "group[quantity]", placeholder: "Quantity")
|
||||
.attributes(.type(.number), .min("1"), .value("1"), .required, .value(group?.quantity ?? 1))
|
||||
div(.class("flex justify-end")) {
|
||||
TrashButton()
|
||||
.attributes(.data("remove", value: "true"), .class("mx-2"))
|
||||
}
|
||||
|
||||
LabeledInput(
|
||||
"Letter",
|
||||
.name("group[letter]"),
|
||||
.type(.text),
|
||||
.value(group?.letter),
|
||||
.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()
|
||||
.attributes(
|
||||
.data("remove", value: "true"),
|
||||
.class("me-2 btn-block")
|
||||
)
|
||||
}
|
||||
.attributes(.class("space-x-2"), .hx.ext("remove"))
|
||||
}
|
||||
@@ -274,12 +302,15 @@ struct GroupSelect: HTML, Sendable {
|
||||
let style: EffectiveLength.EffectiveLengthType
|
||||
|
||||
var body: some HTML {
|
||||
select(
|
||||
.name("group[group]"),
|
||||
.class("select")
|
||||
) {
|
||||
for value in style.selectOptions {
|
||||
option(.value("\(value)")) { "\(value)" }
|
||||
label(.class("select")) {
|
||||
span(.class("label")) { "Group" }
|
||||
select(
|
||||
.name("group[group]"),
|
||||
.autofocus
|
||||
) {
|
||||
for value in style.selectOptions {
|
||||
option(.value("\(value)")) { "\(value)" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -291,13 +322,16 @@ struct GroupTypeSelect: HTML, Sendable {
|
||||
let projectID: Project.ID
|
||||
let selected: EffectiveLength.EffectiveLengthType
|
||||
|
||||
var body: some HTML<HTMLTag.select> {
|
||||
select(.class("select"), .name("type"), .id("type")) {
|
||||
for value in EffectiveLength.EffectiveLengthType.allCases {
|
||||
option(
|
||||
.value("\(value.rawValue)"),
|
||||
) { value.title }
|
||||
.attributes(.selected, when: value == selected)
|
||||
var body: some HTML<HTMLTag.label> {
|
||||
label(.class("select")) {
|
||||
span(.class("label")) { "Type" }
|
||||
select(.name("type"), .id("type")) {
|
||||
for value in EffectiveLength.EffectiveLengthType.allCases {
|
||||
option(
|
||||
.value("\(value.rawValue)"),
|
||||
) { value.title }
|
||||
.attributes(.selected, when: value == selected)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,10 @@ import ElementaryHTMX
|
||||
import ManualDCore
|
||||
import Styleguide
|
||||
|
||||
// TODO: Group into grids of supply / return.
|
||||
|
||||
struct EffectiveLengthsView: HTML, Sendable {
|
||||
|
||||
@Environment(ProjectViewValue.$projectID) var projectID
|
||||
|
||||
// let projectID: Project.ID
|
||||
let effectiveLengths: [EffectiveLength]
|
||||
|
||||
var supplies: [EffectiveLength] {
|
||||
@@ -41,9 +38,8 @@ struct EffectiveLengthsView: HTML, Sendable {
|
||||
.attributes(.class("hidden"), when: supplies.count == 0)
|
||||
|
||||
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)
|
||||
.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" }
|
||||
.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")) {
|
||||
for (n, row) in returns.enumerated() {
|
||||
for row in returns {
|
||||
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"),
|
||||
.id(id)
|
||||
) {
|
||||
div(.class("card-body")) {
|
||||
div(.class("card-body text-lg")) {
|
||||
Row {
|
||||
h2 { effectiveLength.name }
|
||||
div(
|
||||
.class("space-x-4")
|
||||
) {
|
||||
span(.class("text-sm italic")) {
|
||||
span(.class("text-primary text-sm italic")) {
|
||||
"Total"
|
||||
}
|
||||
.attributes(.class("text-primary"), when: effectiveLength.type == .supply)
|
||||
.attributes(.class("text-secondary"), when: effectiveLength.type == .return)
|
||||
|
||||
Number(self.effectiveLength.totalEquivalentLength, digits: 0)
|
||||
.attributes(.class("badge badge-outline text-lg"))
|
||||
.attributes(
|
||||
.class("badge-primary"), when: effectiveLength.type == .supply
|
||||
)
|
||||
.attributes(
|
||||
.class("badge-secondary"), when: effectiveLength.type == .return
|
||||
)
|
||||
.attributes(.class("badge badge-outline badge-primary text-lg"))
|
||||
}
|
||||
}
|
||||
.attributes(.class("card-title pb-6"))
|
||||
|
||||
Label("Straight Lengths")
|
||||
Row {
|
||||
Label { "Straight Lengths" }
|
||||
|
||||
for length in effectiveLength.straightLengths {
|
||||
div(.class("flex justify-end")) {
|
||||
Number(length)
|
||||
ul {
|
||||
for length in effectiveLength.straightLengths {
|
||||
li {
|
||||
Number(length)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.attributes(.class("pb-6"))
|
||||
|
||||
Row {
|
||||
Label("Groups")
|
||||
Label("Equivalent Length")
|
||||
Label("Quantity")
|
||||
span { "Groups" }
|
||||
span { "Equivalent Length" }
|
||||
span { "Quantity" }
|
||||
}
|
||||
.attributes(.class("border-b border-gray-200"))
|
||||
.attributes(.class("label font-bold border-b border-label"))
|
||||
|
||||
for group in effectiveLength.groups {
|
||||
Row {
|
||||
|
||||
@@ -29,7 +29,7 @@ struct EquipmentInfoView: HTML, Sendable {
|
||||
table(.class("table table-zebra")) {
|
||||
tbody(.class("text-lg")) {
|
||||
tr {
|
||||
td { span { "Static Pressure" } }
|
||||
td { Label { "Static Pressure" } }
|
||||
td {
|
||||
div(.class("flex justify-end")) {
|
||||
Number(equipmentInfo.staticPressure)
|
||||
@@ -37,7 +37,7 @@ struct EquipmentInfoView: HTML, Sendable {
|
||||
}
|
||||
}
|
||||
tr {
|
||||
td { span { "Heating CFM" } }
|
||||
td { Label { "Heating CFM" } }
|
||||
td {
|
||||
div(.class("flex justify-end")) {
|
||||
Number(equipmentInfo.heatingCFM)
|
||||
@@ -45,7 +45,7 @@ struct EquipmentInfoView: HTML, Sendable {
|
||||
}
|
||||
}
|
||||
tr {
|
||||
td { span { "Cooling CFM" } }
|
||||
td { Label { "Cooling CFM" } }
|
||||
td {
|
||||
div(.class("flex justify-end")) {
|
||||
Number(equipmentInfo.coolingCFM)
|
||||
|
||||
@@ -21,7 +21,7 @@ struct ProjectDetail: HTML, Sendable {
|
||||
table(.class("table table-zebra text-lg")) {
|
||||
tbody {
|
||||
tr {
|
||||
td { "Name" }
|
||||
td(.class("label font-bold")) { "Name" }
|
||||
td {
|
||||
div(.class("flex justify-end")) {
|
||||
project.name
|
||||
@@ -29,7 +29,7 @@ struct ProjectDetail: HTML, Sendable {
|
||||
}
|
||||
}
|
||||
tr {
|
||||
td { "Street Address" }
|
||||
td(.class("label font-bold")) { "Street Address" }
|
||||
td {
|
||||
div(.class("flex justify-end")) {
|
||||
project.streetAddress
|
||||
@@ -37,7 +37,7 @@ struct ProjectDetail: HTML, Sendable {
|
||||
}
|
||||
}
|
||||
tr {
|
||||
td { "City" }
|
||||
td(.class("label font-bold")) { "City" }
|
||||
td {
|
||||
div(.class("flex justify-end")) {
|
||||
project.city
|
||||
@@ -45,7 +45,7 @@ struct ProjectDetail: HTML, Sendable {
|
||||
}
|
||||
}
|
||||
tr {
|
||||
td { "State" }
|
||||
td(.class("label font-bold")) { "State" }
|
||||
td {
|
||||
div(.class("flex justify-end")) {
|
||||
project.state
|
||||
@@ -53,7 +53,7 @@ struct ProjectDetail: HTML, Sendable {
|
||||
}
|
||||
}
|
||||
tr {
|
||||
td { "Zip" }
|
||||
td(.class("label font-bold")) { "Zip" }
|
||||
td {
|
||||
div(.class("flex justify-end")) {
|
||||
project.zipCode
|
||||
|
||||
@@ -29,36 +29,39 @@ struct RoomsView: HTML, Sendable {
|
||||
) {
|
||||
LabeledContent {
|
||||
div(.class("flex justify-end items-end space-x-4")) {
|
||||
// SVG(.squarePen)
|
||||
span(.class("font-bold")) {
|
||||
Label {
|
||||
"Sensible Heat Ratio"
|
||||
}
|
||||
.attributes(.class("me-8"), when: sensibleHeatRatio == nil)
|
||||
}
|
||||
} content: {
|
||||
if let 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 items-end space-x-4")) {
|
||||
span(.class("font-bold")) { "Heating Total" }
|
||||
Label { "Heating Total" }
|
||||
Badge(number: rooms.heatingTotal, digits: 0)
|
||||
.attributes(.class("badge-error"))
|
||||
}
|
||||
|
||||
div(.class("flex items-end space-x-4")) {
|
||||
span(.class("font-bold")) { "Cooling Total" }
|
||||
Label { "Cooling Total" }
|
||||
Badge(number: rooms.coolingTotal, digits: 0)
|
||||
.attributes(.class("badge-success"))
|
||||
}
|
||||
|
||||
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)
|
||||
.attributes(.class("badge-info"))
|
||||
}
|
||||
@@ -95,7 +98,7 @@ struct RoomsView: HTML, Sendable {
|
||||
}
|
||||
}
|
||||
th {
|
||||
div(.class("flex justify-end")) {
|
||||
div(.class("flex justify-end me-2")) {
|
||||
Tooltip("Add Room") {
|
||||
PlusButton()
|
||||
.attributes(
|
||||
@@ -201,6 +204,12 @@ struct RoomsView: HTML, Sendable {
|
||||
let projectID: Project.ID
|
||||
let sensibleHeatRatio: Double?
|
||||
|
||||
var route: String {
|
||||
SiteRoute.View.router
|
||||
.path(for: .project(.detail(projectID, .rooms(.index))))
|
||||
.appendingPath("update-shr")
|
||||
}
|
||||
|
||||
var body: some HTML {
|
||||
ModalForm(id: Self.id, dismiss: true) {
|
||||
h1(.class("text-xl font-bold mb-6")) {
|
||||
@@ -208,19 +217,20 @@ struct RoomsView: HTML, Sendable {
|
||||
}
|
||||
form(
|
||||
.class("grid grid-cols-1 gap-4"),
|
||||
.hx.patch("/projects/\(projectID)/rooms/update-shr"),
|
||||
.hx.patch(route),
|
||||
.hx.target("body"),
|
||||
.hx.swap(.outerHTML)
|
||||
) {
|
||||
input(.class("hidden"), .name("projectID"), .value("\(projectID)"))
|
||||
LabeledInput(
|
||||
"SHR",
|
||||
.name("sensibleHeatRatio"),
|
||||
.type(.number),
|
||||
.value(sensibleHeatRatio),
|
||||
.placeholder("0.83"),
|
||||
.min("0"),
|
||||
.max("1"),
|
||||
.step("0.01"),
|
||||
.value(sensibleHeatRatio),
|
||||
.autofocus
|
||||
)
|
||||
SubmitButton()
|
||||
|
||||
Reference in New Issue
Block a user