Feat: Working on pressure estimations feature
This commit is contained in:
@@ -59,7 +59,8 @@ let package = Package(
|
||||
.target(
|
||||
name: "Styleguide",
|
||||
dependencies: [
|
||||
"SharedModels"
|
||||
"SharedModels",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
]
|
||||
),
|
||||
.testTarget(
|
||||
|
||||
@@ -6,22 +6,39 @@ import SwiftUI
|
||||
@Reducer
|
||||
public struct EstimateSettingsForm {
|
||||
|
||||
@CasePathable
|
||||
public enum InfoView {
|
||||
case budgets
|
||||
case updatedAirflow
|
||||
}
|
||||
|
||||
@Reducer(state: .equatable)
|
||||
public enum Destination {
|
||||
case infoView(InfoViewFeature)
|
||||
}
|
||||
|
||||
@ObservableState
|
||||
public struct State: Equatable {
|
||||
|
||||
@Presents public var destination: Destination.State?
|
||||
public var budgets: BudgetedPercentEnvelope
|
||||
public let equipmentType: EquipmentType
|
||||
public let equipmentMeasurement: EquipmentMeasurement
|
||||
public var fanType: FanType
|
||||
public var focusedField: Field? = nil
|
||||
public var updatedAirflow: Double?
|
||||
|
||||
public init(
|
||||
equipmentType: EquipmentType,
|
||||
destination: Destination.State? = nil,
|
||||
equipmentMeasurement: EquipmentMeasurement,
|
||||
fanType: FanType = .constantSpeed,
|
||||
updatedAirflow: Double? = nil
|
||||
) {
|
||||
self.budgets = .init(equipmentType: equipmentType, fanType: fanType)
|
||||
self.equipmentType = equipmentType
|
||||
self.destination = destination
|
||||
self.equipmentMeasurement = equipmentMeasurement
|
||||
self.budgets = .init(
|
||||
equipmentType: equipmentMeasurement.equipmentType,
|
||||
fanType: fanType
|
||||
)
|
||||
self.fanType = fanType
|
||||
self.updatedAirflow = updatedAirflow
|
||||
}
|
||||
@@ -42,19 +59,15 @@ public struct EstimateSettingsForm {
|
||||
|
||||
public enum Action: BindableAction, ViewAction {
|
||||
case binding(BindingAction<State>)
|
||||
case destination(PresentationAction<Destination.Action>)
|
||||
case view(View)
|
||||
|
||||
@CasePathable
|
||||
public enum View {
|
||||
case infoButtonTapped(InfoButton)
|
||||
case infoButtonTapped(InfoView)
|
||||
case nextButtonTapped
|
||||
case resetButtonTapped
|
||||
case submitField
|
||||
|
||||
@CasePathable
|
||||
public enum InfoButton {
|
||||
case budgets
|
||||
case updatedAirflow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,9 +75,10 @@ public struct EstimateSettingsForm {
|
||||
BindingReducer()
|
||||
Reduce<State, Action> { state, action in
|
||||
switch action {
|
||||
|
||||
case .binding(\.fanType):
|
||||
state.budgets = .init(
|
||||
equipmentType: state.equipmentType,
|
||||
equipmentType: state.equipmentMeasurement.equipmentType,
|
||||
fanType: state.fanType
|
||||
)
|
||||
return .none
|
||||
@@ -72,15 +86,29 @@ public struct EstimateSettingsForm {
|
||||
case .binding:
|
||||
return .none
|
||||
|
||||
case .destination(.dismiss):
|
||||
state.destination = nil
|
||||
return .none
|
||||
|
||||
case .destination:
|
||||
return .none
|
||||
|
||||
case let .view(action):
|
||||
switch action {
|
||||
|
||||
case .infoButtonTapped:
|
||||
case let .infoButtonTapped(infoView):
|
||||
state.destination = .infoView(.init(view: infoView))
|
||||
return .none
|
||||
|
||||
case .nextButtonTapped:
|
||||
#warning("Fix me.")
|
||||
return .none
|
||||
|
||||
case .resetButtonTapped:
|
||||
state.budgets = .init(equipmentType: state.equipmentType, fanType: state.fanType)
|
||||
state.budgets = .init(
|
||||
equipmentType: state.equipmentMeasurement.equipmentType,
|
||||
fanType: state.fanType
|
||||
)
|
||||
state.updatedAirflow = nil
|
||||
return .none
|
||||
|
||||
@@ -91,6 +119,7 @@ public struct EstimateSettingsForm {
|
||||
}
|
||||
}
|
||||
}
|
||||
.ifLet(\.$destination, action: \.destination)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +150,7 @@ public struct EstimateSettingsFormView: View {
|
||||
|
||||
Section {
|
||||
Grid(alignment: .leading, horizontalSpacing: 40) {
|
||||
if store.equipmentType == .furnaceAndCoil {
|
||||
if store.equipmentMeasurement.equipmentType == .furnaceAndCoil {
|
||||
GridRow {
|
||||
TextLabel("Coil")
|
||||
percentField("Coil Budget", value: $store.budgets.coilBudget.fraction)
|
||||
@@ -177,7 +206,11 @@ public struct EstimateSettingsFormView: View {
|
||||
}
|
||||
|
||||
Section {
|
||||
|
||||
TextField(
|
||||
"Airflow",
|
||||
value: $store.updatedAirflow,
|
||||
fractionLength: 0
|
||||
)
|
||||
} header: {
|
||||
HStack {
|
||||
Text("Updated Airflow")
|
||||
@@ -189,8 +222,9 @@ public struct EstimateSettingsFormView: View {
|
||||
ResetButton { send(.resetButtonTapped) }
|
||||
Spacer()
|
||||
Button("Next") {
|
||||
#warning("Fix me.")
|
||||
send(.nextButtonTapped)
|
||||
}
|
||||
.disabled(!store.isValid)
|
||||
}
|
||||
.padding(.top)
|
||||
}
|
||||
@@ -198,6 +232,16 @@ public struct EstimateSettingsFormView: View {
|
||||
.labelsHidden()
|
||||
.bind($focusedField, to: $store.focusedField)
|
||||
.navigationTitle("Estimate Settings")
|
||||
.sheet(
|
||||
item: $store.scope(
|
||||
state: \.destination?.infoView,
|
||||
action: \.destination.infoView
|
||||
)
|
||||
) { store in
|
||||
NavigationStack {
|
||||
InfoView(store: store)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func percentField(
|
||||
@@ -219,11 +263,36 @@ fileprivate struct BudgetFlagViewStyle: FlaggedViewStyle {
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate extension InfoViewFeature.State {
|
||||
init(view: EstimateSettingsForm.InfoView) {
|
||||
switch view {
|
||||
case .budgets:
|
||||
self.init(
|
||||
title: "Budgets",
|
||||
body: """
|
||||
Budgeted percentages for static pressure estimations, these generally are set to
|
||||
reasonable defaults, however you can change them if desired.
|
||||
|
||||
Note: These must total up to 100%.
|
||||
"""
|
||||
)
|
||||
case .updatedAirflow:
|
||||
self.init(
|
||||
title: "Updated Airflow",
|
||||
body: """
|
||||
This is used to generated estimated static pressures at the updated airflow
|
||||
compared to the existing airflow of the system.
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
NavigationStack {
|
||||
EstimateSettingsFormView(
|
||||
store: Store(
|
||||
initialState: EstimateSettingsForm.State(equipmentType: .furnaceAndCoil)
|
||||
initialState: EstimateSettingsForm.State(equipmentMeasurement: .furnaceAndCoil(.init()))
|
||||
) {
|
||||
EstimateSettingsForm()
|
||||
}
|
||||
|
||||
@@ -14,7 +14,11 @@ public struct InfoButton: View {
|
||||
Button(action: action) {
|
||||
Label("Info", systemImage: "info.circle")
|
||||
}
|
||||
.buttonStyle(infoButtonStyle)
|
||||
.buttonStyle(.plain)
|
||||
.labelStyle(.iconOnly)
|
||||
.font(.title2)
|
||||
.foregroundStyle(Color.accentColor)
|
||||
// .buttonStyle(infoButtonStyle)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,48 +1,74 @@
|
||||
import ComposableArchitecture
|
||||
import SwiftUI
|
||||
|
||||
public struct InfoView: View {
|
||||
let headingText: String
|
||||
@Reducer
|
||||
public struct InfoViewFeature {
|
||||
|
||||
public init() { }
|
||||
|
||||
@ObservableState
|
||||
public struct State: Equatable {
|
||||
let titleText: String
|
||||
let bodyText: String
|
||||
|
||||
|
||||
public init(heading headingText: String, body bodyText: String) {
|
||||
self.headingText = headingText
|
||||
public init(
|
||||
title titleText: String,
|
||||
body bodyText: String
|
||||
) {
|
||||
self.titleText = titleText
|
||||
self.bodyText = bodyText
|
||||
}
|
||||
}
|
||||
|
||||
public enum Action {
|
||||
case dismissButtonTapped
|
||||
}
|
||||
|
||||
@Dependency(\.dismiss) var dismiss
|
||||
|
||||
public var body: some ReducerOf<Self> {
|
||||
Reduce { _, action in
|
||||
switch action {
|
||||
case .dismissButtonTapped:
|
||||
return .run { _ in await self.dismiss() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct InfoView: View {
|
||||
let store: StoreOf<InfoViewFeature>
|
||||
|
||||
public init(store: StoreOf<InfoViewFeature>) {
|
||||
self.store = store
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
VStack(spacing: 40) {
|
||||
|
||||
ZStack {
|
||||
Text(headingText)
|
||||
.font(.headline)
|
||||
.bold()
|
||||
.foregroundStyle(Color.white)
|
||||
.padding()
|
||||
}
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.foregroundStyle(Color.orange.opacity(0.6))
|
||||
}
|
||||
|
||||
|
||||
ZStack {
|
||||
Text(bodyText)
|
||||
Text(store.bodyText)
|
||||
.font(.callout)
|
||||
.padding()
|
||||
}
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.foregroundStyle(Color.secondary.opacity(0.3))
|
||||
}
|
||||
.padding(.horizontal, 10)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.navigationTitle(store.titleText)
|
||||
.padding(.horizontal)
|
||||
.navigationBarBackButtonHidden()
|
||||
.toolbar {
|
||||
Button("Done") {
|
||||
store.send(.dismissButtonTapped)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
InfoView(heading: "Generic Info View", body: "Explain what this view does here...")
|
||||
InfoView(
|
||||
store: Store(
|
||||
initialState: InfoViewFeature.State(
|
||||
title: "Generic Info View",
|
||||
body: "Explain what this view does here..."
|
||||
),
|
||||
reducer: InfoViewFeature.init
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ public struct DefaultInfoButtonStyle: PrimitiveButtonStyle {
|
||||
public func makeBody(configuration: Configuration) -> some View {
|
||||
configuration.label
|
||||
.buttonStyle(.plain)
|
||||
.font(.title2)
|
||||
.foregroundStyle(Color.accentColor)
|
||||
.labelStyle(.iconOnly)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user