feat: Style updates, begins adding name/label to trunk sizes. Need to remove register id.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ public struct Badge<Inner: HTML>: HTML, Sendable where Inner: Sendable {
|
||||
}
|
||||
|
||||
public var body: some HTML<HTMLTag.div> {
|
||||
div(.class("badge badge-lg badge-outline font-bold")) {
|
||||
div(.class("badge badge-lg badge-outline")) {
|
||||
inner
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,10 +43,14 @@ extension HTMLAttribute where Tag == HTMLTag.button {
|
||||
|
||||
extension HTML where Tag: HTMLTrait.Attributes.Global {
|
||||
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> {
|
||||
attributes(.class("hidden"), when: shouldHide)
|
||||
}
|
||||
|
||||
public func bold(when shouldBeBold: Bool = true) -> _AttributedElement<Self> {
|
||||
attributes(.class("font-bold"), when: shouldBeBold)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<HTMLTag.tr> {
|
||||
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")) {
|
||||
|
||||
@@ -331,7 +331,7 @@ struct GroupTypeSelect: HTML, Sendable {
|
||||
let selected: EffectiveLength.EffectiveLengthType
|
||||
|
||||
var body: some HTML<HTMLTag.label> {
|
||||
label(.class("select")) {
|
||||
label(.class("select w-full")) {
|
||||
span(.class("label")) { "Type" }
|
||||
select(.name("type"), .id("type")) {
|
||||
for value in EffectiveLength.EffectiveLengthType.allCases {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -55,7 +55,8 @@ struct EquipmentInfoView: HTML, Sendable {
|
||||
}
|
||||
}
|
||||
EquipmentInfoForm(
|
||||
dismiss: true, projectID: projectID, equipmentInfo: equipmentInfo
|
||||
dismiss: equipmentInfo != nil,
|
||||
equipmentInfo: equipmentInfo
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user