From 0a68177aa839e1215f5879e4ce64283ca115e456 Mon Sep 17 00:00:00 2001 From: Michael Housh Date: Sun, 11 Jan 2026 20:57:06 -0500 Subject: [PATCH] feat: Style updates. --- Public/css/output.css | 56 +++++++++ Sources/DatabaseClient/Projects.swift | 3 +- Sources/ManualDCore/Project.swift | 9 +- Sources/ManualDCore/Routes/ViewRoute.swift | 1 + Sources/Styleguide/Badge.swift | 28 +++++ Sources/Styleguide/Buttons.swift | 2 +- Sources/Styleguide/ElementaryExtensions.swift | 6 + Sources/Styleguide/LabeledContent.swift | 72 +++++++++++ Sources/Styleguide/SVG.swift | 5 + Sources/ViewController/Live.swift | 6 +- .../ComponentLoss/ComponentLossesView.swift | 38 +++--- .../Views/DuctSizing/DuctSizingView.swift | 6 +- .../EquipmentInfo/EquipmentInfoView.swift | 14 +-- .../Views/FrictionRate/FrictionRateView.swift | 44 ++++--- Sources/ViewController/Views/MainPage.swift | 8 +- Sources/ViewController/Views/Navbar.swift | 3 +- .../Views/Project/ProjectDetail.swift | 114 +++++++++++------ .../Views/Project/ProjectView.swift | 19 +++ .../Views/Rooms/RoomsView.swift | 117 ++++++++++++------ 19 files changed, 410 insertions(+), 141 deletions(-) create mode 100644 Sources/Styleguide/Badge.swift create mode 100644 Sources/Styleguide/LabeledContent.swift diff --git a/Public/css/output.css b/Public/css/output.css index faf4842..82a9555 100644 --- a/Public/css/output.css +++ b/Public/css/output.css @@ -5581,6 +5581,9 @@ .ms-4 { margin-inline-start: calc(var(--spacing) * 4); } + .me-4 { + margin-inline-end: calc(var(--spacing) * 4); + } .modal-action { @layer daisyui.l1.l2.l3 { margin-top: calc(0.25rem * 6); @@ -6444,6 +6447,9 @@ .h-\[50px\] { height: 50px; } + .h-auto { + height: auto; + } .h-fit { height: fit-content; } @@ -6599,6 +6605,9 @@ .w-\[80px\] { width: 80px; } + .w-auto { + width: auto; + } .w-fit { width: fit-content; } @@ -6798,12 +6807,18 @@ .grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); } + .grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } .grid-cols-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); } .flex-col { flex-direction: column; } + .flex-row { + flex-direction: row; + } .flex-wrap { flex-wrap: wrap; } @@ -6828,6 +6843,9 @@ .justify-end { justify-content: flex-end; } + .justify-start { + justify-content: flex-start; + } .gap-1 { gap: calc(var(--spacing) * 1); } @@ -7280,6 +7298,12 @@ border-color: currentColor; } } + .border-base-100 { + border-color: var(--color-base-100); + } + .border-base-200 { + border-color: var(--color-base-200); + } .border-gray-200 { border-color: var(--color-gray-200); } @@ -7872,6 +7896,9 @@ } } } + .px-2 { + padding-inline: calc(var(--spacing) * 2); + } .px-3 { padding-inline: calc(var(--spacing) * 3); } @@ -9443,6 +9470,13 @@ border-color: var(--color-red-500); } } + .hover\:rounded-lg { + &:hover { + @media (hover: hover) { + border-radius: var(--radius-lg); + } + } + } .hover\:bg-gray-900 { &:hover { @media (hover: hover) { @@ -9471,6 +9505,13 @@ } } } + .hover\:bg-success { + &:hover { + @media (hover: hover) { + background-color: var(--color-success); + } + } + } .hover\:text-black { &:hover { @media (hover: hover) { @@ -9485,6 +9526,16 @@ } } } + .hover\:btn-success { + &:hover { + @media (hover: hover) { + @layer daisyui.l1.l2.l3 { + --btn-color: var(--color-success); + --btn-fg: var(--color-success-content); + } + } + } + } .focus\:outline { &:focus { outline-style: var(--tw-outline-style); @@ -9806,6 +9857,11 @@ display: flex; } } + .is-drawer-open\:hidden { + &:where(.drawer-toggle:checked ~ .drawer-side, .drawer-toggle:checked ~ .drawer-side *) { + display: none; + } + } .is-drawer-open\:w-64 { &:where(.drawer-toggle:checked ~ .drawer-side, .drawer-toggle:checked ~ .drawer-side *) { width: calc(var(--spacing) * 64); diff --git a/Sources/DatabaseClient/Projects.swift b/Sources/DatabaseClient/Projects.swift index d33ac61..e604a86 100644 --- a/Sources/DatabaseClient/Projects.swift +++ b/Sources/DatabaseClient/Projects.swift @@ -66,9 +66,10 @@ extension DatabaseClient.Projects: TestDependencyKey { .first() return .init( + equipmentInfo: equipmentInfo != nil, rooms: roomsCount > 0, equivalentLength: equivalentLengthsCompleted, - frictionRate: equipmentInfo != nil && componentLosses > 0 + frictionRate: componentLosses > 0 ) }, getSensibleHeatRatio: { id in diff --git a/Sources/ManualDCore/Project.swift b/Sources/ManualDCore/Project.swift index 539c0a9..eeb49f9 100644 --- a/Sources/ManualDCore/Project.swift +++ b/Sources/ManualDCore/Project.swift @@ -66,11 +66,18 @@ extension Project { public struct CompletedSteps: Codable, Equatable, Sendable { + public let equipmentInfo: Bool public let rooms: Bool public let equivalentLength: Bool public let frictionRate: Bool - public init(rooms: Bool, equivalentLength: Bool, frictionRate: Bool) { + public init( + equipmentInfo: Bool, + rooms: Bool, + equivalentLength: Bool, + frictionRate: Bool + ) { + self.equipmentInfo = equipmentInfo self.rooms = rooms self.equivalentLength = equivalentLength self.frictionRate = frictionRate diff --git a/Sources/ManualDCore/Routes/ViewRoute.swift b/Sources/ManualDCore/Routes/ViewRoute.swift index dbbd3ec..c5d489c 100644 --- a/Sources/ManualDCore/Routes/ViewRoute.swift +++ b/Sources/ManualDCore/Routes/ViewRoute.swift @@ -189,6 +189,7 @@ extension SiteRoute.View.ProjectRoute { public enum Tab: String, CaseIterable, Equatable, Sendable { case project + case equipment case rooms case equivalentLength case frictionRate diff --git a/Sources/Styleguide/Badge.swift b/Sources/Styleguide/Badge.swift new file mode 100644 index 0000000..bacfd99 --- /dev/null +++ b/Sources/Styleguide/Badge.swift @@ -0,0 +1,28 @@ +import Elementary + +public struct Badge: HTML, Sendable where Inner: Sendable { + + let inner: Inner + + public init( + @HTMLBuilder inner: () -> Inner + ) { + self.inner = inner() + } + + public var body: some HTML { + div(.class("badge badge-lg badge-outline font-bold")) { + inner + } + } +} + +extension Badge where Inner == Number { + public init(number: Int) { + self.inner = Number(number) + } + + public init(number: Double, digits: Int = 2) { + self.inner = Number(number, digits: digits) + } +} diff --git a/Sources/Styleguide/Buttons.swift b/Sources/Styleguide/Buttons.swift index ca6b558..36f6072 100644 --- a/Sources/Styleguide/Buttons.swift +++ b/Sources/Styleguide/Buttons.swift @@ -65,7 +65,7 @@ public struct EditButton: HTML, Sendable { } public var body: some HTML { - button(.class("btn btn-success btn-circle"), .type(type)) { + button(.class("btn hover:btn-success"), .type(type)) { div(.class("flex")) { if let title { span(.class("pe-2")) { title } diff --git a/Sources/Styleguide/ElementaryExtensions.swift b/Sources/Styleguide/ElementaryExtensions.swift index 805e410..c3fc01e 100644 --- a/Sources/Styleguide/ElementaryExtensions.swift +++ b/Sources/Styleguide/ElementaryExtensions.swift @@ -40,3 +40,9 @@ extension HTMLAttribute where Tag == HTMLTag.button { .on(.click, "\(id).showModal()") } } + +extension HTML where Tag: HTMLTrait.Attributes.Global { + public func badge() -> _AttributedElement { + attributes(.class("badge badge-lg badge-outline font-bold")) + } +} diff --git a/Sources/Styleguide/LabeledContent.swift b/Sources/Styleguide/LabeledContent.swift new file mode 100644 index 0000000..ce1bb50 --- /dev/null +++ b/Sources/Styleguide/LabeledContent.swift @@ -0,0 +1,72 @@ +import Elementary + +public struct LabeledContent: HTML { + let label: @Sendable () -> Label + let content: @Sendable () -> Content + let position: LabelPosition + + public init( + position: LabelPosition = .default, + @HTMLBuilder label: @escaping @Sendable () -> Label, + @HTMLBuilder content: @escaping @Sendable () -> Content + ) { + self.position = position + self.label = label + self.content = content + } + + public var body: some HTML { + div { + switch position { + case .leading: + label() + content() + case .trailing: + content() + label() + case .top: + label() + content() + case .bottom: + content() + label() + } + } + .attributes(.class("flex space-x-4"), when: position.isHorizontal) + .attributes(.class("space-y-4"), when: position.isVertical) + } +} + +// TODO: Merge / use TooltipPosition +public enum LabelPosition: String, CaseIterable, Equatable, Sendable { + case leading + case trailing + case top + case bottom + + var isHorizontal: Bool { + self == .leading || self == .trailing + } + + var isVertical: Bool { + self == .top || self == .bottom + } + + public static let `default` = Self.leading +} + +extension LabeledContent: Sendable where Label: Sendable, Content: Sendable {} + +extension LabeledContent where Label == Styleguide.Label { + + public init( + _ label: String, position: LabelPosition = .default, + @HTMLBuilder content: @escaping @Sendable () -> Content + ) { + self.init( + position: position, + label: { Label(label) }, + content: content + ) + } +} diff --git a/Sources/Styleguide/SVG.swift b/Sources/Styleguide/SVG.swift index 54afd7f..045c238 100644 --- a/Sources/Styleguide/SVG.swift +++ b/Sources/Styleguide/SVG.swift @@ -23,6 +23,7 @@ extension SVG { case close case doorClosed case email + case fan case key case mapPin case rulerDimensionLine @@ -78,6 +79,10 @@ extension SVG { """ + case .fan: + return """ + + """ case .key: return """ diff --git a/Sources/ViewController/Live.swift b/Sources/ViewController/Live.swift index 89aae50..f375825 100644 --- a/Sources/ViewController/Live.swift +++ b/Sources/ViewController/Live.swift @@ -188,10 +188,8 @@ extension SiteRoute.View.ProjectRoute.EquipmentInfoRoute { switch self { case .index: - return await ResultView { - try await database.equipment.fetch(projectID) - } onSuccess: { equipment in - EquipmentInfoView(equipmentInfo: equipment, projectID: projectID) + return request.view { + ProjectView(projectID: projectID, activeTab: .equipment) } case .form(let dismiss): return await ResultView { diff --git a/Sources/ViewController/Views/ComponentLoss/ComponentLossesView.swift b/Sources/ViewController/Views/ComponentLoss/ComponentLossesView.swift index 3ec8627..e244c9d 100644 --- a/Sources/ViewController/Views/ComponentLoss/ComponentLossesView.swift +++ b/Sources/ViewController/Views/ComponentLoss/ComponentLossesView.swift @@ -15,37 +15,31 @@ struct ComponentPressureLossesView: HTML, Sendable { } var body: some HTML { - div( - .class( - """ - border border-gray-200 rounded-lg shadow-lg space-y-4 p-4 - """ - ) - ) { + div(.class("space-y-4")) { Row { - div(.class("flex space-x-4 items-center")) { - h1(.class("text-2xl font-bold")) { "Component Pressure Losses" } - div(.class("flex text-primary space-x-2 items-baseline")) { - Number(total) - .attributes(.class("text-xl font-bold badge badge-outline badge-primary")) - span(.class("text-sm italic")) { "Total" } - } - } - Tooltip("Add Component Loss") { - PlusButton() - .attributes( - .class("btn-ghost text-2xl"), - .showModal(id: ComponentLossForm.id()) - ) + h1(.class("text-2xl font-bold")) { "Component Pressure Losses" } + LabeledContent("Total") { + Badge(number: total) } } + .attributes(.class("px-4")) table(.class("table table-zebra")) { thead { tr(.class("text-xl font-bold")) { th { "Name" } th { "Value" } - th {} + th { + div(.class("flex justify-end mx-auto")) { + Tooltip("Add Component Loss") { + PlusButton() + .attributes( + .class("btn-ghost text-2xl"), + .showModal(id: ComponentLossForm.id()) + ) + } + } + } } } tbody { diff --git a/Sources/ViewController/Views/DuctSizing/DuctSizingView.swift b/Sources/ViewController/Views/DuctSizing/DuctSizingView.swift index b1ff561..078094f 100644 --- a/Sources/ViewController/Views/DuctSizing/DuctSizingView.swift +++ b/Sources/ViewController/Views/DuctSizing/DuctSizingView.swift @@ -41,7 +41,7 @@ struct DuctSizingView: HTML, Sendable { th(.class("hidden 2xl:table-cell")) { "Htg CFM" } th(.class("hidden 2xl:table-cell")) { "Clg CFM" } th { "Dsn CFM" } - th(.class("hidden xl:table-cell")) { "Round Size" } + th(.class("hidden 2xl:table-cell")) { "Round Size" } th { "Velocity" } th { "Final Size" } th { "Flex Size" } @@ -84,7 +84,7 @@ struct DuctSizingView: HTML, Sendable { .attributes( .class("badge badge-outline badge-\(room.designCFM.color) text-xl font-bold")) } - td(.class("hidden xl:table-cell")) { Number(room.roundSize, digits: 0) } + td(.class("hidden 2xl:table-cell")) { Number(room.roundSize, digits: 0) } td { Number(room.velocity) } td { Number(room.finalSize) @@ -131,7 +131,7 @@ struct DuctSizingView: HTML, Sendable { EditButton() .attributes( - .class("join-item btn-ghost text-success hover:text-white"), + .class("join-item btn-ghost"), .showModal(id: RectangularSizeForm.id(room)) ) } diff --git a/Sources/ViewController/Views/EquipmentInfo/EquipmentInfoView.swift b/Sources/ViewController/Views/EquipmentInfo/EquipmentInfoView.swift index 68405a5..12fbf4f 100644 --- a/Sources/ViewController/Views/EquipmentInfo/EquipmentInfoView.swift +++ b/Sources/ViewController/Views/EquipmentInfo/EquipmentInfoView.swift @@ -8,7 +8,7 @@ struct EquipmentInfoView: HTML, Sendable { var body: some HTML { div( - .class("space-y-4 border border-gray-200 rounded-lg shadow-lg p-4"), + .class("space-y-4 p-4"), .id("equipmentInfo") ) { @@ -27,23 +27,17 @@ struct EquipmentInfoView: HTML, Sendable { if let equipmentInfo { table(.class("table table-zebra")) { - thead { - tr { - th { Label("Name") } - th { Label("Value") } - } - } tbody(.class("text-lg")) { tr { - td { "Static Pressure" } + td { Label("Static Pressure") } td { Number(equipmentInfo.staticPressure) } } tr { - td { "Heating CFM" } + td { Label("Heating CFM") } td { Number(equipmentInfo.heatingCFM) } } tr { - td { "Cooling CFM" } + td { Label("Cooling CFM") } td { Number(equipmentInfo.coolingCFM) } } } diff --git a/Sources/ViewController/Views/FrictionRate/FrictionRateView.swift b/Sources/ViewController/Views/FrictionRate/FrictionRateView.swift index 5c3a115..34c007d 100644 --- a/Sources/ViewController/Views/FrictionRate/FrictionRateView.swift +++ b/Sources/ViewController/Views/FrictionRate/FrictionRateView.swift @@ -43,24 +43,26 @@ struct FrictionRateView: HTML, Sendable { } var body: some HTML { - div(.class("p-4 space-y-6")) { - Row { - h1(.class("text-4xl font-bold pb-6")) { "Friction Rate" } - div(.class("space-y-4")) { - div(.class("flex space-x-4 justify-end")) { - if let availableStaticPressure { - Label("Available Static Pressure") - Number(availableStaticPressure, digits: 2) - .attributes(.class("badge badge-lg badge-outline font-bold ms-4")) + div(.class("space-y-6")) { + div(.class("grid grid-cols-2 px-4")) { + + h1(.class("text-4xl font-bold items-end my-auto")) { "Friction Rate" } + + div(.class("space-y-4 justify-end")) { + + if let frictionRateDesignValue { + LabeledContent("Friction Rate Design Value") { + Badge(number: frictionRateDesignValue, digits: 2) + .attributes(.class("\(badgeColor)")) } + .attributes(.class("justify-end")) } - div(.class("flex space-x-4 justify-end")) { - if let frictionRateDesignValue { - Label("Friction Rate Design Value") - Number(frictionRateDesignValue, digits: 2) - .attributes(.class("badge badge-lg badge-outline \(badgeColor) font-bold")) + if let availableStaticPressure { + LabeledContent("Available Static Pressure") { + Badge(number: availableStaticPressure, digits: 2) } + .attributes(.class("justify-end")) } } } @@ -94,12 +96,14 @@ struct FrictionRateView: HTML, Sendable { .attributes(.class("hidden"), when: !showHighErrors) } - div(.class("grid grid-cols-1 lg:grid-cols-2 gap-4")) { - EquipmentInfoView(equipmentInfo: equipmentInfo, projectID: projectID) - ComponentPressureLossesView( - componentPressureLosses: componentLosses, projectID: projectID - ) - } + div(.class("divider")) {} + + // div(.class("grid grid-cols-1 lg:grid-cols-2 gap-4")) { + // EquipmentInfoView(equipmentInfo: equipmentInfo, projectID: projectID) + ComponentPressureLossesView( + componentPressureLosses: componentLosses, projectID: projectID + ) + // } } } } diff --git a/Sources/ViewController/Views/MainPage.swift b/Sources/ViewController/Views/MainPage.swift index 98f1259..fe90a94 100644 --- a/Sources/ViewController/Views/MainPage.swift +++ b/Sources/ViewController/Views/MainPage.swift @@ -5,7 +5,7 @@ import Styleguide public struct MainPage: SendableHTMLDocument where Inner: Sendable { - public var title: String { "Manual-D" } + public var title: String { "Duct Calc" } public var lang: String { "en" } let inner: Inner @@ -22,7 +22,11 @@ public struct MainPage: SendableHTMLDocument where Inner: Sendable script(.src("https://unpkg.com/htmx.org@2.0.8")) {} script(.src("/js/main.js")) {} link(.rel(.stylesheet), .href("/css/output.css")) - link(.rel(.icon), .href("/images/favicon.ico"), .custom(name: "type", value: "image/x-icon")) + link( + .rel(.icon), + .href("/images/favicon.ico"), + .init(name: "type", value: "image/x-icon") + ) link( .rel(.icon), .href("/images/favicon-32x32.png"), diff --git a/Sources/ViewController/Views/Navbar.swift b/Sources/ViewController/Views/Navbar.swift index 748e9fb..b0538aa 100644 --- a/Sources/ViewController/Views/Navbar.swift +++ b/Sources/ViewController/Views/Navbar.swift @@ -25,6 +25,7 @@ struct Navbar: HTML, Sendable { } .navButton() } + } Tooltip("Home", position: .right) { @@ -35,7 +36,7 @@ struct Navbar: HTML, Sendable { img( .src("/images/mand_logo_sm.webp"), ) - span { "Manual-D" } + span { "Duct Calc" } } .navButton() } diff --git a/Sources/ViewController/Views/Project/ProjectDetail.swift b/Sources/ViewController/Views/Project/ProjectDetail.swift index ccbbeca..c0a5ce1 100644 --- a/Sources/ViewController/Views/Project/ProjectDetail.swift +++ b/Sources/ViewController/Views/Project/ProjectDetail.swift @@ -7,51 +7,95 @@ struct ProjectDetail: HTML, Sendable { let project: Project var body: some HTML { - div( - .class( - """ - border border-gray-200 rounded-lg shadow-lg space-y-4 p-4 m-4 - """ - ) - ) { + div { Row { h1(.class("text-2xl font-bold")) { "Project" } EditButton() .attributes( + .class("btn-ghost"), .on(.click, "projectForm.showModal()") ) } - Row { - Label("Name") - span { project.name } + div(.class("overflow-x-auto")) { + table(.class("table table-zebra text-lg")) { + tbody { + tr { + td { Label("Name") } + td { project.name } + } + tr { + td { Label("Street Address") } + td { project.streetAddress } + } + tr { + td { Label("City") } + td { project.city } + } + tr { + td { Label("State") } + td { project.state } + } + tr { + td { Label("Zip") } + td { project.zipCode } + } + } + } } - .attributes(.class("border-b border-gray-200")) - Row { - Label("Address") - span { project.streetAddress } - } - .attributes(.class("border-b border-gray-200")) - - Row { - Label("City") - span { project.city } - } - .attributes(.class("border-b border-gray-200")) - - Row { - Label("State") - span { project.state } - } - .attributes(.class("border-b border-gray-200")) - - Row { - Label("Zip") - span { project.zipCode } - } + ProjectForm(dismiss: true, project: project) } - - ProjectForm(dismiss: true, project: project) } + + // var body: some HTML { + // div( + // .class( + // """ + // space-y-4 p-4 m-4 + // """ + // ) + // ) { + // Row { + // h1(.class("text-2xl font-bold")) { "Project" } + // EditButton() + // .attributes( + // .class("btn-ghost"), + // .on(.click, "projectForm.showModal()") + // ) + // } + // + // Row { + // Label("Name") + // span { project.name } + // } + // .attributes(.class("border-b border-gray-200")) + // + // Row { + // Label("Address") + // span { project.streetAddress } + // } + // .attributes(.class("border-b border-gray-200")) + // + // Row { + // Label("City") + // span { project.city } + // } + // .attributes(.class("border-b border-gray-200")) + // + // Row { + // Label("State") + // span { project.state } + // } + // .attributes(.class("border-b border-gray-200")) + // + // Row { + // Label("Zip") + // span { project.zipCode } + // } + // } + // + // ProjectForm(dismiss: true, project: project) + // } } + diff --git a/Sources/ViewController/Views/Project/ProjectView.swift b/Sources/ViewController/Views/Project/ProjectView.swift index 5a2b220..514650d 100644 --- a/Sources/ViewController/Views/Project/ProjectView.swift +++ b/Sources/ViewController/Views/Project/ProjectView.swift @@ -48,6 +48,14 @@ struct ProjectView: HTML, Sendable { } onSuccess: { project in ProjectDetail(project: project) } + case .equipment: + await resultView(projectID) { + try await database.equipment.fetch(projectID) + } onSuccess: { equipment in + EquipmentInfoView(equipmentInfo: equipment, projectID: projectID) + } + // FIX: + // div { "Fix Me" } case .rooms: await resultView(projectID) { try await ( @@ -179,6 +187,16 @@ extension ProjectView { .attributes(.data("active", value: "true"), when: active == .rooms) } + li(.class("flex w-full")) { + row( + title: "Equipment", + icon: .fan, + route: .project(.detail(projectID, .equipment(.index))), + isComplete: completedSteps.equipmentInfo + ) + .attributes(.data("active", value: "true"), when: active == .equipment) + } + li(.class("w-full")) { // Tooltip("Equivalent Lengths", position: .right) { row( @@ -235,6 +253,7 @@ extension ProjectView { """ ), .hx.get(href), + .hx.pushURL(true), .hx.target("body"), .hx.swap(.outerHTML) ) { diff --git a/Sources/ViewController/Views/Rooms/RoomsView.swift b/Sources/ViewController/Views/Rooms/RoomsView.swift index f807c35..cf6d8a8 100644 --- a/Sources/ViewController/Views/Rooms/RoomsView.swift +++ b/Sources/ViewController/Views/Rooms/RoomsView.swift @@ -12,44 +12,65 @@ struct RoomsView: HTML, Sendable { let sensibleHeatRatio: Double? var body: some HTML { - div { - h1(.class("text-2xl font-bold pb-6")) { "Room Loads" } + div(.class("flex w-full flex-col")) { + Row { + h1( + .class("flex flex-row text-2xl font-bold pb-6 h-full items-center") + ) { "Room Loads" } - div(.class("border rounded-lg mb-6")) { - Row { - div { - div(.class("space-x-6 my-2 items-center")) { - Label("Sensible Heat Ratio") - .attributes(.class("my-auto")) - if let sensibleHeatRatio { - Number(sensibleHeatRatio) - .attributes(.class("badge badge-outline")) + div(.class("flex justify-end")) { + Tooltip("Project wide sensible heat ratio", position: .left) { + button( + .class( + """ + grid grid-cols-1 gap-2 p-4 justify-end + hover:bg-neutral hover:text-white hover:rounded-lg + """ + ), + .showModal(id: SHRForm.id) + ) { + LabeledContent("Sensible Heat Ratio") { + if let sensibleHeatRatio { + Badge(number: sensibleHeatRatio) + } + } + div(.class("flex justify-end")) { + SVG(.squarePen) } } - p(.class("text-sm italic")) { - "Project wide sensible heat ratio" - } - } - - Tooltip("Edit SHR") { - EditButton() - .attributes(.class("btn-ghost"), .showModal(id: SHRForm.id)) } } - .attributes(.class("m-4")) - - SHRForm(projectID: projectID, sensibleHeatRatio: sensibleHeatRatio) } - div(.class("overflow-x-auto rounded-box border")) { + div(.class("divider")) {} + + SHRForm(projectID: projectID, sensibleHeatRatio: sensibleHeatRatio) + + div(.class("overflow-x-auto")) { table(.class("table table-zebra"), .id("roomsTable")) { thead { tr { th { Label("Name") } - th { Label("Heating Load") } - th { Label("Cooling Total") } - th { Label("Cooling Sensible") } - th { Label("Register Count") } + th { + div(.class("flex justify-center")) { + Label("Heating Load") + } + } + th { + div(.class("flex justify-center")) { + Label("Cooling Total") + } + } + th { + div(.class("flex justify-center")) { + Label("Cooling Sensible") + } + } + th { + div(.class("flex justify-center")) { + Label("Register Count") + } + } th { div(.class("flex justify-end")) { Tooltip("Add Room") { @@ -58,8 +79,8 @@ struct RoomsView: HTML, Sendable { .class("btn-ghost mx-auto"), .showModal(id: RoomForm.id()) ) + .attributes(.class("tooltip-left")) } - .attributes(.class("tooltip-left")) } } } @@ -72,16 +93,22 @@ struct RoomsView: HTML, Sendable { tr(.class("font-bold text-xl")) { td { Label("Total") } td { - Number(rooms.heatingTotal, digits: 0) - .attributes(.class("badge badge-outline badge-error badge-xl")) + div(.class("flex justify-center")) { + Badge(number: rooms.heatingTotal) + .attributes(.class("badge-error badge-xl")) + } } td { - Number(rooms.coolingTotal, digits: 0) - .attributes(.class("badge badge-outline badge-success badge-xl")) + div(.class("flex justify-center")) { + Badge(number: rooms.coolingTotal, digits: 0) + .attributes(.class("badge-success badge-xl")) + } } td { - Number(rooms.coolingSensible(shr: sensibleHeatRatio), digits: 0) - .attributes(.class("badge badge-outline badge-info badge-xl")) + div(.class("flex justify-center")) { + Badge(number: rooms.coolingSensible(shr: sensibleHeatRatio), digits: 0) + .attributes(.class("badge-info badge-xl")) + } } td {} td {} @@ -113,19 +140,27 @@ struct RoomsView: HTML, Sendable { tr(.id("roomRow_\(room.name)")) { td { room.name } td { - Number(room.heatingLoad, digits: 0) - .attributes(.class("text-error")) + div(.class("flex justify-center")) { + Number(room.heatingLoad, digits: 0) + .attributes(.class("text-error")) + } } td { - Number(room.coolingTotal, digits: 0) - .attributes(.class("text-success")) + div(.class("flex justify-center")) { + Number(room.coolingTotal, digits: 0) + .attributes(.class("text-success")) + } } td { - Number(coolingSensible, digits: 0) - .attributes(.class("text-info")) + div(.class("flex justify-center")) { + Number(coolingSensible, digits: 0) + .attributes(.class("text-info")) + } } td { - Number(room.registerCount) + div(.class("flex justify-center")) { + Number(room.registerCount) + } } td { div(.class("flex justify-end")) {