import Elementary import ManualDClient import ManualDCore import Styleguide struct FrictionRateView: HTML, Sendable { @Environment(ProjectViewValue.$projectID) var projectID let componentLosses: [ComponentPressureLoss] let equivalentLengths: EffectiveLength.MaxContainer let frictionRate: FrictionRate? private var availableStaticPressure: Double? { frictionRate?.availableStaticPressure } private var shouldShowBadges: Bool { frictionRate != nil } private var badgeColor: String { let base = "badge-info" guard let frictionRate = frictionRate?.value else { return base } if frictionRate >= 0.18 || frictionRate <= 0.02 { return "badge-error" } return base } private var showHighErrors: Bool { guard let frictionRate = frictionRate?.value else { return false } return frictionRate >= 0.18 } private var showLowErrors: Bool { guard let frictionRate = frictionRate?.value else { return false } return frictionRate <= 0.02 } private var showNoComponentLossesError: Bool { componentLosses.count == 0 } private var showIncompleteSectionsError: Bool { availableStaticPressure == nil || frictionRate?.value == nil } private var hasAlerts: Bool { showLowErrors || showHighErrors || showNoComponentLossesError || showIncompleteSectionsError } var body: some HTML { div(.class("space-y-6")) { PageTitleRow { div(.class("grid grid-cols-2 px-4 w-full")) { PageTitle { "Friction Rate" } div(.class("space-y-2 justify-end font-bold text-lg")) { if shouldShowBadges { if let frictionRate = frictionRate?.value { LabeledContent { span { "Friction Rate Design Value" } } content: { Badge(number: frictionRate, digits: 2) .attributes(.class("\(badgeColor) badge-lg")) .bold() } .attributes(.class("justify-end mx-auto")) } if let availableStaticPressure { LabeledContent { span { "Available Static Pressure" } } content: { Badge(number: availableStaticPressure, digits: 2) } .attributes(.class("justify-end mx-auto")) } LabeledContent { span { "Component Pressure Losses" } } content: { Badge(number: componentLosses.total, digits: 2) } .attributes(.class("justify-end mx-auto")) } } div(.class("text-error font-bold italic col-span-2")) { Alert { p { "Must complete previous sections." } } .hidden(when: !showIncompleteSectionsError) Alert { p { "No component pressures losses" } } .hidden(when: !showNoComponentLossesError) Alert { p(.class("block")) { "Calculated friction rate is below 0.02. The fan may not deliver the required CFM." br() " * Increase the blower speed" br() " * Increase the blower size" br() " * Decrease the Total Effective Length (TEL)" } } .hidden(when: !showLowErrors) Alert { p(.class("block")) { "Calculated friction rate is above 0.18. The fan may deliver too many CFM." br() " * Decrease the blower speed" br() " * Decreae the blower size" br() " * Increase the Total Effective Length (TEL)" } } .hidden(when: !showHighErrors) } .attributes(.class("mt-4"), when: hasAlerts) } } ComponentPressureLossesView( componentPressureLosses: componentLosses, projectID: projectID ) } } }