WIP: Adds database field to delegate airflow to another room, adds select to room form.
Some checks failed
CI / Linux Tests (push) Failing after 6m39s

This commit is contained in:
2026-02-06 12:11:01 -05:00
parent 728a6c3000
commit f2c79ad56f
8 changed files with 134 additions and 7 deletions

View File

@@ -30,6 +30,10 @@ enum RoomRowType {
struct RoomCreateParser: ParserPrinter {
// FIX: The delegated to field won't work here, as we potentially have not created
// the room yet, so we will need an intermediate representation for the csv data
// that uses a room's name or disregard and require user to delegate airflow in
// the ui.
var body: some ParserPrinter<Substring.UTF8View, Room.Create> {
ParsePrint {
Prefix { $0 != UInt8(ascii: ",") }.map(.string)
@@ -45,6 +49,10 @@ struct RoomCreateParser: ParserPrinter {
}
",".utf8
Int.parser()
",".utf8
Optionally {
Room.ID.parser()
}
}
.map(.memberwise(Room.Create.init))
}

View File

@@ -89,6 +89,7 @@ extension Room.Create {
heatingLoad: heatingLoad,
coolingLoad: coolingLoad,
registerCount: registerCount,
delegetedToID: delegatedTo,
projectID: projectID
)
}
@@ -105,6 +106,7 @@ extension Room {
.field("heatingLoad", .double, .required)
.field("coolingLoad", .dictionary, .required)
.field("registerCount", .int8, .required)
.field("delegatedToID", .uuid, .references(RoomModel.schema, "id"))
.field("rectangularSizes", .array)
.field("createdAt", .datetime)
.field("updatedAt", .datetime)
@@ -140,6 +142,9 @@ final class RoomModel: Model, @unchecked Sendable, Validatable {
@Field(key: "registerCount")
var registerCount: Int
@OptionalParent(key: "delegatedToID")
var room: RoomModel?
@Field(key: "rectangularSizes")
var rectangularSizes: [Room.RectangularSize]?
@@ -160,6 +165,7 @@ final class RoomModel: Model, @unchecked Sendable, Validatable {
heatingLoad: Double,
coolingLoad: Room.CoolingLoad,
registerCount: Int,
delegetedToID: UUID? = nil,
rectangularSizes: [Room.RectangularSize]? = nil,
createdAt: Date? = nil,
updatedAt: Date? = nil,
@@ -170,6 +176,7 @@ final class RoomModel: Model, @unchecked Sendable, Validatable {
self.heatingLoad = heatingLoad
self.coolingLoad = coolingLoad
self.registerCount = registerCount
$room.id = delegetedToID
self.rectangularSizes = rectangularSizes
self.createdAt = createdAt
self.updatedAt = updatedAt
@@ -184,6 +191,7 @@ final class RoomModel: Model, @unchecked Sendable, Validatable {
heatingLoad: heatingLoad,
coolingLoad: coolingLoad,
registerCount: registerCount,
delegatedTo: $room.id,
rectangularSizes: rectangularSizes,
createdAt: createdAt!,
updatedAt: updatedAt!

View File

@@ -7,6 +7,7 @@ import Foundation
/// room, the number of registers in the room, and any rectangular
/// duct size calculations stored for the room.
public struct Room: Codable, Equatable, Identifiable, Sendable {
/// The unique id of the room.
public let id: UUID
@@ -24,6 +25,10 @@ public struct Room: Codable, Equatable, Identifiable, Sendable {
/// The number of registers for the room.
public let registerCount: Int
/// An optional room that the airflow is delegated to.
public let delegatedTo: Room.ID?
/// The rectangular duct size calculations for a room.
///
/// **NOTE:** These are optionally set after the round sizes have been calculate
@@ -43,6 +48,7 @@ public struct Room: Codable, Equatable, Identifiable, Sendable {
heatingLoad: Double,
coolingLoad: CoolingLoad,
registerCount: Int = 1,
delegatedTo: Room.ID? = nil,
rectangularSizes: [RectangularSize]? = nil,
createdAt: Date,
updatedAt: Date
@@ -53,6 +59,7 @@ public struct Room: Codable, Equatable, Identifiable, Sendable {
self.heatingLoad = heatingLoad
self.coolingLoad = coolingLoad
self.registerCount = registerCount
self.delegatedTo = delegatedTo
self.rectangularSizes = rectangularSizes
self.createdAt = createdAt
self.updatedAt = updatedAt
@@ -98,15 +105,22 @@ extension Room {
public struct Create: Codable, Equatable, Sendable {
/// A unique name for the room in the project.
public let name: String
/// The heating load required for the room (from Manual-J).
public let heatingLoad: Double
/// The total cooling load required for the room (from Manual-J).
public let coolingTotal: Double?
/// An optional sensible cooling load for the room.
public let coolingSensible: Double?
/// The number of registers for the room.
public let registerCount: Int
/// An optional room that this room delegates it's airflow to.
public let delegatedTo: Room.ID?
public var coolingLoad: Room.CoolingLoad {
.init(total: coolingTotal, sensible: coolingSensible)
}
@@ -116,13 +130,15 @@ extension Room {
heatingLoad: Double,
coolingTotal: Double? = nil,
coolingSensible: Double? = nil,
registerCount: Int = 1
registerCount: Int = 1,
delegatedTo: Room.ID? = nil
) {
self.name = name
self.heatingLoad = heatingLoad
self.coolingTotal = coolingTotal
self.coolingSensible = coolingSensible
self.registerCount = registerCount
self.delegatedTo = delegatedTo
}
}

View File

@@ -233,7 +233,6 @@ extension SiteRoute.View.ProjectRoute {
Method.post
Body {
FormData {
// Field("projectID") { Project.ID.parser() }
Field("name", .string)
Field("heatingLoad") { Double.parser() }
Optionally {
@@ -243,6 +242,9 @@ extension SiteRoute.View.ProjectRoute {
Field("coolingSensible") { Double.parser() }
}
Field("registerCount") { Digits() }
Optionally {
Field("delegatedTo") { Room.ID.parser() }
}
}
.map(.memberwise(Room.Create.init))
}

View File

@@ -0,0 +1,61 @@
import Elementary
import Foundation
/// NOTE: This does not have the 'select' class added to it, because it's generally
/// added to the label of the field.
public struct Select<Label, Element>: HTML where Label: HTML {
let label: @Sendable (Element) -> Label
let value: @Sendable (Element) -> String
let selected: @Sendable (Element) -> Bool
let items: [Element]
let placeholder: String?
public init(
_ items: [Element],
placeholder: String? = nil,
value: @escaping @Sendable (Element) -> String,
selected: @escaping @Sendable (Element) -> Bool = { _ in false },
@HTMLBuilder label: @escaping @Sendable (Element) -> Label
) {
self.label = label
self.items = items
self.placeholder = placeholder
self.selected = selected
self.value = value
}
public var body: some HTML<HTMLTag.select> {
select {
if let placeholder {
option(.selected, .disabled) { placeholder }
}
for item in items {
option(.value(value(item))) { label(item) }
.attributes(.selected, when: selected(item))
}
}
}
}
extension Select: Sendable where Element: Sendable, Label: Sendable {}
extension Select where Element: Identifiable, Element.ID == UUID, Element: Sendable {
public init(
_ items: [Element],
placeholder: String? = nil,
selected: @escaping @Sendable (Element) -> Bool = { _ in false },
@HTMLBuilder label: @escaping @Sendable (Element) -> Label
) {
self.init(
items,
placeholder: placeholder,
value: { $0.id.uuidString },
selected: selected,
label: label
)
}
}

View File

@@ -108,6 +108,7 @@ public struct MainPage<Inner: HTML>: SendableHTMLDocument where Inner: Sendable
}
}
.attributes(.data("theme", value: theme?.rawValue ?? "default"), when: theme != nil)
}
}

View File

@@ -5,7 +5,6 @@ import Foundation
import ManualDCore
import Styleguide
// TODO: Need to hold the project ID in hidden input field.
struct RoomForm: HTML, Sendable {
static func id(_ room: Room? = nil) -> String {
@@ -17,14 +16,17 @@ struct RoomForm: HTML, Sendable {
let dismiss: Bool
let projectID: Project.ID
let room: Room?
let rooms: [Room]
init(
dismiss: Bool,
projectID: Project.ID,
rooms: [Room],
room: Room? = nil
) {
self.dismiss = dismiss
self.projectID = projectID
self.rooms = rooms
self.room = room
}
@@ -38,6 +40,7 @@ struct RoomForm: HTML, Sendable {
var body: some HTML {
ModalForm(id: Self.id(room), dismiss: dismiss) {
h1(.class("text-3xl font-bold pb-6")) { "Room" }
form(
.class("grid grid-cols-1 gap-4"),
room == nil
@@ -97,12 +100,32 @@ struct RoomForm: HTML, Sendable {
.type(.number),
.min("1"),
.required,
.value(room?.registerCount ?? 1)
.value(room?.registerCount ?? 1),
.id("registerCount")
)
label(.class("select w-full"), .id("delegateToSelect")) {
span(.class("label")) { "Room" }
Select(rooms, placeholder: "Delegate Airflow") {
$0.name
}
.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);
}
"""
}
}
}

View File

@@ -17,6 +17,11 @@ 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")) {
@@ -129,17 +134,18 @@ struct RoomsView: HTML, Sendable {
}
tbody {
for room in rooms {
RoomRow(room: room, shr: sensibleHeatRatio)
RoomRow(room: room, shr: sensibleHeatRatio, rooms: rooms)
}
}
}
RoomForm(dismiss: true, projectID: projectID, room: nil)
RoomForm(dismiss: true, projectID: projectID, rooms: rooms, room: nil)
UploadCSVForm(dismiss: true)
}
}
public struct RoomRow: HTML, Sendable {
let rooms: [Room]
let room: Room
let shr: Double
@@ -151,9 +157,10 @@ struct RoomsView: HTML, Sendable {
// return value
}
init(room: Room, shr: Double?) {
init(room: Room, shr: Double?, rooms: [Room]) {
self.room = room
self.shr = shr ?? 1.0
self.rooms = rooms
}
public var body: some HTML {
@@ -208,6 +215,7 @@ struct RoomsView: HTML, Sendable {
RoomForm(
dismiss: true,
projectID: room.projectID,
rooms: rooms,
room: room
)
}