WIP: Moves friction rate route to be part of project detail routes.

This commit is contained in:
2026-01-05 09:01:49 -05:00
parent 4aca134abd
commit 55a3adde25
14 changed files with 71 additions and 6759 deletions

View File

@@ -103,6 +103,7 @@ let package = Package(
.target( .target(
name: "ViewController", name: "ViewController",
dependencies: [ dependencies: [
.target(name: "DatabaseClient"),
.target(name: "ManualDCore"), .target(name: "ManualDCore"),
.target(name: "Styleguide"), .target(name: "Styleguide"),
.product(name: "Dependencies", package: "swift-dependencies"), .product(name: "Dependencies", package: "swift-dependencies"),

File diff suppressed because it is too large Load Diff

View File

@@ -15,7 +15,6 @@ extension SiteRoute.View {
var middleware: [any Middleware]? { var middleware: [any Middleware]? {
switch self { switch self {
case .project, case .project,
.frictionRate,
.effectiveLength: .effectiveLength:
return viewRouteMiddleware return viewRouteMiddleware
case .login, .signup: case .login, .signup:

View File

@@ -10,7 +10,7 @@ extension DatabaseClient {
public var create: public var create:
@Sendable (ComponentPressureLoss.Create) async throws -> ComponentPressureLoss @Sendable (ComponentPressureLoss.Create) async throws -> ComponentPressureLoss
public var delete: @Sendable (ComponentPressureLoss.ID) async throws -> Void public var delete: @Sendable (ComponentPressureLoss.ID) async throws -> Void
public var fetch: @Sendable (Project.ID) async throws -> ComponentPressureLoss public var fetch: @Sendable (Project.ID) async throws -> [ComponentPressureLoss]
public var get: @Sendable (ComponentPressureLoss.ID) async throws -> ComponentPressureLoss? public var get: @Sendable (ComponentPressureLoss.ID) async throws -> ComponentPressureLoss?
} }
} }
@@ -34,14 +34,11 @@ extension DatabaseClient.ComponentLoss {
try await model.delete(on: database) try await model.delete(on: database)
}, },
fetch: { projectID in fetch: { projectID in
guard try await ComponentLossModel.query(on: database)
let model = try await ComponentLossModel.query(on: database) .with(\.$project)
.filter("projectID", .equal, projectID) .filter(\.$project.$id, .equal, projectID)
.first() .all()
else { .map { try $0.toDTO() }
throw NotFoundError()
}
return try model.toDTO()
}, },
get: { id in get: { id in

View File

@@ -10,7 +10,7 @@ extension SiteRoute {
case login(LoginRoute) case login(LoginRoute)
case signup(SignupRoute) case signup(SignupRoute)
case project(ProjectRoute) case project(ProjectRoute)
case frictionRate(FrictionRateRoute) // case frictionRate(FrictionRateRoute)
case effectiveLength(EffectiveLengthRoute) case effectiveLength(EffectiveLengthRoute)
// case user(UserRoute) // case user(UserRoute)
@@ -24,9 +24,9 @@ extension SiteRoute {
Route(.case(Self.project)) { Route(.case(Self.project)) {
SiteRoute.View.ProjectRoute.router SiteRoute.View.ProjectRoute.router
} }
Route(.case(Self.frictionRate)) { // Route(.case(Self.frictionRate)) {
SiteRoute.View.FrictionRateRoute.router // SiteRoute.View.FrictionRateRoute.router
} // }
Route(.case(Self.effectiveLength)) { Route(.case(Self.effectiveLength)) {
SiteRoute.View.EffectiveLengthRoute.router SiteRoute.View.EffectiveLengthRoute.router
} }
@@ -102,12 +102,16 @@ extension SiteRoute.View.ProjectRoute {
public enum DetailRoute: Equatable, Sendable { public enum DetailRoute: Equatable, Sendable {
case index case index
case frictionRate(FrictionRateRoute)
case rooms(RoomRoute) case rooms(RoomRoute)
static let router = OneOf { static let router = OneOf {
Route(.case(Self.index)) { Route(.case(Self.index)) {
Method.get Method.get
} }
Route(.case(Self.frictionRate)) {
FrictionRateRoute.router
}
Route(.case(Self.rooms)) { Route(.case(Self.rooms)) {
RoomRoute.router RoomRoute.router
} }
@@ -153,9 +157,7 @@ extension SiteRoute.View.ProjectRoute {
} }
} }
} }
}
extension SiteRoute.View {
public enum FrictionRateRoute: Equatable, Sendable { public enum FrictionRateRoute: Equatable, Sendable {
case index case index
case form(FormType, dismiss: Bool = false) case form(FormType, dismiss: Bool = false)
@@ -179,15 +181,12 @@ extension SiteRoute.View {
} }
} }
} }
}
}
extension SiteRoute.View.FrictionRateRoute { public enum FormType: String, CaseIterable, Codable, Equatable, Sendable {
public enum FormType: String, CaseIterable, Codable, Equatable, Sendable { case equipmentInfo
case equipmentInfo case componentPressureLoss
case componentPressureLoss }
} }
} }
extension SiteRoute.View { extension SiteRoute.View {

View File

@@ -39,10 +39,6 @@ extension ViewController.Request {
} }
case .project(let route): case .project(let route):
return try await route.renderView(on: self) return try await route.renderView(on: self)
// case .room(let route):
// return try await route.renderView(on: self)
case .frictionRate(let route):
return try await route.renderView(isHtmxRequest: isHtmxRequest)
case .effectiveLength(let route): case .effectiveLength(let route):
return try await route.renderView(isHtmxRequest: isHtmxRequest) return try await route.renderView(isHtmxRequest: isHtmxRequest)
// case .user(let route): // case .user(let route):
@@ -110,6 +106,9 @@ extension SiteRoute.View.ProjectRoute {
ProjectDetail(project: project) ProjectDetail(project: project)
} }
} }
case .frictionRate(let route):
return try await route.renderView(on: request, projectID: projectID)
case .rooms(let route): case .rooms(let route):
return try await route.renderView(on: request, projectID: projectID) return try await route.renderView(on: request, projectID: projectID)
} }
@@ -155,26 +154,34 @@ extension SiteRoute.View.ProjectRoute.RoomRoute {
} }
} }
extension SiteRoute.View.FrictionRateRoute { extension SiteRoute.View.ProjectRoute.FrictionRateRoute {
func renderView(isHtmxRequest: Bool) async throws -> AnySendableHTML { func renderView(on request: ViewController.Request, projectID: Project.ID) async throws
-> AnySendableHTML
{
@Dependency(\.database) var database
switch self { switch self {
case .index: case .index:
return _render(isHtmxRequest: isHtmxRequest, active: .frictionRate) { let componentLosses = try await database.componentLoss.fetch(projectID)
FrictionRateView()
return request.view {
ProjectView(projectID: projectID, activeTab: .frictionRate) {
FrictionRateView(componentLosses: componentLosses, projectID: projectID)
}
} }
case .form(let type, let dismiss): case .form(let type, let dismiss):
// FIX: Forms need to reference existing items. // FIX: Forms need to reference existing items.
switch type { switch type {
case .equipmentInfo: case .equipmentInfo:
return EquipmentForm(dismiss: dismiss) return EquipmentForm(dismiss: dismiss, projectID: projectID)
case .componentPressureLoss: case .componentPressureLoss:
return ComponentLossForm(dismiss: dismiss) return ComponentLossForm(dismiss: dismiss, projectID: projectID)
} }
} }
} }
} }
extension SiteRoute.View.FrictionRateRoute.FormType { extension SiteRoute.View.ProjectRoute.FrictionRateRoute.FormType {
var id: String { var id: String {
switch self { switch self {
case .equipmentInfo: case .equipmentInfo:

View File

@@ -5,6 +5,7 @@ import Styleguide
struct ComponentLossForm: HTML, Sendable { struct ComponentLossForm: HTML, Sendable {
let dismiss: Bool let dismiss: Bool
let projectID: Project.ID
var body: some HTML { var body: some HTML {
ModalForm(id: "componentLossForm", dismiss: dismiss) { ModalForm(id: "componentLossForm", dismiss: dismiss) {
@@ -25,7 +26,10 @@ struct ComponentLossForm: HTML, Sendable {
div { div {
CancelButton() CancelButton()
.attributes( .attributes(
.hx.get(route: .frictionRate(.form(.componentPressureLoss, dismiss: true))), .hx.get(
route: .project(
.detail(projectID, .frictionRate(.form(.componentPressureLoss, dismiss: true))))
),
.hx.target("#componentLossForm"), .hx.target("#componentLossForm"),
.hx.swap(.outerHTML) .hx.swap(.outerHTML)
) )

View File

@@ -3,9 +3,12 @@ import ElementaryHTMX
import ManualDCore import ManualDCore
import Styleguide import Styleguide
// TODO: Load component losses when view appears??
struct ComponentPressureLossesView: HTML, Sendable { struct ComponentPressureLossesView: HTML, Sendable {
let componentPressureLosses: [ComponentPressureLoss] let componentPressureLosses: [ComponentPressureLoss]
let projectID: Project.ID
private var total: Double { private var total: Double {
componentPressureLosses.reduce(into: 0) { $0 += $1.value } componentPressureLosses.reduce(into: 0) { $0 += $1.value }
@@ -23,7 +26,10 @@ struct ComponentPressureLossesView: HTML, Sendable {
h1(.class("text-2xl font-bold")) { "Component Pressure Losses" } h1(.class("text-2xl font-bold")) { "Component Pressure Losses" }
PlusButton() PlusButton()
.attributes( .attributes(
.hx.get(route: .frictionRate(.form(.componentPressureLoss, dismiss: false))), .hx.get(
route: .project(
.detail(projectID, .frictionRate(.form(.componentPressureLoss, dismiss: false))))
),
.hx.target("#componentLossForm"), .hx.target("#componentLossForm"),
.hx.swap(.outerHTML) .hx.swap(.outerHTML)
) )
@@ -43,8 +49,7 @@ struct ComponentPressureLossesView: HTML, Sendable {
.attributes(.class("text-xl font-bold")) .attributes(.class("text-xl font-bold"))
} }
} }
// div(.id("componentLossForm")) {} ComponentLossForm(dismiss: true, projectID: projectID)
ComponentLossForm(dismiss: true)
} }
} }

View File

@@ -6,6 +6,7 @@ import Styleguide
struct EquipmentForm: HTML, Sendable { struct EquipmentForm: HTML, Sendable {
let dismiss: Bool let dismiss: Bool
let projectID: Project.ID
var body: some HTML { var body: some HTML {
ModalForm(id: "equipmentForm", dismiss: dismiss) { ModalForm(id: "equipmentForm", dismiss: dismiss) {
@@ -33,7 +34,11 @@ struct EquipmentForm: HTML, Sendable {
div(.class("space-x-4")) { div(.class("space-x-4")) {
CancelButton() CancelButton()
.attributes( .attributes(
.hx.get(route: .frictionRate(.form(.equipmentInfo, dismiss: true))), .hx.get(
route: .project(
.detail(projectID, .frictionRate(.form(.equipmentInfo, dismiss: true)))
)
),
.hx.target("#equipmentForm"), .hx.target("#equipmentForm"),
.hx.swap(.outerHTML) .hx.swap(.outerHTML)
) )

View File

@@ -4,6 +4,7 @@ import Styleguide
struct EquipmentInfoView: HTML, Sendable { struct EquipmentInfoView: HTML, Sendable {
let equipmentInfo: EquipmentInfo let equipmentInfo: EquipmentInfo
var projectID: Project.ID { equipmentInfo.projectID }
var body: some HTML { var body: some HTML {
div(.class("space-y-4 border border-gray-200 rounded-lg shadow-lg p-4")) { div(.class("space-y-4 border border-gray-200 rounded-lg shadow-lg p-4")) {
@@ -33,7 +34,7 @@ struct EquipmentInfoView: HTML, Sendable {
div {} div {}
EditButton() EditButton()
.attributes( .attributes(
.hx.get(route: .frictionRate(.form(.equipmentInfo))), .hx.get(route: .project(.detail(projectID, .frictionRate(.form(.equipmentInfo))))),
.hx.target("#equipmentForm"), .hx.target("#equipmentForm"),
.hx.swap(.outerHTML) .hx.swap(.outerHTML)
) )

View File

@@ -4,11 +4,16 @@ import Styleguide
struct FrictionRateView: HTML, Sendable { struct FrictionRateView: HTML, Sendable {
let componentLosses: [ComponentPressureLoss]
let projectID: Project.ID
var body: some HTML { var body: some HTML {
div(.class("p-4 space-y-6")) { div(.class("p-4 space-y-6")) {
h1(.class("text-4xl font-bold pb-6")) { "Friction Rate" } h1(.class("text-4xl font-bold pb-6")) { "Friction Rate" }
EquipmentInfoView(equipmentInfo: EquipmentInfo.mock) EquipmentInfoView(equipmentInfo: EquipmentInfo.mock)
ComponentPressureLossesView(componentPressureLosses: ComponentPressureLoss.mock) ComponentPressureLossesView(
componentPressureLosses: componentLosses, projectID: projectID
)
} }
} }
} }

View File

@@ -65,8 +65,12 @@ struct Sidebar: HTML {
row(title: "Equivalent Lengths", icon: .rulerDimensionLine, route: .effectiveLength(.index)) row(title: "Equivalent Lengths", icon: .rulerDimensionLine, route: .effectiveLength(.index))
.attributes(.data("active", value: active == .effectiveLength ? "true" : "false")) .attributes(.data("active", value: active == .effectiveLength ? "true" : "false"))
row(title: "Friction Rate", icon: .squareFunction, route: .frictionRate(.index)) row(
.attributes(.data("active", value: active == .frictionRate ? "true" : "false")) title: "Friction Rate",
icon: .squareFunction,
route: .project(.detail(projectID, .frictionRate(.index)))
)
.attributes(.data("active", value: active == .frictionRate ? "true" : "false"))
row(title: "Duct Sizes", icon: .wind, href: "#") row(title: "Duct Sizes", icon: .wind, href: "#")
.attributes(.data("active", value: active == .ductSizing ? "true" : "false")) .attributes(.data("active", value: active == .ductSizing ? "true" : "false"))

View File

@@ -4,7 +4,7 @@ install-deps:
@curl -sL daisyui.com/fast | bash @curl -sL daisyui.com/fast | bash
run-css: run-css:
@./tailwindcss -i input.css -o output.css --watch @./tailwindcss -i Public/css/main.css -o Public/css/output.css --watch
run: run:
@swift run App serve --log debug @swift run App serve --log debug

4051
output.css

File diff suppressed because it is too large Load Diff