diff --git a/Modules/Sources/PointOfSale/Presentation/Orders/POSOrderDetailsView.swift b/Modules/Sources/PointOfSale/Presentation/Orders/POSOrderDetailsView.swift index 03edd60f98b..e5dda75b9c5 100644 --- a/Modules/Sources/PointOfSale/Presentation/Orders/POSOrderDetailsView.swift +++ b/Modules/Sources/PointOfSale/Presentation/Orders/POSOrderDetailsView.swift @@ -14,6 +14,7 @@ struct POSOrderDetailsView: View { @Environment(\.siteTimezone) private var siteTimezone @Environment(POSOrderListModel.self) private var orderListModel @Environment(\.posAnalytics) private var analytics + @Environment(\.posFeatureFlags) private var featureFlags @State private var isShowingEmailReceiptView: Bool = false private var shouldShowBackButton: Bool { @@ -32,9 +33,7 @@ struct POSOrderDetailsView: View { title: POSOrderListView.Localization.orderTitle(order.number), backButtonConfiguration: shouldShowBackButton ? .init(state: .enabled, action: onBack) : nil, trailingContent: { - if actions.isNotEmpty { - actionsSection(actions) - } + actionsSection(actions: availableActions) }, bottomContent: { headerBottomContent(for: order) @@ -377,59 +376,98 @@ private extension POSOrderDetailsView { // MARK: - Actions private extension POSOrderDetailsView { - enum POSOrderDetailsAction: Identifiable, CaseIterable { + enum POSAction: Identifiable, CaseIterable { + case issueRefund case emailReceipt var id: String { title } var title: String { switch self { - case .emailReceipt: - Localization.emailReceiptActionTitle + case .issueRefund: Localization.issueRefundActionTitle + case .emailReceipt: Localization.emailReceiptActionTitle + } + } + + var accessibilityHint: String { + switch self { + case .issueRefund: Localization.issueRefundAccessibilityHint + case .emailReceipt: Localization.emailReceiptAccessibilityHint + } + } + + var priority: Int { + switch self { + case .issueRefund: 100 + case .emailReceipt: 50 } } - func available(for order: POSOrder) -> Bool { + func isAvailable(for order: POSOrder, flags: POSFeatureFlagProviding) -> Bool { + guard order.status == .completed else { return false } switch self { + case .issueRefund: + return flags.isFeatureFlagEnabled(.pointOfSaleRefundsi1) case .emailReceipt: - order.status == .completed + return true + } + } + } + + func handler(for action: POSAction) -> @MainActor () -> Void { + switch action { + case .emailReceipt: + return { + analytics.track(event: WooAnalyticsEvent.PointOfSale.orderDetailsEmailReceiptTapped()) + isShowingEmailReceiptView = true } + case .issueRefund: + return { } } } - var actions: [POSOrderDetailsAction] { - POSOrderDetailsAction.allCases.filter { $0.available(for: order) } + var availableActions: [POSAction] { + POSAction.allCases + .filter { $0.isAvailable(for: order, flags: featureFlags) } + .sorted { $0.priority > $1.priority } } @ViewBuilder - func actionsSection(_ actions: [POSOrderDetailsAction]) -> some View { - VStack { - HStack { - ForEach(actions) { action in - Button(action: { - switch action { - case .emailReceipt: - analytics.track(event: WooAnalyticsEvent.PointOfSale.orderDetailsEmailReceiptTapped()) - isShowingEmailReceiptView = true + func actionsSection(actions: [POSAction]) -> some View { + if actions.isEmpty { + EmptyView() + } else { + HStack(spacing: POSSpacing.large) { + let primary = actions[0] + Button(primary.title, action: handler(for: primary)) + .buttonStyle(POSFilledButtonStyle(size: .extraSmall)) + .accessibilityHint(primary.accessibilityHint) + .lineLimit(1) + .minimumScaleFactor(0.5) + + let overflow = actions.dropFirst() + if !overflow.isEmpty { + Menu { + ForEach(Array(overflow)) { action in + Button(action.title, action: handler(for: action)) + .accessibilityHint(action.accessibilityHint) } - }) { - Text(Localization.emailReceiptActionTitle) - .lineLimit(1) - .minimumScaleFactor(0.5) + } label: { + Image(systemName: "ellipsis") + .font(.posBodyLargeBold) + .dynamicTypeSize(...DynamicTypeSize.accessibility2) + .foregroundColor(.posOnSurface) + .padding(POSPadding.small) } - .buttonStyle(POSFilledButtonStyle(size: .extraSmall)) - .accessibilityHint(accessibilityHint(for: action)) + .menuIndicator(.hidden) } } - Spacer() } } - private func accessibilityHint(for action: POSOrderDetailsAction) -> String { - switch action { - case .emailReceipt: - return Localization.emailReceiptAccessibilityHint - } + func emailReceiptAction() { + analytics.track(event: WooAnalyticsEvent.PointOfSale.orderDetailsEmailReceiptTapped()) + isShowingEmailReceiptView = true } } @@ -529,6 +567,24 @@ private enum Localization { comment: "Accessibility hint for email receipt button on order details view" ) + static let issueRefundActionTitle = NSLocalizedString( + "pos.orderDetailsView.issueRefundAction.title", + value: "Issue refund", + comment: "Primary action button to start issuing a refund on the order details view" + ) + + static let issueRefundAccessibilityHint = NSLocalizedString( + "pos.orderDetailsView.issueRefundAction.accessibilityHint", + value: "Start refund flow for this order", + comment: "Accessibility hint for issue refund button" + ) + + static let moreActionsA11yLabel = NSLocalizedString( + "pos.orderDetailsView.moreActions.label", + value: "More actions", + comment: "Accessibility label for the overflow actions menu button (three dots)" + ) + static func headerBottomContentAccessibilityLabel(date: String, email: String?, status: String) -> String { let baseFormat = NSLocalizedString( "pos.orderDetailsView.headerBottomContent.accessibilityLabel",