쿠폰등록 페이지 추가
This commit is contained in:
parent
8857dd3e98
commit
3387a1b369
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "ic_coupon.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 873 B |
|
@ -117,4 +117,6 @@ enum AppStep {
|
|||
case creatorCommunityWrite(onSuccess: () -> Void)
|
||||
|
||||
case creatorCommunityModify(postId: Int, onSuccess: () -> Void)
|
||||
|
||||
case canCoupon
|
||||
}
|
||||
|
|
|
@ -172,6 +172,9 @@ struct ContentView: View {
|
|||
case .creatorCommunityModify(let postId, let onSuccess):
|
||||
CreatorCommunityModifyView(postId: postId, onSuccess: onSuccess)
|
||||
|
||||
case .canCoupon:
|
||||
CanCouponView()
|
||||
|
||||
default:
|
||||
EmptyView()
|
||||
.frame(width: 0, height: 0, alignment: .topLeading)
|
||||
|
|
|
@ -19,6 +19,8 @@ enum CanApi {
|
|||
|
||||
case pgChargeCan(request: PgChargeRequest)
|
||||
case pgVerify(request: PgVerifyRequest)
|
||||
|
||||
case useCanCoupon(request: UseCanCouponRequest)
|
||||
}
|
||||
|
||||
extension CanApi: TargetType {
|
||||
|
@ -51,6 +53,9 @@ extension CanApi: TargetType {
|
|||
|
||||
case .pgVerify:
|
||||
return "/charge/verify"
|
||||
|
||||
case .useCanCoupon:
|
||||
return "/can/coupon/use"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,7 +64,7 @@ extension CanApi: TargetType {
|
|||
case .getCanStatus, .getCanChargeStatus, .getCanUseStatus, .getCans:
|
||||
return .get
|
||||
|
||||
case .chargeCan, .verify, .pgChargeCan, .pgVerify:
|
||||
case .chargeCan, .verify, .pgChargeCan, .pgVerify, .useCanCoupon:
|
||||
return .post
|
||||
}
|
||||
}
|
||||
|
@ -97,6 +102,9 @@ extension CanApi: TargetType {
|
|||
|
||||
case .pgVerify(let request):
|
||||
return .requestJSONEncodable(request)
|
||||
|
||||
case .useCanCoupon(let request):
|
||||
return .requestJSONEncodable(request)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,5 +44,10 @@ final class CanRepository {
|
|||
func getCans() -> AnyPublisher<Response, MoyaError> {
|
||||
return api.requestPublisher(.getCans)
|
||||
}
|
||||
|
||||
func useCanCoupon(couponNumber: String) -> AnyPublisher<Response, MoyaError> {
|
||||
let request = UseCanCouponRequest(couponNumber: couponNumber)
|
||||
return api.requestPublisher(.useCanCoupon(request: request))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// CanChargeCouponButtonView.swift
|
||||
// SodaLive
|
||||
//
|
||||
// Created by klaus on 2024/01/03.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct CanChargeCouponButtonView: View {
|
||||
var body: some View {
|
||||
HStack(spacing: 5.3) {
|
||||
Text("쿠폰 등록")
|
||||
.font(.custom(Font.bold.rawValue, size: 16))
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
|
||||
Image("ic_coupon")
|
||||
|
||||
Spacer()
|
||||
|
||||
Image("ic_forward")
|
||||
}
|
||||
.padding(.horizontal, 13.3)
|
||||
.padding(.vertical, 20)
|
||||
.background(Color(hex: "13181b"))
|
||||
.cornerRadius(6.7)
|
||||
}
|
||||
}
|
||||
|
||||
struct CanChargeCouponButtonView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
CanChargeCouponButtonView()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// CanCouponNoticeItemView.swift
|
||||
// SodaLive
|
||||
//
|
||||
// Created by klaus on 2024/01/03.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct CanCouponNoticeItemView: View {
|
||||
|
||||
let notice: String
|
||||
|
||||
var body: some View {
|
||||
HStack(alignment: .top, spacing: 4) {
|
||||
Text("-")
|
||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
|
||||
Text(notice)
|
||||
.multilineTextAlignment(.leading)
|
||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
.padding(.horizontal, 10)
|
||||
}
|
||||
}
|
||||
|
||||
struct CanCouponNoticeItemView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
CanCouponNoticeItemView(notice: "본인인증한 계정 1회에 한해 이벤트 쿠폰등록이 적용 됩니다.")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
//
|
||||
// CanCouponView.swift
|
||||
// SodaLive
|
||||
//
|
||||
// Created by klaus on 2024/01/03.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct CanCouponView: View {
|
||||
|
||||
@StateObject var viewModel = CanCouponViewModel()
|
||||
|
||||
var body: some View {
|
||||
BaseView(isLoading: $viewModel.isLoading) {
|
||||
GeometryReader { proxy in
|
||||
VStack(spacing: 0) {
|
||||
DetailNavigationBar(title: "쿠폰등록")
|
||||
|
||||
ScrollView(.vertical, showsIndicators: false) {
|
||||
VStack(spacing: 0) {
|
||||
Text("쿠폰번호 입력")
|
||||
.font(.custom(Font.bold.rawValue, size: 16.7))
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
TextField("쿠폰번호를 입력하세요", text: $viewModel.couponNumber)
|
||||
.autocapitalization(.allCharacters) // Force uppercase keyboard
|
||||
.textContentType(.none)
|
||||
.disableAutocorrection(true)
|
||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
.padding(.vertical, 16.7)
|
||||
.padding(.horizontal, 13.3)
|
||||
.background(Color(hex: "222222"))
|
||||
.cornerRadius(6.7)
|
||||
.keyboardType(.default)
|
||||
.padding(.top, 13.3)
|
||||
.onChange(of: viewModel.couponNumber) { newString in
|
||||
let filtered = newString.filter { $0.isUppercase || $0.isNumber }
|
||||
if filtered != newString {
|
||||
viewModel.couponNumber = filtered
|
||||
}
|
||||
}
|
||||
|
||||
Text("등록하기")
|
||||
.font(.custom(Font.bold.rawValue, size: 18.3))
|
||||
.foregroundColor(.white)
|
||||
.padding(.vertical, 16)
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(Color(hex: "3bb9f1"))
|
||||
.cornerRadius(10)
|
||||
.padding(.top, 21.3)
|
||||
.onTapGesture {
|
||||
viewModel.useCoupon()
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Text("등록안내")
|
||||
.font(.custom(Font.bold.rawValue, size: 13.3))
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
|
||||
CanCouponNoticeItemView(notice: "공백없이 쿠폰번호 16자리를 입력해주세요.")
|
||||
.padding(.top, 8)
|
||||
|
||||
CanCouponNoticeItemView(notice: "입력한 쿠폰은 소멸시까지 취소처리가 되지 않습니다.")
|
||||
.padding(.top, 4)
|
||||
|
||||
CanCouponNoticeItemView(notice: "충전된 캔은 캔 충전현황에서 확인할 수 있습니다.")
|
||||
.padding(.top, 4)
|
||||
|
||||
Rectangle()
|
||||
.foregroundColor(.clear)
|
||||
.frame(maxWidth: .infinity, minHeight: 1, maxHeight: 1)
|
||||
.background(Color(hex: "555555"))
|
||||
.padding(.vertical, 26.7)
|
||||
|
||||
Text("주의사항")
|
||||
.font(.custom(Font.bold.rawValue, size: 13.3))
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
|
||||
CanCouponNoticeItemView(notice: "이벤트 쿠폰을 통해 충전한 캔은 환불되지 않습니다.")
|
||||
.padding(.top, 8)
|
||||
|
||||
CanCouponNoticeItemView(notice: "쿠폰은 상업적 용도로 사용하거나 매매할 수 없습니다.")
|
||||
.padding(.top, 4)
|
||||
|
||||
CanCouponNoticeItemView(notice: "한번 등록한 쿠폰은 재사용이 불가합니다.")
|
||||
.padding(.top, 4)
|
||||
|
||||
CanCouponNoticeItemView(notice: "연령 제한 정책에 따라 쿠폰이용은 본인인증한 회원만 이용 가능합니다.")
|
||||
.padding(.top, 4)
|
||||
}
|
||||
.padding(.horizontal, 13.3)
|
||||
.padding(.vertical, 26.7)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.background(Color(hex: "13181b"))
|
||||
.cornerRadius(6.7)
|
||||
.padding(.top, 21.3)
|
||||
}
|
||||
.padding(13.3)
|
||||
}
|
||||
}
|
||||
.popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .top, autohideIn: 2) {
|
||||
GeometryReader { geo in
|
||||
HStack {
|
||||
Spacer()
|
||||
Text(viewModel.errorMessage)
|
||||
.padding(.vertical, 13.3)
|
||||
.padding(.horizontal, 6.7)
|
||||
.frame(width: geo.size.width - 66.7, alignment: .center)
|
||||
.font(.custom(Font.medium.rawValue, size: 12))
|
||||
.background(Color(hex: "9970ff"))
|
||||
.foregroundColor(Color.white)
|
||||
.multilineTextAlignment(.leading)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.cornerRadius(20)
|
||||
.padding(.top, 66.7)
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CanCouponView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
CanCouponView()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
//
|
||||
// CanCouponViewModel.swift
|
||||
// SodaLive
|
||||
//
|
||||
// Created by klaus on 2024/01/03.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
final class CanCouponViewModel: ObservableObject {
|
||||
|
||||
private let repository = CanRepository()
|
||||
private var subscription = Set<AnyCancellable>()
|
||||
|
||||
@Published var errorMessage = ""
|
||||
@Published var isShowPopup = false
|
||||
@Published var isLoading = false
|
||||
|
||||
@Published var couponNumber = ""
|
||||
|
||||
func useCoupon() {
|
||||
if !isLoading {
|
||||
isLoading = true
|
||||
repository.useCanCoupon(couponNumber: couponNumber)
|
||||
.sink { result in
|
||||
switch result {
|
||||
case .finished:
|
||||
DEBUG_LOG("finish")
|
||||
case .failure(let error):
|
||||
ERROR_LOG(error.localizedDescription)
|
||||
}
|
||||
} receiveValue: { [unowned self] response in
|
||||
self.isLoading = false
|
||||
let responseData = response.data
|
||||
|
||||
do {
|
||||
let jsonDecoder = JSONDecoder()
|
||||
let decoded = try jsonDecoder.decode(ApiResponseWithoutData.self, from: responseData)
|
||||
|
||||
if decoded.success {
|
||||
self.errorMessage = "해당 쿠폰의 캔이 충전되었습니다."
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
AppState.shared.setAppStep(step: .canStatus(refresh: {}))
|
||||
}
|
||||
} else {
|
||||
if let message = decoded.message {
|
||||
self.errorMessage = message
|
||||
} else {
|
||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
}
|
||||
}
|
||||
|
||||
self.isShowPopup = true
|
||||
} catch {
|
||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
self.isShowPopup = true
|
||||
}
|
||||
}
|
||||
.store(in: &subscription)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
//
|
||||
// UseCanCouponRequest.swift
|
||||
// SodaLive
|
||||
//
|
||||
// Created by klaus on 2024/01/03.
|
||||
//
|
||||
|
||||
struct UseCanCouponRequest: Encodable {
|
||||
let couponNumber: String
|
||||
}
|
|
@ -52,9 +52,9 @@ struct CanCardView: View {
|
|||
}
|
||||
}
|
||||
.padding(.horizontal, 13.3)
|
||||
.padding(.vertical, 16.7)
|
||||
.padding(.vertical, 10)
|
||||
.background(Color(hex: "222222"))
|
||||
.cornerRadius(16.7)
|
||||
.cornerRadius(6.7)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -87,6 +87,15 @@ struct MyPageView: View {
|
|||
.frame(width: screenSize().width - 26.7)
|
||||
.padding(.top, 26.7)
|
||||
|
||||
if data.isAuth {
|
||||
CanChargeCouponButtonView()
|
||||
.frame(width: screenSize().width - 26.7)
|
||||
.padding(.top, 13.3)
|
||||
.onTapGesture {
|
||||
AppState.shared.setAppStep(step: .canCoupon)
|
||||
}
|
||||
}
|
||||
|
||||
ReservationStatusView(data: data)
|
||||
.padding(.top, 33.3)
|
||||
|
||||
|
|
|
@ -25,8 +25,8 @@ struct ReservationStatusView: View {
|
|||
.frame(width: 20, height: 20)
|
||||
|
||||
Text("라이브")
|
||||
.font(.custom(Font.medium.rawValue, size: 14.7))
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
.font(.custom(Font.bold.rawValue, size: 14.7))
|
||||
.foregroundColor(Color(hex: "3bb9f1"))
|
||||
|
||||
Text("\(data.liveReservationCount)")
|
||||
.font(.custom(Font.medium.rawValue, size: 14.7))
|
||||
|
|
Loading…
Reference in New Issue