feat: Finalizes room-pressure view.
This commit is contained in:
@@ -46,7 +46,8 @@ extension ViewController: DependencyKey {
|
||||
@Dependency(\.psychrometricClient) var psychrometricClient
|
||||
|
||||
return .init(view: { request in
|
||||
request.logger.debug("View route: \(request.route)")
|
||||
let logger = request.logger
|
||||
logger.debug("View route: \(request.route)")
|
||||
switch request.route {
|
||||
case .index:
|
||||
return MainPage {
|
||||
@@ -108,9 +109,9 @@ extension ViewController: DependencyKey {
|
||||
case let .index(mode):
|
||||
return request.respond(RoomPressureForm(mode: mode, response: nil))
|
||||
|
||||
// FIX:
|
||||
case .submit:
|
||||
fatalError()
|
||||
case let .submit(request):
|
||||
let response = try await request.respond(logger: logger)
|
||||
return RoomPressureResult(response: response)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -82,7 +82,7 @@ private struct Header: HTML {
|
||||
|
||||
private struct Footer: HTML {
|
||||
var content: some HTML {
|
||||
div(.class("bg-blue-500 text-yellow-300 text-sm font-semibold border-t border-yellow-300")) {
|
||||
div(.class("bg-blue-500 text-yellow-300 text-sm font-semibold border-t border-yellow-300 mt-20")) {
|
||||
div(.class("sm:grid sm:grid-cols-1 lg:flex lg:justify-between")) {
|
||||
div(.class("grid grid-cols-1 p-4")) {
|
||||
div(.class("flex")) {
|
||||
|
||||
@@ -16,12 +16,6 @@ struct RoomPressureForm: HTML, Sendable {
|
||||
div(.class("relative")) {
|
||||
FormHeader(label: "Room Pressure Calculator", svg: .leftRightArrow)
|
||||
|
||||
// FIX: Remove when done testing.
|
||||
WarningBox(
|
||||
"This calculator is currently under construction, so it does not do anything when the form is submitted."
|
||||
)
|
||||
.attributes(.class("mb-8"))
|
||||
|
||||
// Mode toggle / buttons.
|
||||
div(.class("absolute top-0 right-0 flex items-center gap-x-0")) {
|
||||
switch mode {
|
||||
@@ -74,7 +68,10 @@ struct RoomPressureForm: HTML, Sendable {
|
||||
let mode: RoomPressure.Mode
|
||||
|
||||
var content: some HTML<HTMLTag.form> {
|
||||
form {
|
||||
form(
|
||||
.hx.post(route: .roomPressure(.index)),
|
||||
.hx.target("#result")
|
||||
) {
|
||||
div(.class("space-y-6")) {
|
||||
LabeledContent(label: pressureLabel) {
|
||||
// NB: using .attributes(..., when:...) not working, so using a switch statement.
|
||||
@@ -171,4 +168,76 @@ struct RoomPressureForm: HTML, Sendable {
|
||||
|
||||
struct RoomPressureResult: HTML, Sendable {
|
||||
let response: RoomPressure.Response
|
||||
|
||||
var content: some HTML {
|
||||
ResultContainer(reset: .roomPressure(.index(mode: response.mode))) {
|
||||
div(.class("grid grid-cols-1 lg:grid-cols-2 gap-4")) {
|
||||
RoundedContainer(title: "Return / Transfer Grille") {
|
||||
div(.class("flex justify-between mt-6")) {
|
||||
span(.class("font-semibold")) { "Standard Size:" }
|
||||
span { """
|
||||
\(response.grilleSize.width)" x \(response.grilleSize.height)"
|
||||
""" }
|
||||
}
|
||||
div(.class("flex justify-between mt-3")) {
|
||||
span(.class("font-semibold")) { "Required Net Free Area:" }
|
||||
span {
|
||||
"""
|
||||
\(double: response.grilleSize.area, fractionDigits: 1) in
|
||||
"""
|
||||
sup { "2" }
|
||||
}
|
||||
}
|
||||
div(.class("mt-8 text-sm")) {
|
||||
span(.class("font-semibold")) { "Note: " }
|
||||
span {
|
||||
"Select a grille with at least \(double: response.grilleSize.area, fractionDigits: 1) in"
|
||||
sup { "2" }
|
||||
span { " net free area." }
|
||||
}
|
||||
}
|
||||
}
|
||||
.attributes(.class("bg-blue-100 border border-blue-600 text-blue-600"))
|
||||
|
||||
RoundedContainer(title: "Return / Transfer Duct") {
|
||||
div(.class("flex justify-between mt-6")) {
|
||||
span(.class("font-semibold")) { "Standard Size:" }
|
||||
span { "\(response.ductSize.diameter)\"" }
|
||||
}
|
||||
div(.class("flex justify-between mt-3")) {
|
||||
span(.class("font-semibold")) { "Air Velocity:" }
|
||||
span { "\(double: response.ductSize.velocity, fractionDigits: 1) FPM" }
|
||||
}
|
||||
}
|
||||
.attributes(.class("bg-purple-100 border border-purple-600 text-purple-600"))
|
||||
}
|
||||
|
||||
WarningBox(warnings: response.warnings)
|
||||
|
||||
Note {
|
||||
"""
|
||||
Calculations are based on a target velocity of 400 FPM for return/transfer air paths.
|
||||
The required net free area is the minimum needed - select a grille that meets or exceeds this value.
|
||||
Verify manufacturer specifications for actual net free area of selected grilles.
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct RoundedContainer<Body: HTML>: HTML, Sendable where Body: Sendable {
|
||||
let title: String
|
||||
let body: Body
|
||||
|
||||
init(title: String, @HTMLBuilder body: () -> Body) {
|
||||
self.title = title
|
||||
self.body = body()
|
||||
}
|
||||
|
||||
var content: some HTML<HTMLTag.div> {
|
||||
div(.class("rounded-xl p-6")) {
|
||||
h4(.class("text-xl font-bold")) { title }
|
||||
body
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user