From 0775474f57ba1d8160411bd38178dee1ca97f135 Mon Sep 17 00:00:00 2001 From: Michael Housh Date: Fri, 6 Feb 2026 17:01:43 -0500 Subject: [PATCH] WIP: Updates test html snapshots, working on validation when delegating airflow to a different room. --- Sources/DatabaseClient/Internal/Rooms.swift | 25 +++- Sources/Styleguide/Select.swift | 21 ++++ .../ViewController/Views/Rooms/RoomForm.swift | 27 ++-- .../Views/Rooms/RoomsView.swift | 28 +++-- Tests/CSVParsingTests/CSVParsingTests.swift | 6 +- .../ViewControllerTests/projectDetail.2.html | 119 +++++++++++++++--- 6 files changed, 179 insertions(+), 47 deletions(-) diff --git a/Sources/DatabaseClient/Internal/Rooms.swift b/Sources/DatabaseClient/Internal/Rooms.swift index 3602899..099c6de 100644 --- a/Sources/DatabaseClient/Internal/Rooms.swift +++ b/Sources/DatabaseClient/Internal/Rooms.swift @@ -84,6 +84,14 @@ extension DatabaseClient.Rooms: TestDependencyKey { extension Room.Create { func toModel(projectID: Project.ID) throws -> RoomModel { + var registerCount = registerCount + // Set register count appropriately when delegatedTo is set / changes. + if delegatedTo != nil { + registerCount = 0 + } else if registerCount == 0 { + registerCount = 1 + } + return .init( name: name, heatingLoad: heatingLoad, @@ -229,13 +237,28 @@ final class RoomModel: Model, @unchecked Sendable, Validatable { Validator.validate(\.coolingLoad) .errorLabel("Cooling Load", inline: true) - Validator.validate(\.registerCount, with: .greaterThanOrEquals(1)) + Validator.validate(\.registerCount, with: .greaterThanOrEquals($room.id == nil ? 1 : 0)) .errorLabel("Register Count", inline: true) Validator.validate(\.rectangularSizes) } } + + func validateAndSave(on database: Database) async throws { + try self.validate() + if let delegateTo = $room.id { + guard let parent = try await RoomModel.find(delegateTo, on: database) else { + throw ValidationError("Can not find room: \(delegateTo), to delegate airflow to.") + } + guard parent.$room.id == nil else { + throw ValidationError( + "Can not delegate airflow to a room that also delegates it's own airflow." + ) + } + } + try await save(on: database) + } } extension Room.CoolingLoad: Validatable { diff --git a/Sources/Styleguide/Select.swift b/Sources/Styleguide/Select.swift index 4728a58..7f29a70 100644 --- a/Sources/Styleguide/Select.swift +++ b/Sources/Styleguide/Select.swift @@ -59,3 +59,24 @@ extension Select where Element: Identifiable, Element.ID == UUID, Element: Senda } } + +extension Select +where Element: Identifiable, Element.ID == UUID, Element: Sendable, Label == HTMLText { + + public init( + _ items: [Element], + label keyPath: KeyPath, + placeholder: String? = nil, + selected: @escaping @Sendable (Element) -> Bool = { _ in false } + ) { + self.init( + items, + placeholder: placeholder, + value: { $0.id.uuidString }, + selected: selected, + label: { HTMLText($0[keyPath: keyPath]) } + ) + + } + +} diff --git a/Sources/ViewController/Views/Rooms/RoomForm.swift b/Sources/ViewController/Views/Rooms/RoomForm.swift index 5d80a27..c63c156 100644 --- a/Sources/ViewController/Views/Rooms/RoomForm.swift +++ b/Sources/ViewController/Views/Rooms/RoomForm.swift @@ -30,13 +30,17 @@ struct RoomForm: HTML, Sendable { self.room = room } - var route: String { + private var route: String { SiteRoute.View.router.path( for: .project(.detail(projectID, .rooms(.index))) ) .appendingPath(room?.id) } + private var selectableRooms: [Room] { + rooms.filter { $0.delegatedTo == nil } + } + var body: some HTML { ModalForm(id: Self.id(room), dismiss: dismiss) { h1(.class("text-3xl font-bold pb-6")) { "Room" } @@ -104,28 +108,15 @@ struct RoomForm: HTML, Sendable { .id("registerCount") ) - label(.class("select w-full"), .id("delegateToSelect")) { + label(.class("select w-full")) { span(.class("label")) { "Room" } - Select(rooms, placeholder: "Delegate Airflow") { - $0.name - } - .attributes(.name("delegatedTo")) + Select(selectableRooms, label: \.name, placeholder: "Delegate Airflow") + .attributes(.name("delegatedTo")) } + SubmitButton() .attributes(.class("btn-block")) } } - - script { - """ - function myClick() { - console.log('clicked'); - const simple = document.getElementById('simple'); - console.log(simple.style.display); - simple.style.display = 'block'; - console.log(simple.style.display); - } - """ - } } } diff --git a/Sources/ViewController/Views/Rooms/RoomsView.swift b/Sources/ViewController/Views/Rooms/RoomsView.swift index b9dadcf..4d94f5f 100644 --- a/Sources/ViewController/Views/Rooms/RoomsView.swift +++ b/Sources/ViewController/Views/Rooms/RoomsView.swift @@ -17,11 +17,6 @@ struct RoomsView: HTML, Sendable { var body: some HTML { div(.class("flex w-full flex-col")) { - input(.type(.checkbox), .name("delegateToCheckbox"), .on(.click, "showElement('simple');")) - div(.style("display: none;"), .id("simple"), .class("hidden")) { - "This is hidden" - } - PageTitleRow { div(.class("flex grid grid-cols-3 w-full gap-y-4")) { @@ -107,6 +102,11 @@ struct RoomsView: HTML, Sendable { "Register Count" } } + th { + div(.class("flex justify-center")) { + "Delegated To" + } + } th { div(.class("flex justify-end me-2 space-x-4")) { @@ -151,10 +151,11 @@ struct RoomsView: HTML, Sendable { var coolingSensible: Double { try! room.coolingLoad.ensured(shr: shr).sensible - // guard let value = room.coolingSensible else { - // return room.coolingTotal * shr - // } - // return value + } + + var delegatedToRoomName: String? { + guard let delegatedToID = room.delegatedTo else { return nil } + return rooms.first(where: { $0.id == delegatedToID })?.name } init(room: Room, shr: Double?, rooms: [Room]) { @@ -186,7 +187,14 @@ struct RoomsView: HTML, Sendable { } td { div(.class("flex justify-center")) { - Number(room.registerCount) + Number(delegatedToRoomName != nil ? 0 : room.registerCount) + } + } + td { + if let name = delegatedToRoomName { + div(.class("flex justify-center")) { + name + } } } td { diff --git a/Tests/CSVParsingTests/CSVParsingTests.swift b/Tests/CSVParsingTests/CSVParsingTests.swift index 66db46f..4f48c49 100644 --- a/Tests/CSVParsingTests/CSVParsingTests.swift +++ b/Tests/CSVParsingTests/CSVParsingTests.swift @@ -11,9 +11,9 @@ struct CSVParsingTests { let parser = CSVParser.liveValue let input = """ - Name,Heating Load,Cooling Total,Cooling Sensible,Register Count - Bed-1,12345,12345,,2 - Bed-2,1223,,1123,1 + Name,Heating Load,Cooling Total,Cooling Sensible,Register Count,Delegated To + Bed-1,12345,12345,,2, + Bed-2,1223,,1123,1, """ let rooms = try await parser.parseRooms(.init(file: Data(input.utf8))) diff --git a/Tests/ViewControllerTests/__Snapshots__/ViewControllerTests/projectDetail.2.html b/Tests/ViewControllerTests/__Snapshots__/ViewControllerTests/projectDetail.2.html index 37b1222..33c65b6 100644 --- a/Tests/ViewControllerTests/__Snapshots__/ViewControllerTests/projectDetail.2.html +++ b/Tests/ViewControllerTests/__Snapshots__/ViewControllerTests/projectDetail.2.html @@ -54,7 +54,7 @@ p-6 w-full">

Room Loads

-
+
-
-
- - -
-
Heating Total @@ -113,7 +107,13 @@ p-6 w-full">
Register Count
-
+
Delegated To
+ + +
+
+ +
@@ -136,6 +136,7 @@ p-6 w-full">
1
+
@@ -167,7 +168,17 @@ p-6 w-full"> Cooling Sensible + + Room
@@ -188,6 +199,7 @@ p-6 w-full">
2
+
@@ -219,7 +231,17 @@ p-6 w-full"> Cooling Sensible + + Room
@@ -240,6 +262,7 @@ p-6 w-full">
3
+
@@ -271,7 +294,17 @@ p-6 w-full"> Cooling Sensible + + Room
@@ -292,6 +325,7 @@ p-6 w-full">
2
+
@@ -323,7 +357,17 @@ p-6 w-full"> Cooling Sensible + + Room
@@ -344,6 +388,7 @@ p-6 w-full">
2
+
@@ -375,7 +420,17 @@ p-6 w-full"> Cooling Sensible + + Room
@@ -396,6 +451,7 @@ p-6 w-full">
2
+
@@ -427,7 +483,17 @@ p-6 w-full"> Cooling Sensible + + Room
@@ -450,11 +516,34 @@ p-6 w-full"> Cooling Sensible + + Room
+ + +