feat: 포인트 내역 UI 추가
This commit is contained in:
		@@ -157,4 +157,6 @@ enum AppStep {
 | 
			
		||||
    case introduceCreatorAll
 | 
			
		||||
    
 | 
			
		||||
    case message
 | 
			
		||||
    
 | 
			
		||||
    case pointStatus(refresh: () -> Void)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -242,6 +242,9 @@ struct ContentView: View {
 | 
			
		||||
            case .message:
 | 
			
		||||
                MessageView()
 | 
			
		||||
                
 | 
			
		||||
            case .pointStatus(let refresh):
 | 
			
		||||
                PointStatusView(refresh: refresh)
 | 
			
		||||
                
 | 
			
		||||
            default:
 | 
			
		||||
                EmptyView()
 | 
			
		||||
                    .frame(width: 0, height: 0, alignment: .topLeading)
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@ struct CanStatusView: View {
 | 
			
		||||
                                    .resizable()
 | 
			
		||||
                                    .frame(width: 26.7, height: 26.7)
 | 
			
		||||
                                
 | 
			
		||||
                                Text("\(viewModel.totalCan) 캔")
 | 
			
		||||
                                Text("\(viewModel.totalCan)")
 | 
			
		||||
                                    .font(.custom(Font.bold.rawValue, size: 18.3))
 | 
			
		||||
                                    .foregroundColor(Color(hex: "eeeeee"))
 | 
			
		||||
                            }
 | 
			
		||||
 
 | 
			
		||||
@@ -124,14 +124,23 @@ struct MyPageView: View {
 | 
			
		||||
                                            .frame(width: screenSize().width - 26.7)
 | 
			
		||||
                                            .padding(.top, 26.7)
 | 
			
		||||
                                            
 | 
			
		||||
                                            HStack(spacing: 6.7) {
 | 
			
		||||
                                                Text("\(data.point)")
 | 
			
		||||
                                                    .font(.custom(Font.bold.rawValue, size: 18.3))
 | 
			
		||||
                                                    .foregroundColor(.grayee)
 | 
			
		||||
                                            HStack {
 | 
			
		||||
                                                HStack(spacing: 6.7) {
 | 
			
		||||
                                                    Text("\(data.point)")
 | 
			
		||||
                                                        .font(.custom(Font.bold.rawValue, size: 18.3))
 | 
			
		||||
                                                        .foregroundColor(.grayee)
 | 
			
		||||
                                                    
 | 
			
		||||
                                                Image("ic_point")
 | 
			
		||||
                                                    .resizable()
 | 
			
		||||
                                                    .frame(width: 20, height: 20)
 | 
			
		||||
                                                    Image("ic_point")
 | 
			
		||||
                                                        .resizable()
 | 
			
		||||
                                                        .frame(width: 20, height: 20)
 | 
			
		||||
                                                    
 | 
			
		||||
                                                    Image("ic_forward")
 | 
			
		||||
                                                        .resizable()
 | 
			
		||||
                                                        .frame(width: 20, height: 20)
 | 
			
		||||
                                                }
 | 
			
		||||
                                                .onTapGesture {
 | 
			
		||||
                                                    AppState.shared.setAppStep(step: .pointStatus(refresh: { viewModel.getMypage() }))
 | 
			
		||||
                                                }
 | 
			
		||||
                                                
 | 
			
		||||
                                                Spacer()
 | 
			
		||||
                                            }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								SodaLive/Sources/MyPage/Point/GetPointStatusResponse.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								SodaLive/Sources/MyPage/Point/GetPointStatusResponse.swift
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
//
 | 
			
		||||
//  GetPointStatusResponse.swift
 | 
			
		||||
//  SodaLive
 | 
			
		||||
//
 | 
			
		||||
//  Created by klaus on 5/20/25.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
struct GetPointStatusResponse: Decodable {
 | 
			
		||||
    let point: Int
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										55
									
								
								SodaLive/Sources/MyPage/Point/PointStatusApi.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								SodaLive/Sources/MyPage/Point/PointStatusApi.swift
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
//
 | 
			
		||||
//  PointStatusApi.swift
 | 
			
		||||
//  SodaLive
 | 
			
		||||
//
 | 
			
		||||
//  Created by klaus on 5/20/25.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
import Foundation
 | 
			
		||||
import Moya
 | 
			
		||||
 | 
			
		||||
enum PointStatusApi {
 | 
			
		||||
    case getPointStatus
 | 
			
		||||
    case getPointRewardStatus
 | 
			
		||||
    case getPointUseStatus
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extension PointStatusApi: TargetType {
 | 
			
		||||
    var baseURL: URL {
 | 
			
		||||
        return URL(string: BASE_URL)!
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    var path: String {
 | 
			
		||||
        switch self {
 | 
			
		||||
        case .getPointStatus:
 | 
			
		||||
            return "/point/status"
 | 
			
		||||
        
 | 
			
		||||
        case .getPointRewardStatus:
 | 
			
		||||
            return "/point/status/reward"
 | 
			
		||||
        
 | 
			
		||||
        case .getPointUseStatus:
 | 
			
		||||
            return "/point/status/use"
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    var method: Moya.Method {
 | 
			
		||||
        return .get
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    var task: Moya.Task {
 | 
			
		||||
        switch self {
 | 
			
		||||
        case .getPointStatus:
 | 
			
		||||
            return .requestPlain
 | 
			
		||||
        
 | 
			
		||||
        case .getPointRewardStatus, .getPointUseStatus:
 | 
			
		||||
            return .requestParameters(
 | 
			
		||||
                parameters: ["timezone": TimeZone.current.identifier],
 | 
			
		||||
                encoding: URLEncoding.queryString
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    var headers: [String : String]? {
 | 
			
		||||
        return ["Authorization": "Bearer \(UserDefaults.string(forKey: UserDefaultsKey.token))"]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								SodaLive/Sources/MyPage/Point/PointStatusRepository.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								SodaLive/Sources/MyPage/Point/PointStatusRepository.swift
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
//
 | 
			
		||||
//  PointStatusRepository.swift
 | 
			
		||||
//  SodaLive
 | 
			
		||||
//
 | 
			
		||||
//  Created by klaus on 5/20/25.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
import Foundation
 | 
			
		||||
import CombineMoya
 | 
			
		||||
import Combine
 | 
			
		||||
import Moya
 | 
			
		||||
 | 
			
		||||
class PointStatusRepository {
 | 
			
		||||
    private let api = MoyaProvider<PointStatusApi>()
 | 
			
		||||
    
 | 
			
		||||
    func getPointStatus() -> AnyPublisher<Response, MoyaError> {
 | 
			
		||||
        return api.requestPublisher(.getPointStatus)
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    func getPointRewardStatus() -> AnyPublisher<Response, MoyaError> {
 | 
			
		||||
        return api.requestPublisher(.getPointRewardStatus)
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    func getPointUseStatus() -> AnyPublisher<Response, MoyaError> {
 | 
			
		||||
        return api.requestPublisher(.getPointUseStatus)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										135
									
								
								SodaLive/Sources/MyPage/Point/PointStatusView.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								SodaLive/Sources/MyPage/Point/PointStatusView.swift
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,135 @@
 | 
			
		||||
//
 | 
			
		||||
//  PointStatusView.swift
 | 
			
		||||
//  SodaLive
 | 
			
		||||
//
 | 
			
		||||
//  Created by klaus on 5/20/25.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
import SwiftUI
 | 
			
		||||
 | 
			
		||||
struct PointStatusView: View {
 | 
			
		||||
    
 | 
			
		||||
    let refresh: () -> Void
 | 
			
		||||
    
 | 
			
		||||
    @StateObject var viewModel = PointStatusViewModel()
 | 
			
		||||
    
 | 
			
		||||
    var body: some View {
 | 
			
		||||
        BaseView(isLoading: $viewModel.isLoading) {
 | 
			
		||||
            GeometryReader { proxy in
 | 
			
		||||
                VStack(spacing: 0) {
 | 
			
		||||
                    DetailNavigationBar(title: "포인트 내역") {
 | 
			
		||||
                        AppState.shared.setAppStep(step: .main)
 | 
			
		||||
                    }
 | 
			
		||||
                    
 | 
			
		||||
                    ScrollView(.vertical, showsIndicators: false) {
 | 
			
		||||
                        VStack(spacing: 26.7) {
 | 
			
		||||
                            HStack(spacing: 6.7) {
 | 
			
		||||
                                Image("ic_point")
 | 
			
		||||
                                    .resizable()
 | 
			
		||||
                                    .frame(width: 26.7, height: 26.7)
 | 
			
		||||
                                
 | 
			
		||||
                                Text("\(viewModel.totalCan)")
 | 
			
		||||
                                    .font(.custom(Font.bold.rawValue, size: 18.3))
 | 
			
		||||
                                    .foregroundColor(.grayee)
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        .padding(.vertical, 13.3)
 | 
			
		||||
                        .frame(maxWidth: .infinity)
 | 
			
		||||
                        .padding(.horizontal, 13.3)
 | 
			
		||||
                        .background(Color.gray22)
 | 
			
		||||
                        .cornerRadius(16.7)
 | 
			
		||||
                        .padding(.top, 13.3)
 | 
			
		||||
                        
 | 
			
		||||
                        HStack(spacing: 0) {
 | 
			
		||||
                            VStack(spacing: 0) {
 | 
			
		||||
                                Spacer()
 | 
			
		||||
                                Text("받은내역")
 | 
			
		||||
                                    .font(.custom(Font.medium.rawValue, size: 13.3))
 | 
			
		||||
                                    .foregroundColor(viewModel.currentTab == .reward ? .grayee : .gray77)
 | 
			
		||||
                                Spacer()
 | 
			
		||||
                                Rectangle()
 | 
			
		||||
                                    .frame(height: 1)
 | 
			
		||||
                                    .foregroundColor(
 | 
			
		||||
                                        .button
 | 
			
		||||
                                            .opacity(viewModel.currentTab == .reward ? 1 : 0)
 | 
			
		||||
                                    )
 | 
			
		||||
                            }
 | 
			
		||||
                            .frame(maxWidth: .infinity)
 | 
			
		||||
                            .frame(height: 50)
 | 
			
		||||
                            .onTapGesture {
 | 
			
		||||
                                if viewModel.currentTab != .reward {
 | 
			
		||||
                                    viewModel.currentTab = .reward
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            
 | 
			
		||||
                            VStack(spacing: 0) {
 | 
			
		||||
                                Spacer()
 | 
			
		||||
                                Text("사용내역")
 | 
			
		||||
                                    .font(.custom(Font.medium.rawValue, size: 13.3))
 | 
			
		||||
                                    .foregroundColor(viewModel.currentTab == .use ? .grayee : .gray77)
 | 
			
		||||
                                Spacer()
 | 
			
		||||
                                Rectangle()
 | 
			
		||||
                                    .frame(height: 1)
 | 
			
		||||
                                    .foregroundColor(
 | 
			
		||||
                                        .button
 | 
			
		||||
                                            .opacity(viewModel.currentTab == .use ? 1 : 0)
 | 
			
		||||
                                    )
 | 
			
		||||
                            }
 | 
			
		||||
                            .frame(maxWidth: .infinity)
 | 
			
		||||
                            .frame(height: 50)
 | 
			
		||||
                            .onTapGesture {
 | 
			
		||||
                                if viewModel.currentTab != .use {
 | 
			
		||||
                                    viewModel.currentTab = .use
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        .padding(.top, 13.3)
 | 
			
		||||
                        
 | 
			
		||||
                        switch viewModel.currentTab {
 | 
			
		||||
                        case .reward:
 | 
			
		||||
                            PointRewardStatusView()
 | 
			
		||||
                            
 | 
			
		||||
                        case .use:
 | 
			
		||||
                            PointUseStatusView()
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    
 | 
			
		||||
                    Spacer()
 | 
			
		||||
                    
 | 
			
		||||
                    if proxy.safeAreaInsets.bottom > 0 {
 | 
			
		||||
                        Rectangle()
 | 
			
		||||
                            .foregroundColor(.black)
 | 
			
		||||
                            .frame(width: proxy.size.width, height: 15.3)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                .edgesIgnoringSafeArea(.bottom)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        .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.button)
 | 
			
		||||
                        .foregroundColor(Color.white)
 | 
			
		||||
                        .multilineTextAlignment(.leading)
 | 
			
		||||
                        .fixedSize(horizontal: false, vertical: true)
 | 
			
		||||
                        .cornerRadius(20)
 | 
			
		||||
                        .padding(.top, 66.7)
 | 
			
		||||
                    Spacer()
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        .onAppear {
 | 
			
		||||
            viewModel.getPointStatus()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#Preview {
 | 
			
		||||
    PointStatusView {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										143
									
								
								SodaLive/Sources/MyPage/Point/PointStatusViewModel.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								SodaLive/Sources/MyPage/Point/PointStatusViewModel.swift
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,143 @@
 | 
			
		||||
//
 | 
			
		||||
//  PointStatusViewModel.swift
 | 
			
		||||
//  SodaLive
 | 
			
		||||
//
 | 
			
		||||
//  Created by klaus on 5/20/25.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
import Foundation
 | 
			
		||||
import Combine
 | 
			
		||||
 | 
			
		||||
final class PointStatusViewModel: ObservableObject {
 | 
			
		||||
    private let repository = PointStatusRepository()
 | 
			
		||||
    private var subscription = Set<AnyCancellable>()
 | 
			
		||||
    
 | 
			
		||||
    @Published var currentTab: CurrentTab = .reward
 | 
			
		||||
    
 | 
			
		||||
    @Published var errorMessage = ""
 | 
			
		||||
    @Published var isShowPopup = false
 | 
			
		||||
    @Published var isLoading = false
 | 
			
		||||
    
 | 
			
		||||
    @Published var totalCan: Int = 0
 | 
			
		||||
    
 | 
			
		||||
    @Published var useStatusItems: [GetPointUseStatusResponse] = []
 | 
			
		||||
    @Published var rewardStatusItems: [GetPointRewardStatusResponse] = []
 | 
			
		||||
    
 | 
			
		||||
    func getPointStatus() {
 | 
			
		||||
        isLoading = true
 | 
			
		||||
        
 | 
			
		||||
        repository.getPointStatus()
 | 
			
		||||
            .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(ApiResponse<GetPointStatusResponse>.self, from: responseData)
 | 
			
		||||
                    
 | 
			
		||||
                    if let data = decoded.data, decoded.success {
 | 
			
		||||
                        self.totalCan = data.point
 | 
			
		||||
                    } 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)
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    func getPointRewardStatus() {
 | 
			
		||||
        isLoading = true
 | 
			
		||||
        
 | 
			
		||||
        repository.getPointRewardStatus()
 | 
			
		||||
            .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(ApiResponse<[GetPointRewardStatusResponse]>.self, from: responseData)
 | 
			
		||||
                    
 | 
			
		||||
                    if let data = decoded.data, decoded.success {
 | 
			
		||||
                        self.rewardStatusItems.append(contentsOf: data)
 | 
			
		||||
                    } 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)
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    func getPointUseStatus() {
 | 
			
		||||
        isLoading = true
 | 
			
		||||
        
 | 
			
		||||
        repository.getPointUseStatus()
 | 
			
		||||
            .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(ApiResponse<[GetPointUseStatusResponse]>.self, from: responseData)
 | 
			
		||||
                    
 | 
			
		||||
                    if let data = decoded.data, decoded.success {
 | 
			
		||||
                        self.useStatusItems.append(contentsOf: data)
 | 
			
		||||
                    } 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)
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    enum CurrentTab: String {
 | 
			
		||||
        case reward, use
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,12 @@
 | 
			
		||||
//
 | 
			
		||||
//  GetPointRewardStatusResponse.swift
 | 
			
		||||
//  SodaLive
 | 
			
		||||
//
 | 
			
		||||
//  Created by klaus on 5/20/25.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
struct GetPointRewardStatusResponse: Decodable, Hashable {
 | 
			
		||||
    let rewardPoint: String
 | 
			
		||||
    let date: String
 | 
			
		||||
    let method: String
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,66 @@
 | 
			
		||||
//
 | 
			
		||||
//  PointRewardStatusView.swift
 | 
			
		||||
//  SodaLive
 | 
			
		||||
//
 | 
			
		||||
//  Created by klaus on 5/20/25.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
import SwiftUI
 | 
			
		||||
 | 
			
		||||
struct PointRewardStatusView: View {
 | 
			
		||||
    
 | 
			
		||||
    @StateObject var viewModel = PointStatusViewModel()
 | 
			
		||||
    
 | 
			
		||||
    var body: some View {
 | 
			
		||||
        ZStack {
 | 
			
		||||
            VStack(spacing: 13.3) {
 | 
			
		||||
                ForEach(viewModel.rewardStatusItems, id: \.self) { item in
 | 
			
		||||
                    PointRewardStatusItemView(item: item)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            .padding(.top, 13.3)
 | 
			
		||||
            
 | 
			
		||||
            if viewModel.isLoading {
 | 
			
		||||
                LoadingView()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        .onAppear {
 | 
			
		||||
            viewModel.getPointRewardStatus()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct PointRewardStatusItemView: View {
 | 
			
		||||
    
 | 
			
		||||
    let item: GetPointRewardStatusResponse
 | 
			
		||||
    
 | 
			
		||||
    var body: some View {
 | 
			
		||||
        HStack(spacing: 0) {
 | 
			
		||||
            VStack(alignment: .leading, spacing: 6.7) {
 | 
			
		||||
                Text(item.rewardPoint)
 | 
			
		||||
                    .font(.custom(Font.medium.rawValue, size: 13.3))
 | 
			
		||||
                    .foregroundColor(.grayee)
 | 
			
		||||
                
 | 
			
		||||
                Text(item.date)
 | 
			
		||||
                    .font(.custom(Font.medium.rawValue, size: 12))
 | 
			
		||||
                    .foregroundColor(.gray77)
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            Spacer()
 | 
			
		||||
            
 | 
			
		||||
            Text(item.method)
 | 
			
		||||
                .font(.custom(Font.medium.rawValue, size: 13.3))
 | 
			
		||||
                .foregroundColor(.grayee)
 | 
			
		||||
        }
 | 
			
		||||
        .padding(.horizontal, 13.3)
 | 
			
		||||
        .padding(.vertical, 16)
 | 
			
		||||
        .background(Color.gray11)
 | 
			
		||||
        .cornerRadius(16.7)
 | 
			
		||||
        .padding(.horizontal, 13.3)
 | 
			
		||||
        .frame(maxWidth: .infinity)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#Preview {
 | 
			
		||||
    PointRewardStatusView()
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,12 @@
 | 
			
		||||
//
 | 
			
		||||
//  GetPointUseStatusResponse.swift
 | 
			
		||||
//  SodaLive
 | 
			
		||||
//
 | 
			
		||||
//  Created by klaus on 5/20/25.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
struct GetPointUseStatusResponse: Decodable, Hashable {
 | 
			
		||||
    let title: String
 | 
			
		||||
    let date: String
 | 
			
		||||
    let point: Int
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										71
									
								
								SodaLive/Sources/MyPage/Point/Use/PointUseStatusView.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								SodaLive/Sources/MyPage/Point/Use/PointUseStatusView.swift
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
//
 | 
			
		||||
//  PointUseStatusView.swift
 | 
			
		||||
//  SodaLive
 | 
			
		||||
//
 | 
			
		||||
//  Created by klaus on 5/20/25.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
import SwiftUI
 | 
			
		||||
 | 
			
		||||
struct PointUseStatusView: View {
 | 
			
		||||
    
 | 
			
		||||
    @StateObject var viewModel = PointStatusViewModel()
 | 
			
		||||
    
 | 
			
		||||
    var body: some View {
 | 
			
		||||
        ZStack {
 | 
			
		||||
            VStack(spacing: 13.3) {
 | 
			
		||||
                ForEach(viewModel.useStatusItems, id: \.self) { item in
 | 
			
		||||
                    PointUseStatusItemView(item: item)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            .padding(.top, 13.3)
 | 
			
		||||
            
 | 
			
		||||
            if viewModel.isLoading {
 | 
			
		||||
                LoadingView()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        .onAppear {
 | 
			
		||||
            viewModel.getPointUseStatus()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct PointUseStatusItemView: View {
 | 
			
		||||
    
 | 
			
		||||
    let item: GetPointUseStatusResponse
 | 
			
		||||
    
 | 
			
		||||
    var body: some View {
 | 
			
		||||
        HStack(spacing: 0) {
 | 
			
		||||
            VStack(alignment: .leading, spacing: 6.7) {
 | 
			
		||||
                Text(item.title)
 | 
			
		||||
                    .font(.custom(Font.medium.rawValue, size: 13.3))
 | 
			
		||||
                    .foregroundColor(.grayee)
 | 
			
		||||
                
 | 
			
		||||
                Text(item.date)
 | 
			
		||||
                    .font(.custom(Font.medium.rawValue, size: 12))
 | 
			
		||||
                    .foregroundColor(.gray77)
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            Spacer()
 | 
			
		||||
            
 | 
			
		||||
            Text("\(item.point)")
 | 
			
		||||
                .font(.custom(Font.medium.rawValue, size: 13.3))
 | 
			
		||||
                .foregroundColor(.grayee)
 | 
			
		||||
            
 | 
			
		||||
            Image("ic_point")
 | 
			
		||||
                .resizable()
 | 
			
		||||
                .frame(width: 26.7, height: 26.7)
 | 
			
		||||
                .padding(.leading, 6.7)
 | 
			
		||||
        }
 | 
			
		||||
        .padding(.horizontal, 13.3)
 | 
			
		||||
        .padding(.vertical, 16)
 | 
			
		||||
        .background(Color.gray11)
 | 
			
		||||
        .cornerRadius(16.7)
 | 
			
		||||
        .padding(.horizontal, 13.3)
 | 
			
		||||
        .frame(maxWidth: .infinity)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#Preview {
 | 
			
		||||
    PointUseStatusView()
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user