Feat: Working on pressure estimations feature
This commit is contained in:
236
Sources/PressureEstimationsFeature/EstimateSettingsForm.swift
Normal file
236
Sources/PressureEstimationsFeature/EstimateSettingsForm.swift
Normal file
@@ -0,0 +1,236 @@
|
||||
import ComposableArchitecture
|
||||
import SharedModels
|
||||
import Styleguide
|
||||
import SwiftUI
|
||||
|
||||
@Reducer
|
||||
public struct EstimateSettingsForm {
|
||||
|
||||
@ObservableState
|
||||
public struct State: Equatable {
|
||||
|
||||
public var budgets: BudgetedPercentEnvelope
|
||||
public let equipmentType: EquipmentType
|
||||
public var fanType: FanType
|
||||
public var focusedField: Field? = nil
|
||||
public var updatedAirflow: Double?
|
||||
|
||||
public init(
|
||||
equipmentType: EquipmentType,
|
||||
fanType: FanType = .constantSpeed,
|
||||
updatedAirflow: Double? = nil
|
||||
) {
|
||||
self.budgets = .init(equipmentType: equipmentType, fanType: fanType)
|
||||
self.equipmentType = equipmentType
|
||||
self.fanType = fanType
|
||||
self.updatedAirflow = updatedAirflow
|
||||
}
|
||||
|
||||
public var isValid: Bool {
|
||||
updatedAirflow != nil
|
||||
&& Int(budgets.total.rawValue) == 100
|
||||
}
|
||||
|
||||
public enum Field: Hashable, CaseIterable, FocusableField {
|
||||
case coilBudget
|
||||
case filterBudget
|
||||
case returnPlenumBudget
|
||||
case supplyPlenumBudget
|
||||
case updatedAirflow
|
||||
}
|
||||
}
|
||||
|
||||
public enum Action: BindableAction, ViewAction {
|
||||
case binding(BindingAction<State>)
|
||||
case view(View)
|
||||
|
||||
@CasePathable
|
||||
public enum View {
|
||||
case infoButtonTapped(InfoButton)
|
||||
case resetButtonTapped
|
||||
case submitField
|
||||
|
||||
@CasePathable
|
||||
public enum InfoButton {
|
||||
case budgets
|
||||
case updatedAirflow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var body: some Reducer<State, Action> {
|
||||
BindingReducer()
|
||||
Reduce<State, Action> { state, action in
|
||||
switch action {
|
||||
case .binding(\.fanType):
|
||||
state.budgets = .init(
|
||||
equipmentType: state.equipmentType,
|
||||
fanType: state.fanType
|
||||
)
|
||||
return .none
|
||||
|
||||
case .binding:
|
||||
return .none
|
||||
|
||||
case let .view(action):
|
||||
switch action {
|
||||
|
||||
case .infoButtonTapped:
|
||||
#warning("Fix me.")
|
||||
return .none
|
||||
|
||||
case .resetButtonTapped:
|
||||
state.budgets = .init(equipmentType: state.equipmentType, fanType: state.fanType)
|
||||
state.updatedAirflow = nil
|
||||
return .none
|
||||
|
||||
case .submitField:
|
||||
state.focusedField = state.focusedField?.next
|
||||
return .none
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewAction(for: EstimateSettingsForm.self)
|
||||
public struct EstimateSettingsFormView: View {
|
||||
|
||||
@FocusState private var focusedField: EstimateSettingsForm.State.Field?
|
||||
@Bindable public var store: StoreOf<EstimateSettingsForm>
|
||||
|
||||
public init(store: StoreOf<EstimateSettingsForm>) {
|
||||
self.store = store
|
||||
self.focusedField = store.focusedField
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
Form {
|
||||
Section {
|
||||
EmptyView()
|
||||
} header: {
|
||||
TextLabel("Fan Type")
|
||||
#if os(macOS)
|
||||
.font(.title2)
|
||||
#endif
|
||||
} footer: {
|
||||
FanTypePicker(selection: $store.fanType)
|
||||
.pickerStyle(.segmented)
|
||||
}
|
||||
|
||||
Section {
|
||||
Grid(alignment: .leading, horizontalSpacing: 40) {
|
||||
if store.equipmentType == .furnaceAndCoil {
|
||||
GridRow {
|
||||
TextLabel("Coil")
|
||||
percentField("Coil Budget", value: $store.budgets.coilBudget.fraction)
|
||||
.focused($focusedField, equals: .coilBudget)
|
||||
.onSubmit { send(.submitField) }
|
||||
.numberPad()
|
||||
}
|
||||
}
|
||||
|
||||
GridRow {
|
||||
TextLabel("Filter")
|
||||
percentField("Filter Budget", value: $store.budgets.filterBudget.fraction)
|
||||
.focused($focusedField, equals: .filterBudget)
|
||||
.onSubmit { send(.submitField) }
|
||||
.numberPad()
|
||||
}
|
||||
|
||||
GridRow {
|
||||
TextLabel("Return")
|
||||
percentField("Return Plenum Budget", value: $store.budgets.returnPlenumBudget.fraction)
|
||||
.focused($focusedField, equals: .returnPlenumBudget)
|
||||
.onSubmit { send(.submitField) }
|
||||
.numberPad()
|
||||
}
|
||||
|
||||
GridRow {
|
||||
TextLabel("Supply")
|
||||
percentField("Supply Plenum Budget", value: $store.budgets.supplyPlenumBudget.fraction)
|
||||
.focused($focusedField, equals: .supplyPlenumBudget)
|
||||
.onSubmit { send(.submitField) }
|
||||
.numberPad()
|
||||
}
|
||||
}
|
||||
.textLabelStyle(.boldSecondary)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
|
||||
} header: {
|
||||
VStack {
|
||||
HStack {
|
||||
Text("Budgets")
|
||||
Spacer()
|
||||
InfoButton { send(.infoButtonTapped(.budgets)) }
|
||||
}
|
||||
#if os(macOS)
|
||||
.font(.title2)
|
||||
.padding(.top, 20)
|
||||
#endif
|
||||
FlaggedView(
|
||||
flagged: Flagged(wrappedValue: store.budgets.total.rawValue, .percent())
|
||||
)
|
||||
.flaggedViewStyle(BudgetFlagViewStyle())
|
||||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
|
||||
} header: {
|
||||
HStack {
|
||||
Text("Updated Airflow")
|
||||
Spacer()
|
||||
InfoButton { send(.infoButtonTapped(.updatedAirflow)) }
|
||||
}
|
||||
} footer: {
|
||||
HStack {
|
||||
ResetButton { send(.resetButtonTapped) }
|
||||
Spacer()
|
||||
Button("Next") {
|
||||
#warning("Fix me.")
|
||||
}
|
||||
}
|
||||
.padding(.top)
|
||||
}
|
||||
}
|
||||
.labelsHidden()
|
||||
.bind($focusedField, to: $store.focusedField)
|
||||
.navigationTitle("Estimate Settings")
|
||||
}
|
||||
|
||||
private func percentField(
|
||||
_ title: LocalizedStringKey,
|
||||
value: Binding<Double>
|
||||
) -> some View {
|
||||
TextField(title, value: value, format: .percent, prompt: Text(title))
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate struct BudgetFlagViewStyle: FlaggedViewStyle {
|
||||
|
||||
func makeBody(configuration: Configuration) -> some View {
|
||||
HStack {
|
||||
configuration.flagged.flagImage
|
||||
Spacer()
|
||||
configuration.flagged.messageView
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
NavigationStack {
|
||||
EstimateSettingsFormView(
|
||||
store: Store(
|
||||
initialState: EstimateSettingsForm.State(equipmentType: .furnaceAndCoil)
|
||||
) {
|
||||
EstimateSettingsForm()
|
||||
}
|
||||
)
|
||||
#if os(macOS)
|
||||
.frame(width: 400, height: 600)
|
||||
.padding()
|
||||
#endif
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user