diff --git a/Sources/ManualDCore/Room.swift b/Sources/ManualDCore/Room.swift index 8f4fd56..d3d58d8 100644 --- a/Sources/ManualDCore/Room.swift +++ b/Sources/ManualDCore/Room.swift @@ -1,3 +1,4 @@ +import Dependencies import Foundation public struct Room: Codable, Equatable, Identifiable, Sendable { @@ -59,3 +60,42 @@ extension Room { } } } + +#if DEBUG + + extension Room { + public static let mocks = [ + Room( + id: UUID(0), + projectID: UUID(0), + name: "Test", + heatingLoad: 12345, + coolingLoad: .init(total: 12345, sensible: 12345), + registerCount: 2, + createdAt: Date(), + updatedAt: Date() + ), + Room( + id: UUID(1), + projectID: UUID(1), + name: "Test", + heatingLoad: 12345, + coolingLoad: .init(total: 12345, sensible: 12345), + registerCount: 2, + createdAt: Date(), + updatedAt: Date() + ), + Room( + id: UUID(2), + projectID: UUID(2), + name: "Test", + heatingLoad: 12345, + coolingLoad: .init(total: 12345, sensible: 12345), + registerCount: 2, + createdAt: Date(), + updatedAt: Date() + ), + ] + } + +#endif diff --git a/Sources/ManualDCore/Routes/ViewRoute.swift b/Sources/ManualDCore/Routes/ViewRoute.swift index 2eafa4f..14b69e6 100644 --- a/Sources/ManualDCore/Routes/ViewRoute.swift +++ b/Sources/ManualDCore/Routes/ViewRoute.swift @@ -8,11 +8,15 @@ extension SiteRoute { /// The routes return html. public enum View: Equatable, Sendable { case project(ProjectRoute) + case room(RoomRoute) public static let router = OneOf { Route(.case(Self.project)) { SiteRoute.View.ProjectRoute.router } + Route(.case(Self.room)) { + SiteRoute.View.RoomRoute.router + } } } } @@ -54,3 +58,18 @@ extension SiteRoute.View { } } } + +extension SiteRoute.View { + public enum RoomRoute: Equatable, Sendable { + case form + + static let rootPath = "rooms" + + public static let router = OneOf { + Route(.case(Self.form)) { + Path { rootPath } + Method.get + } + } + } +} diff --git a/Sources/ViewController/Live.swift b/Sources/ViewController/Live.swift index 2ebc6fe..5b7c2a6 100644 --- a/Sources/ViewController/Live.swift +++ b/Sources/ViewController/Live.swift @@ -7,6 +7,8 @@ extension ViewController.Request { switch route { case .project(let route): return try await route.renderView(isHtmxRequest: isHtmxRequest) + case .room(let route): + return try await route.renderView(isHtmxRequest: isHtmxRequest) default: // FIX: FIX return mainPage @@ -18,7 +20,9 @@ extension SiteRoute.View.ProjectRoute { func renderView(isHtmxRequest: Bool) async throws -> AnySendableHTML { switch self { case .index: - return mainPage + return MainPage { + ProjectForm() + } case .form: return MainPage { ProjectForm() @@ -29,6 +33,17 @@ extension SiteRoute.View.ProjectRoute { } } +extension SiteRoute.View.RoomRoute { + func renderView(isHtmxRequest: Bool) async throws -> AnySendableHTML { + switch self { + case .form: + return MainPage { + RoomTable(rooms: Room.mocks) + } + } + } +} + private let mainPage: AnySendableHTML = { MainPage { div { diff --git a/Sources/ViewController/Views/MainPage.swift b/Sources/ViewController/Views/MainPage.swift index ff57736..4f966e7 100644 --- a/Sources/ViewController/Views/MainPage.swift +++ b/Sources/ViewController/Views/MainPage.swift @@ -20,9 +20,11 @@ public struct MainPage: SendableHTMLDocument where Inner: Sendable } public var body: some HTML { - div(.class("bg-white dark:bg-gray-800 dark:text-white")) { + div(.class("flex bg-white dark:bg-gray-800 dark:text-white")) { Sidebar() - inner + main { + inner + } } script(.src("https://unpkg.com/lucide@latest")) {} script { diff --git a/Sources/ViewController/Views/Project/ProjectForm.swift b/Sources/ViewController/Views/Project/ProjectForm.swift index a3fbc56..8f954b2 100644 --- a/Sources/ViewController/Views/Project/ProjectForm.swift +++ b/Sources/ViewController/Views/Project/ProjectForm.swift @@ -15,6 +15,7 @@ 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( diff --git a/Sources/ViewController/Views/Rooms/RoomForm.swift b/Sources/ViewController/Views/Rooms/RoomForm.swift new file mode 100644 index 0000000..d439200 --- /dev/null +++ b/Sources/ViewController/Views/Rooms/RoomForm.swift @@ -0,0 +1,97 @@ +import Dependencies +import Elementary +import Foundation +import ManualDCore + +// TODO: Need to hold the project ID in hidden input field. +struct RoomForm: HTML, Sendable { + + var body: some HTML { + div(.class("mx-10 my-10")) { + h1(.class("text-3xl font-bold pb-6")) { "Rooms " } + form( + .class( + """ + grid md:grid-cols-3 gap-4 + """ + ) + ) { + div(.class("col-span-1")) { + div { + label(.for("name")) { "Name:" } + } + input( + .type(.text), .name("name"), .id("name"), .placeholder("Room Name"), .required, + .autofocus + ) + } + div(.class("col-span-1")) { + div { + label(.for("heatingLoad")) { "Heating Load:" } + } + input( + .type(.number), .name("heatingLoad"), .id("heatingLoad"), .placeholder("Heating Load"), + .required + ) + } + div(.class("col-span-1")) { + div { + label(.for("coolingLoad")) { "Cooling Load:" } + } + input( + .type(.number), .name("coolingLoad"), .id("coolingLoad"), .placeholder("Cooling Load"), + .required + ) + } + } + } + } +} + +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) + } + } + } + } + + 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" } + } + } + + 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)" } + } + } + } + + } +} diff --git a/Sources/ViewController/Views/Sidebar.swift b/Sources/ViewController/Views/Sidebar.swift index f916ff7..e69add9 100644 --- a/Sources/ViewController/Views/Sidebar.swift +++ b/Sources/ViewController/Views/Sidebar.swift @@ -3,30 +3,33 @@ import Elementary struct Sidebar: HTML { var body: some HTML { - div( + aside( .class( """ - h-screen w-64 pt-10 border-r-3 border-gray-800 bg-gray-100 shadow - """) + h-screen sticky top-0 border-r-3 border-gray-800 bg-gray-100 shadow + """ + ) ) { - row(title: "Project", icon: "map-pin") - row(title: "Rooms", icon: "door-closed") - row(title: "Equivalent Lengths", icon: "ruler-dimension-line") - row(title: "Friction Rate", icon: "square-function") - row(title: "Duct Sizes", icon: "wind") + row(title: "Project", icon: "map-pin", href: "/projects") + row(title: "Rooms", icon: "door-closed", href: "/rooms") + row(title: "Equivalent Lengths", icon: "ruler-dimension-line", href: "#") + row(title: "Friction Rate", icon: "square-function", href: "#") + row(title: "Duct Sizes", icon: "wind", href: "#") } } private func row( title: String, - icon: String + icon: String, + href: String ) -> some HTML { - button( + a( .class( """ - flex w-full items-center gap-4 text-gray-800 hover:bg-gray-300 pl-4 py-2 + flex w-full items-center gap-4 text-gray-800 hover:bg-gray-300 px-4 py-2 """ - ) + ), + .href(href) ) { i(.data("lucide", value: icon)) {} p(