WIP: Begins rooms table.
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import Dependencies
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct Room: Codable, Equatable, Identifiable, Sendable {
|
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
|
||||||
|
|||||||
@@ -8,11 +8,15 @@ extension SiteRoute {
|
|||||||
/// The routes return html.
|
/// The routes return html.
|
||||||
public enum View: Equatable, Sendable {
|
public enum View: Equatable, Sendable {
|
||||||
case project(ProjectRoute)
|
case project(ProjectRoute)
|
||||||
|
case room(RoomRoute)
|
||||||
|
|
||||||
public static let router = OneOf {
|
public static let router = OneOf {
|
||||||
Route(.case(Self.project)) {
|
Route(.case(Self.project)) {
|
||||||
SiteRoute.View.ProjectRoute.router
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ extension ViewController.Request {
|
|||||||
switch route {
|
switch route {
|
||||||
case .project(let route):
|
case .project(let route):
|
||||||
return try await route.renderView(isHtmxRequest: isHtmxRequest)
|
return try await route.renderView(isHtmxRequest: isHtmxRequest)
|
||||||
|
case .room(let route):
|
||||||
|
return try await route.renderView(isHtmxRequest: isHtmxRequest)
|
||||||
default:
|
default:
|
||||||
// FIX: FIX
|
// FIX: FIX
|
||||||
return mainPage
|
return mainPage
|
||||||
@@ -18,7 +20,9 @@ extension SiteRoute.View.ProjectRoute {
|
|||||||
func renderView(isHtmxRequest: Bool) async throws -> AnySendableHTML {
|
func renderView(isHtmxRequest: Bool) async throws -> AnySendableHTML {
|
||||||
switch self {
|
switch self {
|
||||||
case .index:
|
case .index:
|
||||||
return mainPage
|
return MainPage {
|
||||||
|
ProjectForm()
|
||||||
|
}
|
||||||
case .form:
|
case .form:
|
||||||
return MainPage {
|
return MainPage {
|
||||||
ProjectForm()
|
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 = {
|
private let mainPage: AnySendableHTML = {
|
||||||
MainPage {
|
MainPage {
|
||||||
div {
|
div {
|
||||||
|
|||||||
@@ -20,9 +20,11 @@ public struct MainPage<Inner: HTML>: SendableHTMLDocument where Inner: Sendable
|
|||||||
}
|
}
|
||||||
|
|
||||||
public var body: some HTML {
|
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()
|
Sidebar()
|
||||||
inner
|
main {
|
||||||
|
inner
|
||||||
|
}
|
||||||
}
|
}
|
||||||
script(.src("https://unpkg.com/lucide@latest")) {}
|
script(.src("https://unpkg.com/lucide@latest")) {}
|
||||||
script {
|
script {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ struct ProjectForm: HTML, Sendable {
|
|||||||
var body: some HTML {
|
var body: some HTML {
|
||||||
// TODO: Add htmx attributes.
|
// TODO: Add htmx attributes.
|
||||||
div(.class("mx-20 my-20")) {
|
div(.class("mx-20 my-20")) {
|
||||||
|
h1(.class("text-3xl font-bold")) { "Project" }
|
||||||
form(.class("w-full max-w-sm")) {
|
form(.class("w-full max-w-sm")) {
|
||||||
div(.class("flex items-center mb-6")) {
|
div(.class("flex items-center mb-6")) {
|
||||||
label(
|
label(
|
||||||
|
|||||||
97
Sources/ViewController/Views/Rooms/RoomForm.swift
Normal file
97
Sources/ViewController/Views/Rooms/RoomForm.swift
Normal file
@@ -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<HTMLTag.tr> {
|
||||||
|
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)" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,30 +3,33 @@ import Elementary
|
|||||||
struct Sidebar: HTML {
|
struct Sidebar: HTML {
|
||||||
|
|
||||||
var body: some HTML {
|
var body: some HTML {
|
||||||
div(
|
aside(
|
||||||
.class(
|
.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: "Project", icon: "map-pin", href: "/projects")
|
||||||
row(title: "Rooms", icon: "door-closed")
|
row(title: "Rooms", icon: "door-closed", href: "/rooms")
|
||||||
row(title: "Equivalent Lengths", icon: "ruler-dimension-line")
|
row(title: "Equivalent Lengths", icon: "ruler-dimension-line", href: "#")
|
||||||
row(title: "Friction Rate", icon: "square-function")
|
row(title: "Friction Rate", icon: "square-function", href: "#")
|
||||||
row(title: "Duct Sizes", icon: "wind")
|
row(title: "Duct Sizes", icon: "wind", href: "#")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func row(
|
private func row(
|
||||||
title: String,
|
title: String,
|
||||||
icon: String
|
icon: String,
|
||||||
|
href: String
|
||||||
) -> some HTML {
|
) -> some HTML {
|
||||||
button(
|
a(
|
||||||
.class(
|
.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)) {}
|
i(.data("lucide", value: icon)) {}
|
||||||
p(
|
p(
|
||||||
|
|||||||
Reference in New Issue
Block a user