From 6723f7a410423184e732cd4b69ffae80e3e9c464 Mon Sep 17 00:00:00 2001 From: Michael Housh Date: Thu, 29 Jan 2026 15:16:26 -0500 Subject: [PATCH] feat: Adds some documentation strings in ManualDCore module. --- Sources/DatabaseClient/EffectiveLength.swift | 2 +- Sources/ManualDClient/Helpers.swift | 4 - ...sses.swift => ComponentPressureLoss.swift} | 19 +-- .../ManualDCore/EffectiveLengthGroup.swift | 2 + Sources/ManualDCore/EquipmentInfo.swift | 4 + Sources/ManualDCore/EquivalentLength.swift | 112 +++++++++++------- .../{ => Extensions}/Numbers+string.swift | 0 .../PageRequest+extensions.swift | 0 Sources/ManualDCore/FrictionRate.swift | 12 ++ Sources/ManualDCore/Project.swift | 52 +++++++- Sources/ManualDCore/Room.swift | 85 +++++++------ Sources/ManualDCore/Theme.swift | 3 + Sources/ManualDCore/TrunkSize.swift | 41 ++++++- Sources/ManualDCore/User.swift | 20 +++- Sources/ManualDCore/UserProfile.swift | 34 ++++++ .../Views/EquivalentLengthTable.swift | 2 +- .../DatabaseClient+calculateDuctSizes.swift | 2 +- .../Internal/ManualDClient+frictionRate.swift | 4 +- .../EquivalentLengthForm+extensions.swift | 4 +- .../EffectiveLength/EffectiveLengthForm.swift | 4 +- .../Views/Project/ProjectView.swift | 2 +- 21 files changed, 294 insertions(+), 114 deletions(-) rename Sources/ManualDCore/{ComponentPressureLosses.swift => ComponentPressureLoss.swift} (91%) rename Sources/ManualDCore/{ => Extensions}/Numbers+string.swift (100%) rename Sources/ManualDCore/{ => Extensions}/PageRequest+extensions.swift (100%) diff --git a/Sources/DatabaseClient/EffectiveLength.swift b/Sources/DatabaseClient/EffectiveLength.swift index f6fbd9f..cce229e 100644 --- a/Sources/DatabaseClient/EffectiveLength.swift +++ b/Sources/DatabaseClient/EffectiveLength.swift @@ -180,7 +180,7 @@ final class EffectiveLengthModel: Model, @unchecked Sendable { name: name, type: .init(rawValue: type)!, straightLengths: straightLengths, - groups: JSONDecoder().decode([EquivalentLength.Group].self, from: groups), + groups: JSONDecoder().decode([EquivalentLength.FittingGroup].self, from: groups), createdAt: createdAt!, updatedAt: updatedAt! ) diff --git a/Sources/ManualDClient/Helpers.swift b/Sources/ManualDClient/Helpers.swift index ee77d61..a571926 100644 --- a/Sources/ManualDClient/Helpers.swift +++ b/Sources/ManualDClient/Helpers.swift @@ -46,10 +46,6 @@ extension TrunkSize { } } -extension ComponentPressureLosses { - var totalLosses: Double { values.reduce(0) { $0 + $1 } } -} - extension Array where Element == EffectiveLengthGroup { var totalEffectiveLength: Int { reduce(0) { $0 + $1.effectiveLength } diff --git a/Sources/ManualDCore/ComponentPressureLosses.swift b/Sources/ManualDCore/ComponentPressureLoss.swift similarity index 91% rename from Sources/ManualDCore/ComponentPressureLosses.swift rename to Sources/ManualDCore/ComponentPressureLoss.swift index b719e70..3c822da 100644 --- a/Sources/ManualDCore/ComponentPressureLosses.swift +++ b/Sources/ManualDCore/ComponentPressureLoss.swift @@ -1,6 +1,10 @@ import Dependencies import Foundation +/// Represents component pressure losses used in the friction rate worksheet. +/// +/// These are items such as filter, evaporator-coils, balance-dampers, etc. that +/// need to be overcome by the system fan. public struct ComponentPressureLoss: Codable, Equatable, Identifiable, Sendable { public let id: UUID @@ -44,7 +48,7 @@ extension ComponentPressureLoss { self.value = value } - // Return's commonly used default component pressure losses. + /// Commonly used default component pressure losses. public static func `default`(projectID: Project.ID) -> [Self] { [ .init(projectID: projectID, name: "supply-outlet", value: 0.03), @@ -75,20 +79,7 @@ extension Array where Element == ComponentPressureLoss { } } -public typealias ComponentPressureLosses = [String: Double] - #if DEBUG - extension ComponentPressureLosses { - public static var mock: Self { - [ - "evaporator-coil": 0.2, - "filter": 0.1, - "supply-outlet": 0.03, - "return-grille": 0.03, - "balancing-damper": 0.03, - ] - } - } extension Array where Element == ComponentPressureLoss { public static func mock(projectID: Project.ID) -> Self { diff --git a/Sources/ManualDCore/EffectiveLengthGroup.swift b/Sources/ManualDCore/EffectiveLengthGroup.swift index a621ac8..3839b36 100644 --- a/Sources/ManualDCore/EffectiveLengthGroup.swift +++ b/Sources/ManualDCore/EffectiveLengthGroup.swift @@ -1,5 +1,7 @@ import Foundation +// TODO: These are not used, should they be removed?? + // TODO: Add other description / label for items that have same group & letter, but // different effective length. public struct EffectiveLengthGroup: Codable, Equatable, Sendable { diff --git a/Sources/ManualDCore/EquipmentInfo.swift b/Sources/ManualDCore/EquipmentInfo.swift index a4d9c8e..39b4468 100644 --- a/Sources/ManualDCore/EquipmentInfo.swift +++ b/Sources/ManualDCore/EquipmentInfo.swift @@ -1,6 +1,10 @@ import Dependencies import Foundation +/// Represents the equipment information for a project. +/// +/// This is used in the friction rate worksheet and sizing ducts. It holds on to items +/// such as the target static pressure, cooling CFM, and heating CFM for the project. public struct EquipmentInfo: Codable, Equatable, Identifiable, Sendable { public let id: UUID public let projectID: Project.ID diff --git a/Sources/ManualDCore/EquivalentLength.swift b/Sources/ManualDCore/EquivalentLength.swift index 3a87e9e..9c29bad 100644 --- a/Sources/ManualDCore/EquivalentLength.swift +++ b/Sources/ManualDCore/EquivalentLength.swift @@ -1,18 +1,36 @@ import Dependencies import Foundation -// TODO: Not sure how to model effective length groups in the database. -// thinking perhaps just have a 'data' field that encoded / decodes -// to swift types?? +/// Represents the equivalent length of a single duct path. +/// +/// These consist of both straight lengths of duct / trunks, as well as the +/// equivalent length of duct fittings. They are used to determine the worst +/// case total equivalent length of duct that the system fan has to move air +/// through. +/// +/// There can be many equivalent lengths saved for a project, however the only +/// ones that matter in most calculations are the longest supply path and the +/// the longest return path. +/// +/// It is required that project has at least one equivalent length saved for +/// the supply and one saved for return, otherwise duct sizes can not be calculated. public struct EquivalentLength: Codable, Equatable, Identifiable, Sendable { + /// The id of the equivalent length. public let id: UUID + /// The project that this equivalent length is associated with. public let projectID: Project.ID + /// A unique name / label for this equivalent length. public let name: String + /// The type (supply or return) of the equivalent length. public let type: EffectiveLengthType + /// The straight lengths of duct for this equivalent length. public let straightLengths: [Int] - public let groups: [Group] + /// The fitting groups associated with this equivalent length. + public let groups: [FittingGroup] + /// When this equivalent length was created in the database. public let createdAt: Date + /// When this equivalent length was updated in the database. public let updatedAt: Date public init( @@ -21,7 +39,7 @@ public struct EquivalentLength: Codable, Equatable, Identifiable, Sendable { name: String, type: EquivalentLength.EffectiveLengthType, straightLengths: [Int], - groups: [EquivalentLength.Group], + groups: [EquivalentLength.FittingGroup], createdAt: Date, updatedAt: Date ) { @@ -38,20 +56,26 @@ public struct EquivalentLength: Codable, Equatable, Identifiable, Sendable { extension EquivalentLength { + /// Represents the data needed to create a new ``EquivalentLength`` in the database. public struct Create: Codable, Equatable, Sendable { + /// The project that this equivalent length is associated with. public let projectID: Project.ID + /// A unique name / label for this equivalent length. public let name: String + /// The type (supply or return) of the equivalent length. public let type: EffectiveLengthType + /// The straight lengths of duct for this equivalent length. public let straightLengths: [Int] - public let groups: [Group] + /// The fitting groups associated with this equivalent length. + public let groups: [FittingGroup] public init( projectID: Project.ID, name: String, type: EquivalentLength.EffectiveLengthType, straightLengths: [Int], - groups: [EquivalentLength.Group] + groups: [EquivalentLength.FittingGroup] ) { self.projectID = projectID self.name = name @@ -61,18 +85,25 @@ extension EquivalentLength { } } + /// Represents the data needed to update an ``EquivalentLength`` in the database. + /// + /// Only the supplied fields are updated. public struct Update: Codable, Equatable, Sendable { + /// A unique name / label for this equivalent length. public let name: String? + /// The type (supply or return) of the equivalent length. public let type: EffectiveLengthType? + /// The straight lengths of duct for this equivalent length. public let straightLengths: [Int]? - public let groups: [Group]? + /// The fitting groups associated with this equivalent length. + public let groups: [FittingGroup]? public init( name: String? = nil, type: EquivalentLength.EffectiveLengthType? = nil, straightLengths: [Int]? = nil, - groups: [EquivalentLength.Group]? = nil + groups: [EquivalentLength.FittingGroup]? = nil ) { self.name = name self.type = type @@ -81,16 +112,24 @@ extension EquivalentLength { } } + /// Represents the type of equivalent length, either supply or return. public enum EffectiveLengthType: String, CaseIterable, Codable, Sendable { case `return` case supply } - public struct Group: Codable, Equatable, Sendable { - + /// Represents a Manual-D fitting group. + /// + /// These are defined by Manual-D and convert different types of fittings into + /// an equivalent length of straight duct. + public struct FittingGroup: Codable, Equatable, Sendable { + /// The fitting group number. public let group: Int + /// The fitting group letter. public let letter: String + /// The equivalent length of the fitting. public let value: Double + /// The quantity of the fittings in the path. public let quantity: Int public init( @@ -106,11 +145,22 @@ extension EquivalentLength { } } + // TODO: Should these not be optional and we just throw an error or return nil from + // a database query. + + /// Represents the max ``EquivalentLength``'s for a project. + /// + /// Calculating the duct sizes for a project requires there to be a max supply + /// and a max return equivalent length, so this container represents those values + /// when they exist in the database. public struct MaxContainer: Codable, Equatable, Sendable { + + /// The longest supply equivalent length. public let supply: EquivalentLength? + /// The longest return equivalent length. public let `return`: EquivalentLength? - public var total: Double? { + public var totalEquivalentLength: Double? { guard let supply else { return nil } guard let `return` else { return nil } return supply.totalEquivalentLength + `return`.totalEquivalentLength @@ -124,14 +174,19 @@ extension EquivalentLength { } extension EquivalentLength { + + /// The calculated total equivalent length. + /// + /// This is the sum of all the straigth lengths and fitting groups (with quantities). public var totalEquivalentLength: Double { straightLengths.reduce(into: 0.0) { $0 += Double($1) } + groups.totalEquivalentLength } } -extension Array where Element == EquivalentLength.Group { +extension Array where Element == EquivalentLength.FittingGroup { + /// The calculated total equivalent length for the fitting groups. public var totalEquivalentLength: Double { reduce(into: 0.0) { $0 += ($1.value * Double($1.quantity)) @@ -179,37 +234,6 @@ extension Array where Element == EquivalentLength.Group { ), ] } - - public static let mocks: [Self] = [ - .init( - id: UUID(0), - projectID: UUID(0), - name: "Test Supply - 1", - type: .supply, - straightLengths: [10, 20, 25], - groups: [ - .init(group: 1, letter: "a", value: 20), - .init(group: 2, letter: "b", value: 15, quantity: 2), - .init(group: 3, letter: "c", value: 10, quantity: 1), - ], - createdAt: Date(), - updatedAt: Date() - ), - .init( - id: UUID(1), - projectID: UUID(0), - name: "Test Return - 1", - type: .return, - straightLengths: [10, 20, 25], - groups: [ - .init(group: 1, letter: "a", value: 20), - .init(group: 2, letter: "b", value: 15, quantity: 2), - .init(group: 3, letter: "c", value: 10, quantity: 1), - ], - createdAt: Date(), - updatedAt: Date() - ), - ] } #endif diff --git a/Sources/ManualDCore/Numbers+string.swift b/Sources/ManualDCore/Extensions/Numbers+string.swift similarity index 100% rename from Sources/ManualDCore/Numbers+string.swift rename to Sources/ManualDCore/Extensions/Numbers+string.swift diff --git a/Sources/ManualDCore/PageRequest+extensions.swift b/Sources/ManualDCore/Extensions/PageRequest+extensions.swift similarity index 100% rename from Sources/ManualDCore/PageRequest+extensions.swift rename to Sources/ManualDCore/Extensions/PageRequest+extensions.swift diff --git a/Sources/ManualDCore/FrictionRate.swift b/Sources/ManualDCore/FrictionRate.swift index 83f1be7..5491529 100644 --- a/Sources/ManualDCore/FrictionRate.swift +++ b/Sources/ManualDCore/FrictionRate.swift @@ -1,8 +1,14 @@ /// Holds onto values returned when calculating the design /// friction rate for a project. +/// +/// **NOTE:** This is not stored in the database, it is calculated on the fly. public struct FrictionRate: Codable, Equatable, Sendable { + /// The available static pressure is the equipment's design static pressure + /// minus the ``ComponentPressureLoss``es for the project. public let availableStaticPressure: Double + /// The calculated design friction rate value. public let value: Double + /// Whether the design friction rate is within a valid range. public var hasErrors: Bool { error != nil } public init( @@ -13,6 +19,7 @@ public struct FrictionRate: Codable, Equatable, Sendable { self.value = value } + /// The error if the design friction rate is out of a valid range. public var error: FrictionRateError? { if value >= 0.18 { return .init( @@ -37,8 +44,13 @@ public struct FrictionRate: Codable, Equatable, Sendable { } } +/// Represents an error when the ``FrictionRate`` is out of a valid range. +/// +/// This holds onto the reason for the error as well as possible resolutions. public struct FrictionRateError: Error, Equatable, Sendable { + /// The reason for the error. public let reason: String + /// The possible resolutions to the error. public let resolutions: [String] public init( diff --git a/Sources/ManualDCore/Project.swift b/Sources/ManualDCore/Project.swift index 04afc82..73ca8ec 100644 --- a/Sources/ManualDCore/Project.swift +++ b/Sources/ManualDCore/Project.swift @@ -1,16 +1,29 @@ import Dependencies import Foundation +/// Represents a single duct design project / system. +/// +/// Holds items such as project name and address. public struct Project: Codable, Equatable, Identifiable, Sendable { - + /// The unique ID of the project. public let id: UUID + /// The name of the project. public let name: String + /// The street address of the project. public let streetAddress: String + /// The city of the project. public let city: String + /// The state of the project. public let state: String + /// The zip code of the project. public let zipCode: String + /// The global sensible heat ratio for the project. + /// + /// **NOTE:** This is used for calculating the sensible cooling load for rooms. public let sensibleHeatRatio: Double? + /// When the project was created in the database. public let createdAt: Date + /// When the project was updated in the database. public let updatedAt: Date public init( @@ -37,14 +50,20 @@ public struct Project: Codable, Equatable, Identifiable, Sendable { } extension Project { - + /// Represents the data needed to create a new project. public struct Create: Codable, Equatable, Sendable { + /// The name of the project. public let name: String + /// The street address of the project. public let streetAddress: String + /// The city of the project. public let city: String + /// The state of the project. public let state: String + /// The zip code of the project. public let zipCode: String + /// The global sensible heat ratio for the project. public let sensibleHeatRatio: Double? public init( @@ -64,11 +83,19 @@ extension Project { } } + /// Represents steps that are completed in order to calculate the duct sizes + /// for a project. + /// + /// This is primarily used on the web pages to display errors or color of the + /// different steps of a project. public struct CompletedSteps: Codable, Equatable, Sendable { - + /// Whether there is ``EquipmentInfo`` for a project. public let equipmentInfo: Bool + /// Whether there are ``Room``'s for a project. public let rooms: Bool + /// Whether there are ``EquivalentLength``'s for a project. public let equivalentLength: Bool + /// Whether there is a ``FrictionRate`` for a project. public let frictionRate: Bool public init( @@ -84,13 +111,23 @@ extension Project { } } + /// Represents project details loaded from the database. + /// + /// This is generally used to perform duct sizing calculations for the + /// project, once all the steps have been completed. public struct Detail: Codable, Equatable, Sendable { + /// The project. public let project: Project + /// The component pressure losses for the project. public let componentLosses: [ComponentPressureLoss] + /// The equipment info for the project. public let equipmentInfo: EquipmentInfo + /// The equivalent lengths for the project. public let equivalentLengths: [EquivalentLength] + /// The rooms in the project. public let rooms: [Room] + /// The trunk sizes in the project. public let trunks: [TrunkSize] public init( @@ -110,13 +147,22 @@ extension Project { } } + /// Represents fields that can be updated for a project that has already been created. + /// + /// Only fields that are supplied get updated in the database. public struct Update: Codable, Equatable, Sendable { + /// The name of the project. public let name: String? + /// The street address of the project. public let streetAddress: String? + /// The city of the project. public let city: String? + /// The state of the project. public let state: String? + /// The zip code of the project. public let zipCode: String? + /// The global sensible heat ratio for the project. public let sensibleHeatRatio: Double? public init( diff --git a/Sources/ManualDCore/Room.swift b/Sources/ManualDCore/Room.swift index de12d32..54130e6 100644 --- a/Sources/ManualDCore/Room.swift +++ b/Sources/ManualDCore/Room.swift @@ -1,16 +1,37 @@ import Dependencies import Foundation +/// Represents a room in a project. +/// +/// This contains data such as the heating and cooling load for the +/// room, the number of registers in the room, and any rectangular +/// duct size calculations stored for the room. public struct Room: Codable, Equatable, Identifiable, Sendable { + /// The unique id of the room. public let id: UUID + /// The project this room is associated with. public let projectID: Project.ID + /// A unique name for the room in the project. public let name: String + /// The heating load required for the room (from Manual-J). public let heatingLoad: Double + /// The total cooling load required for the room (from Manual-J). public let coolingTotal: Double + /// An optional sensible cooling load for the room. + /// + /// **NOTE:** This is generally not set, but calculated from the project wide + /// sensible heat ratio. public let coolingSensible: Double? + /// The number of registers for the room. public let registerCount: Int + /// The rectangular duct size calculations for a room. + /// + /// **NOTE:** These are optionally set after the round sizes have been calculate + /// for a room. public let rectangularSizes: [RectangularSize]? + /// When the room was created in the database. public let createdAt: Date + /// When the room was updated in the database. public let updatedAt: Date public init( @@ -39,13 +60,19 @@ public struct Room: Codable, Equatable, Identifiable, Sendable { } extension Room { - + /// Represents the data required to create a new room for a project. public struct Create: Codable, Equatable, Sendable { + /// The project this room is associated with. public let projectID: Project.ID + /// A unique name for the room in the project. public let name: String + /// The heating load required for the room (from Manual-J). public let heatingLoad: Double + /// The total cooling load required for the room (from Manual-J). public let coolingTotal: Double + /// An optional sensible cooling load for the room. public let coolingSensible: Double? + /// The number of registers for the room. public let registerCount: Int public init( @@ -65,10 +92,17 @@ extension Room { } } + /// Represents a rectangular size calculation that is stored in the + /// database for a given room. + /// + /// These are done after the round duct sizes have been calculated and + /// can be used to calculate the equivalent rectangular size for a given run. public struct RectangularSize: Codable, Equatable, Identifiable, Sendable { - + /// The unique id of the rectangular size. public let id: UUID + /// The register the rectangular size is associated with. public let register: Int? + /// The height of the rectangular size, the width gets calculated. public let height: Int public init( @@ -82,12 +116,21 @@ extension Room { } } + /// Represents field that can be updated on a room after it's been created in the database. + /// + /// Only fields that are supplied get updated. public struct Update: Codable, Equatable, Sendable { + /// A unique name for the room in the project. public let name: String? + /// The heating load required for the room (from Manual-J). public let heatingLoad: Double? + /// The total cooling load required for the room (from Manual-J). public let coolingTotal: Double? + /// An optional sensible cooling load for the room. public let coolingSensible: Double? + /// The number of registers for the room. public let registerCount: Int? + /// The rectangular duct size calculations for a room. public let rectangularSizes: [RectangularSize]? public init( @@ -120,14 +163,20 @@ extension Room { extension Array where Element == Room { + /// The sum of heating loads for an array of rooms. public var totalHeatingLoad: Double { reduce(into: 0) { $0 += $1.heatingLoad } } + /// The sum of total cooling loads for an array of rooms. public var totalCoolingLoad: Double { reduce(into: 0) { $0 += $1.coolingTotal } } + /// The sum of sensible cooling loads for an array of rooms. + /// + /// - Parameters: + /// - shr: The project wide sensible heat ratio. public func totalCoolingSensible(shr: Double) -> Double { reduce(into: 0) { let sensible = $1.coolingSensible ?? ($1.coolingTotal * shr) @@ -139,38 +188,6 @@ extension Array where Element == Room { #if DEBUG extension Room { - public static let mocks = [ - Room( - id: UUID(0), - projectID: UUID(0), - name: "Kitchen", - heatingLoad: 12345, - coolingTotal: 1234, - registerCount: 2, - createdAt: Date(), - updatedAt: Date() - ), - Room( - id: UUID(1), - projectID: UUID(1), - name: "Bedroom - 1", - heatingLoad: 12345, - coolingTotal: 1456, - registerCount: 1, - createdAt: Date(), - updatedAt: Date() - ), - Room( - id: UUID(2), - projectID: UUID(2), - name: "Family Room", - heatingLoad: 12345, - coolingTotal: 1673, - registerCount: 3, - createdAt: Date(), - updatedAt: Date() - ), - ] public static func mock(projectID: Project.ID) -> [Self] { @Dependency(\.uuid) var uuid diff --git a/Sources/ManualDCore/Theme.swift b/Sources/ManualDCore/Theme.swift index 798e0ff..45c280f 100644 --- a/Sources/ManualDCore/Theme.swift +++ b/Sources/ManualDCore/Theme.swift @@ -1,5 +1,6 @@ import Foundation +/// Represents supported color themes for the website. public enum Theme: String, CaseIterable, Codable, Equatable, Sendable { case aqua case cupcake @@ -13,6 +14,7 @@ public enum Theme: String, CaseIterable, Codable, Equatable, Sendable { case retro case synthwave + /// Represents dark color themes. public static let darkThemes = [ Self.aqua, Self.cyberpunk, @@ -22,6 +24,7 @@ public enum Theme: String, CaseIterable, Codable, Equatable, Sendable { Self.synthwave, ] + /// Represents light color themes. public static let lightThemes = [ Self.cupcake, Self.light, diff --git a/Sources/ManualDCore/TrunkSize.swift b/Sources/ManualDCore/TrunkSize.swift index ad1eaa4..dc16d70 100644 --- a/Sources/ManualDCore/TrunkSize.swift +++ b/Sources/ManualDCore/TrunkSize.swift @@ -1,14 +1,23 @@ import Dependencies import Foundation -// Represents the database model. +/// Represents trunk calculations for a project. +/// +/// These are used to size trunk ducts / runs for multiple rooms or registers. public struct TrunkSize: Codable, Equatable, Identifiable, Sendable { + /// The unique identifier of the trunk size. public let id: UUID + /// The project the trunk size is for. public let projectID: Project.ID + /// The type of the trunk size (supply or return). public let type: TrunkType + /// The rooms / registers associated with the trunk size. public let rooms: [RoomProxy] + /// An optional rectangular height used to calculate the equivalent + /// rectangular size of the trunk. public let height: Int? + /// An optional name / label used for identifying the trunk. public let name: String? public init( @@ -29,12 +38,19 @@ public struct TrunkSize: Codable, Equatable, Identifiable, Sendable { } extension TrunkSize { + /// Represents the data needed to create a new ``TrunkSize`` in the database. public struct Create: Codable, Equatable, Sendable { + /// The project the trunk size is for. public let projectID: Project.ID + /// The type of the trunk size (supply or return). public let type: TrunkType + /// The rooms / registers associated with the trunk size. public let rooms: [Room.ID: [Int]] + /// An optional rectangular height used to calculate the equivalent + /// rectangular size of the trunk. public let height: Int? + /// An optional name / label used for identifying the trunk. public let name: String? public init( @@ -52,11 +68,19 @@ extension TrunkSize { } } + /// Represents the fields that can be updated on a ``TrunkSize`` in the database. + /// + /// Only supplied fields are updated. public struct Update: Codable, Equatable, Sendable { + /// The type of the trunk size (supply or return). public let type: TrunkType? + /// The rooms / registers associated with the trunk size. public let rooms: [Room.ID: [Int]]? + /// An optional rectangular height used to calculate the equivalent + /// rectangular size of the trunk. public let height: Int? + /// An optional name / label used for identifying the trunk. public let name: String? public init( @@ -72,18 +96,29 @@ extension TrunkSize { } } - public struct RoomProxy: Codable, Equatable, Identifiable, Sendable { + /// A container / wrapper around a ``Room`` that is used with a ``TrunkSize``. + /// + /// This is needed because a room can have multiple registers and it is possible + /// that a trunk does not serve all registers in that room. + @dynamicMemberLookup + public struct RoomProxy: Codable, Equatable, Sendable { - public var id: Room.ID { room.id } + /// The room associated with the ``TrunkSize``. public let room: Room + /// The specific room registers associated with the ``TrunkSize``. public let registers: [Int] public init(room: Room, registers: [Int]) { self.room = room self.registers = registers } + + public subscript(dynamicMember keyPath: KeyPath) -> T { + room[keyPath: keyPath] + } } + /// Represents the type of a ``TrunkSize``, either supply or return. public enum TrunkType: String, CaseIterable, Codable, Equatable, Sendable { case `return` case supply diff --git a/Sources/ManualDCore/User.swift b/Sources/ManualDCore/User.swift index f0869a8..96976a4 100644 --- a/Sources/ManualDCore/User.swift +++ b/Sources/ManualDCore/User.swift @@ -1,12 +1,17 @@ import Dependencies import Foundation -// FIX: Remove username. +/// Represents a user of the site. +/// public struct User: Codable, Equatable, Identifiable, Sendable { + /// The unique id of the user. public let id: UUID + /// The user's email address. public let email: String + /// When the user was created in the database. public let createdAt: Date + /// When the user was updated in the database. public let updatedAt: Date public init( @@ -23,10 +28,14 @@ public struct User: Codable, Equatable, Identifiable, Sendable { } extension User { + /// Represents the data required to create a new user. public struct Create: Codable, Equatable, Sendable { + /// The user's email address. public let email: String + /// The password for the user. public let password: String + /// The password confirmation, must match the password. public let confirmPassword: String public init( @@ -40,9 +49,13 @@ extension User { } } + /// Represents data required to login a user. public struct Login: Codable, Equatable, Sendable { + /// The user's email address. public let email: String + /// The password for the user. public let password: String + /// An optional page / route to navigate to after logging in the user. public let next: String? public init(email: String, password: String, next: String? = nil) { @@ -52,10 +65,13 @@ extension User { } } + /// Represents a user session token, for a logged in user. public struct Token: Codable, Equatable, Identifiable, Sendable { - + /// The unique id of the token. public let id: UUID + /// The user id the token is for. public let userID: User.ID + /// The token value. public let value: String public init(id: UUID, userID: User.ID, value: String) { diff --git a/Sources/ManualDCore/UserProfile.swift b/Sources/ManualDCore/UserProfile.swift index cae86a2..b5f2e9d 100644 --- a/Sources/ManualDCore/UserProfile.swift +++ b/Sources/ManualDCore/UserProfile.swift @@ -2,19 +2,32 @@ import Dependencies import Foundation extension User { + /// Represents a user's profile. Which contains extra information about a user of the site. public struct Profile: Codable, Equatable, Identifiable, Sendable { + /// The unique id of the profile public let id: UUID + /// The user id the profile is for. public let userID: User.ID + /// The user's first name. public let firstName: String + /// The user's last name. public let lastName: String + /// The user's company name. public let companyName: String + /// The user's street address. public let streetAddress: String + /// The user's city. public let city: String + /// The user's state. public let state: String + /// The user's zip code. public let zipCode: String + /// An optional theme that the user prefers. public let theme: Theme? + /// When the profile was created in the database. public let createdAt: Date + /// When the profile was updated in the database. public let updatedAt: Date public init( @@ -49,15 +62,25 @@ extension User { extension User.Profile { + /// Represents the data required to create a user profile. public struct Create: Codable, Equatable, Sendable { + /// The user id the profile is for. public let userID: User.ID + /// The user's first name. public let firstName: String + /// The user's last name. public let lastName: String + /// The user's company name. public let companyName: String + /// The user's street address. public let streetAddress: String + /// The user's city. public let city: String + /// The user's state. public let state: String + /// The user's zip code. public let zipCode: String + /// An optional theme that the user prefers. public let theme: Theme? public init( @@ -83,14 +106,25 @@ extension User.Profile { } } + /// Represents the fields that can be updated on a user's profile. + /// + /// Only fields that are supplied get updated. public struct Update: Codable, Equatable, Sendable { + /// The user's first name. public let firstName: String? + /// The user's last name. public let lastName: String? + /// The user's company name. public let companyName: String? + /// The user's street address. public let streetAddress: String? + /// The user's city. public let city: String? + /// The user's state. public let state: String? + /// The user's zip code. public let zipCode: String? + /// An optional theme that the user prefers. public let theme: Theme? public init( diff --git a/Sources/PdfClient/Views/EquivalentLengthTable.swift b/Sources/PdfClient/Views/EquivalentLengthTable.swift index 76f772a..4c2342e 100644 --- a/Sources/PdfClient/Views/EquivalentLengthTable.swift +++ b/Sources/PdfClient/Views/EquivalentLengthTable.swift @@ -41,7 +41,7 @@ struct EffectiveLengthsTable: HTML, Sendable { } struct EffectiveLengthGroupTable: HTML, Sendable { - let groups: [EquivalentLength.Group] + let groups: [EquivalentLength.FittingGroup] var body: some HTML { table { diff --git a/Sources/ProjectClient/Internal/DatabaseClient+calculateDuctSizes.swift b/Sources/ProjectClient/Internal/DatabaseClient+calculateDuctSizes.swift index c37aaac..130a7f6 100644 --- a/Sources/ProjectClient/Internal/DatabaseClient+calculateDuctSizes.swift +++ b/Sources/ProjectClient/Internal/DatabaseClient+calculateDuctSizes.swift @@ -169,7 +169,7 @@ extension DatabaseClient { equipmentInfo: EquipmentInfo, equivalentLengths: EquivalentLength.MaxContainer ) -> DesignFrictionRateResponse? { - guard let tel = equivalentLengths.total, + guard let tel = equivalentLengths.totalEquivalentLength, componentLosses.count > 0 else { return nil } diff --git a/Sources/ProjectClient/Internal/ManualDClient+frictionRate.swift b/Sources/ProjectClient/Internal/ManualDClient+frictionRate.swift index 3447c27..7941a4b 100644 --- a/Sources/ProjectClient/Internal/ManualDClient+frictionRate.swift +++ b/Sources/ProjectClient/Internal/ManualDClient+frictionRate.swift @@ -8,7 +8,7 @@ extension ManualDClient { func frictionRate(details: Project.Detail) async throws -> ProjectClient.FrictionRateResponse { let maxContainer = details.maxContainer - guard let totalEquivalentLength = maxContainer.total else { + guard let totalEquivalentLength = maxContainer.totalEquivalentLength else { return .init(componentLosses: details.componentLosses, equivalentLengths: maxContainer) } @@ -36,7 +36,7 @@ extension ManualDClient { return .init(componentLosses: componentLosses, equivalentLengths: lengths) } - guard let totalEquivalentLength = lengths.total else { + guard let totalEquivalentLength = lengths.totalEquivalentLength else { return .init(componentLosses: componentLosses, equivalentLengths: lengths) } diff --git a/Sources/ViewController/Extensions/EquivalentLengthForm+extensions.swift b/Sources/ViewController/Extensions/EquivalentLengthForm+extensions.swift index fa03b6b..6a7916d 100644 --- a/Sources/ViewController/Extensions/EquivalentLengthForm+extensions.swift +++ b/Sources/ViewController/Extensions/EquivalentLengthForm+extensions.swift @@ -12,8 +12,8 @@ extension SiteRoute.View.ProjectRoute.EquivalentLengthRoute.StepThree { } } - var groups: [EquivalentLength.Group] { - var groups = [EquivalentLength.Group]() + var groups: [EquivalentLength.FittingGroup] { + var groups = [EquivalentLength.FittingGroup]() for (n, group) in groupGroups.enumerated() { groups.append( .init( diff --git a/Sources/ViewController/Views/EffectiveLength/EffectiveLengthForm.swift b/Sources/ViewController/Views/EffectiveLength/EffectiveLengthForm.swift index b6b965b..17e2f24 100644 --- a/Sources/ViewController/Views/EffectiveLength/EffectiveLengthForm.swift +++ b/Sources/ViewController/Views/EffectiveLength/EffectiveLengthForm.swift @@ -255,9 +255,9 @@ struct StraightLengthField: HTML, Sendable { struct GroupField: HTML, Sendable { let style: EquivalentLength.EffectiveLengthType - let group: EquivalentLength.Group? + let group: EquivalentLength.FittingGroup? - init(style: EquivalentLength.EffectiveLengthType, group: EquivalentLength.Group? = nil) { + init(style: EquivalentLength.EffectiveLengthType, group: EquivalentLength.FittingGroup? = nil) { self.style = style self.group = group } diff --git a/Sources/ViewController/Views/Project/ProjectView.swift b/Sources/ViewController/Views/Project/ProjectView.swift index e4ce1ac..9a70a4f 100644 --- a/Sources/ViewController/Views/Project/ProjectView.swift +++ b/Sources/ViewController/Views/Project/ProjectView.swift @@ -238,7 +238,7 @@ extension ManualDClient { guard let staticPressure = equipmentInfo?.staticPressure else { return nil } - guard let totalEquivalentLength = effectiveLength.total else { + guard let totalEquivalentLength = effectiveLength.totalEquivalentLength else { return nil } return try await self.frictionRate(