Feat: Adds flagged equipment measurement view and style

This commit is contained in:
2024-06-05 11:00:23 -04:00
parent c13b3740f2
commit ae7b413409
6 changed files with 240 additions and 71 deletions

View File

@@ -97,24 +97,50 @@ public enum EquipmentMeasurement: Equatable {
}
}
extension EquipmentMeasurement.AirHandler {
//extension EquipmentMeasurement.AirHandler {
//
// public enum Key: String, Equatable, CaseIterable {
// case returnPlenumPressure
// case postFilterPressure
// case postCoilPressure
// case supplyPlenumPressure
// case airflow
// }
//}
//
//extension EquipmentMeasurement.FurnaceAndCoil {
//
// public enum Key: String, Equatable, CaseIterable {
// case returnPlenumPressure
// case postFilterPressure
// case preCoilPressure
// case supplyPlenumPressure
// case airflow
// }
//}
public enum Key: String, Equatable, CaseIterable {
case returnPlenumPressure
case postFilterPressure
case postCoilPressure
case supplyPlenumPressure
case airflow
}
}
#if DEBUG
extension EquipmentMeasurement {
public static func mock(type equipmentType: EquipmentType) -> Self {
switch equipmentType {
case .airHandler:
return .airHandler(.init(
airflow: 1200,
returnPlenumPressure: 0.3,
postFilterPressure: 0.6,
postCoilPressure: 0.9,
supplyPlenumPressure: 0.2
))
extension EquipmentMeasurement.FurnaceAndCoil {
public enum Key: String, Equatable, CaseIterable {
case returnPlenumPressure
case postFilterPressure
case preCoilPressure
case supplyPlenumPressure
case airflow
case .furnaceAndCoil:
return .furnaceAndCoil(.init(
airflow: 1200,
returnPlenumPressure: 0.3,
postFilterPressure: 0.6,
preCoilPressure: 0.4,
supplyPlenumPressure: 0.1
))
}
}
}
#endif

View File

@@ -120,54 +120,19 @@ public struct FlaggedEquipmentMeasurement: Equatable {
}
}
// MARK: - Key
extension FlaggedEquipmentMeasurement {
// NOTE: These need to be kept in display order.
public enum Key: Equatable, CaseIterable {
case returnPlenum
case filterDrop
case coilDrop
case supplyPlenum
case staticPressure
case airflow
public var title: String {
switch self {
case .returnPlenum:
return "Return Plenum"
case .filterDrop:
return "Filter Pressure Drop"
case .coilDrop:
return "Coil Pressure Drop"
case .supplyPlenum:
return "Supply Plenum"
case .staticPressure:
return "External Static Pressure"
case .airflow:
return "System Airflow"
}
}
public var flaggedKeyPath: KeyPath<FlaggedEquipmentMeasurement, Flagged> {
switch self {
case .returnPlenum:
return \.returnPlenumPressure
case .filterDrop:
return \.filterPressureDrop
case .coilDrop:
return \.coilPressureDrop
case .supplyPlenum:
return \.supplyPlenumPressure
case .staticPressure:
return \.externalStaticPressure
case .airflow:
return \.airflow
}
}
}
}
// MARK: - Helpers
#if DEBUG
extension FlaggedEquipmentMeasurement {
public static func mock(type equipmentType: EquipmentType) -> Self {
.init(
budgets: .init(equipmentType: equipmentType, fanType: .variableSpeed),
measurement: .mock(type: equipmentType),
ratedPressures: .init(),
tons: .default
)
}
}
#endif
fileprivate extension Flagged {
init(

View File

@@ -0,0 +1,26 @@
import SharedModels
import SwiftUI
public struct FlaggedEquipmentMeasurementView: View {
@Environment(\.flaggedEquipmentMeasurementStyle) private var style
let measurement: FlaggedEquipmentMeasurement
public init(_ measurement: FlaggedEquipmentMeasurement) {
self.measurement = measurement
}
public var body: some View {
style.makeBody(
configuration: FlaggedEquipmentMeasurementStyleConfiguration(
measurement: measurement
)
)
}
}
#Preview {
FlaggedEquipmentMeasurementView(.mock(type: .airHandler))
.padding()
}

View File

@@ -18,7 +18,7 @@ public struct FlaggedView<Label: View>: View {
public var body: some View {
flaggedViewStyle.makeBody(
configuration: .init(flagged: flagged, label: .init(label()))
configuration: FlaggedViewStyleConfiguration(flagged: flagged, label: .init(label()))
)
}
}
@@ -27,6 +27,10 @@ extension FlaggedView where Label == Text {
public init(_ title: LocalizedStringKey, flagged: Flagged) {
self.init(flagged: flagged) { Text(title) }
}
public init<S: StringProtocol>(_ title: S, flagged: Flagged) {
self.init(flagged: flagged) { Text(title) }
}
}
extension FlaggedView where Label == EmptyView {

View File

@@ -0,0 +1,123 @@
import SharedModels
import SwiftUI
public protocol FlaggedEquipmentMeasurementStyle {
associatedtype Body: View
typealias Configuration = FlaggedEquipmentMeasurementStyleConfiguration
@ViewBuilder
func makeBody(configuration: Self.Configuration) -> Self.Body
}
public struct FlaggedEquipmentMeasurementStyleConfiguration {
public let measurement: FlaggedEquipmentMeasurement
}
public struct AnyFlaggedEquipmentMeasurementStyle: FlaggedEquipmentMeasurementStyle {
private var _makeBody: (Configuration) -> AnyView
internal init(makeBody: @escaping (Configuration) -> AnyView) {
self._makeBody = makeBody
}
public init<S: FlaggedEquipmentMeasurementStyle>(_ style: S) {
self.init { configuration in
AnyView(style.makeBody(configuration: configuration))
}
}
public func makeBody(configuration: Configuration) -> some View {
_makeBody(configuration)
}
}
public struct GridFlaggedEquipmentMeasurementStyle: FlaggedEquipmentMeasurementStyle {
public func makeBody(configuration: Configuration) -> some View {
Grid(alignment: .leading, verticalSpacing: 20) {
ForEach(FlaggedEquipmentMeasurement.Key.allCases) { field in
FlaggedView(
field.title,
flagged: configuration.measurement[keyPath: field.flaggedKeyPath]
)
.flaggedViewStyle(.gridRow(fractionLength: field == .airflow ? 0 : 2))
}
}
}
}
extension FlaggedEquipmentMeasurementStyle where Self == GridFlaggedEquipmentMeasurementStyle {
public static var grid: Self { .init() }
}
private struct FlaggedEquipmentMeasurementStyleKey: EnvironmentKey {
static let defaultValue = AnyFlaggedEquipmentMeasurementStyle(GridFlaggedEquipmentMeasurementStyle())
}
extension EnvironmentValues {
public var flaggedEquipmentMeasurementStyle: AnyFlaggedEquipmentMeasurementStyle {
get { self[FlaggedEquipmentMeasurementStyleKey.self] }
set { self[FlaggedEquipmentMeasurementStyleKey.self] = newValue }
}
}
extension View {
public func flaggedEquipmentMeasurementStyle(_ style: AnyFlaggedEquipmentMeasurementStyle) -> some View {
environment(\.flaggedEquipmentMeasurementStyle, style)
}
public func flaggedEquipmentMeasurementStyle<S: FlaggedEquipmentMeasurementStyle>(
_ style: S
) -> some View {
flaggedEquipmentMeasurementStyle(AnyFlaggedEquipmentMeasurementStyle(style))
}
}
// MARK: - Key
fileprivate extension FlaggedEquipmentMeasurement {
// NOTE: These need to be kept in display order.
enum Key: Hashable, CaseIterable, Identifiable {
case returnPlenum
case filterDrop
case coilDrop
case supplyPlenum
case staticPressure
case airflow
var id: Self { self }
var title: String {
switch self {
case .returnPlenum:
return "Return Plenum"
case .filterDrop:
return "Filter Pressure Drop"
case .coilDrop:
return "Coil Pressure Drop"
case .supplyPlenum:
return "Supply Plenum"
case .staticPressure:
return "External Static Pressure"
case .airflow:
return "System Airflow"
}
}
var flaggedKeyPath: KeyPath<FlaggedEquipmentMeasurement, Flagged> {
switch self {
case .returnPlenum:
return \.returnPlenumPressure
case .filterDrop:
return \.filterPressureDrop
case .coilDrop:
return \.coilPressureDrop
case .supplyPlenum:
return \.supplyPlenumPressure
case .staticPressure:
return \.externalStaticPressure
case .airflow:
return \.airflow
}
}
}
}

View File

@@ -71,7 +71,6 @@ public struct DefaultFlagViewStyle: FlaggedViewStyle {
flaggedMessageView(flagged: configuration.flagged)
}
}
}
public struct FlagAndMessageOnlyStyle: FlaggedViewStyle {
@@ -106,6 +105,33 @@ extension FlaggedViewStyle where Self == FlagAndMessageOnlyStyle {
}
}
public struct FlaggedGridRowStyle: FlaggedViewStyle {
let fractionLength: Int
public func makeBody(configuration: Configuration) -> some View {
GridRow {
VStack(alignment: .leading, spacing: 8) {
configuration.label
.foregroundStyle(Color.secondary)
flaggedMessageView(flagged: configuration.flagged)
}
Text(
configuration.flagged.wrappedValue,
format: .number.precision(.fractionLength(fractionLength))
)
configuration.flagged.flagImage
.gridCellAnchor(.trailing)
}
}
}
extension FlaggedViewStyle where Self == FlaggedGridRowStyle {
public static func gridRow(fractionLength: Int = 2) -> Self {
.init(fractionLength: fractionLength)
}
}
private struct FlaggedViewStyleKey: EnvironmentKey {
static let defaultValue = AnyFlaggedViewStyle(style: DefaultFlagViewStyle())
}
@@ -123,10 +149,9 @@ extension FlaggedViewStyle where Self == DefaultFlagViewStyle {
}
}
fileprivate extension Flagged.CheckResult.Key {
extension Flagged.CheckResult.Key {
public var flagColor: Color {
var flagColor: Color {
switch self {
case .good:
return .green
@@ -140,7 +165,7 @@ extension Flagged.CheckResult.Key {
extension Flagged {
public var flagColor: Color { projectedValue.key.flagColor }
var flagColor: Color { projectedValue.key.flagColor }
public var flagImage: some View {
Image(systemName: "flag.fill")
@@ -151,7 +176,7 @@ extension Flagged {
}
@ViewBuilder
private func flaggedMessageView(flagged: Flagged) -> some View {
fileprivate func flaggedMessageView(flagged: Flagged) -> some View {
if let message = flagged.message {
HStack {
Text(flagged.projectedValue.key.title)
@@ -171,6 +196,6 @@ extension View {
public func flaggedViewStyle<S: FlaggedViewStyle>(
_ style: S
) -> some View {
environment(\.flaggedViewStyle, AnyFlaggedViewStyle(style: style))
flaggedViewStyle(AnyFlaggedViewStyle(style: style))
}
}