Skip to content

Commit bd89c5b

Browse files
committed
Draft sectioned approach
1 parent 8b00c93 commit bd89c5b

File tree

2 files changed

+213
-125
lines changed

2 files changed

+213
-125
lines changed

WooCommerce/Classes/ViewModels/Booking Details/BookingDetailsViewModel.swift

Lines changed: 152 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -8,102 +8,172 @@ extension BookingDetailsViewModel {
88
}
99
}
1010

11-
private extension BookingDetailsViewModel {
12-
static let dateFormatter = {
13-
let dateFormatter = DateFormatter()
14-
dateFormatter.dateFormat = "dd/MM/yyyy, hh:mm a"
15-
return dateFormatter
16-
}()
17-
18-
static let appointmentDateFormatter = {
19-
let dateFormatter = DateFormatter()
20-
dateFormatter.dateFormat = "EEEE, dd MMMM yyyy"
21-
return dateFormatter
22-
}()
23-
24-
static let appointmentTimeFrameFormatter = {
25-
let dateFormatter = DateFormatter()
26-
dateFormatter.dateFormat = "hh:mm a"
27-
return dateFormatter
28-
}()
29-
}
30-
31-
final class BookingDetailsViewModel: ObservableObject {
32-
33-
init(booking: Booking) {
34-
bookingDate = Self.dateFormatter.string(from: booking.startDate)
11+
extension BookingDetailsViewModel {
12+
struct Section: Identifiable {
13+
var id: String {
14+
return content.id
15+
}
16+
17+
let headerText: String?
18+
let footerText: String?
19+
let content: SectionContent
20+
21+
init(
22+
headerText: String? = nil,
23+
footerText: String? = nil,
24+
content: SectionContent
25+
) {
26+
self.headerText = headerText
27+
self.footerText = footerText
28+
self.content = content
29+
}
30+
}
3531

36-
// This will be assigned later
37-
serviceName = "Women's Haircut"
38-
customerName = "Margarita Nikolaevna"
39-
service = "Women's Haircut"
40-
status = [.paid, .booked]
41-
quantity = 1
42-
servicesCost = "$62.68"
43-
tax = "$7.32"
44-
45-
appointmentDate = Self.appointmentDateFormatter.string(from: booking.startDate)
46-
appointmentTimeFrame = [
47-
Self.appointmentTimeFrameFormatter.string(from: booking.startDate),
48-
Self.appointmentTimeFrameFormatter.string(from: booking.endDate)
49-
].joined(separator: " - ")
50-
durationMinutes = Int(booking.endDate.timeIntervalSince(booking.startDate) / 60)
51-
52-
cost = booking.cost
53-
total = booking.cost
54-
paid = booking.cost
32+
enum SectionContent: Identifiable {
33+
var id: String {
34+
switch self {
35+
case .header:
36+
return "header"
37+
case .appointmentDetails:
38+
return "appointmentDetails"
39+
}
40+
}
41+
42+
case header(HeaderContent)
43+
case appointmentDetails(AppointmentDetailsContent)
44+
// case attendance(AttendanceContent)
45+
// case payment(PaymentContent)
46+
// case customer(CustomerContent)
47+
// case teamMember(TeamMemberContent)
5548
}
49+
}
5650

57-
// MARK: - Header Properties
58-
let bookingDate: String
59-
let serviceName: String
60-
let customerName: String
61-
let status: [Status]
62-
63-
// MARK: - Appointment Details
64-
let appointmentDate: String
65-
let appointmentTimeFrame: String
66-
let service: String
67-
let quantity: Int
68-
let durationMinutes: Int
69-
let cost: String
70-
71-
// MARK: - Payment Details
72-
let servicesCost: String
73-
let tax: String
74-
let total: String
75-
let paid: String
76-
77-
// MARK: - Customer Details
78-
var customerEmail: String {
79-
// This will be fetched from the customer details later
80-
51+
extension BookingDetailsViewModel {
52+
struct HeaderContent: Hashable {
53+
static let dateFormatter = {
54+
let dateFormatter = DateFormatter()
55+
dateFormatter.dateFormat = "dd/MM/yyyy, hh:mm a"
56+
return dateFormatter
57+
}()
58+
59+
let bookingDate: String
60+
let serviceName: String
61+
let customerName: String
62+
let status: [Status]
63+
64+
init(_ booking: Booking) {
65+
bookingDate = Self.dateFormatter.string(from: booking.startDate)
66+
serviceName = "Women's Haircut"
67+
customerName = "Margarita Nikolaevna"
68+
status = [.paid, .booked]
69+
}
8170
}
8271

83-
var customerPhone: String {
84-
// This will be fetched from the customer details later
85-
return "+1742582943798"
72+
struct AppointmentDetailsContent {
73+
static let appointmentDateFormatter = {
74+
let dateFormatter = DateFormatter()
75+
dateFormatter.dateFormat = "EEEE, dd MMMM yyyy"
76+
return dateFormatter
77+
}()
78+
79+
static let appointmentTimeFrameFormatter = {
80+
let dateFormatter = DateFormatter()
81+
dateFormatter.dateFormat = "hh:mm a"
82+
return dateFormatter
83+
}()
84+
85+
let rows: [Row]
86+
87+
struct Row: Identifiable {
88+
let title: String
89+
let value: String
90+
91+
var id: String {
92+
return title
93+
}
94+
}
95+
96+
init(_ booking: Booking) {
97+
let durationMinutes = Int(booking.endDate.timeIntervalSince(booking.startDate) / 60)
98+
let appointmentDate = Self.appointmentDateFormatter.string(from: booking.startDate)
99+
let appointmentTimeFrame = [
100+
Self.appointmentTimeFrameFormatter.string(from: booking.startDate),
101+
Self.appointmentTimeFrameFormatter.string(from: booking.endDate)
102+
].joined(separator: " - ")
103+
104+
rows = [
105+
Row(title: "Date", value: appointmentDate),
106+
Row(title: "Time", value: appointmentTimeFrame),
107+
Row(title: "Service", value: "Women's Haircut"),
108+
Row(title: "Quantity", value: "1"),
109+
Row(title: "Duration", value: String(durationMinutes)),
110+
Row(title: "Cost", value: booking.cost)
111+
]
112+
}
86113
}
87114

88-
var billingAddress: String {
89-
// This will be fetched from the customer details later
90-
return "238 Willow Creek Drive\nMontgomery\nAL 36109"
115+
struct AttendanceContent {
116+
91117
}
92118

93-
// MARK: - Actions
94-
func rescheduleBooking() {
95-
// Placeholder for reschedule logic
119+
struct PaymentContent {
120+
96121
}
97122

98-
func cancelBooking() {
99-
// Placeholder for cancel logic
123+
struct CustomerContent {
124+
100125
}
101126

102-
func markAsPaid() {
103-
// Placeholder for mark as paid logic
127+
struct TeamMemberContent {
128+
104129
}
130+
}
131+
132+
133+
final class BookingDetailsViewModel: ObservableObject {
134+
// // MARK: - Payment Details
135+
// let servicesCost: String
136+
// let tax: String
137+
// let total: String
138+
// let paid: String
139+
//
140+
// // MARK: - Customer Details
141+
// var customerEmail: String {
142+
// // This will be fetched from the customer details later
143+
// return "[email protected]"
144+
// }
145+
//
146+
// var customerPhone: String {
147+
// // This will be fetched from the customer details later
148+
// return "+1742582943798"
149+
// }
150+
//
151+
// var billingAddress: String {
152+
// // This will be fetched from the customer details later
153+
// return "238 Willow Creek Drive\nMontgomery\nAL 36109"
154+
// }
155+
156+
let sections: [Section]
105157

106-
func viewOrder() {
107-
// Placeholder for view order logic
158+
init(booking: Booking) {
159+
let headerSection = Section.init(
160+
content: .header(HeaderContent(booking))
161+
)
162+
163+
let appointmentDetailsSection = Section(
164+
headerText: "Appointment Details".uppercased(),
165+
content: .appointmentDetails(AppointmentDetailsContent(booking))
166+
)
167+
168+
sections = [
169+
headerSection,
170+
appointmentDetailsSection
171+
]
172+
173+
// This will be assigned later
174+
// servicesCost = "$62.68"
175+
// tax = "$7.32"
176+
// total = booking.cost
177+
// paid = booking.cost
108178
}
109179
}

WooCommerce/Classes/ViewRelated/Bookings/Booking Details/BookingDetailsView.swift

Lines changed: 61 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,73 @@ struct BookingDetailsView: View {
1919
static let bookingStatusLabel: Color = .gray
2020
}
2121

22+
func sectionView(with section: BookingDetailsViewModel.Section) -> some View {
23+
VStack(alignment: .leading, spacing: Layout.headerContentVerticalPadding) {
24+
if let headerText = section.headerText {
25+
Text(headerText)
26+
.font(.caption)
27+
.foregroundColor(.gray)
28+
}
29+
30+
switch section.content {
31+
case .header(let content):
32+
headerView(with: content)
33+
case .appointmentDetails(let content):
34+
appointmentDetailsView(with: content)
35+
}
36+
37+
if let footerText = section.footerText {
38+
Text(footerText)
39+
.font(.caption)
40+
.foregroundColor(.gray)
41+
}
42+
}
43+
}
44+
45+
func headerView(with headerContent: BookingDetailsViewModel.HeaderContent) -> some View {
46+
VStack(alignment: .leading, spacing: Layout.headerContentVerticalPadding) {
47+
Text(headerContent.bookingDate)
48+
.font(.caption)
49+
.foregroundColor(.secondary)
50+
Text(headerContent.serviceName)
51+
.font(TextFont.headerBodyText)
52+
Text(headerContent.customerName)
53+
.font(TextFont.headerBodyText)
54+
.foregroundColor(.secondary)
55+
HStack {
56+
ForEach(headerContent.status, id: \.self) { status in
57+
Text(status.labelText)
58+
.font(.caption)
59+
.padding(4)
60+
.background(status.labelColor)
61+
.cornerRadius(4)
62+
}
63+
}
64+
.padding(.top, Layout.headerBadgesAdditionalTopPadding)
65+
}
66+
}
67+
68+
func appointmentDetailsView(with content: BookingDetailsViewModel.AppointmentDetailsContent) -> some View {
69+
VStack(alignment: .leading, spacing: 16) {
70+
ForEach(content.rows) { row in
71+
DetailRow(title: row.title, value: row.value)
72+
Divider()
73+
}
74+
}
75+
}
76+
2277
var body: some View {
2378
RefreshablePlainList(action: {
2479
print("Refresh triggered")
2580
}) {
2681
VStack(alignment: .leading) {
27-
headerView
28-
.padding(.horizontal)
82+
ForEach(viewModel.sections) { section in
83+
sectionView(with: section)
84+
.padding(.horizontal)
85+
86+
Divider()
87+
}
2988

30-
Divider()
3189
//
3290
// // Appointment Details
3391
// appointmentDetailsSectionView
@@ -137,46 +195,6 @@ struct DetailRow: View {
137195
}
138196
}
139197

140-
private extension BookingDetailsView {
141-
var headerView: some View {
142-
VStack(alignment: .leading, spacing: Layout.headerContentVerticalPadding) {
143-
Text(viewModel.bookingDate)
144-
.font(.caption)
145-
.foregroundColor(.secondary)
146-
Text(viewModel.serviceName)
147-
.font(TextFont.headerBodyText)
148-
Text(viewModel.customerName)
149-
.font(TextFont.headerBodyText)
150-
.foregroundColor(.secondary)
151-
HStack {
152-
ForEach(viewModel.status, id: \.self) { status in
153-
Text(status.labelText)
154-
.font(.caption)
155-
.padding(4)
156-
.background(status.labelColor)
157-
.cornerRadius(4)
158-
}
159-
}
160-
.padding(.top, Layout.headerBadgesAdditionalTopPadding)
161-
}
162-
}
163-
164-
// var appointmentDetailsSectionView: some View {
165-
// VStack(alignment: .leading, spacing: 16) {
166-
// Text("APPOINTMENT DETAILS")
167-
// .font(.caption)
168-
// .foregroundColor(.gray)
169-
//
170-
// DetailRow(title: "Date", value: viewModel.appointmentDate)
171-
// DetailRow(title: "Time", value: viewModel.appointmentTime)
172-
// DetailRow(title: "Service", value: viewModel.service)
173-
// DetailRow(title: "Quantity", value: "\(viewModel.quantity)")
174-
// DetailRow(title: "Duration", value: viewModel.duration)
175-
// DetailRow(title: "Cost", value: viewModel.cost)
176-
// }
177-
// }
178-
}
179-
180198
extension BookingDetailsViewModel.Status {
181199
var labelText: String {
182200
switch self {

0 commit comments

Comments
 (0)