import Elementary import PsychrometricClient import Routes import Styleguide struct MoldRiskForm: HTML { var content: some HTML { FormHeader(label: "Mold Risk Calculator", svg: .thermometer) form(.action("#")) { 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) } LabeledContent(label: "Indor Humdity (%)") { Input(id: "humidity", placeholder: "Relative humidity") .attributes(.type(.number), .step("0.1"), .min("0.1")) } div { SubmitButton(label: "Calculate Mold Risk") } div(.id("result")) { MoldRiskResponse(response: .mock) } } } } } struct MoldRiskResponse: HTML { let response: MoldRisk.Response var content: some HTML { ResultContainer { // TODO: Color needs to be derived from risk level. div(.class("p-2 rounded-lg shadow-lg bg-lime-100 border-2 border border-lime-600")) { LabeledContent { p(.class("text-lg font-semibold \(text: .green) dark:text-lime-600 mt-2")) { "Risk Level: \(response.riskLevel.rawValue.capitalized)" } } label: { SVG(.exclamation, color: "text-green-600 dark:text-lime-600") } .attributes(.class("flex items-center gap-2")) } PsychrometricPropertiesGrid(properties: response.psychrometricProperties) .attributes(.class("mx-6")) div(.class("mt-8 p-4 bg-gray-100 dark:bg-gray-700 rounded-md shadow-md border border-blue-500")) { p(.class("text-sm text-blue-500")) { span(.class("font-extrabold pe-2")) { "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. """ } } } } } struct PsychrometricPropertiesGrid: HTML { let properties: PsychrometricProperties var content: some HTML { div(.class("grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mt-6 md:mx-20")) { displayProperty("Dew Point", \.dewPoint.rawValue) displayProperty("Wet Bulb", \.wetBulb.rawValue) displayProperty("Enthalpy", \.enthalpy.rawValue) displayProperty("Density", \.density.rawValue) displayProperty("Vapor Pressure", \.vaporPressure.rawValue) displayProperty("Specific Volume", properties.specificVolume.rawValue) displayProperty("Absolute Humidity", \.absoluteHumidity) displayProperty("Humidity Ratio", properties.humidityRatio.value) displayProperty("Degree of Saturation", properties.degreeOfSaturation.value) } } private func displayProperty(_ label: String, _ value: Double, _ symbol: String? = nil) -> some HTML { let symbol = "\(symbol == nil ? "" : " \(symbol!)")" return p(.class("text-blue-500 dark:text-slate-200")) { span(.class("font-semibold")) { "\(label): " } span(.class("font-light")) { "\(double: value)\(symbol)" } } } private func displayProperty( _ label: String, _ keyPath: KeyPath ) -> some HTML where N.Number == Double, N.Units: RawRepresentable, N.Units.RawValue == String { let property = properties[keyPath: keyPath] return displayProperty(label, property.rawValue, property.units.rawValue) } }