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(
name: "ViewController",
dependencies: [
.target(name: "DatabaseClient"),
.target(name: "ManualDCore"),
.target(name: "Styleguide"),
.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]? {
switch self {
case .project,
.frictionRate,
.effectiveLength:
return viewRouteMiddleware
case .login, .signup:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,11 +4,16 @@ import Styleguide
struct FrictionRateView: HTML, Sendable {
let componentLosses: [ComponentPressureLoss]
let projectID: Project.ID
var body: some HTML {
div(.class("p-4 space-y-6")) {
h1(.class("text-4xl font-bold pb-6")) { "Friction Rate" }
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))
.attributes(.data("active", value: active == .effectiveLength ? "true" : "false"))
row(title: "Friction Rate", icon: .squareFunction, route: .frictionRate(.index))
.attributes(.data("active", value: active == .frictionRate ? "true" : "false"))
row(
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: "#")
.attributes(.data("active", value: active == .ductSizing ? "true" : "false"))

View File

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

4051
output.css

File diff suppressed because it is too large Load Diff