Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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 @@ -4,6 +4,8 @@ import Networking
struct BookingDetailsView: View {
@Environment(\.safeAreaInsets) var safeAreaInsets: EdgeInsets
@Environment(\.dismiss) private var dismiss
@State private var showingOptions = false
@State private var showingStatusSheet = false

@ObservedObject private var viewModel: BookingDetailsViewModel

Expand Down Expand Up @@ -51,13 +53,31 @@ struct BookingDetailsView: View {
}
ToolbarItem(placement: .navigationBarTrailing) {
Button {
//TODO: - present an action sheet
print("On ellipsis item tap")
showingOptions = true
} label: {
Image(systemName: "ellipsis")
}
.confirmationDialog("", isPresented: $showingOptions, titleVisibility: .hidden) {
Button(Localization.markAsPaid) {
print("On mark as paid tap")
}
Button(Localization.viewOrder) {
print("On view order tap")
}
Button(Localization.cancelBookingAction, role: .destructive) {
print("On cancel booking tap")
}
}
}
}
.sheet(isPresented: $showingStatusSheet) {
UpdateAttendanceStatusView { selectedStatus in
print("Selected status: \(selectedStatus)")
}
.padding(.top)
.presentationDetents([.medium])
.presentationDragIndicator(.visible)
}
}
}

Expand Down Expand Up @@ -145,7 +165,9 @@ private extension BookingDetailsView {
value: .placeholder(content.value),
selectionStyle: .disclosure,
horizontalPadding: 0
)
) {
showingStatusSheet = true
}
}

