205 lines
5.1 KiB
Swift
205 lines
5.1 KiB
Swift
import ComposableArchitecture
|
|
import SharedModels
|
|
import Styleguide
|
|
import SwiftUI
|
|
|
|
@Reducer
|
|
public struct EstimationForm {
|
|
public init() { }
|
|
|
|
@ObservableState
|
|
public struct State: Equatable, Sendable {
|
|
@SharedReader public var existingMeasurement: EquipmentMeasurement?
|
|
|
|
public let id: SharedPressureEstimationState.FlaggedEstimationContainer.ID?
|
|
public var airflowSelection: AirflowSelection
|
|
public var cfmTextField: Int?
|
|
public var coolingCapacity: EquipmentMetadata.CoolingCapacity
|
|
public var filterPressureDrop: Double?
|
|
public var name: String
|
|
|
|
public init(
|
|
id: SharedPressureEstimationState.FlaggedEstimationContainer.ID? = nil,
|
|
existingMeasurement: SharedReader<EquipmentMeasurement?>,
|
|
airflowSelection: AirflowSelection = .cfmPerTon,
|
|
cfmTextField: Int? = nil,
|
|
coolingCapacity: EquipmentMetadata.CoolingCapacity = .default,
|
|
filterPressureDrop: Double? = nil,
|
|
name: String = ""
|
|
) {
|
|
self.id = id
|
|
self._existingMeasurement = existingMeasurement
|
|
self.airflowSelection = airflowSelection
|
|
self.cfmTextField = cfmTextField
|
|
self.filterPressureDrop = filterPressureDrop
|
|
self.coolingCapacity = coolingCapacity
|
|
self.name = name
|
|
}
|
|
|
|
public var airflow: Double {
|
|
let cfmTextField = Double(self.cfmTextField ?? 0)
|
|
switch airflowSelection {
|
|
case .cfmPerTon:
|
|
return Double(cfmTextField) * coolingCapacity.rawValue
|
|
case .cfm:
|
|
return Double(cfmTextField)
|
|
}
|
|
}
|
|
|
|
public var isValid: Bool {
|
|
!name.isEmpty
|
|
&& cfmTextField != nil
|
|
}
|
|
|
|
// Note: Keep in display order of the picker.
|
|
public enum AirflowSelection: Hashable, CaseIterable, Identifiable, CustomStringConvertible {
|
|
case cfmPerTon
|
|
case cfm
|
|
|
|
init(
|
|
_ container: SharedPressureEstimationState.FlaggedEstimationContainer.EstimationState.CFMContainer
|
|
) {
|
|
switch container {
|
|
case .cfm:
|
|
self = .cfm
|
|
case .cfmPerTon:
|
|
self = .cfmPerTon
|
|
}
|
|
}
|
|
|
|
public var id: Self { self }
|
|
|
|
public var description: String {
|
|
switch self {
|
|
case .cfm:
|
|
return "CFM"
|
|
case .cfmPerTon:
|
|
return "CFM / Ton"
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
public enum Action: BindableAction {
|
|
case binding(BindingAction<State>)
|
|
}
|
|
|
|
public var body: some Reducer<State, Action> {
|
|
BindingReducer()
|
|
Reduce<State, Action> { state, action in
|
|
switch action {
|
|
|
|
case .binding:
|
|
return .none
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public struct EstimationFormView: View {
|
|
@Bindable public var store: StoreOf<EstimationForm>
|
|
|
|
public var body: some View {
|
|
Form {
|
|
Section("Estimation Name") {
|
|
HStack {
|
|
TextLabel("Name")
|
|
.padding(.trailing, 40)
|
|
TextField(
|
|
"Name",
|
|
text: $store.name,
|
|
prompt: Text("Required")
|
|
)
|
|
}
|
|
}
|
|
Section("Airflow") {
|
|
VStack {
|
|
CaseIterablePicker(
|
|
"Airflow Type",
|
|
selection: $store.airflowSelection
|
|
)
|
|
.pickerStyle(.segmented)
|
|
|
|
Grid(alignment: .leading, horizontalSpacing: 40) {
|
|
if store.airflowSelection == .cfmPerTon {
|
|
GridRow {
|
|
HStack {
|
|
TextLabel("Capacity")
|
|
Spacer()
|
|
CoolingCapacityPicker(
|
|
selection: $store.coolingCapacity
|
|
)
|
|
}
|
|
.gridCellColumns(2)
|
|
}
|
|
}
|
|
GridRow {
|
|
HStack {
|
|
TextLabel(store.airflowSelection.description)
|
|
Spacer()
|
|
TextField(
|
|
"CFM / Ton",
|
|
value: $store.cfmTextField,
|
|
format: .number,
|
|
prompt: Text("CFM")
|
|
)
|
|
.frame(width: 100)
|
|
.numberPad()
|
|
}
|
|
.gridCellColumns(2)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if let existingsMeasurement = store.existingMeasurement,
|
|
existingsMeasurement.hasFilterDrop
|
|
{
|
|
Section("Filter Pressure Drop") {
|
|
HStack {
|
|
TextLabel("Pressure Drop")
|
|
Spacer()
|
|
TextField(
|
|
"Filter Drop",
|
|
value: $store.filterPressureDrop,
|
|
fractionLength: 2,
|
|
prompt: Text("Optional")
|
|
)
|
|
.frame(width: 100)
|
|
.decimalPad()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.applyFormStyle()
|
|
}
|
|
}
|
|
|
|
fileprivate extension EquipmentMeasurement {
|
|
|
|
var hasFilterDrop: Bool {
|
|
switch self {
|
|
case let .airHandler(airHandler):
|
|
return airHandler.postFilterPressure > 0
|
|
case let .furnaceAndCoil(furnace):
|
|
return furnace.postFilterPressure > 0
|
|
}
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
EstimationFormView(
|
|
store: Store(
|
|
initialState: EstimationForm.State(
|
|
existingMeasurement: SharedReader(
|
|
Shared(EquipmentMeasurement.mock(type: .airHandler))
|
|
)
|
|
)
|
|
) {
|
|
EstimationForm()
|
|
}
|
|
)
|
|
}
|