WIP: Begins effective length views.
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import Dependencies
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
// TODO: Not sure how to model effective length groups in the database.
|
// TODO: Not sure how to model effective length groups in the database.
|
||||||
@@ -85,3 +86,40 @@ extension EffectiveLength {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
|
||||||
|
extension EffectiveLength {
|
||||||
|
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
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ extension SiteRoute {
|
|||||||
case project(ProjectRoute)
|
case project(ProjectRoute)
|
||||||
case room(RoomRoute)
|
case room(RoomRoute)
|
||||||
case frictionRate(FrictionRateRoute)
|
case frictionRate(FrictionRateRoute)
|
||||||
|
case effectiveLength(EffectiveLengthRoute)
|
||||||
|
|
||||||
public static let router = OneOf {
|
public static let router = OneOf {
|
||||||
Route(.case(Self.project)) {
|
Route(.case(Self.project)) {
|
||||||
@@ -21,6 +22,9 @@ extension SiteRoute {
|
|||||||
Route(.case(Self.frictionRate)) {
|
Route(.case(Self.frictionRate)) {
|
||||||
SiteRoute.View.FrictionRateRoute.router
|
SiteRoute.View.FrictionRateRoute.router
|
||||||
}
|
}
|
||||||
|
Route(.case(Self.effectiveLength)) {
|
||||||
|
SiteRoute.View.EffectiveLengthRoute.router
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,3 +129,29 @@ extension SiteRoute.View.FrictionRateRoute {
|
|||||||
case componentPressureLoss
|
case componentPressureLoss
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension SiteRoute.View {
|
||||||
|
public enum EffectiveLengthRoute: Equatable, Sendable {
|
||||||
|
case form(dismiss: Bool = false)
|
||||||
|
case index
|
||||||
|
|
||||||
|
static let rootPath = "effective-lengths"
|
||||||
|
|
||||||
|
public static let router = OneOf {
|
||||||
|
Route(.case(Self.index)) {
|
||||||
|
Path { rootPath }
|
||||||
|
Method.get
|
||||||
|
}
|
||||||
|
Route(.case(Self.form(dismiss:))) {
|
||||||
|
Path {
|
||||||
|
rootPath
|
||||||
|
"create"
|
||||||
|
}
|
||||||
|
Method.get
|
||||||
|
Query {
|
||||||
|
Field("dismiss", default: false) { Bool.parser() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ extension ViewController.Request {
|
|||||||
return try await route.renderView(isHtmxRequest: isHtmxRequest)
|
return try await route.renderView(isHtmxRequest: isHtmxRequest)
|
||||||
case .frictionRate(let route):
|
case .frictionRate(let route):
|
||||||
return try await route.renderView(isHtmxRequest: isHtmxRequest)
|
return try await route.renderView(isHtmxRequest: isHtmxRequest)
|
||||||
|
case .effectiveLength(let route):
|
||||||
|
return try await route.renderView(isHtmxRequest: isHtmxRequest)
|
||||||
default:
|
default:
|
||||||
// FIX: FIX
|
// FIX: FIX
|
||||||
return mainPage
|
return mainPage
|
||||||
@@ -86,6 +88,23 @@ extension SiteRoute.View.FrictionRateRoute.FormType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension SiteRoute.View.EffectiveLengthRoute {
|
||||||
|
|
||||||
|
func renderView(isHtmxRequest: Bool) async throws -> AnySendableHTML {
|
||||||
|
switch self {
|
||||||
|
case .index:
|
||||||
|
return MainPage {
|
||||||
|
EffectiveLengthsView(effectiveLengths: EffectiveLength.mocks)
|
||||||
|
}
|
||||||
|
case .form(let dismiss):
|
||||||
|
guard !dismiss else {
|
||||||
|
return div(.id("effectiveLengthForm")) {}
|
||||||
|
}
|
||||||
|
return EffectiveLengthForm()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private let mainPage: AnySendableHTML = {
|
private let mainPage: AnySendableHTML = {
|
||||||
MainPage {
|
MainPage {
|
||||||
div {
|
div {
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import Elementary
|
||||||
|
import ElementaryHTMX
|
||||||
|
import ManualDCore
|
||||||
|
import Styleguide
|
||||||
|
|
||||||
|
struct EffectiveLengthForm: HTML, Sendable {
|
||||||
|
|
||||||
|
var body: some HTML {
|
||||||
|
div(
|
||||||
|
.id("effectiveLengthForm"),
|
||||||
|
.class(
|
||||||
|
"""
|
||||||
|
fixed top-40 left-[25vw] w-1/2 z-50 text-gray-800
|
||||||
|
bg-gray-200 border border-gray-400
|
||||||
|
rounded-lg shadow-lg mx-10
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
h1(.class("text-2xl font-bold")) { "Effective Length" }
|
||||||
|
form(.class("space-y-4 p-4")) {
|
||||||
|
// FIX: Add fields
|
||||||
|
|
||||||
|
Row {
|
||||||
|
div {}
|
||||||
|
div {
|
||||||
|
CancelButton()
|
||||||
|
.attributes(
|
||||||
|
.hx.get(route: .effectiveLength(.form(dismiss: true))),
|
||||||
|
.hx.target("#effectiveLengthForm"),
|
||||||
|
.hx.swap(.outerHTML)
|
||||||
|
)
|
||||||
|
SubmitButton()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
import Elementary
|
||||||
|
import ElementaryHTMX
|
||||||
|
import ManualDCore
|
||||||
|
import Styleguide
|
||||||
|
|
||||||
|
struct EffectiveLengthsView: HTML, Sendable {
|
||||||
|
|
||||||
|
let effectiveLengths: [EffectiveLength]
|
||||||
|
|
||||||
|
var body: some HTML {
|
||||||
|
div(
|
||||||
|
.class(
|
||||||
|
"""
|
||||||
|
m-4
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Row {
|
||||||
|
h1(.class("text-2xl font-bold")) { "Effective Lengths" }
|
||||||
|
button(
|
||||||
|
.hx.get(route: .effectiveLength(.form(dismiss: false))),
|
||||||
|
.hx.target("#effectiveLengthForm"),
|
||||||
|
.hx.swap(.outerHTML)
|
||||||
|
) {
|
||||||
|
Icon(.circlePlus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.attributes(.class("pb-6"))
|
||||||
|
|
||||||
|
div(
|
||||||
|
.id("effectiveLengths"),
|
||||||
|
.class(
|
||||||
|
"""
|
||||||
|
border border-gray-200 rounded-lg shadow-lg
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
for row in effectiveLengths {
|
||||||
|
EffectiveLengthView(effectiveLength: row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div(.id("effectiveLengthForm")) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct EffectiveLengthView: HTML, Sendable {
|
||||||
|
|
||||||
|
let effectiveLength: EffectiveLength
|
||||||
|
|
||||||
|
var straightLengthsTotal: Int {
|
||||||
|
effectiveLength.straightLengths
|
||||||
|
.reduce(into: 0) { $0 += $1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
var groupsTotal: Double {
|
||||||
|
effectiveLength.groups.reduce(into: 0) {
|
||||||
|
$0 += ($1.value * Double($1.quantity))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some HTML<HTMLTag.div> {
|
||||||
|
div(
|
||||||
|
.class(
|
||||||
|
"""
|
||||||
|
pb-6
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Row {
|
||||||
|
span(.class("text-xl font-bold")) { effectiveLength.name }
|
||||||
|
}
|
||||||
|
Row {
|
||||||
|
Label("Straight Lengths")
|
||||||
|
}
|
||||||
|
for length in effectiveLength.straightLengths {
|
||||||
|
Row {
|
||||||
|
div {}
|
||||||
|
Number(length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
Label("Groups")
|
||||||
|
Label("Equivalent Length")
|
||||||
|
Label("Quantity")
|
||||||
|
}
|
||||||
|
.attributes(.class("border-b border-gray-200"))
|
||||||
|
|
||||||
|
for group in effectiveLength.groups {
|
||||||
|
Row {
|
||||||
|
span { "\(group.group)-\(group.letter)" }
|
||||||
|
Number(group.value)
|
||||||
|
Number(group.quantity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Row {
|
||||||
|
Label("Total")
|
||||||
|
Number(Double(straightLengthsTotal) + groupsTotal, digits: 0)
|
||||||
|
.attributes(.class("text-xl font-bold"))
|
||||||
|
}
|
||||||
|
.attributes(.class("border border-gray-200"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,13 +16,14 @@ struct Sidebar: HTML {
|
|||||||
) {
|
) {
|
||||||
row(title: "Project", icon: .mapPin, href: "/projects")
|
row(title: "Project", icon: .mapPin, href: "/projects")
|
||||||
row(title: "Rooms", icon: .doorClosed, href: "/rooms")
|
row(title: "Rooms", icon: .doorClosed, href: "/rooms")
|
||||||
row(title: "Equivalent Lengths", icon: .rulerDimensionLine, href: "#")
|
row(title: "Equivalent Lengths", icon: .rulerDimensionLine, href: "/effective-lengths")
|
||||||
row(title: "Friction Rate", icon: .squareFunction, href: "/friction-rate")
|
row(title: "Friction Rate", icon: .squareFunction, href: "/friction-rate")
|
||||||
.attributes(.data("active", value: "true"))
|
.attributes(.data("active", value: "true"))
|
||||||
row(title: "Duct Sizes", icon: .wind, href: "#")
|
row(title: "Duct Sizes", icon: .wind, href: "#")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Use SiteRoute.View routes as href.
|
||||||
private func row(
|
private func row(
|
||||||
title: String,
|
title: String,
|
||||||
icon: Icon.Key,
|
icon: Icon.Key,
|
||||||
|
|||||||
Reference in New Issue
Block a user