func appointmentDetailsView(with content: BookingDetailsViewModel.AppointmentDetailsContent) -> some View {
Expand Down Expand Up @@ -312,6 +334,22 @@ private extension View {

private extension BookingDetailsView {
enum Localization {
static let markAsPaid = NSLocalizedString(
"BookingDetailsView.options.markAsPaid",
value: "Mark as paid",
comment: "Action sheet option to mark a booking as paid."
)
static let viewOrder = NSLocalizedString(
"BookingDetailsView.options.viewOrder",
value: "View order",
comment: "Action sheet option to view the order for a booking."
)
static let cancelBookingAction = NSLocalizedString(
"BookingDetailsView.options.cancelBooking",
value: "Cancel booking",
comment: "Action sheet option to cancel a booking."
)

static let cancelBooking = NSLocalizedString(
"BookingDetailsView.customer.cancelBookingButton.title",
value: "Cancel booking",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import SwiftUI

struct UpdateAttendanceStatusView: View {
@Environment(\.dismiss) private var dismiss
private let statuses = AttendanceStatus.allCases
private let onStatusSelected: (AttendanceStatus) -> Void

init(onStatusSelected: @escaping (AttendanceStatus) -> Void) {
self.onStatusSelected = onStatusSelected
}

var body: some View {
VStack(alignment: .leading, spacing: 24) {
Text(Localization.title)
.font(.subheadline.weight(.medium))
.foregroundColor(.secondary)
.padding(.horizontal)

ForEach(statuses) { status in
HStack(spacing: 16) {
Image(systemName: status.iconName)
.font(.title3.weight(.medium))
.foregroundStyle(Color(.systemGray))
VStack(alignment: .leading, spacing: 4) {
Text(status.title)
.font(.body.weight(.medium))
Text(status.description)
.font(.subheadline)
.foregroundColor(.secondary)
}
}
.padding(.horizontal)
.contentShape(Rectangle())
.tappable {
onStatusSelected(status)
dismiss()
}
}
Spacer()
}
.padding(.top)
}
}

extension UpdateAttendanceStatusView {
enum AttendanceStatus: CaseIterable, Identifiable {
case booked
case checkedIn
case noShow

var id: Self { self }
}
}

private extension UpdateAttendanceStatusView.AttendanceStatus {
var title: String {
switch self {
case .booked:
return UpdateAttendanceStatusView.Localization.bookedTitle
case .checkedIn:
return UpdateAttendanceStatusView.Localization.checkedInTitle
case .noShow:
return UpdateAttendanceStatusView.Localization.noShowTitle
}
}

var description: String {
switch self {
case .booked:
return UpdateAttendanceStatusView.Localization.bookedDescription
case .checkedIn:
return UpdateAttendanceStatusView.Localization.checkedInDescription
case .noShow:
return UpdateAttendanceStatusView.Localization.noShowDescription
}
}

var iconName: String {
switch self {
case .booked:
return "calendar.badge.checkmark"
case .checkedIn:
return "calendar.and.person"
case .noShow:
return "calendar.badge.exclamationmark"
}
}
}

private extension UpdateAttendanceStatusView {
enum Localization {
static let title = NSLocalizedString(
"UpdateAttendanceStatusView.title",
value: "Update attendance status",
comment: "Title of the update attendance status bottom sheet."
)

static let bookedTitle = NSLocalizedString(
"UpdateAttendanceStatusView.booked.title",
value: "Booked",
comment: "Title for the 'Booked' attendance status."
)
static let bookedDescription = NSLocalizedString(
"UpdateAttendanceStatusView.booked.description",
value: "The appointment is scheduled but hasn't happened yet.",
comment: "Description for the 'Booked' attendance status."
)

static let checkedInTitle = NSLocalizedString(
"UpdateAttendanceStatusView.checkedIn.title",
value: "Checked-in",
comment: "Title for the 'Checked-in' attendance status."
)
static let checkedInDescription = NSLocalizedString(
"UpdateAttendanceStatusView.checkedIn.description",
value: "The customer arrived and the session took place as planned.",
comment: "Description for the 'Checked-in' attendance status."
)

static let noShowTitle = NSLocalizedString(
"UpdateAttendanceStatusView.noShow.title",
value: "No-show",
comment: "Title for the 'No-show' attendance status."
)
static let noShowDescription = NSLocalizedString(
"UpdateAttendanceStatusView.noShow.description",
value: "The client missed the appointment without canceling in advance.",
comment: "Description for the 'No-show' attendance status."
)
}
}

#if DEBUG
struct UpdateAttendanceStatusView_Previews: PreviewProvider {
static var previews: some View {
UpdateAttendanceStatusView { selectedStatus in
print("Selected status: \(selectedStatus)")
}
}
}
#endif
4 changes: 4 additions & 0 deletions WooCommerce/WooCommerce.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1261,6 +1261,7 @@
2D05E8112E8A9905004111FD /* CustomerContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D05E8102E8A98FE004111FD /* CustomerContent.swift */; };
2D05E8132E8AADB9004111FD /* PaymentContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D05E8122E8AADB2004111FD /* PaymentContent.swift */; };
2D05F7102E8BE921004111FD /* View+Tappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D05F70F2E8BE91E004111FD /* View+Tappable.swift */; };
2D05FE962E8D71EA004111FD /* UpdateAttendanceStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D05FE952E8D71EA004111FD /* UpdateAttendanceStatusView.swift */; };
2D09E0D12E61BC7F005C26F3 /* ApplicationPasswordsExperimentState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D09E0D02E61BC7D005C26F3 /* ApplicationPasswordsExperimentState.swift */; };
2D09E0D52E65C9B9005C26F3 /* ApplicationPasswordsExperimentStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D09E0D42E65C9B9005C26F3 /* ApplicationPasswordsExperimentStateTests.swift */; };
2D7A3E232E7891DB00C46401 /* CIABEligibilityCheckerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D7A3E222E7891D200C46401 /* CIABEligibilityCheckerTests.swift */; };
Expand Down Expand Up @@ -4469,6 +4470,7 @@
2D05E8102E8A98FE004111FD /* CustomerContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomerContent.swift; sourceTree = "<group>"; };
2D05E8122E8AADB2004111FD /* PaymentContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentContent.swift; sourceTree = "<group>"; };
2D05F70F2E8BE91E004111FD /* View+Tappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Tappable.swift"; sourceTree = "<group>"; };
2D05FE952E8D71EA004111FD /* UpdateAttendanceStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateAttendanceStatusView.swift; sourceTree = "<group>"; };
2D09E0D02E61BC7D005C26F3 /* ApplicationPasswordsExperimentState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationPasswordsExperimentState.swift; sourceTree = "<group>"; };
2D09E0D42E65C9B9005C26F3 /* ApplicationPasswordsExperimentStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationPasswordsExperimentStateTests.swift; sourceTree = "<group>"; };
2D7A3E222E7891D200C46401 /* CIABEligibilityCheckerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIABEligibilityCheckerTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -9184,6 +9186,7 @@
isa = PBXGroup;
children = (
2DAC2C972E82A185008521AF /* BookingDetailsView.swift */,
2D05FE952E8D71EA004111FD /* UpdateAttendanceStatusView.swift */,
);
path = "Booking Details";
sourceTree = "<group>";
Expand Down Expand Up @@ -16558,6 +16561,7 @@
DE2FE5862925DA050018040A /* SiteCredentialLoginView.swift in Sources */,
020DD48F232392C9005822B1 /* UIViewController+AppReview.swift in Sources */,
2687165524D21BC80042F6AE /* SurveySubmittedViewController.swift in Sources */,
2D05FE962E8D71EA004111FD /* UpdateAttendanceStatusView.swift in Sources */,
CE263DE8206ACE3E0015A693 /* MainTabBarController.swift in Sources */,
20A3AFE32B10EF860033AF2D /* CardReaderSettingsFlowPresentingView.swift in Sources */,
CE14452E2188C11700A991D8 /* ZendeskManager.swift in Sources */,
Expand Down