쿠폰등록 페이지 추가
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 creatorCommunityWrite(onSuccess: () -> Void)
|
||||||
|
|
||||||
case creatorCommunityModify(postId: Int, onSuccess: () -> Void)
|
case creatorCommunityModify(postId: Int, onSuccess: () -> Void)
|
||||||
|
|
||||||
|
case canCoupon
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,6 +172,9 @@ struct ContentView: View {
|
||||||
case .creatorCommunityModify(let postId, let onSuccess):
|
case .creatorCommunityModify(let postId, let onSuccess):
|
||||||
CreatorCommunityModifyView(postId: postId, onSuccess: onSuccess)
|
CreatorCommunityModifyView(postId: postId, onSuccess: onSuccess)
|
||||||
|
|
||||||
|
case .canCoupon:
|
||||||
|
CanCouponView()
|
||||||
|
|
||||||
default:
|
default:
|
||||||
EmptyView()
|
EmptyView()
|
||||||
.frame(width: 0, height: 0, alignment: .topLeading)
|
.frame(width: 0, height: 0, alignment: .topLeading)
|
||||||
|
|
|
@ -19,6 +19,8 @@ enum CanApi {
|
||||||
|
|
||||||
case pgChargeCan(request: PgChargeRequest)
|
case pgChargeCan(request: PgChargeRequest)
|
||||||
case pgVerify(request: PgVerifyRequest)
|
case pgVerify(request: PgVerifyRequest)
|
||||||
|
|
||||||
|
case useCanCoupon(request: UseCanCouponRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
extension CanApi: TargetType {
|
extension CanApi: TargetType {
|
||||||
|
@ -51,6 +53,9 @@ extension CanApi: TargetType {
|
||||||
|
|
||||||
case .pgVerify:
|
case .pgVerify:
|
||||||
return "/charge/verify"
|
return "/charge/verify"
|
||||||
|
|
||||||
|
case .useCanCoupon:
|
||||||
|
return "/can/coupon/use"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +64,7 @@ extension CanApi: TargetType {
|
||||||
case .getCanStatus, .getCanChargeStatus, .getCanUseStatus, .getCans:
|
case .getCanStatus, .getCanChargeStatus, .getCanUseStatus, .getCans:
|
||||||
return .get
|
return .get
|
||||||
|
|
||||||
case .chargeCan, .verify, .pgChargeCan, .pgVerify:
|
case .chargeCan, .verify, .pgChargeCan, .pgVerify, .useCanCoupon:
|
||||||
return .post
|
return .post
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,6 +102,9 @@ extension CanApi: TargetType {
|
||||||
|
|
||||||
case .pgVerify(let request):
|
case .pgVerify(let request):
|
||||||
return .requestJSONEncodable(request)
|
return .requestJSONEncodable(request)
|
||||||
|
|
||||||
|
case .useCanCoupon(let request):
|
||||||
|
return .requestJSONEncodable(request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,5 +44,10 @@ final class CanRepository {
|
||||||
func getCans() -> AnyPublisher<Response, MoyaError> {
|
func getCans() -> AnyPublisher<Response, MoyaError> {
|
||||||
return api.requestPublisher(.getCans)
|
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(.horizontal, 13.3)
|
||||||
.padding(.vertical, 16.7)
|
.padding(.vertical, 10)
|
||||||
.background(Color(hex: "222222"))
|
.background(Color(hex: "222222"))
|
||||||
.cornerRadius(16.7)
|
.cornerRadius(6.7)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -87,6 +87,15 @@ struct MyPageView: View {
|
||||||
.frame(width: screenSize().width - 26.7)
|
.frame(width: screenSize().width - 26.7)
|
||||||
.padding(.top, 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)
|
ReservationStatusView(data: data)
|
||||||
.padding(.top, 33.3)
|
.padding(.top, 33.3)
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,8 @@ struct ReservationStatusView: View {
|
||||||
.frame(width: 20, height: 20)
|
.frame(width: 20, height: 20)
|
||||||
|
|
||||||
Text("라이브")
|
Text("라이브")
|
||||||
.font(.custom(Font.medium.rawValue, size: 14.7))
|
.font(.custom(Font.bold.rawValue, size: 14.7))
|
||||||
.foregroundColor(Color(hex: "eeeeee"))
|
.foregroundColor(Color(hex: "3bb9f1"))
|
||||||
|
|
||||||
Text("\(data.liveReservationCount)")
|
Text("\(data.liveReservationCount)")
|
||||||
.font(.custom(Font.medium.rawValue, size: 14.7))
|
.font(.custom(Font.medium.rawValue, size: 14.7))
|
||||||
|
|
Loading…
Reference in New Issue