Compare commits
2 Commits
432533c940
...
930db145a8
| Author | SHA1 | Date | |
|---|---|---|---|
|
930db145a8
|
|||
|
df600a5471
|
@@ -5225,9 +5225,6 @@
|
|||||||
.m-1 {
|
.m-1 {
|
||||||
margin: calc(var(--spacing) * 1);
|
margin: calc(var(--spacing) * 1);
|
||||||
}
|
}
|
||||||
.m-4 {
|
|
||||||
margin: calc(var(--spacing) * 4);
|
|
||||||
}
|
|
||||||
.m-6 {
|
.m-6 {
|
||||||
margin: calc(var(--spacing) * 6);
|
margin: calc(var(--spacing) * 6);
|
||||||
}
|
}
|
||||||
@@ -5371,12 +5368,6 @@
|
|||||||
.-my-2 {
|
.-my-2 {
|
||||||
margin-block: calc(var(--spacing) * -2);
|
margin-block: calc(var(--spacing) * -2);
|
||||||
}
|
}
|
||||||
.-my-4 {
|
|
||||||
margin-block: calc(var(--spacing) * -4);
|
|
||||||
}
|
|
||||||
.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);
|
||||||
}
|
}
|
||||||
@@ -5589,11 +5580,14 @@
|
|||||||
border-width: var(--border, 1px) 0 var(--border, 1px) var(--border, 1px);
|
border-width: var(--border, 1px) 0 var(--border, 1px) var(--border, 1px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.me-2 {
|
||||||
|
margin-inline-end: calc(var(--spacing) * 2);
|
||||||
|
}
|
||||||
.me-4 {
|
.me-4 {
|
||||||
margin-inline-end: calc(var(--spacing) * 4);
|
margin-inline-end: calc(var(--spacing) * 4);
|
||||||
}
|
}
|
||||||
.me-6 {
|
.me-8 {
|
||||||
margin-inline-end: calc(var(--spacing) * 6);
|
margin-inline-end: calc(var(--spacing) * 8);
|
||||||
}
|
}
|
||||||
.modal-action {
|
.modal-action {
|
||||||
@layer daisyui.l1.l2.l3 {
|
@layer daisyui.l1.l2.l3 {
|
||||||
@@ -5634,12 +5628,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.mt-1 {
|
|
||||||
margin-top: calc(var(--spacing) * 1);
|
|
||||||
}
|
|
||||||
.mt-2 {
|
|
||||||
margin-top: calc(var(--spacing) * 2);
|
|
||||||
}
|
|
||||||
.mt-4 {
|
.mt-4 {
|
||||||
margin-top: calc(var(--spacing) * 4);
|
margin-top: calc(var(--spacing) * 4);
|
||||||
}
|
}
|
||||||
@@ -6592,6 +6580,9 @@
|
|||||||
.w-full {
|
.w-full {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
.max-w-\[300px\] {
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
.flex-1 {
|
.flex-1 {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
@@ -6797,9 +6788,6 @@
|
|||||||
.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;
|
||||||
}
|
}
|
||||||
@@ -7269,6 +7257,12 @@
|
|||||||
border-color: currentColor;
|
border-color: currentColor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.border-base-300 {
|
||||||
|
border-color: var(--color-base-300);
|
||||||
|
}
|
||||||
|
.border-error {
|
||||||
|
border-color: var(--color-error);
|
||||||
|
}
|
||||||
.border-gray-200 {
|
.border-gray-200 {
|
||||||
border-color: var(--color-gray-200);
|
border-color: var(--color-gray-200);
|
||||||
}
|
}
|
||||||
@@ -7852,21 +7846,12 @@
|
|||||||
.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);
|
||||||
}
|
}
|
||||||
.py-2 {
|
.py-2 {
|
||||||
padding-block: calc(var(--spacing) * 2);
|
padding-block: calc(var(--spacing) * 2);
|
||||||
}
|
}
|
||||||
.py-4 {
|
|
||||||
padding-block: calc(var(--spacing) * 4);
|
|
||||||
}
|
|
||||||
.py-6 {
|
|
||||||
padding-block: calc(var(--spacing) * 6);
|
|
||||||
}
|
|
||||||
.ps-2 {
|
.ps-2 {
|
||||||
padding-inline-start: calc(var(--spacing) * 2);
|
padding-inline-start: calc(var(--spacing) * 2);
|
||||||
}
|
}
|
||||||
@@ -7884,6 +7869,9 @@
|
|||||||
.pe-2 {
|
.pe-2 {
|
||||||
padding-inline-end: calc(var(--spacing) * 2);
|
padding-inline-end: calc(var(--spacing) * 2);
|
||||||
}
|
}
|
||||||
|
.pt-4 {
|
||||||
|
padding-top: calc(var(--spacing) * 4);
|
||||||
|
}
|
||||||
.pt-6 {
|
.pt-6 {
|
||||||
padding-top: calc(var(--spacing) * 6);
|
padding-top: calc(var(--spacing) * 6);
|
||||||
}
|
}
|
||||||
@@ -7941,14 +7929,6 @@
|
|||||||
font-size: var(--text-3xl);
|
font-size: var(--text-3xl);
|
||||||
line-height: var(--tw-leading, var(--text-3xl--line-height));
|
line-height: var(--tw-leading, var(--text-3xl--line-height));
|
||||||
}
|
}
|
||||||
.text-4xl {
|
|
||||||
font-size: var(--text-4xl);
|
|
||||||
line-height: var(--tw-leading, var(--text-4xl--line-height));
|
|
||||||
}
|
|
||||||
.text-base {
|
|
||||||
font-size: var(--text-base);
|
|
||||||
line-height: var(--tw-leading, var(--text-base--line-height));
|
|
||||||
}
|
|
||||||
.text-lg {
|
.text-lg {
|
||||||
font-size: var(--text-lg);
|
font-size: var(--text-lg);
|
||||||
line-height: var(--tw-leading, var(--text-lg--line-height));
|
line-height: var(--tw-leading, var(--text-lg--line-height));
|
||||||
@@ -8518,6 +8498,9 @@
|
|||||||
.text-base-100 {
|
.text-base-100 {
|
||||||
color: var(--color-base-100);
|
color: var(--color-base-100);
|
||||||
}
|
}
|
||||||
|
.text-base-300 {
|
||||||
|
color: var(--color-base-300);
|
||||||
|
}
|
||||||
.text-base-content {
|
.text-base-content {
|
||||||
color: var(--color-base-content);
|
color: var(--color-base-content);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ public struct DatabaseClient: Sendable {
|
|||||||
public var effectiveLength: EffectiveLengthClient
|
public var effectiveLength: EffectiveLengthClient
|
||||||
public var users: Users
|
public var users: Users
|
||||||
public var userProfile: UserProfile
|
public var userProfile: UserProfile
|
||||||
|
public var trunkSizes: TrunkSizes
|
||||||
}
|
}
|
||||||
|
|
||||||
extension DatabaseClient: TestDependencyKey {
|
extension DatabaseClient: TestDependencyKey {
|
||||||
@@ -31,7 +32,8 @@ extension DatabaseClient: TestDependencyKey {
|
|||||||
componentLoss: .testValue,
|
componentLoss: .testValue,
|
||||||
effectiveLength: .testValue,
|
effectiveLength: .testValue,
|
||||||
users: .testValue,
|
users: .testValue,
|
||||||
userProfile: .testValue
|
userProfile: .testValue,
|
||||||
|
trunkSizes: .testValue
|
||||||
)
|
)
|
||||||
|
|
||||||
public static func live(database: any Database) -> Self {
|
public static func live(database: any Database) -> Self {
|
||||||
@@ -43,7 +45,8 @@ extension DatabaseClient: TestDependencyKey {
|
|||||||
componentLoss: .live(database: database),
|
componentLoss: .live(database: database),
|
||||||
effectiveLength: .live(database: database),
|
effectiveLength: .live(database: database),
|
||||||
users: .live(database: database),
|
users: .live(database: database),
|
||||||
userProfile: .live(database: database)
|
userProfile: .live(database: database),
|
||||||
|
trunkSizes: .live(database: database)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,6 +74,7 @@ extension DatabaseClient.Migrations: DependencyKey {
|
|||||||
EquipmentInfo.Migrate(),
|
EquipmentInfo.Migrate(),
|
||||||
Room.Migrate(),
|
Room.Migrate(),
|
||||||
EffectiveLength.Migrate(),
|
EffectiveLength.Migrate(),
|
||||||
|
DuctSizing.TrunkSize.Migrate(),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
212
Sources/DatabaseClient/Trunk.swift
Normal file
212
Sources/DatabaseClient/Trunk.swift
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
import Dependencies
|
||||||
|
import DependenciesMacros
|
||||||
|
import Fluent
|
||||||
|
import Foundation
|
||||||
|
import ManualDCore
|
||||||
|
|
||||||
|
extension DatabaseClient {
|
||||||
|
@DependencyClient
|
||||||
|
public struct TrunkSizes: Sendable {
|
||||||
|
public var create: @Sendable (DuctSizing.TrunkSize.Create) async throws -> DuctSizing.TrunkSize
|
||||||
|
public var delete: @Sendable (DuctSizing.TrunkSize.ID) async throws -> Void
|
||||||
|
public var fetch: @Sendable (Project.ID) async throws -> [DuctSizing.TrunkSize]
|
||||||
|
public var get: @Sendable (DuctSizing.TrunkSize.ID) async throws -> DuctSizing.TrunkSize?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DatabaseClient.TrunkSizes: TestDependencyKey {
|
||||||
|
public static let testValue = Self()
|
||||||
|
|
||||||
|
public static func live(database: any Database) -> Self {
|
||||||
|
.init(
|
||||||
|
create: { request in
|
||||||
|
try request.validate()
|
||||||
|
|
||||||
|
let trunk = request.toModel()
|
||||||
|
var roomProxies = [DuctSizing.TrunkSize.RoomProxy]()
|
||||||
|
|
||||||
|
try await trunk.save(on: database)
|
||||||
|
|
||||||
|
for (roomID, registers) in request.rooms {
|
||||||
|
guard let room = try await RoomModel.find(roomID, on: database) else {
|
||||||
|
throw NotFoundError()
|
||||||
|
}
|
||||||
|
let model = try TrunkRoomModel(
|
||||||
|
trunkID: trunk.requireID(),
|
||||||
|
roomID: room.requireID(),
|
||||||
|
registers: registers
|
||||||
|
)
|
||||||
|
try await model.save(on: database)
|
||||||
|
try roomProxies.append(model.toDTO())
|
||||||
|
}
|
||||||
|
|
||||||
|
return try .init(
|
||||||
|
id: trunk.requireID(),
|
||||||
|
projectID: trunk.$project.id,
|
||||||
|
type: .init(rawValue: trunk.type)!,
|
||||||
|
rooms: roomProxies
|
||||||
|
)
|
||||||
|
},
|
||||||
|
delete: { id in
|
||||||
|
guard let model = try await TrunkModel.find(id, on: database) else {
|
||||||
|
throw NotFoundError()
|
||||||
|
}
|
||||||
|
try await model.delete(on: database)
|
||||||
|
},
|
||||||
|
fetch: { projectID in
|
||||||
|
try await TrunkModel.query(on: database)
|
||||||
|
.with(\.$rooms)
|
||||||
|
.with(\.$project)
|
||||||
|
.filter(\.$project.$id == projectID)
|
||||||
|
.all()
|
||||||
|
.map { try $0.toDTO() }
|
||||||
|
},
|
||||||
|
get: { id in
|
||||||
|
try await TrunkModel.find(id, on: database)
|
||||||
|
.map { try $0.toDTO() }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DuctSizing.TrunkSize.Create {
|
||||||
|
|
||||||
|
func validate() throws(ValidationError) {
|
||||||
|
guard rooms.count > 0 else {
|
||||||
|
throw ValidationError("Trunk size should have associated rooms / registers.")
|
||||||
|
}
|
||||||
|
if let height {
|
||||||
|
guard height > 0 else {
|
||||||
|
throw ValidationError("Trunk size height should be greater than 0.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toModel() -> TrunkModel {
|
||||||
|
.init(
|
||||||
|
projectID: projectID,
|
||||||
|
type: type,
|
||||||
|
height: height
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DuctSizing.TrunkSize {
|
||||||
|
|
||||||
|
struct Migrate: AsyncMigration {
|
||||||
|
let name = "CreateTrunkSize"
|
||||||
|
|
||||||
|
func prepare(on database: any Database) async throws {
|
||||||
|
try await database.schema(TrunkModel.schema)
|
||||||
|
.id()
|
||||||
|
.field("height", .int8)
|
||||||
|
.field("type", .string, .required)
|
||||||
|
.field(
|
||||||
|
"projectID", .uuid, .required, .references(ProjectModel.schema, "id", onDelete: .cascade)
|
||||||
|
)
|
||||||
|
.create()
|
||||||
|
|
||||||
|
try await database.schema(TrunkRoomModel.schema)
|
||||||
|
.id()
|
||||||
|
.field("registers", .array(of: .int), .required)
|
||||||
|
.field(
|
||||||
|
"trunkID", .uuid, .required, .references(TrunkModel.schema, "id", onDelete: .cascade)
|
||||||
|
)
|
||||||
|
.field(
|
||||||
|
"roomID", .uuid, .required, .references(RoomModel.schema, "id", onDelete: .cascade)
|
||||||
|
)
|
||||||
|
.create()
|
||||||
|
}
|
||||||
|
|
||||||
|
func revert(on database: any Database) async throws {
|
||||||
|
try await database.schema(TrunkRoomModel.schema).delete()
|
||||||
|
try await database.schema(TrunkModel.schema).delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pivot table for associating rooms and trunks.
|
||||||
|
final class TrunkRoomModel: Model, @unchecked Sendable {
|
||||||
|
|
||||||
|
static let schema = "room+trunk"
|
||||||
|
|
||||||
|
@ID(key: .id)
|
||||||
|
var id: UUID?
|
||||||
|
|
||||||
|
@Parent(key: "trunkID")
|
||||||
|
var trunk: TrunkModel
|
||||||
|
|
||||||
|
@Parent(key: "roomID")
|
||||||
|
var room: RoomModel
|
||||||
|
|
||||||
|
@Field(key: "registers")
|
||||||
|
var registers: [Int]
|
||||||
|
|
||||||
|
init() {}
|
||||||
|
|
||||||
|
init(
|
||||||
|
id: UUID? = nil,
|
||||||
|
trunkID: TrunkModel.IDValue,
|
||||||
|
roomID: RoomModel.IDValue,
|
||||||
|
registers: [Int]
|
||||||
|
) {
|
||||||
|
self.id = id
|
||||||
|
$trunk.id = trunkID
|
||||||
|
$room.id = roomID
|
||||||
|
self.registers = registers
|
||||||
|
}
|
||||||
|
|
||||||
|
func toDTO() throws -> DuctSizing.TrunkSize.RoomProxy {
|
||||||
|
.init(
|
||||||
|
room: try room.toDTO(),
|
||||||
|
registers: registers
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
final class TrunkModel: Model, @unchecked Sendable {
|
||||||
|
|
||||||
|
static let schema = "trunk"
|
||||||
|
|
||||||
|
@ID(key: .id)
|
||||||
|
var id: UUID?
|
||||||
|
|
||||||
|
@Parent(key: "projectID")
|
||||||
|
var project: ProjectModel
|
||||||
|
|
||||||
|
@Field(key: "height")
|
||||||
|
var height: Int?
|
||||||
|
|
||||||
|
@Field(key: "type")
|
||||||
|
var type: String
|
||||||
|
|
||||||
|
@Children(for: \.$trunk)
|
||||||
|
var rooms: [TrunkRoomModel]
|
||||||
|
|
||||||
|
init() {}
|
||||||
|
|
||||||
|
init(
|
||||||
|
id: UUID? = nil,
|
||||||
|
projectID: Project.ID,
|
||||||
|
type: DuctSizing.TrunkSize.TrunkType,
|
||||||
|
height: Int? = nil,
|
||||||
|
) {
|
||||||
|
self.id = id
|
||||||
|
$project.id = projectID
|
||||||
|
self.height = height
|
||||||
|
self.type = type.rawValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func toDTO() throws -> DuctSizing.TrunkSize {
|
||||||
|
try .init(
|
||||||
|
id: requireID(),
|
||||||
|
projectID: $project.id,
|
||||||
|
type: .init(rawValue: type)!,
|
||||||
|
rooms: rooms.map { try $0.toDTO() },
|
||||||
|
height: height
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -91,3 +91,70 @@ public enum DuctSizing {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension DuctSizing {
|
||||||
|
|
||||||
|
public struct TrunkSize: Codable, Equatable, Identifiable, Sendable {
|
||||||
|
|
||||||
|
public let id: UUID
|
||||||
|
public let projectID: Project.ID
|
||||||
|
public let type: TrunkType
|
||||||
|
public let rooms: [RoomProxy]
|
||||||
|
public let height: Int?
|
||||||
|
|
||||||
|
public init(
|
||||||
|
id: UUID,
|
||||||
|
projectID: Project.ID,
|
||||||
|
type: DuctSizing.TrunkSize.TrunkType,
|
||||||
|
rooms: [DuctSizing.TrunkSize.RoomProxy],
|
||||||
|
height: Int? = nil
|
||||||
|
) {
|
||||||
|
self.id = id
|
||||||
|
self.projectID = projectID
|
||||||
|
self.type = type
|
||||||
|
self.rooms = rooms
|
||||||
|
self.height = height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DuctSizing.TrunkSize {
|
||||||
|
public struct Create: Codable, Equatable, Sendable {
|
||||||
|
|
||||||
|
public let projectID: Project.ID
|
||||||
|
public let type: TrunkType
|
||||||
|
public let rooms: [Room.ID: [Int]]
|
||||||
|
public let height: Int?
|
||||||
|
|
||||||
|
public init(
|
||||||
|
projectID: Project.ID,
|
||||||
|
type: DuctSizing.TrunkSize.TrunkType,
|
||||||
|
rooms: [Room.ID: [Int]],
|
||||||
|
height: Int? = nil
|
||||||
|
) {
|
||||||
|
self.projectID = projectID
|
||||||
|
self.type = type
|
||||||
|
self.rooms = rooms
|
||||||
|
self.height = height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Make registers non-optional
|
||||||
|
public struct RoomProxy: Codable, Equatable, Identifiable, Sendable {
|
||||||
|
|
||||||
|
public var id: Room.ID { room.id }
|
||||||
|
public let room: Room
|
||||||
|
public let registers: [Int]?
|
||||||
|
|
||||||
|
public init(room: Room, registers: [Int]? = nil) {
|
||||||
|
self.room = room
|
||||||
|
self.registers = registers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TrunkType: String, Codable, Equatable, Sendable {
|
||||||
|
case `return`
|
||||||
|
case supply
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public struct Label: HTML, Sendable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public var body: some HTML<HTMLTag.span> {
|
public var body: some HTML<HTMLTag.span> {
|
||||||
span(.class("text-lg text-secondary font-bold")) {
|
span(.class("text-lg label font-bold")) {
|
||||||
title
|
title
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -211,10 +211,8 @@ extension SiteRoute.View.ProjectRoute.EquipmentInfoRoute {
|
|||||||
return await equipmentView(on: request, projectID: projectID)
|
return await equipmentView(on: request, projectID: projectID)
|
||||||
|
|
||||||
case .submit(let form):
|
case .submit(let form):
|
||||||
return await ResultView {
|
return await equipmentView(on: request, projectID: projectID) {
|
||||||
try await database.equipment.create(form)
|
_ = try await database.equipment.create(form)
|
||||||
} onSuccess: { equipment in
|
|
||||||
EquipmentInfoView(equipmentInfo: equipment, projectID: projectID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case .update(let id, let updates):
|
case .update(let id, let updates):
|
||||||
@@ -576,15 +574,17 @@ extension SiteRoute.View.ProjectRoute.DuctSizingRoute {
|
|||||||
) async -> AnySendableHTML {
|
) async -> AnySendableHTML {
|
||||||
@Dependency(\.database) var database
|
@Dependency(\.database) var database
|
||||||
|
|
||||||
return await ResultView {
|
return await request.view {
|
||||||
try await catching()
|
await ResultView {
|
||||||
return (
|
try await catching()
|
||||||
try await database.projects.getCompletedSteps(projectID),
|
return (
|
||||||
try await database.calculateDuctSizes(projectID: projectID)
|
try await database.projects.getCompletedSteps(projectID),
|
||||||
)
|
try await database.calculateDuctSizes(projectID: projectID)
|
||||||
} onSuccess: { (steps, rooms) in
|
)
|
||||||
ProjectView(projectID: projectID, activeTab: .ductSizing, completedSteps: steps) {
|
} onSuccess: { (steps, rooms) in
|
||||||
DuctSizingView(rooms: rooms)
|
ProjectView(projectID: projectID, activeTab: .ductSizing, completedSteps: steps) {
|
||||||
|
DuctSizingView(rooms: rooms)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,19 +42,27 @@ struct ComponentLossForm: HTML, Sendable {
|
|||||||
|
|
||||||
input(.class("hidden"), .name("projectID"), .value("\(projectID)"))
|
input(.class("hidden"), .name("projectID"), .value("\(projectID)"))
|
||||||
|
|
||||||
div {
|
LabeledInput(
|
||||||
label(.for("name")) { "Name" }
|
"Name",
|
||||||
Input(id: "name", placeholder: "Name")
|
.name("name"),
|
||||||
.attributes(.type(.text), .required, .autofocus, .value(componentLoss?.name))
|
.type(.text),
|
||||||
}
|
.value(componentLoss?.name),
|
||||||
div {
|
.placeholder("Name"),
|
||||||
label(.for("value")) { "Value" }
|
.required,
|
||||||
Input(id: "value", placeholder: "Pressure loss")
|
.autofocus
|
||||||
.attributes(
|
)
|
||||||
.type(.number), .min("0.03"), .max("1.0"), .step("0.01"), .required,
|
|
||||||
.value(componentLoss?.value)
|
LabeledInput(
|
||||||
)
|
"Value",
|
||||||
}
|
.name("value"),
|
||||||
|
.type(.number),
|
||||||
|
.value(componentLoss?.value),
|
||||||
|
.placeholder("0.2"),
|
||||||
|
.min("0.03"),
|
||||||
|
.max("1.0"),
|
||||||
|
.step("0.01"),
|
||||||
|
.required
|
||||||
|
)
|
||||||
|
|
||||||
SubmitButton()
|
SubmitButton()
|
||||||
.attributes(.class("btn-block"))
|
.attributes(.class("btn-block"))
|
||||||
|
|||||||
@@ -24,30 +24,32 @@ struct ComponentPressureLossesView: HTML, Sendable {
|
|||||||
}
|
}
|
||||||
.attributes(.class("px-4"))
|
.attributes(.class("px-4"))
|
||||||
|
|
||||||
table(.class("table table-zebra")) {
|
div(.class("overflow-x-auto")) {
|
||||||
thead {
|
table(.class("table table-zebra")) {
|
||||||
tr(.class("text-xl font-bold")) {
|
thead {
|
||||||
th { "Name" }
|
tr(.class("text-xl font-bold")) {
|
||||||
th { "Value" }
|
th { "Name" }
|
||||||
th {
|
th { "Value" }
|
||||||
div(.class("flex justify-end mx-auto")) {
|
th {
|
||||||
Tooltip("Add Component Loss") {
|
div(.class("flex justify-end mx-auto")) {
|
||||||
PlusButton()
|
Tooltip("Add Component Loss") {
|
||||||
.attributes(
|
PlusButton()
|
||||||
.class("btn-ghost text-2xl"),
|
.attributes(
|
||||||
.showModal(id: ComponentLossForm.id())
|
.class("btn-ghost text-2xl me-2"),
|
||||||
)
|
.showModal(id: ComponentLossForm.id())
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
tbody {
|
||||||
tbody {
|
for row in componentPressureLosses {
|
||||||
for row in componentPressureLosses {
|
TableRow(row: row)
|
||||||
TableRow(row: row)
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ComponentLossForm(dismiss: true, projectID: projectID, componentLoss: nil)
|
ComponentLossForm(dismiss: true, projectID: projectID, componentLoss: nil)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ struct DuctSizingView: HTML, Sendable {
|
|||||||
let rooms: [DuctSizing.RoomContainer]
|
let rooms: [DuctSizing.RoomContainer]
|
||||||
|
|
||||||
var body: some HTML {
|
var body: some HTML {
|
||||||
div {
|
div(.class("space-y-4")) {
|
||||||
PageTitle { "Duct Sizes" }
|
PageTitle { "Duct Sizes" }
|
||||||
if rooms.count == 0 {
|
if rooms.count == 0 {
|
||||||
p(.class("text-error italic")) {
|
p(.class("text-error italic")) {
|
||||||
|
|||||||
@@ -80,8 +80,16 @@ struct RectangularSizeForm: HTML, Sendable {
|
|||||||
input(.class("hidden"), .name("register"), .value(register))
|
input(.class("hidden"), .name("register"), .value(register))
|
||||||
input(.class("hidden"), .name("id"), .value(rectangularSizeID))
|
input(.class("hidden"), .name("id"), .value(rectangularSizeID))
|
||||||
|
|
||||||
Input(id: "height", placeholder: "Height")
|
LabeledInput(
|
||||||
.attributes(.type(.number), .min("0"), .value(height), .required, .autofocus)
|
"Height",
|
||||||
|
.name("height"),
|
||||||
|
.type(.number),
|
||||||
|
.value(height),
|
||||||
|
.placeholder("8"),
|
||||||
|
.min("0"),
|
||||||
|
.required,
|
||||||
|
.autofocus
|
||||||
|
)
|
||||||
|
|
||||||
SubmitButton()
|
SubmitButton()
|
||||||
.attributes(.class("btn-block"))
|
.attributes(.class("btn-block"))
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ struct EffectiveLengthForm: HTML, Sendable {
|
|||||||
dismiss: dismiss
|
dismiss: dismiss
|
||||||
) {
|
) {
|
||||||
h1(.class("text-2xl font-bold")) { "Effective Length" }
|
h1(.class("text-2xl font-bold")) { "Effective Length" }
|
||||||
div(.id("formStep_\(id)")) {
|
div(.id("formStep_\(id)"), .class("mt-4")) {
|
||||||
StepOne(projectID: projectID, effectiveLength: effectiveLength)
|
StepOne(projectID: projectID, effectiveLength: effectiveLength)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,8 +74,15 @@ struct EffectiveLengthForm: HTML, Sendable {
|
|||||||
if let id = effectiveLength?.id {
|
if let id = effectiveLength?.id {
|
||||||
input(.class("hidden"), .name("id"), .value("\(id)"))
|
input(.class("hidden"), .name("id"), .value("\(id)"))
|
||||||
}
|
}
|
||||||
Input(id: "name", placeholder: "Name")
|
|
||||||
.attributes(.type(.text), .required, .autofocus, .value(effectiveLength?.name))
|
LabeledInput(
|
||||||
|
"Name",
|
||||||
|
.name("name"),
|
||||||
|
.type(.text),
|
||||||
|
.value(effectiveLength?.name),
|
||||||
|
.required,
|
||||||
|
.autofocus
|
||||||
|
)
|
||||||
|
|
||||||
GroupTypeSelect(projectID: projectID, selected: effectiveLength?.type ?? .supply)
|
GroupTypeSelect(projectID: projectID, selected: effectiveLength?.type ?? .supply)
|
||||||
|
|
||||||
@@ -193,14 +200,6 @@ struct EffectiveLengthForm: HTML, Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
div(.class("grid grid-cols-5 gap-2")) {
|
|
||||||
Label("Group")
|
|
||||||
Label("Letter")
|
|
||||||
Label("Length")
|
|
||||||
Label("Quantity")
|
|
||||||
.attributes(.class("col-span-2"))
|
|
||||||
}
|
|
||||||
|
|
||||||
div(.id("groups"), .class("space-y-4")) {
|
div(.id("groups"), .class("space-y-4")) {
|
||||||
if let effectiveLength {
|
if let effectiveLength {
|
||||||
for group in effectiveLength.groups {
|
for group in effectiveLength.groups {
|
||||||
@@ -228,12 +227,16 @@ struct StraightLengthField: HTML, Sendable {
|
|||||||
|
|
||||||
var body: some HTML<HTMLTag.div> {
|
var body: some HTML<HTMLTag.div> {
|
||||||
Row {
|
Row {
|
||||||
Input(
|
LabeledInput(
|
||||||
name: "straightLengths",
|
"Length",
|
||||||
placeholder: "Length"
|
.name("straightLengths"),
|
||||||
|
.type(.number),
|
||||||
|
.value(value),
|
||||||
|
.placeholder("10"),
|
||||||
|
.min("0"),
|
||||||
|
.autofocus,
|
||||||
|
.required
|
||||||
)
|
)
|
||||||
.attributes(.type(.number), .min("0"), .autofocus, .required, .value(value))
|
|
||||||
|
|
||||||
TrashButton()
|
TrashButton()
|
||||||
.attributes(.data("remove", value: "true"))
|
.attributes(.data("remove", value: "true"))
|
||||||
}
|
}
|
||||||
@@ -252,18 +255,43 @@ struct GroupField: HTML, Sendable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some HTML {
|
var body: some HTML {
|
||||||
div(.class("grid grid-cols-5 gap-2")) {
|
div(.class("grid grid-cols-3 gap-2 p-2 border rounded-lg shadow-sm")) {
|
||||||
GroupSelect(style: style)
|
GroupSelect(style: style)
|
||||||
Input(name: "group[letter]", placeholder: "Letter")
|
|
||||||
.attributes(.type(.text), .autofocus, .required, .value(group?.letter))
|
LabeledInput(
|
||||||
Input(name: "group[length]", placeholder: "Length")
|
"Letter",
|
||||||
.attributes(.type(.number), .min("0"), .required, .value(group?.value))
|
.name("group[letter]"),
|
||||||
Input(name: "group[quantity]", placeholder: "Quantity")
|
.type(.text),
|
||||||
.attributes(.type(.number), .min("1"), .value("1"), .required, .value(group?.quantity ?? 1))
|
.value(group?.letter),
|
||||||
div(.class("flex justify-end")) {
|
.placeholder("a"),
|
||||||
TrashButton()
|
.required
|
||||||
.attributes(.data("remove", value: "true"), .class("mx-2"))
|
)
|
||||||
}
|
|
||||||
|
LabeledInput(
|
||||||
|
"Length",
|
||||||
|
.name("group[length]"),
|
||||||
|
.type(.number),
|
||||||
|
.value(group?.value),
|
||||||
|
.placeholder("10"),
|
||||||
|
.min("0"),
|
||||||
|
.required
|
||||||
|
)
|
||||||
|
|
||||||
|
LabeledInput(
|
||||||
|
"Quantity",
|
||||||
|
.name("group[quantity]"),
|
||||||
|
.type(.number),
|
||||||
|
.value(group?.quantity ?? 1),
|
||||||
|
.min("1"),
|
||||||
|
.required
|
||||||
|
)
|
||||||
|
.attributes(.class("col-span-2"))
|
||||||
|
|
||||||
|
TrashButton()
|
||||||
|
.attributes(
|
||||||
|
.data("remove", value: "true"),
|
||||||
|
.class("me-2 btn-block")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
.attributes(.class("space-x-2"), .hx.ext("remove"))
|
.attributes(.class("space-x-2"), .hx.ext("remove"))
|
||||||
}
|
}
|
||||||
@@ -274,12 +302,15 @@ struct GroupSelect: HTML, Sendable {
|
|||||||
let style: EffectiveLength.EffectiveLengthType
|
let style: EffectiveLength.EffectiveLengthType
|
||||||
|
|
||||||
var body: some HTML {
|
var body: some HTML {
|
||||||
select(
|
label(.class("select")) {
|
||||||
.name("group[group]"),
|
span(.class("label")) { "Group" }
|
||||||
.class("select")
|
select(
|
||||||
) {
|
.name("group[group]"),
|
||||||
for value in style.selectOptions {
|
.autofocus
|
||||||
option(.value("\(value)")) { "\(value)" }
|
) {
|
||||||
|
for value in style.selectOptions {
|
||||||
|
option(.value("\(value)")) { "\(value)" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -291,13 +322,16 @@ struct GroupTypeSelect: HTML, Sendable {
|
|||||||
let projectID: Project.ID
|
let projectID: Project.ID
|
||||||
let selected: EffectiveLength.EffectiveLengthType
|
let selected: EffectiveLength.EffectiveLengthType
|
||||||
|
|
||||||
var body: some HTML<HTMLTag.select> {
|
var body: some HTML<HTMLTag.label> {
|
||||||
select(.class("select"), .name("type"), .id("type")) {
|
label(.class("select")) {
|
||||||
for value in EffectiveLength.EffectiveLengthType.allCases {
|
span(.class("label")) { "Type" }
|
||||||
option(
|
select(.name("type"), .id("type")) {
|
||||||
.value("\(value.rawValue)"),
|
for value in EffectiveLength.EffectiveLengthType.allCases {
|
||||||
) { value.title }
|
option(
|
||||||
.attributes(.selected, when: value == selected)
|
.value("\(value.rawValue)"),
|
||||||
|
) { value.title }
|
||||||
|
.attributes(.selected, when: value == selected)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,10 @@ import ElementaryHTMX
|
|||||||
import ManualDCore
|
import ManualDCore
|
||||||
import Styleguide
|
import Styleguide
|
||||||
|
|
||||||
// TODO: Group into grids of supply / return.
|
|
||||||
|
|
||||||
struct EffectiveLengthsView: HTML, Sendable {
|
struct EffectiveLengthsView: HTML, Sendable {
|
||||||
|
|
||||||
@Environment(ProjectViewValue.$projectID) var projectID
|
@Environment(ProjectViewValue.$projectID) var projectID
|
||||||
|
|
||||||
// let projectID: Project.ID
|
|
||||||
let effectiveLengths: [EffectiveLength]
|
let effectiveLengths: [EffectiveLength]
|
||||||
|
|
||||||
var supplies: [EffectiveLength] {
|
var supplies: [EffectiveLength] {
|
||||||
@@ -41,9 +38,8 @@ struct EffectiveLengthsView: HTML, Sendable {
|
|||||||
.attributes(.class("hidden"), when: supplies.count == 0)
|
.attributes(.class("hidden"), when: supplies.count == 0)
|
||||||
|
|
||||||
div(.class("grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4")) {
|
div(.class("grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4")) {
|
||||||
for (n, row) in supplies.enumerated() {
|
for row in supplies {
|
||||||
EffectiveLengthView(effectiveLength: row)
|
EffectiveLengthView(effectiveLength: row)
|
||||||
.attributes(.class(n == 0 ? "border-primary" : "border-gray-200"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,9 +48,8 @@ struct EffectiveLengthsView: HTML, Sendable {
|
|||||||
h2(.class("text-xl font-bold pb-4")) { "Returns" }
|
h2(.class("text-xl font-bold pb-4")) { "Returns" }
|
||||||
.attributes(.class("hidden"), when: returns.count == 0)
|
.attributes(.class("hidden"), when: returns.count == 0)
|
||||||
div(.class("grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 space-x-4 space-y-4")) {
|
div(.class("grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 space-x-4 space-y-4")) {
|
||||||
for (n, row) in returns.enumerated() {
|
for row in returns {
|
||||||
EffectiveLengthView(effectiveLength: row)
|
EffectiveLengthView(effectiveLength: row)
|
||||||
.attributes(.class(n == 0 ? "border-secondary" : "border-gray-200"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,44 +79,41 @@ struct EffectiveLengthsView: HTML, Sendable {
|
|||||||
.class("card h-full bg-base-100 shadow-sm border rounded-lg"),
|
.class("card h-full bg-base-100 shadow-sm border rounded-lg"),
|
||||||
.id(id)
|
.id(id)
|
||||||
) {
|
) {
|
||||||
div(.class("card-body")) {
|
div(.class("card-body text-lg")) {
|
||||||
Row {
|
Row {
|
||||||
h2 { effectiveLength.name }
|
h2 { effectiveLength.name }
|
||||||
div(
|
div(
|
||||||
.class("space-x-4")
|
.class("space-x-4")
|
||||||
) {
|
) {
|
||||||
span(.class("text-sm italic")) {
|
span(.class("text-primary text-sm italic")) {
|
||||||
"Total"
|
"Total"
|
||||||
}
|
}
|
||||||
.attributes(.class("text-primary"), when: effectiveLength.type == .supply)
|
|
||||||
.attributes(.class("text-secondary"), when: effectiveLength.type == .return)
|
|
||||||
|
|
||||||
Number(self.effectiveLength.totalEquivalentLength, digits: 0)
|
Number(self.effectiveLength.totalEquivalentLength, digits: 0)
|
||||||
.attributes(.class("badge badge-outline text-lg"))
|
.attributes(.class("badge badge-outline badge-primary text-lg"))
|
||||||
.attributes(
|
|
||||||
.class("badge-primary"), when: effectiveLength.type == .supply
|
|
||||||
)
|
|
||||||
.attributes(
|
|
||||||
.class("badge-secondary"), when: effectiveLength.type == .return
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.attributes(.class("card-title pb-6"))
|
.attributes(.class("card-title pb-6"))
|
||||||
|
|
||||||
Label("Straight Lengths")
|
Row {
|
||||||
|
Label { "Straight Lengths" }
|
||||||
|
|
||||||
for length in effectiveLength.straightLengths {
|
ul {
|
||||||
div(.class("flex justify-end")) {
|
for length in effectiveLength.straightLengths {
|
||||||
Number(length)
|
li {
|
||||||
|
Number(length)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.attributes(.class("pb-6"))
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
Label("Groups")
|
span { "Groups" }
|
||||||
Label("Equivalent Length")
|
span { "Equivalent Length" }
|
||||||
Label("Quantity")
|
span { "Quantity" }
|
||||||
}
|
}
|
||||||
.attributes(.class("border-b border-gray-200"))
|
.attributes(.class("label font-bold border-b border-label"))
|
||||||
|
|
||||||
for group in effectiveLength.groups {
|
for group in effectiveLength.groups {
|
||||||
Row {
|
Row {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ struct EquipmentInfoView: HTML, Sendable {
|
|||||||
table(.class("table table-zebra")) {
|
table(.class("table table-zebra")) {
|
||||||
tbody(.class("text-lg")) {
|
tbody(.class("text-lg")) {
|
||||||
tr {
|
tr {
|
||||||
td { span { "Static Pressure" } }
|
td { Label { "Static Pressure" } }
|
||||||
td {
|
td {
|
||||||
div(.class("flex justify-end")) {
|
div(.class("flex justify-end")) {
|
||||||
Number(equipmentInfo.staticPressure)
|
Number(equipmentInfo.staticPressure)
|
||||||
@@ -37,7 +37,7 @@ struct EquipmentInfoView: HTML, Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
tr {
|
tr {
|
||||||
td { span { "Heating CFM" } }
|
td { Label { "Heating CFM" } }
|
||||||
td {
|
td {
|
||||||
div(.class("flex justify-end")) {
|
div(.class("flex justify-end")) {
|
||||||
Number(equipmentInfo.heatingCFM)
|
Number(equipmentInfo.heatingCFM)
|
||||||
@@ -45,7 +45,7 @@ struct EquipmentInfoView: HTML, Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
tr {
|
tr {
|
||||||
td { span { "Cooling CFM" } }
|
td { Label { "Cooling CFM" } }
|
||||||
td {
|
td {
|
||||||
div(.class("flex justify-end")) {
|
div(.class("flex justify-end")) {
|
||||||
Number(equipmentInfo.coolingCFM)
|
Number(equipmentInfo.coolingCFM)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ struct ProjectDetail: HTML, Sendable {
|
|||||||
table(.class("table table-zebra text-lg")) {
|
table(.class("table table-zebra text-lg")) {
|
||||||
tbody {
|
tbody {
|
||||||
tr {
|
tr {
|
||||||
td { "Name" }
|
td(.class("label font-bold")) { "Name" }
|
||||||
td {
|
td {
|
||||||
div(.class("flex justify-end")) {
|
div(.class("flex justify-end")) {
|
||||||
project.name
|
project.name
|
||||||
@@ -29,7 +29,7 @@ struct ProjectDetail: HTML, Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
tr {
|
tr {
|
||||||
td { "Street Address" }
|
td(.class("label font-bold")) { "Street Address" }
|
||||||
td {
|
td {
|
||||||
div(.class("flex justify-end")) {
|
div(.class("flex justify-end")) {
|
||||||
project.streetAddress
|
project.streetAddress
|
||||||
@@ -37,7 +37,7 @@ struct ProjectDetail: HTML, Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
tr {
|
tr {
|
||||||
td { "City" }
|
td(.class("label font-bold")) { "City" }
|
||||||
td {
|
td {
|
||||||
div(.class("flex justify-end")) {
|
div(.class("flex justify-end")) {
|
||||||
project.city
|
project.city
|
||||||
@@ -45,7 +45,7 @@ struct ProjectDetail: HTML, Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
tr {
|
tr {
|
||||||
td { "State" }
|
td(.class("label font-bold")) { "State" }
|
||||||
td {
|
td {
|
||||||
div(.class("flex justify-end")) {
|
div(.class("flex justify-end")) {
|
||||||
project.state
|
project.state
|
||||||
@@ -53,7 +53,7 @@ struct ProjectDetail: HTML, Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
tr {
|
tr {
|
||||||
td { "Zip" }
|
td(.class("label font-bold")) { "Zip" }
|
||||||
td {
|
td {
|
||||||
div(.class("flex justify-end")) {
|
div(.class("flex justify-end")) {
|
||||||
project.zipCode
|
project.zipCode
|
||||||
|
|||||||
@@ -29,36 +29,39 @@ struct RoomsView: HTML, Sendable {
|
|||||||
) {
|
) {
|
||||||
LabeledContent {
|
LabeledContent {
|
||||||
div(.class("flex justify-end items-end space-x-4")) {
|
div(.class("flex justify-end items-end space-x-4")) {
|
||||||
// SVG(.squarePen)
|
Label {
|
||||||
span(.class("font-bold")) {
|
|
||||||
"Sensible Heat Ratio"
|
"Sensible Heat Ratio"
|
||||||
}
|
}
|
||||||
|
.attributes(.class("me-8"), when: sensibleHeatRatio == nil)
|
||||||
}
|
}
|
||||||
} content: {
|
} content: {
|
||||||
if let sensibleHeatRatio {
|
if let sensibleHeatRatio {
|
||||||
Badge(number: sensibleHeatRatio)
|
Badge(number: sensibleHeatRatio)
|
||||||
|
} else {
|
||||||
|
SVG(.squarePen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.attributes(.class("border rounded-lg border-error"), when: sensibleHeatRatio == nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
div(.class("flex flex-wrap justify-between mt-6")) {
|
div(.class("flex flex-wrap justify-between mt-6")) {
|
||||||
div(.class("flex items-end space-x-4")) {
|
div(.class("flex items-end space-x-4")) {
|
||||||
span(.class("font-bold")) { "Heating Total" }
|
Label { "Heating Total" }
|
||||||
Badge(number: rooms.heatingTotal, digits: 0)
|
Badge(number: rooms.heatingTotal, digits: 0)
|
||||||
.attributes(.class("badge-error"))
|
.attributes(.class("badge-error"))
|
||||||
}
|
}
|
||||||
|
|
||||||
div(.class("flex items-end space-x-4")) {
|
div(.class("flex items-end space-x-4")) {
|
||||||
span(.class("font-bold")) { "Cooling Total" }
|
Label { "Cooling Total" }
|
||||||
Badge(number: rooms.coolingTotal, digits: 0)
|
Badge(number: rooms.coolingTotal, digits: 0)
|
||||||
.attributes(.class("badge-success"))
|
.attributes(.class("badge-success"))
|
||||||
}
|
}
|
||||||
|
|
||||||
div(.class("flex justify-end items-end space-x-4 me-4")) {
|
div(.class("flex justify-end items-end space-x-4 me-4")) {
|
||||||
span(.class("font-bold")) { "Cooling Sensible" }
|
Label { "Cooling Sensible" }
|
||||||
Badge(number: rooms.coolingSensible(shr: sensibleHeatRatio), digits: 0)
|
Badge(number: rooms.coolingSensible(shr: sensibleHeatRatio), digits: 0)
|
||||||
.attributes(.class("badge-info"))
|
.attributes(.class("badge-info"))
|
||||||
}
|
}
|
||||||
@@ -95,7 +98,7 @@ struct RoomsView: HTML, Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
th {
|
th {
|
||||||
div(.class("flex justify-end")) {
|
div(.class("flex justify-end me-2")) {
|
||||||
Tooltip("Add Room") {
|
Tooltip("Add Room") {
|
||||||
PlusButton()
|
PlusButton()
|
||||||
.attributes(
|
.attributes(
|
||||||
@@ -201,6 +204,12 @@ struct RoomsView: HTML, Sendable {
|
|||||||
let projectID: Project.ID
|
let projectID: Project.ID
|
||||||
let sensibleHeatRatio: Double?
|
let sensibleHeatRatio: Double?
|
||||||
|
|
||||||
|
var route: String {
|
||||||
|
SiteRoute.View.router
|
||||||
|
.path(for: .project(.detail(projectID, .rooms(.index))))
|
||||||
|
.appendingPath("update-shr")
|
||||||
|
}
|
||||||
|
|
||||||
var body: some HTML {
|
var body: some HTML {
|
||||||
ModalForm(id: Self.id, dismiss: true) {
|
ModalForm(id: Self.id, dismiss: true) {
|
||||||
h1(.class("text-xl font-bold mb-6")) {
|
h1(.class("text-xl font-bold mb-6")) {
|
||||||
@@ -208,19 +217,20 @@ struct RoomsView: HTML, Sendable {
|
|||||||
}
|
}
|
||||||
form(
|
form(
|
||||||
.class("grid grid-cols-1 gap-4"),
|
.class("grid grid-cols-1 gap-4"),
|
||||||
.hx.patch("/projects/\(projectID)/rooms/update-shr"),
|
.hx.patch(route),
|
||||||
.hx.target("body"),
|
.hx.target("body"),
|
||||||
.hx.swap(.outerHTML)
|
.hx.swap(.outerHTML)
|
||||||
) {
|
) {
|
||||||
input(.class("hidden"), .name("projectID"), .value("\(projectID)"))
|
input(.class("hidden"), .name("projectID"), .value("\(projectID)"))
|
||||||
LabeledInput(
|
LabeledInput(
|
||||||
"SHR",
|
"SHR",
|
||||||
|
.name("sensibleHeatRatio"),
|
||||||
.type(.number),
|
.type(.number),
|
||||||
|
.value(sensibleHeatRatio),
|
||||||
.placeholder("0.83"),
|
.placeholder("0.83"),
|
||||||
.min("0"),
|
.min("0"),
|
||||||
.max("1"),
|
.max("1"),
|
||||||
.step("0.01"),
|
.step("0.01"),
|
||||||
.value(sensibleHeatRatio),
|
|
||||||
.autofocus
|
.autofocus
|
||||||
)
|
)
|
||||||
SubmitButton()
|
SubmitButton()
|
||||||
|
|||||||
Reference in New Issue
Block a user