From 7c37392390c7b10b509bbc5c6417738eae54e0a3 Mon Sep 17 00:00:00 2001 From: Michael Housh Date: Thu, 1 Jan 2026 10:12:02 -0500 Subject: [PATCH] WIP: Updates rooms views. --- Sources/ManualDCore/Routes/ViewRoute.swift | 10 +- Sources/ViewController/Live.swift | 18 +-- .../Views/Project/ProjectForm.swift | 114 ++++++++---------- .../Views/Project/ProjectView.swift | 7 +- .../ViewController/Views/Rooms/RoomForm.swift | 24 ++-- .../Views/Rooms/RoomTable.swift | 71 ----------- .../Views/Rooms/RoomsView.swift | 64 ++++++++++ 7 files changed, 148 insertions(+), 160 deletions(-) delete mode 100644 Sources/ViewController/Views/Rooms/RoomTable.swift create mode 100644 Sources/ViewController/Views/Rooms/RoomsView.swift diff --git a/Sources/ManualDCore/Routes/ViewRoute.swift b/Sources/ManualDCore/Routes/ViewRoute.swift index f087ed4..789fbb0 100644 --- a/Sources/ManualDCore/Routes/ViewRoute.swift +++ b/Sources/ManualDCore/Routes/ViewRoute.swift @@ -28,7 +28,7 @@ extension SiteRoute { extension SiteRoute.View { public enum ProjectRoute: Equatable, Sendable { case create(Project.Create) - case form + case form(dismiss: Bool = false) case index static let rootPath = "projects" @@ -54,6 +54,9 @@ extension SiteRoute.View { "create" } Method.get + Query { + Field("dismiss", default: false) { Bool.parser() } + } } Route(.case(Self.index)) { Path { rootPath } @@ -65,7 +68,7 @@ extension SiteRoute.View { extension SiteRoute.View { public enum RoomRoute: Equatable, Sendable { - case form + case form(dismiss: Bool = false) case index static let rootPath = "rooms" @@ -77,6 +80,9 @@ extension SiteRoute.View { "create" } Method.get + Query { + Field("dismiss", default: false) { Bool.parser() } + } } Route(.case(Self.index)) { Path { rootPath } diff --git a/Sources/ViewController/Live.swift b/Sources/ViewController/Live.swift index 41cc022..869b041 100644 --- a/Sources/ViewController/Live.swift +++ b/Sources/ViewController/Live.swift @@ -25,10 +25,12 @@ extension SiteRoute.View.ProjectRoute { return MainPage { ProjectView(project: .mock) } - case .form: - return MainPage { - ProjectForm() + case .form(let dismiss): + guard !dismiss else { + return div(.id("projectForm")) {} } + return ProjectForm() + case .create: return mainPage } @@ -38,14 +40,14 @@ extension SiteRoute.View.ProjectRoute { extension SiteRoute.View.RoomRoute { func renderView(isHtmxRequest: Bool) async throws -> AnySendableHTML { switch self { - case .form: - // TODO: Check that it's an htmx request. + case .form(let dismiss): + guard !dismiss else { + return div(.id("roomForm")) {} + } return RoomForm() case .index: return MainPage { - div { - RoomTable(rooms: Room.mocks) - } + RoomsView(rooms: Room.mocks) } } } diff --git a/Sources/ViewController/Views/Project/ProjectForm.swift b/Sources/ViewController/Views/Project/ProjectForm.swift index 8f954b2..2bea579 100644 --- a/Sources/ViewController/Views/Project/ProjectForm.swift +++ b/Sources/ViewController/Views/Project/ProjectForm.swift @@ -1,6 +1,7 @@ import Elementary import ElementaryHTMX import ManualDCore +import Styleguide struct ProjectForm: HTML, Sendable { @@ -13,75 +14,58 @@ struct ProjectForm: HTML, Sendable { } var body: some HTML { - // TODO: Add htmx attributes. - div(.class("mx-20 my-20")) { - h1(.class("text-3xl font-bold")) { "Project" } - form(.class("w-full max-w-sm")) { - div(.class("flex items-center mb-6")) { - label( - .for("name"), .class("block text-gray-500 font-bold mr-4") - ) { "Name:" } - input( - .type(.text), .name("name"), .placeholder("Customer Name"), - .value(project?.name ?? ""), .required, .autofocus - ) - .defaultInput() + div( + .id("projectForm"), + .class( + """ + fixed top-40 left-[25vw] w-1/2 z-50 text-gray-800 + bg-gray-200 border border-gray-400 + rounded-lg shadow-lg mx-10 + """ + ) + ) { + h1(.class("text-3xl font-bold pb-6 ps-2")) { "Project" } + form(.class("space-y-4 p-4")) { + div { + label(.for("name")) { "Name" } + Input(id: "name", placeholder: "Name") + .attributes(.type(.text), .required, .autofocus) } - div(.class("flex items-center mb-6")) { - label(.for("streetAddress"), .class("block text-gray-500 font-bold mr-4")) { "Address:" } - input( - .type(.text), .name("streetAddress"), - .placeholder("Street Address"), - .value(project?.streetAddress ?? ""), - .required - ) - .defaultInput() + div { + label(.for("streetAddress")) { "Address" } + Input(id: "streetAddress", placeholder: "Street Address") + .attributes(.type(.text), .required) + } + div { + label(.for("city")) { "City" } + Input(id: "city", placeholder: "City") + .attributes(.type(.text), .required) + } + div { + label(.for("state")) { "State" } + Input(id: "state", placeholder: "State") + .attributes(.type(.text), .required) + } + div { + label(.for("zipCode")) { "Zip" } + Input(id: "zipCode", placeholder: "Zip code") + .attributes(.type(.text), .required) + } + + Row { + div {} + div(.class("space-x-4")) { + CancelButton() + .attributes( + .hx.get(route: .project(.form(dismiss: true))), + .hx.target("#projectForm"), + .hx.swap(.outerHTML) + ) + SubmitButton() + } } - // div(.class("w-full space-x-2")) { - // label(.for("city")) { "City:" } - // - // input( - // .type(.text), .name("city"), - // .placeholder("City"), - // .value(project?.city ?? ""), - // .required - // ) - // .defaultInput() - // } - // div(.class("w-full space-y-2")) { - // label(.for("state")) { "State:" } - // input( - // .type(.text), .name("state"), - // .placeholder("State"), - // .value(project?.state ?? ""), - // .required - // ) - // .defaultInput() - // } - // div(.class("w-full space-y-2")) { - // label(.for("zipCode")) { - // "Zip:" - // } - // input( - // .type(.text), .name("zipCode"), - // .placeholder("Zip Code"), - // .value(project?.zipCode ?? ""), - // .required - // ) - // .defaultInput() - // } } } } -} -// TODO: Move -extension input { - func defaultInput() -> some HTML { - attributes( - .class( - "w-full rounded-md bg-white px-3 py-1.5 text-slate-900 outline-1 -outline-offset-1 outline-slate-300 focus:outline focus:-outline-offset-2 focus:outline-indigo-600" - ) - ) - } } diff --git a/Sources/ViewController/Views/Project/ProjectView.swift b/Sources/ViewController/Views/Project/ProjectView.swift index b2e20a6..c5ae8df 100644 --- a/Sources/ViewController/Views/Project/ProjectView.swift +++ b/Sources/ViewController/Views/Project/ProjectView.swift @@ -16,7 +16,12 @@ struct ProjectView: HTML, Sendable { ) { Row { h1(.class("text-2xl font-bold")) { "Project" } - // FIX: Add edit button. + EditButton() + .attributes( + .hx.get(route: .project(.form(dismiss: false))), + .hx.target("#projectForm"), + .hx.swap(.outerHTML) + ) } Row { diff --git a/Sources/ViewController/Views/Rooms/RoomForm.swift b/Sources/ViewController/Views/Rooms/RoomForm.swift index 8003847..ba61792 100644 --- a/Sources/ViewController/Views/Rooms/RoomForm.swift +++ b/Sources/ViewController/Views/Rooms/RoomForm.swift @@ -10,19 +10,17 @@ struct RoomForm: HTML, Sendable { var body: some HTML { div( + .id("roomForm"), .class( - "fixed top-20 z-50 w-1/2 mx-[20vw] my-10 bg-gray-700 rounded-lg shadow-lg p-4" - ), - .id("roomForm") + """ + fixed top-40 left-[25vw] w-1/2 z-50 text-gray-800 + bg-gray-200 border border-gray-400 + rounded-lg shadow-lg mx-10 p-4 + """ + ) ) { - h1(.class("text-3xl font-bold pb-6")) { "New Room" } - form( - .class( - """ - space-y-4 - """ - ) - ) { + h1(.class("text-3xl font-bold pb-6")) { "Room" } + form { div { label(.for("name")) { "Name:" } Input(id: "name", placeholder: "Room Name") @@ -49,8 +47,8 @@ struct RoomForm: HTML, Sendable { div(.class("space-x-4")) { CancelButton() .attributes( - .hx.get(route: .room(.index)), - .hx.target("body"), + .hx.get(route: .room(.form(dismiss: true))), + .hx.target("#roomForm"), .hx.swap(.outerHTML) ) SubmitButton() diff --git a/Sources/ViewController/Views/Rooms/RoomTable.swift b/Sources/ViewController/Views/Rooms/RoomTable.swift deleted file mode 100644 index be8c6ab..0000000 --- a/Sources/ViewController/Views/Rooms/RoomTable.swift +++ /dev/null @@ -1,71 +0,0 @@ -import Dependencies -import Elementary -import ElementaryHTMX -import Foundation -import ManualDCore -import Styleguide - -struct RoomTable: HTML, Sendable { - let rooms: [Room] - - var body: some HTML { - div(.class("m-10")) { - h1(.class("text-3xl font-bold")) { "Rooms" } - table( - .id("rooms"), - .class( - "w-full border-collapse border border-gray-200 table-fixed" - ) - ) { - thead { tableHeader } - tbody { - Rows(rooms: rooms) - } - } - div(.id("roomForm")) {} - } - } - - private var tableHeader: some HTML { - tr { - th(.class("border border-gray-200 text-xl font-bold")) { "Name" } - th(.class("border border-gray-200 text-xl font-bold")) { "Heating Load" } - th(.class("border border-gray-200 text-xl font-bold")) { "Cooling Total" } - th(.class("border border-gray-200 text-xl font-bold")) { "Cooling Sensible" } - th(.class("border border-gray-200 text-xl font-bold")) { "Register Count" } - th(.class("border border-gray-200 text-xl font-bold")) { - div(.class("flex justify-between")) { - div {} - button( - .class("px-2"), - .hx.get(route: .room(.form)), - .hx.target("#roomForm"), - .hx.swap(.outerHTML) - ) { - Icon(.circlePlus) - } - } - } - } - } - - private struct Rows: HTML, Sendable { - let rooms: [Room] - - var body: some HTML { - for room in rooms { - tr { - td(.class("border border-gray-200 p-2")) { room.name } - td(.class("border border-gray-200 p-2")) { "\(room.heatingLoad)" } - td(.class("border border-gray-200 p-2")) { "\(room.coolingLoad.total)" } - td(.class("border border-gray-200 p-2")) { "\(room.coolingLoad.sensible)" } - td(.class("border border-gray-200 p-2")) { "\(room.registerCount)" } - td(.class("border border-gray-200 p-2")) { - // TODO: Add edit button. - } - } - } - } - - } -} diff --git a/Sources/ViewController/Views/Rooms/RoomsView.swift b/Sources/ViewController/Views/Rooms/RoomsView.swift new file mode 100644 index 0000000..0fbb9ec --- /dev/null +++ b/Sources/ViewController/Views/Rooms/RoomsView.swift @@ -0,0 +1,64 @@ +import Dependencies +import Elementary +import ElementaryHTMX +import Foundation +import ManualDCore +import Styleguide + +struct RoomsView: HTML, Sendable { + let rooms: [Room] + + var body: some HTML { + div(.class("m-10")) { + Row { + h1(.class("text-3xl font-bold pb-6")) { "Rooms" } + button( + .hx.get(route: .room(.form(dismiss: false))), + .hx.target("#roomForm"), + .hx.swap(.outerHTML) + ) { + Icon(.circlePlus) + } + } + + div( + .id("roomTable"), + .class( + """ + border border-gray-200 rounded-lg shadow-lg + space-y-4 p-4 + """ + ) + ) { + // Header + Row { + Label("Name") + Label("Heating Load") + Label("Cooling Total") + Label("Cooling Sensible") + Label("Register Count") + } + .attributes(.class("border-b border-gray-200")) + + // Rows + for row in rooms { + Row { + span { row.name } + Number(row.heatingLoad) + .attributes(.class("text-red-500")) + Number(row.coolingLoad.total) + .attributes(.class("text-green-400")) + Number(row.coolingLoad.sensible) + .attributes(.class("text-blue-400")) + Number(row.registerCount) + } + .attributes(.class("border-b border-gray-200")) + } + + } + + div(.id("roomForm")) {} + } + } + +}