Files
swift-hvac-toolbox/Sources/ViewController/Views/MoldRisk.swift

146 lines
4.3 KiB
Swift

import Elementary
import ElementaryHTMX
import PsychrometricClient
import Routes
import Styleguide
struct MoldRiskForm: HTML {
let response: MoldRisk.Response?
var content: some HTML {
FormHeader(label: "Mold Risk Calculator", svg: .thermometer)
form(
// Using index to get the correct path, but really uses the `submit` end-point.
.hx.post(route: .moldRisk(.index)),
.hx.target("#result")
) {
div(.class("space-y-6")) {
LabeledContent(label: "Indoor Temperature (°F)") {
Input(id: "temperature", placeholder: "Dry bulb temperature")
.attributes(.type(.number), .step("0.1"), .min("0.1"), .autofocus, .required)
}
LabeledContent(label: "Indoor Humdity (%)") {
Input(id: "humidity", placeholder: "Relative humidity")
.attributes(.type(.number), .step("0.1"), .min("0.1"), .required)
}
div {
SubmitButton(label: "Calculate Mold Risk")
}
}
}
div(.id("result")) {
if let response {
MoldRiskResponse(response: response)
}
}
}
}
struct MoldRiskResponse: HTML {
let response: MoldRisk.Response
var content: some HTML {
ResultContainer(reset: .moldRisk(.index)) {
div(
.class("""
p-2 rounded-lg shadow-lg \(response.riskLevel.backgroundColor) border-2 border \(response.riskLevel.borderColor)
""")
) {
// Risk level and days to mold header.
div(.class("\(response.riskLevel.textColor)")) {
div(.class("flex flex-wrap mt-2")) {
div(.class("w-full sm:w-1/2 flex gap-2")) {
SVG(.exclamation, color: "\(response.riskLevel.textColor)")
span(.class("text-lg font-extrabold")) {
"Risk Level: \(response.riskLevel.rawValue.capitalized)"
}
}
if let daysToMold = response.daysToMold {
div(.class("w-full sm:w-1/2 gap-2")) {
span(.class("font-semibold")) { "Estimated Days to Mold Growth: " }
span { "\(daysToMold) days" }
}
}
}
// Recommendations
if response.recommendations.count > 0 {
div(.class("mt-6 pb-4")) {
p(.class("font-semibold mb-4")) {
u {
"Recommendation\(response.recommendations.count == 1 ? "" : "s"):"
}
}
ul(.class("list-disc mx-10")) {
for recommendation in response.recommendations {
li { recommendation }
}
}
}
}
}
}
// Display psychrometric properties.
Properties(properties: response.psychrometricProperties)
.attributes(.class("mt-8"))
// Disclaimer.
Note {
"""
These calculations are based on typical indoor conditions and common mold species. Actual mold growth can
vary based on surface materials, air movement, and other environmental factors. Always address moisture
issues promptly and consult professionals for severe cases.
"""
}
}
}
private struct Properties: HTML, Sendable {
let properties: PsychrometricProperties
var content: some HTML<HTMLTag.div> {
div(.class("w-full rounded-lg border")) {
h3(.class("flex justify-center text-xl font-semibold mb-6 mt-2")) { "Psychrometric Properties" }
PsychrometricPropertiesView(properties: properties)
.attributes(.class("""
grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-6 gap-y-2 px-4 pb-4
"""))
}
}
}
}
private extension MoldRisk.RiskLevel {
var backgroundColor: String {
switch self {
case .low: return "bg-green-200"
case .moderate: return "bg-amber-200"
case .high: return "bg-orange-200"
case .severe: return "bg-red-200"
}
}
var borderColor: String {
switch self {
case .low: return "border-green-500"
case .moderate: return "border-amber-500"
case .high: return "border-orange-500"
case .severe: return "border-red-500"
}
}
var textColor: String {
switch self {
case .low: return "text-green-500"
case .moderate: return "text-amber-500"
case .high: return "text-orange-500"
case .severe: return "text-red-500"
}
}
}