feat: Adds quick calculation views, need to add buttons / links in navbar / home page.

This commit is contained in:
2026-02-09 15:34:28 -05:00
parent 88af6f722e
commit 007d13be2f
23 changed files with 584 additions and 490 deletions

View File

@@ -53,6 +53,13 @@ extension ViewController: DependencyKey {
extension ViewController.Request {
var isLoggedIn: Bool {
if (try? currentUser()) != nil {
return true
}
return false
}
func currentUser() throws -> User {
@Dependency(\.auth.currentUser) var currentUser
return try currentUser()

View File

@@ -3,6 +3,7 @@ import DatabaseClient
import Dependencies
import Elementary
import Foundation
import ManualDClient
import ManualDCore
import PdfClient
import ProjectClient
@@ -38,7 +39,9 @@ extension ViewController.Request {
// }
// }
// return try! await pdfClient.html(.mock())
return EmptyHTML()
return await view {
TestPage()
}
case .login(let route):
switch route {
case .index(let next):
@@ -93,6 +96,9 @@ extension ViewController.Request {
case .project(let route):
return await route.renderView(on: self)
case .quickCalc(let route):
return await route.renderView(on: self)
case .user(let route):
return await route.renderView(on: self)
}
@@ -705,3 +711,33 @@ extension SiteRoute.View.UserRoute.Profile {
}
}
}
extension SiteRoute.View.QuickCalcRoute {
func renderView(
on request: ViewController.Request
) async -> AnySendableHTML {
@Dependency(\.manualD) var manualD
switch self {
case .index:
return await request.view {
QuickCalcView(
isLoggedIn: request.isLoggedIn
)
}
case .submit(let form):
return await ResultView {
let ductSize = try await manualD.ductSize(cfm: form.cfm, frictionRate: form.frictionRate)
var rectangularSize: ManualDClient.RectangularSize? = nil
if let height = form.height {
rectangularSize = try await manualD.rectangularSize(
round: ductSize.finalSize, height: height)
}
return (ductSize, rectangularSize)
} onSuccess: { (ductSize, rectangularSize) in
QuickCalcView.Result(ductSize: ductSize, rectangularSize: rectangularSize)
}
}
}
}

View File

@@ -2,7 +2,6 @@ import Elementary
import ManualDCore
import Styleguide
// TODO: Have form hold onto equipment info model to edit.
struct EquipmentInfoForm: HTML, Sendable {
static let id = "equipmentForm"

View File

@@ -14,6 +14,13 @@ struct Navbar: HTML, Sendable {
self.userProfile = userProfile
}
var homeRoute: SiteRoute.View {
if userProfile {
return .project(.index)
}
return .home
}
var body: some HTML<HTMLTag.nav> {
nav(
.class(
@@ -37,7 +44,7 @@ struct Navbar: HTML, Sendable {
a(
.class("flex w-fit h-fit text-xl items-end px-4 py-2"),
.href(route: .project(.index))
.href(route: homeRoute)
) {
img(
.src("/images/mand_logo_sm.webp"),
@@ -48,11 +55,11 @@ struct Navbar: HTML, Sendable {
.tooltip("Home", position: .right)
}
if userProfile {
// TODO: Make dropdown
div(.class("flex-none dropdown dropdown-end dropdown-hover")) {
div(.class("btn m-1"), .tabindex(0), .role("button")) {
SVG(.circleUser)
}
.navButton()
ul(
.tabindex(-1),
.class("dropdown-content menu bg-base-200 rounded-box z-1 w-52 py-2 shadow-sm")

View File

@@ -0,0 +1,141 @@
import Dependencies
import Elementary
import ElementaryHTMX
import Foundation
import ManualDClient
import ManualDCore
import Styleguide
struct QuickCalcView: HTML, Sendable {
let isLoggedIn: Bool
init(isLoggedIn: Bool = false) {
self.isLoggedIn = isLoggedIn
}
var body: some HTML {
div {
Navbar(
sidebarToggle: false,
userProfile: isLoggedIn
)
div(.class("flex justify-center items-center px-10")) {
div(
.class(
"""
bg-base-300 rounded-3xl shadow-3xl
p-6 w-full
"""
)
) {
div(.class("flex space-x-6 items-center text-4xl")) {
SVG(.calculator)
h1(.class("text-4xl font-bold me-10")) {
"Duct Size"
}
}
p(.class("text-primary font-bold italic")) {
"Calculate duct size for the given parameters"
}
form(
.class("space-y-4 mt-6"),
.hx.post(route: .quickCalc(.index)),
.hx.target("#\(Result.id)"),
.hx.swap(.outerHTML)
) {
LabeledInput(
"CFM",
.name("cfm"),
.type(.number),
.placeholder("1000"),
.required,
.autofocus
)
LabeledInput(
"Friction Rate",
.name("frictionRate"),
.value("0.06"),
.required,
.type(.number),
.min("0.01"),
.step("0.01")
)
LabeledInput(
"Height",
.name("height"),
.type(.number),
.placeholder("Height (Optional)"),
)
SubmitButton()
.attributes(.class("btn-block mt-6"))
}
// Populate when submitted
div(.id(Result.id)) {}
}
}
}
}
struct Result: HTML, Sendable {
static let id = "resultView"
let ductSize: ManualDClient.DuctSize
let rectangularSize: ManualDClient.RectangularSize?
var body: some HTML<HTMLTag.div> {
div(
.id(Self.id),
.class(
"""
border-2 border-accent rounded-lg shadow-lg
w-full p-6 my-6
"""
)
) {
div(.class("flex justify-between p-4")) {
h2(.class("text-3xl font-bold")) { "Result" }
button(
.class("btn btn-primary"),
.hx.get(route: .quickCalc(.index)),
.hx.target("body"),
.hx.swap(.outerHTML)
) {
"Reset"
}
.tooltip("Reset form", position: .left)
}
table(.class("table table-zebra text-lg font-bold")) {
tbody {
tr {
td { Label("Calculated Size") }
td { Number(ductSize.calculatedSize, digits: 2) }
}
tr {
td { Label("Final Size") }
td { Number(ductSize.finalSize) }
}
tr {
td { Label("Flex Size") }
td { Number(ductSize.flexSize) }
}
if let rectangularSize {
tr {
td { Label("Rectangular Size") }
td { "\(rectangularSize.width) x \(rectangularSize.height)" }
}
}
}
}
}
}
}
}

View File

@@ -1,6 +1,7 @@
import Dependencies
import Elementary
import Foundation
import ManualDClient
import ManualDCore
import Styleguide
@@ -8,24 +9,77 @@ struct TestPage: HTML, Sendable {
// let ductSizes: DuctSizes
var body: some HTML {
div {}
// div(.class("overflow-auto")) {
// DuctSizingView.TrunkTable(ductSizes: ductSizes)
//
// Row {
// h2(.class("text-2xl font-bold")) { "Trunk Sizes" }
//
// PlusButton()
// .attributes(
// .class("me-6"),
// .showModal(id: TrunkSizeForm.id())
// )
// }
// .attributes(.class("mt-6"))
//
// div(.class("divider -mt-2")) {}
//
// DuctSizingView.TrunkTable(ductSizes: ductSizes)
// }
div {
Navbar(sidebarToggle: false, userProfile: false)
div(.class("flex justify-center items-center px-10")) {
div(
.class(
"""
bg-base-300 rounded-3xl shadow-3xl
p-6 w-full
"""
)
) {
div(.class("flex space-x-6 items-center text-4xl")) {
SVG(.calculator)
h1(.class("text-4xl font-bold me-10")) {
"Duct Size"
}
}
p(.class("text-primary font-bold italic")) {
"Calculate duct size for the given parameters"
}
form(
.class("space-y-4 mt-6"),
.action("#")
) {
LabeledInput(
"CFM",
.required,
.type(.number),
.placeholder("1000"),
.name("cfm")
)
LabeledInput(
"Friction Rate",
.value("0.06"),
.required,
.type(.number),
.name("frictionRate")
)
LabeledInput(
"Height",
.required,
.type(.number),
.placeholder("Height (Optional)"),
.name("frictionRate")
)
SubmitButton()
.attributes(.class("btn-block mt-6"))
}
}
// Populate when submitted
div(.id(Result.id)) {}
}
}
}
struct Result: HTML, Sendable {
static let id = "resultView"
let ductSize: ManualDClient.DuctSize
let rectangularSize: ManualDClient.RectangularSize?
var body: some HTML<HTMLTag.div> {
div(.id(Self.id)) {
}
}
}
}