diff --git a/Public/css/output.css b/Public/css/output.css index 1d6c50b..1cf99c7 100644 --- a/Public/css/output.css +++ b/Public/css/output.css @@ -9,8 +9,6 @@ monospace; --color-red-500: oklch(63.7% 0.237 25.331); --color-red-600: oklch(57.7% 0.245 27.325); - --color-blue-500: oklch(62.3% 0.214 259.815); - --color-blue-600: oklch(54.6% 0.245 262.881); --color-indigo-600: oklch(51.1% 0.262 276.966); --color-slate-300: oklch(86.9% 0.022 252.894); --color-slate-900: oklch(20.8% 0.042 265.755); @@ -21,6 +19,8 @@ --color-black: #000; --color-white: #fff; --spacing: 0.25rem; + --breakpoint-lg: 64rem; + --container-lg: 32rem; --container-xl: 36rem; --text-sm: 0.875rem; --text-sm--line-height: calc(1.25 / 0.875); @@ -5222,6 +5222,9 @@ .m-4 { margin: calc(var(--spacing) * 4); } + .m-6 { + margin: calc(var(--spacing) * 6); + } .filter { @layer daisyui.l1.l2.l3 { display: flex; @@ -5353,6 +5356,9 @@ } } } + .my-2 { + margin-block: calc(var(--spacing) * 2); + } .label { @layer daisyui.l1.l2.l3 { display: inline-flex; @@ -6525,9 +6531,6 @@ width: calc(var(--size-selector, 0.25rem) * 4); } } - .w-\[40px\] { - width: 40px; - } .w-full { width: 100%; } @@ -7776,9 +7779,6 @@ .pe-2 { padding-inline-end: calc(var(--spacing) * 2); } - .pe-4 { - padding-inline-end: calc(var(--spacing) * 4); - } .pb-4 { padding-bottom: calc(var(--spacing) * 4); } @@ -8401,6 +8401,9 @@ .text-gray-400 { color: var(--color-gray-400); } + .text-info { + color: var(--color-info); + } .text-slate-900 { color: var(--color-slate-900); } diff --git a/Sources/Styleguide/Buttons.swift b/Sources/Styleguide/Buttons.swift index e9db3ac..232c9ec 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 dark:text-white"), .type(type)) { + button(.class("btn btn-success btn-circle dark:text-white"), .type(type)) { div(.class("flex")) { if let title { span(.class("pe-2")) { title } @@ -83,7 +83,7 @@ public struct PlusButton: HTML, Sendable { public var body: some HTML { button( .type(.button), - .class("btn btn-primary") + .class("btn btn-primary btn-circle text-xl") ) { SVG(.circlePlus) } } } @@ -94,7 +94,7 @@ public struct TrashButton: HTML, Sendable { public var body: some HTML { button( .type(.button), - .class("btn btn-error dark:text-white") + .class("btn btn-error btn-circle dark:text-white") ) { SVG(.trash) } diff --git a/Sources/Styleguide/SVG.swift b/Sources/Styleguide/SVG.swift index ffe3e0e..6da5d16 100644 --- a/Sources/Styleguide/SVG.swift +++ b/Sources/Styleguide/SVG.swift @@ -15,6 +15,7 @@ public struct SVG: HTML, Sendable { extension SVG { public enum Key: Sendable { + case chevronRight case circlePlus case close case email @@ -25,6 +26,10 @@ extension SVG { var svg: String { switch self { + case .chevronRight: + return """ + + """ case .circlePlus: return """ diff --git a/Sources/ViewController/Live.swift b/Sources/ViewController/Live.swift index b4c124b..a80bfb5 100644 --- a/Sources/ViewController/Live.swift +++ b/Sources/ViewController/Live.swift @@ -179,13 +179,14 @@ extension SiteRoute.View.ProjectRoute.RoomRoute { case .submit(let form): request.logger.debug("New room form submitted.") + // FIX: Just return a room row?? let _ = try await database.rooms.create(form) return request.view { ProjectView(projectID: projectID, activeTab: .rooms) } case .update(let form): - _ = try await database.rooms.update(form) + let _ = try await database.rooms.update(form) return ProjectView(projectID: projectID, activeTab: .rooms) case .updateSensibleHeatRatio(let form): diff --git a/Sources/ViewController/Views/Project/ProjectForm.swift b/Sources/ViewController/Views/Project/ProjectForm.swift index 555ca24..a192585 100644 --- a/Sources/ViewController/Views/Project/ProjectForm.swift +++ b/Sources/ViewController/Views/Project/ProjectForm.swift @@ -5,6 +5,8 @@ import Styleguide struct ProjectForm: HTML, Sendable { + static let id = "projectForm" + let project: Project? let dismiss: Bool @@ -17,7 +19,7 @@ struct ProjectForm: HTML, Sendable { } var body: some HTML { - ModalForm(id: "projectForm", dismiss: dismiss) { + ModalForm(id: Self.id, dismiss: dismiss) { h1(.class("text-3xl font-bold pb-6 ps-2")) { "Project" } form( .class("space-y-4 p-4"), diff --git a/Sources/ViewController/Views/Project/ProjectView.swift b/Sources/ViewController/Views/Project/ProjectView.swift index fb188d3..97f866a 100644 --- a/Sources/ViewController/Views/Project/ProjectView.swift +++ b/Sources/ViewController/Views/Project/ProjectView.swift @@ -80,7 +80,7 @@ struct Sidebar: HTML { div(.class("flex")) { // TODO: Move somewhere outside of the sidebar. button( - .class("w-full btn btn-secondary"), + .class("btn btn-secondary btn-block"), .hx.get(route: .project(.index)), .hx.target("body"), .hx.pushURL(true), diff --git a/Sources/ViewController/Views/Project/ProjectsTable.swift b/Sources/ViewController/Views/Project/ProjectsTable.swift index 6f94f32..3b07562 100644 --- a/Sources/ViewController/Views/Project/ProjectsTable.swift +++ b/Sources/ViewController/Views/Project/ProjectsTable.swift @@ -16,21 +16,17 @@ struct ProjectsTable: HTML, Sendable { } var body: some HTML { - div { + div(.class("m-6")) { Row { h1(.class("text-2xl font-bold")) { "Projects" } div( .class("tooltip tooltip-left"), .data("tip", value: "Add project") ) { - button( - .class("btn btn-primary w-[40px] text-2xl"), - .hx.get(route: .project(.form(dismiss: false))), - .hx.target("#projectForm"), - .hx.swap(.outerHTML) - ) { - "+" - } + PlusButton() + .attributes( + .showModal(id: ProjectForm.id) + ) } } .attributes(.class("pb-6")) @@ -75,9 +71,11 @@ extension ProjectsTable { .hx.target("closest tr") ) a( - .class("btn btn-success dark:text-white"), + .class("btn btn-success btn-circle dark:text-white"), .href(route: .project(.detail(project.id, .index()))) - ) { ">" } + ) { + SVG(.chevronRight) + } } } } diff --git a/Sources/ViewController/Views/Rooms/RoomForm.swift b/Sources/ViewController/Views/Rooms/RoomForm.swift index 08f211c..4b01083 100644 --- a/Sources/ViewController/Views/Rooms/RoomForm.swift +++ b/Sources/ViewController/Views/Rooms/RoomForm.swift @@ -10,14 +10,26 @@ struct RoomForm: HTML, Sendable { static let id = "roomForm" + let id: String let dismiss: Bool let projectID: Project.ID let room: Room? + init( + id: String = Self.id, + dismiss: Bool, + projectID: Project.ID, + room: Room? = nil + ) { + self.id = id + self.dismiss = dismiss + self.projectID = projectID + self.room = room + } + var body: some HTML { - ModalForm(id: Self.id, dismiss: dismiss) { + ModalForm(id: id, dismiss: dismiss) { h1(.class("text-3xl font-bold pb-6")) { "Room" } - // TODO: Use htmx here. form( .class("modal-backdrop"), .init(name: "method", value: "dialog"), @@ -62,9 +74,8 @@ struct RoomForm: HTML, Sendable { .value("\(room != nil ? room!.registerCount : 1)"), ) } - div(.class("flex justify-end space-x-4")) { - SubmitButton() - } + SubmitButton() + .attributes(.class("btn-block")) } } } diff --git a/Sources/ViewController/Views/Rooms/RoomsView.swift b/Sources/ViewController/Views/Rooms/RoomsView.swift index 7f11c47..6341874 100644 --- a/Sources/ViewController/Views/Rooms/RoomsView.swift +++ b/Sources/ViewController/Views/Rooms/RoomsView.swift @@ -20,11 +20,9 @@ struct RoomsView: HTML, Sendable { .class("tooltip tooltip-left"), .data("tip", value: "Add room") ) { - button( - .showModal(id: RoomForm.id), - .class("btn btn-primary w-[40px] text-2xl") - ) { - "+" + div(.class("flex me-4")) { + PlusButton() + .attributes(.showModal(id: RoomForm.id)) } } } @@ -32,7 +30,7 @@ struct RoomsView: HTML, Sendable { div(.class("border rounded-lg mb-6")) { Row { - div(.class("space-x-6")) { + div(.class("space-x-6 my-2")) { Label("Sensible Heat Ratio") if let sensibleHeatRatio { Number(sensibleHeatRatio) @@ -54,27 +52,29 @@ struct RoomsView: HTML, Sendable { th { Label("Name") } th { Label("Heating Load") } th { Label("Cooling Total") } + th { Label("Cooling Sensible") } th { Label("Register Count") } th {} } } tbody { - div(.id("rooms")) { - for room in rooms { - RoomRow(room: room) - } + for room in rooms { + RoomRow(room: room, shr: sensibleHeatRatio) } // TOTALS tr(.class("font-bold text-xl")) { td { Label("Total") } td { - Number(rooms.heatingTotal) + Number(rooms.heatingTotal, digits: 0) .attributes(.class("badge badge-outline badge-error badge-xl")) } td { - Number(rooms.coolingTotal) - .attributes( - .class("badge badge-outline badge-success badge-xl")) + Number(rooms.coolingTotal, digits: 0) + .attributes(.class("badge badge-outline badge-success badge-xl")) + } + td { + Number(rooms.coolingSensible(shr: sensibleHeatRatio), digits: 0) + .attributes(.class("badge badge-outline badge-info badge-xl")) } td {} td {} @@ -88,19 +88,35 @@ struct RoomsView: HTML, Sendable { public struct RoomRow: HTML, Sendable { let room: Room + let shr: Double + + var coolingSensible: Double { + guard let value = room.coolingSensible else { + return room.coolingTotal * shr + } + return value + } + + init(room: Room, shr: Double?) { + self.room = room + self.shr = shr ?? 1.0 + } public var body: some HTML { - tr(.id("\(room.id)")) { + tr(.id("roomRow_\(room.name)")) { td { room.name } td { - Number(room.heatingLoad) + Number(room.heatingLoad, digits: 0) .attributes(.class("text-error")) } td { - Number(room.coolingTotal) + Number(room.coolingTotal, digits: 0) .attributes(.class("text-success")) } - // FIX: Add cooling sensible. + td { + Number(coolingSensible, digits: 0) + .attributes(.class("text-info")) + } td { Number(room.registerCount) } @@ -115,15 +131,15 @@ struct RoomsView: HTML, Sendable { ) EditButton() .attributes( - .hx.get( - route: .project( - .detail(room.projectID, .rooms(.form(id: room.id, dismiss: false))) - ) - ), - .hx.target("#roomForm"), - .hx.swap(.outerHTML) + .showModal(id: "roomForm_\(room.name)") ) } + RoomForm( + id: "roomForm_\(room.name)", + dismiss: true, + projectID: room.projectID, + room: room + ) } } } @@ -167,4 +183,13 @@ extension Array where Element == Room { var coolingTotal: Double { reduce(into: 0) { $0 += $1.coolingTotal } } + + func coolingSensible(shr: Double?) -> Double { + let shr = shr ?? 1.0 + + return reduce(into: 0) { + let sensible = $1.coolingSensible ?? ($1.coolingTotal * shr) + $0 += sensible + } + } } diff --git a/Sources/ViewController/Views/User/LoginForm.swift b/Sources/ViewController/Views/User/LoginForm.swift index fd2c748..29b81cb 100644 --- a/Sources/ViewController/Views/User/LoginForm.swift +++ b/Sources/ViewController/Views/User/LoginForm.swift @@ -32,7 +32,8 @@ struct LoginForm: HTML, Sendable { input( .type(.text), .required, .placeholder("Username"), .name("username"), .id("username"), - .minlength("3"), .pattern(.username) + .minlength("3"), .pattern(.username), + .autofocus ) } div(.class("validator-hint hidden")) { @@ -48,7 +49,7 @@ struct LoginForm: HTML, Sendable { SVG(.email) input( .type(.email), .placeholder("Email"), .required, - .name("email"), .id("email"), + .name("email"), .id("email"), .autofocus ) } div(.class("validator-hint hidden")) { "Enter valid email address." }