diff --git a/Public/css/output.css b/Public/css/output.css index 7994648..a868615 100644 --- a/Public/css/output.css +++ b/Public/css/output.css @@ -5288,6 +5288,12 @@ } } } + .mx-2 { + margin-inline: calc(var(--spacing) * 2); + } + .mx-4 { + margin-inline: calc(var(--spacing) * 4); + } .mx-auto { margin-inline: auto; } @@ -5377,9 +5383,15 @@ } } } + .my-1 { + margin-block: calc(var(--spacing) * 1); + } .my-1\.5 { margin-block: calc(var(--spacing) * 1.5); } + .my-4 { + margin-block: calc(var(--spacing) * 4); + } .my-6 { margin-block: calc(var(--spacing) * 6); } @@ -6613,6 +6625,12 @@ .w-full { width: 100%; } + .min-w-\[100px\] { + min-width: 100px; + } + .min-w-\[200px\] { + min-width: 200px; + } .min-w-\[220px\] { min-width: 220px; } @@ -6827,6 +6845,9 @@ .flex-col { flex-direction: column; } + .flex-row { + flex-direction: row; + } .flex-wrap { flex-wrap: wrap; } @@ -6915,6 +6936,9 @@ margin-inline-end: calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-x-reverse))); } } + .gap-y-2 { + row-gap: calc(var(--spacing) * 2); + } .gap-y-4 { row-gap: calc(var(--spacing) * 4); } @@ -7915,6 +7939,9 @@ .px-4 { padding-inline: calc(var(--spacing) * 4); } + .py-1 { + padding-block: calc(var(--spacing) * 1); + } .py-1\.5 { padding-block: calc(var(--spacing) * 1.5); } @@ -9527,6 +9554,11 @@ grid-template-columns: repeat(2, minmax(0, 1fr)); } } + .md\:grid-cols-3 { + @media (width >= 48rem) { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + } .lg\:drawer-open { @media (width >= 64rem) { @layer daisyui.l1.l2.l3 { @@ -9580,6 +9612,11 @@ } } } + .lg\:grid-cols-4 { + @media (width >= 64rem) { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + } .is-drawer-close\:mx-auto { &:where(.drawer-toggle:not(:checked) ~ .drawer-side, .drawer-toggle:not(:checked) ~ .drawer-side *) { margin-inline: auto; diff --git a/Sources/DatabaseClient/TrunkSizes.swift b/Sources/DatabaseClient/TrunkSizes.swift index e5c851b..37f9d7c 100644 --- a/Sources/DatabaseClient/TrunkSizes.swift +++ b/Sources/DatabaseClient/TrunkSizes.swift @@ -58,27 +58,12 @@ extension DatabaseClient.TrunkSizes: TestDependencyKey { try await model.delete(on: database) }, fetch: { projectID in - let models = try await TrunkModel.query(on: database) + try await TrunkModel.query(on: database) .with(\.$project) .with(\.$rooms) .filter(\.$project.$id == projectID) .all() - - return try await withThrowingTaskGroup(of: DuctSizing.TrunkSize.self) { group in - for model in models { - group.addTask { - try await model.toDTO(on: database) - } - } - - return try await group.reduce(into: [DuctSizing.TrunkSize]()) { - $0.append($1) - } - } - - // return try await models.map { - // try await $0.toDTO(on: database) - // } + .toDTO(on: database) }, get: { id in guard let model = try await TrunkModel.find(id, on: database) else { @@ -122,7 +107,8 @@ extension DuctSizing.TrunkSize.Create { .init( projectID: projectID, type: type, - height: height + height: height, + name: name ) } } @@ -151,6 +137,7 @@ extension DuctSizing.TrunkSize { try await database.schema(TrunkModel.schema) .id() .field("height", .int8) + .field("name", .string) .field("type", .string, .required) .field( "projectID", .uuid, .required, .references(ProjectModel.schema, "id", onDelete: .cascade) @@ -236,12 +223,15 @@ final class TrunkModel: Model, @unchecked Sendable { @Parent(key: "projectID") var project: ProjectModel - @Field(key: "height") + @OptionalField(key: "height") var height: Int? @Field(key: "type") var type: String + @OptionalField(key: "name") + var name: String? + @Children(for: \.$trunk) var rooms: [TrunkRoomModel] @@ -252,11 +242,13 @@ final class TrunkModel: Model, @unchecked Sendable { projectID: Project.ID, type: DuctSizing.TrunkSize.TrunkType, height: Int? = nil, + name: String? = nil ) { self.id = id $project.id = projectID self.height = height self.type = type.rawValue + self.name = name } func toDTO(on database: any Database) async throws -> DuctSizing.TrunkSize { @@ -278,7 +270,8 @@ final class TrunkModel: Model, @unchecked Sendable { projectID: $project.id, type: .init(rawValue: type)!, rooms: rooms, - height: height + height: height, + name: name ) } @@ -293,6 +286,9 @@ final class TrunkModel: Model, @unchecked Sendable { if let height = updates.height, height != self.height { self.height = height } + if let name = updates.name, name != self.name { + self.name = name + } if hasChanges { try await self.save(on: database) } @@ -341,3 +337,20 @@ final class TrunkModel: Model, @unchecked Sendable { } } + +extension Array where Element == TrunkModel { + + func toDTO(on database: any Database) async throws -> [DuctSizing.TrunkSize] { + try await withThrowingTaskGroup(of: DuctSizing.TrunkSize.self) { group in + for model in self { + group.addTask { + try await model.toDTO(on: database) + } + } + + return try await group.reduce(into: [DuctSizing.TrunkSize]()) { + $0.append($1) + } + } + } +} diff --git a/Sources/ManualDClient/Live.swift b/Sources/ManualDClient/Live.swift index 2b37c81..9088598 100644 --- a/Sources/ManualDClient/Live.swift +++ b/Sources/ManualDClient/Live.swift @@ -25,7 +25,7 @@ extension ManualDClient: DependencyKey { throw ManualDError(message: "Total Effective Length should be greater than 0.") } - let totalComponentLosses = request.componentPressureLosses.totalComponentPressureLoss + let totalComponentLosses = request.componentPressureLosses.total let availableStaticPressure = request.externalStaticPressure - totalComponentLosses let frictionRate = availableStaticPressure * 100.0 / Double(request.totalEffectiveLength) return .init(availableStaticPressure: availableStaticPressure, frictionRate: frictionRate) diff --git a/Sources/ManualDCore/ComponentPressureLosses.swift b/Sources/ManualDCore/ComponentPressureLosses.swift index fa332d9..f316095 100644 --- a/Sources/ManualDCore/ComponentPressureLosses.swift +++ b/Sources/ManualDCore/ComponentPressureLosses.swift @@ -69,7 +69,7 @@ extension ComponentPressureLoss { } extension Array where Element == ComponentPressureLoss { - public var totalComponentPressureLoss: Double { + public var total: Double { reduce(into: 0) { $0 += $1.value } } } diff --git a/Sources/ManualDCore/DuctSizing.swift b/Sources/ManualDCore/DuctSizing.swift index 28b8cf0..6ddecb0 100644 --- a/Sources/ManualDCore/DuctSizing.swift +++ b/Sources/ManualDCore/DuctSizing.swift @@ -51,6 +51,7 @@ public enum DuctSizing { } // TODO: Remove registerID and just use the roomName + // TODO: Uses SizeContainer public struct RoomContainer: Codable, Equatable, Sendable { @@ -164,19 +165,22 @@ extension DuctSizing { public let type: TrunkType public let rooms: [RoomProxy] public let height: Int? + public let name: String? public init( id: UUID, projectID: Project.ID, type: DuctSizing.TrunkSize.TrunkType, rooms: [DuctSizing.TrunkSize.RoomProxy], - height: Int? = nil + height: Int? = nil, + name: String? = nil ) { self.id = id self.projectID = projectID self.type = type self.rooms = rooms self.height = height + self.name = name } } @@ -189,17 +193,20 @@ extension DuctSizing.TrunkSize { public let type: TrunkType public let rooms: [Room.ID: [Int]] public let height: Int? + public let name: String? public init( projectID: Project.ID, type: DuctSizing.TrunkSize.TrunkType, rooms: [Room.ID: [Int]], - height: Int? = nil + height: Int? = nil, + name: String? = nil ) { self.projectID = projectID self.type = type self.rooms = rooms self.height = height + self.name = name } } @@ -208,15 +215,18 @@ extension DuctSizing.TrunkSize { public let type: TrunkType? public let rooms: [Room.ID: [Int]]? public let height: Int? + public let name: String? public init( type: DuctSizing.TrunkSize.TrunkType? = nil, rooms: [Room.ID: [Int]]? = nil, - height: Int? = nil + height: Int? = nil, + name: String? = nil ) { self.type = type self.rooms = rooms self.height = height + self.name = name } } diff --git a/Sources/ManualDCore/Routes/ViewRoute.swift b/Sources/ManualDCore/Routes/ViewRoute.swift index 3ccfe5a..5470251 100644 --- a/Sources/ManualDCore/Routes/ViewRoute.swift +++ b/Sources/ManualDCore/Routes/ViewRoute.swift @@ -693,6 +693,10 @@ extension SiteRoute.View.ProjectRoute { Field("type") { DuctSizing.TrunkSize.TrunkType.parser() } Optionally { Field("height") { Int.parser() } + + } + Optionally { + Field("name", .string) } Many { Field("rooms", .string) @@ -714,6 +718,9 @@ extension SiteRoute.View.ProjectRoute { Optionally { Field("height") { Int.parser() } } + Optionally { + Field("name", .string) + } Many { Field("rooms", .string) } @@ -734,6 +741,7 @@ extension SiteRoute.View.ProjectRoute { public let projectID: Project.ID public let type: DuctSizing.TrunkSize.TrunkType public let height: Int? + public let name: String? public let rooms: [String] } } diff --git a/Sources/Styleguide/Badge.swift b/Sources/Styleguide/Badge.swift index bacfd99..8384ac3 100644 --- a/Sources/Styleguide/Badge.swift +++ b/Sources/Styleguide/Badge.swift @@ -11,7 +11,7 @@ public struct Badge: HTML, Sendable where Inner: Sendable { } public var body: some HTML { - div(.class("badge badge-lg badge-outline font-bold")) { + div(.class("badge badge-lg badge-outline")) { inner } } diff --git a/Sources/Styleguide/ElementaryExtensions.swift b/Sources/Styleguide/ElementaryExtensions.swift index 13d1c2d..9744b1c 100644 --- a/Sources/Styleguide/ElementaryExtensions.swift +++ b/Sources/Styleguide/ElementaryExtensions.swift @@ -43,10 +43,14 @@ extension HTMLAttribute where Tag == HTMLTag.button { extension HTML where Tag: HTMLTrait.Attributes.Global { public func badge() -> _AttributedElement { - attributes(.class("badge badge-lg badge-outline font-bold")) + attributes(.class("badge badge-lg badge-outline")) } public func hidden(when shouldHide: Bool) -> _AttributedElement { attributes(.class("hidden"), when: shouldHide) } + + public func bold(when shouldBeBold: Bool = true) -> _AttributedElement { + attributes(.class("font-bold"), when: shouldBeBold) + } } diff --git a/Sources/ViewController/Extensions/DatabaseExtensions.swift b/Sources/ViewController/Extensions/DatabaseExtensions.swift index 872f981..13888a5 100644 --- a/Sources/ViewController/Extensions/DatabaseExtensions.swift +++ b/Sources/ViewController/Extensions/DatabaseExtensions.swift @@ -53,7 +53,7 @@ extension DatabaseClient { guard componentLosses.count > 0 else { return nil } let availableStaticPressure = - equipmentInfo.staticPressure - componentLosses.totalComponentPressureLoss + equipmentInfo.staticPressure - componentLosses.total let designFrictionRate = (availableStaticPressure * 100) / tel diff --git a/Sources/ViewController/Extensions/TrunkSizeForm+extensions.swift b/Sources/ViewController/Extensions/TrunkSizeForm+extensions.swift index 2212b38..163607f 100644 --- a/Sources/ViewController/Extensions/TrunkSizeForm+extensions.swift +++ b/Sources/ViewController/Extensions/TrunkSizeForm+extensions.swift @@ -9,7 +9,8 @@ extension SiteRoute.View.ProjectRoute.DuctSizingRoute.TrunkSizeForm { projectID: projectID, type: type, rooms: makeRooms(logger: logger), - height: height + height: height, + name: name ) } @@ -17,7 +18,8 @@ extension SiteRoute.View.ProjectRoute.DuctSizingRoute.TrunkSizeForm { try .init( type: type, rooms: makeRooms(logger: logger), - height: height + height: height, + name: name ) } diff --git a/Sources/ViewController/Live.swift b/Sources/ViewController/Live.swift index 7972b8c..c346d6e 100644 --- a/Sources/ViewController/Live.swift +++ b/Sources/ViewController/Live.swift @@ -140,21 +140,23 @@ extension SiteRoute.View.ProjectRoute { } case .create(let form): - return await ResultView { - let user = try request.currentUser() - let project = try await database.projects.create(user.id, form) - try await database.componentLoss.createDefaults(projectID: project.id) - let rooms = try await database.rooms.fetch(project.id) - let shr = try await database.projects.getSensibleHeatRatio(project.id) - let completedSteps = try await database.projects.getCompletedSteps(project.id) - return (project.id, rooms, shr, completedSteps) - } onSuccess: { (projectID, rooms, shr, completedSteps) in - ProjectView( - projectID: projectID, - activeTab: .rooms, - completedSteps: completedSteps - ) { - RoomsView(rooms: rooms, sensibleHeatRatio: shr) + return await request.view { + await ResultView { + let user = try request.currentUser() + let project = try await database.projects.create(user.id, form) + try await database.componentLoss.createDefaults(projectID: project.id) + let rooms = try await database.rooms.fetch(project.id) + let shr = try await database.projects.getSensibleHeatRatio(project.id) + let completedSteps = try await database.projects.getCompletedSteps(project.id) + return (project.id, rooms, shr, completedSteps) + } onSuccess: { (projectID, rooms, shr, completedSteps) in + ProjectView( + projectID: projectID, + activeTab: .rooms, + completedSteps: completedSteps + ) { + RoomsView(rooms: rooms, sensibleHeatRatio: shr) + } } } diff --git a/Sources/ViewController/Views/ComponentLoss/ComponentLossesView.swift b/Sources/ViewController/Views/ComponentLoss/ComponentLossesView.swift index 6146618..6297654 100644 --- a/Sources/ViewController/Views/ComponentLoss/ComponentLossesView.swift +++ b/Sources/ViewController/Views/ComponentLoss/ComponentLossesView.swift @@ -3,24 +3,31 @@ import ElementaryHTMX import ManualDCore import Styleguide -// TODO: Load component losses when view appears?? - struct ComponentPressureLossesView: HTML, Sendable { let componentPressureLosses: [ComponentPressureLoss] let projectID: Project.ID private var total: Double { - componentPressureLosses.reduce(into: 0) { $0 += $1.value } + componentPressureLosses.total + } + + private var sortedLosses: [ComponentPressureLoss] { + componentPressureLosses.sorted { + $0.value > $1.value + } } var body: some HTML { div(.class("space-y-4")) { Row { h1(.class("text-2xl font-bold")) { "Component Pressure Losses" } - LabeledContent("Total") { - Badge(number: total) - } + PlusButton() + .attributes( + .class("btn-primary text-2xl me-2"), + .showModal(id: ComponentLossForm.id()) + ) + .tooltip("Add component loss") } .attributes(.class("px-4")) @@ -29,20 +36,11 @@ struct ComponentPressureLossesView: HTML, Sendable { tr(.class("text-xl font-bold")) { th { "Name" } th { "Value" } - th { - div(.class("flex justify-end mx-auto")) { - PlusButton() - .attributes( - .class("btn-primary text-2xl me-2"), - .showModal(id: ComponentLossForm.id()) - ) - .tooltip("Add component loss") - } - } + th(.class("min-w-[200px]")) {} } } tbody { - for row in componentPressureLosses { + for row in sortedLosses { TableRow(row: row) } } diff --git a/Sources/ViewController/Views/DuctSizing/DuctSizingView.swift b/Sources/ViewController/Views/DuctSizing/DuctSizingView.swift index 0b434c1..1790490 100644 --- a/Sources/ViewController/Views/DuctSizing/DuctSizingView.swift +++ b/Sources/ViewController/Views/DuctSizing/DuctSizingView.swift @@ -13,7 +13,7 @@ struct DuctSizingView: HTML, Sendable { var body: some HTML { div(.class("space-y-4")) { PageTitleRow { - div(.class("space-y-4")) { + div { PageTitle("Duct Sizes") Alert( @@ -22,7 +22,7 @@ struct DuctSizingView: HTML, Sendable { """ ) .hidden(when: rooms.count > 0) - .attributes(.class("text-error font-bold italic")) + .attributes(.class("text-error font-bold italic mt-4")) } } diff --git a/Sources/ViewController/Views/DuctSizing/TrunkSizeForm.swift b/Sources/ViewController/Views/DuctSizing/TrunkSizeForm.swift index a252010..82521dc 100644 --- a/Sources/ViewController/Views/DuctSizing/TrunkSizeForm.swift +++ b/Sources/ViewController/Views/DuctSizing/TrunkSizeForm.swift @@ -40,7 +40,7 @@ struct TrunkSizeForm: HTML, Sendable { var body: some HTML { ModalForm(id: Self.id(container), dismiss: dismiss) { - h1(.class("text-lg font-bold mb-4")) { "Trunk Size" } + h1(.class("text-lg font-bold mb-4")) { "Trunk / Runout Size" } form( .class("space-y-4"), trunk == nil @@ -72,30 +72,49 @@ struct TrunkSizeForm: HTML, Sendable { ) } - // Add room select here. - div(.class("grid grid-cols-5 gap-6")) { - h2(.class("label font-bold col-span-5")) { "Associated Supply Runs" } - for room in rooms { - div(.class("flex justify-center items-center col-span-1")) { - div(.class("grid grid-cols-1 justify-center items-center space-y-1")) { - p(.class("label block")) { room.roomName } - input( - .class("checkbox mx-auto"), - .type(.checkbox), - .name("rooms"), - .value("\(room.roomID)_\(room.roomRegister)") - ) - .attributes( - .checked, - when: trunk == nil ? false : trunk!.rooms.hasRoom(room) - ) + LabeledInput( + "Name", + .type(.text), + .name("name"), + .value(trunk?.name), + .placeholder("Trunk-1 (Optional)") + ) + + div { + h2(.class("label font-bold col-span-3 mb-6")) { "Associated Supply Runs" } + div( + .class( + """ + grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 justify-center items-center gap-4 + """ + ) + ) { + for room in rooms { + div(.class("block grow")) { + div(.class("grid grid-cols-1 space-y-1")) { + div(.class("flex justify-center")) { + p(.class("label")) { room.roomName } + } + div(.class("flex justify-center")) { + input( + .class("checkbox"), + .type(.checkbox), + .name("rooms"), + .value("\(room.roomID)_\(room.roomRegister)") + ) + .attributes( + .checked, + when: trunk == nil ? false : trunk!.rooms.hasRoom(room) + ) + } + } } } } } SubmitButton() - .attributes(.class("btn-block")) + .attributes(.class("btn-block mt-6")) } } } diff --git a/Sources/ViewController/Views/DuctSizing/TrunksTable.swift b/Sources/ViewController/Views/DuctSizing/TrunksTable.swift index 2ecae17..de131b6 100644 --- a/Sources/ViewController/Views/DuctSizing/TrunksTable.swift +++ b/Sources/ViewController/Views/DuctSizing/TrunksTable.swift @@ -11,14 +11,16 @@ extension DuctSizingView { let rooms: [DuctSizing.RoomContainer] private var sortedTrunks: [DuctSizing.TrunkContainer] { - trunks.sorted(by: { $0.type.rawValue > $1.type.rawValue }) + trunks + .sorted(by: { $0.designCFM.value > $1.designCFM.value }) + .sorted(by: { $0.type.rawValue > $1.type.rawValue }) } var body: some HTML { table(.class("table table-zebra text-lg")) { thead { tr(.class("text-lg")) { - th { "Type" } + th { "Name / Type" } th { "Associated Supplies" } th { "Dsn CFM" } th { "Velocity" } @@ -45,11 +47,17 @@ extension DuctSizingView { var body: some HTML { tr { td { - Badge { - trunk.trunk.type.rawValue + div(.class("grid grid-cols-1 space-y-2")) { + if let name = trunk.name { + p(.class("w-fit")) { name } + } + + Badge { + trunk.trunk.type.rawValue + } + .attributes(.class("badge-info"), when: trunk.type == .supply) + .attributes(.class("badge-error"), when: trunk.type == .return) } - .attributes(.class("badge-info"), when: trunk.type == .supply) - .attributes(.class("badge-error"), when: trunk.type == .return) } td { div(.class("flex flex-wrap space-x-2 space-y-2")) { diff --git a/Sources/ViewController/Views/EffectiveLength/EffectiveLengthForm.swift b/Sources/ViewController/Views/EffectiveLength/EffectiveLengthForm.swift index 5b53ae6..daf818d 100644 --- a/Sources/ViewController/Views/EffectiveLength/EffectiveLengthForm.swift +++ b/Sources/ViewController/Views/EffectiveLength/EffectiveLengthForm.swift @@ -331,7 +331,7 @@ struct GroupTypeSelect: HTML, Sendable { let selected: EffectiveLength.EffectiveLengthType var body: some HTML { - label(.class("select")) { + label(.class("select w-full")) { span(.class("label")) { "Type" } select(.name("type"), .id("type")) { for value in EffectiveLength.EffectiveLengthType.allCases { diff --git a/Sources/ViewController/Views/EffectiveLength/EffectiveLengthsTable.swift b/Sources/ViewController/Views/EffectiveLength/EffectiveLengthsTable.swift index a0a61fd..3c42ccb 100644 --- a/Sources/ViewController/Views/EffectiveLength/EffectiveLengthsTable.swift +++ b/Sources/ViewController/Views/EffectiveLength/EffectiveLengthsTable.swift @@ -105,7 +105,7 @@ struct EffectiveLengthsTable: HTML, Sendable { // Row { div(.class("flex justify-end mx-auto space-x-4")) { Badge(number: effectiveLength.totalEquivalentLength, digits: 0) - .attributes(.class("badge-primary text-xl pt-2")) + .attributes(.class("badge-primary badge-lg pt-2")) // Buttons div(.class("flex justify-end -mt-2")) { @@ -118,11 +118,14 @@ struct EffectiveLengthsTable: HTML, Sendable { .hx.target("#\(effectiveLength.id.idString)"), .hx.swap(.outerHTML) ) + .tooltip("Delete", position: .bottom) + EditButton() .attributes( .class("join-item btn-ghost"), .showModal(id: EffectiveLengthForm.id(effectiveLength)) ) + .tooltip("Edit", position: .bottom) } } } diff --git a/Sources/ViewController/Views/EffectiveLength/EffectiveLengthsView.swift b/Sources/ViewController/Views/EffectiveLength/EffectiveLengthsView.swift index a0ea302..afe7bbf 100644 --- a/Sources/ViewController/Views/EffectiveLength/EffectiveLengthsView.swift +++ b/Sources/ViewController/Views/EffectiveLength/EffectiveLengthsView.swift @@ -38,102 +38,4 @@ struct EffectiveLengthsView: HTML, Sendable { } } - - // TODO: Remove if using table view. - private struct EffectiveLengthView: HTML, Sendable { - - let effectiveLength: EffectiveLength - - var straightLengthsTotal: Int { - effectiveLength.straightLengths - .reduce(into: 0) { $0 += $1 } - } - - var groupsTotal: Double { - effectiveLength.groups.totalEquivalentLength - } - - var id: String { - return "effectiveLenghtCard_\(effectiveLength.id.uuidString.replacing("-", with: ""))" - } - - var body: some HTML { - div( - .class("card h-full bg-base-100 shadow-sm border rounded-lg"), - .id(id) - ) { - div(.class("card-body text-lg")) { - Row { - h2 { effectiveLength.name } - div( - .class("space-x-4") - ) { - span(.class("text-primary text-sm italic")) { - "Total" - } - - Number(self.effectiveLength.totalEquivalentLength, digits: 0) - .attributes(.class("badge badge-outline badge-primary text-lg")) - } - } - .attributes(.class("card-title pb-6")) - - Row { - Label { "Straight Lengths" } - - ul { - for length in effectiveLength.straightLengths { - li { - Number(length) - } - } - } - } - .attributes(.class("pb-6")) - - Row { - span { "Groups" } - span { "Equivalent Length" } - span { "Quantity" } - } - .attributes(.class("label font-bold border-b border-label")) - - for group in effectiveLength.groups { - Row { - span { "\(group.group)-\(group.letter)" } - Number(group.value) - Number(group.quantity) - } - } - - div(.class("card-actions justify-end pt-6 space-y-4 mt-auto")) { - div(.class("join")) { - TrashButton() - .attributes( - .class("join-item btn-ghost"), - .hx.delete( - route: .project( - .detail( - effectiveLength.projectID, - .equivalentLength(.delete(id: effectiveLength.id)) - ) - ) - ), - .hx.confirm("Are you sure?"), - .hx.target("#\(id)"), - .hx.swap(.outerHTML) - ) - EditButton() - .attributes( - .class("join-item btn-ghost"), - .showModal(id: EffectiveLengthForm.id(effectiveLength)) - ) - } - } - - EffectiveLengthForm(effectiveLength: effectiveLength) - } - } - } - } } diff --git a/Sources/ViewController/Views/EquipmentInfo/EquipmentInfoForm.swift b/Sources/ViewController/Views/EquipmentInfo/EquipmentInfoForm.swift index a5b8e79..55bea9c 100644 --- a/Sources/ViewController/Views/EquipmentInfo/EquipmentInfoForm.swift +++ b/Sources/ViewController/Views/EquipmentInfo/EquipmentInfoForm.swift @@ -7,8 +7,9 @@ struct EquipmentInfoForm: HTML, Sendable { static let id = "equipmentForm" + @Environment(ProjectViewValue.$projectID) var projectID + let dismiss: Bool - let projectID: Project.ID let equipmentInfo: EquipmentInfo? var staticPressure: String { diff --git a/Sources/ViewController/Views/EquipmentInfo/EquipmentInfoView.swift b/Sources/ViewController/Views/EquipmentInfo/EquipmentInfoView.swift index 8d2db0d..11d8045 100644 --- a/Sources/ViewController/Views/EquipmentInfo/EquipmentInfoView.swift +++ b/Sources/ViewController/Views/EquipmentInfo/EquipmentInfoView.swift @@ -55,7 +55,8 @@ struct EquipmentInfoView: HTML, Sendable { } } EquipmentInfoForm( - dismiss: true, projectID: projectID, equipmentInfo: equipmentInfo + dismiss: equipmentInfo != nil, + equipmentInfo: equipmentInfo ) } } diff --git a/Sources/ViewController/Views/FrictionRate/FrictionRateView.swift b/Sources/ViewController/Views/FrictionRate/FrictionRateView.swift index 5d18d5d..2a68af5 100644 --- a/Sources/ViewController/Views/FrictionRate/FrictionRateView.swift +++ b/Sources/ViewController/Views/FrictionRate/FrictionRateView.swift @@ -3,8 +3,6 @@ import ManualDClient import ManualDCore import Styleguide -// FIX: Need to update available static, etc. when equipment info is submitted. - struct FrictionRateView: HTML, Sendable { @Environment(ProjectViewValue.$projectID) var projectID @@ -13,16 +11,20 @@ struct FrictionRateView: HTML, Sendable { let equivalentLengths: EffectiveLength.MaxContainer let frictionRateResponse: ManualDClient.FrictionRateResponse? - var availableStaticPressure: Double? { + private var availableStaticPressure: Double? { frictionRateResponse?.availableStaticPressure } - var frictionRateDesignValue: Double? { + private var frictionRateDesignValue: Double? { frictionRateResponse?.frictionRate } - var badgeColor: String { - let base = "badge-primary" + private var shouldShowBadges: Bool { + frictionRateDesignValue != nil || availableStaticPressure != nil + } + + private var badgeColor: String { + let base = "badge-info" guard let frictionRateDesignValue else { return base } if frictionRateDesignValue >= 0.18 || frictionRateDesignValue <= 0.02 { return "badge-error" @@ -30,38 +32,68 @@ struct FrictionRateView: HTML, Sendable { return base } - var showHighErrors: Bool { + private var showHighErrors: Bool { guard let frictionRateDesignValue else { return false } return frictionRateDesignValue >= 0.18 } - var showLowErrors: Bool { + private var showLowErrors: Bool { guard let frictionRateDesignValue else { return false } return frictionRateDesignValue <= 0.02 } + private var showNoComponentLossesError: Bool { + componentLosses.count == 0 + } + + private var showIncompleteSectionsError: Bool { + availableStaticPressure == nil || frictionRateDesignValue == nil + } + + private var hasAlerts: Bool { + showLowErrors + || showHighErrors + || showNoComponentLossesError + || showIncompleteSectionsError + + } + var body: some HTML { div(.class("space-y-6")) { PageTitleRow { - div(.class("grid grid-cols-2 px-4 gap-y-4")) { + div(.class("grid grid-cols-2 px-4 w-full")) { PageTitle { "Friction Rate" } - div(.class("space-y-4 justify-end")) { + div(.class("space-y-2 justify-end font-bold text-lg")) { + if shouldShowBadges { - if let frictionRateDesignValue { - LabeledContent("Friction Rate Design Value") { - Badge(number: frictionRateDesignValue, digits: 2) - .attributes(.class("\(badgeColor)")) + if let frictionRateDesignValue { + LabeledContent { + span { "Friction Rate Design Value" } + } content: { + Badge(number: frictionRateDesignValue, digits: 2) + .attributes(.class("\(badgeColor) badge-lg")) + .bold() + } + .attributes(.class("justify-end mx-auto")) } - .attributes(.class("justify-end")) - } - if let availableStaticPressure { - LabeledContent("Available Static Pressure") { - Badge(number: availableStaticPressure, digits: 2) + if let availableStaticPressure { + LabeledContent { + span { "Available Static Pressure" } + } content: { + Badge(number: availableStaticPressure, digits: 2) + } + .attributes(.class("justify-end mx-auto")) } - .attributes(.class("justify-end")) + + LabeledContent { + span { "Component Pressure Losses" } + } content: { + Badge(number: componentLosses.total, digits: 2) + } + .attributes(.class("justify-end mx-auto")) } } @@ -71,16 +103,14 @@ struct FrictionRateView: HTML, Sendable { "Must complete previous sections." } } - .hidden( - when: availableStaticPressure != nil && frictionRateDesignValue != nil - ) + .hidden(when: !showIncompleteSectionsError) Alert { p { "No component pressures losses" } } - .hidden(when: componentLosses.totalComponentPressureLoss > 0) + .hidden(when: !showNoComponentLossesError) Alert { p(.class("block")) { @@ -107,7 +137,9 @@ struct FrictionRateView: HTML, Sendable { } } .hidden(when: !showHighErrors) + } + .attributes(.class("mt-4"), when: hasAlerts) } } diff --git a/Sources/ViewController/Views/Rooms/RoomsView.swift b/Sources/ViewController/Views/Rooms/RoomsView.swift index 1df10b0..ed86c4c 100644 --- a/Sources/ViewController/Views/Rooms/RoomsView.swift +++ b/Sources/ViewController/Views/Rooms/RoomsView.swift @@ -65,7 +65,10 @@ struct RoomsView: HTML, Sendable { } } - SHRForm(projectID: projectID, sensibleHeatRatio: sensibleHeatRatio) + SHRForm( + sensibleHeatRatio: sensibleHeatRatio, + dismiss: sensibleHeatRatio != nil + ) table(.class("table table-zebra text-lg"), .id("roomsTable")) { thead { @@ -194,8 +197,9 @@ struct RoomsView: HTML, Sendable { struct SHRForm: HTML, Sendable { static let id = "shrForm" - let projectID: Project.ID + @Environment(ProjectViewValue.$projectID) var projectID let sensibleHeatRatio: Double? + let dismiss: Bool var route: String { SiteRoute.View.router @@ -204,7 +208,7 @@ struct RoomsView: HTML, Sendable { } var body: some HTML { - ModalForm(id: Self.id, dismiss: true) { + ModalForm(id: Self.id, dismiss: dismiss) { h1(.class("text-xl font-bold mb-6")) { "Sensible Heat Ratio" }