feat: Style updates, begins adding name/label to trunk sizes. Need to remove register id.

This commit is contained in:
2026-01-15 13:00:46 -05:00
parent 7471e11bd2
commit 4ecd4dba7b
22 changed files with 262 additions and 218 deletions

View File

@@ -5288,6 +5288,12 @@
} }
} }
} }
.mx-2 {
margin-inline: calc(var(--spacing) * 2);
}
.mx-4 {
margin-inline: calc(var(--spacing) * 4);
}
.mx-auto { .mx-auto {
margin-inline: auto; margin-inline: auto;
} }
@@ -5377,9 +5383,15 @@
} }
} }
} }
.my-1 {
margin-block: calc(var(--spacing) * 1);
}
.my-1\.5 { .my-1\.5 {
margin-block: calc(var(--spacing) * 1.5); margin-block: calc(var(--spacing) * 1.5);
} }
.my-4 {
margin-block: calc(var(--spacing) * 4);
}
.my-6 { .my-6 {
margin-block: calc(var(--spacing) * 6); margin-block: calc(var(--spacing) * 6);
} }
@@ -6613,6 +6625,12 @@
.w-full { .w-full {
width: 100%; width: 100%;
} }
.min-w-\[100px\] {
min-width: 100px;
}
.min-w-\[200px\] {
min-width: 200px;
}
.min-w-\[220px\] { .min-w-\[220px\] {
min-width: 220px; min-width: 220px;
} }
@@ -6827,6 +6845,9 @@
.flex-col { .flex-col {
flex-direction: column; flex-direction: column;
} }
.flex-row {
flex-direction: row;
}
.flex-wrap { .flex-wrap {
flex-wrap: wrap; flex-wrap: wrap;
} }
@@ -6915,6 +6936,9 @@
margin-inline-end: calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-x-reverse))); 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 { .gap-y-4 {
row-gap: calc(var(--spacing) * 4); row-gap: calc(var(--spacing) * 4);
} }
@@ -7915,6 +7939,9 @@
.px-4 { .px-4 {
padding-inline: calc(var(--spacing) * 4); padding-inline: calc(var(--spacing) * 4);
} }
.py-1 {
padding-block: calc(var(--spacing) * 1);
}
.py-1\.5 { .py-1\.5 {
padding-block: calc(var(--spacing) * 1.5); padding-block: calc(var(--spacing) * 1.5);
} }
@@ -9527,6 +9554,11 @@
grid-template-columns: repeat(2, minmax(0, 1fr)); 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 { .lg\:drawer-open {
@media (width >= 64rem) { @media (width >= 64rem) {
@layer daisyui.l1.l2.l3 { @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 { .is-drawer-close\:mx-auto {
&:where(.drawer-toggle:not(:checked) ~ .drawer-side, .drawer-toggle:not(:checked) ~ .drawer-side *) { &:where(.drawer-toggle:not(:checked) ~ .drawer-side, .drawer-toggle:not(:checked) ~ .drawer-side *) {
margin-inline: auto; margin-inline: auto;

View File

@@ -58,27 +58,12 @@ extension DatabaseClient.TrunkSizes: TestDependencyKey {
try await model.delete(on: database) try await model.delete(on: database)
}, },
fetch: { projectID in fetch: { projectID in
let models = try await TrunkModel.query(on: database) try await TrunkModel.query(on: database)
.with(\.$project) .with(\.$project)
.with(\.$rooms) .with(\.$rooms)
.filter(\.$project.$id == projectID) .filter(\.$project.$id == projectID)
.all() .all()
.toDTO(on: database)
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)
// }
}, },
get: { id in get: { id in
guard let model = try await TrunkModel.find(id, on: database) else { guard let model = try await TrunkModel.find(id, on: database) else {
@@ -122,7 +107,8 @@ extension DuctSizing.TrunkSize.Create {
.init( .init(
projectID: projectID, projectID: projectID,
type: type, type: type,
height: height height: height,
name: name
) )
} }
} }
@@ -151,6 +137,7 @@ extension DuctSizing.TrunkSize {
try await database.schema(TrunkModel.schema) try await database.schema(TrunkModel.schema)
.id() .id()
.field("height", .int8) .field("height", .int8)
.field("name", .string)
.field("type", .string, .required) .field("type", .string, .required)
.field( .field(
"projectID", .uuid, .required, .references(ProjectModel.schema, "id", onDelete: .cascade) "projectID", .uuid, .required, .references(ProjectModel.schema, "id", onDelete: .cascade)
@@ -236,12 +223,15 @@ final class TrunkModel: Model, @unchecked Sendable {
@Parent(key: "projectID") @Parent(key: "projectID")
var project: ProjectModel var project: ProjectModel
@Field(key: "height") @OptionalField(key: "height")
var height: Int? var height: Int?
@Field(key: "type") @Field(key: "type")
var type: String var type: String
@OptionalField(key: "name")
var name: String?
@Children(for: \.$trunk) @Children(for: \.$trunk)
var rooms: [TrunkRoomModel] var rooms: [TrunkRoomModel]
@@ -252,11 +242,13 @@ final class TrunkModel: Model, @unchecked Sendable {
projectID: Project.ID, projectID: Project.ID,
type: DuctSizing.TrunkSize.TrunkType, type: DuctSizing.TrunkSize.TrunkType,
height: Int? = nil, height: Int? = nil,
name: String? = nil
) { ) {
self.id = id self.id = id
$project.id = projectID $project.id = projectID
self.height = height self.height = height
self.type = type.rawValue self.type = type.rawValue
self.name = name
} }
func toDTO(on database: any Database) async throws -> DuctSizing.TrunkSize { func toDTO(on database: any Database) async throws -> DuctSizing.TrunkSize {
@@ -278,7 +270,8 @@ final class TrunkModel: Model, @unchecked Sendable {
projectID: $project.id, projectID: $project.id,
type: .init(rawValue: type)!, type: .init(rawValue: type)!,
rooms: rooms, 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 { if let height = updates.height, height != self.height {
self.height = height self.height = height
} }
if let name = updates.name, name != self.name {
self.name = name
}
if hasChanges { if hasChanges {
try await self.save(on: database) 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)
}
}
}
}

View File

@@ -25,7 +25,7 @@ extension ManualDClient: DependencyKey {
throw ManualDError(message: "Total Effective Length should be greater than 0.") 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 availableStaticPressure = request.externalStaticPressure - totalComponentLosses
let frictionRate = availableStaticPressure * 100.0 / Double(request.totalEffectiveLength) let frictionRate = availableStaticPressure * 100.0 / Double(request.totalEffectiveLength)
return .init(availableStaticPressure: availableStaticPressure, frictionRate: frictionRate) return .init(availableStaticPressure: availableStaticPressure, frictionRate: frictionRate)

View File

@@ -69,7 +69,7 @@ extension ComponentPressureLoss {
} }
extension Array where Element == ComponentPressureLoss { extension Array where Element == ComponentPressureLoss {
public var totalComponentPressureLoss: Double { public var total: Double {
reduce(into: 0) { $0 += $1.value } reduce(into: 0) { $0 += $1.value }
} }
} }

View File

@@ -51,6 +51,7 @@ public enum DuctSizing {
} }
// TODO: Remove registerID and just use the roomName // TODO: Remove registerID and just use the roomName
// TODO: Uses SizeContainer
public struct RoomContainer: Codable, Equatable, Sendable { public struct RoomContainer: Codable, Equatable, Sendable {
@@ -164,19 +165,22 @@ extension DuctSizing {
public let type: TrunkType public let type: TrunkType
public let rooms: [RoomProxy] public let rooms: [RoomProxy]
public let height: Int? public let height: Int?
public let name: String?
public init( public init(
id: UUID, id: UUID,
projectID: Project.ID, projectID: Project.ID,
type: DuctSizing.TrunkSize.TrunkType, type: DuctSizing.TrunkSize.TrunkType,
rooms: [DuctSizing.TrunkSize.RoomProxy], rooms: [DuctSizing.TrunkSize.RoomProxy],
height: Int? = nil height: Int? = nil,
name: String? = nil
) { ) {
self.id = id self.id = id
self.projectID = projectID self.projectID = projectID
self.type = type self.type = type
self.rooms = rooms self.rooms = rooms
self.height = height self.height = height
self.name = name
} }
} }
@@ -189,17 +193,20 @@ extension DuctSizing.TrunkSize {
public let type: TrunkType public let type: TrunkType
public let rooms: [Room.ID: [Int]] public let rooms: [Room.ID: [Int]]
public let height: Int? public let height: Int?
public let name: String?
public init( public init(
projectID: Project.ID, projectID: Project.ID,
type: DuctSizing.TrunkSize.TrunkType, type: DuctSizing.TrunkSize.TrunkType,
rooms: [Room.ID: [Int]], rooms: [Room.ID: [Int]],
height: Int? = nil height: Int? = nil,
name: String? = nil
) { ) {
self.projectID = projectID self.projectID = projectID
self.type = type self.type = type
self.rooms = rooms self.rooms = rooms
self.height = height self.height = height
self.name = name
} }
} }
@@ -208,15 +215,18 @@ extension DuctSizing.TrunkSize {
public let type: TrunkType? public let type: TrunkType?
public let rooms: [Room.ID: [Int]]? public let rooms: [Room.ID: [Int]]?
public let height: Int? public let height: Int?
public let name: String?
public init( public init(
type: DuctSizing.TrunkSize.TrunkType? = nil, type: DuctSizing.TrunkSize.TrunkType? = nil,
rooms: [Room.ID: [Int]]? = nil, rooms: [Room.ID: [Int]]? = nil,
height: Int? = nil height: Int? = nil,
name: String? = nil
) { ) {
self.type = type self.type = type
self.rooms = rooms self.rooms = rooms
self.height = height self.height = height
self.name = name
} }
} }

View File

@@ -693,6 +693,10 @@ extension SiteRoute.View.ProjectRoute {
Field("type") { DuctSizing.TrunkSize.TrunkType.parser() } Field("type") { DuctSizing.TrunkSize.TrunkType.parser() }
Optionally { Optionally {
Field("height") { Int.parser() } Field("height") { Int.parser() }
}
Optionally {
Field("name", .string)
} }
Many { Many {
Field("rooms", .string) Field("rooms", .string)
@@ -714,6 +718,9 @@ extension SiteRoute.View.ProjectRoute {
Optionally { Optionally {
Field("height") { Int.parser() } Field("height") { Int.parser() }
} }
Optionally {
Field("name", .string)
}
Many { Many {
Field("rooms", .string) Field("rooms", .string)
} }
@@ -734,6 +741,7 @@ extension SiteRoute.View.ProjectRoute {
public let projectID: Project.ID public let projectID: Project.ID
public let type: DuctSizing.TrunkSize.TrunkType public let type: DuctSizing.TrunkSize.TrunkType
public let height: Int? public let height: Int?
public let name: String?
public let rooms: [String] public let rooms: [String]
} }
} }

View File

@@ -11,7 +11,7 @@ public struct Badge<Inner: HTML>: HTML, Sendable where Inner: Sendable {
} }
public var body: some HTML<HTMLTag.div> { public var body: some HTML<HTMLTag.div> {
div(.class("badge badge-lg badge-outline font-bold")) { div(.class("badge badge-lg badge-outline")) {
inner inner
} }
} }

View File

@@ -43,10 +43,14 @@ extension HTMLAttribute where Tag == HTMLTag.button {
extension HTML where Tag: HTMLTrait.Attributes.Global { extension HTML where Tag: HTMLTrait.Attributes.Global {
public func badge() -> _AttributedElement<Self> { public func badge() -> _AttributedElement<Self> {
attributes(.class("badge badge-lg badge-outline font-bold")) attributes(.class("badge badge-lg badge-outline"))
} }
public func hidden(when shouldHide: Bool) -> _AttributedElement<Self> { public func hidden(when shouldHide: Bool) -> _AttributedElement<Self> {
attributes(.class("hidden"), when: shouldHide) attributes(.class("hidden"), when: shouldHide)
} }
public func bold(when shouldBeBold: Bool = true) -> _AttributedElement<Self> {
attributes(.class("font-bold"), when: shouldBeBold)
}
} }

View File

@@ -53,7 +53,7 @@ extension DatabaseClient {
guard componentLosses.count > 0 else { return nil } guard componentLosses.count > 0 else { return nil }
let availableStaticPressure = let availableStaticPressure =
equipmentInfo.staticPressure - componentLosses.totalComponentPressureLoss equipmentInfo.staticPressure - componentLosses.total
let designFrictionRate = (availableStaticPressure * 100) / tel let designFrictionRate = (availableStaticPressure * 100) / tel

View File

@@ -9,7 +9,8 @@ extension SiteRoute.View.ProjectRoute.DuctSizingRoute.TrunkSizeForm {
projectID: projectID, projectID: projectID,
type: type, type: type,
rooms: makeRooms(logger: logger), rooms: makeRooms(logger: logger),
height: height height: height,
name: name
) )
} }
@@ -17,7 +18,8 @@ extension SiteRoute.View.ProjectRoute.DuctSizingRoute.TrunkSizeForm {
try .init( try .init(
type: type, type: type,
rooms: makeRooms(logger: logger), rooms: makeRooms(logger: logger),
height: height height: height,
name: name
) )
} }

View File

@@ -140,21 +140,23 @@ extension SiteRoute.View.ProjectRoute {
} }
case .create(let form): case .create(let form):
return await ResultView { return await request.view {
let user = try request.currentUser() await ResultView {
let project = try await database.projects.create(user.id, form) let user = try request.currentUser()
try await database.componentLoss.createDefaults(projectID: project.id) let project = try await database.projects.create(user.id, form)
let rooms = try await database.rooms.fetch(project.id) try await database.componentLoss.createDefaults(projectID: project.id)
let shr = try await database.projects.getSensibleHeatRatio(project.id) let rooms = try await database.rooms.fetch(project.id)
let completedSteps = try await database.projects.getCompletedSteps(project.id) let shr = try await database.projects.getSensibleHeatRatio(project.id)
return (project.id, rooms, shr, completedSteps) let completedSteps = try await database.projects.getCompletedSteps(project.id)
} onSuccess: { (projectID, rooms, shr, completedSteps) in return (project.id, rooms, shr, completedSteps)
ProjectView( } onSuccess: { (projectID, rooms, shr, completedSteps) in
projectID: projectID, ProjectView(
activeTab: .rooms, projectID: projectID,
completedSteps: completedSteps activeTab: .rooms,
) { completedSteps: completedSteps
RoomsView(rooms: rooms, sensibleHeatRatio: shr) ) {
RoomsView(rooms: rooms, sensibleHeatRatio: shr)
}
} }
} }

View File

@@ -3,24 +3,31 @@ import ElementaryHTMX
import ManualDCore import ManualDCore
import Styleguide import Styleguide
// TODO: Load component losses when view appears??
struct ComponentPressureLossesView: HTML, Sendable { struct ComponentPressureLossesView: HTML, Sendable {
let componentPressureLosses: [ComponentPressureLoss] let componentPressureLosses: [ComponentPressureLoss]
let projectID: Project.ID let projectID: Project.ID
private var total: Double { 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 { var body: some HTML {
div(.class("space-y-4")) { div(.class("space-y-4")) {
Row { Row {
h1(.class("text-2xl font-bold")) { "Component Pressure Losses" } h1(.class("text-2xl font-bold")) { "Component Pressure Losses" }
LabeledContent("Total") { PlusButton()
Badge(number: total) .attributes(
} .class("btn-primary text-2xl me-2"),
.showModal(id: ComponentLossForm.id())
)
.tooltip("Add component loss")
} }
.attributes(.class("px-4")) .attributes(.class("px-4"))
@@ -29,20 +36,11 @@ struct ComponentPressureLossesView: HTML, Sendable {
tr(.class("text-xl font-bold")) { tr(.class("text-xl font-bold")) {
th { "Name" } th { "Name" }
th { "Value" } th { "Value" }
th { th(.class("min-w-[200px]")) {}
div(.class("flex justify-end mx-auto")) {
PlusButton()
.attributes(
.class("btn-primary text-2xl me-2"),
.showModal(id: ComponentLossForm.id())
)
.tooltip("Add component loss")
}
}
} }
} }
tbody { tbody {
for row in componentPressureLosses { for row in sortedLosses {
TableRow(row: row) TableRow(row: row)
} }
} }

View File

@@ -13,7 +13,7 @@ struct DuctSizingView: HTML, Sendable {
var body: some HTML { var body: some HTML {
div(.class("space-y-4")) { div(.class("space-y-4")) {
PageTitleRow { PageTitleRow {
div(.class("space-y-4")) { div {
PageTitle("Duct Sizes") PageTitle("Duct Sizes")
Alert( Alert(
@@ -22,7 +22,7 @@ struct DuctSizingView: HTML, Sendable {
""" """
) )
.hidden(when: rooms.count > 0) .hidden(when: rooms.count > 0)
.attributes(.class("text-error font-bold italic")) .attributes(.class("text-error font-bold italic mt-4"))
} }
} }

View File

@@ -40,7 +40,7 @@ struct TrunkSizeForm: HTML, Sendable {
var body: some HTML { var body: some HTML {
ModalForm(id: Self.id(container), dismiss: dismiss) { 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( form(
.class("space-y-4"), .class("space-y-4"),
trunk == nil trunk == nil
@@ -72,30 +72,49 @@ struct TrunkSizeForm: HTML, Sendable {
) )
} }
// Add room select here. LabeledInput(
div(.class("grid grid-cols-5 gap-6")) { "Name",
h2(.class("label font-bold col-span-5")) { "Associated Supply Runs" } .type(.text),
for room in rooms { .name("name"),
div(.class("flex justify-center items-center col-span-1")) { .value(trunk?.name),
div(.class("grid grid-cols-1 justify-center items-center space-y-1")) { .placeholder("Trunk-1 (Optional)")
p(.class("label block")) { room.roomName } )
input(
.class("checkbox mx-auto"), div {
.type(.checkbox), h2(.class("label font-bold col-span-3 mb-6")) { "Associated Supply Runs" }
.name("rooms"), div(
.value("\(room.roomID)_\(room.roomRegister)") .class(
) """
.attributes( grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 justify-center items-center gap-4
.checked, """
when: trunk == nil ? false : trunk!.rooms.hasRoom(room) )
) ) {
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() SubmitButton()
.attributes(.class("btn-block")) .attributes(.class("btn-block mt-6"))
} }
} }
} }

View File

@@ -11,14 +11,16 @@ extension DuctSizingView {
let rooms: [DuctSizing.RoomContainer] let rooms: [DuctSizing.RoomContainer]
private var sortedTrunks: [DuctSizing.TrunkContainer] { 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 { var body: some HTML {
table(.class("table table-zebra text-lg")) { table(.class("table table-zebra text-lg")) {
thead { thead {
tr(.class("text-lg")) { tr(.class("text-lg")) {
th { "Type" } th { "Name / Type" }
th { "Associated Supplies" } th { "Associated Supplies" }
th { "Dsn CFM" } th { "Dsn CFM" }
th { "Velocity" } th { "Velocity" }
@@ -45,11 +47,17 @@ extension DuctSizingView {
var body: some HTML<HTMLTag.tr> { var body: some HTML<HTMLTag.tr> {
tr { tr {
td { td {
Badge { div(.class("grid grid-cols-1 space-y-2")) {
trunk.trunk.type.rawValue 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 { td {
div(.class("flex flex-wrap space-x-2 space-y-2")) { div(.class("flex flex-wrap space-x-2 space-y-2")) {

View File

@@ -331,7 +331,7 @@ struct GroupTypeSelect: HTML, Sendable {
let selected: EffectiveLength.EffectiveLengthType let selected: EffectiveLength.EffectiveLengthType
var body: some HTML<HTMLTag.label> { var body: some HTML<HTMLTag.label> {
label(.class("select")) { label(.class("select w-full")) {
span(.class("label")) { "Type" } span(.class("label")) { "Type" }
select(.name("type"), .id("type")) { select(.name("type"), .id("type")) {
for value in EffectiveLength.EffectiveLengthType.allCases { for value in EffectiveLength.EffectiveLengthType.allCases {

View File

@@ -105,7 +105,7 @@ struct EffectiveLengthsTable: HTML, Sendable {
// Row { // Row {
div(.class("flex justify-end mx-auto space-x-4")) { div(.class("flex justify-end mx-auto space-x-4")) {
Badge(number: effectiveLength.totalEquivalentLength, digits: 0) Badge(number: effectiveLength.totalEquivalentLength, digits: 0)
.attributes(.class("badge-primary text-xl pt-2")) .attributes(.class("badge-primary badge-lg pt-2"))
// Buttons // Buttons
div(.class("flex justify-end -mt-2")) { div(.class("flex justify-end -mt-2")) {
@@ -118,11 +118,14 @@ struct EffectiveLengthsTable: HTML, Sendable {
.hx.target("#\(effectiveLength.id.idString)"), .hx.target("#\(effectiveLength.id.idString)"),
.hx.swap(.outerHTML) .hx.swap(.outerHTML)
) )
.tooltip("Delete", position: .bottom)
EditButton() EditButton()
.attributes( .attributes(
.class("join-item btn-ghost"), .class("join-item btn-ghost"),
.showModal(id: EffectiveLengthForm.id(effectiveLength)) .showModal(id: EffectiveLengthForm.id(effectiveLength))
) )
.tooltip("Edit", position: .bottom)
} }
} }
} }

View File

@@ -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<HTMLTag.div> {
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)
}
}
}
}
} }

View File

@@ -7,8 +7,9 @@ struct EquipmentInfoForm: HTML, Sendable {
static let id = "equipmentForm" static let id = "equipmentForm"
@Environment(ProjectViewValue.$projectID) var projectID
let dismiss: Bool let dismiss: Bool
let projectID: Project.ID
let equipmentInfo: EquipmentInfo? let equipmentInfo: EquipmentInfo?
var staticPressure: String { var staticPressure: String {

View File

@@ -55,7 +55,8 @@ struct EquipmentInfoView: HTML, Sendable {
} }
} }
EquipmentInfoForm( EquipmentInfoForm(
dismiss: true, projectID: projectID, equipmentInfo: equipmentInfo dismiss: equipmentInfo != nil,
equipmentInfo: equipmentInfo
) )
} }
} }

View File

@@ -3,8 +3,6 @@ import ManualDClient
import ManualDCore import ManualDCore
import Styleguide import Styleguide
// FIX: Need to update available static, etc. when equipment info is submitted.
struct FrictionRateView: HTML, Sendable { struct FrictionRateView: HTML, Sendable {
@Environment(ProjectViewValue.$projectID) var projectID @Environment(ProjectViewValue.$projectID) var projectID
@@ -13,16 +11,20 @@ struct FrictionRateView: HTML, Sendable {
let equivalentLengths: EffectiveLength.MaxContainer let equivalentLengths: EffectiveLength.MaxContainer
let frictionRateResponse: ManualDClient.FrictionRateResponse? let frictionRateResponse: ManualDClient.FrictionRateResponse?
var availableStaticPressure: Double? { private var availableStaticPressure: Double? {
frictionRateResponse?.availableStaticPressure frictionRateResponse?.availableStaticPressure
} }
var frictionRateDesignValue: Double? { private var frictionRateDesignValue: Double? {
frictionRateResponse?.frictionRate frictionRateResponse?.frictionRate
} }
var badgeColor: String { private var shouldShowBadges: Bool {
let base = "badge-primary" frictionRateDesignValue != nil || availableStaticPressure != nil
}
private var badgeColor: String {
let base = "badge-info"
guard let frictionRateDesignValue else { return base } guard let frictionRateDesignValue else { return base }
if frictionRateDesignValue >= 0.18 || frictionRateDesignValue <= 0.02 { if frictionRateDesignValue >= 0.18 || frictionRateDesignValue <= 0.02 {
return "badge-error" return "badge-error"
@@ -30,38 +32,68 @@ struct FrictionRateView: HTML, Sendable {
return base return base
} }
var showHighErrors: Bool { private var showHighErrors: Bool {
guard let frictionRateDesignValue else { return false } guard let frictionRateDesignValue else { return false }
return frictionRateDesignValue >= 0.18 return frictionRateDesignValue >= 0.18
} }
var showLowErrors: Bool { private var showLowErrors: Bool {
guard let frictionRateDesignValue else { return false } guard let frictionRateDesignValue else { return false }
return frictionRateDesignValue <= 0.02 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 { var body: some HTML {
div(.class("space-y-6")) { div(.class("space-y-6")) {
PageTitleRow { 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" } 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 { if let frictionRateDesignValue {
LabeledContent("Friction Rate Design Value") { LabeledContent {
Badge(number: frictionRateDesignValue, digits: 2) span { "Friction Rate Design Value" }
.attributes(.class("\(badgeColor)")) } 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 { if let availableStaticPressure {
LabeledContent("Available Static Pressure") { LabeledContent {
Badge(number: availableStaticPressure, digits: 2) 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." "Must complete previous sections."
} }
} }
.hidden( .hidden(when: !showIncompleteSectionsError)
when: availableStaticPressure != nil && frictionRateDesignValue != nil
)
Alert { Alert {
p { p {
"No component pressures losses" "No component pressures losses"
} }
} }
.hidden(when: componentLosses.totalComponentPressureLoss > 0) .hidden(when: !showNoComponentLossesError)
Alert { Alert {
p(.class("block")) { p(.class("block")) {
@@ -107,7 +137,9 @@ struct FrictionRateView: HTML, Sendable {
} }
} }
.hidden(when: !showHighErrors) .hidden(when: !showHighErrors)
} }
.attributes(.class("mt-4"), when: hasAlerts)
} }
} }

View File

@@ -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")) { table(.class("table table-zebra text-lg"), .id("roomsTable")) {
thead { thead {
@@ -194,8 +197,9 @@ struct RoomsView: HTML, Sendable {
struct SHRForm: HTML, Sendable { struct SHRForm: HTML, Sendable {
static let id = "shrForm" static let id = "shrForm"
let projectID: Project.ID @Environment(ProjectViewValue.$projectID) var projectID
let sensibleHeatRatio: Double? let sensibleHeatRatio: Double?
let dismiss: Bool
var route: String { var route: String {
SiteRoute.View.router SiteRoute.View.router
@@ -204,7 +208,7 @@ struct RoomsView: HTML, Sendable {
} }
var body: some HTML { var body: some HTML {
ModalForm(id: Self.id, dismiss: true) { ModalForm(id: Self.id, dismiss: dismiss) {
h1(.class("text-xl font-bold mb-6")) { h1(.class("text-xl font-bold mb-6")) {
"Sensible Heat Ratio" "Sensible Heat Ratio"
} }