diff --git a/Public/css/output.css b/Public/css/output.css index fbc6f47..d958a0f 100644 --- a/Public/css/output.css +++ b/Public/css/output.css @@ -5225,9 +5225,6 @@ .m-1 { margin: calc(var(--spacing) * 1); } - .m-4 { - margin: calc(var(--spacing) * 4); - } .m-6 { margin: calc(var(--spacing) * 6); } @@ -5371,12 +5368,6 @@ .-my-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 { margin-block: calc(var(--spacing) * 1.5); } @@ -5589,11 +5580,14 @@ border-width: var(--border, 1px) 0 var(--border, 1px) var(--border, 1px); } } + .me-2 { + margin-inline-end: calc(var(--spacing) * 2); + } .me-4 { margin-inline-end: calc(var(--spacing) * 4); } - .me-6 { - margin-inline-end: calc(var(--spacing) * 6); + .me-8 { + margin-inline-end: calc(var(--spacing) * 8); } .modal-action { @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 { margin-top: calc(var(--spacing) * 4); } @@ -6592,6 +6580,9 @@ .w-full { width: 100%; } + .max-w-\[300px\] { + max-width: 300px; + } .flex-1 { flex: 1; } @@ -6797,9 +6788,6 @@ .flex-col { flex-direction: column; } - .flex-row { - flex-direction: row; - } .flex-wrap { flex-wrap: wrap; } @@ -7269,6 +7257,12 @@ border-color: currentColor; } } + .border-base-300 { + border-color: var(--color-base-300); + } + .border-error { + border-color: var(--color-error); + } .border-gray-200 { border-color: var(--color-gray-200); } @@ -7852,21 +7846,12 @@ .px-4 { padding-inline: calc(var(--spacing) * 4); } - .py-1 { - padding-block: calc(var(--spacing) * 1); - } .py-1\.5 { padding-block: calc(var(--spacing) * 1.5); } .py-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 { padding-inline-start: calc(var(--spacing) * 2); } @@ -7884,6 +7869,9 @@ .pe-2 { padding-inline-end: calc(var(--spacing) * 2); } + .pt-4 { + padding-top: calc(var(--spacing) * 4); + } .pt-6 { padding-top: calc(var(--spacing) * 6); } @@ -7941,14 +7929,6 @@ font-size: var(--text-3xl); 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 { font-size: var(--text-lg); line-height: var(--tw-leading, var(--text-lg--line-height)); @@ -8518,6 +8498,9 @@ .text-base-100 { color: var(--color-base-100); } + .text-base-300 { + color: var(--color-base-300); + } .text-base-content { color: var(--color-base-content); } diff --git a/Sources/Styleguide/Label.swift b/Sources/Styleguide/Label.swift index 5492c46..e265c46 100644 --- a/Sources/Styleguide/Label.swift +++ b/Sources/Styleguide/Label.swift @@ -13,7 +13,7 @@ public struct Label: HTML, Sendable { } public var body: some HTML { - span(.class("text-lg text-secondary font-bold")) { + span(.class("text-lg label font-bold")) { title } } diff --git a/Sources/ViewController/Live.swift b/Sources/ViewController/Live.swift index 8430807..b9b1c44 100644 --- a/Sources/ViewController/Live.swift +++ b/Sources/ViewController/Live.swift @@ -211,10 +211,8 @@ extension SiteRoute.View.ProjectRoute.EquipmentInfoRoute { return await equipmentView(on: request, projectID: projectID) case .submit(let form): - return await ResultView { - try await database.equipment.create(form) - } onSuccess: { equipment in - EquipmentInfoView(equipmentInfo: equipment, projectID: projectID) + return await equipmentView(on: request, projectID: projectID) { + _ = try await database.equipment.create(form) } case .update(let id, let updates): @@ -576,15 +574,17 @@ extension SiteRoute.View.ProjectRoute.DuctSizingRoute { ) async -> AnySendableHTML { @Dependency(\.database) var database - return await ResultView { - try await catching() - return ( - try await database.projects.getCompletedSteps(projectID), - try await database.calculateDuctSizes(projectID: projectID) - ) - } onSuccess: { (steps, rooms) in - ProjectView(projectID: projectID, activeTab: .ductSizing, completedSteps: steps) { - DuctSizingView(rooms: rooms) + return await request.view { + await ResultView { + try await catching() + return ( + try await database.projects.getCompletedSteps(projectID), + try await database.calculateDuctSizes(projectID: projectID) + ) + } onSuccess: { (steps, rooms) in + ProjectView(projectID: projectID, activeTab: .ductSizing, completedSteps: steps) { + DuctSizingView(rooms: rooms) + } } } } diff --git a/Sources/ViewController/Views/ComponentLoss/ComponentLossForm.swift b/Sources/ViewController/Views/ComponentLoss/ComponentLossForm.swift index fc229f6..0e785cd 100644 --- a/Sources/ViewController/Views/ComponentLoss/ComponentLossForm.swift +++ b/Sources/ViewController/Views/ComponentLoss/ComponentLossForm.swift @@ -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")) diff --git a/Sources/ViewController/Views/ComponentLoss/ComponentLossesView.swift b/Sources/ViewController/Views/ComponentLoss/ComponentLossesView.swift index e244c9d..b97fcb8 100644 --- a/Sources/ViewController/Views/ComponentLoss/ComponentLossesView.swift +++ b/Sources/ViewController/Views/ComponentLoss/ComponentLossesView.swift @@ -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) diff --git a/Sources/ViewController/Views/DuctSizing/DuctSizingView.swift b/Sources/ViewController/Views/DuctSizing/DuctSizingView.swift index 21ef93f..4e5e5b9 100644 --- a/Sources/ViewController/Views/DuctSizing/DuctSizingView.swift +++ b/Sources/ViewController/Views/DuctSizing/DuctSizingView.swift @@ -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")) { diff --git a/Sources/ViewController/Views/DuctSizing/RectangularSizeForm.swift b/Sources/ViewController/Views/DuctSizing/RectangularSizeForm.swift index 4cae986..f9b6d4b 100644 --- a/Sources/ViewController/Views/DuctSizing/RectangularSizeForm.swift +++ b/Sources/ViewController/Views/DuctSizing/RectangularSizeForm.swift @@ -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")) diff --git a/Sources/ViewController/Views/EffectiveLength/EffectiveLengthForm.swift b/Sources/ViewController/Views/EffectiveLength/EffectiveLengthForm.swift index 7e310ce..9a7e509 100644 --- a/Sources/ViewController/Views/EffectiveLength/EffectiveLengthForm.swift +++ b/Sources/ViewController/Views/EffectiveLength/EffectiveLengthForm.swift @@ -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 { 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 { - 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 { + 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) + } } } } diff --git a/Sources/ViewController/Views/EffectiveLength/EffectiveLengthsView.swift b/Sources/ViewController/Views/EffectiveLength/EffectiveLengthsView.swift index e8ef378..c358a56 100644 --- a/Sources/ViewController/Views/EffectiveLength/EffectiveLengthsView.swift +++ b/Sources/ViewController/Views/EffectiveLength/EffectiveLengthsView.swift @@ -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 { diff --git a/Sources/ViewController/Views/EquipmentInfo/EquipmentInfoView.swift b/Sources/ViewController/Views/EquipmentInfo/EquipmentInfoView.swift index 8422c5c..7ffa955 100644 --- a/Sources/ViewController/Views/EquipmentInfo/EquipmentInfoView.swift +++ b/Sources/ViewController/Views/EquipmentInfo/EquipmentInfoView.swift @@ -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) diff --git a/Sources/ViewController/Views/Project/ProjectDetail.swift b/Sources/ViewController/Views/Project/ProjectDetail.swift index 99f9d14..82acaa4 100644 --- a/Sources/ViewController/Views/Project/ProjectDetail.swift +++ b/Sources/ViewController/Views/Project/ProjectDetail.swift @@ -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 diff --git a/Sources/ViewController/Views/Rooms/RoomsView.swift b/Sources/ViewController/Views/Rooms/RoomsView.swift index b7cb56e..89ff533 100644 --- a/Sources/ViewController/Views/Rooms/RoomsView.swift +++ b/Sources/ViewController/Views/Rooms/RoomsView.swift @@ -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()