diff --git a/SodaLive/Sources/V2/Gift/Detail/Components/GiftCancelCompleteView.swift b/SodaLive/Sources/V2/Gift/Detail/Components/GiftCancelCompleteView.swift new file mode 100644 index 00000000..2837d0d7 --- /dev/null +++ b/SodaLive/Sources/V2/Gift/Detail/Components/GiftCancelCompleteView.swift @@ -0,0 +1,91 @@ +import SwiftUI + +struct GiftCancelCompleteView: View { + let model: SentGiftDetailDisplayModel + @State private var isNavigating = false + + var body: some View { + VStack(alignment: .leading, spacing: SodaSpacing.s32) { + VStack(alignment: .leading, spacing: SodaSpacing.s14) { + Text("취소 완료") + .appFont(.caption2) + .foregroundColor(.white) + .padding(.horizontal, SodaSpacing.s8) + .padding(.vertical, SodaSpacing.s4) + .background(Color.gray700) + .clipShape(RoundedRectangle(cornerRadius: SodaRadius.r4)) + + Text("선물 신청이 취소되었어요") + .appFont(.heading2) + .foregroundColor(.white) + .accessibilityAddTraits(.isHeader) + + Text(completionMessage) + .appFont(.body6) + .foregroundColor(.gray300) + } + .fixedSize(horizontal: false, vertical: true) + .frame(maxWidth: .infinity, alignment: .leading) + + VStack(spacing: SodaSpacing.s12) { + GiftInfoSectionView(title: "취소 상세") { + if let creatorNickname { + GiftInfoRow(title: "크리에이터", value: creatorNickname) + } + GiftInfoRow(title: "선물 사이즈", value: model.sizeName) + GiftInfoRow(title: "카테고리", value: model.categoryName) + GiftInfoRow(title: "신청번호", value: model.applicationNo) + if let applicationDate = model.applicationDateText { + GiftInfoRow(title: "신청일", value: applicationDate) + } + if let canceledDate = model.canceledDateText { + GiftInfoRow(title: "취소일", value: canceledDate) + } + if let paidCan = model.paidCan { + GiftInfoRow(title: "환불 캔", value: String(paidCan)) + } + } + + inquiryButton + } + } + .frame(maxWidth: .infinity, alignment: .leading) + .onAppear { + isNavigating = false + } + } + + private var inquiryButton: some View { + Button { + guard !isNavigating else { return } + isNavigating = true + AppState.shared.setAppStep(step: GiftConstants.inquiryStep) + } label: { + Text("문의하기") + .appFont(.body2) + .foregroundColor(isNavigating ? .gray500 : .white) + .multilineTextAlignment(.center) + .fixedSize(horizontal: false, vertical: true) + .padding(.horizontal, SodaSpacing.s16) + .padding(.vertical, SodaSpacing.s12) + .frame(maxWidth: .infinity, minHeight: SodaSpacing.s48) + .overlay(Capsule().strokeBorder(Color.gray800)) + .contentShape(Capsule()) + } + .buttonStyle(.plain) + .disabled(isNavigating) + } + + private var creatorNickname: String? { + guard let nickname = model.recipientCreatorNickname?.trimmingCharacters(in: .whitespacesAndNewlines), + !nickname.isEmpty else { return nil } + return nickname + } + + private var completionMessage: String { + let recipient = creatorNickname.map { "\($0)에게 보내는 " } ?? "" + let applicationNo = model.applicationNo.trimmingCharacters(in: .whitespacesAndNewlines) + let application = applicationNo.isEmpty ? "" : "(\(applicationNo))" + return "\(recipient)선물 신청\(application)이 취소되었습니다. 취소한 신청은 되돌릴 수 없으며, 다시 보내려면 새로 신청해 주세요." + } +} diff --git a/SodaLive/Sources/V2/Gift/Detail/Components/GiftDetailActionBar.swift b/SodaLive/Sources/V2/Gift/Detail/Components/GiftDetailActionBar.swift new file mode 100644 index 00000000..046be189 --- /dev/null +++ b/SodaLive/Sources/V2/Gift/Detail/Components/GiftDetailActionBar.swift @@ -0,0 +1,84 @@ +import SwiftUI + +struct GiftDetailActionBar: View { + let model: SentGiftDetailDisplayModel + var onCancel: (() -> Void)? = nil + let onPolicy: () -> Void + @State private var isNavigating = false + + var body: some View { + if model.trackingRequired || model.status == .received || model.status == .undeliverable { + VStack(spacing: SodaSpacing.s8) { + if model.trackingRequired { + Button { + guard !isNavigating else { return } + isNavigating = true + AppState.shared.setAppStep(step: .giftTrackingRegistration(applicationNo: model.applicationNo)) + } label: { + SelectedButtonView( + title: "운송장 작성하기", + isActive: !isNavigating, + isSelected: false, + bgDefaultColor: .button, + textDefaultColor: .white + ) + .multilineTextAlignment(.center) + .fixedSize(horizontal: false, vertical: true) + .frame(minHeight: SodaSpacing.s48) + .contentShape(Rectangle()) + } + .disabled(isNavigating) + } + + if model.status == .received { + Button { + onCancel?() + } label: { + secondaryLabel("신청 취소", isEnabled: onCancel != nil) + } + .disabled(onCancel == nil) + .accessibilityHint(onCancel == nil ? "현재 신청 취소를 이용할 수 없습니다." : "") + } + + if model.status == .undeliverable { + Button { + guard !isNavigating else { return } + isNavigating = true + AppState.shared.setAppStep(step: GiftConstants.inquiryStep) + } label: { + secondaryLabel("문의하기", isEnabled: !isNavigating) + } + .disabled(isNavigating) + + Button { + onPolicy() + } label: { + secondaryLabel("선물 정책 확인") + } + } + } + .buttonStyle(.plain) + .padding(.horizontal, SodaSpacing.s14) + .padding(.vertical, SodaSpacing.s12) + .frame(maxWidth: .infinity) + .background(Color.black) + .onAppear { + isNavigating = false + } + } + } + + private func secondaryLabel(_ title: String, isEnabled: Bool = true) -> some View { + Text(title) + .appFont(.body2) + .foregroundColor(isEnabled ? .white : .gray500) + .multilineTextAlignment(.center) + .fixedSize(horizontal: false, vertical: true) + .padding(.horizontal, SodaSpacing.s16) + .padding(.vertical, SodaSpacing.s12) + .frame(maxWidth: .infinity, minHeight: SodaSpacing.s48) + .background(Color.gray900) + .clipShape(RoundedRectangle(cornerRadius: SodaRadius.r8)) + .contentShape(Rectangle()) + } +}