fix: Fixes duct sizing rooms table not showing forms correctly, updates the table styles.

This commit is contained in:
2026-01-14 10:32:57 -05:00
parent 71848c607a
commit 450791b37e
8 changed files with 158 additions and 143 deletions

View File

@@ -14,6 +14,8 @@ extension DatabaseClient {
public var get: @Sendable (Room.ID) async throws -> Room?
public var fetch: @Sendable (Project.ID) async throws -> [Room]
public var update: @Sendable (Room.ID, Room.Update) async throws -> Room
public var updateRectangularSize:
@Sendable (Room.ID, DuctSizing.RectangularDuct) async throws -> Room
}
}
@@ -67,6 +69,19 @@ extension DatabaseClient.Rooms: TestDependencyKey {
try await model.save(on: database)
}
return try model.toDTO()
},
updateRectangularSize: { id, size in
guard let model = try await RoomModel.find(id, on: database) else {
throw NotFoundError()
}
var rectangularSizes = model.rectangularSizes ?? []
rectangularSizes.removeAll {
$0.id == size.id
}
rectangularSizes.append(size)
model.rectangularSizes = rectangularSizes
try await model.save(on: database)
return try model.toDTO()
}
)
}

View File

@@ -607,7 +607,7 @@ extension SiteRoute.View.ProjectRoute {
public enum DuctSizingRoute: Equatable, Sendable {
case index
case deleteRectangularSize(Room.ID, DuctSizing.RectangularDuct.ID)
case deleteRectangularSize(Room.ID, DeleteRectangularDuct)
case roomRectangularForm(Room.ID, RoomRectangularForm)
case trunk(TrunkRoute)
@@ -628,7 +628,9 @@ extension SiteRoute.View.ProjectRoute {
Method.delete
Query {
Field("rectangularSize") { DuctSizing.RectangularDuct.ID.parser() }
Field("register") { Int.parser() }
}
.map(.memberwise(DeleteRectangularDuct.init))
}
Route(.case(Self.roomRectangularForm)) {
Path {
@@ -654,6 +656,17 @@ extension SiteRoute.View.ProjectRoute {
}
}
public struct DeleteRectangularDuct: Equatable, Sendable {
public let rectangularSizeID: DuctSizing.RectangularDuct.ID
public let register: Int
public init(rectangularSizeID: DuctSizing.RectangularDuct.ID, register: Int) {
self.rectangularSizeID = rectangularSizeID
self.register = register
}
}
public enum TrunkRoute: Equatable, Sendable {
case delete(DuctSizing.TrunkSize.ID)
case submit(TrunkSizeForm)

View File

@@ -17,4 +17,8 @@ extension String {
func appendingPath(_ id: UUID) -> Self {
return appendingPath(id.uuidString)
}
var idString: Self {
replacing("-", with: "")
}
}

View File

@@ -2,6 +2,6 @@ import Foundation
extension UUID {
var idString: String {
uuidString.replacing("-", with: "")
uuidString.idString
}
}

View File

@@ -538,33 +538,29 @@ extension SiteRoute.View.ProjectRoute.DuctSizingRoute {
case .index:
return await view(on: request, projectID: projectID)
case .deleteRectangularSize(let roomID, let rectangularSizeID):
case .deleteRectangularSize(let roomID, let request):
return await ResultView {
let room = try await database.rooms.deleteRectangularSize(roomID, rectangularSizeID)
let room = try await database.rooms.deleteRectangularSize(roomID, request.rectangularSizeID)
return try await database.calculateDuctSizes(projectID: projectID)
.rooms
.filter({ $0.roomID == room.id })
.filter({ $0.roomID == room.id && $0.roomRegister == request.register })
.first!
} onSuccess: { container in
DuctSizingView.RoomRow(room: container)
} onSuccess: { room in
DuctSizingView.RoomRow(room: room)
}
case .roomRectangularForm(let roomID, let form):
return await ResultView {
let room = try await database.rooms.update(
let room = try await database.rooms.updateRectangularSize(
roomID,
.init(
rectangularSizes: [
.init(id: form.id ?? .init(), register: form.register, height: form.height)
]
)
)
return try await database.calculateDuctSizes(projectID: projectID)
.rooms
.filter({ $0.roomID == room.id })
.filter({ $0.roomID == room.id && $0.roomRegister == form.register })
.first!
} onSuccess: { container in
DuctSizingView.RoomRow(room: container)
} onSuccess: { room in
DuctSizingView.RoomRow(room: room)
}
case .trunk(let route):

View File

@@ -31,30 +31,30 @@ struct DuctSizingView: HTML, Sendable {
RoomsTable(rooms: rooms)
}
// Row {
// h2(.class("text-2xl font-bold")) { "Trunk Sizes" }
//
// PlusButton()
// .attributes(
// .class("me-6"),
// .showModal(id: TrunkSizeForm.id())
// )
// }
// .attributes(.class("mt-6"))
//
// div(.class("divider -mt-2")) {}
//
// if supplyTrunks.count > 0 {
// h2(.class("text-lg font-bold text-info")) { "Supply Trunks" }
// TrunkTable(trunks: supplyTrunks, rooms: rooms)
// }
//
// if returnTrunks.count > 0 {
// h2(.class("text-lg font-bold text-error")) { "Return Trunks" }
// TrunkTable(trunks: returnTrunks, rooms: rooms)
// }
//
// TrunkSizeForm(rooms: rooms, dismiss: true)
Row {
h2(.class("text-2xl font-bold")) { "Trunk Sizes" }
PlusButton()
.attributes(
.class("me-6"),
.showModal(id: TrunkSizeForm.id())
)
}
.attributes(.class("mt-6"))
div(.class("divider -mt-2")) {}
if supplyTrunks.count > 0 {
h2(.class("text-lg font-bold text-info")) { "Supply Trunks" }
TrunkTable(trunks: supplyTrunks, rooms: rooms)
}
if returnTrunks.count > 0 {
h2(.class("text-lg font-bold text-error")) { "Return Trunks" }
TrunkTable(trunks: returnTrunks, rooms: rooms)
}
TrunkSizeForm(rooms: rooms, dismiss: true)
}
}

View File

@@ -5,58 +5,25 @@ import Styleguide
struct RectangularSizeForm: HTML, Sendable {
static func id(_ roomID: Room.ID? = nil) -> String {
let base = "rectangularSize"
guard let roomID else { return base }
return "\(base)_\(roomID.idString)"
}
static func id(_ room: DuctSizing.RoomContainer) -> String {
return id(room.roomID)
let base = "rectangularSize"
return "\(base)_\(room.registerID.idString)"
}
@Environment(ProjectViewValue.$projectID) var projectID
let id: String
let roomID: Room.ID
let rectangularSizeID: DuctSizing.RectangularDuct.ID?
let register: Int
let height: Int?
let room: DuctSizing.RoomContainer
let dismiss: Bool
init(
id: String? = nil,
roomID: Room.ID,
rectangularSizeID: DuctSizing.RectangularDuct.ID? = nil,
register: Int,
height: Int? = nil,
dismiss: Bool = true
) {
self.id = id ?? Self.id(roomID)
self.roomID = roomID
self.rectangularSizeID = rectangularSizeID
self.register = register
self.height = height
self.dismiss = dismiss
}
init(
id: String? = nil,
room: DuctSizing.RoomContainer,
dismiss: Bool = true
) {
let register =
room.rectangularSize?.register
?? (Int("\(room.roomName.last!)") ?? 1)
self.init(
id: id,
roomID: room.roomID,
rectangularSizeID: room.rectangularSize?.id,
register: register,
height: room.rectangularSize?.height,
dismiss: dismiss
)
self.id = Self.id(room)
self.room = room
self.dismiss = dismiss
}
var route: String {
@@ -64,23 +31,30 @@ struct RectangularSizeForm: HTML, Sendable {
for: .project(.detail(projectID, .ductSizing(.index)))
)
.appendingPath("room")
.appendingPath(roomID)
.appendingPath(room.roomID)
}
var rowID: String {
DuctSizingView.RoomRow.id(room)
}
var height: Int? {
room.rectangularSize?.height
}
var body: some HTML<HTMLTag.dialog> {
ModalForm(id: id, dismiss: dismiss) {
h1(.class("text-lg pb-6")) { "Rectangular Size" }
form(
.class("space-y-4"),
.hx.post(route),
.hx.target("closest tr"),
.hx.target("#\(rowID)"),
.hx.swap(.outerHTML)
) {
input(.class("hidden"), .name("register"), .value(register))
input(.class("hidden"), .name("id"), .value(rectangularSizeID))
input(.class("hidden"), .name("register"), .value(room.roomRegister))
input(.class("hidden"), .name("id"), .value(room.rectangularSize?.id))
LabeledInput(
"Height",

View File

@@ -21,10 +21,8 @@ extension DuctSizingView {
th { "Name" }
th { "BTU" }
th { "CFM" }
th(.class("hidden 2xl:table-cell")) { "Round Size" }
th { "Velocity" }
th { "Size" }
th {}
}
}
tbody {
@@ -39,6 +37,10 @@ extension DuctSizingView {
struct RoomRow: HTML, Sendable {
static func id(_ room: DuctSizing.RoomContainer) -> String {
"roomRow_\(room.registerID.idString)"
}
@Environment(ProjectViewValue.$projectID) var projectID
let room: DuctSizing.RoomContainer
@@ -51,14 +53,20 @@ extension DuctSizingView {
for: .project(
.detail(
projectID,
.ductSizing(.deleteRectangularSize(room.roomID, id))
.ductSizing(
.deleteRectangularSize(
room.roomID,
.init(rectangularSizeID: id, register: room.roomRegister)
))
)
)
)
}
var rowID: String { Self.id(room) }
var body: some HTML<HTMLTag.tr> {
tr(.class("text-lg items-baseline"), .id(room.roomID.idString)) {
tr(.class("text-lg items-baseline"), .id(rowID)) {
td { room.registerID }
td { room.roomName }
td {
@@ -75,82 +83,87 @@ extension DuctSizingView {
div(.class("grid grid-cols-2 gap-2")) {
span(.class("label")) { "Design" }
div(.class("flex justify-center")) {
Badge(number: room.designCFM.value, digits: 0)
}
span(.class("label")) { "Heating" }
div(.class("flex justify-center")) {
Number(room.heatingCFM, digits: 0)
}
span(.class("label")) { "Cooling" }
div(.class("flex justify-center")) {
Number(room.coolingCFM, digits: 0)
}
}
}
td(.class("hidden 2xl:table-cell")) { Number(room.roundSize, digits: 1) }
td { Number(room.velocity) }
td {
div(.class("grid grid-cols-2 gap-2")) {
div(.class("grid grid-cols-3 gap-2")) {
span(.class("label")) { "Final" }
div(.class("label")) { "Calculated" }
div(.class("flex justify-center")) {
Badge(number: room.roundSize, digits: 1)
}
div {}
div(.class("label")) { "Final" }
div(.class("flex justify-center")) {
Badge(number: room.finalSize)
.attributes(.class("badge-secondary"))
}
div {}
span(.class("label")) { "Flex" }
div(.class("label")) { "Flex" }
div(.class("flex justify-center")) {
Badge(number: room.flexSize)
.attributes(.class("badge-primary"))
}
div {}
div(.class("label")) { "Rectangular" }
div(.class("flex justify-center")) {
if let width = room.rectangularWidth,
let height = room.rectangularSize?.height
{
span(.class("label")) { "Rectangular" }
Badge {
span { "\(width) x \(height)" }
}
.attributes(.class("badge-info"))
}
}
}
td {
div(.class("flex justify-end space-x-4")) {
div(.class("flex justify-end")) {
div(.class("join")) {
if room.rectangularSize != nil {
// FIX: Delete rectangular size from room.
Tooltip("Delete Size", position: .bottom) {
TrashButton()
.attributes(.class("join-item btn-ghost"))
.attributes(
.hx.delete(deleteRoute),
.hx.target("closest tr"),
.hx.target("#\(rowID)"),
.hx.swap(.outerHTML),
when: room.rectangularSize != nil
)
}
}
Tooltip("Edit Size", position: .bottom) {
EditButton()
.attributes(
.class("join-item btn-ghost"),
.showModal(id: formID)
// .showModal(id: RectangularSizeForm.id(room))
.showModal(id: RectangularSizeForm.id(room))
)
}
}
// FakeForm(id: formID)
RectangularSizeForm(id: formID, room: room)
// .attributes(.class("modal-open"))
}
}
RectangularSizeForm(room: room)
}
}
struct FakeForm: HTML, Sendable {
let id: String
var body: some HTML<HTMLTag.dialog> {
ModalForm(id: id, dismiss: true) {
div { "Fake Form" }
}
}
}