diff --git a/WooCommerce/Classes/Bookings/BookingBadgeView.swift b/WooCommerce/Classes/Bookings/BookingBadgeView.swift new file mode 100644 index 00000000000..cb317b03ee8 --- /dev/null +++ b/WooCommerce/Classes/Bookings/BookingBadgeView.swift @@ -0,0 +1,62 @@ +import SwiftUI +import enum Yosemite.BookingAttendanceStatus +import enum Yosemite.BookingStatus + +struct BookingBadgeView: View { + let text: String + let color: Color + + var body: some View { + BadgeView(text: text, + customizations: .init(textColor: Color(UIColor.label.resolvedColor(with: .init(userInterfaceStyle: .light))), + backgroundColor: color, + bordered: false, + bold: false)) + } +} + +protocol BookingBadgeable { + var text: String { get } + var badgeColor: Color { get } +} + +extension BookingBadgeView { + init(_ badgeable: BookingBadgeable) { + self.init(text: badgeable.text, color: badgeable.badgeColor) + } +} + +extension BookingAttendanceStatus: BookingBadgeable { + var badgeColor: Color { + switch self { + case .noShow: + return BadgeColor.info + default: + return BadgeColor.default + } + } + + var text: String { + self.localizedTitle + } +} + +extension BookingStatus: BookingBadgeable { + var badgeColor: Color { + switch self { + case .unpaid: + return BadgeColor.info + default: + return BadgeColor.default + } + } + + var text: String { + self.localizedTitle + } +} + +fileprivate enum BadgeColor { + static let `default` = Color(uiColor: .systemGray6.resolvedColor(with: .init(userInterfaceStyle: .light))) + static let info = try! Color(rgbString: "rgba(255, 227, 101, 1)") +} diff --git a/WooCommerce/Classes/Bookings/BookingList/BookingListView.swift b/WooCommerce/Classes/Bookings/BookingList/BookingListView.swift index e662c436b3e..e9142b7eefc 100644 --- a/WooCommerce/Classes/Bookings/BookingList/BookingListView.swift +++ b/WooCommerce/Classes/Bookings/BookingList/BookingListView.swift @@ -134,25 +134,14 @@ private extension BookingListView { .foregroundStyle(Color.secondary) HStack { - // TODO: update this when attendance status is available - // Update badge colors if design changes as statuses are not clarified now. - statusBadge(text: booking.attendanceStatus.localizedTitle, color: Layout.defaultBadgeColor) - statusBadge(text: booking.bookingStatus.localizedTitle, color: Layout.defaultBadgeColor) + BookingBadgeView(booking.attendanceStatus) + BookingBadgeView(booking.bookingStatus) Spacer() } } } } - func statusBadge(text: String, color: Color) -> some View { - Text(text) - .font(.caption2) - .foregroundStyle(Color.primary) - .padding(.horizontal, 8) - .padding(.vertical, 4) - .background(color.clipShape(RoundedRectangle(cornerRadius: 4))) - } - func emptyStateView(isSearching: Bool, onRefresh: @escaping () async -> Void) -> some View { GeometryReader { proxy in ScrollView { @@ -225,7 +214,6 @@ private extension BookingListView { static let viewPadding: CGFloat = 16 static let emptyStatePadding: CGFloat = 24 static let emptyStateImageWidth: CGFloat = 67 - static let defaultBadgeColor = Color(uiColor: .init(light: .systemGray6, dark: .systemGray5)) static let cornerRadius: CGFloat = 8 } diff --git a/WooCommerce/Classes/ViewModels/Booking Details/BookingAttendanceStatus+Localization.swift b/WooCommerce/Classes/ViewModels/Booking Details/BookingAttendanceStatus+Localization.swift index c121fea5664..1ed6abad359 100644 --- a/WooCommerce/Classes/ViewModels/Booking Details/BookingAttendanceStatus+Localization.swift +++ b/WooCommerce/Classes/ViewModels/Booking Details/BookingAttendanceStatus+Localization.swift @@ -13,7 +13,7 @@ extension BookingAttendanceStatus { case .checkedIn: return NSLocalizedString( "BookingAttendanceStatus.checkedIn", - value: "Checked In", + value: "Checked-in", comment: "Title for 'Checked In' booking attendance status." ) case .cancelled: @@ -25,7 +25,7 @@ extension BookingAttendanceStatus { case .noShow: return NSLocalizedString( "BookingAttendanceStatus.noShow", - value: "No Show", + value: "No-show", comment: "Title for 'No Show' booking attendance status." ) case .unknown: diff --git a/WooCommerce/Classes/ViewModels/Booking Details/HeaderContent.swift b/WooCommerce/Classes/ViewModels/Booking Details/HeaderContent.swift index 88002c4b69a..eae058c9fb7 100644 --- a/WooCommerce/Classes/ViewModels/Booking Details/HeaderContent.swift +++ b/WooCommerce/Classes/ViewModels/Booking Details/HeaderContent.swift @@ -3,11 +3,14 @@ import struct Yosemite.Booking import struct Yosemite.BookingProductInfo import struct Yosemite.Customer import struct Yosemite.Address +import enum Yosemite.BookingAttendanceStatus +import enum Yosemite.BookingStatus extension BookingDetailsViewModel { final class HeaderContent: ObservableObject { @Published private(set) var bookingDate: String = "" - @Published private(set) var status: [String] = [] + @Published private(set) var attendanceStatus: BookingAttendanceStatus = .unknown + @Published private(set) var bookingStatus: BookingStatus = .unknown @Published private(set) var serviceAndCustomerLine: String = "" func update(with booking: Booking) { @@ -17,11 +20,8 @@ extension BookingDetailsViewModel { timeZone: BookingListTab.utcTimeZone ) serviceAndCustomerLine = booking.summaryText - - status = [ - booking.attendanceStatus.localizedTitle, - booking.bookingStatus.localizedTitle - ] + attendanceStatus = booking.attendanceStatus + bookingStatus = booking.bookingStatus } } } diff --git a/WooCommerce/Classes/ViewRelated/Bookings/Booking Details/BookingDetailsView.swift b/WooCommerce/Classes/ViewRelated/Bookings/Booking Details/BookingDetailsView.swift index a1064ecb958..bc9622b7c6f 100644 --- a/WooCommerce/Classes/ViewRelated/Bookings/Booking Details/BookingDetailsView.swift +++ b/WooCommerce/Classes/ViewRelated/Bookings/Booking Details/BookingDetailsView.swift @@ -18,12 +18,6 @@ struct BookingDetailsView: View { static let headerBadgesAdditionalTopPadding: CGFloat = 4 static let sectionFooterTextVerticalPadding: CGFloat = 8 static let rowTextVerticalPadding: CGFloat = 11 - static let defaultBadgeColor = Color( - uiColor: .init( - light: .systemGray6, - dark: .systemGray5 - ) - ) } enum TextFont { diff --git a/WooCommerce/Classes/ViewRelated/Bookings/Booking Details/HeaderView.swift b/WooCommerce/Classes/ViewRelated/Bookings/Booking Details/HeaderView.swift index 2eab2f64aaa..3e17b5024a4 100644 --- a/WooCommerce/Classes/ViewRelated/Bookings/Booking Details/HeaderView.swift +++ b/WooCommerce/Classes/ViewRelated/Bookings/Booking Details/HeaderView.swift @@ -17,16 +17,8 @@ extension BookingDetailsView { .foregroundColor(.secondary) } HStack { - ForEach(content.status, id: \.self) { statusString in - Text(statusString) - .font(.caption2) - .padding(.vertical, 4.5) - .padding(.horizontal, 8) - .background( - BookingDetailsView.Layout.defaultBadgeColor - ) - .cornerRadius(4) - } + BookingBadgeView(content.attendanceStatus) + BookingBadgeView(content.bookingStatus) } .padding(.top, Layout.headerBadgesAdditionalTopPadding) } diff --git a/WooCommerce/Classes/ViewRelated/ReusableViews/SwiftUI Components/BadgeView.swift b/WooCommerce/Classes/ViewRelated/ReusableViews/SwiftUI Components/BadgeView.swift index 2e3f3f5e1b8..6d059920da6 100644 --- a/WooCommerce/Classes/ViewRelated/ReusableViews/SwiftUI Components/BadgeView.swift +++ b/WooCommerce/Classes/ViewRelated/ReusableViews/SwiftUI Components/BadgeView.swift @@ -35,11 +35,17 @@ struct BadgeView: View { struct Customizations { let textColor: Color let backgroundColor: Color + let bordered: Bool + let bold: Bool init(textColor: Color = Color(.textBrand), - backgroundColor: Color = Color(.wooCommercePurple(.shade0))) { + backgroundColor: Color = Color(.wooCommercePurple(.shade0)), + bordered: Bool = true, + bold: Bool = true) { self.textColor = textColor self.backgroundColor = backgroundColor + self.bordered = bordered + self.bold = bold } } @@ -64,7 +70,9 @@ struct BadgeView: View { var body: some View { if let text = type.title { Text(text) - .bold() + .if(customizations.bold) { + $0.bold() + } .foregroundColor(customizations.textColor) .captionStyle() .padding(.leading, Layout.horizontalPadding) @@ -98,14 +106,20 @@ private extension BadgeView { case .circle: Circle() .fill(customizations.backgroundColor) - .stroke(Color.white, lineWidth: Layout.borderLineWidth) + .if(customizations.bordered) { view in + view.overlay { + Circle().stroke(Color.white, lineWidth: Layout.borderLineWidth) + } + } case .roundedRectangle(let cornerRadius): RoundedRectangle(cornerRadius: cornerRadius) - .stroke(.white, lineWidth: Layout.borderLineWidth) - .background( - RoundedRectangle(cornerRadius: cornerRadius) - .fill(customizations.backgroundColor) - ) + .fill(customizations.backgroundColor) + .if(customizations.bordered) { view in + view.overlay { + RoundedRectangle(cornerRadius: cornerRadius) + .stroke(Color.white, lineWidth: Layout.borderLineWidth) + } + } } } } @@ -119,7 +133,7 @@ private extension BadgeView.BadgeType { private extension BadgeView { enum Layout { - static let horizontalPadding: CGFloat = 6 + static let horizontalPadding: CGFloat = 8 static let verticalPadding: CGFloat = 4 static let borderLineWidth: CGFloat = 1 static let cornerRadius: CGFloat = 8 diff --git a/WooCommerce/Resources/en.lproj/Localizable.strings b/WooCommerce/Resources/en.lproj/Localizable.strings index 6b59f145141..05d341b383e 100644 --- a/WooCommerce/Resources/en.lproj/Localizable.strings +++ b/WooCommerce/Resources/en.lproj/Localizable.strings @@ -1840,10 +1840,10 @@ which should be translated separately and considered part of this sentence. */ "BookingAttendanceStatus.cancelled" = "Cancelled"; /* Title for 'Checked In' booking attendance status. */ -"BookingAttendanceStatus.checkedIn" = "Checked In"; +"BookingAttendanceStatus.checkedIn" = "Checked-in"; /* Title for 'No Show' booking attendance status. */ -"BookingAttendanceStatus.noShow" = "No Show"; +"BookingAttendanceStatus.noShow" = "No-show"; /* Title for 'Unknown' booking attendance status. */ "BookingAttendanceStatus.unknown" = "Unknown"; diff --git a/WooCommerce/WooCommerceTests/ViewRelated/Bookings/BookingDetailsViewModelTests.swift b/WooCommerce/WooCommerceTests/ViewRelated/Bookings/BookingDetailsViewModelTests.swift index e055e11b506..b857d06f723 100644 --- a/WooCommerce/WooCommerceTests/ViewRelated/Bookings/BookingDetailsViewModelTests.swift +++ b/WooCommerce/WooCommerceTests/ViewRelated/Bookings/BookingDetailsViewModelTests.swift @@ -306,7 +306,8 @@ final class BookingDetailsViewModelTests: XCTestCase { XCTFail("Header section not found") return } - XCTAssertEqual(headerContent.status, ["Checked In", "Paid"]) + XCTAssertEqual(headerContent.attendanceStatus.localizedTitle, "Checked-in") + XCTAssertEqual(headerContent.bookingStatus.localizedTitle, "Paid") } func test_init_whenBookingHasAttendanceStatus_updatesAttendanceContentWithCorrectLocalizedString() { @@ -332,7 +333,7 @@ final class BookingDetailsViewModelTests: XCTestCase { return } - XCTAssertEqual(attendanceContent.value, "No Show") + XCTAssertEqual(attendanceContent.value, "No-show") } func test_attendance_section_is_hidden_when_booking_is_cancelled() {