Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public struct DefaultFeatureFlagService: FeatureFlagService {
case .pointOfSaleOrdersi2:
return buildConfig == .localDeveloper || buildConfig == .alpha
case .pointOfSaleBarcodeScanningi2:
return false
return buildConfig == .localDeveloper || buildConfig == .alpha
default:
return true
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import SwiftUI

@available(iOS 17.0, *)
struct PointOfSaleBarcodeScannerSetup: View {
@Binding var isPresented: Bool
@State private var flowManager: PointOfSaleBarcodeScannerSetupFlowManager

init(isPresented: Binding<Bool>) {
self._isPresented = isPresented
self.flowManager = PointOfSaleBarcodeScannerSetupFlowManager(isPresented: isPresented)
}

var body: some View {
VStack(spacing: POSSpacing.xxLarge) {
// Header
PointOfSaleModalHeader(isPresented: $isPresented,
title: .constant(AttributedString(currentTitle)))

VStack {
currentContent
Spacer()
}
.scrollVerticallyIfNeeded()

// Bottom buttons
PointOfSaleFlowButtonsView(configuration: flowManager.buttonConfiguration)
}
.padding(POSPadding.xxLarge)
.background(Color.posSurfaceBright)
.containerRelativeFrame([.horizontal, .vertical]) { length, _ in
max(length * 0.75, Constants.modalFrameMaxSmallDimension)
}
.onAppear {
ServiceLocator.analytics.track(.pointOfSaleBarcodeScannerSetupFlowShown)
}
}

// MARK: - Computed Properties
private var currentTitle: String {
switch flowManager.currentState {
case .scannerSelection:
return Localization.setupHeading
case .setupFlow:
return flowManager.getCurrentStep()?.title ?? Localization.setupHeading
}
}

@ViewBuilder
private var currentContent: some View {
switch flowManager.currentState {
case .scannerSelection:
PointOfSaleBarcodeScannerSetupSelectionView(options: scannerOptions) { scannerType in
flowManager.selectScanner(scannerType)
}
case .setupFlow:
if let step = flowManager.getCurrentStep() {
AnyView(step.content)
}
}
}

private var scannerOptions: [PointOfSaleBarcodeScannerSetupFlowOption] {
[
PointOfSaleBarcodeScannerSetupFlowOption(
title: Localization.socketS720Title,
subtitle: Localization.socketS720Subtitle,
scannerType: .socketS720
),
PointOfSaleBarcodeScannerSetupFlowOption(
title: Localization.starBSH20BTitle,
subtitle: Localization.starBSH20BSubtitle,
scannerType: .starBSH20B
),
PointOfSaleBarcodeScannerSetupFlowOption(
title: Localization.tbcScannerTitle,
subtitle: Localization.tbcScannerSubtitle,
scannerType: .tbcScanner
),
PointOfSaleBarcodeScannerSetupFlowOption(
title: Localization.otherTitle,
subtitle: Localization.otherSubtitle,
scannerType: .other
)
]
}
}

// MARK: - Constants
private enum Constants {
static var modalFrameMaxSmallDimension: CGFloat { 752 }
}

// MARK: - Private Localization Extension
@available(iOS 17.0, *)
private extension PointOfSaleBarcodeScannerSetup {
enum Localization {
static let setupHeading = NSLocalizedString(
"pos.barcodeScannerSetup.heading",
value: "Barcode Scanner Setup",
comment: "Heading for the barcode scanner setup flow in POS"
)

static let socketS720Title = NSLocalizedString(
"pos.barcodeScannerSetup.socketS720.title",
value: "Socket S720",
comment: "Title for Socket S720 scanner option in barcode scanner setup"
)
static let socketS720Subtitle = NSLocalizedString(
"pos.barcodeScannerSetup.socketS720.subtitle",
value: "Small handheld scanner with a charging dock or stand",
comment: "Subtitle for Socket S720 scanner option in barcode scanner setup"
)
static let starBSH20BTitle = NSLocalizedString(
"pos.barcodeScannerSetup.starBSH20B.title",
value: "Star BSH-20B",
comment: "Title for Star BSH-20B scanner option in barcode scanner setup"
)
static let starBSH20BSubtitle = NSLocalizedString(
"pos.barcodeScannerSetup.starBSH20B.subtitle",
value: "Ergonomic scanner with a stand",
comment: "Subtitle for Star BSH-20B scanner option in barcode scanner setup"
)
static let tbcScannerTitle = NSLocalizedString(
"pos.barcodeScannerSetup.tbcScanner.title",
value: "Scanner TBC",
comment: "Title for TBC scanner option in barcode scanner setup"
)
static let tbcScannerSubtitle = NSLocalizedString(
"pos.barcodeScannerSetup.tbcScanner.subtitle",
value: "Recommended scanner",
comment: "Subtitle for TBC scanner option in barcode scanner setup"
)
static let otherTitle = NSLocalizedString(
"pos.barcodeScannerSetup.other.title",
value: "Other",
comment: "Title for other scanner option in barcode scanner setup"
)
static let otherSubtitle = NSLocalizedString(
"pos.barcodeScannerSetup.other.subtitle",
value: "General scanner setup instructions",
comment: "Subtitle for other scanner option in barcode scanner setup"
)
}
}

@available(iOS 17.0, *)
#Preview {
PointOfSaleBarcodeScannerSetup(isPresented: .constant(true))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import SwiftUI

// MARK: - Point of Sale Barcode Scanner Setup Flow
@available(iOS 17.0, *)
class PointOfSaleBarcodeScannerSetupFlow {
private let scannerType: PointOfSaleBarcodeScannerType
private let onComplete: () -> Void
private let onBackToSelection: () -> Void
private var currentStepIndex: Int = 0

init(scannerType: PointOfSaleBarcodeScannerType,
onComplete: @escaping () -> Void,
onBackToSelection: @escaping () -> Void) {
self.scannerType = scannerType
self.onComplete = onComplete
self.onBackToSelection = onBackToSelection
}

var currentStep: PointOfSaleBarcodeScannerSetupStep? {
steps[safe: currentStepIndex]
}

var isComplete: Bool {
currentStepIndex >= steps.count - 1
}

var nextButtonTitle: String {
isComplete ? Localization.doneButtonTitle : Localization.nextButtonTitle
}

var isNextButtonEnabled: Bool {
true
}

var shouldShowBackButton: Bool {
true
}

func nextStep() {
if currentStepIndex < steps.count - 1 {
currentStepIndex += 1
} else {
onComplete()
}
}

func previousStep() {
if currentStepIndex > 0 {
currentStepIndex -= 1
} else {
onBackToSelection()
}
}

func restartFlow() {
currentStepIndex = 0
}

func getButtonConfiguration() -> PointOfSaleFlowButtonConfiguration {
guard let step = currentStep else {
return .noButtons()
}

// Use step customization if available
if let customization = step.customization {
return customization.customizeButtons(for: self)
}

// Default button configuration
return PointOfSaleFlowButtonConfiguration(
shouldShowBackButton: shouldShowBackButton,
shouldShowNextButton: true,
nextButtonTitle: nextButtonTitle,
isNextButtonEnabled: isNextButtonEnabled,
onBack: { [weak self] in
self?.previousStep()
},
onNext: { [weak self] in
self?.nextStep()
}
)
}

private var steps: [PointOfSaleBarcodeScannerSetupStep] {
switch scannerType {
case .socketS720:
return [
createWelcomeStep(title: "Socket S720 Setup")
// TODO: Add more steps for Socket S720 WOOMOB-698
]
case .starBSH20B:
return [
createWelcomeStep(title: "Star BSH-20B Setup")
// TODO: Add more steps for Star BSH-20B WOOMOB-696
]
case .tbcScanner:
return [
createWelcomeStep(title: "TBC Scanner Setup")
// TODO: Add more steps for TBC Scanner WOOMOB-699
]
case .other:
return [
PointOfSaleBarcodeScannerSetupStep(
title: "General Scanner Setup",
content: { BarcodeScannerInformationContent() }
)
]
}
}

private func createWelcomeStep(title: String) -> PointOfSaleBarcodeScannerSetupStep {
PointOfSaleBarcodeScannerSetupStep(
title: title,
content: { PointOfSaleBarcodeScannerWelcomeView(title: title) },
customization: PointOfSaleBarcodeScannerWelcomeStepCustomization()
)
}
}

// MARK: - Example Step Customizations
@available(iOS 17.0, *)
struct PointOfSaleBarcodeScannerWelcomeStepCustomization: PointOfSaleBarcodeScannerStepCustomization {
func customizeButtons(for flow: PointOfSaleBarcodeScannerSetupFlow) -> PointOfSaleFlowButtonConfiguration {
return .doneOnly {
flow.nextStep()
}
}
}

// MARK: - Private Localization Extension
@available(iOS 17.0, *)
private extension PointOfSaleBarcodeScannerSetupFlow {
enum Localization {
static let doneButtonTitle = NSLocalizedString(
"pos.barcodeScannerSetup.done.button.title",
value: "Done",
comment: "Title for the done button in barcode scanner setup navigation"
)
static let nextButtonTitle = NSLocalizedString(
"pos.barcodeScannerSetup.next.button.title",
value: "Next",
comment: "Title for the next button in barcode scanner setup navigation"
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import SwiftUI

// MARK: - Point of Sale Barcode Scanner Setup Flow Manager
@available(iOS 17.0, *)
@Observable
class PointOfSaleBarcodeScannerSetupFlowManager {
var currentState: PointOfSaleBarcodeScannerSetupFlowState = .scannerSelection
@ObservationIgnored @Binding var isPresented: Bool
private var currentFlow: PointOfSaleBarcodeScannerSetupFlow?

init(isPresented: Binding<Bool>) {
self._isPresented = isPresented
}

func selectScanner(_ scannerType: PointOfSaleBarcodeScannerType) {
currentFlow = PointOfSaleBarcodeScannerSetupFlow(scannerType: scannerType, onComplete: { [weak self] in
self?.isPresented = false
}, onBackToSelection: { [weak self] in
self?.goBackToSelection()
})
currentState = .setupFlow(scannerType)
}

func goBackToSelection() {
currentState = .scannerSelection
currentFlow = nil
}

func nextStep() {
currentFlow?.nextStep()
}

func previousStep() {
currentFlow?.previousStep()
}

func getCurrentStep() -> PointOfSaleBarcodeScannerSetupStep? {
currentFlow?.currentStep
}

func isComplete() -> Bool {
currentFlow?.isComplete ?? false
}

var buttonConfiguration: PointOfSaleFlowButtonConfiguration {
switch currentState {
case .scannerSelection:
return .noButtons()
case .setupFlow:
guard let flow = currentFlow else {
return .noButtons()
}

return flow.getButtonConfiguration()
}
}
}
Loading