feat: Adds level field to rooms, updates urls to point to public mirror of the project.
Some checks failed
CI / Linux Tests (push) Failing after 15s
Some checks failed
CI / Linux Tests (push) Failing after 15s
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"originHash" : "9668a267c19bc66e844dc3e46718c4415359171b587f26c2a592bc347bd5446a",
|
"originHash" : "5bbd172c602e6484b32782f8cb68faca2d5120adc511acdff74e62fec2178c15",
|
||||||
"pins" : [
|
"pins" : [
|
||||||
{
|
{
|
||||||
"identity" : "async-http-client",
|
"identity" : "async-http-client",
|
||||||
@@ -456,8 +456,8 @@
|
|||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/m-housh/swift-validations.git",
|
"location" : "https://github.com/m-housh/swift-validations.git",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "95ea5d267e37f6cdb9f91c5c8a01e718b9299db6",
|
"revision" : "ae939c146f380ca12d0a04ca1f6b0c4c270fdd5a",
|
||||||
"version" : "0.3.4"
|
"version" : "0.3.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ let package = Package(
|
|||||||
.package(url: "https://github.com/elementary-swift/elementary.git", from: "0.6.0"),
|
.package(url: "https://github.com/elementary-swift/elementary.git", from: "0.6.0"),
|
||||||
.package(url: "https://github.com/elementary-swift/elementary-htmx.git", from: "0.5.0"),
|
.package(url: "https://github.com/elementary-swift/elementary-htmx.git", from: "0.5.0"),
|
||||||
.package(url: "https://github.com/vapor-community/vapor-elementary.git", from: "0.1.0"),
|
.package(url: "https://github.com/vapor-community/vapor-elementary.git", from: "0.1.0"),
|
||||||
.package(url: "https://github.com/m-housh/swift-validations.git", from: "0.1.0"),
|
.package(url: "https://github.com/m-housh/swift-validations.git", from: "0.3.5"),
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
.executableTarget(
|
.executableTarget(
|
||||||
@@ -168,6 +168,7 @@ let package = Package(
|
|||||||
.product(name: "CasePaths", package: "swift-case-paths"),
|
.product(name: "CasePaths", package: "swift-case-paths"),
|
||||||
.product(name: "Dependencies", package: "swift-dependencies"),
|
.product(name: "Dependencies", package: "swift-dependencies"),
|
||||||
.product(name: "Fluent", package: "fluent"),
|
.product(name: "Fluent", package: "fluent"),
|
||||||
|
.product(name: "Tagged", package: "swift-tagged"),
|
||||||
.product(name: "URLRouting", package: "swift-url-routing"),
|
.product(name: "URLRouting", package: "swift-url-routing"),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -6920,6 +6920,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.grid-flow-col {
|
||||||
|
grid-auto-flow: column;
|
||||||
|
}
|
||||||
|
.grid-flow-row {
|
||||||
|
grid-auto-flow: row;
|
||||||
|
}
|
||||||
.grid-cols-1 {
|
.grid-cols-1 {
|
||||||
grid-template-columns: repeat(1, minmax(0, 1fr));
|
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
@@ -8753,6 +8759,9 @@
|
|||||||
color: var(--color-warning);
|
color: var(--color-warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.text-accent {
|
||||||
|
color: var(--color-accent);
|
||||||
|
}
|
||||||
.text-base-content {
|
.text-base-content {
|
||||||
color: var(--color-base-content);
|
color: var(--color-base-content);
|
||||||
}
|
}
|
||||||
@@ -9732,6 +9741,31 @@
|
|||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.md\:justify-between {
|
||||||
|
@media (width >= 48rem) {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.md\:place-self-center {
|
||||||
|
@media (width >= 48rem) {
|
||||||
|
place-self: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.md\:place-self-end {
|
||||||
|
@media (width >= 48rem) {
|
||||||
|
place-self: end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.md\:place-self-start {
|
||||||
|
@media (width >= 48rem) {
|
||||||
|
place-self: start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.md\:justify-self-end {
|
||||||
|
@media (width >= 48rem) {
|
||||||
|
justify-self: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
.lg\:drawer-open {
|
.lg\:drawer-open {
|
||||||
@media (width >= 64rem) {
|
@media (width >= 64rem) {
|
||||||
@layer daisyui.l1.l2.l3 {
|
@layer daisyui.l1.l2.l3 {
|
||||||
|
|||||||
@@ -38,6 +38,11 @@ struct RoomCreateParser: ParserPrinter {
|
|||||||
ParsePrint {
|
ParsePrint {
|
||||||
Prefix { $0 != UInt8(ascii: ",") }.map(.string)
|
Prefix { $0 != UInt8(ascii: ",") }.map(.string)
|
||||||
",".utf8
|
",".utf8
|
||||||
|
Optionally {
|
||||||
|
Int.parser()
|
||||||
|
.map(.memberwise(Room.Level.init(rawValue:)))
|
||||||
|
}
|
||||||
|
",".utf8
|
||||||
Double.parser()
|
Double.parser()
|
||||||
",".utf8
|
",".utf8
|
||||||
Optionally {
|
Optionally {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ extension DatabaseClient.Rooms: TestDependencyKey {
|
|||||||
$0.delegatedToName != nil && $0.delegatedToName != ""
|
$0.delegatedToName != nil && $0.delegatedToName != ""
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Filter out the rest of the rooms that don't delegate their airflow / loads.
|
||||||
let initialRooms = rows.filter({
|
let initialRooms = rows.filter({
|
||||||
$0.delegatedToName == nil || $0.delegatedToName == ""
|
$0.delegatedToName == nil || $0.delegatedToName == ""
|
||||||
})
|
})
|
||||||
@@ -54,6 +55,7 @@ extension DatabaseClient.Rooms: TestDependencyKey {
|
|||||||
array.append(
|
array.append(
|
||||||
Room.Create.init(
|
Room.Create.init(
|
||||||
name: row.name,
|
name: row.name,
|
||||||
|
level: row.level,
|
||||||
heatingLoad: row.heatingLoad,
|
heatingLoad: row.heatingLoad,
|
||||||
coolingTotal: row.coolingTotal,
|
coolingTotal: row.coolingTotal,
|
||||||
coolingSensible: row.coolingSensible,
|
coolingSensible: row.coolingSensible,
|
||||||
@@ -134,6 +136,7 @@ extension Room.CSV.Row {
|
|||||||
assert(delegatedToName == nil || delegatedToName == "")
|
assert(delegatedToName == nil || delegatedToName == "")
|
||||||
return .init(
|
return .init(
|
||||||
name: name,
|
name: name,
|
||||||
|
level: level,
|
||||||
heatingLoad: heatingLoad,
|
heatingLoad: heatingLoad,
|
||||||
coolingTotal: coolingTotal,
|
coolingTotal: coolingTotal,
|
||||||
coolingSensible: coolingSensible,
|
coolingSensible: coolingSensible,
|
||||||
@@ -170,6 +173,7 @@ extension Room.Create {
|
|||||||
|
|
||||||
return .init(
|
return .init(
|
||||||
name: name,
|
name: name,
|
||||||
|
level: level?.rawValue,
|
||||||
heatingLoad: heatingLoad,
|
heatingLoad: heatingLoad,
|
||||||
coolingLoad: coolingLoad,
|
coolingLoad: coolingLoad,
|
||||||
registerCount: registerCount,
|
registerCount: registerCount,
|
||||||
@@ -187,6 +191,7 @@ extension Room {
|
|||||||
try await database.schema(RoomModel.schema)
|
try await database.schema(RoomModel.schema)
|
||||||
.id()
|
.id()
|
||||||
.field("name", .string, .required)
|
.field("name", .string, .required)
|
||||||
|
.field("level", .int8)
|
||||||
.field("heatingLoad", .double, .required)
|
.field("heatingLoad", .double, .required)
|
||||||
.field("coolingLoad", .dictionary, .required)
|
.field("coolingLoad", .dictionary, .required)
|
||||||
.field("registerCount", .int8, .required)
|
.field("registerCount", .int8, .required)
|
||||||
@@ -217,6 +222,9 @@ final class RoomModel: Model, @unchecked Sendable, Validatable {
|
|||||||
@Field(key: "name")
|
@Field(key: "name")
|
||||||
var name: String
|
var name: String
|
||||||
|
|
||||||
|
@Field(key: "level")
|
||||||
|
var level: Int?
|
||||||
|
|
||||||
@Field(key: "heatingLoad")
|
@Field(key: "heatingLoad")
|
||||||
var heatingLoad: Double
|
var heatingLoad: Double
|
||||||
|
|
||||||
@@ -246,6 +254,7 @@ final class RoomModel: Model, @unchecked Sendable, Validatable {
|
|||||||
init(
|
init(
|
||||||
id: UUID? = nil,
|
id: UUID? = nil,
|
||||||
name: String,
|
name: String,
|
||||||
|
level: Int? = nil,
|
||||||
heatingLoad: Double,
|
heatingLoad: Double,
|
||||||
coolingLoad: Room.CoolingLoad,
|
coolingLoad: Room.CoolingLoad,
|
||||||
registerCount: Int,
|
registerCount: Int,
|
||||||
@@ -257,6 +266,7 @@ final class RoomModel: Model, @unchecked Sendable, Validatable {
|
|||||||
) {
|
) {
|
||||||
self.id = id
|
self.id = id
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.level = level
|
||||||
self.heatingLoad = heatingLoad
|
self.heatingLoad = heatingLoad
|
||||||
self.coolingLoad = coolingLoad
|
self.coolingLoad = coolingLoad
|
||||||
self.registerCount = registerCount
|
self.registerCount = registerCount
|
||||||
@@ -272,6 +282,7 @@ final class RoomModel: Model, @unchecked Sendable, Validatable {
|
|||||||
id: requireID(),
|
id: requireID(),
|
||||||
projectID: $project.id,
|
projectID: $project.id,
|
||||||
name: name,
|
name: name,
|
||||||
|
level: level.map(Room.Level.init(rawValue:)),
|
||||||
heatingLoad: heatingLoad,
|
heatingLoad: heatingLoad,
|
||||||
coolingLoad: coolingLoad,
|
coolingLoad: coolingLoad,
|
||||||
registerCount: registerCount,
|
registerCount: registerCount,
|
||||||
@@ -287,6 +298,9 @@ final class RoomModel: Model, @unchecked Sendable, Validatable {
|
|||||||
if let name = updates.name, name != self.name {
|
if let name = updates.name, name != self.name {
|
||||||
self.name = name
|
self.name = name
|
||||||
}
|
}
|
||||||
|
if let level = updates.level?.rawValue, level != self.level {
|
||||||
|
self.level = level
|
||||||
|
}
|
||||||
if let heatingLoad = updates.heatingLoad, heatingLoad != self.heatingLoad {
|
if let heatingLoad = updates.heatingLoad, heatingLoad != self.heatingLoad {
|
||||||
self.heatingLoad = heatingLoad
|
self.heatingLoad = heatingLoad
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import Dependencies
|
import Dependencies
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import Tagged
|
||||||
|
|
||||||
/// Represents a room in a project.
|
/// Represents a room in a project.
|
||||||
///
|
///
|
||||||
@@ -17,6 +18,9 @@ public struct Room: Codable, Equatable, Identifiable, Sendable {
|
|||||||
/// A unique name for the room in the project.
|
/// A unique name for the room in the project.
|
||||||
public let name: String
|
public let name: String
|
||||||
|
|
||||||
|
/// The level of the home the room is on.
|
||||||
|
public let level: Level?
|
||||||
|
|
||||||
/// The heating load required for the room (from Manual-J).
|
/// The heating load required for the room (from Manual-J).
|
||||||
public let heatingLoad: Double
|
public let heatingLoad: Double
|
||||||
|
|
||||||
@@ -45,6 +49,7 @@ public struct Room: Codable, Equatable, Identifiable, Sendable {
|
|||||||
id: UUID,
|
id: UUID,
|
||||||
projectID: Project.ID,
|
projectID: Project.ID,
|
||||||
name: String,
|
name: String,
|
||||||
|
level: Level? = nil,
|
||||||
heatingLoad: Double,
|
heatingLoad: Double,
|
||||||
coolingLoad: CoolingLoad,
|
coolingLoad: CoolingLoad,
|
||||||
registerCount: Int = 1,
|
registerCount: Int = 1,
|
||||||
@@ -56,6 +61,7 @@ public struct Room: Codable, Equatable, Identifiable, Sendable {
|
|||||||
self.id = id
|
self.id = id
|
||||||
self.projectID = projectID
|
self.projectID = projectID
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.level = level
|
||||||
self.heatingLoad = heatingLoad
|
self.heatingLoad = heatingLoad
|
||||||
self.coolingLoad = coolingLoad
|
self.coolingLoad = coolingLoad
|
||||||
self.registerCount = registerCount
|
self.registerCount = registerCount
|
||||||
@@ -98,6 +104,11 @@ public struct Room: Codable, Equatable, Identifiable, Sendable {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum LevelTag {}
|
||||||
|
|
||||||
|
public typealias Level = Tagged<LevelTag, Int>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Room {
|
extension Room {
|
||||||
@@ -106,6 +117,9 @@ extension Room {
|
|||||||
/// A unique name for the room in the project.
|
/// A unique name for the room in the project.
|
||||||
public let name: String
|
public let name: String
|
||||||
|
|
||||||
|
/// An optional level of the home the room is on.
|
||||||
|
public let level: Room.Level?
|
||||||
|
|
||||||
/// The heating load required for the room (from Manual-J).
|
/// The heating load required for the room (from Manual-J).
|
||||||
public let heatingLoad: Double
|
public let heatingLoad: Double
|
||||||
|
|
||||||
@@ -127,6 +141,7 @@ extension Room {
|
|||||||
|
|
||||||
public init(
|
public init(
|
||||||
name: String,
|
name: String,
|
||||||
|
level: Room.Level? = nil,
|
||||||
heatingLoad: Double,
|
heatingLoad: Double,
|
||||||
coolingTotal: Double? = nil,
|
coolingTotal: Double? = nil,
|
||||||
coolingSensible: Double? = nil,
|
coolingSensible: Double? = nil,
|
||||||
@@ -134,6 +149,7 @@ extension Room {
|
|||||||
delegatedTo: Room.ID? = nil
|
delegatedTo: Room.ID? = nil
|
||||||
) {
|
) {
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.level = level
|
||||||
self.heatingLoad = heatingLoad
|
self.heatingLoad = heatingLoad
|
||||||
self.coolingTotal = coolingTotal
|
self.coolingTotal = coolingTotal
|
||||||
self.coolingSensible = coolingSensible
|
self.coolingSensible = coolingSensible
|
||||||
@@ -160,6 +176,9 @@ extension Room {
|
|||||||
/// A unique name for the room in the project.
|
/// A unique name for the room in the project.
|
||||||
public let name: String
|
public let name: String
|
||||||
|
|
||||||
|
/// An optional level of the home the room is on.
|
||||||
|
public let level: Room.Level?
|
||||||
|
|
||||||
/// The heating load required for the room (from Manual-J).
|
/// The heating load required for the room (from Manual-J).
|
||||||
public let heatingLoad: Double
|
public let heatingLoad: Double
|
||||||
|
|
||||||
@@ -177,6 +196,7 @@ extension Room {
|
|||||||
|
|
||||||
public init(
|
public init(
|
||||||
name: String,
|
name: String,
|
||||||
|
level: Room.Level? = nil,
|
||||||
heatingLoad: Double,
|
heatingLoad: Double,
|
||||||
coolingTotal: Double? = nil,
|
coolingTotal: Double? = nil,
|
||||||
coolingSensible: Double? = nil,
|
coolingSensible: Double? = nil,
|
||||||
@@ -184,6 +204,7 @@ extension Room {
|
|||||||
delegatedToName: String? = nil
|
delegatedToName: String? = nil
|
||||||
) {
|
) {
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.level = level
|
||||||
self.heatingLoad = heatingLoad
|
self.heatingLoad = heatingLoad
|
||||||
self.coolingTotal = coolingTotal
|
self.coolingTotal = coolingTotal
|
||||||
self.coolingSensible = coolingSensible
|
self.coolingSensible = coolingSensible
|
||||||
@@ -226,6 +247,10 @@ extension Room {
|
|||||||
public struct Update: Codable, Equatable, Sendable {
|
public struct Update: Codable, Equatable, Sendable {
|
||||||
/// A unique name for the room in the project.
|
/// A unique name for the room in the project.
|
||||||
public let name: String?
|
public let name: String?
|
||||||
|
|
||||||
|
/// An optional level of the home the room is on.
|
||||||
|
public let level: Room.Level?
|
||||||
|
|
||||||
/// The heating load required for the room (from Manual-J).
|
/// The heating load required for the room (from Manual-J).
|
||||||
public let heatingLoad: Double?
|
public let heatingLoad: Double?
|
||||||
/// The total cooling load required for the room (from Manual-J).
|
/// The total cooling load required for the room (from Manual-J).
|
||||||
@@ -246,12 +271,14 @@ extension Room {
|
|||||||
|
|
||||||
public init(
|
public init(
|
||||||
name: String? = nil,
|
name: String? = nil,
|
||||||
|
level: Room.Level? = nil,
|
||||||
heatingLoad: Double? = nil,
|
heatingLoad: Double? = nil,
|
||||||
coolingTotal: Double? = nil,
|
coolingTotal: Double? = nil,
|
||||||
coolingSensible: Double? = nil,
|
coolingSensible: Double? = nil,
|
||||||
registerCount: Int? = nil
|
registerCount: Int? = nil
|
||||||
) {
|
) {
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.level = level
|
||||||
self.heatingLoad = heatingLoad
|
self.heatingLoad = heatingLoad
|
||||||
self.coolingTotal = coolingTotal
|
self.coolingTotal = coolingTotal
|
||||||
self.coolingSensible = coolingSensible
|
self.coolingSensible = coolingSensible
|
||||||
@@ -263,6 +290,7 @@ extension Room {
|
|||||||
rectangularSizes: [RectangularSize]
|
rectangularSizes: [RectangularSize]
|
||||||
) {
|
) {
|
||||||
self.name = nil
|
self.name = nil
|
||||||
|
self.level = nil
|
||||||
self.heatingLoad = nil
|
self.heatingLoad = nil
|
||||||
self.coolingTotal = nil
|
self.coolingTotal = nil
|
||||||
self.coolingSensible = nil
|
self.coolingSensible = nil
|
||||||
@@ -304,6 +332,16 @@ public struct CoolingLoadError: Error, Equatable, Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Room.Level {
|
||||||
|
/// The label for the level, i.e. 'Basement' or 'Level-1', etc.
|
||||||
|
public var label: String {
|
||||||
|
if rawValue <= 0 {
|
||||||
|
return "Basement"
|
||||||
|
}
|
||||||
|
return "Level-\(rawValue)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
|
||||||
extension Room {
|
extension Room {
|
||||||
|
|||||||
@@ -216,12 +216,6 @@ extension SiteRoute.View.ProjectRoute {
|
|||||||
}
|
}
|
||||||
Method.post
|
Method.post
|
||||||
Body().map(.memberwise(Room.CSV.init))
|
Body().map(.memberwise(Room.CSV.init))
|
||||||
// Body {
|
|
||||||
// FormData {
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// .map(.memberwise(Room.CSV.init))
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
Route(.case(Self.delete)) {
|
Route(.case(Self.delete)) {
|
||||||
Path {
|
Path {
|
||||||
@@ -242,6 +236,12 @@ extension SiteRoute.View.ProjectRoute {
|
|||||||
Body {
|
Body {
|
||||||
FormData {
|
FormData {
|
||||||
Field("name", .string)
|
Field("name", .string)
|
||||||
|
Optionally {
|
||||||
|
Field("level") {
|
||||||
|
Int.parser()
|
||||||
|
}
|
||||||
|
.map(.memberwise(Room.Level.init(rawValue:)))
|
||||||
|
}
|
||||||
Field("heatingLoad") { Double.parser() }
|
Field("heatingLoad") { Double.parser() }
|
||||||
Optionally {
|
Optionally {
|
||||||
Field("coolingTotal") { Double.parser() }
|
Field("coolingTotal") { Double.parser() }
|
||||||
@@ -268,6 +268,12 @@ extension SiteRoute.View.ProjectRoute {
|
|||||||
Optionally {
|
Optionally {
|
||||||
Field("name", .string)
|
Field("name", .string)
|
||||||
}
|
}
|
||||||
|
Optionally {
|
||||||
|
Field("level") {
|
||||||
|
Int.parser()
|
||||||
|
}
|
||||||
|
.map(.memberwise(Room.Level.init(rawValue:)))
|
||||||
|
}
|
||||||
Optionally {
|
Optionally {
|
||||||
Field("heatingLoad") { Double.parser() }
|
Field("heatingLoad") { Double.parser() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import Elementary
|
import Elementary
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import Validations
|
||||||
|
|
||||||
public struct ResultView<ValueView, ErrorView>: HTML where ValueView: HTML, ErrorView: HTML {
|
public struct ResultView<ValueView, ErrorView>: HTML where ValueView: HTML, ErrorView: HTML {
|
||||||
|
|
||||||
@@ -69,7 +70,11 @@ public struct ErrorView: HTML, Sendable {
|
|||||||
div {
|
div {
|
||||||
h1(.class("text-xl font-bold text-error")) { "Oops: Error" }
|
h1(.class("text-xl font-bold text-error")) { "Oops: Error" }
|
||||||
p {
|
p {
|
||||||
"\(error.localizedDescription)"
|
if let validationError = (error as? ValidationError) {
|
||||||
|
"\(validationError.debugDescription)"
|
||||||
|
} else {
|
||||||
|
"\(error.localizedDescription)"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ struct HomeView: HTML, Sendable {
|
|||||||
header
|
header
|
||||||
a(
|
a(
|
||||||
.class("btn btn-ghost text-md text-primary font-bold italic"),
|
.class("btn btn-ghost text-md text-primary font-bold italic"),
|
||||||
.href("https://git.housh.dev/michael/swift-duct-calc"),
|
.href("https://github.com/m-housh/swift-duct-calc"),
|
||||||
.target(.blank)
|
.target(.blank)
|
||||||
) {
|
) {
|
||||||
"Open source residential duct design program"
|
"Open source residential duct design program"
|
||||||
|
|||||||
@@ -94,22 +94,36 @@ public struct MainPage<Inner: HTML>: SendableHTMLDocument where Inner: Sendable
|
|||||||
footer(
|
footer(
|
||||||
.class(
|
.class(
|
||||||
"""
|
"""
|
||||||
footer sm:footer-horizontal footer-center
|
footer footer-horizontal footer-center
|
||||||
bg-base-300 text-base-content p-4
|
bg-base-300 text-base-content p-4
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
aside {
|
aside(
|
||||||
p {
|
.class("grid-flow-row items-center")
|
||||||
"Copyright © \(Date().description.prefix(4)) - All rights reserved by Michael Housh"
|
) {
|
||||||
|
|
||||||
|
div(.class("flex mx-auto")) {
|
||||||
|
a(
|
||||||
|
.class("btn btn-ghost"),
|
||||||
|
.href("mailto:support@ductcalc.pro")
|
||||||
|
) {
|
||||||
|
SVG(.email)
|
||||||
|
span { "support@ductcalc.pro" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a(
|
a(
|
||||||
.class("btn btn-ghost"),
|
.class("btn btn-ghost mx-auto"),
|
||||||
.href("https://git.housh.dev/michael/swift-duct-calc/src/branch/main/LICENSE"),
|
.href("https://github.com/m-housh/swift-duct-calc/src/branch/main/LICENSE"),
|
||||||
.target(.blank)
|
.target(.blank)
|
||||||
) {
|
) {
|
||||||
"Openly licensed via CC-BY-NC-SA 4.0"
|
"Openly licensed via CC-BY-NC-SA 4.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p(.class("")) {
|
||||||
|
"Copyright © \(Date().description.prefix(4)) - All rights reserved by Michael Housh"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,12 +34,12 @@ struct Navbar: HTML, Sendable {
|
|||||||
label(
|
label(
|
||||||
.for("my-drawer-1"),
|
.for("my-drawer-1"),
|
||||||
.class("size-7"),
|
.class("size-7"),
|
||||||
.init(name: "aria-label", value: "open sidebar")
|
.init(name: "aria-label", value: "open / close sidebar")
|
||||||
) {
|
) {
|
||||||
SVG(.sidebarToggle)
|
SVG(.sidebarToggle)
|
||||||
}
|
}
|
||||||
.navButton()
|
.navButton()
|
||||||
.tooltip("Open sidebar", position: .right)
|
.tooltip("Open / close sidebar", position: .right)
|
||||||
}
|
}
|
||||||
|
|
||||||
a(
|
a(
|
||||||
|
|||||||
@@ -68,6 +68,21 @@ struct RoomForm: HTML, Sendable {
|
|||||||
.value(room?.name)
|
.value(room?.name)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
LabeledInput(
|
||||||
|
"Level",
|
||||||
|
.name("level"),
|
||||||
|
.type(.number),
|
||||||
|
.placeholder("1 (Optional)"),
|
||||||
|
.value(room?.level?.rawValue),
|
||||||
|
.min("-1"),
|
||||||
|
.step("1")
|
||||||
|
)
|
||||||
|
div(.class("text-sm italic -mt-2")) {
|
||||||
|
span(.class("text-primary")) {
|
||||||
|
"Use -1 or 0 for a basement"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LabeledInput(
|
LabeledInput(
|
||||||
"Heating Load",
|
"Heating Load",
|
||||||
.name("heatingLoad"),
|
.name("heatingLoad"),
|
||||||
@@ -78,8 +93,6 @@ struct RoomForm: HTML, Sendable {
|
|||||||
.value(room?.heatingLoad)
|
.value(room?.heatingLoad)
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: Add description that only one is required (cooling total or sensible)
|
|
||||||
|
|
||||||
LabeledInput(
|
LabeledInput(
|
||||||
"Cooling Total",
|
"Cooling Total",
|
||||||
.name("coolingTotal"),
|
.name("coolingTotal"),
|
||||||
@@ -97,6 +110,14 @@ struct RoomForm: HTML, Sendable {
|
|||||||
.min("0"),
|
.min("0"),
|
||||||
.value(room?.coolingLoad.sensible)
|
.value(room?.coolingLoad.sensible)
|
||||||
)
|
)
|
||||||
|
div(.class("text-primary text-sm italic -mt-2")) {
|
||||||
|
p {
|
||||||
|
"Should enter at least one of the cooling loads."
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
"Both are also acceptable."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LabeledInput(
|
LabeledInput(
|
||||||
"Registers",
|
"Registers",
|
||||||
|
|||||||
@@ -15,6 +15,14 @@ struct RoomsView: HTML, Sendable {
|
|||||||
.appendingPath("csv")
|
.appendingPath("csv")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sort the rooms based on level, they should already be sorted by name,
|
||||||
|
// so this puts lower level rooms towards the top in alphabetical order.
|
||||||
|
//
|
||||||
|
// If rooms do not have a level we shove those all the way to the bottom.
|
||||||
|
private var sortedRooms: [Room] {
|
||||||
|
rooms.sorted { ($0.level?.rawValue ?? 20) < ($1.level?.rawValue ?? 20) }
|
||||||
|
}
|
||||||
|
|
||||||
var body: some HTML {
|
var body: some HTML {
|
||||||
div(.class("flex w-full flex-col")) {
|
div(.class("flex w-full flex-col")) {
|
||||||
PageTitleRow {
|
PageTitleRow {
|
||||||
@@ -133,7 +141,7 @@ struct RoomsView: HTML, Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
tbody {
|
tbody {
|
||||||
for room in rooms {
|
for room in sortedRooms {
|
||||||
RoomRow(room: room, shr: sensibleHeatRatio, rooms: rooms)
|
RoomRow(room: room, shr: sensibleHeatRatio, rooms: rooms)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -166,7 +174,13 @@ struct RoomsView: HTML, Sendable {
|
|||||||
|
|
||||||
public var body: some HTML {
|
public var body: some HTML {
|
||||||
tr(.id("roomRow_\(room.id.idString)")) {
|
tr(.id("roomRow_\(room.id.idString)")) {
|
||||||
td { room.name }
|
td {
|
||||||
|
if let level = room.level {
|
||||||
|
"\(level.label) - \(room.name)"
|
||||||
|
} else {
|
||||||
|
room.name
|
||||||
|
}
|
||||||
|
}
|
||||||
td {
|
td {
|
||||||
div(.class("flex justify-center")) {
|
div(.class("flex justify-center")) {
|
||||||
Number(room.heatingLoad, digits: 0)
|
Number(room.heatingLoad, digits: 0)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import Elementary
|
import Elementary
|
||||||
import ElementaryHTMX
|
import ElementaryHTMX
|
||||||
|
import ManualDCore
|
||||||
import Styleguide
|
import Styleguide
|
||||||
|
|
||||||
struct LoginForm: HTML, Sendable {
|
struct LoginForm: HTML, Sendable {
|
||||||
@@ -12,6 +13,13 @@ struct LoginForm: HTML, Sendable {
|
|||||||
self.next = next
|
self.next = next
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var route: SiteRoute.View {
|
||||||
|
if style == .login {
|
||||||
|
return .login(.index(next: next))
|
||||||
|
}
|
||||||
|
return .signup(.index)
|
||||||
|
}
|
||||||
|
|
||||||
var body: some HTML {
|
var body: some HTML {
|
||||||
ModalForm(id: "loginForm", closeButton: false, dismiss: false) {
|
ModalForm(id: "loginForm", closeButton: false, dismiss: false) {
|
||||||
h1(.class("text-2xl font-bold mb-6")) { style.title }
|
h1(.class("text-2xl font-bold mb-6")) { style.title }
|
||||||
|
|||||||
@@ -150,6 +150,10 @@ struct UserProfileForm: HTML, Sendable {
|
|||||||
.attributes(.class("btn-block"))
|
.attributes(.class("btn-block"))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
.attributes(
|
||||||
|
.hx.pushURL("/projects"),
|
||||||
|
when: signup == true
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
TODO.md
10
TODO.md
@@ -3,8 +3,8 @@
|
|||||||
- [x] Fix theme not working when selected upon signup.
|
- [x] Fix theme not working when selected upon signup.
|
||||||
- [x] Pdf generation
|
- [x] Pdf generation
|
||||||
- [x] Add postgres / mysql support
|
- [x] Add postgres / mysql support
|
||||||
- [x] Opensource / license ??
|
- [x] Opensource / license
|
||||||
- [x] Figure out domain to host (currently thinking ductcalc.pro)
|
- [x] Figure out domain to host
|
||||||
- [x] Add ability for either sensible or total load while specifying a room load.
|
- [x] Add ability for either sensible or total load while specifying a room load.
|
||||||
- CoolCalc current version specifies the sensible cooling for a room break down,
|
- CoolCalc current version specifies the sensible cooling for a room break down,
|
||||||
and currently we require the total load and calculate sensible based on project
|
and currently we require the total load and calculate sensible based on project
|
||||||
@@ -14,5 +14,9 @@
|
|||||||
- They will overlap each other making it difficult to read / decipher which checkbox belongs
|
- They will overlap each other making it difficult to read / decipher which checkbox belongs
|
||||||
to which label.
|
to which label.
|
||||||
- [x] Add select all rooms for trunks, useful for sizing main supply or return trunks.
|
- [x] Add select all rooms for trunks, useful for sizing main supply or return trunks.
|
||||||
- [ ] Add optional level to room model.
|
- [x] Add optional level to room model.
|
||||||
- [ ] Add way to sponsor the project.
|
- [ ] Add way to sponsor the project.
|
||||||
|
- [x] Need to push url after signup.
|
||||||
|
- [x] Update urls to point to public mirror of repository
|
||||||
|
- [x] Add email to footer
|
||||||
|
- [x] Validation errors, if they occur are vague in ResultView
|
||||||
|
|||||||
@@ -11,12 +11,15 @@ struct CSVParsingTests {
|
|||||||
let parser = CSVParser.liveValue
|
let parser = CSVParser.liveValue
|
||||||
|
|
||||||
let input = """
|
let input = """
|
||||||
Name,Heating Load,Cooling Total,Cooling Sensible,Register Count,Delegated To
|
Name,Level,Heating Load,Cooling Total,Cooling Sensible,Register Count,Delegated To
|
||||||
Bed-1,12345,12345,,2,
|
Bed-1,2,12345,2345,2345,2,
|
||||||
Bed-2,1223,,1123,1,
|
Entry,1,3456,1234,990,1,
|
||||||
|
Kitchen,1,6789,3456,,2,
|
||||||
|
Bath-1,1,890,,345,0,Kitchen
|
||||||
"""
|
"""
|
||||||
let rooms = try await parser.parseRooms(.init(file: Data(input.utf8)))
|
let rooms = try await parser.parseRooms(.init(file: Data(input.utf8)))
|
||||||
|
|
||||||
#expect(rooms.count == 2)
|
#expect(rooms.count == 4)
|
||||||
|
#expect(rooms.first!.level == 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
Name,Heating Load,Cooling Total,Cooling Sensible,Register Count,Delegated To
|
Name,Level,Heating Load,Cooling Total,Cooling Sensible,Register Count,Delegated To
|
||||||
Bed-1,2345,1234,1321,1,
|
Bed-1,2,2345,1234,1321,1,
|
||||||
Entry,3456,2345,1234,1,
|
Entry,1,3456,2345,1234,1,
|
||||||
Kitchen,7654,3456,2453,2,
|
Kitchen,1,7654,3456,2453,2,
|
||||||
Bath-1,890,345,,0,Kitchen
|
Bath-1,1,890,345,,0,Kitchen
|
||||||
|
|||||||
|
@@ -69,11 +69,25 @@ p-6 w-full">
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<div class="bottom-0 left-0 bg-error">
|
<div class="bottom-0 left-0 bg-error">
|
||||||
<footer class="footer sm:footer-horizontal footer-center
|
<footer class="footer footer-horizontal footer-center
|
||||||
bg-base-300 text-base-content p-4">
|
bg-base-300 text-base-content p-4">
|
||||||
<aside>
|
<aside class="grid-flow-row items-center">
|
||||||
<p>Copyright © 2026 - All rights reserved by Michael Housh</p>
|
<div class="flex mx-auto">
|
||||||
Openly licensed via CC-BY-NC-SA 4.0<a class="btn btn-ghost" href="https://git.housh.dev/michael/swift-duct-calc/src/branch/main/LICENSE" target="_blank"></a>
|
<a class="btn btn-ghost" href="mailto:support@ductcalc.pro"> <svg class="h-[1em] opacity-50" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<g
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-width="2.5"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<rect width="20" height="16" x="2" y="4" rx="2"></rect>
|
||||||
|
<path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"></path>
|
||||||
|
</g>
|
||||||
|
</svg><span>support@ductcalc.pro</span></a>
|
||||||
|
</div>
|
||||||
|
Openly licensed via CC-BY-NC-SA 4.0<a class="btn btn-ghost mx-auto" href="https://github.com/m-housh/swift-duct-calc/src/branch/main/LICENSE" target="_blank"></a>
|
||||||
|
<p class="">Copyright © 2026 - All rights reserved by Michael Housh</p>
|
||||||
</aside>
|
</aside>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ text-8xl font-bold my-auto space-2">
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
Open source residential duct design program<a class="btn btn-ghost text-md text-primary font-bold italic" href="https://git.housh.dev/michael/swift-duct-calc" target="_blank"></a>
|
Open source residential duct design program<a class="btn btn-ghost text-md text-primary font-bold italic" href="https://github.com/m-housh/swift-duct-calc" target="_blank"></a>
|
||||||
<p class="text-3xl py-6">Manual-D™ speed sheet, but on the web!</p>
|
<p class="text-3xl py-6">Manual-D™ speed sheet, but on the web!</p>
|
||||||
<button class="btn btn-xl btn-primary mt-6" hx-get="/signup" hx-target="body" hx-swap="outerHTML">Get Started</button>
|
<button class="btn btn-xl btn-primary mt-6" hx-get="/signup" hx-target="body" hx-swap="outerHTML">Get Started</button>
|
||||||
<p class="text-xs italic my-6">
|
<p class="text-xs italic my-6">
|
||||||
@@ -100,11 +100,25 @@ text-8xl font-bold my-auto space-2">
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<div class="bottom-0 left-0 bg-error">
|
<div class="bottom-0 left-0 bg-error">
|
||||||
<footer class="footer sm:footer-horizontal footer-center
|
<footer class="footer footer-horizontal footer-center
|
||||||
bg-base-300 text-base-content p-4">
|
bg-base-300 text-base-content p-4">
|
||||||
<aside>
|
<aside class="grid-flow-row items-center">
|
||||||
<p>Copyright © 2026 - All rights reserved by Michael Housh</p>
|
<div class="flex mx-auto">
|
||||||
Openly licensed via CC-BY-NC-SA 4.0<a class="btn btn-ghost" href="https://git.housh.dev/michael/swift-duct-calc/src/branch/main/LICENSE" target="_blank"></a>
|
<a class="btn btn-ghost" href="mailto:support@ductcalc.pro"> <svg class="h-[1em] opacity-50" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<g
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-width="2.5"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<rect width="20" height="16" x="2" y="4" rx="2"></rect>
|
||||||
|
<path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"></path>
|
||||||
|
</g>
|
||||||
|
</svg><span>support@ductcalc.pro</span></a>
|
||||||
|
</div>
|
||||||
|
Openly licensed via CC-BY-NC-SA 4.0<a class="btn btn-ghost mx-auto" href="https://github.com/m-housh/swift-duct-calc/src/branch/main/LICENSE" target="_blank"></a>
|
||||||
|
<p class="">Copyright © 2026 - All rights reserved by Michael Housh</p>
|
||||||
</aside>
|
</aside>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
<div class="drawer-content overflow-auto">
|
<div class="drawer-content overflow-auto">
|
||||||
<nav class="navbar w-full bg-base-300 text-base-content shadow-sm mb-4">
|
<nav class="navbar w-full bg-base-300 text-base-content shadow-sm mb-4">
|
||||||
<div class="flex flex-1 space-x-4 items-center">
|
<div class="flex flex-1 space-x-4 items-center">
|
||||||
<div class="tooltip tooltip-right" data-tip="Open sidebar"><label for="my-drawer-1" class="size-7 btn btn-square btn-ghost hover:bg-neutral hover:text-white" aria-label="open sidebar"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-linejoin="round" stroke-linecap="round" stroke-width="2" fill="none" stroke="currentColor" class="my-1.5 inline-block"><path d="M4 4m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z"></path><path d="M9 4v16"></path><path d="M14 10l2 2l-2 2"></path></svg></label></div>
|
<div class="tooltip tooltip-right" data-tip="Open / close sidebar"><label for="my-drawer-1" class="size-7 btn btn-square btn-ghost hover:bg-neutral hover:text-white" aria-label="open / close sidebar"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-linejoin="round" stroke-linecap="round" stroke-width="2" fill="none" stroke="currentColor" class="my-1.5 inline-block"><path d="M4 4m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z"></path><path d="M9 4v16"></path><path d="M14 10l2 2l-2 2"></path></svg></label></div>
|
||||||
<div class="tooltip tooltip-right" data-tip="Projects">
|
<div class="tooltip tooltip-right" data-tip="Projects">
|
||||||
<a class="flex w-fit h-fit text-2xl items-end px-4 py-2 btn btn-square btn-ghost hover:bg-neutral hover:text-white" href="/projects">
|
<a class="flex w-fit h-fit text-2xl items-end px-4 py-2 btn btn-square btn-ghost hover:bg-neutral hover:text-white" href="/projects">
|
||||||
<img src="/images/mand_logo_sm.webp">
|
<img src="/images/mand_logo_sm.webp">
|
||||||
@@ -246,11 +246,25 @@ is-drawer-close:justify-center is-drawer-close:mx-auto is-drawer-close:space-y-2
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<div class="bottom-0 left-0 bg-error">
|
<div class="bottom-0 left-0 bg-error">
|
||||||
<footer class="footer sm:footer-horizontal footer-center
|
<footer class="footer footer-horizontal footer-center
|
||||||
bg-base-300 text-base-content p-4">
|
bg-base-300 text-base-content p-4">
|
||||||
<aside>
|
<aside class="grid-flow-row items-center">
|
||||||
<p>Copyright © 2026 - All rights reserved by Michael Housh</p>
|
<div class="flex mx-auto">
|
||||||
Openly licensed via CC-BY-NC-SA 4.0<a class="btn btn-ghost" href="https://git.housh.dev/michael/swift-duct-calc/src/branch/main/LICENSE" target="_blank"></a>
|
<a class="btn btn-ghost" href="mailto:support@ductcalc.pro"> <svg class="h-[1em] opacity-50" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<g
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-width="2.5"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<rect width="20" height="16" x="2" y="4" rx="2"></rect>
|
||||||
|
<path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"></path>
|
||||||
|
</g>
|
||||||
|
</svg><span>support@ductcalc.pro</span></a>
|
||||||
|
</div>
|
||||||
|
Openly licensed via CC-BY-NC-SA 4.0<a class="btn btn-ghost mx-auto" href="https://github.com/m-housh/swift-duct-calc/src/branch/main/LICENSE" target="_blank"></a>
|
||||||
|
<p class="">Copyright © 2026 - All rights reserved by Michael Housh</p>
|
||||||
</aside>
|
</aside>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
<div class="drawer-content overflow-auto">
|
<div class="drawer-content overflow-auto">
|
||||||
<nav class="navbar w-full bg-base-300 text-base-content shadow-sm mb-4">
|
<nav class="navbar w-full bg-base-300 text-base-content shadow-sm mb-4">
|
||||||
<div class="flex flex-1 space-x-4 items-center">
|
<div class="flex flex-1 space-x-4 items-center">
|
||||||
<div class="tooltip tooltip-right" data-tip="Open sidebar"><label for="my-drawer-1" class="size-7 btn btn-square btn-ghost hover:bg-neutral hover:text-white" aria-label="open sidebar"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-linejoin="round" stroke-linecap="round" stroke-width="2" fill="none" stroke="currentColor" class="my-1.5 inline-block"><path d="M4 4m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z"></path><path d="M9 4v16"></path><path d="M14 10l2 2l-2 2"></path></svg></label></div>
|
<div class="tooltip tooltip-right" data-tip="Open / close sidebar"><label for="my-drawer-1" class="size-7 btn btn-square btn-ghost hover:bg-neutral hover:text-white" aria-label="open / close sidebar"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-linejoin="round" stroke-linecap="round" stroke-width="2" fill="none" stroke="currentColor" class="my-1.5 inline-block"><path d="M4 4m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z"></path><path d="M9 4v16"></path><path d="M14 10l2 2l-2 2"></path></svg></label></div>
|
||||||
<div class="tooltip tooltip-right" data-tip="Projects">
|
<div class="tooltip tooltip-right" data-tip="Projects">
|
||||||
<a class="flex w-fit h-fit text-2xl items-end px-4 py-2 btn btn-square btn-ghost hover:bg-neutral hover:text-white" href="/projects">
|
<a class="flex w-fit h-fit text-2xl items-end px-4 py-2 btn btn-square btn-ghost hover:bg-neutral hover:text-white" href="/projects">
|
||||||
<img src="/images/mand_logo_sm.webp">
|
<img src="/images/mand_logo_sm.webp">
|
||||||
@@ -171,13 +171,20 @@ p-6 w-full">
|
|||||||
<input class="hidden" name="id" value="00000000-0000-0000-0000-000000000001">
|
<input class="hidden" name="id" value="00000000-0000-0000-0000-000000000001">
|
||||||
Name<label class="input w-full"><span class="label"></span>
|
Name<label class="input w-full"><span class="label"></span>
|
||||||
<input name="name" type="text" placeholder="Name" required autofocus value="Bed-1">
|
<input name="name" type="text" placeholder="Name" required autofocus value="Bed-1">
|
||||||
Heating Load</label><label class="input w-full"><span class="label"></span>
|
Level</label><label class="input w-full"><span class="label"></span>
|
||||||
|
<input name="level" type="number" placeholder="1 (Optional)" value="" min="-1" step="1"></label>
|
||||||
|
<div class="text-sm italic -mt-2"><span class="text-primary">Use -1 or 0 for a basement</span></div>
|
||||||
|
Heating Load<label class="input w-full"><span class="label"></span>
|
||||||
<input name="heatingLoad" type="number" placeholder="1234" required min="0" value="3913.0">
|
<input name="heatingLoad" type="number" placeholder="1234" required min="0" value="3913.0">
|
||||||
Cooling Total</label><label class="input w-full"><span class="label"></span>
|
Cooling Total</label><label class="input w-full"><span class="label"></span>
|
||||||
<input name="coolingTotal" type="number" placeholder="1234 (Optional)" min="0" value="2472.0">
|
<input name="coolingTotal" type="number" placeholder="1234 (Optional)" min="0" value="2472.0">
|
||||||
Cooling Sensible</label><label class="input w-full"><span class="label"></span>
|
Cooling Sensible</label><label class="input w-full"><span class="label"></span>
|
||||||
<input name="coolingSensible" type="number" placeholder="1234 (Optional)" min="0" value="">
|
<input name="coolingSensible" type="number" placeholder="1234 (Optional)" min="0" value=""></label>
|
||||||
Registers</label><label class="input w-full"><span class="label"></span>
|
<div class="text-primary text-sm italic -mt-2">
|
||||||
|
<p>Should enter at least one of the cooling loads.</p>
|
||||||
|
<p>Both are also acceptable.</p>
|
||||||
|
</div>
|
||||||
|
Registers<label class="input w-full"><span class="label"></span>
|
||||||
<input name="registerCount" type="number" min="1" required value="1" id="registerCount">
|
<input name="registerCount" type="number" min="1" required value="1" id="registerCount">
|
||||||
Room</label><label class="select w-full"><span class="label"></span>
|
Room</label><label class="select w-full"><span class="label"></span>
|
||||||
<select name="delegatedTo">
|
<select name="delegatedTo">
|
||||||
@@ -234,13 +241,20 @@ p-6 w-full">
|
|||||||
<input class="hidden" name="id" value="00000000-0000-0000-0000-000000000002">
|
<input class="hidden" name="id" value="00000000-0000-0000-0000-000000000002">
|
||||||
Name<label class="input w-full"><span class="label"></span>
|
Name<label class="input w-full"><span class="label"></span>
|
||||||
<input name="name" type="text" placeholder="Name" required autofocus value="Entry">
|
<input name="name" type="text" placeholder="Name" required autofocus value="Entry">
|
||||||
Heating Load</label><label class="input w-full"><span class="label"></span>
|
Level</label><label class="input w-full"><span class="label"></span>
|
||||||
|
<input name="level" type="number" placeholder="1 (Optional)" value="" min="-1" step="1"></label>
|
||||||
|
<div class="text-sm italic -mt-2"><span class="text-primary">Use -1 or 0 for a basement</span></div>
|
||||||
|
Heating Load<label class="input w-full"><span class="label"></span>
|
||||||
<input name="heatingLoad" type="number" placeholder="1234" required min="0" value="8284.0">
|
<input name="heatingLoad" type="number" placeholder="1234" required min="0" value="8284.0">
|
||||||
Cooling Total</label><label class="input w-full"><span class="label"></span>
|
Cooling Total</label><label class="input w-full"><span class="label"></span>
|
||||||
<input name="coolingTotal" type="number" placeholder="1234 (Optional)" min="0" value="2916.0">
|
<input name="coolingTotal" type="number" placeholder="1234 (Optional)" min="0" value="2916.0">
|
||||||
Cooling Sensible</label><label class="input w-full"><span class="label"></span>
|
Cooling Sensible</label><label class="input w-full"><span class="label"></span>
|
||||||
<input name="coolingSensible" type="number" placeholder="1234 (Optional)" min="0" value="">
|
<input name="coolingSensible" type="number" placeholder="1234 (Optional)" min="0" value=""></label>
|
||||||
Registers</label><label class="input w-full"><span class="label"></span>
|
<div class="text-primary text-sm italic -mt-2">
|
||||||
|
<p>Should enter at least one of the cooling loads.</p>
|
||||||
|
<p>Both are also acceptable.</p>
|
||||||
|
</div>
|
||||||
|
Registers<label class="input w-full"><span class="label"></span>
|
||||||
<input name="registerCount" type="number" min="1" required value="2" id="registerCount">
|
<input name="registerCount" type="number" min="1" required value="2" id="registerCount">
|
||||||
Room</label><label class="select w-full"><span class="label"></span>
|
Room</label><label class="select w-full"><span class="label"></span>
|
||||||
<select name="delegatedTo">
|
<select name="delegatedTo">
|
||||||
@@ -297,13 +311,20 @@ p-6 w-full">
|
|||||||
<input class="hidden" name="id" value="00000000-0000-0000-0000-000000000003">
|
<input class="hidden" name="id" value="00000000-0000-0000-0000-000000000003">
|
||||||
Name<label class="input w-full"><span class="label"></span>
|
Name<label class="input w-full"><span class="label"></span>
|
||||||
<input name="name" type="text" placeholder="Name" required autofocus value="Family Room">
|
<input name="name" type="text" placeholder="Name" required autofocus value="Family Room">
|
||||||
Heating Load</label><label class="input w-full"><span class="label"></span>
|
Level</label><label class="input w-full"><span class="label"></span>
|
||||||
|
<input name="level" type="number" placeholder="1 (Optional)" value="" min="-1" step="1"></label>
|
||||||
|
<div class="text-sm italic -mt-2"><span class="text-primary">Use -1 or 0 for a basement</span></div>
|
||||||
|
Heating Load<label class="input w-full"><span class="label"></span>
|
||||||
<input name="heatingLoad" type="number" placeholder="1234" required min="0" value="9785.0">
|
<input name="heatingLoad" type="number" placeholder="1234" required min="0" value="9785.0">
|
||||||
Cooling Total</label><label class="input w-full"><span class="label"></span>
|
Cooling Total</label><label class="input w-full"><span class="label"></span>
|
||||||
<input name="coolingTotal" type="number" placeholder="1234 (Optional)" min="0" value="7446.0">
|
<input name="coolingTotal" type="number" placeholder="1234 (Optional)" min="0" value="7446.0">
|
||||||
Cooling Sensible</label><label class="input w-full"><span class="label"></span>
|
Cooling Sensible</label><label class="input w-full"><span class="label"></span>
|
||||||
<input name="coolingSensible" type="number" placeholder="1234 (Optional)" min="0" value="">
|
<input name="coolingSensible" type="number" placeholder="1234 (Optional)" min="0" value=""></label>
|
||||||
Registers</label><label class="input w-full"><span class="label"></span>
|
<div class="text-primary text-sm italic -mt-2">
|
||||||
|
<p>Should enter at least one of the cooling loads.</p>
|
||||||
|
<p>Both are also acceptable.</p>
|
||||||
|
</div>
|
||||||
|
Registers<label class="input w-full"><span class="label"></span>
|
||||||
<input name="registerCount" type="number" min="1" required value="3" id="registerCount">
|
<input name="registerCount" type="number" min="1" required value="3" id="registerCount">
|
||||||
Room</label><label class="select w-full"><span class="label"></span>
|
Room</label><label class="select w-full"><span class="label"></span>
|
||||||
<select name="delegatedTo">
|
<select name="delegatedTo">
|
||||||
@@ -360,13 +381,20 @@ p-6 w-full">
|
|||||||
<input class="hidden" name="id" value="00000000-0000-0000-0000-000000000004">
|
<input class="hidden" name="id" value="00000000-0000-0000-0000-000000000004">
|
||||||
Name<label class="input w-full"><span class="label"></span>
|
Name<label class="input w-full"><span class="label"></span>
|
||||||
<input name="name" type="text" placeholder="Name" required autofocus value="Kitchen">
|
<input name="name" type="text" placeholder="Name" required autofocus value="Kitchen">
|
||||||
Heating Load</label><label class="input w-full"><span class="label"></span>
|
Level</label><label class="input w-full"><span class="label"></span>
|
||||||
|
<input name="level" type="number" placeholder="1 (Optional)" value="" min="-1" step="1"></label>
|
||||||
|
<div class="text-sm italic -mt-2"><span class="text-primary">Use -1 or 0 for a basement</span></div>
|
||||||
|
Heating Load<label class="input w-full"><span class="label"></span>
|
||||||
<input name="heatingLoad" type="number" placeholder="1234" required min="0" value="4518.0">
|
<input name="heatingLoad" type="number" placeholder="1234" required min="0" value="4518.0">
|
||||||
Cooling Total</label><label class="input w-full"><span class="label"></span>
|
Cooling Total</label><label class="input w-full"><span class="label"></span>
|
||||||
<input name="coolingTotal" type="number" placeholder="1234 (Optional)" min="0" value="5096.0">
|
<input name="coolingTotal" type="number" placeholder="1234 (Optional)" min="0" value="5096.0">
|
||||||
Cooling Sensible</label><label class="input w-full"><span class="label"></span>
|
Cooling Sensible</label><label class="input w-full"><span class="label"></span>
|
||||||
<input name="coolingSensible" type="number" placeholder="1234 (Optional)" min="0" value="">
|
<input name="coolingSensible" type="number" placeholder="1234 (Optional)" min="0" value=""></label>
|
||||||
Registers</label><label class="input w-full"><span class="label"></span>
|
<div class="text-primary text-sm italic -mt-2">
|
||||||
|
<p>Should enter at least one of the cooling loads.</p>
|
||||||
|
<p>Both are also acceptable.</p>
|
||||||
|
</div>
|
||||||
|
Registers<label class="input w-full"><span class="label"></span>
|
||||||
<input name="registerCount" type="number" min="1" required value="2" id="registerCount">
|
<input name="registerCount" type="number" min="1" required value="2" id="registerCount">
|
||||||
Room</label><label class="select w-full"><span class="label"></span>
|
Room</label><label class="select w-full"><span class="label"></span>
|
||||||
<select name="delegatedTo">
|
<select name="delegatedTo">
|
||||||
@@ -423,13 +451,20 @@ p-6 w-full">
|
|||||||
<input class="hidden" name="id" value="00000000-0000-0000-0000-000000000005">
|
<input class="hidden" name="id" value="00000000-0000-0000-0000-000000000005">
|
||||||
Name<label class="input w-full"><span class="label"></span>
|
Name<label class="input w-full"><span class="label"></span>
|
||||||
<input name="name" type="text" placeholder="Name" required autofocus value="Living Room">
|
<input name="name" type="text" placeholder="Name" required autofocus value="Living Room">
|
||||||
Heating Load</label><label class="input w-full"><span class="label"></span>
|
Level</label><label class="input w-full"><span class="label"></span>
|
||||||
|
<input name="level" type="number" placeholder="1 (Optional)" value="" min="-1" step="1"></label>
|
||||||
|
<div class="text-sm italic -mt-2"><span class="text-primary">Use -1 or 0 for a basement</span></div>
|
||||||
|
Heating Load<label class="input w-full"><span class="label"></span>
|
||||||
<input name="heatingLoad" type="number" placeholder="1234" required min="0" value="7553.0">
|
<input name="heatingLoad" type="number" placeholder="1234" required min="0" value="7553.0">
|
||||||
Cooling Total</label><label class="input w-full"><span class="label"></span>
|
Cooling Total</label><label class="input w-full"><span class="label"></span>
|
||||||
<input name="coolingTotal" type="number" placeholder="1234 (Optional)" min="0" value="6829.0">
|
<input name="coolingTotal" type="number" placeholder="1234 (Optional)" min="0" value="6829.0">
|
||||||
Cooling Sensible</label><label class="input w-full"><span class="label"></span>
|
Cooling Sensible</label><label class="input w-full"><span class="label"></span>
|
||||||
<input name="coolingSensible" type="number" placeholder="1234 (Optional)" min="0" value="">
|
<input name="coolingSensible" type="number" placeholder="1234 (Optional)" min="0" value=""></label>
|
||||||
Registers</label><label class="input w-full"><span class="label"></span>
|
<div class="text-primary text-sm italic -mt-2">
|
||||||
|
<p>Should enter at least one of the cooling loads.</p>
|
||||||
|
<p>Both are also acceptable.</p>
|
||||||
|
</div>
|
||||||
|
Registers<label class="input w-full"><span class="label"></span>
|
||||||
<input name="registerCount" type="number" min="1" required value="2" id="registerCount">
|
<input name="registerCount" type="number" min="1" required value="2" id="registerCount">
|
||||||
Room</label><label class="select w-full"><span class="label"></span>
|
Room</label><label class="select w-full"><span class="label"></span>
|
||||||
<select name="delegatedTo">
|
<select name="delegatedTo">
|
||||||
@@ -486,13 +521,20 @@ p-6 w-full">
|
|||||||
<input class="hidden" name="id" value="00000000-0000-0000-0000-000000000006">
|
<input class="hidden" name="id" value="00000000-0000-0000-0000-000000000006">
|
||||||
Name<label class="input w-full"><span class="label"></span>
|
Name<label class="input w-full"><span class="label"></span>
|
||||||
<input name="name" type="text" placeholder="Name" required autofocus value="Master">
|
<input name="name" type="text" placeholder="Name" required autofocus value="Master">
|
||||||
Heating Load</label><label class="input w-full"><span class="label"></span>
|
Level</label><label class="input w-full"><span class="label"></span>
|
||||||
|
<input name="level" type="number" placeholder="1 (Optional)" value="" min="-1" step="1"></label>
|
||||||
|
<div class="text-sm italic -mt-2"><span class="text-primary">Use -1 or 0 for a basement</span></div>
|
||||||
|
Heating Load<label class="input w-full"><span class="label"></span>
|
||||||
<input name="heatingLoad" type="number" placeholder="1234" required min="0" value="8202.0">
|
<input name="heatingLoad" type="number" placeholder="1234" required min="0" value="8202.0">
|
||||||
Cooling Total</label><label class="input w-full"><span class="label"></span>
|
Cooling Total</label><label class="input w-full"><span class="label"></span>
|
||||||
<input name="coolingTotal" type="number" placeholder="1234 (Optional)" min="0" value="2076.0">
|
<input name="coolingTotal" type="number" placeholder="1234 (Optional)" min="0" value="2076.0">
|
||||||
Cooling Sensible</label><label class="input w-full"><span class="label"></span>
|
Cooling Sensible</label><label class="input w-full"><span class="label"></span>
|
||||||
<input name="coolingSensible" type="number" placeholder="1234 (Optional)" min="0" value="">
|
<input name="coolingSensible" type="number" placeholder="1234 (Optional)" min="0" value=""></label>
|
||||||
Registers</label><label class="input w-full"><span class="label"></span>
|
<div class="text-primary text-sm italic -mt-2">
|
||||||
|
<p>Should enter at least one of the cooling loads.</p>
|
||||||
|
<p>Both are also acceptable.</p>
|
||||||
|
</div>
|
||||||
|
Registers<label class="input w-full"><span class="label"></span>
|
||||||
<input name="registerCount" type="number" min="1" required value="2" id="registerCount">
|
<input name="registerCount" type="number" min="1" required value="2" id="registerCount">
|
||||||
Room</label><label class="select w-full"><span class="label"></span>
|
Room</label><label class="select w-full"><span class="label"></span>
|
||||||
<select name="delegatedTo">
|
<select name="delegatedTo">
|
||||||
@@ -519,13 +561,20 @@ p-6 w-full">
|
|||||||
<form class="grid grid-cols-1 gap-4" hx-post="/projects/00000000-0000-0000-0000-000000000000/rooms" hx-target="body" hx-swap="outerHTML">
|
<form class="grid grid-cols-1 gap-4" hx-post="/projects/00000000-0000-0000-0000-000000000000/rooms" hx-target="body" hx-swap="outerHTML">
|
||||||
<label class="input w-full"><span class="label">Name</span>
|
<label class="input w-full"><span class="label">Name</span>
|
||||||
<input name="name" type="text" placeholder="Name" required autofocus value="">
|
<input name="name" type="text" placeholder="Name" required autofocus value="">
|
||||||
Heating Load</label><label class="input w-full"><span class="label"></span>
|
Level</label><label class="input w-full"><span class="label"></span>
|
||||||
|
<input name="level" type="number" placeholder="1 (Optional)" value="" min="-1" step="1"></label>
|
||||||
|
<div class="text-sm italic -mt-2"><span class="text-primary">Use -1 or 0 for a basement</span></div>
|
||||||
|
Heating Load<label class="input w-full"><span class="label"></span>
|
||||||
<input name="heatingLoad" type="number" placeholder="1234" required min="0" value="">
|
<input name="heatingLoad" type="number" placeholder="1234" required min="0" value="">
|
||||||
Cooling Total</label><label class="input w-full"><span class="label"></span>
|
Cooling Total</label><label class="input w-full"><span class="label"></span>
|
||||||
<input name="coolingTotal" type="number" placeholder="1234 (Optional)" min="0" value="">
|
<input name="coolingTotal" type="number" placeholder="1234 (Optional)" min="0" value="">
|
||||||
Cooling Sensible</label><label class="input w-full"><span class="label"></span>
|
Cooling Sensible</label><label class="input w-full"><span class="label"></span>
|
||||||
<input name="coolingSensible" type="number" placeholder="1234 (Optional)" min="0" value="">
|
<input name="coolingSensible" type="number" placeholder="1234 (Optional)" min="0" value=""></label>
|
||||||
Registers</label><label class="input w-full"><span class="label"></span>
|
<div class="text-primary text-sm italic -mt-2">
|
||||||
|
<p>Should enter at least one of the cooling loads.</p>
|
||||||
|
<p>Both are also acceptable.</p>
|
||||||
|
</div>
|
||||||
|
Registers<label class="input w-full"><span class="label"></span>
|
||||||
<input name="registerCount" type="number" min="1" required value="1" id="registerCount">
|
<input name="registerCount" type="number" min="1" required value="1" id="registerCount">
|
||||||
Room</label><label class="select w-full"><span class="label"></span>
|
Room</label><label class="select w-full"><span class="label"></span>
|
||||||
<select name="delegatedTo">
|
<select name="delegatedTo">
|
||||||
@@ -676,11 +725,25 @@ is-drawer-close:justify-center is-drawer-close:mx-auto is-drawer-close:space-y-2
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<div class="bottom-0 left-0 bg-error">
|
<div class="bottom-0 left-0 bg-error">
|
||||||
<footer class="footer sm:footer-horizontal footer-center
|
<footer class="footer footer-horizontal footer-center
|
||||||
bg-base-300 text-base-content p-4">
|
bg-base-300 text-base-content p-4">
|
||||||
<aside>
|
<aside class="grid-flow-row items-center">
|
||||||
<p>Copyright © 2026 - All rights reserved by Michael Housh</p>
|
<div class="flex mx-auto">
|
||||||
Openly licensed via CC-BY-NC-SA 4.0<a class="btn btn-ghost" href="https://git.housh.dev/michael/swift-duct-calc/src/branch/main/LICENSE" target="_blank"></a>
|
<a class="btn btn-ghost" href="mailto:support@ductcalc.pro"> <svg class="h-[1em] opacity-50" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<g
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-width="2.5"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<rect width="20" height="16" x="2" y="4" rx="2"></rect>
|
||||||
|
<path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"></path>
|
||||||
|
</g>
|
||||||
|
</svg><span>support@ductcalc.pro</span></a>
|
||||||
|
</div>
|
||||||
|
Openly licensed via CC-BY-NC-SA 4.0<a class="btn btn-ghost mx-auto" href="https://github.com/m-housh/swift-duct-calc/src/branch/main/LICENSE" target="_blank"></a>
|
||||||
|
<p class="">Copyright © 2026 - All rights reserved by Michael Housh</p>
|
||||||
</aside>
|
</aside>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
<div class="drawer-content overflow-auto">
|
<div class="drawer-content overflow-auto">
|
||||||
<nav class="navbar w-full bg-base-300 text-base-content shadow-sm mb-4">
|
<nav class="navbar w-full bg-base-300 text-base-content shadow-sm mb-4">
|
||||||
<div class="flex flex-1 space-x-4 items-center">
|
<div class="flex flex-1 space-x-4 items-center">
|
||||||
<div class="tooltip tooltip-right" data-tip="Open sidebar"><label for="my-drawer-1" class="size-7 btn btn-square btn-ghost hover:bg-neutral hover:text-white" aria-label="open sidebar"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-linejoin="round" stroke-linecap="round" stroke-width="2" fill="none" stroke="currentColor" class="my-1.5 inline-block"><path d="M4 4m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z"></path><path d="M9 4v16"></path><path d="M14 10l2 2l-2 2"></path></svg></label></div>
|
<div class="tooltip tooltip-right" data-tip="Open / close sidebar"><label for="my-drawer-1" class="size-7 btn btn-square btn-ghost hover:bg-neutral hover:text-white" aria-label="open / close sidebar"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-linejoin="round" stroke-linecap="round" stroke-width="2" fill="none" stroke="currentColor" class="my-1.5 inline-block"><path d="M4 4m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z"></path><path d="M9 4v16"></path><path d="M14 10l2 2l-2 2"></path></svg></label></div>
|
||||||
<div class="tooltip tooltip-right" data-tip="Projects">
|
<div class="tooltip tooltip-right" data-tip="Projects">
|
||||||
<a class="flex w-fit h-fit text-2xl items-end px-4 py-2 btn btn-square btn-ghost hover:bg-neutral hover:text-white" href="/projects">
|
<a class="flex w-fit h-fit text-2xl items-end px-4 py-2 btn btn-square btn-ghost hover:bg-neutral hover:text-white" href="/projects">
|
||||||
<img src="/images/mand_logo_sm.webp">
|
<img src="/images/mand_logo_sm.webp">
|
||||||
@@ -231,11 +231,25 @@ is-drawer-close:justify-center is-drawer-close:mx-auto is-drawer-close:space-y-2
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<div class="bottom-0 left-0 bg-error">
|
<div class="bottom-0 left-0 bg-error">
|
||||||
<footer class="footer sm:footer-horizontal footer-center
|
<footer class="footer footer-horizontal footer-center
|
||||||
bg-base-300 text-base-content p-4">
|
bg-base-300 text-base-content p-4">
|
||||||
<aside>
|
<aside class="grid-flow-row items-center">
|
||||||
<p>Copyright © 2026 - All rights reserved by Michael Housh</p>
|
<div class="flex mx-auto">
|
||||||
Openly licensed via CC-BY-NC-SA 4.0<a class="btn btn-ghost" href="https://git.housh.dev/michael/swift-duct-calc/src/branch/main/LICENSE" target="_blank"></a>
|
<a class="btn btn-ghost" href="mailto:support@ductcalc.pro"> <svg class="h-[1em] opacity-50" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<g
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-width="2.5"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<rect width="20" height="16" x="2" y="4" rx="2"></rect>
|
||||||
|
<path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"></path>
|
||||||
|
</g>
|
||||||
|
</svg><span>support@ductcalc.pro</span></a>
|
||||||
|
</div>
|
||||||
|
Openly licensed via CC-BY-NC-SA 4.0<a class="btn btn-ghost mx-auto" href="https://github.com/m-housh/swift-duct-calc/src/branch/main/LICENSE" target="_blank"></a>
|
||||||
|
<p class="">Copyright © 2026 - All rights reserved by Michael Housh</p>
|
||||||
</aside>
|
</aside>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
<div class="drawer-content overflow-auto">
|
<div class="drawer-content overflow-auto">
|
||||||
<nav class="navbar w-full bg-base-300 text-base-content shadow-sm mb-4">
|
<nav class="navbar w-full bg-base-300 text-base-content shadow-sm mb-4">
|
||||||
<div class="flex flex-1 space-x-4 items-center">
|
<div class="flex flex-1 space-x-4 items-center">
|
||||||
<div class="tooltip tooltip-right" data-tip="Open sidebar"><label for="my-drawer-1" class="size-7 btn btn-square btn-ghost hover:bg-neutral hover:text-white" aria-label="open sidebar"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-linejoin="round" stroke-linecap="round" stroke-width="2" fill="none" stroke="currentColor" class="my-1.5 inline-block"><path d="M4 4m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z"></path><path d="M9 4v16"></path><path d="M14 10l2 2l-2 2"></path></svg></label></div>
|
<div class="tooltip tooltip-right" data-tip="Open / close sidebar"><label for="my-drawer-1" class="size-7 btn btn-square btn-ghost hover:bg-neutral hover:text-white" aria-label="open / close sidebar"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-linejoin="round" stroke-linecap="round" stroke-width="2" fill="none" stroke="currentColor" class="my-1.5 inline-block"><path d="M4 4m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z"></path><path d="M9 4v16"></path><path d="M14 10l2 2l-2 2"></path></svg></label></div>
|
||||||
<div class="tooltip tooltip-right" data-tip="Projects">
|
<div class="tooltip tooltip-right" data-tip="Projects">
|
||||||
<a class="flex w-fit h-fit text-2xl items-end px-4 py-2 btn btn-square btn-ghost hover:bg-neutral hover:text-white" href="/projects">
|
<a class="flex w-fit h-fit text-2xl items-end px-4 py-2 btn btn-square btn-ghost hover:bg-neutral hover:text-white" href="/projects">
|
||||||
<img src="/images/mand_logo_sm.webp">
|
<img src="/images/mand_logo_sm.webp">
|
||||||
@@ -361,11 +361,25 @@ is-drawer-close:justify-center is-drawer-close:mx-auto is-drawer-close:space-y-2
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<div class="bottom-0 left-0 bg-error">
|
<div class="bottom-0 left-0 bg-error">
|
||||||
<footer class="footer sm:footer-horizontal footer-center
|
<footer class="footer footer-horizontal footer-center
|
||||||
bg-base-300 text-base-content p-4">
|
bg-base-300 text-base-content p-4">
|
||||||
<aside>
|
<aside class="grid-flow-row items-center">
|
||||||
<p>Copyright © 2026 - All rights reserved by Michael Housh</p>
|
<div class="flex mx-auto">
|
||||||
Openly licensed via CC-BY-NC-SA 4.0<a class="btn btn-ghost" href="https://git.housh.dev/michael/swift-duct-calc/src/branch/main/LICENSE" target="_blank"></a>
|
<a class="btn btn-ghost" href="mailto:support@ductcalc.pro"> <svg class="h-[1em] opacity-50" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<g
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-width="2.5"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<rect width="20" height="16" x="2" y="4" rx="2"></rect>
|
||||||
|
<path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"></path>
|
||||||
|
</g>
|
||||||
|
</svg><span>support@ductcalc.pro</span></a>
|
||||||
|
</div>
|
||||||
|
Openly licensed via CC-BY-NC-SA 4.0<a class="btn btn-ghost mx-auto" href="https://github.com/m-housh/swift-duct-calc/src/branch/main/LICENSE" target="_blank"></a>
|
||||||
|
<p class="">Copyright © 2026 - All rights reserved by Michael Housh</p>
|
||||||
</aside>
|
</aside>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
<div class="drawer-content overflow-auto">
|
<div class="drawer-content overflow-auto">
|
||||||
<nav class="navbar w-full bg-base-300 text-base-content shadow-sm mb-4">
|
<nav class="navbar w-full bg-base-300 text-base-content shadow-sm mb-4">
|
||||||
<div class="flex flex-1 space-x-4 items-center">
|
<div class="flex flex-1 space-x-4 items-center">
|
||||||
<div class="tooltip tooltip-right" data-tip="Open sidebar"><label for="my-drawer-1" class="size-7 btn btn-square btn-ghost hover:bg-neutral hover:text-white" aria-label="open sidebar"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-linejoin="round" stroke-linecap="round" stroke-width="2" fill="none" stroke="currentColor" class="my-1.5 inline-block"><path d="M4 4m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z"></path><path d="M9 4v16"></path><path d="M14 10l2 2l-2 2"></path></svg></label></div>
|
<div class="tooltip tooltip-right" data-tip="Open / close sidebar"><label for="my-drawer-1" class="size-7 btn btn-square btn-ghost hover:bg-neutral hover:text-white" aria-label="open / close sidebar"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-linejoin="round" stroke-linecap="round" stroke-width="2" fill="none" stroke="currentColor" class="my-1.5 inline-block"><path d="M4 4m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z"></path><path d="M9 4v16"></path><path d="M14 10l2 2l-2 2"></path></svg></label></div>
|
||||||
<div class="tooltip tooltip-right" data-tip="Projects">
|
<div class="tooltip tooltip-right" data-tip="Projects">
|
||||||
<a class="flex w-fit h-fit text-2xl items-end px-4 py-2 btn btn-square btn-ghost hover:bg-neutral hover:text-white" href="/projects">
|
<a class="flex w-fit h-fit text-2xl items-end px-4 py-2 btn btn-square btn-ghost hover:bg-neutral hover:text-white" href="/projects">
|
||||||
<img src="/images/mand_logo_sm.webp">
|
<img src="/images/mand_logo_sm.webp">
|
||||||
@@ -437,11 +437,25 @@ is-drawer-close:justify-center is-drawer-close:mx-auto is-drawer-close:space-y-2
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<div class="bottom-0 left-0 bg-error">
|
<div class="bottom-0 left-0 bg-error">
|
||||||
<footer class="footer sm:footer-horizontal footer-center
|
<footer class="footer footer-horizontal footer-center
|
||||||
bg-base-300 text-base-content p-4">
|
bg-base-300 text-base-content p-4">
|
||||||
<aside>
|
<aside class="grid-flow-row items-center">
|
||||||
<p>Copyright © 2026 - All rights reserved by Michael Housh</p>
|
<div class="flex mx-auto">
|
||||||
Openly licensed via CC-BY-NC-SA 4.0<a class="btn btn-ghost" href="https://git.housh.dev/michael/swift-duct-calc/src/branch/main/LICENSE" target="_blank"></a>
|
<a class="btn btn-ghost" href="mailto:support@ductcalc.pro"> <svg class="h-[1em] opacity-50" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<g
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-width="2.5"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<rect width="20" height="16" x="2" y="4" rx="2"></rect>
|
||||||
|
<path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"></path>
|
||||||
|
</g>
|
||||||
|
</svg><span>support@ductcalc.pro</span></a>
|
||||||
|
</div>
|
||||||
|
Openly licensed via CC-BY-NC-SA 4.0<a class="btn btn-ghost mx-auto" href="https://github.com/m-housh/swift-duct-calc/src/branch/main/LICENSE" target="_blank"></a>
|
||||||
|
<p class="">Copyright © 2026 - All rights reserved by Michael Housh</p>
|
||||||
</aside>
|
</aside>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
<div class="drawer-content overflow-auto">
|
<div class="drawer-content overflow-auto">
|
||||||
<nav class="navbar w-full bg-base-300 text-base-content shadow-sm mb-4">
|
<nav class="navbar w-full bg-base-300 text-base-content shadow-sm mb-4">
|
||||||
<div class="flex flex-1 space-x-4 items-center">
|
<div class="flex flex-1 space-x-4 items-center">
|
||||||
<div class="tooltip tooltip-right" data-tip="Open sidebar"><label for="my-drawer-1" class="size-7 btn btn-square btn-ghost hover:bg-neutral hover:text-white" aria-label="open sidebar"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-linejoin="round" stroke-linecap="round" stroke-width="2" fill="none" stroke="currentColor" class="my-1.5 inline-block"><path d="M4 4m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z"></path><path d="M9 4v16"></path><path d="M14 10l2 2l-2 2"></path></svg></label></div>
|
<div class="tooltip tooltip-right" data-tip="Open / close sidebar"><label for="my-drawer-1" class="size-7 btn btn-square btn-ghost hover:bg-neutral hover:text-white" aria-label="open / close sidebar"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-linejoin="round" stroke-linecap="round" stroke-width="2" fill="none" stroke="currentColor" class="my-1.5 inline-block"><path d="M4 4m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z"></path><path d="M9 4v16"></path><path d="M14 10l2 2l-2 2"></path></svg></label></div>
|
||||||
<div class="tooltip tooltip-right" data-tip="Projects">
|
<div class="tooltip tooltip-right" data-tip="Projects">
|
||||||
<a class="flex w-fit h-fit text-2xl items-end px-4 py-2 btn btn-square btn-ghost hover:bg-neutral hover:text-white" href="/projects">
|
<a class="flex w-fit h-fit text-2xl items-end px-4 py-2 btn btn-square btn-ghost hover:bg-neutral hover:text-white" href="/projects">
|
||||||
<img src="/images/mand_logo_sm.webp">
|
<img src="/images/mand_logo_sm.webp">
|
||||||
@@ -1235,11 +1235,25 @@ is-drawer-close:justify-center is-drawer-close:mx-auto is-drawer-close:space-y-2
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<div class="bottom-0 left-0 bg-error">
|
<div class="bottom-0 left-0 bg-error">
|
||||||
<footer class="footer sm:footer-horizontal footer-center
|
<footer class="footer footer-horizontal footer-center
|
||||||
bg-base-300 text-base-content p-4">
|
bg-base-300 text-base-content p-4">
|
||||||
<aside>
|
<aside class="grid-flow-row items-center">
|
||||||
<p>Copyright © 2026 - All rights reserved by Michael Housh</p>
|
<div class="flex mx-auto">
|
||||||
Openly licensed via CC-BY-NC-SA 4.0<a class="btn btn-ghost" href="https://git.housh.dev/michael/swift-duct-calc/src/branch/main/LICENSE" target="_blank"></a>
|
<a class="btn btn-ghost" href="mailto:support@ductcalc.pro"> <svg class="h-[1em] opacity-50" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<g
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-width="2.5"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<rect width="20" height="16" x="2" y="4" rx="2"></rect>
|
||||||
|
<path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"></path>
|
||||||
|
</g>
|
||||||
|
</svg><span>support@ductcalc.pro</span></a>
|
||||||
|
</div>
|
||||||
|
Openly licensed via CC-BY-NC-SA 4.0<a class="btn btn-ghost mx-auto" href="https://github.com/m-housh/swift-duct-calc/src/branch/main/LICENSE" target="_blank"></a>
|
||||||
|
<p class="">Copyright © 2026 - All rights reserved by Michael Housh</p>
|
||||||
</aside>
|
</aside>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -111,11 +111,25 @@ p-6 w-full pb-6">
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<div class="bottom-0 left-0 bg-error">
|
<div class="bottom-0 left-0 bg-error">
|
||||||
<footer class="footer sm:footer-horizontal footer-center
|
<footer class="footer footer-horizontal footer-center
|
||||||
bg-base-300 text-base-content p-4">
|
bg-base-300 text-base-content p-4">
|
||||||
<aside>
|
<aside class="grid-flow-row items-center">
|
||||||
<p>Copyright © 2026 - All rights reserved by Michael Housh</p>
|
<div class="flex mx-auto">
|
||||||
Openly licensed via CC-BY-NC-SA 4.0<a class="btn btn-ghost" href="https://git.housh.dev/michael/swift-duct-calc/src/branch/main/LICENSE" target="_blank"></a>
|
<a class="btn btn-ghost" href="mailto:support@ductcalc.pro"> <svg class="h-[1em] opacity-50" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<g
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-width="2.5"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<rect width="20" height="16" x="2" y="4" rx="2"></rect>
|
||||||
|
<path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"></path>
|
||||||
|
</g>
|
||||||
|
</svg><span>support@ductcalc.pro</span></a>
|
||||||
|
</div>
|
||||||
|
Openly licensed via CC-BY-NC-SA 4.0<a class="btn btn-ghost mx-auto" href="https://github.com/m-housh/swift-duct-calc/src/branch/main/LICENSE" target="_blank"></a>
|
||||||
|
<p class="">Copyright © 2026 - All rights reserved by Michael Housh</p>
|
||||||
</aside>
|
</aside>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -155,11 +155,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<div class="bottom-0 left-0 bg-error">
|
<div class="bottom-0 left-0 bg-error">
|
||||||
<footer class="footer sm:footer-horizontal footer-center
|
<footer class="footer footer-horizontal footer-center
|
||||||
bg-base-300 text-base-content p-4">
|
bg-base-300 text-base-content p-4">
|
||||||
<aside>
|
<aside class="grid-flow-row items-center">
|
||||||
<p>Copyright © 2026 - All rights reserved by Michael Housh</p>
|
<div class="flex mx-auto">
|
||||||
Openly licensed via CC-BY-NC-SA 4.0<a class="btn btn-ghost" href="https://git.housh.dev/michael/swift-duct-calc/src/branch/main/LICENSE" target="_blank"></a>
|
<a class="btn btn-ghost" href="mailto:support@ductcalc.pro"> <svg class="h-[1em] opacity-50" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<g
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-width="2.5"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<rect width="20" height="16" x="2" y="4" rx="2"></rect>
|
||||||
|
<path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"></path>
|
||||||
|
</g>
|
||||||
|
</svg><span>support@ductcalc.pro</span></a>
|
||||||
|
</div>
|
||||||
|
Openly licensed via CC-BY-NC-SA 4.0<a class="btn btn-ghost mx-auto" href="https://github.com/m-housh/swift-duct-calc/src/branch/main/LICENSE" target="_blank"></a>
|
||||||
|
<p class="">Copyright © 2026 - All rights reserved by Michael Housh</p>
|
||||||
</aside>
|
</aside>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user