From 91fb4d7693934e4df5baed1a0aba738add584498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Fri, 10 Oct 2025 02:12:34 +0900 Subject: [PATCH 01/85] =?UTF-8?q?[FEAT]=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- StockMate/.DS_Store | Bin 6148 -> 6148 bytes StockMate/StockMate.xcodeproj/project.pbxproj | 34 ++- .../UserInterfaceState.xcuserstate | Bin 20271 -> 132382 bytes .../xcdebugger/Breakpoints_v2.xcbkptlist | 6 + StockMate/StockMate/app/StockMateApp.swift | 5 +- .../StockMate/app/core/common/AppResult.swift | 19 ++ .../StockMate/app/core/common/SafeApi.swift | 64 +++++ .../app/core/common/Validators.swift | 24 ++ .../core/components/CustomSecureField.swift | 72 ++++++ .../app/core/components/CustomTextField.swift | 69 ++++++ .../app/core/components/PrimaryButton.swift | 18 ++ .../app/core/components/TopToast.swift | 45 ++++ .../app/core/network/ApiClient.swift | 21 ++ .../app/core/network/ApiResponse.swift | 15 ++ .../app/core/network/AuthInterceptor.swift | 23 ++ .../StockMate/app/feature/auth/data/.gitkeep | 0 .../app/feature/auth/data/AuthApi.swift | 52 +++++ .../auth/data/AuthRepositoryImpl.swift | 32 +++ .../feature/auth/data/KeychainHelper.swift | 63 +++++ .../app/feature/auth/data/TokenStore.swift | 42 ++++ .../app/feature/auth/domain/.gitkeep | 0 .../auth/domain/AuthRepositoryProtocol.swift | 14 ++ .../StockMate/app/feature/auth/ui/.gitkeep | 0 .../app/feature/auth/ui/HomeView.swift | 15 ++ .../app/feature/auth/ui/LoginView.swift | 114 +++++++++ .../app/feature/auth/ui/RegisterView.swift | 220 ++++++++++++++++++ .../app/feature/auth/viewmodel/.gitkeep | 0 .../auth/viewmodel/AuthViewModel.swift | 113 +++++++++ .../StockMate/app/navigation/AppNavHost.swift | 29 +++ .../app/navigation/MainTabView.swift | 40 ++++ .../stockmate_logo.imageset/Contents.json | 21 ++ .../Group 148272 (2).png | Bin 0 -> 48990 bytes .../stockmate_logo1.imageset/Contents.json | 21 ++ .../stockmate_logo.png | Bin 0 -> 10210 bytes StockMate/StockMate/resources/Color.swift | 47 ++++ StockMate/StockMate/resources/ColorHex.swift | 41 ++++ 36 files changed, 1276 insertions(+), 3 deletions(-) create mode 100644 StockMate/StockMate.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist create mode 100644 StockMate/StockMate/app/core/common/AppResult.swift create mode 100644 StockMate/StockMate/app/core/common/SafeApi.swift create mode 100644 StockMate/StockMate/app/core/common/Validators.swift create mode 100644 StockMate/StockMate/app/core/components/CustomSecureField.swift create mode 100644 StockMate/StockMate/app/core/components/CustomTextField.swift create mode 100644 StockMate/StockMate/app/core/components/PrimaryButton.swift create mode 100644 StockMate/StockMate/app/core/components/TopToast.swift create mode 100644 StockMate/StockMate/app/core/network/ApiClient.swift create mode 100644 StockMate/StockMate/app/core/network/ApiResponse.swift create mode 100644 StockMate/StockMate/app/core/network/AuthInterceptor.swift delete mode 100644 StockMate/StockMate/app/feature/auth/data/.gitkeep create mode 100644 StockMate/StockMate/app/feature/auth/data/AuthApi.swift create mode 100644 StockMate/StockMate/app/feature/auth/data/AuthRepositoryImpl.swift create mode 100644 StockMate/StockMate/app/feature/auth/data/KeychainHelper.swift create mode 100644 StockMate/StockMate/app/feature/auth/data/TokenStore.swift delete mode 100644 StockMate/StockMate/app/feature/auth/domain/.gitkeep create mode 100644 StockMate/StockMate/app/feature/auth/domain/AuthRepositoryProtocol.swift delete mode 100644 StockMate/StockMate/app/feature/auth/ui/.gitkeep create mode 100644 StockMate/StockMate/app/feature/auth/ui/HomeView.swift create mode 100644 StockMate/StockMate/app/feature/auth/ui/LoginView.swift create mode 100644 StockMate/StockMate/app/feature/auth/ui/RegisterView.swift delete mode 100644 StockMate/StockMate/app/feature/auth/viewmodel/.gitkeep create mode 100644 StockMate/StockMate/app/feature/auth/viewmodel/AuthViewModel.swift create mode 100644 StockMate/StockMate/app/navigation/AppNavHost.swift create mode 100644 StockMate/StockMate/app/navigation/MainTabView.swift create mode 100644 StockMate/StockMate/resources/Assets.xcassets/stockmate_logo.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/stockmate_logo.imageset/Group 148272 (2).png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/stockmate_logo1.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/stockmate_logo1.imageset/stockmate_logo.png create mode 100644 StockMate/StockMate/resources/Color.swift create mode 100644 StockMate/StockMate/resources/ColorHex.swift diff --git a/StockMate/.DS_Store b/StockMate/.DS_Store index df0cfd3031f58ba1d387503f3a7e39fe991e53a0..fb8127ddba0823b381ba269cb4cbe0f2eeba83ad 100644 GIT binary patch delta 76 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 + + diff --git a/StockMate/StockMate/app/StockMateApp.swift b/StockMate/StockMate/app/StockMateApp.swift index 5b8644e..d9d9848 100644 --- a/StockMate/StockMate/app/StockMateApp.swift +++ b/StockMate/StockMate/app/StockMateApp.swift @@ -9,9 +9,12 @@ import SwiftUI @main struct StockMateApp: App { + @StateObject private var authViewModel = AuthViewModel() + var body: some Scene { WindowGroup { - ContentView() + AppNavHost() + .environmentObject(authViewModel) } } } diff --git a/StockMate/StockMate/app/core/common/AppResult.swift b/StockMate/StockMate/app/core/common/AppResult.swift new file mode 100644 index 0000000..848ff05 --- /dev/null +++ b/StockMate/StockMate/app/core/common/AppResult.swift @@ -0,0 +1,19 @@ +// +// AppResult.swift +// StockMate +// +// Created by Admin on 10/7/25. +// + +import Foundation + +enum AppResult { + case success(T) + case failure(AppError) +} + +struct AppError: Error { + let code: Int? + let message: String + let underlying: Error? +} diff --git a/StockMate/StockMate/app/core/common/SafeApi.swift b/StockMate/StockMate/app/core/common/SafeApi.swift new file mode 100644 index 0000000..0588f9c --- /dev/null +++ b/StockMate/StockMate/app/core/common/SafeApi.swift @@ -0,0 +1,64 @@ +// +// SafeApi.swift +// StockMate +// +// Created by Admin on 10/7/25. +// + +import Foundation +import Alamofire + +func httpCodeToMessage(_ code: Int?) -> String { + guard let code = code else { return "알 수 없는 오류가 발생했어요." } + switch code { + case 400: return "요청 형식을 다시 확인해주세요." + case 401: return "로그인이 필요해요. 다시 로그인해주세요." + case 403: return "접근 권한이 없어요." + case 404: return "요청하신 정보를 찾을 수 없어요." + case 408: return "요청 시간이 초과되었어요. 다시 시도해주세요." + case 409: return "이미 존재하거나 충돌이 발생했어요." + case 422: return "입력값을 다시 확인해주세요." + case 429: return "요청이 많아요. 잠시 후 다시 시도해주세요." + case 500: return "서버 내부 오류가 발생했어요." + case 502: return "게이트웨이 오류가 발생했어요." + case 503: return "현재 서버가 점검 중이에요. 잠시 후 다시 시도해주세요." + case 504: return "서버 응답 시간이 초과되었어요." + case 500...599: return "서버 오류가 발생했어요. 잠시 후 다시 시도해주세요." + default: return "문제가 발생했어요. 잠시 후 다시 시도해주세요." + } +} + +@discardableResult +func safeApi(_ request: DataRequest, decodeTo: T.Type) async -> AppResult { + do { + let response = try await request.serializingDecodable(T.self).response + if let status = response.response?.statusCode { + if (200..<300).contains(status), let value = response.value { + return .success(value) + } else { + // 서버가 에러 body에 ApiResponse.message 를 넣어주는 경우 파싱 시도 + if let data = response.data, + let serverErr = try? JSONDecoder().decode(ApiResponse.self, from: data), + !serverErr.message.isEmpty { + return .failure(AppError(code: status, message: serverErr.message, underlying: nil)) + } + return .failure(AppError(code: status, message: httpCodeToMessage(status), underlying: nil)) + } + } else { + return .failure(AppError(code: nil, message: "응답이 유효하지 않습니다.", underlying: nil)) + } + } catch let afError as AFError { + // URLError 기반 메시지 분기 (UnknownHost / Timeout 등) + if let urlErr = afError.underlyingError as? URLError { + switch urlErr.code { + case .notConnectedToInternet: return .failure(AppError(code: nil, message: "인터넷 연결을 확인해주세요.", underlying: afError)) + case .timedOut: return .failure(AppError(code: nil, message: "응답이 지연되고 있어요. 잠시 후 다시 시도해주세요.", underlying: afError)) + default: return .failure(AppError(code: nil, message: afError.errorDescription ?? "네트워크 오류가 발생했어요.", underlying: afError)) + } + } + return .failure(AppError(code: nil, message: afError.errorDescription ?? "네트워크 오류가 발생했어요.", underlying: afError)) + } catch { + return .failure(AppError(code: nil, message: "알 수 없는 오류가 발생했어요.", underlying: error)) + } +} + diff --git a/StockMate/StockMate/app/core/common/Validators.swift b/StockMate/StockMate/app/core/common/Validators.swift new file mode 100644 index 0000000..1b4b4d3 --- /dev/null +++ b/StockMate/StockMate/app/core/common/Validators.swift @@ -0,0 +1,24 @@ +// +// Validators.swift +// StockMate +// +// Created by Admin on 10/7/25. +// + +import Foundation + +func isValidEmail(_ email: String) -> Bool { + let regex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}" + return NSPredicate(format: "SELF MATCHES %@", regex).evaluate(with: email) +} + +func isValidPassword(_ pw: String) -> Bool { + guard pw.count >= 8 else { return false } + return pw.range(of: "[A-Za-z]", options: .regularExpression) != nil && + pw.range(of: "[0-9]", options: .regularExpression) != nil +} + +func isValidBizNo(_ no: String) -> Bool { + let pattern = #"^\d{3}-\d{2}-\d{5}$"# + return no.range(of: pattern, options: .regularExpression) != nil +} diff --git a/StockMate/StockMate/app/core/components/CustomSecureField.swift b/StockMate/StockMate/app/core/components/CustomSecureField.swift new file mode 100644 index 0000000..71da03e --- /dev/null +++ b/StockMate/StockMate/app/core/components/CustomSecureField.swift @@ -0,0 +1,72 @@ +// +// CustomSecureField.swift +// StockMate +// +// Created by Admin on 10/9/25. +// + +import SwiftUI + +struct CustomSecureField: View { + var title: String + var placeholder: String + @Binding var text: String + var errorMessage: String? = nil + @FocusState private var isFocused: Bool + @State private var showPassword = false + + var body: some View { + VStack(alignment: .leading, spacing: 6) { + // 필드 제목 + Text(title) + .font(.system(size: 15, weight: .semibold)) + .foregroundColor(.black) + + // 텍스트 입력 박스 + ZStack { + RoundedRectangle(cornerRadius: 8) + .strokeBorder( + isFocused ? Color.Primary : + (errorMessage == nil ? Color.LightBlue04 : .red), + lineWidth: 1 + ) + .background(RoundedRectangle(cornerRadius: 8).fill(Color.white)) + // 포커스일 때만 그림자 표시 + .shadow( + color: isFocused + ? Color(hex: "#333333").opacity(0.1) + : Color.clear, + radius: 1, + x: 0, + y: 2 + ) + + HStack { + if showPassword { + TextField(placeholder, text: $text) + } else { + SecureField(placeholder, text: $text) + } + + Spacer() + + Button(action: { showPassword.toggle() }) { + Image(systemName: showPassword ? "eye.slash" : "eye") + .foregroundColor(.gray) + } + } + .focused($isFocused) + .padding(.horizontal, 12) + .padding(.vertical, 8) + .textInputAutocapitalization(.never) + .autocorrectionDisabled() + } + .frame(height: 46) + + Text(errorMessage ?? " ") + .font(.caption) + .foregroundColor(.red) + .frame(height: 14) // 고정 높이 확보 + } + } +} diff --git a/StockMate/StockMate/app/core/components/CustomTextField.swift b/StockMate/StockMate/app/core/components/CustomTextField.swift new file mode 100644 index 0000000..62eedc3 --- /dev/null +++ b/StockMate/StockMate/app/core/components/CustomTextField.swift @@ -0,0 +1,69 @@ +// +// CustomTextField.swift +// StockMate +// +// Created by Admin on 10/9/25. +// + +import SwiftUI + +struct CustomTextField: View { + var title: String + var placeholder: String + @Binding var text: String + var isEmail: Bool = false + var errorMessage: String? = nil + + @FocusState private var isFocused: Bool + + var body: some View { + VStack(alignment: .leading, spacing: 6) { + Text(title) + .font(.system(size: 15, weight: .semibold)) + .foregroundColor(.black) + + ZStack { + RoundedRectangle(cornerRadius: 8) + .strokeBorder( + isFocused + ? Color.Primary + : (errorMessage == nil ? Color.LightBlue04 : .red), + lineWidth: 1 + ) + .background( + RoundedRectangle(cornerRadius: 8) + .fill(Color.white) + ) + // 포커스일 때만 그림자 표시 + .shadow( + color: isFocused + ? Color(hex: "#333333").opacity(0.1) + : Color.clear, + radius: 1, + x: 0, + y: 2 + ) + + TextField(placeholder, text: $text) + .focused($isFocused) + .padding(.horizontal, 14) + .padding(.vertical, 12) + .textInputAutocapitalization(.never) + .autocorrectionDisabled() + + } + .frame(height: 46) // 높이 일정하게 고정 + + Text(errorMessage ?? " ") + .font(.caption) + .foregroundColor(.red) + .frame(height: 14) // 고정 높이 확보 + .opacity(errorMessage == nil ? 0 : 1) // 없을 땐 투명 +// if let errorMessage = errorMessage { +// Text(errorMessage) +// .font(.caption) +// .foregroundColor(.red) +// } + } + } +} diff --git a/StockMate/StockMate/app/core/components/PrimaryButton.swift b/StockMate/StockMate/app/core/components/PrimaryButton.swift new file mode 100644 index 0000000..b8f165f --- /dev/null +++ b/StockMate/StockMate/app/core/components/PrimaryButton.swift @@ -0,0 +1,18 @@ +// +// PrimaryButton.swift +// StockMate +// +// Created by Admin on 10/10/25. +// + +import SwiftUI + +struct PrimaryButton: View { + var body: some View { + Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + } +} + +#Preview { + PrimaryButton() +} diff --git a/StockMate/StockMate/app/core/components/TopToast.swift b/StockMate/StockMate/app/core/components/TopToast.swift new file mode 100644 index 0000000..1e61918 --- /dev/null +++ b/StockMate/StockMate/app/core/components/TopToast.swift @@ -0,0 +1,45 @@ +// +// TopToast.swift +// StockMate +// +// Created by Admin on 10/10/25. +// + +import SwiftUI + +struct TopToast: View { + let message: String + @Binding var isVisible: Bool + var duration: Double = 2.2 + + var body: some View { + if isVisible { + VStack { + HStack(spacing: 8) { + Image(systemName: "exclamationmark.triangle.fill") + .foregroundColor(.white) + Text(message) + .foregroundColor(.white) + .font(.system(size: 14, weight: .medium)) + } + .padding() + .background(Color.red.opacity(0.9)) + .cornerRadius(12) + .shadow(radius: 5) + .padding(.horizontal, 16) + .padding(.top, 8) + + Spacer() + } + .transition(.move(edge: .top).combined(with: .opacity)) + .animation(.easeInOut(duration: 0.25), value: isVisible) + .onAppear { + DispatchQueue.main.asyncAfter(deadline: .now() + duration) { + withAnimation { + isVisible = false + } + } + } + } + } +} diff --git a/StockMate/StockMate/app/core/network/ApiClient.swift b/StockMate/StockMate/app/core/network/ApiClient.swift new file mode 100644 index 0000000..94307e0 --- /dev/null +++ b/StockMate/StockMate/app/core/network/ApiClient.swift @@ -0,0 +1,21 @@ +// +// ApiClient.swift +// StockMate +// +// Created by Admin on 10/7/25. +// + +import Foundation +import Alamofire + +struct ApiClient { + static let baseURL = "https://api.stockmate.site/" + static let shared: Session = { + let interceptor = AuthInterceptor() + let config = URLSessionConfiguration.default + config.timeoutIntervalForRequest = 20 + config.timeoutIntervalForResource = 20 + return Session(configuration: config, interceptor: interceptor, eventMonitors: []) + }() +} + diff --git a/StockMate/StockMate/app/core/network/ApiResponse.swift b/StockMate/StockMate/app/core/network/ApiResponse.swift new file mode 100644 index 0000000..fbc4ceb --- /dev/null +++ b/StockMate/StockMate/app/core/network/ApiResponse.swift @@ -0,0 +1,15 @@ +// +// ApiResponse.swift +// StockMate +// +// Created by Admin on 10/7/25. +// + +import Foundation + +struct ApiResponse: Decodable { + let status: Int + let success: Bool + let message: String + let data: T? +} diff --git a/StockMate/StockMate/app/core/network/AuthInterceptor.swift b/StockMate/StockMate/app/core/network/AuthInterceptor.swift new file mode 100644 index 0000000..4a94468 --- /dev/null +++ b/StockMate/StockMate/app/core/network/AuthInterceptor.swift @@ -0,0 +1,23 @@ +// +// AuthInterceptor.swift +// StockMate +// +// Created by Admin on 10/7/25. +// + +import Foundation +import Alamofire + +final class AuthInterceptor: RequestInterceptor, @unchecked Sendable { + private let tokenStore = TokenStore.shared + + func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) { + var req = urlRequest + if let token = tokenStore.getAccessToken() { + req.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") + } + completion(.success(req)) + } + + // 필요 시 retry(_:for:dueTo:completion:) 구현해서 401 -> refresh token 흐름 처리 가능 +} diff --git a/StockMate/StockMate/app/feature/auth/data/.gitkeep b/StockMate/StockMate/app/feature/auth/data/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/StockMate/StockMate/app/feature/auth/data/AuthApi.swift b/StockMate/StockMate/app/feature/auth/data/AuthApi.swift new file mode 100644 index 0000000..d8e4840 --- /dev/null +++ b/StockMate/StockMate/app/feature/auth/data/AuthApi.swift @@ -0,0 +1,52 @@ +// +// AuthApi.swift +// StockMate +// +// Created by Admin on 10/7/25. +// + +import Foundation +import Alamofire + +struct RegisterRequest: Encodable { + let email: String + let password: String + let owner: String + let address: String + let storeName: String + let businessNumber: String +} +struct LoginRequest: Encodable { + let email: String + let password: String +} + +struct RegisterResponse: Decodable { + let status: Int + let success: Bool + let message: String +} + +struct LoginData: Decodable { + let accessToken: String + let refreshToken: String + let role: String +} +struct LoginResponse: Decodable { + let status: Int + let success: Bool + let message: String + let data: LoginData? +} + +enum AuthApi { + static func register(_ req: RegisterRequest) -> DataRequest { + let url = ApiClient.baseURL + "api/v1/auth/register" + return ApiClient.shared.request(url, method: .post, parameters: req, encoder: JSONParameterEncoder.default) + } + + static func login(_ req: LoginRequest) -> DataRequest { + let url = ApiClient.baseURL + "api/v1/auth/login" + return ApiClient.shared.request(url, method: .post, parameters: req, encoder: JSONParameterEncoder.default) + } +} diff --git a/StockMate/StockMate/app/feature/auth/data/AuthRepositoryImpl.swift b/StockMate/StockMate/app/feature/auth/data/AuthRepositoryImpl.swift new file mode 100644 index 0000000..c809339 --- /dev/null +++ b/StockMate/StockMate/app/feature/auth/data/AuthRepositoryImpl.swift @@ -0,0 +1,32 @@ +// +// AuthRepositoryImpl.swift +// StockMate +// +// Created by Admin on 10/7/25. +// + +import Foundation +import Alamofire + +//final class AuthRepositoryImpl: AuthRepositoryProtocol { +// func register(_ req: RegisterRequest) async -> AppResult> { +// let dataReq = AuthApi.register(req) +// return await safeApi(dataReq, decodeTo: ApiResponse.self) +// } +// +// func login(_ req: LoginRequest) async -> AppResult> { +// let dataReq = AuthApi.login(req) +// return await safeApi(dataReq, decodeTo: ApiResponse.self) +// } +//} +final class AuthRepositoryImpl: AuthRepositoryProtocol { + func register(_ req: RegisterRequest) async -> AppResult> { + let dataReq = AuthApi.register(req) + return await safeApi(dataReq, decodeTo: ApiResponse.self) + } + + func login(_ req: LoginRequest) async -> AppResult> { + let dataReq = AuthApi.login(req) + return await safeApi(dataReq, decodeTo: ApiResponse.self) + } +} diff --git a/StockMate/StockMate/app/feature/auth/data/KeychainHelper.swift b/StockMate/StockMate/app/feature/auth/data/KeychainHelper.swift new file mode 100644 index 0000000..7f972fb --- /dev/null +++ b/StockMate/StockMate/app/feature/auth/data/KeychainHelper.swift @@ -0,0 +1,63 @@ +// +// KeychainHelper.swift +// StockMate +// +// Created by Admin on 10/7/25. +// +import Foundation +import Security + +final class KeychainHelper { + + static let standard = KeychainHelper() + private init() {} + + // MARK: - Save + func save(_ value: String, service: String, account: String) { + guard let data = value.data(using: .utf8) else { return } + + // 기존 값이 있으면 먼저 삭제 + delete(service: service, account: account) + + let query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrService as String: service, + kSecAttrAccount as String: account, + kSecValueData as String: data + ] + + SecItemAdd(query as CFDictionary, nil) + } + + // MARK: - Read + func read(service: String, account: String) -> String? { + let query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrService as String: service, + kSecAttrAccount as String: account, + kSecReturnData as String: true, + kSecMatchLimit as String: kSecMatchLimitOne + ] + + var dataTypeRef: AnyObject? + let status = SecItemCopyMatching(query as CFDictionary, &dataTypeRef) + + guard status == errSecSuccess, + let data = dataTypeRef as? Data, + let value = String(data: data, encoding: .utf8) else { + return nil + } + return value + } + + // MARK: - Delete + func delete(service: String, account: String) { + let query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrService as String: service, + kSecAttrAccount as String: account + ] + SecItemDelete(query as CFDictionary) + } +} + diff --git a/StockMate/StockMate/app/feature/auth/data/TokenStore.swift b/StockMate/StockMate/app/feature/auth/data/TokenStore.swift new file mode 100644 index 0000000..6a7e353 --- /dev/null +++ b/StockMate/StockMate/app/feature/auth/data/TokenStore.swift @@ -0,0 +1,42 @@ +// +// TokenStore.swift +// StockMate +// +// Created by Admin on 10/7/25. +// + +import Foundation +import Security + +final class TokenStore: @unchecked Sendable { + static let shared = TokenStore() + private init() {} + + private let defaults = UserDefaults.standard + + func save(access: String, refresh: String, role: String) { + defaults.set(role, forKey: "role") + // access/refresh는 Keychain에 저장하는 걸 권장 + KeychainHelper.standard.save(access, service: "com.stockmate", account: "accessToken") + KeychainHelper.standard.save(refresh, service: "com.stockmate", account: "refreshToken") + } + + func clear() { + defaults.removeObject(forKey: "role") + KeychainHelper.standard.delete(service: "com.stockmate", account: "accessToken") + KeychainHelper.standard.delete(service: "com.stockmate", account: "refreshToken") + } + + func getAccessToken() -> String? { + return KeychainHelper.standard.read(service: "com.stockmate", account: "accessToken") + } + + func getRefreshToken() -> String? { + return KeychainHelper.standard.read(service: "com.stockmate", account: "refreshToken") + } + + func getRole() -> String? { + return defaults.string(forKey: "role") + } +} + diff --git a/StockMate/StockMate/app/feature/auth/domain/.gitkeep b/StockMate/StockMate/app/feature/auth/domain/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/StockMate/StockMate/app/feature/auth/domain/AuthRepositoryProtocol.swift b/StockMate/StockMate/app/feature/auth/domain/AuthRepositoryProtocol.swift new file mode 100644 index 0000000..6691965 --- /dev/null +++ b/StockMate/StockMate/app/feature/auth/domain/AuthRepositoryProtocol.swift @@ -0,0 +1,14 @@ +// +// AuthRepositoryProtocol.swift +// StockMate +// +// Created by Admin on 10/7/25. +// + +import Foundation +import Alamofire + +protocol AuthRepositoryProtocol { + func register(_ req: RegisterRequest) async -> AppResult> + func login(_ req: LoginRequest) async -> AppResult> +} diff --git a/StockMate/StockMate/app/feature/auth/ui/.gitkeep b/StockMate/StockMate/app/feature/auth/ui/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift new file mode 100644 index 0000000..4e891b9 --- /dev/null +++ b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift @@ -0,0 +1,15 @@ +// +// HomeView.swift +// StockMate +// +// Created by Admin on 10/7/25. +// + +import SwiftUI + + +struct HomeView: View { + var body: some View { + Text("홈 화면") + } +} diff --git a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift new file mode 100644 index 0000000..ecc0b9b --- /dev/null +++ b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift @@ -0,0 +1,114 @@ +// +// LoginView.swift +// StockMate +// +// Created by Admin on 10/7/25. +// + + +import SwiftUI + +struct LoginView: View { + @EnvironmentObject var authViewModel: AuthViewModel + + @State private var email = "" + @State private var password = "" + @State private var showPassword = false + + @State private var emailError: String? = nil + @State private var pwError: String? = nil + +// var onLoginSuccess: () -> Void = {} + var onLogin: (String, String) -> Void = { _, _ in } + var onClickRegister: () -> Void = {} + + var body: some View { + VStack { + Spacer().frame(height: 140) + + // MARK: - Logo + Image("stockmate_logo") + .resizable() + .scaledToFit() + .frame(width: 216, height: 44) + + Spacer().frame(height: 67) + + // MARK: - Title + Text("로그인") + .font(.system(size: 28, weight: .bold)) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 24) + .foregroundColor(Color.DarkBlue01) + + Spacer().frame(height: 17) + + // MARK: - Text Fields + CustomTextField( + title: "이메일", + placeholder: "stockmate@gmail.com", + text: $authViewModel.email, + isEmail: true, + errorMessage: emailError + ) + .keyboardType(.emailAddress) + .padding(.horizontal, 24) + + Spacer().frame(height: 18) + + // 비밀번호 + CustomSecureField( + title: "비밀번호", + placeholder: "비밀번호를 입력하세요", + text: $authViewModel.password, + errorMessage: pwError + ) + .padding(.horizontal, 24) + + Spacer().frame(height: 56) + + // MARK: - Login Button + Button(action: { + if isValidForm() { + Task { + await authViewModel.login() + } + } + }) { + Text("로그인") + .font(.system(size: 16, weight: .semibold)) + .foregroundColor(.white) + .frame(maxWidth: .infinity) + .frame(height: 42) + .background(Color.Primary) + .cornerRadius(4) + } + .padding(.horizontal, 24) + + Spacer().frame(height: 24) + + // 회원가입 링크 + HStack { + Text("계정이 없으신가요?") + .foregroundColor(Color.Secondary) + .font(.system(size: 13)) + Button(action: { onClickRegister() }) { + Text("회원가입") + .font(.system(size: 13, weight: .bold)) + .foregroundColor(Color.Secondary) + } + } + Spacer() + } + .background(Color.Light) + .ignoresSafeArea() + } + + // MARK: - 유효성 검사 함수 + private func isValidForm() -> Bool { + emailError = isValidEmail(authViewModel.email) ? nil : "이메일 형식을 확인해주세요" + pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" + return emailError == nil && pwError == nil + } + +} diff --git a/StockMate/StockMate/app/feature/auth/ui/RegisterView.swift b/StockMate/StockMate/app/feature/auth/ui/RegisterView.swift new file mode 100644 index 0000000..7656f2c --- /dev/null +++ b/StockMate/StockMate/app/feature/auth/ui/RegisterView.swift @@ -0,0 +1,220 @@ +// +// RegisterView.swift +// StockMate +// +// Created by Admin on 10/7/25. +// + +import SwiftUI + +struct RegisterView: View { + @EnvironmentObject private var viewModel: AuthViewModel + + @State private var email = "" + @State private var password = "" + @State private var confirmPassword = "" + @State private var owner = "" + @State private var storeName = "" + @State private var address = "" + @State private var bizNo = "" + + // 에러 메시지 상태 + @State private var emailError: String? = nil + @State private var pwError: String? = nil + @State private var confirmPasswordError: String? = nil + @State private var ownerError: String? = nil + @State private var storeNameError: String? = nil + @State private var addressError: String? = nil + @State private var bizNoError: String? = nil + + @State private var isLoading = false + @State private var showToast = false + + var body: some View { + ScrollView { + VStack(spacing: 16) { + Spacer().frame(height: 70) + + // MARK: - Logo + Image("stockmate_logo") + .resizable() + .scaledToFit() + .frame(width: 216, height: 44) + + Spacer().frame(height: 4) + + // MARK: - Title + Text("회원가입") + .font(.system(size: 28, weight: .bold)) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 24) + .foregroundColor(Color.DarkBlue01) + + // MARK: - Text Fields + VStack { + CustomTextField( + title: "이메일", + placeholder: "stockmate@gmail.com", + text: $email, + errorMessage: emailError + ) + .keyboardType(.emailAddress) + CustomSecureField( + title: "비밀번호", + placeholder: "비밀번호를 입력하세요", + text: $password, + errorMessage: pwError + ) + CustomSecureField( + title: "비밀번호 확인", + placeholder: "비밀번호를 다시 입력하세요", + text: $confirmPassword, + errorMessage: confirmPasswordError + ) + CustomTextField( + title: "대표자 이름", + placeholder: "홍길동", + text: $owner, + errorMessage: ownerError + ) + CustomTextField( + title: "지점 이름", + placeholder: "서울 1호점", + text: $storeName, + errorMessage: storeNameError + ) + CustomTextField( + title: "주소", + placeholder: "서울특별시 강남구 ...", + text: $address, + errorMessage: addressError + ) + CustomTextField( + title: "사업자등록번호", + placeholder: "123-45-67890", + text: $bizNo, + errorMessage: bizNoError + ) + .keyboardType(.numbersAndPunctuation) + } + .padding(.horizontal, 24) + + + // MARK: - Register Button + if isLoading { + ProgressView("회원가입 중...") + .progressViewStyle(CircularProgressViewStyle()) + } else { + Button(action: { + handleRegister() + }) { + Text("회원가입") + .font(.system(size: 16, weight: .semibold)) + .foregroundColor(.white) + .frame(maxWidth: .infinity) + .frame(height: 48) + .background(Color(hex: "#1D4ED8")) + .cornerRadius(8) + } + .padding(.horizontal, 24) + } + + TopToast(message: viewModel.message, isVisible: $showToast) + + Spacer().frame(height: 5) + // MARK: - Login Link + HStack(spacing: 4) { + Text("이미 계정이 있으신가요?") + .foregroundColor(Color.Secondary) + .font(.system(size: 13)) + Button(action: { + viewModel.goToLogin() + print("로그인으로 이동") + }) { + Text("로그인") + .fontWeight(.semibold) + .font(.system(size: 13, weight: .bold)) + .foregroundColor(Color.Secondary) + } + } + .padding(.bottom, 40) + } + } + .background(Color.Light) + .ignoresSafeArea() + } + + // MARK: - 유효성 검사 함수 + private func isValidForm() -> Bool { + emailError = isValidEmail(email) ? nil : "이메일 형식을 확인해주세요" + pwError = isValidPassword(password) ? nil : "영문과 숫자를 포함한 8자 이상 비밀번호를 입력해주세요" + confirmPasswordError = (password == confirmPassword) ? nil : "비밀번호가 일치하지 않습니다." + bizNoError = isValidBizNo(bizNo) ? nil : "사업자등록번호 형식이 올바르지 않습니다. (예: 123-45-67890)" + + return emailError == nil && pwError == nil + && confirmPasswordError == nil && bizNoError == nil + } + + // MARK: - Register Handler + private func handleRegister() { + // 초기화 + emailError = nil + pwError = nil + confirmPasswordError = nil + ownerError = nil + storeNameError = nil + addressError = nil + bizNoError = nil + + var hasEmptyField = false + + // 필수 필드 체크 + if email.isEmpty { + emailError = "이메일을 입력해주세요." + hasEmptyField = true + } + if password.isEmpty { + pwError = "비밀번호를 입력해주세요." + hasEmptyField = true + } + if confirmPassword.isEmpty { + confirmPasswordError = "비밀번호를 다시 입력해주세요." + hasEmptyField = true + } + if owner.isEmpty { + ownerError = "대표자 이름을 입력해주세요." + showToast = true + } + if storeName.isEmpty { + storeNameError = "지점 이름을 입력해주세요." + showToast = true + } + if address.isEmpty { + addressError = "주소를 입력해주세요." + showToast = true + } + if bizNo.isEmpty { + bizNoError = "사업자등록번호를 입력해주세요." + hasEmptyField = true + } + + // 빈 칸이 하나라도 있으면 종료 + guard !hasEmptyField else { return } + // 유효성 검사 함수 실행 + guard isValidForm() else { return } + + // 통과 → 회원가입 진행 + Task { + isLoading = true + await viewModel.register( + email: email, + password: password, + owner: owner, + address: address, + storeName: storeName, + bizNo: bizNo + ) + isLoading = false + } + } +} diff --git a/StockMate/StockMate/app/feature/auth/viewmodel/.gitkeep b/StockMate/StockMate/app/feature/auth/viewmodel/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/StockMate/StockMate/app/feature/auth/viewmodel/AuthViewModel.swift b/StockMate/StockMate/app/feature/auth/viewmodel/AuthViewModel.swift new file mode 100644 index 0000000..4ff557b --- /dev/null +++ b/StockMate/StockMate/app/feature/auth/viewmodel/AuthViewModel.swift @@ -0,0 +1,113 @@ +// +// AuthViewModel.swift +// StockMate +// +// Created by Admin on 10/7/25. +// + +import SwiftUI + +enum AuthState { + case unauthenticated + case registering + case authenticated +} + +@MainActor +final class AuthViewModel: ObservableObject { + @Published var email = "" + @Published var password = "" + @Published var message: String = "" + @Published var authState: AuthState = .unauthenticated + + private let repo: AuthRepositoryProtocol + + init(repo: AuthRepositoryProtocol = AuthRepositoryImpl()) { + self.repo = repo + } + + // MARK: - 로그인 + func login() async { + print("로그인 시도 - email: \(email), password: \(password)") + let req = LoginRequest(email: email, password: password) + let result = await repo.login(req) + print("로그인 응답 도착: \(result)") + + switch result { + case .success(let apiResp): + guard let data = apiResp.data else { + print("데이터 없음: \(apiResp.message)") + message = apiResp.message + return + } + TokenStore.shared.save( + access: data.accessToken, + refresh: data.refreshToken, + role: data.role + ) + message = "로그인 성공" + authState = .authenticated + print("authState 변경됨 → authenticated") + + case .failure(let err): + print("로그인 실패: \(err.message)") + message = err.message + } + } + + func logout() { + TokenStore.shared.clear() + authState = .unauthenticated + } + + func goToLogin() { + authState = .unauthenticated + } + + func goToRegister() { + authState = .registering + } + + // MARK: - 회원가입 + func register( + email: String, + password: String, + owner: String, + address: String, + storeName: String, + bizNo: String + ) async { + + print( + """ + [회원가입 시도] + email: \(email) + password: \(password) + owner: \(owner) + address: \(address) + storeName: \(storeName) + businessNumber: \(bizNo) + """ + ) + + let req = RegisterRequest( + email: email, + password: password, + owner: owner, + address: address, + storeName: storeName, + businessNumber: bizNo + ) + + let result = await repo.register(req) + print("회원가입 응답 도착: \(result)") + switch result { + case .success(let apiResp): + message = apiResp.message + authState = .unauthenticated + case .failure(let err): + message = err.message + } + } + +} diff --git a/StockMate/StockMate/app/navigation/AppNavHost.swift b/StockMate/StockMate/app/navigation/AppNavHost.swift new file mode 100644 index 0000000..f2c5bca --- /dev/null +++ b/StockMate/StockMate/app/navigation/AppNavHost.swift @@ -0,0 +1,29 @@ +// +// AppNavHost.swift +// StockMate +// +// Created by Admin on 10/7/25. +// + +import SwiftUI + +struct AppNavHost: View { + @EnvironmentObject var authViewModel: AuthViewModel + + var body: some View { + NavigationStack { + switch authViewModel.authState { + + case .unauthenticated: + LoginView(onClickRegister: { + authViewModel.goToRegister() + }) + case .registering: + RegisterView() + case .authenticated: + MainTabView() + } + } + } +} + diff --git a/StockMate/StockMate/app/navigation/MainTabView.swift b/StockMate/StockMate/app/navigation/MainTabView.swift new file mode 100644 index 0000000..838fb14 --- /dev/null +++ b/StockMate/StockMate/app/navigation/MainTabView.swift @@ -0,0 +1,40 @@ +// +// MainTabView.swift +// StockMate +// +// Created by Admin on 10/7/25. +// + +import SwiftUI + +struct MainTabView: View { + @State private var selectedTab = 0 + + var body: some View { + TabView(selection: $selectedTab) { + HomeView() + .tabItem { + Label("홈", systemImage: "house.fill") + }.tag(0) + +// SearchView() + ContentView() + .tabItem { + Label("발주", systemImage: "magnifyingglass") + }.tag(1) + +// NotificationView() + ContentView() + .tabItem { + Label("재고관리", systemImage: "bell.fill") + }.tag(2) + +// MyPageView() + ContentView() + .tabItem { + Label("사용자", systemImage: "person.fill") + }.tag(3) + } + } +} + diff --git a/StockMate/StockMate/resources/Assets.xcassets/stockmate_logo.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/stockmate_logo.imageset/Contents.json new file mode 100644 index 0000000..cbb69f9 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/stockmate_logo.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Group 148272 (2).png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/stockmate_logo.imageset/Group 148272 (2).png b/StockMate/StockMate/resources/Assets.xcassets/stockmate_logo.imageset/Group 148272 (2).png new file mode 100644 index 0000000000000000000000000000000000000000..11a3e3e2e7034b371341a0576f9f6784333fbd1f GIT binary patch literal 48990 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 Is2A}60eX8nmH+?% literal 0 HcmV?d00001 diff --git a/StockMate/StockMate/resources/Assets.xcassets/stockmate_logo1.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/stockmate_logo1.imageset/Contents.json new file mode 100644 index 0000000..f63b80b --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/stockmate_logo1.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "stockmate_logo.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/stockmate_logo1.imageset/stockmate_logo.png b/StockMate/StockMate/resources/Assets.xcassets/stockmate_logo1.imageset/stockmate_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..0b4cb42c922d15c8cbe67d1fb9dbbe6f10829003 GIT binary patch literal 10210 zcmV<8Cmq;{P)iopdx~bC_%&mSU`|22+|P{kkEvP#L!zv zLLfjwLP$v2&Gs_oyLWbGvsuFPh!4K_zTX~x!%SvoZ$0Op|M{Q03qVkU5|p3>B`A+W zF!0Y5j6ax>EqBE!3H1O9^GM{*=sinvTc7oR4uTSt$0+|q1LE5BRTFzuR9o2v3l2iv z3=1$80~pDGPztwRxasIU@4WmZd<_RdBMQo66de5X1beoAX;|2kvZ5l2HdHwP>vUtl z?g7B=z`-qwk~e83&3LO_Ne}og3xX1q$0+|a1L9!W(2ikg#TFX+9Rg=@fHAO@&;bDk zJ!MI}q>7=tGz&inK#Bn6f)bR+BL6f4V*52(9ODpyW(*)O3Kp z13)G>8`=#7y--jdqmbaQ3sPkk2nw8-LUs;Si%N}dF>aBwKo9R#*7B(e@5#^@0PF4F zompAi-TV^~JUlJ{S|B5#3Fui^Z-66MIm`ctj7b77Z6r|#1SKesQT~bnA;^Sunr<|7 zWy?1G$IS9bMcri#Ft8pVs>c8fhc1JFnvcOV#(K{<{2SfJ;F;u~gAP;Jfx}r(;f=CS zHmwhz%N340B0apt-+v83)C9jRYOIaLra<6MoO&H8KH_K00h7ES57M=v^q9hi)}04Y+2wYa$5W zg7R49VGM{1*qcn)qi^}#pfcwTinSV{9y25k|Hn9uMux$AiW*AKRu|wah<^u%cE`Ga z0OP#H;9d`d@h`J%^#_HrXx>W&B`A+m9>#zW7skXt5ANA=JQNE=BvfXo{R|_M;W#4! z6!bn;n5uD!7)YAY=$RL;;XI7PcvvUgBRpO#2(L}PT8L`GK%fUO-ttg+EPo$8r?@wR z5|qa$f7XDIqS6Dlx_|NHkr;!CNa!k95(Yh(R%FRMoAsRQ)`~y!QonV9zb;~UxCO=y z9he_p!vai&gK)%u1>uAe*}wSwf0exd;ipfXI&(c)A(xhWXqUK2)96c!=g(aU{(E`Y zWcGvJ_ggam_sE|%pxD)30Wj=y|`h+nk)Qgg#I!A zq1=D00$j-{YTJviCP^`34}Qx2O$G<6X0QmGajf2;NsPLYcJe# zj5_!G4EQ^W7 zVN`zpMKgHB2J~k<2NvCPl0nCUM2aC;7O`hC7B9|m41-G&PaQm3p>XekdP@5p$Y>ZK zP}tzF2r?f0;K`e&U-oB=DY%hK7$RbTR7?Pw1P3ae29S^*)Q0GR2)enlqI;T^D}H$W zdO_te;5YCqHf)H{#dIr`MLkg_kLXwypD?6s!h{w7O-}7^7mUIY9plwtIEiYF0X(c6 z*|~*^ZQKfI!gW9sp$B4R7_&DuD;0Rh=07cG+(JBj)YQ2b&Sw$2NCS(?A=QyUtyEQl zM=R{7@2B)OQguq?5l@tf!s74sf9~ac@Hgn{pD`e&?$Eij-FF%9`^cPixG_CNz7u!{ z?a{&(qS^ydShcYTXLL}4A%TY$ChrcLb^hcfrP?54SWM2Ct#-INtARnUb3}%Pn-wwz zC6cH>MOis)3&l#IN4isVeKhbR77K3Q234jCP*rXM<&_nnsJPe}6?ON|j>F$geAHe( zj$wiu$5SfR? zaQlO~k1_P4-w`7%zUwwwsz7=9J*=|461!7cS_S?FsjcBy;Qi2W-4u@pkDRIrHI&z@U*CKrbt~HW?ej2VPe>I>;2f=p21_X>Sm@cP zfa*lG^v{REn>KB#ii(N~Y1F8b+V36o{Lif9K1FTQri~&hstGKokx-71Zmuzhw1+`! zA7J2t&ID1b&WdZVk}Hc4Di;J6gDW)(`pl(*rLg}bc<_SE#Yi_xz~+*|B8dK2QfTM{ zzLiqL!iNnTCaza6P8%H^ZD`rDrPA-cz*`OmADht*$ocvq9qsX;odYB*+z<(ELl1251aY^><`Jr>9s!zUZH`z^_CZ6o&;@~s z6Y@nMkWfI|dHxJ&2$0CA27OPRKMwSvA-LN^fnLvaxw!Ag?}njb&rLkSuAGSSxd1VV z*OqojO&YU*Z0=s*)OFwmEbPiB%+*O6Y#^gG@TL=y*x-wHZfKci#}| z<%Si#;x6~MI$SiFE=O*<6=2F)K(P`Q?HoXjp+tac z;8tE?+dK(zwf7z-1o*V~jOK>ZbEet=F#aAEuv3AAH=P(L&prr3Q6mtuhCC2*?<@nV zlc#>UAi%f&VOhA787JCl!y;h89tay)ASb0l;Li5-^b4Dsrg%;ngs%LhJ zfmDjK3=PehqXcR8=FK~C3?Tw`iyPF5qMHVcD>eD;xr=9gwrq+WE+Q(u7WRYT#;P1E zB5&U%^=U`!gH<~9;L)#s_{;iLa|eR^jfhi{d0(uUwqo5+3yW{x0je;*9O#ZKaEvS{ zykl?}jY-J&FzD$MCvwJ4pFVx&f&~li1(eP8<61tp!)i6MQkfjD@p**he6z}l z=fh8y%v-zer zKX&X~W3@^F6iOKb1t*}I(;|laZpGGkf+T=gsse7ir!iljKrCTJmE?ta^H;@@DkYp> z5;<55XIM~LQXqGG=(v*^nLCnRo{{&$fDSE(4%c^pljZ1x#6|bS)EAb3 zz~-+Rw&pXgIVAp2@mGg-PiC1ayfy=g1NO-AGd*8<<^74ET2UW_;kgyn>TtmEasZ09 zB3Qtn6@-fjpb@#DHPf(T3}Xcz1{cPao7DGbFHK##?8hk*xr)|?XjmMgVXNHs17AGhuuFm~dc9KKD0k40zCOXFs&o|Up>q21-Awc$FJz{IT8=m5A} z323VWxIHdRq0_<@-c9e?cWlC&?=AWb@Wz9YY<1sQj@o4X+xXhlkC&}l_wyTJ4eNQt zA~?kqhWG8+A?47|t7ieP@p`=qTJ`wQmu484J%{OR)`i;~M%HB09^~Ki#|HHD_vd## zb8?Aj+pl_Xx#k_$qTkw{ypJ&-~p=3VEMTNLOJ|9xc~SO_s2i}F_GZx zn7=?>l$YGxtyPD@UgKz=1SXM-sWlsSEKeLTzWjyJZ+`poE6H7^Oqm@RllWQ}*S6`c z_%2WW(YbxQLk^dV0Q?Rnz)&FZsn&bCc4_-dmnYh%w`yGHBL5L;@{Es@KVP__Z)kJ` z^4Gv*v|>`R)b?zjE}uH?<|K7&8vahFxORn=pT>nm zYN7G4z;0tj{d=|lc-O|Ivq50M>Me&mV1FQUsWOclJ}7DOw7J>3h;T*~9?BfexPsuG zo*y%M@V~}R+V)4G6BlOF@{>TWb7td07 zgv56nS(10-MwnVFr!QX0e>y%sKFYVc684G)EQ8fp`dPojcMnt>M6HJ~1Z=>2O!fD9 z1S4X3V`KcLNPuV@+=Z zp+=|;dZ5r?pvvyTj+{6hdo2CJyB~kHw&3{}r|bg(Ro$(sQu|yg{Le#q@NL_+1yUN+ zn?Wg*>ol?a-!;8^MT^fa5|F9;l?7f7MC;BvXZ6P>!; z95!Oo^i`V`tuqetb~t#(WzBEa!O zM|BGq|2Pee8G<<_ChR+vQUvjnj17;C0ijCQSex122@qZkiE;5U8wj06TQ{xRvS-it zmfGsxym{T#h!904(s-l|a=8xV=jU|?ezBgp%3=VxcS?}6g|M-$F6i8K;G4ceA*ONrW2YLPg+I9U zi*F(u=yljyt_82+OqpfD!`hu*pThzL!GD}58o$&Cef5k+_ zj&QpejDZmLAsvvnn^1Yz|M2;s#Kb|(RH0AIyk{;OeDzAc+U9ZqiAW4Ykg<^vI40IA z(Ox_1r=O1vnELLb3txZnW;_rAdpqB5fhmFHV|I3Spc1fIc}4w68G&3aX0uzts29il zviPfG{-Y!aH>#~0*ZC$V_u3~K4Xm5-ux;BVbUTy2A9U#4E$QUtEkLc;Vph8yBzEqS zwtM#y69D&ZO+n@0AAB$aK}Z|~k_WuVq$Id}t#G)*abu)bC4ym$M`VXF)Uf9V?fG%} z90c1C9bD_aMhqD^8b%nGkc5a+t`LQ%w@j&l{IrJ&trJ5p`+nQP2@@tPzI3Z<;n_b< z;~Im8DJv}{-kLsc&NnI3JU#pV3n3jY3`2k?0K$0_O;ARUo|d4Ctvm31#>sk+HB_K) z_=^b=dVR`RP+A$uG7N|RG00HaZB{7&wYQklZld<(HvPio4l<>jdrz&AgUgvWUmQJd zMmMY5Ns=hnf#EC*X+s#-i0pQ$nM7Nt861;|NN~?YHL7Wq{>I}=xUiI1%#ySmcObe1 zHvy^J2}4X4BiUM@n_w`3o=3PR7>>`7{633>km(z^+~EA>3+J9Zcdmo~3W$_qF90c{ zpC34NX-t$lS%k3+czEIBinO%7SK)ayx{;KWRM+K_b!An`L3eMPyI;x930LWq3`5hT zAyi8*`f6p|2{`+D?E?r5!;wJ} zF#$;;nncP287uZs>cMB{&)?E;#*7zk0#N(DF)uOd?J!MyAAkMzygR*~eQ9I*>D*BY znV7v(bQ|PGDi2)C%T3UR=n+)L;ybl7L*q%|%3x_0#acLy2UfwM_9;`Qh%Vi*;p8|dQZ3DrY; zYBwNLWrZFwb7)Zzi6qpe>{|oRUb?}>h!`deBF5#RSx6LV^bqPnR)c`^aq-gG2BiPU z1j03V&|*1D=9}43neSkm&}*IEbBv47iPzXID7J?niv`u|M<8_Lr_ew4!Te(eWlpDiouegy{~s?iovRva(X#JNBi#mNd|k=3#Wv;IByy<=VM( zCyH}^ zWFYzi3T~qbX3NjJP;jjP56DFc+yiPOf%%4!pv;BiTefcfue@tlF{MVuo;rW62h2?| z1k9D0?N+>wLGt_sp$y>7OQB&yq#lF$Mv$Y-?p7)8o;-C@tJX;=mQ_Rjm*Vy6#@cbf zB^zP75eNAB)XLlqV(T%K2a$x($bmQJ<6<>#*Vycs*=o#bJwcOnKchR{`8;jxPm+4FW`^`2! z`9!@__3KT%*zL*LuAu`NZj^TJQ*Aw+8S@o*xSins9@jr+@izhDyD#URXx1WZ4(0Y@ zb3!b|?%uiC00ewM7+4Cv)}lp=`^;$Mm?6KLiVHbyMHFhCm?oWTX$KJc@O%2Xeu@wS zx5B%fRyHA_!%glXW-DMIti|C!xmE=Z9z8R-mOdcy-^oA#|FL0fejx-WaF5;OB>VPh zw+SUY;A5CW0oavmx8$9kei7}wK-zGhp)#~LjzZI4P2vJ3EiKJSSKUdr*0>8JCj&j2lC(|m)1gIW!!WBZOpPM3|l zuDnxBOnf=%)icL7$}J^lbhgrSI!ozUof%%Eb5&7>&RA5ev)(KF;CGs1sXs4( zw+<=hayto)icu;=^y!mlJ1t$h0TCFU!13Xr4Pd#XCWF-&GvdPO18X|AZ+DNTybBKw z0}lz|!C6PtVBnvuhATaSRcP@+Yr200F^w8MN6HBn=9qA)Oo5f&x;$pfmLkz1Mf1H5!AjQ^~pEH(2=9f!3a&skkEX?B)0f&>3y)ra07yRc9j5VZL zvyO)J^ea90AHVTy*WNGP>OE-m#4%&uibZw$)i0hndDfe|4rWYIs-z537`M#@BD9Ll z!aJ*1uXj07oQ-zHVCcIx*X6x>4Ihqrh4yXj-L@?jwd;pftFoHbkIb?;Y=|wwNfAyR zJ$>VsH{Shx3ap0<0ML2DYwrv=lVi!W+p1t}$>I(dBoI2ZDPH#NXTPC$Zr{!{Mu*4_ zS_ka)2B~w32xtOx1&rZA7V~ zzTeP}?=N0+_4S8h@bJK7Y^+v1x9Paz4#LxBWAnR2g@w67kfGP zM(pv7?BzcnId{uO)UE2=dwgl6cWzOT9V1{~iBU;Zc*l-0NO6Qf9)ISkCWBhF zZq7Iy&>s>Qf#R2?D>r_WRS2xz22LvK(r3JB-e>F1-!8hRfH@iVPH{Q?bLzom(4e_C zvm`EzCI=CV3U3PI=0N?bA0_G(9u?(66ewK&Ntss3Z2b9Rrcgh65e4heO>yxLR1JVr z_c?#;i@6_Vjv6^&ucfS%h>413`##lr7zo%O$1R2e{6n#rABJxf5xUW4gP|i_Diko$ zee%N9cSg^cx2j9GXL=+g_8*usZ{-iWe>!%Oz{M~=qWSGb00f{}e9xXO#gJpLbB4ji zpcu95(7D4shfXQL6*1){Ra`uapeHrBZ|B~$3l0!Wu&Y%H=IYIxy7fCwWOnU4v3$^r zGYg&?IHB~4NmB`v%~Og>3Oq|!>^aw~*WlLZ^EC{J4WOf8wYal_rkga4Jk$Ft_Ew)S z+2Tfx>ZZFYX$m=W0fw>uo+l8lftp^a)*O?I32m2HIWa!*?lh%Ak?eNjKE_UnQ?Z)m z;phkCG>Z5-;2|WYas6nTazo0~jgc^lRjCymz{$(IF28y$JM3z1cKDs*B9S)SfC4k3 z%47j*ksSMa&dbd}%|#cBP!pqU<|=Ob;p&ezbAk)!FT@rXm8#BUWW+)!#c`wa=g+rv ziwkesq(L3rY<0r!(3n2Vz!nx35|=NYkGXNfGhK&p*!t zesBYp=Xv?Lr=!ASLKst}873b92-Ry&8xK5iprdxFKAPz4+XK z=Jp;?9XG|U5!DaTKClm=I>#~v($)wp!()oT99XfQttTwA} zYJGc${mMb_t^W3-XH#ZQY&?0wt07~@j&*{7MGg)agYmeZ%La*jO~DWD-@NUqF75Lx zjdrw;8Ykp(YV-0f<8uq?Uo&r2?fqoIlF<%m<_eV*E=4rs-BG~;sr0^ffcTct$u^?{ zVnn!j!PN^=(BRVPv$5r-GSW(cI0)4c1|;WxG&TLDr0&m2#d6YSvcY_o2uPJOh%7P< zzoi|G&CbjUGh59-YtS<;o0BZezX^s@Er>=hXDT{=E-8 zJ+!TA$dDn00b_-4-MISXtao1XsXUy~+5wZU>T@zz_f z-iuM%WJ3q{*r4AiK-LoX<3!Uw=4zc=NUKSH_L*H{D_Jz!ZoC(?^7ZWedLR^2YRqOHf;8%$g2?7T3{DfDPDme{JWw-XKrfVFCw$p-fm#!+!tcZRer%CHaQ;5`D?R*s!cSvyXHb2QhkcApLgD5L_qwgW zD#n6`6W?zepty%2<;}ZwACOd9QKhUjmMf%?KWY)z%62;K*u{HyukXsp$l%}&sJS%w z^Z*|2e*OAM+H~oeTyodgKrEKBR#W*MlCJ!2!-fquP)!93>=>1pI7}H9VHn=1b=wwK zbFaywBEwxChq0*W#+9|d{PK(O-_!>{HR(VPOGy#u?mLw9{D_I|v$Hd$;bAdUNnviG zxgvjcYHF$(_-(mIpM#5l=6X>@O9??hvYHVyB!2H)Mp`bYsodP|gQuS!(%xb*sbB_? zVhGY{&>IX#4*YZ&!a=O0!qMJsv*I+3Gb(*FcI?oGQ{dqQ>(i%?uC&VD9D~iEShu)- zr0(jTJ$vp6Z577qg%`%wsoS{a_@cWNT7763bNlMWJ9)-}<>3b&p!LC3Ja+}B~oJuce`m26Q<8QbMnASI8H854KIDVF?#eQ z!};8-7oQ(Gx=zluTyev?4cw=H|Lw-Tk}K;kU%qVfpQD-v)bsU)$iw&>NT8?T_rsz6 z#<;(c2(gdw6$bu2pYgE%M#LM0zwuza0pUEWSHp<-Awi6S6i%_#Ods2{U(^HqhwUao z3CiCsKK2PF^+kaOai8G`K|JlkDX}CS8jJ7CxZGDBxV;(tcivdDnT8Ft;scPDr zGZ4W4?62UJ5Jf8djT+!WSvqJbG(nh+ca1i?^YgS*4Nrie@dV``AU*?PDAJdS3S`p) zi2-?t1f1ADb)f0uHzx2uF9M7S8c$FjyZ8cQiLzxXzc-Tc&+XKfKjC#b-b{JGr)J5tx5d4! zIF_z3031@`FyRPqLLP(U&muA8-(y60?zwAS!9(l-2ue`?uS%d1aI|Yg*DEbTV@jT9 zTnzdWJLM$;Sey}s+C2rw7v-ILL!e0?<pgaRtisEC2ui07*qoM6N<$g1{icbN~PV literal 0 HcmV?d00001 diff --git a/StockMate/StockMate/resources/Color.swift b/StockMate/StockMate/resources/Color.swift new file mode 100644 index 0000000..a4e0a95 --- /dev/null +++ b/StockMate/StockMate/resources/Color.swift @@ -0,0 +1,47 @@ +// +// StockMateApp.swift +// StockMate +// +// Created by Admin on 10/5/25. +// + + +import Foundation +import SwiftUI + +extension Color { + + //keyColor + static let Primary = Color(hex: "#1D4ED8") + static let Secondary = Color(hex: "#60A5FA") + + // DarkBlue + static let DarkBlue01 = Color(hex: "#182D53") + static let DarkBlue02 = Color(hex: "#1E3A8A") + static let DarkBlue03 = Color(hex: "#1E40AF") + + // LightBlue + static let LightBlue01 = Color(hex: "#2563EB") + static let LightBlue02 = Color(hex: "#3B82F6") + static let LightBlue03 = Color(hex: "#93C5FD") + static let LightBlue04 = Color(hex: "#DBEAFE") + + // Etc + static let Gray = Color(hex: "#ABABAB") + static let White = Color(hex: "#FFFFFF") + static let Dark = Color(hex: "#04150C") + static let Light = Color(hex: "#F7F7F7") + + // State + static let Success = Color(hex: "#00CB6A") + static let Warning = Color(hex: "#EFBE24") + static let Danger = Color(hex: "#F26666") + + // Text + static let textBlack = Color(hex: "#152C07") + static let textGray1 = Color(hex: "#5D5C5D") + static let textGray2 = Color(hex: "#BEBEBE") + + // 투명도 포함 예시 + static let boxBgWhite = Color(hex: "#40FFFFFF") // 투명도 포함 +} diff --git a/StockMate/StockMate/resources/ColorHex.swift b/StockMate/StockMate/resources/ColorHex.swift new file mode 100644 index 0000000..02fa4c8 --- /dev/null +++ b/StockMate/StockMate/resources/ColorHex.swift @@ -0,0 +1,41 @@ +// +// StockMateApp.swift +// StockMate +// +// Created by Admin on 10/5/25. +// + + +import SwiftUI + +extension Color { + init(hex: String) { + var hexSanitized = hex.trimmingCharacters(in: .whitespacesAndNewlines) + hexSanitized = hexSanitized.replacingOccurrences(of: "#", with: "") + + var rgb: UInt64 = 0 + Scanner(string: hexSanitized).scanHexInt64(&rgb) + + let r, g, b, a: Double + + switch hexSanitized.count { + case 6: // RGB (예: FFFFFF) + r = Double((rgb >> 16) & 0xFF) / 255.0 + g = Double((rgb >> 8) & 0xFF) / 255.0 + b = Double(rgb & 0xFF) / 255.0 + a = 1.0 + + case 8: // ARGB (예: 40FFFFFF → 투명 흰색) + a = Double((rgb >> 24) & 0xFF) / 255.0 + r = Double((rgb >> 16) & 0xFF) / 255.0 + g = Double((rgb >> 8) & 0xFF) / 255.0 + b = Double(rgb & 0xFF) / 255.0 + + default: + r = 0; g = 0; b = 0; a = 1 + } + + self.init(.sRGB, red: r, green: g, blue: b, opacity: a) + } +} + From cf10ea02c58ce92b15045f16f86beb1740934964 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sun, 12 Oct 2025 02:01:50 +0900 Subject: [PATCH 02/85] =?UTF-8?q?[FEAT]=20=ED=83=AD=EB=B0=94=20=EC=BB=A4?= =?UTF-8?q?=EC=8A=A4=ED=84=B0=EB=A7=88=EC=9D=B4=EC=A7=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UserInterfaceState.xcuserstate | Bin 132382 -> 139730 bytes StockMate/StockMate/ContentView.swift | 2 +- .../auth/data/AuthRepositoryImpl.swift | 11 --- .../app/feature/auth/ui/LoginView.swift | 14 ++++ .../feature/inventory/ui/InventoryView.swift | 18 +++++ .../app/feature/orders/ui/OrderView.swift | 18 +++++ .../app/navigation/MainTabView.swift | 69 ++++++++++++------ .../stockmate_logo.png | Bin 10210 -> 0 bytes .../Contents.json | 2 +- .../tabHome.imageset/tabHome.svg | 3 + .../tabInventory.imageset/Contents.json | 21 ++++++ .../tabInventory.imageset/tabInventory.svg | 3 + .../tabPackage.imageset/Contents.json | 21 ++++++ .../tabPackage.imageset/tabPackage.svg | 3 + .../tabProfile.imageset/Contents.json | 21 ++++++ .../tabProfile.imageset/tabProfile.svg | 3 + 16 files changed, 173 insertions(+), 36 deletions(-) create mode 100644 StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift create mode 100644 StockMate/StockMate/app/feature/orders/ui/OrderView.swift delete mode 100644 StockMate/StockMate/resources/Assets.xcassets/stockmate_logo1.imageset/stockmate_logo.png rename StockMate/StockMate/resources/Assets.xcassets/{stockmate_logo1.imageset => tabHome.imageset}/Contents.json (86%) create mode 100644 StockMate/StockMate/resources/Assets.xcassets/tabHome.imageset/tabHome.svg create mode 100644 StockMate/StockMate/resources/Assets.xcassets/tabInventory.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/tabInventory.imageset/tabInventory.svg create mode 100644 StockMate/StockMate/resources/Assets.xcassets/tabPackage.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/tabPackage.imageset/tabPackage.svg create mode 100644 StockMate/StockMate/resources/Assets.xcassets/tabProfile.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/tabProfile.imageset/tabProfile.svg diff --git a/StockMate/StockMate.xcodeproj/project.xcworkspace/xcuserdata/admin.xcuserdatad/UserInterfaceState.xcuserstate b/StockMate/StockMate.xcodeproj/project.xcworkspace/xcuserdata/admin.xcuserdatad/UserInterfaceState.xcuserstate index bd930b5fae8b5160379c8da675b62877c2953afb..4b84ae05056e66245b96ccb9ed7ea69d42e3d637 100644 GIT binary patch literal 139730 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 literal 132382 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 AppResult> { -// let dataReq = AuthApi.register(req) -// return await safeApi(dataReq, decodeTo: ApiResponse.self) -// } -// -// func login(_ req: LoginRequest) async -> AppResult> { -// let dataReq = AuthApi.login(req) -// return await safeApi(dataReq, decodeTo: ApiResponse.self) -// } -//} final class AuthRepositoryImpl: AuthRepositoryProtocol { func register(_ req: RegisterRequest) async -> AppResult> { let dataReq = AuthApi.register(req) diff --git a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift index ecc0b9b..23da13f 100644 --- a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift @@ -98,6 +98,20 @@ struct LoginView: View { .foregroundColor(Color.Secondary) } } + + // 승인 아이디 받기 전 + // 홈화면으로 이동 + HStack { + Button(action: { + authViewModel.authState = .authenticated + }) { + Text("홈화면으로 이동") + .font(.system(size: 13, weight: .bold)) + .foregroundColor(Color.Secondary) + } + } + .padding(.top, 5) + Spacer() } .background(Color.Light) diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift new file mode 100644 index 0000000..6f8161f --- /dev/null +++ b/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift @@ -0,0 +1,18 @@ +// +// InventoryView.swift +// StockMate +// +// Created by Admin on 10/12/25. +// + +import SwiftUI + +struct InventoryView: View { + var body: some View { + Text("재고 관리 화면") + } +} + +#Preview { + InventoryView() +} diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderView.swift new file mode 100644 index 0000000..1b4aa7a --- /dev/null +++ b/StockMate/StockMate/app/feature/orders/ui/OrderView.swift @@ -0,0 +1,18 @@ +// +// OrderView.swift +// StockMate +// +// Created by Admin on 10/12/25. +// + +import SwiftUI + +struct OrderView: View { + var body: some View { + Text("발주 화면") + } +} + +#Preview { + OrderView() +} diff --git a/StockMate/StockMate/app/navigation/MainTabView.swift b/StockMate/StockMate/app/navigation/MainTabView.swift index 838fb14..48835cc 100644 --- a/StockMate/StockMate/app/navigation/MainTabView.swift +++ b/StockMate/StockMate/app/navigation/MainTabView.swift @@ -11,30 +11,53 @@ struct MainTabView: View { @State private var selectedTab = 0 var body: some View { - TabView(selection: $selectedTab) { - HomeView() - .tabItem { - Label("홈", systemImage: "house.fill") - }.tag(0) + VStack(spacing: 0) { + // 메인 화면 + ZStack { + switch selectedTab { + case 0: HomeView() + case 1: OrderView() + case 2: InventoryView() + case 3: ContentView() + default: HomeView() + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity) -// SearchView() - ContentView() - .tabItem { - Label("발주", systemImage: "magnifyingglass") - }.tag(1) - -// NotificationView() - ContentView() - .tabItem { - Label("재고관리", systemImage: "bell.fill") - }.tag(2) - -// MyPageView() - ContentView() - .tabItem { - Label("사용자", systemImage: "person.fill") - }.tag(3) + // 커스텀 탭바 + HStack { + tabButton(index: 0, icon: "tabHome", text: "홈") + tabButton(index: 1, icon: "tabPackage", text: "발주") + tabButton(index: 2, icon: "tabInventory", text: "재고관리") + tabButton(index: 3, icon: "tabProfile", text: "사용자") + } + .padding(.vertical, 24) // 탭 높이 조절 + .padding(.horizontal, 20) + .background(Color.White) // 탭바 배경색 + } + .edgesIgnoringSafeArea(.bottom) + } + + // 커스텀 탭 버튼 + func tabButton(index: Int, icon: String, text: String) -> some View { + let isSelected = selectedTab == index + return Button { + withAnimation(.easeInOut) { // 탭 전환 애니메이션. 없애도 됨 + selectedTab = index + } + } label: { + VStack(spacing: 6) { + Image(icon) + .renderingMode(.template) + .scaledToFit() + .frame(height: 20) + .foregroundColor(isSelected ? Color.Primary : Color.textGray2) + + Text(text) + .font(.system(size: 13, weight: .semibold)) + .foregroundColor(isSelected ? Color.Primary : Color.textGray2) + } + .frame(maxWidth: .infinity) } } } - diff --git a/StockMate/StockMate/resources/Assets.xcassets/stockmate_logo1.imageset/stockmate_logo.png b/StockMate/StockMate/resources/Assets.xcassets/stockmate_logo1.imageset/stockmate_logo.png deleted file mode 100644 index 0b4cb42c922d15c8cbe67d1fb9dbbe6f10829003..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10210 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 diff --git a/StockMate/StockMate/resources/Assets.xcassets/stockmate_logo1.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/tabHome.imageset/Contents.json similarity index 86% rename from StockMate/StockMate/resources/Assets.xcassets/stockmate_logo1.imageset/Contents.json rename to StockMate/StockMate/resources/Assets.xcassets/tabHome.imageset/Contents.json index f63b80b..e847277 100644 --- a/StockMate/StockMate/resources/Assets.xcassets/stockmate_logo1.imageset/Contents.json +++ b/StockMate/StockMate/resources/Assets.xcassets/tabHome.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "stockmate_logo.png", + "filename" : "tabHome.svg", "idiom" : "universal", "scale" : "1x" }, diff --git a/StockMate/StockMate/resources/Assets.xcassets/tabHome.imageset/tabHome.svg b/StockMate/StockMate/resources/Assets.xcassets/tabHome.imageset/tabHome.svg new file mode 100644 index 0000000..7539862 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/tabHome.imageset/tabHome.svg @@ -0,0 +1,3 @@ + + + diff --git a/StockMate/StockMate/resources/Assets.xcassets/tabInventory.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/tabInventory.imageset/Contents.json new file mode 100644 index 0000000..3421138 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/tabInventory.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "tabInventory.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/tabInventory.imageset/tabInventory.svg b/StockMate/StockMate/resources/Assets.xcassets/tabInventory.imageset/tabInventory.svg new file mode 100644 index 0000000..cf21e73 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/tabInventory.imageset/tabInventory.svg @@ -0,0 +1,3 @@ + + + diff --git a/StockMate/StockMate/resources/Assets.xcassets/tabPackage.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/tabPackage.imageset/Contents.json new file mode 100644 index 0000000..c6df33b --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/tabPackage.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "tabPackage.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/tabPackage.imageset/tabPackage.svg b/StockMate/StockMate/resources/Assets.xcassets/tabPackage.imageset/tabPackage.svg new file mode 100644 index 0000000..f8c3043 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/tabPackage.imageset/tabPackage.svg @@ -0,0 +1,3 @@ + + + diff --git a/StockMate/StockMate/resources/Assets.xcassets/tabProfile.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/tabProfile.imageset/Contents.json new file mode 100644 index 0000000..9bea2a3 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/tabProfile.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "tabProfile.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/tabProfile.imageset/tabProfile.svg b/StockMate/StockMate/resources/Assets.xcassets/tabProfile.imageset/tabProfile.svg new file mode 100644 index 0000000..c98979c --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/tabProfile.imageset/tabProfile.svg @@ -0,0 +1,3 @@ + + + From 271d654cb0bfe74863e1503d2dddea9d729df96b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sun, 12 Oct 2025 02:11:16 +0900 Subject: [PATCH 03/85] [CHORE] remove xcuserdata from tracking and update .gitignore --- .gitignore | 6 +++++- .../UserInterfaceState.xcuserstate | Bin 139730 -> 0 bytes 2 files changed, 5 insertions(+), 1 deletion(-) delete mode 100644 StockMate/StockMate.xcodeproj/project.xcworkspace/xcuserdata/admin.xcuserdatad/UserInterfaceState.xcuserstate diff --git a/.gitignore b/.gitignore index ca4915e..703c1ad 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,8 @@ DerivedData/ Pods/ # Swift Package Manager -Packages/ \ No newline at end of file +Packages/ + +# Xcode user state files +*.xcuserstate +*.xcuserdata/ diff --git a/StockMate/StockMate.xcodeproj/project.xcworkspace/xcuserdata/admin.xcuserdatad/UserInterfaceState.xcuserstate b/StockMate/StockMate.xcodeproj/project.xcworkspace/xcuserdata/admin.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index 4b84ae05056e66245b96ccb9ed7ea69d42e3d637..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 139730 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 From 39f295deff90dffaaa255dd7afd00ba181bc029d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sun, 12 Oct 2025 19:42:25 +0900 Subject: [PATCH 04/85] =?UTF-8?q?[FEAT]=20=ED=99=88=ED=99=94=EB=A9=B4=20UI?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=20=EA=B3=B5=ED=86=B5=20?= =?UTF-8?q?=EC=83=89=EC=83=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/feature/auth/ui/HomeView.swift | 191 +++++++++++++++++- .../defect.imageset/Contents.json | 23 +++ .../defect.imageset/defect@1x.png | Bin 0 -> 418 bytes .../defect.imageset/defect@2x.png | Bin 0 -> 657 bytes .../defect.imageset/defect@3x.png | Bin 0 -> 1018 bytes .../incoming.imageset/Contents.json | 23 +++ .../incoming.imageset/incoming@1x.png | Bin 0 -> 397 bytes .../incoming.imageset/incoming@2x.png | Bin 0 -> 645 bytes .../incoming.imageset/incoming@3x.png | Bin 0 -> 908 bytes .../lack.imageset/Contents.json | 23 +++ .../Assets.xcassets/lack.imageset/lack@1x.png | Bin 0 -> 488 bytes .../Assets.xcassets/lack.imageset/lack@2x.png | Bin 0 -> 872 bytes .../Assets.xcassets/lack.imageset/lack@3x.png | Bin 0 -> 1213 bytes .../location.imageset/Contents.json | 21 ++ .../location.imageset/location.svg | 8 + .../notification.imageset/Contents.json | 21 ++ .../notification.imageset/notification.svg | 3 + .../profile_sample.imageset/Contents.json | 21 ++ .../profile_sample.png | Bin 0 -> 504 bytes .../transfer.imageset/Contents.json | 23 +++ .../transfer.imageset/transfer@1x.png | Bin 0 -> 480 bytes .../transfer.imageset/transfer@2x.png | Bin 0 -> 885 bytes .../transfer.imageset/transfer@3x.png | Bin 0 -> 1201 bytes .../wait.imageset/Contents.json | 23 +++ .../Assets.xcassets/wait.imageset/wait@1x.png | Bin 0 -> 463 bytes .../Assets.xcassets/wait.imageset/wait@2x.png | Bin 0 -> 830 bytes .../Assets.xcassets/wait.imageset/wait@3x.png | Bin 0 -> 1161 bytes StockMate/StockMate/resources/Color.swift | 43 +++- 28 files changed, 418 insertions(+), 5 deletions(-) create mode 100644 StockMate/StockMate/resources/Assets.xcassets/defect.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/defect.imageset/defect@1x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/defect.imageset/defect@2x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/defect.imageset/defect@3x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/incoming.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/incoming.imageset/incoming@1x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/incoming.imageset/incoming@2x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/incoming.imageset/incoming@3x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/lack.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/lack.imageset/lack@1x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/lack.imageset/lack@2x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/lack.imageset/lack@3x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/location.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/location.imageset/location.svg create mode 100644 StockMate/StockMate/resources/Assets.xcassets/notification.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/notification.imageset/notification.svg create mode 100644 StockMate/StockMate/resources/Assets.xcassets/profile_sample.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/profile_sample.imageset/profile_sample.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/transfer.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/transfer.imageset/transfer@1x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/transfer.imageset/transfer@2x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/transfer.imageset/transfer@3x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/wait.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/wait.imageset/wait@1x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/wait.imageset/wait@2x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/wait.imageset/wait@3x.png diff --git a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift index 4e891b9..33c3bb4 100644 --- a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift @@ -10,6 +10,195 @@ import SwiftUI struct HomeView: View { var body: some View { - Text("홈 화면") + ScrollView { + VStack(alignment: .leading, spacing: 24) { + // 상단 프로필 + HStack(spacing: 16) { + Image("profile_sample") // 샘플 이미지 + .resizable() + .frame(width: 50, height: 50) + .clipShape(Circle()) + + VStack(alignment: .leading, spacing: 4) { + HStack { + Image("location") + .foregroundColor(.gray) + Text("가산점") + .foregroundColor(.gray) + .font(.subheadline) + } + Text("Nadila Aulia") + .font(.title3.bold()) + .foregroundColor(Color(hex: "#2B3A1A")) + } + + Spacer() + + Image("notification") + .font(.system(size: 20)) + .foregroundColor(.gray) + } + .padding(.horizontal) + + + // 검색창 + HStack { + Image(systemName: "magnifyingglass") + .foregroundColor(Color(hex: "#4B5565")) + Text("Search for Accessories") + .foregroundColor(.gray) + Spacer() + } + .padding() + .background( + RoundedRectangle(cornerRadius: 120) + .fill(Color(hex: "#EEF2F6")) + ) + .overlay( + RoundedRectangle(cornerRadius: 120) + .stroke(Color(hex: "#9AA4B2"), lineWidth: 1) + ) + .padding(.horizontal) + + + + // 상태 요약 카드 + HStack(spacing: 13) { + StatusItem(title: "입고", count: 77, color: .IncomingBg, icon: "incoming") + StatusItem(title: "부족", count: 33, color: .DangerBg, icon: "lack") + StatusItem(title: "승인대기", count: 27, color: .WarningBg, icon: "wait") + StatusItem(title: "반품/불량", count: 4, color: .DefectBg, icon: "defect") + StatusItem(title: "이동요청", count: 33, color: .TransferBg, icon: "transfer") + } + .padding() + .background(Color.white) + .cornerRadius(16) + .padding(.horizontal) + + + + // 도넛 차트 섹션 + VStack(alignment: .leading, spacing: 18) { + Text("제목") + .font(.headline) + .padding() + + HStack{ + DonutChartView() + .frame(height: 170) + .padding() + .background(Color.white) + .cornerRadius(16) + .shadow(color: .gray.opacity(0.1), radius: 4) + + Spacer() + } + } + .padding() + .background(Color.white) + .cornerRadius(16) + .padding(.horizontal) + + + + // 막대그래프 섹션 + VStack(alignment: .leading, spacing: 8) { + Text("제목") + .font(.headline) + .padding(.top) + .padding(.leading) + + BarChartView() + .frame(height: 200) +// .padding() + .background(Color.white) + .shadow(color: .gray.opacity(0.1), radius: 4) + } + .padding() + .background(Color.white) + .cornerRadius(16) + .padding(.horizontal) + } + .padding(.vertical) + } + .background(Color.Light) + } +} + + + + +// MARK: - 상태 아이템 컴포넌트 +struct StatusItem: View { + let title: String + let count: Int + let color: Color + let icon: String + + var body: some View { + VStack(spacing: 3) { + Image(icon) + .resizable() + .scaledToFit() + .frame(width: 25, height: 25) + .foregroundColor(.white) + .padding(12) + .background(color) + .clipShape(RoundedRectangle(cornerRadius: 10)) + + Text(title) + .font(.system(size: 15, weight: .semibold)) + .padding(.top, 5) + .lineLimit(1) + + Text("\(count)건") + .font(.caption) + .font(.system(size: 11, weight: .semibold)) + .foregroundColor(.textGray1) + }.frame(maxWidth: .infinity, minHeight: 70) } } + +// MARK: - 도넛 차트 (더미) +struct DonutChartView: View { + var body: some View { + ZStack { + Circle() + .trim(from: 0, to: 0.521) + .stroke(Color.black, lineWidth: 40) + Circle() + .trim(from: 0.521, to: 0.749) + .stroke(Color(hex: "#7DBBFF"), lineWidth: 40) + Circle() + .trim(from: 0.749, to: 0.888) + .stroke(Color(hex: "#71DD8C"), lineWidth: 40) + Circle() + .trim(from: 0.888, to: 1) + .stroke(Color(hex: "#A0BCE8"), lineWidth: 40) + } + .rotationEffect(.degrees(-89.9)) + .padding() + } +} + +// MARK: - 막대그래프 (더미) +struct BarChartView: View { + let values: [CGFloat] = [0.89, 0.5, 0.9, 0.3, 0.7] + let colors: [Color] = [ + Color(hex: "6BE6D3"), .black, Color(hex: "7DBBFF"), Color(hex: "B899EB"), Color(hex: "71DD8C")] + // 6BE6D3 + var body: some View { + HStack(alignment: .bottom, spacing: 33) { + ForEach(0..kPWm7L(gGgf}=o$-t; z&wajI-^s*Qwj5^wAGN%NA;V!RL9T%6mHE018;r2U_zrM$twuOmcF%|P8)IhXKKj9; zHlR(MGJ75X7BqtK$;q!v45u4qXhQ-smwZh6)`on-DizWVR3P*3iHy!07^lpCrds>s zfrjhQPU^2BLL=ao2Apjy2yMASnig0^?D)a2Kxvd8wrF4e>%0$dmgP>Ho z#PJ!@H?UAB|G_d8v z+dvS8|Jj#Z$RX+=;+DJ|;#6Q#0dx?X27n59lt2R*cVH?2SAb8zjV(c(fCD?st|Xp~ zB){jCFazJgUit0e*X+(evui>FJW3O|Zvz&ybl({|K>%$2vi`hWI?2P`B{fJPY6`L)e3&F#n%b01nU+&AKPJ!pk_%O^<+ zy(DI>)nauL1LedE^tF?50ED97ge~Tb5y=k*pMbc?pXMVwZS3G#3T0L{`Lwxx_0+uZ_T>px2136W5{U9kvm= r6YlmFEc^{YGdP~Y+@Jb{sGIf-L;)Om2?D<}00000NkvXXu0mjf4m%zZ literal 0 HcmV?d00001 diff --git a/StockMate/StockMate/resources/Assets.xcassets/defect.imageset/defect@3x.png b/StockMate/StockMate/resources/Assets.xcassets/defect.imageset/defect@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..518e2594251d0d5a67efc27c68fd010c968955ae GIT binary patch literal 1018 zcmV0)PsD3Lpgp;R@nZfT!Ubqg$=2JV?M_ zV#i*D^+!}4l{iw`4gEqLp!>HH;e{sZf_}55q_JwCE-2nm(qw-?U0^g!qZObw@Um3t zIte3Cs2R_~RmYDo^Rc8fD`q?jqATnB2;Jl!wH`mi125njCYfTnIZDqW4jUry_d*29 z`unzwXm&E^SA4c+rk3{L5cd&Q)XAPw>q-XSugW6brcXK9^k(kBF&?V44hFB3NRO5^ zJdpFrLm%((VKNDrXN>e%+4va>Cvc2=VM*U)#`D5Lx>%MMy-XyMGz<(M5Jc*JGdqFS zLmIN>M1Giztd~KJKjHZ4ij~ zx~}I0zwdCu_9LJ9Addj39+>ePp2=jZcdjDnpwDAMbE(oysby7%Q!u zuX*|Dhl$SR$F^laE3z=lu(o}x*^P6M9&aQ&haCU3DI^{<#q+FX)v2K{NkdT|B3Vo$ z=Y8GGLPi*TjoLsWOsb2zp!KCh_+mYu!d^x5RB!9i2En?zKEy zGkdiHRFg=?=qu*nG4UX7p{iuon)||1zq|5J^{Tdv+-Udqymy*UE$Q{X?^(ks69K;u zvKMl~$y}1&P72OF=_dgBb*-VnL;Q^Zyn0BlbRck^lez07*qoM6N<$g6WIdt^fc4 literal 0 HcmV?d00001 diff --git a/StockMate/StockMate/resources/Assets.xcassets/incoming.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/incoming.imageset/Contents.json new file mode 100644 index 0000000..3e19bc2 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/incoming.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "incoming@1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "incoming@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "incoming@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/incoming.imageset/incoming@1x.png b/StockMate/StockMate/resources/Assets.xcassets/incoming.imageset/incoming@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..09bdaf4db14fd01ec1760ce67879e54189e92f70 GIT binary patch literal 397 zcmV;80doF{P)C_{>QqRp1moFi`^78kS=-IWIbEjX*!1pmMoQ&|)KNF%`x- zd%vA`B)$VcV+_DrYc@sfI!N&cyeKVK(^j(c2%B&;8lCto>hFpuuha4-W(1;2xfy3Q zlO#|qiO9IG@LAF9?OH=cq$r5x!i*PHr#?iog*x~+JBSV;!BKBjWyy@^UuAHvinAbz z`?+p!{|6#2MTB)igey_$SwJrb$>e`YD(r!asd7_{d+dR#J~nyJURMUp^PvLY1<`TR rWb!asq;5Jh0=D9J&UW5g zJ;kcW%#ExPnjomyQQLWIwH1GVHv5lG+I=*ZxUDIs7a55<~Tc=CSFkUu`*`tD?E*S!Y#N=Ol#gHX625$P#M=l-$IiM=c$uo8*0G$L!Jfsz3S%qSU>Q|r$#9>K{{YMP56UUs%mUDJ5Uq}MF< zs%gCOs|gfmtl+*f#b-%q17z&VqSjA+DA5+P%2fFozZ$_}KmaB+g1C zjutUW*+nBF=tIfKI_8mNpC>3AM30ED3ne4P^%I5FHS-OWgx(3d)w6CmYlt?kQnrRg zNklur;q;BRhKrOLNnB$2lEagrkij f06}N+_%q@c;!WlnKs+!#00000NkvXXu0mjf`F$FJ literal 0 HcmV?d00001 diff --git a/StockMate/StockMate/resources/Assets.xcassets/incoming.imageset/incoming@3x.png b/StockMate/StockMate/resources/Assets.xcassets/incoming.imageset/incoming@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..433e6a2f37a86e122bf473dd3f2de2db754f7783 GIT binary patch literal 908 zcmV;719SX|P)gydp(kj%hbG*BHLdmf zBoZ1Dn?|*Ym5jel9NBT^8%s}5PbU0@xOY-X^lhwwWf1BNRFR(>rLoms+CGOz7P3yn zy`w7YsHypOjR+NnS1KIQs(e0>*|=`)QUddT@7lH3T{xoB8qG~Lt8Yvkn?J@=k?;Y7 z8=AQj_gZy0G9k0k`_wcy#ZyX5QW2pu(>q*#^gzanuzrs)s7mkXxCq_LtY03*0f`K8 z60$PpVp6J!FqvA&%9yAHKOy&jF_ny8>0>F+Rg>E{tO3XnCz`z9q*arB4wnjMH3VC>C%qLM4X9eN&C_>kD=^Ua=8ZyYUvjbCPA;!T1W&=}Dd2|_X=9^H) zj2B9oP$^|XrIZPk-1Cf`%p6vTX)tpG9>^slB7O3Z@!E7>3iZfY-_F&|G~>`m>Td7B z?6ks6BbYHmP|uN*Tr#pLwCiIX68ApVr8u#MoOsEa2{r&sRr8vJtDd$N;U#P4f@*K$ zl#^ztlro`G%7jWO6Dp-ls2G_(<^F=D#b=o1+^myJlpBGn8<%0w+Q*t4prG{rrPR72}%jTBzV*qIvu literal 0 HcmV?d00001 diff --git a/StockMate/StockMate/resources/Assets.xcassets/lack.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/lack.imageset/Contents.json new file mode 100644 index 0000000..b0bb50e --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/lack.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "lack@1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "lack@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "lack@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/lack.imageset/lack@1x.png b/StockMate/StockMate/resources/Assets.xcassets/lack.imageset/lack@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..fc64ec340933b7876ea593443c9e9a847722ff09 GIT binary patch literal 488 zcmVP)?B63P=eI3>RjSbvL3T7?X(k zE}Qq>x8K{hKNEx$u;bKr`+XaTTFl%;04v!C2oZ4war|2G{UFU3&o{QV-3I14Al4uo zq43;uVfwz<+4e}xO93&3CA_)#JVRQ9dLU04!UYhK@wMj-j^FakUazCK!T<13@w~-{ zplz?=nD{g>;zSA|cj8lq;3hK71{R4fB5J6;isN+_Pf|7@Z4D?3haK5$7fVnam756sel3q4ba)l%Oi6e3P9THjb!+jdBn&tZjtCdk0*V)p!jeI za4L{c>eV7~&bu}NkLYjBsR%f%5RN!w8+kU^Jg%C#zkJFU=0svNGm$7LE e_3`*eG2a2QOOQP)pXb8>0000q+Kn7Hl$xDWYx<{MkEp!TI-C^+94sFpiygI3(*sJpzW8G zvU}WUbZG;`I$zeMlJhti6ftNi60Ozt?72~w{1jU6HyHre)Zm&b zU;oSHT!gtI82~G$*ouJ#L+csA@iw|nByPzNIT!@{@5IHsShY)lYXc8R&Pr(a3nLDP zwMoR;EbS@KmXVmu=d`Z~k(Ed^)3m2R+qu;HIsc|Oyozn-c{Xt*?Kv#aCktq$KEf0J z0ZjxNcaFJSLCEwD4-b8uUv9_(=(Bf#rRa`Pn5?l=?E$xp90Sqw)~;HNi#}9HslF`6WstC(Y&+Uq+EcjYd^6or^5YP#n*_ zsr8shV5-yM6(HZ zX7ls>=dsz$`044Zh4by|U@UcXbQ#;U=sqhedkO4TSnk~t-@<(GwB0Tt-dyp@BHO|y zVX?eO9J4fGiAxU>-P300003@H8cI+ zfEpl#ugqw`-#G{-IV68(=FXja?+7x4-rdb#wA)3klq;5&*0k2^IGh(mZqIc@o)E&r z#}=;nBc*J-$!43PT5XX51SJTIyll63v=Bwox=6DiL__=j&)1pEkL6m;ql8FA@I|Fk zMC*K+S>!Mv9=+gTrB-XujLan1xX?j7y!KY>^&d1PQwhGRRNPywk1Pu9MTp~9Xz^*f zx}*uYESGm~egF59iDk4d-70l*zO_}N37M#cFSfV8!m94lGXestEA{%`1lNzIYkf~3 zcAcIfc&_GaDL2RI;v)qI;}zY9`sUAPi1$j$jZvaw1*b^tJ)WP-6`UBSCDv%&DfOMw zBbIR`hTbWL;IIpZ3ho-HXE|o$K$l(5wA)`|&JPlpdm)Y}#h|hF-q!2Y{^J9JQ#fvi z(Si{x^c%5(E`7_cV*150`M?|Oqo;)f-?-DdbA!NC6<&1ov@paPAGcUKe#6w^@C)PY zfDEHMJ@$cz7Piv5aQasd4?l+wotMks@l+qlAmLViXyN`}!U#(Jia7R@3=`HC2xiOt zq^lp(K-;}D5(vg}uSjzaJg{Ju*Xhzyexf-CA)@#EE>BCP{9JeEd%E3PX0qAT`ODL? zgs`zxN)>5A(7c*L-(I5yfwi*H%F}|t*$;`oE-eUfEA!mKE-eo3#Bu4g^CEcOVZk0* z0&C&?IL8d)Nmwo;iRUPAubp6J)`dUwF|q{`?-@n(N7xI;1_8p0aydwMVwJ|xQEM#> zPwOm$_ad! ze!z7j|E{h+DXonTC%CTO+rz_mq5D5FkI6K!87^e&SuF(y#qtzo6wYP!RqYKd4}Bpx z*=21(VG3CPmLFl41_ZM;vysL$4d(kjC!&NYG62A}5tyj0%#SAE9Q10fb{usscDZu4 zz5QFHtda*}o3P4-VdrB*Jn+Cabf;Q974WkvAvWW#i51Kq7``ACqPt=@oDrpmBZGMS z1%YVWtD6bnZmgD<3r-TnrD4vtwyJQ`?8TtQfDIJH?mJ^{3Yj>@a$c#FxMo5;af(KF ze1(IkYP&e53M!o<+K4>szOWk$dj#GfJ4ntHTL5O>|fnhEn$y{Hrb4dkmvc>^-2ymme;^D1@AE}A+e{sI)=W-3l b&{n+vA+!3{za2zy00000NkvXXu0mjfEkQUi literal 0 HcmV?d00001 diff --git a/StockMate/StockMate/resources/Assets.xcassets/location.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/location.imageset/Contents.json new file mode 100644 index 0000000..306ca5c --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/location.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "location.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/location.imageset/location.svg b/StockMate/StockMate/resources/Assets.xcassets/location.imageset/location.svg new file mode 100644 index 0000000..79f625f --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/location.imageset/location.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/StockMate/StockMate/resources/Assets.xcassets/notification.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/notification.imageset/Contents.json new file mode 100644 index 0000000..acfc443 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/notification.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "notification.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/notification.imageset/notification.svg b/StockMate/StockMate/resources/Assets.xcassets/notification.imageset/notification.svg new file mode 100644 index 0000000..a28bd9f --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/notification.imageset/notification.svg @@ -0,0 +1,3 @@ + + + diff --git a/StockMate/StockMate/resources/Assets.xcassets/profile_sample.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/profile_sample.imageset/Contents.json new file mode 100644 index 0000000..54602a7 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/profile_sample.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "profile_sample.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/profile_sample.imageset/profile_sample.png b/StockMate/StockMate/resources/Assets.xcassets/profile_sample.imageset/profile_sample.png new file mode 100644 index 0000000000000000000000000000000000000000..f5266f9b6a274bbc1567b029d4db89f30b5e54e3 GIT binary patch literal 504 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nC-&H|6fVg?3oVGw3ym^DWND9BhG zZvV`bwwOsOmCJ=cxTzoPc+nur6)SYtWJc4y5`>AWHNEOa{2z~*mrRgnr>LTw=D73U2-mL`fhO< z9{1fznS$^5%HDRRP84z!aoKfe?}OQ!1g8lFDi(gZ{Y=c&{NMM|3susKm~O6%@M39w nJ+*cUM_NB>h)D?E{lSoYwq@e)e&&0?h+^<`^>bP0l+XkKAq~=j literal 0 HcmV?d00001 diff --git a/StockMate/StockMate/resources/Assets.xcassets/transfer.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/transfer.imageset/Contents.json new file mode 100644 index 0000000..cd18e01 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/transfer.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "transfer@1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "transfer@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "transfer@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/transfer.imageset/transfer@1x.png b/StockMate/StockMate/resources/Assets.xcassets/transfer.imageset/transfer@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..24c23e143a45e1591b1febad9f6a9dd16cfef4fd GIT binary patch literal 480 zcmV<60U!Q}P)L6-7bqS;JpsIHV-p|EI?mS8w6)n* z?3c8=ZD+pu*_kOq4n|M!b^W17yd45z8<14^fpE+FXWl(5I}ImK7qc(U)^ALN3)ZfK z6-*8^&k~E|!(i>-@N!>F7P_nWwirD6)0COrrq3UcXa;+MZkL_rBqroAFO4<$AI>>^ zHzM>mQ3(0=3GPDc2v-)uKx)~-GTur3*#bBOE%gShpouw@Ayfr|Q-O#IssiG%Afh%@ z2PL3l)S`-4HX$t!xZpLEz?KQa-c$vGBPau+7Y>=cA~~tQP6(-2B*%y13aM)jS9f5O z(^6gt64vrUo5WpC$pljmGDuFaNYk1102Y2nY4|u4=V=Off``+t}3w`<* zGULdq+lakS)#0k!obfU?`6wb{8I$*MlI)jd^V3c7F91EMg=6xc)%ymB W7mO>@bj*kV0000o<^5leu71{sJo(X3n3<;vc}&ZB`B)M=m_j^KC7Tp z1>C!R5RIM8ud%69hy2a@<<|On?pt}Cd+mo)i&~oiH7>U{jwkhI3MeLxpW^S_^kE4Q zVNlX)ySZJC#`{#(fMp6N@NTcfPBP~!UI{2>g(V6%g1_6oI~{-c!o<7n`!9U?3M++< zc}+IbyMciFKOeogqp$e|4^~U$#Li~A9tS3lJ8{i|aIOB&1UGiNwe%*@U{8DTA25JJ zTK%HBaU$)VoBIYL9D)I?#j{1 z|HBediTDI&Bc11@v&JDafVU8JpY^G1K~s=0Eb)f|ZhK%zl5|Qu@635r%z;4vn)0?PVfr(K5?!)ve3vV`gVi&%WQwbtN5k@1FE6q$Rxn3U=C`2f%7 z;N`C!900000 LNkvXXu0mjfUPYEF literal 0 HcmV?d00001 diff --git a/StockMate/StockMate/resources/Assets.xcassets/transfer.imageset/transfer@3x.png b/StockMate/StockMate/resources/Assets.xcassets/transfer.imageset/transfer@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..31120bbf35ef9b352c89385ae0e11bb273b56517 GIT binary patch literal 1201 zcmV;i1Wx;jP)yIwR*PzE=Yr(w+UEabPvqYx~E|1s&EZSG~ZezOBl4`U(FL+-b zu>x713VO}nb#2-eQw58Gy02?C4Mf`U4ew;?`$;DwbAJY>ar>Aa4gU<1ipA2gvr;-fqqVI2D<3gRN++y^L__$Y^h#WKTcqo1O& z2hK<+n5~%&6(>+$808J7^ed=1gt#aFRW#H#RG)BKE6?;w=PxgcZ}_%S!CH)N1X+sb z_BN~u3UFJDRt?q!P6h2KDTTnAKu9%Jk;o`3*4%mNvh!bX)lqi^DqT=5+quGU6(ST; zcuqzgMUh#^-LX0XeYhqB{&zs7$-N}&t_b!sEO+wpq1F*5`fakO)d#K7l5B$*732tX z!s(s9<~6)pq<=(lGR=W?!87|RDm*!$6GE4BXnoL&BU;0KnA4>fpb7S88|QnQPd@H# z*7@ZDuPaTv1ck}MP_P-8ZO}vKQ!FVi%_#xVJJ5jPo8it73f4HSCuS0&7NaTC*_DH# zYspis@G1bam+@S#7%^=lwI!|sNaewX0^t!o{rGIN6?N^sV*63(a$@7?vb}gRd>wzY zip4bMAl}F}5-mjr#ewS38SmPg2oKp2d981&C=Q;fH>_pCE=>p)Yes-N3|OJC5k4MR z<~WH7fb-qWw$yQr4r(T1 zpv=HHP3y0WGj~V4bm>Vy2;pig7P8O!7_>eoblatV4imZEsEFDOs0JQ_`Kq^2gP%D5AjPL##JHTgTNC)6MWZk<6GvINFlHW^i z=I*}T+5PrH@GyUi>d0zcU7mrc1xS?dPzsT;exFQdGV9dq2&dddH z@ga$vm002ovPDHLk FV1f?J#&Q4v literal 0 HcmV?d00001 diff --git a/StockMate/StockMate/resources/Assets.xcassets/wait.imageset/wait@2x.png b/StockMate/StockMate/resources/Assets.xcassets/wait.imageset/wait@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..15de6d4508c5a3bd4f0bc51e720eaf213548c98a GIT binary patch literal 830 zcmV-E1Ht@>P)iImb+S@1awa4 zz_VFEW55G{-VsxDD4$1L05=oM{p}aNt4OC<*eDiO9imxLp(h0f-Yw6V9OLX6J2|vk z0u!tEILqyo*U0Pa-q@@iS1{%EIXvE+fi|gZeB(<8mthH7yO!&RhYcdL`(?!`y78DV zcp*@m^|Im;oOtX#_DmeiZ~GwmfNLT#w0f&)@-Y<{cyDdUjmJG~lt-?SCVI z@+!MFAHqPr37wTx+ZeA!Q)v^NGC8O#oX!b0atC#Vz%wXeqqa~-Z4w)`MK+Z{!+D-j zngYgY_&OOigc2B{I;jZ!Uh1eZpV4E;Hhh|L(UvKG&U{4LjQl255XJ&S}1t_^G0Eh_EB zV%{~=tD2g});@EG?Apwaw(^S(F3XaSt?v%M%+6+L{XyM;%dt?!qpXJf^4t8)w5Z=I zjWTQR>1c^BHy_kDmJjr&2%|d8Q*QmSUngBhF(OKjKbxXUf9)! zi-aT+oPp3G*!+Xw)c*2vhdG%`uvhz3hV*Zcnb0AKZrMLv%wy^TG4X2KJVyOTEK96H z$|KbV?|%CTF^Sc~Y{Hjp!rzf62vu?qmpDNgl*LH1m z{>&Tf(5O_MCVHaa;)v~_M8EA$TC_}dTGIbn9I*;+D-S)h02)uEVC|stJ7y=rZMM#$ zemT~pwYw*IgCZkwYVS9$hHsAuF5b9}q{W_lci4%IG3oYMx?N57Ltzbz2}eW#U^)w^l`MNKEzOi(j2$0z5jgMx#012o=H!e%}tlCVDn96J#Km9 zpFRV<-0R_?Z(^nB)20P$&%DgtPx?5W#fa(Qz(2sM6IgneX9N?zV$~shR&OHgY6q*2 zHf!!-tBC96IVm{;!CLgLA)t!jQM`jSK?wRFc#hh@nh;3IJq#*iZ6M{e3cn4Efh@<4 zOBbDY!I?tVG5`313=tVc9~2|AfeOEHFhIr64zps!CGk9lO1LW!le=?PgO{a41#&~n< z1W6ED|(yyM{3W;42 z&)c^nQ!}yjj77`rpBu4udI^pIC@(I*VL7;Jfi%w%fP{xUHV+`hWrn?-_;kzrF`gut z(2UQ$u8U+GB*WFACyvG}@|4;_1@{x4Qmcd|6+8T&HcT#h>`^>+K*ZCY{0pJN#%(vP bqj>)T&DE-(b`F{-00000NkvXXu0mjft^Xk0 literal 0 HcmV?d00001 diff --git a/StockMate/StockMate/resources/Color.swift b/StockMate/StockMate/resources/Color.swift index a4e0a95..0133471 100644 --- a/StockMate/StockMate/resources/Color.swift +++ b/StockMate/StockMate/resources/Color.swift @@ -32,16 +32,51 @@ extension Color { static let Dark = Color(hex: "#04150C") static let Light = Color(hex: "#F7F7F7") - // State - static let Success = Color(hex: "#00CB6A") - static let Warning = Color(hex: "#EFBE24") - static let Danger = Color(hex: "#F26666") + // Text static let textBlack = Color(hex: "#152C07") static let textGray1 = Color(hex: "#5D5C5D") static let textGray2 = Color(hex: "#BEBEBE") + // State + static let Success = Color(hex: "#00CB6A") + static let Incoming = Color(hex: "#5EDE99") + static let Danger = Color(hex: "#F26666") + static let Warning = Color(hex: "#EFBE24") + static let Defect = Color(hex: "#FF9E29") + static let Transfer = Color(hex: "#4DC2EC") + + // State Background Color + static let SuccessBg = Color(hex: "#00CB6A") + static let IncomingBg = Color(hex: "#E0FFF0") + static let DangerBg = Color(hex: "#FFE0E0") + static let WarningBg = Color(hex: "#FFFCE0") + static let DefectBg = Color(hex: "#FFDEB7") + static let TransferBg = Color(hex: "#E3F5FC") + + + // Inventory Status + static let InvIncoming = Color(hex: "#7CE12F") + static let InvUse = Color(hex: "#F6AD12") + static let InvStock = Color(hex: "#C1ACFF") + + // Inventory Status Background + static let InvIncomingBg = Color(hex: "#EAFADE") + static let InvUseBg = Color(hex: "#FEF2D9") + static let InvStockBg = Color(hex: "#E1D7FF") + + + // Status + static let StatusGreen = Color(hex: "#82E239") + static let StatusRed = Color(hex: "#F34838") + static let StatusPurple = Color(hex: "#7562FF") + + // Status Background + static let StatusGreenBg = Color(hex: "#EAFADE") + static let StatusRedBg = Color(hex: "#FDE0DD") + static let StatusPurpleBg = Color(hex: "#E9E6FF") + // 투명도 포함 예시 static let boxBgWhite = Color(hex: "#40FFFFFF") // 투명도 포함 } From 97cd4660acee1977c7b707e54161e83ca3691aab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Tue, 14 Oct 2025 09:55:42 +0900 Subject: [PATCH 05/85] =?UTF-8?q?[FEAT]=20=EC=9E=AC=EA=B3=A0=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=ED=99=94=EB=A9=B4=20UI=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../inventory/ui/IncomingScanView.swift | 70 ++++++ .../feature/inventory/ui/InventoryView.swift | 199 +++++++++++++++++- .../app/navigation/MainTabView.swift | 10 +- .../InvIncoming.imageset/Contents.json | 23 ++ .../InvIncoming.imageset/InvIncoming@1x.png | Bin 0 -> 320 bytes .../InvIncoming.imageset/InvIncoming@2x.png | Bin 0 -> 426 bytes .../InvIncoming.imageset/InvIncoming@3x.png | Bin 0 -> 539 bytes .../InvStock.imageset/Contents.json | 23 ++ .../InvStock.imageset/InvStock@1x.png | Bin 0 -> 377 bytes .../InvStock.imageset/InvStock@2x.png | Bin 0 -> 567 bytes .../InvStock.imageset/InvStock@3x.png | Bin 0 -> 760 bytes .../InvTrans.imageset/Contents.json | 23 ++ .../InvTrans.imageset/InvTrans@1x.png | Bin 0 -> 381 bytes .../InvTrans.imageset/InvTrans@2x.png | Bin 0 -> 598 bytes .../InvTrans.imageset/InvTrans@3x.png | Bin 0 -> 748 bytes .../InvUse.imageset/Contents.json | 23 ++ .../InvUse.imageset/InvUse@1x.png | Bin 0 -> 403 bytes .../InvUse.imageset/InvUse@2x.png | Bin 0 -> 633 bytes .../InvUse.imageset/InvUse@3x.png | Bin 0 -> 919 bytes 19 files changed, 365 insertions(+), 6 deletions(-) create mode 100644 StockMate/StockMate/app/feature/inventory/ui/IncomingScanView.swift create mode 100644 StockMate/StockMate/resources/Assets.xcassets/InvIncoming.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/InvIncoming.imageset/InvIncoming@1x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/InvIncoming.imageset/InvIncoming@2x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/InvIncoming.imageset/InvIncoming@3x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/InvStock.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/InvStock.imageset/InvStock@1x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/InvStock.imageset/InvStock@2x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/InvStock.imageset/InvStock@3x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/InvTrans.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/InvTrans.imageset/InvTrans@1x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/InvTrans.imageset/InvTrans@2x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/InvTrans.imageset/InvTrans@3x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/InvUse.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/InvUse.imageset/InvUse@1x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/InvUse.imageset/InvUse@2x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/InvUse.imageset/InvUse@3x.png diff --git a/StockMate/StockMate/app/feature/inventory/ui/IncomingScanView.swift b/StockMate/StockMate/app/feature/inventory/ui/IncomingScanView.swift new file mode 100644 index 0000000..706a507 --- /dev/null +++ b/StockMate/StockMate/app/feature/inventory/ui/IncomingScanView.swift @@ -0,0 +1,70 @@ +// +// IncomingScanView.swift +// StockMate +// +// Created by Admin on 10/13/25. +// + +import SwiftUI + +struct IncomingScanView: View { + var body: some View { + VStack(spacing: 30) { + // 상단 타이틀 + Text("입고 부품의 QR을 스캔해주세요") + .font(.headline) + .padding(.top, 30) + + // 스캔 영역 + ZStack { + RoundedRectangle(cornerRadius: 12) + .fill(Color.gray.opacity(0.2)) + .frame(width: 250, height: 250) + + RoundedRectangle(cornerRadius: 8) + .stroke(Color.blue, lineWidth: 3) + .frame(width: 220, height: 220) + .overlay( + // 모서리 강조 + ZStack { + Color.clear + .overlay( + Rectangle() + .trim(from: 0, to: 0.25) + .stroke(Color.blue, style: StrokeStyle(lineWidth: 5, lineCap: .round)) + ) + } + ) + } + .padding(.top, 20) + + Spacer() + + // 직접 등록 버튼 + Button(action: { + print("직접 등록 tapped") + }) { + Text("직접 등록 하기") + .fontWeight(.semibold) + .foregroundColor(.black) + .frame(maxWidth: .infinity) + .padding() + .background(Color.white) + .cornerRadius(10) + .shadow(color: .gray.opacity(0.3), radius: 2, x: 0, y: 2) + } + .padding(.horizontal, 40) + .padding(.bottom, 40) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background(Color.gray.opacity(0.1)) + .navigationTitle("입고 부품 등록") + .navigationBarTitleDisplayMode(.inline) + } +} + +#Preview { + NavigationStack { + IncomingScanView() + } +} diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift index 6f8161f..3480c5b 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift @@ -9,10 +9,207 @@ import SwiftUI struct InventoryView: View { var body: some View { - Text("재고 관리 화면") + NavigationStack { + VStack(alignment: .leading, spacing: 13) { + Text("재고 관리") + .font(.title2) + .bold() + .padding(.top, 13) + .frame(maxWidth: .infinity, alignment: .center) + + // 4개 버튼 영역 + GridMenuView() + + // 섹션 타이틀 + Text("얼마 남지 않았어요!") + .font(.system(size: 22, weight: .semibold)) + .foregroundColor(.black) + .padding(.horizontal,25) + .padding(.top) + + + // 공통 컴포넌트 리스트 + ScrollView{ + VStack(spacing: 12) { + // 예시 데이터 + let dummyData: [(String, String, Int, Int)] = [ + ("브레이크", "현대 아이오닉5", 5, 10), + ("엔진오일", "기아 EV6", 10, 12), + ("에어필터", "현대 코나", 8, 10) + ] + + ForEach(dummyData, id: \.0) { item in + StockShortageCard( + partName: item.0, + carModel: item.1, + currentCount: item.2, + minCount: item.3 + ) + } + } + .padding(.horizontal) + } + } + .background(Color.Light) + } } } +struct GridMenuView: View { + let menuItems = [ + ("입고 처리", "입고 내역", Color.InvIncoming, Color.InvIncomingBg, "InvIncoming", AnyView(IncomingScanView())), + ("사용 처리", "사용 내역", Color.InvUse, Color.InvUseBg, "InvUse", AnyView(IncomingScanView())), + ("이동 요청", "이동 내역", Color.Transfer, Color.TransferBg, "InvTrans", AnyView(IncomingScanView())), + ("재고 조회", "", Color.InvStock, Color.InvStockBg, "InvStock", AnyView(IncomingScanView())) + ] + + var body: some View { + LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())], spacing: 15) { + ForEach(menuItems, id: \.0) { item in + VStack(spacing: 8) { + + Image(item.4) + .resizable() + .scaledToFit() + .frame(width: 15, height: 15) + .foregroundColor(.white) + .padding(12) + .background(item.3) + .clipShape(RoundedRectangle(cornerRadius: 10)) + + HStack(spacing: 8) { + NavigationLink(destination: item.5) { + Text(item.0) + .font(.caption2) + .fontWeight(.semibold) + .foregroundColor(item.2) + .padding(.vertical, 6) + .padding(.horizontal, 10) + .background(item.3) + .cornerRadius(5) + } + + if !item.1.isEmpty { + Text(item.1) + .font(.caption2) + .fontWeight(.semibold) + .foregroundColor(item.2) + .padding(.vertical, 6) + .padding(.horizontal, 10) + .background(item.3) + .cornerRadius(5) + } + } + .padding(.top,5) + .font(.subheadline) + } + .frame(maxWidth: .infinity, minHeight: 120) + .background(RoundedRectangle(cornerRadius: 12).stroke(item.2, lineWidth: 1.5)) + } + .padding(.horizontal,3) + } + .padding() + .background(Color.White) + .padding(.horizontal) + } +} + +struct MenuCard: View { + let title1: String + let title2: String + let color: Color + let bgColor: Color + + var body: some View { + VStack(spacing: 8) { + Image(systemName: "shippingbox") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .foregroundColor(.white) + .padding(12) + .background(bgColor) + .clipShape(RoundedRectangle(cornerRadius: 10)) + + HStack(spacing: 8) { + Text(title1) + .font(.caption2) + .fontWeight(.semibold) + .foregroundColor(color) + .padding(.vertical, 6) + .padding(.horizontal, 10) + .background(bgColor) + .cornerRadius(5) + + if !title2.isEmpty { + Text(title2) + .font(.caption2) + .fontWeight(.semibold) + .foregroundColor(color) + .padding(.vertical, 6) + .padding(.horizontal, 10) + .background(bgColor) + .cornerRadius(5) + } + } + .padding(.top, 5) + } + .frame(maxWidth: .infinity, minHeight: 120) + .background(RoundedRectangle(cornerRadius: 12).stroke(color, lineWidth: 1.5)) + } +} + +struct StockShortageCard: View { + let partName: String + let carModel: String + let currentCount: Int + let minCount: Int + + var body: some View { + HStack(alignment: .center, spacing: 12) { + RoundedRectangle(cornerRadius: 8) + .fill(Color.gray.opacity(0.2)) + .frame(width: 68, height: 68) + + VStack(alignment: .leading, spacing: 4) { + Text(partName) + .font(.subheadline) + .fontWeight(.semibold) + Text(carModel) + .font(.subheadline) + .foregroundColor(.textGray1) + + } + + Spacer() + + VStack(alignment: .leading){ + Text("수량 부족") + .font(.system(size: 14, weight: .semibold)) + .fontWeight(.regular) + .foregroundColor(.StatusRed) + .padding(.vertical, 6) + .padding(.horizontal, 12) + .background(Color.StatusRedBg) + .cornerRadius(14) + + Text("현재수량: \(currentCount)개") + .font(.caption) + .foregroundColor(.textGray1) + + Text("최소수량: \(minCount)개") + .font(.caption) + .foregroundColor(.textGray1) + } + } + .padding(12) + .background(Color.white) + .cornerRadius(12) + .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 2) + } +} + + #Preview { InventoryView() } diff --git a/StockMate/StockMate/app/navigation/MainTabView.swift b/StockMate/StockMate/app/navigation/MainTabView.swift index 48835cc..0bf4973 100644 --- a/StockMate/StockMate/app/navigation/MainTabView.swift +++ b/StockMate/StockMate/app/navigation/MainTabView.swift @@ -15,11 +15,11 @@ struct MainTabView: View { // 메인 화면 ZStack { switch selectedTab { - case 0: HomeView() - case 1: OrderView() - case 2: InventoryView() - case 3: ContentView() - default: HomeView() + case 0: NavigationStack{ HomeView() } + case 1: NavigationStack{ OrderView() } + case 2: NavigationStack{ InventoryView() } + case 3: NavigationStack{ ContentView() } + default: NavigationStack{ HomeView() } } } .frame(maxWidth: .infinity, maxHeight: .infinity) diff --git a/StockMate/StockMate/resources/Assets.xcassets/InvIncoming.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/InvIncoming.imageset/Contents.json new file mode 100644 index 0000000..cd9ccd9 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/InvIncoming.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "InvIncoming@1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "InvIncoming@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "InvIncoming@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/InvIncoming.imageset/InvIncoming@1x.png b/StockMate/StockMate/resources/Assets.xcassets/InvIncoming.imageset/InvIncoming@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..3555741aed52cf11c59fb1bf63513ce452520a8d GIT binary patch literal 320 zcmeAS@N?(olHy`uVBq!ia0vp^{2``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eB{#8#G$B+ufqZfS7F**vcJ=p&1bhg)}TXyWv_lQ?vi*Kp@ZgFGhwjATSXD%McL-z+IN-ot=s7hp8ZQZYI z->$T8*3a@Aw;GBk2<@F*{J-8JJ79;+x9R#iQy=>tyFI~C+~wqwXOpdEdOXygzbb15 PdXT}>)z4*}Q$iB}#Vmte literal 0 HcmV?d00001 diff --git a/StockMate/StockMate/resources/Assets.xcassets/InvIncoming.imageset/InvIncoming@2x.png b/StockMate/StockMate/resources/Assets.xcassets/InvIncoming.imageset/InvIncoming@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..550dfd63d7b9e8539eedcc13b5560994cb2ed5fc GIT binary patch literal 426 zcmV;b0agBqP)E>IbAsRUT^o zwE0PiEhoxnJ9nRtmYXcKn%}_48AuTsB2?PaEU}79X58S15UpaTIZb4h#eODKS&KCy zqJSKVr+#;nZ{zEiv8fP|hfU_ir_xOoeu>KyrbRRP3~6K})D0pc5uHinuQq|BM~Sr3 z4}=Htar8>Qoj2J^=5yB#64i(SbH4vPt)50^)}@nkFe?1*=aIQa1m?|zL-)hqhIBfA z;iT`2;duELVwFk8YXs}X7w@uU UR;WndaR2}S07*qoM6N<$g4a2>^Z)<= literal 0 HcmV?d00001 diff --git a/StockMate/StockMate/resources/Assets.xcassets/InvIncoming.imageset/InvIncoming@3x.png b/StockMate/StockMate/resources/Assets.xcassets/InvIncoming.imageset/InvIncoming@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..99fa46886770fce25f6082ae02528822b3df6a73 GIT binary patch literal 539 zcmeAS@N?(olHy`uVBq!ia0vp^x**KK1|+Sd9?b$$oCO|{#S9FJ79h;%I?XTvD9BhG zoIx7<}RuGzi$=JfAQ z0*72L+*wh=5YXSKrqkT8RpwbQFR!z`pGV7W>7(nFZnr({sJZ0v(y8;q1CbWfTc3j! zzbvT@opVJw@q(~U^MSiXNjqPMUf&=lxBZd8w8H&7Pfj%NxgBERIA@C{5Pe&^w!2O0 z=BFs@zq?b@9j87}icVlUz2NQx@oKH59isMs3Ytp=0++be*-TAq307G8@P*C1z>DwK zH>IcPIsJ-qGjYG$;V-yRHEO?4RN=GdQ?Gvv-12qxf}R&rrpo7Ut3JKd^&xIf`ERgY zKgu@14tj`a$Jb`A z{C1+B|L_jo^CEt?(9zZK%eiU`_p5vjpX;6$wMea`a{I598=@Gsmj<6ny_c_L{?+L3 ztRLUb-_lRtZF8*d&$88%HVA$^^`|J~e^tHU7mL}2d;!LhTGN$gr96ExNq^sl7Orz) SB^m!h^2U|f5hJ4*0P-KW#D@MoMcqg6|cZ> zX+}T0;}V*&gVK>cxRb!d_au0SLdStknDG{#>lrVtmB1KzS3p<}M)YzPmXKXH$_<<> zj42mHp#pFX40J%igzE3?>fHI=nD}$w+-{6M~?W0Jm8)G6sDV2oan?Z`I(w zMD~hyD(TUMxkG^%cEqr_QE~@=g#SleS~fwWo$(`w@Ls` zg0n$S4oDtp*KM%B{$MQ>O-!d6khGFEXdbLL2W^>nS0>c1VOxCLdX-Z? zcV%KdE#i!Helt^Pg=xc}2HVCZEy9IkmUR&dgw44kW(*Bh?`NHS=fp)TZ?e7zJEFCT z6~~vRI_S#S72NRFUJ?iO`ET(zU*yvN*qcUi3meAHxwZyA53}3!F)rO(TM1wZ!e*!*Bt9~Ue_fG%-002ovPDHLk FV1joh?05hG literal 0 HcmV?d00001 diff --git a/StockMate/StockMate/resources/Assets.xcassets/InvStock.imageset/InvStock@3x.png b/StockMate/StockMate/resources/Assets.xcassets/InvStock.imageset/InvStock@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..bb4c9891acd8e3b055ce68d407208ecd23d54948 GIT binary patch literal 760 zcmV`eKlma8ZHj)~T-yr^bg<)yU_jv~ za`VG>ti=WP1hMED3hrW$u!3%#zs=&wyoR^!Pm*9qh=(>Z8U7YdMP@l@mjo+idJOiE zN^~eH(V?jO1T+0K>N&c(otOItKQmUTQD)HNTRuY|yhgf~VMxJ!+zD11EKVJhBGwy1 zAe={fQSJ{*I|WS&*U|46<7srh!GkR46Z($A@*DoF=Ve+vaqOvf?IJUIb20 z>&0F~3j~`U&Z0STf7bPLq@R7PFNO2;$mESCs`v*^WA zKE~6E*g%d!d}VLj$1*O)*HgbOwv-joCFeVfHrc)LFQsPYHgtR+pQ6J``5iu_Xcw=s zQVSg6rr*1D{VTop25@(I!0ozz*^tjD!`D95eXv=~a+Wt6+V~Gu7wg``W z$lZxy-8q?;Kn_c~qc2e9#(wTUiL5|5Ax{^_kP61$AnTyR1_F1treqmk2uRcu^5FOB zbMSMhVQLpH5HWhf;N#(Vns-qj(~K4U;j=^koUB-*QrCNJ_xsHG_c;_BE*`g1d>Y0u zr(}7?v*vE;_)HGvl`Y9`X1zTZzu$lLe(sODpVjKkcdmEEsI{+sl^=95+c#{@Ad^tXl!Ye zI}mU=e?ooq$)n3wKPa)=aipl2Ve`YC69w9x-khni^Szt%`BMy*{s$vPffp=Yy3U`Ol z%sk$Jk>cPu42s$CldO^E>+d%+?}HG?V7{106CnsbI@x`sYZ^?J;q;)B}vAsT4NGbmhnoP|207dxb{|b0#L1dMwu8 zqgFb%#$|W{C1E+z7y+aoJLl4TST|nWaV0_Sv`zGv`Qm5Y1P9FIB*xgbuxvc6n@}9d z7~2+>Z(29Of&QQYbm)V+$Ew$p!R+6rX%Z8-av&&jX`?gSDuTj_zi%LauonVI<4z_n z@=fmjQ^-1bN5^>al;UG@`nQXw*xAX`ekU&pC%-Nt=hUGjh#ne+GI{0uKHHJ6Dy+3< zct%>pu@=$8+kauo#NzMrWoQ6m)stL*n|3YJfp@3K4=}kOQjm?&98)PY{4aPzBSnRC zuT~ySqa~pEoEk}qv-f$nXe)4z29WyVjij7m5kjYwOr&mV*)zuTSS)WTS5Ebn4};3K k`q+@Awjt;>L^SrAHz)|OfqbjJZvX%Q07*qoM6N<$f{-T;Jpcdz literal 0 HcmV?d00001 diff --git a/StockMate/StockMate/resources/Assets.xcassets/InvTrans.imageset/InvTrans@3x.png b/StockMate/StockMate/resources/Assets.xcassets/InvTrans.imageset/InvTrans@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..4c743a4dfcce4893536d830ec25cc4a6e4da07be GIT binary patch literal 748 zcmV*@&Pah-`oi2X*(gB3dv!@t;08gN7WQ zFOJD#Yeiczk4;mWX>Y#l!LPXTZB1vm~- zE%*Dn`vk1?c*dgtOY^uLHtmpoNg-cVJY&3!LW;ARu`=#73XdhxnL|v#0yI`=Ypfz$ zo8A=8i_hN?nh{bWV-)h*Vrw3o!r=|Q8vz?^g1RPu}hQ+NP84DO*=jE77PrG@G zd}pn`R#HJQkzJv3{m;--_6=jksG)5#8H`xBS zY`Gnj;$TwOtyVp%+g!M5IK}$AJE6aSxqkdB+WWe6Mtmp+jVPyu3{&95_m;skZB^uSbsgjf&lCoiE!)C3@7b z*hfxz!pP9cBh|d%p`Aqsiqq$NeXdWGbi}Aiy1PIg+4T3s5w(X>$G-kYN!tu`2@*>Y eZmjj?k?jvS%>184nsL+s0000P)s xy>ADKjlN8}LQ<1hzM$$U7?Hvd2TESq_!IIckHbMM5G()y002ovPDHLkV1l^PogV-I literal 0 HcmV?d00001 diff --git a/StockMate/StockMate/resources/Assets.xcassets/InvUse.imageset/InvUse@2x.png b/StockMate/StockMate/resources/Assets.xcassets/InvUse.imageset/InvUse@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..429fcbcbda2d9b934372c5abc82522631f475aa9 GIT binary patch literal 633 zcmV-<0*3vGP)@K@kyhADhccVH*}Gvt7Le|244W1^HU~7n?a)6j-3Bh7B&d zgVeCm^D{2Y8{DsFivrbYl>j(P1+A!98)hVd4v4DSbr^b*-h1}q;uB}w^EY9mQSd0J#DjqnHjHP9_ zzaIlO4S_7#80ZY28E4qdpZOr0?Dw~ekRrbGrYxmAq`yrBC`bSg0k6=&)13YN3Q__g zCHz+LkROI=72O4aECSv$&lf(BM(}N2^pb z=)W>~h?MUDGSo8MU|UFeP4_EL+6Kt8`&RJ`)bH*gVp~XP=Bu3s_l<@}zg0RzkTpmL z7meqYoc+y>8ag^iz{bmJXAkLH?^F92(ndmC(y&?VS5vty9AW>wctc}yGtf!zR?vD- z(u8~pp)c-k1ce#X5cG6=DiwZMJ(vu1VQ96T*(k?8(qi`q!3yDqhW2cfNRmMt(OQzpaHhiae2nz7EzPP=K z63%aTYLJjv=^;E!l}q%hs>g}Xst-x{R7aSJmCoB<2I0PJ|EzBfJ zc`W|Z5FcnUrakK%T9@84cl1|CqevW0xwIdks6>ggaBjTr_7rI4>4`Xv+|S$|txgG} z@|41ib`ikhX0>P2s^+QWXt|T#HpMJbOL_V2uJ)v%%DNiQinVlBtfjMJN}9_fSRtZ( zc%$em{F}^NDmYzO5hAt--3^MBP1?5(26>Clv|vRDZz)q$XpZ{sTP25@iIxX4g_az+ tf**}(v)ZvanazuuOU9)Uam2#h_yg19p9U&smqY*n002ovPDHLkV1k6Qt5E;| literal 0 HcmV?d00001 From 5fc9887ccd282c248762a7f4d24156965900d310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Tue, 14 Oct 2025 11:07:25 +0900 Subject: [PATCH 06/85] =?UTF-8?q?[FEAT]=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20API=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/feature/user/data/UserApi.swift | 8 ++++++++ .../feature/user/data/UserRepositoryImpl.swift | 8 ++++++++ .../user/domain/UserRepositoryProtocol.swift | 8 ++++++++ .../feature/user/ui/ProfileCircleView.swift | 18 ++++++++++++++++++ .../app/feature/user/ui/UserInfoView.swift | 18 ++++++++++++++++++ .../feature/user/viewmodel/UserViewModel.swift | 8 ++++++++ 6 files changed, 68 insertions(+) create mode 100644 StockMate/StockMate/app/feature/user/data/UserApi.swift create mode 100644 StockMate/StockMate/app/feature/user/data/UserRepositoryImpl.swift create mode 100644 StockMate/StockMate/app/feature/user/domain/UserRepositoryProtocol.swift create mode 100644 StockMate/StockMate/app/feature/user/ui/ProfileCircleView.swift create mode 100644 StockMate/StockMate/app/feature/user/ui/UserInfoView.swift create mode 100644 StockMate/StockMate/app/feature/user/viewmodel/UserViewModel.swift diff --git a/StockMate/StockMate/app/feature/user/data/UserApi.swift b/StockMate/StockMate/app/feature/user/data/UserApi.swift new file mode 100644 index 0000000..5dacff4 --- /dev/null +++ b/StockMate/StockMate/app/feature/user/data/UserApi.swift @@ -0,0 +1,8 @@ +// +// UserApi.swift +// StockMate +// +// Created by Admin on 10/14/25. +// + +import Foundation diff --git a/StockMate/StockMate/app/feature/user/data/UserRepositoryImpl.swift b/StockMate/StockMate/app/feature/user/data/UserRepositoryImpl.swift new file mode 100644 index 0000000..bc24e15 --- /dev/null +++ b/StockMate/StockMate/app/feature/user/data/UserRepositoryImpl.swift @@ -0,0 +1,8 @@ +// +// UserRepositoryImpl.swift +// StockMate +// +// Created by Admin on 10/14/25. +// + +import Foundation diff --git a/StockMate/StockMate/app/feature/user/domain/UserRepositoryProtocol.swift b/StockMate/StockMate/app/feature/user/domain/UserRepositoryProtocol.swift new file mode 100644 index 0000000..ce3096e --- /dev/null +++ b/StockMate/StockMate/app/feature/user/domain/UserRepositoryProtocol.swift @@ -0,0 +1,8 @@ +// +// UserRepositoryProtocol.swift +// StockMate +// +// Created by Admin on 10/14/25. +// + +import Foundation diff --git a/StockMate/StockMate/app/feature/user/ui/ProfileCircleView.swift b/StockMate/StockMate/app/feature/user/ui/ProfileCircleView.swift new file mode 100644 index 0000000..b144a70 --- /dev/null +++ b/StockMate/StockMate/app/feature/user/ui/ProfileCircleView.swift @@ -0,0 +1,18 @@ +// +// ProfileCircleView.swift +// StockMate +// +// Created by Admin on 10/14/25. +// + +import SwiftUI + +struct ProfileCircleView: View { + var body: some View { + Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + } +} + +#Preview { + ProfileCircleView() +} diff --git a/StockMate/StockMate/app/feature/user/ui/UserInfoView.swift b/StockMate/StockMate/app/feature/user/ui/UserInfoView.swift new file mode 100644 index 0000000..8ba7f71 --- /dev/null +++ b/StockMate/StockMate/app/feature/user/ui/UserInfoView.swift @@ -0,0 +1,18 @@ +// +// UserInfoView.swift +// StockMate +// +// Created by Admin on 10/14/25. +// + +import SwiftUI + +struct UserInfoView: View { + var body: some View { + Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + } +} + +#Preview { + UserInfoView() +} diff --git a/StockMate/StockMate/app/feature/user/viewmodel/UserViewModel.swift b/StockMate/StockMate/app/feature/user/viewmodel/UserViewModel.swift new file mode 100644 index 0000000..9378545 --- /dev/null +++ b/StockMate/StockMate/app/feature/user/viewmodel/UserViewModel.swift @@ -0,0 +1,8 @@ +// +// UserViewModel.swift +// StockMate +// +// Created by Admin on 10/14/25. +// + +import Foundation From 1c29c302ac7ed6ea9327742c77273a7a7148a114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Wed, 15 Oct 2025 14:26:19 +0900 Subject: [PATCH 07/85] =?UTF-8?q?[FEAT]=20=EC=9E=AC=EA=B3=A0=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20UI=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/feature/auth/ui/HomeView.swift | 23 +- .../feature/inventory/ui/InventoryView.swift | 228 +++++++----------- .../app/feature/orders/ui/OrderView.swift | 122 +++++++++- .../app/feature/user/data/UserApi.swift | 31 +++ .../user/data/UserRepositoryImpl.swift | 8 + .../user/domain/UserRepositoryProtocol.swift | 4 + .../feature/user/ui/ProfileCircleView.swift | 18 +- .../app/feature/user/ui/UserInfoView.swift | 26 +- .../user/viewmodel/UserViewModel.swift | 37 ++- .../app/navigation/MainTabView.swift | 4 +- StockMate/StockMate/resources/Color.swift | 2 +- 11 files changed, 350 insertions(+), 153 deletions(-) diff --git a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift index 33c3bb4..8a8d395 100644 --- a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift @@ -9,25 +9,25 @@ import SwiftUI struct HomeView: View { + @EnvironmentObject var authViewModel: AuthViewModel + @StateObject private var userViewModel = UserViewModel() + var body: some View { ScrollView { VStack(alignment: .leading, spacing: 24) { // 상단 프로필 HStack(spacing: 16) { - Image("profile_sample") // 샘플 이미지 - .resizable() - .frame(width: 50, height: 50) - .clipShape(Circle()) + ProfileCircleView(name: userViewModel.userInfo?.owner ?? "사용자", size: 50) VStack(alignment: .leading, spacing: 4) { HStack { Image("location") .foregroundColor(.gray) - Text("가산점") + Text(userViewModel.userInfo?.storeName ?? "가게명 없음") .foregroundColor(.gray) .font(.subheadline) } - Text("Nadila Aulia") + Text(userViewModel.userInfo?.owner ?? "이름 없음") .font(.title3.bold()) .foregroundColor(Color(hex: "#2B3A1A")) } @@ -122,6 +122,17 @@ struct HomeView: View { .padding(.vertical) } .background(Color.Light) + .onAppear { + Task { await userViewModel.loadUserInfo() } + } + // 화면 디자인 시 잠시 주석처리 +// // ✅ 세션 만료 시 자동으로 로그인 뷰로 이동 +// .onChange(of: userViewModel.shouldGoToLogin) { shouldGo in +// if shouldGo { +// print("세션 만료됨 → 로그인 화면으로 이동") +// authViewModel.logout() +// } +// } } } diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift index 3480c5b..96756f0 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift @@ -11,102 +11,108 @@ struct InventoryView: View { var body: some View { NavigationStack { VStack(alignment: .leading, spacing: 13) { - Text("재고 관리") - .font(.title2) - .bold() - .padding(.top, 13) - .frame(maxWidth: .infinity, alignment: .center) - - // 4개 버튼 영역 - GridMenuView() - - // 섹션 타이틀 - Text("얼마 남지 않았어요!") - .font(.system(size: 22, weight: .semibold)) - .foregroundColor(.black) - .padding(.horizontal,25) - .padding(.top) - - - // 공통 컴포넌트 리스트 - ScrollView{ - VStack(spacing: 12) { - // 예시 데이터 - let dummyData: [(String, String, Int, Int)] = [ - ("브레이크", "현대 아이오닉5", 5, 10), - ("엔진오일", "기아 EV6", 10, 12), - ("에어필터", "현대 코나", 8, 10) - ] - - ForEach(dummyData, id: \.0) { item in - StockShortageCard( - partName: item.0, - carModel: item.1, - currentCount: item.2, - minCount: item.3 - ) - } + Text("재고 관리") + .font(.title2) + .bold() + .padding(.top, 13) + .frame(maxWidth: .infinity, alignment: .center) + + // 4개 버튼 영역 + GridMenuView() + + // 섹션 타이틀 + Text("얼마 남지 않았어요!") + .font(.system(size: 22, weight: .semibold)) + .foregroundColor(.black) + .padding(.horizontal, 25) + .padding(.top) + + // 공통 컴포넌트 리스트 + ScrollView { + VStack(spacing: 12) { + // 예시 데이터 + let dummyData: [(String, String, Int, Int)] = [ + ("브레이크", "현대 아이오닉5", 5, 10), + ("엔진오일", "기아 EV6", 10, 12), + ("에어필터", "현대 코나", 8, 10), + ] + + ForEach(dummyData, id: \.0) { item in + StockShortageCard( + partName: item.0, + carModel: item.1, + currentCount: item.2, + minCount: item.3 + ) } - .padding(.horizontal) } + .padding(.horizontal) } - .background(Color.Light) + } + .background(Color.Light) } } } struct GridMenuView: View { let menuItems = [ - ("입고 처리", "입고 내역", Color.InvIncoming, Color.InvIncomingBg, "InvIncoming", AnyView(IncomingScanView())), - ("사용 처리", "사용 내역", Color.InvUse, Color.InvUseBg, "InvUse", AnyView(IncomingScanView())), - ("이동 요청", "이동 내역", Color.Transfer, Color.TransferBg, "InvTrans", AnyView(IncomingScanView())), - ("재고 조회", "", Color.InvStock, Color.InvStockBg, "InvStock", AnyView(IncomingScanView())) + ("재고조회", Color.InvIncoming, Color.InvIncomingBg, AnyView(IncomingScanView())), + ("입출고 히스토리", Color.InvUse, Color.InvUseBg, AnyView(IncomingScanView())), + ("입고처리", Color.Transfer, Color.TransferBg, AnyView(IncomingScanView())), + ("사용처리", Color.InvStock, Color.InvStockBg, AnyView(IncomingScanView())), ] - + var body: some View { - LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())], spacing: 15) { - ForEach(menuItems, id: \.0) { item in - VStack(spacing: 8) { - - Image(item.4) - .resizable() - .scaledToFit() - .frame(width: 15, height: 15) - .foregroundColor(.white) - .padding(12) - .background(item.3) - .clipShape(RoundedRectangle(cornerRadius: 10)) - - HStack(spacing: 8) { - NavigationLink(destination: item.5) { - Text(item.0) - .font(.caption2) - .fontWeight(.semibold) - .foregroundColor(item.2) - .padding(.vertical, 6) - .padding(.horizontal, 10) - .background(item.3) - .cornerRadius(5) - } - - if !item.1.isEmpty { - Text(item.1) - .font(.caption2) - .fontWeight(.semibold) - .foregroundColor(item.2) - .padding(.vertical, 6) - .padding(.horizontal, 10) - .background(item.3) - .cornerRadius(5) + VStack(spacing: 20) { + LazyVGrid( + columns: [ + GridItem(.flexible(), spacing: 15), + GridItem(.flexible(), spacing: 15), + ], + spacing: 15 + ) { + + ForEach(menuItems, id: \.0) { item in + ZStack { + // 그림자 전용 레이어 (NavigationLink 뒤에 위치) + RoundedRectangle(cornerRadius: 16) + .fill(Color.white) + .shadow( + color: .black.opacity(0.25), + radius: 4, + x: 0, + y: 4 + ) + + // 버튼 본체 + NavigationLink(destination: item.3) { + VStack(spacing: 12) { + Image("InvStock") + .renderingMode(.template) + .resizable() + .scaledToFit() + .frame(width: 24, height: 24) + .foregroundColor(item.1) + .padding(.top, 20) + + Text(item.0) + .font(.system(size: 15, weight: .semibold)) + .foregroundColor(item.1) + .padding(.bottom, 20) + } + .frame(maxWidth: .infinity, minHeight: 120) + .background(item.2.opacity(0.5)) + .overlay( + RoundedRectangle(cornerRadius: 16) + .stroke(item.1, lineWidth: 1.2) + ) + .cornerRadius(16) } + .buttonStyle(PlainButtonStyle()) } - .padding(.top,5) - .font(.subheadline) } - .frame(maxWidth: .infinity, minHeight: 120) - .background(RoundedRectangle(cornerRadius: 12).stroke(item.2, lineWidth: 1.5)) } - .padding(.horizontal,3) + .padding(.horizontal, 10) } .padding() .background(Color.White) @@ -114,63 +120,18 @@ struct GridMenuView: View { } } -struct MenuCard: View { - let title1: String - let title2: String - let color: Color - let bgColor: Color - - var body: some View { - VStack(spacing: 8) { - Image(systemName: "shippingbox") - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .foregroundColor(.white) - .padding(12) - .background(bgColor) - .clipShape(RoundedRectangle(cornerRadius: 10)) - - HStack(spacing: 8) { - Text(title1) - .font(.caption2) - .fontWeight(.semibold) - .foregroundColor(color) - .padding(.vertical, 6) - .padding(.horizontal, 10) - .background(bgColor) - .cornerRadius(5) - - if !title2.isEmpty { - Text(title2) - .font(.caption2) - .fontWeight(.semibold) - .foregroundColor(color) - .padding(.vertical, 6) - .padding(.horizontal, 10) - .background(bgColor) - .cornerRadius(5) - } - } - .padding(.top, 5) - } - .frame(maxWidth: .infinity, minHeight: 120) - .background(RoundedRectangle(cornerRadius: 12).stroke(color, lineWidth: 1.5)) - } -} - struct StockShortageCard: View { let partName: String let carModel: String let currentCount: Int let minCount: Int - + var body: some View { HStack(alignment: .center, spacing: 12) { RoundedRectangle(cornerRadius: 8) .fill(Color.gray.opacity(0.2)) .frame(width: 68, height: 68) - + VStack(alignment: .leading, spacing: 4) { Text(partName) .font(.subheadline) @@ -178,12 +139,10 @@ struct StockShortageCard: View { Text(carModel) .font(.subheadline) .foregroundColor(.textGray1) - } - Spacer() - - VStack(alignment: .leading){ + + VStack(alignment: .leading) { Text("수량 부족") .font(.system(size: 14, weight: .semibold)) .fontWeight(.regular) @@ -192,11 +151,11 @@ struct StockShortageCard: View { .padding(.horizontal, 12) .background(Color.StatusRedBg) .cornerRadius(14) - + Text("현재수량: \(currentCount)개") .font(.caption) .foregroundColor(.textGray1) - + Text("최소수량: \(minCount)개") .font(.caption) .foregroundColor(.textGray1) @@ -209,7 +168,6 @@ struct StockShortageCard: View { } } - #Preview { InventoryView() } diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderView.swift index 1b4aa7a..95b7f90 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderView.swift @@ -2,17 +2,135 @@ // OrderView.swift // StockMate // -// Created by Admin on 10/12/25. +// Created by Admin on 10/14/25. // import SwiftUI struct OrderView: View { var body: some View { - Text("발주 화면") + VStack(spacing: 0) { + // 상단 타이틀 + Text("발주 목록") + .font(.headline) + .padding(.top, 16) + + // 필터 탭 버튼 +// HStack(spacing: 10) { +// ForEach(["Product", "Category", "Payment", "status"], id: \.self) { title in +// Text(title) +// .font(.system(size: 14, weight: .medium)) +// .foregroundColor(.black.opacity(0.8)) +// .padding(.horizontal, 14) +// .padding(.vertical, 8) +// .background(Color.white) +// .cornerRadius(10) +// .shadow(color: Color.black.opacity(0.05), radius: 1, x: 0, y: 1) +// } +// } +// .padding(.top, 12) + + // 주문 리스트 + ScrollView { + VStack(alignment: .leading, spacing: 16) { + OrderSection(date: "2025/09/30", orders: [ + OrderItem(image: "bolt.fill", name: "현대 아이오닉5", detail: "브레이크 등/ 5개", price: "₩300,000", status: "배송중", color: .green), + OrderItem(image: "bolt.fill", name: "현대 아이오닉5", detail: "브레이크 등/ 5개", price: "₩300,000", status: "승인완료", color: .green), + OrderItem(image: "bolt.fill", name: "현대 아이오닉5", detail: "브레이크 등/ 5개", price: "₩300,000", status: "승인완료", color: .green), + OrderItem(image: "bolt.fill", name: "현대 아이오닉5", detail: "브레이크 등/ 5개", price: "₩300,000", status: "승인 대기", color: .orange) + ]) + + OrderSection(date: "2025/09/29", orders: [ + OrderItem(image: "bolt.fill", name: "현대 아이오닉5", detail: "브레이크 등/ 5개", price: "₩300,000", status: "승인 거절", color: .red), + OrderItem(image: "bolt.fill", name: "현대 아이오닉5", detail: "브레이크 등/ 5개", price: "₩300,000", status: "입고완료", color: .blue) + ]) + } + .padding(.horizontal) + .padding(.vertical, 10) + } + + // 하단 고정 버튼 + Button(action: { + // 발주 요청 액션 + }) { + Text("발주 요청") + .font(.system(size: 16, weight: .semibold)) + .foregroundColor(.white) + .frame(maxWidth: .infinity) + .padding() + .background(Color(#colorLiteral(red: 0.215, green: 0.318, blue: 0.686, alpha: 1))) // #374EAF + .cornerRadius(12) + .padding(.horizontal) + .padding(.bottom, 12) + } + } + .background(Color(.systemGray6)) + .ignoresSafeArea(edges: .bottom) } } +struct OrderSection: View { + var date: String + var orders: [OrderItem] + + var body: some View { + VStack(alignment: .leading, spacing: 10) { + Text(date) + .font(.system(size: 13, weight: .medium)) + .foregroundColor(.gray) + + ForEach(orders, id: \.id) { order in + HStack { + Image(systemName: order.image) + .resizable() + .scaledToFit() + .frame(width: 40, height: 40) + .padding(6) + .background(Color.white) + .cornerRadius(10) + .shadow(color: .black.opacity(0.05), radius: 2) + + VStack(alignment: .leading, spacing: 2) { + Text(order.name) + .font(.system(size: 15, weight: .semibold)) + Text(order.detail) + .font(.system(size: 13)) + .foregroundColor(.gray) + } + Spacer() + + VStack(alignment: .trailing, spacing: 6) { + Text(order.price) + .font(.system(size: 15, weight: .medium)) + .foregroundColor(.purple) + Text(order.status) + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(order.color) + .padding(.horizontal, 10) + .padding(.vertical, 4) + .background(order.color.opacity(0.1)) + .cornerRadius(10) + } + } + .padding() + .background(Color.white) + .cornerRadius(14) + .shadow(color: Color.black.opacity(0.03), radius: 2) + } + } + } +} + +struct OrderItem: Identifiable { + let id = UUID() + var image: String + var name: String + var detail: String + var price: String + var status: String + var color: Color +} + #Preview { OrderView() } diff --git a/StockMate/StockMate/app/feature/user/data/UserApi.swift b/StockMate/StockMate/app/feature/user/data/UserApi.swift index 5dacff4..368f6ff 100644 --- a/StockMate/StockMate/app/feature/user/data/UserApi.swift +++ b/StockMate/StockMate/app/feature/user/data/UserApi.swift @@ -6,3 +6,34 @@ // import Foundation +import Alamofire + +struct UserInfoResponse: Decodable { + let status: Int + let success: Bool + let message: String + let data: UserInfo? +} + +struct UserInfo: Decodable { + let createdAt: String + let updatedAt: String + let id: Int + let memberId: Int + let email: String + let owner: String + let address: String + let storeName: String + let businessNumber: String + let latitude: Double + let longitude: Double + let role: String + let verified: String +} + +enum UserApi { + static func getUserInfo() -> DataRequest { + let url = ApiClient.baseURL + "api/v1/user/my" + return ApiClient.shared.request(url, method: .get) + } +} diff --git a/StockMate/StockMate/app/feature/user/data/UserRepositoryImpl.swift b/StockMate/StockMate/app/feature/user/data/UserRepositoryImpl.swift index bc24e15..7bd1289 100644 --- a/StockMate/StockMate/app/feature/user/data/UserRepositoryImpl.swift +++ b/StockMate/StockMate/app/feature/user/data/UserRepositoryImpl.swift @@ -6,3 +6,11 @@ // import Foundation +import Alamofire + +final class UserRepositoryImpl: UserRepositoryProtocol { + func getUserInfo() async -> AppResult> { + let dataReq = UserApi.getUserInfo() + return await safeApi(dataReq, decodeTo: ApiResponse.self) + } +} diff --git a/StockMate/StockMate/app/feature/user/domain/UserRepositoryProtocol.swift b/StockMate/StockMate/app/feature/user/domain/UserRepositoryProtocol.swift index ce3096e..f5b031b 100644 --- a/StockMate/StockMate/app/feature/user/domain/UserRepositoryProtocol.swift +++ b/StockMate/StockMate/app/feature/user/domain/UserRepositoryProtocol.swift @@ -6,3 +6,7 @@ // import Foundation + +protocol UserRepositoryProtocol { + func getUserInfo() async -> AppResult> +} diff --git a/StockMate/StockMate/app/feature/user/ui/ProfileCircleView.swift b/StockMate/StockMate/app/feature/user/ui/ProfileCircleView.swift index b144a70..16fbe17 100644 --- a/StockMate/StockMate/app/feature/user/ui/ProfileCircleView.swift +++ b/StockMate/StockMate/app/feature/user/ui/ProfileCircleView.swift @@ -8,11 +8,25 @@ import SwiftUI struct ProfileCircleView: View { + let name: String + let size: CGFloat + + private var initials: String { + let trimmed = name.trimmingCharacters(in: .whitespacesAndNewlines) + let firstTwo = trimmed.prefix(2) + return String(firstTwo) + } + var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + Text(initials) + .font(.headline) + .foregroundColor(Color(hex: "#374EAF")) + .frame(width: size, height: size) + .background(Color(hex: "#DCE0F1")) // 고정 색상 + .clipShape(Circle()) } } #Preview { - ProfileCircleView() + ProfileCircleView(name: "유현아", size: 50) } diff --git a/StockMate/StockMate/app/feature/user/ui/UserInfoView.swift b/StockMate/StockMate/app/feature/user/ui/UserInfoView.swift index 8ba7f71..2410fb9 100644 --- a/StockMate/StockMate/app/feature/user/ui/UserInfoView.swift +++ b/StockMate/StockMate/app/feature/user/ui/UserInfoView.swift @@ -8,11 +8,27 @@ import SwiftUI struct UserInfoView: View { + @StateObject private var viewModel = UserViewModel() + var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + VStack(spacing: 12) { + if let info = viewModel.userInfo { + Text("이메일: \(info.email)") + Text("가게명: \(info.storeName)") + Text("주소: \(info.address)") + Text("사업자번호: \(info.businessNumber)") + Text("권한: \(info.role)") + } else if !viewModel.message.isEmpty { + Text(viewModel.message).foregroundColor(.red) + } else { + ProgressView("유저 정보 불러오는 중...") + } + } + .padding() + .onAppear { + Task { + await viewModel.loadUserInfo() + } + } } } - -#Preview { - UserInfoView() -} diff --git a/StockMate/StockMate/app/feature/user/viewmodel/UserViewModel.swift b/StockMate/StockMate/app/feature/user/viewmodel/UserViewModel.swift index 9378545..e47d22e 100644 --- a/StockMate/StockMate/app/feature/user/viewmodel/UserViewModel.swift +++ b/StockMate/StockMate/app/feature/user/viewmodel/UserViewModel.swift @@ -5,4 +5,39 @@ // Created by Admin on 10/14/25. // -import Foundation +import SwiftUI + +@MainActor +final class UserViewModel: ObservableObject { + @Published var userInfo: UserInfo? + @Published var message: String = "" + @Published var shouldGoToLogin: Bool = false + + private let repo: UserRepositoryProtocol + + init(repo: UserRepositoryProtocol = UserRepositoryImpl()) { + self.repo = repo + } + + func loadUserInfo() async { + let result = await repo.getUserInfo() + switch result { + case .success(let apiResp): + if let info = apiResp.data { + userInfo = info + print("유저 정보 불러오기 성공:", info) + } else { + message = apiResp.message + print("데이터 없음:", apiResp.message) + } + case .failure(let err): + message = err.message + print("유저 정보 불러오기 실패:", err.message) + // ✅ 세션 만료나 인증 문제면 로그인화면으로 유도 + if err.code == 401 || err.code == 403 { + shouldGoToLogin = true + } + + } + } +} diff --git a/StockMate/StockMate/app/navigation/MainTabView.swift b/StockMate/StockMate/app/navigation/MainTabView.swift index 0bf4973..ed4fcef 100644 --- a/StockMate/StockMate/app/navigation/MainTabView.swift +++ b/StockMate/StockMate/app/navigation/MainTabView.swift @@ -15,7 +15,9 @@ struct MainTabView: View { // 메인 화면 ZStack { switch selectedTab { - case 0: NavigationStack{ HomeView() } + case 0: NavigationStack{ HomeView() +// UserInfoView() + } case 1: NavigationStack{ OrderView() } case 2: NavigationStack{ InventoryView() } case 3: NavigationStack{ ContentView() } diff --git a/StockMate/StockMate/resources/Color.swift b/StockMate/StockMate/resources/Color.swift index 0133471..f98469f 100644 --- a/StockMate/StockMate/resources/Color.swift +++ b/StockMate/StockMate/resources/Color.swift @@ -64,7 +64,7 @@ extension Color { // Inventory Status Background static let InvIncomingBg = Color(hex: "#EAFADE") static let InvUseBg = Color(hex: "#FEF2D9") - static let InvStockBg = Color(hex: "#E1D7FF") + static let InvStockBg = Color(hex: "#F6F4FF") // Status From e6201176e92b516f5b182938dbe562a491628690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Wed, 15 Oct 2025 14:35:39 +0900 Subject: [PATCH 08/85] =?UTF-8?q?[SETTING]=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/coderabbit.yaml | 15 +++++++++++++++ StockMate/.DS_Store | Bin 6148 -> 6148 bytes 2 files changed, 15 insertions(+) create mode 100644 .github/coderabbit.yaml diff --git a/.github/coderabbit.yaml b/.github/coderabbit.yaml new file mode 100644 index 0000000..6c8789f --- /dev/null +++ b/.github/coderabbit.yaml @@ -0,0 +1,15 @@ +language: "ko-KR" + +reviews: + profile: "chill" + request_changes_workflow: false + high_level_summary: true + poem: false + review_status: true + auto_review: + enabled: true + drafts: false + branches: + include: + - main + - dev diff --git a/StockMate/.DS_Store b/StockMate/.DS_Store index fb8127ddba0823b381ba269cb4cbe0f2eeba83ad..8b131bc6d6be2f3fa147df58dc1424275991ebff 100644 GIT binary patch delta 70 zcmZoMXffCj%gW4jc=_Z6Rx^&bj0|&M`yF=#a&#u2V&y^>(Bd!1Fbq!4&n;j80cL}Z Ng`XHVvvd6A2LNgN7oq?F delta 70 zcmZoMXffCj%gW67uXu6-s~N|2oy*<@UdJ7Q9G%IhSh>Pjj0e9#a1poj5 From 2c09be6cbf85d6be6d8f28c0e0a6024843ce5356 Mon Sep 17 00:00:00 2001 From: Taegeun Kwack Date: Wed, 15 Oct 2025 14:51:56 +0900 Subject: [PATCH 09/85] =?UTF-8?q?[SETTING]=20=EC=9D=B4=EC=8A=88=20?= =?UTF-8?q?=EC=9E=90=EB=8F=99=20=EC=A2=85=EB=A3=8C=20=EB=93=B1=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/close-dev-issue.yml | 45 +++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 .github/workflows/close-dev-issue.yml diff --git a/.github/workflows/close-dev-issue.yml b/.github/workflows/close-dev-issue.yml new file mode 100644 index 0000000..e137bc5 --- /dev/null +++ b/.github/workflows/close-dev-issue.yml @@ -0,0 +1,45 @@ +name: Auto Close Issues on dev merge + +permissions: + issues: write + +on: + pull_request: + types: [closed] + +jobs: + close-issues: + if: > + github.event.pull_request.merged == true && + github.event.pull_request.base.ref == 'dev' + runs-on: ubuntu-latest + + steps: + - name: Close linked issues + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const prBody = context.payload.pull_request.body; + const issueLines = prBody.split('\n'); + + // 클로징 키워드가 있는 줄에서만 이슈 번호 추출 + const closingKeywords = ['close', 'closes', 'closed', 'fix', 'fixes', 'fixed', 'resolve', 'resolves', 'resolved']; + const issuePattern = /#(\d+)/g; + + for (const line of issueLines) { + const lower = line.toLowerCase(); + if (closingKeywords.some(k => lower.includes(k))) { + let match; + while ((match = issuePattern.exec(line)) !== null) { + const issue_number = parseInt(match[1]); + await github.rest.issues.update({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number, + state: 'closed' + }); + console.log(`✅ Closed issue #${issue_number}`); + } + } + } From cbd38858a5def6bf01ee2969b4498c49c767f57c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Thu, 16 Oct 2025 11:37:25 +0900 Subject: [PATCH 10/85] =?UTF-8?q?[FEAT]=20=EC=9E=AC=EA=B3=A0=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20API=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/feature/auth/ui/LoginView.swift | 4 +- .../feature/inventory/data/InventoryApi.swift | 66 ++++++ .../data/InventoryRepositoryImpl.swift | 28 +++ .../domain/InventoryRepositoryProtocol.swift | 18 ++ .../inventory/ui/InventorySearchView.swift | 223 ++++++++++++++++++ .../feature/inventory/ui/InventoryView.swift | 2 +- .../viewmodel/InventoryViewModel.swift | 104 ++++++++ 7 files changed, 442 insertions(+), 3 deletions(-) create mode 100644 StockMate/StockMate/app/feature/inventory/data/InventoryApi.swift create mode 100644 StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift create mode 100644 StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift create mode 100644 StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift create mode 100644 StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift diff --git a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift index 23da13f..06d745a 100644 --- a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift @@ -121,8 +121,8 @@ struct LoginView: View { // MARK: - 유효성 검사 함수 private func isValidForm() -> Bool { emailError = isValidEmail(authViewModel.email) ? nil : "이메일 형식을 확인해주세요" - pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" - return emailError == nil && pwError == nil + //pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" + return emailError == nil //&& pwError == nil } } diff --git a/StockMate/StockMate/app/feature/inventory/data/InventoryApi.swift b/StockMate/StockMate/app/feature/inventory/data/InventoryApi.swift new file mode 100644 index 0000000..1a2720c --- /dev/null +++ b/StockMate/StockMate/app/feature/inventory/data/InventoryApi.swift @@ -0,0 +1,66 @@ +// +// InventoryApi.swift +// StockMate +// +// Created by Admin on 10/15/25. +// + + +import Foundation +import Alamofire + +struct InventoryResponse: Decodable { + let status: Int + let success: Bool + let message: String + let data: InventoryPageData? +} + +struct InventoryPageData: Decodable { + let content: [InventoryItem] + let page: Int + let size: Int + let totalElements: Int + let totalPages: Int +} + +struct InventoryItem: Decodable, Identifiable { + let id: Int + let name: String + let price: Int + let image: String + let trim: String + let model: String + let category: Int + let korName: String + let engName: String + let categoryName: String + let stock: Int // 본사 재고 + let amount: Int // 지점별 재고 + let limitAmount: Int // 지점별 최소 수량 + let isLack: Bool +} + +enum InventoryApi { + static func getInventoryList( + page: Int, + size: Int, + categoryNames: [String], + trims: [String], + models: [String] + ) -> DataRequest { + var url = ApiClient.baseURL + "api/v1/store/search?page=\(page)&size=\(size)" + + for c in categoryNames { + url += "&categoryName=\(c.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")" + } + for t in trims { + url += "&trim=\(t.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")" + } + for m in models { + url += "&model=\(m.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")" + } + + return ApiClient.shared.request(url, method: .get) + } +} diff --git a/StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift b/StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift new file mode 100644 index 0000000..7436a94 --- /dev/null +++ b/StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift @@ -0,0 +1,28 @@ +// +// InventoryRepositoryImpl.swift +// StockMate +// +// Created by Admin on 10/15/25. +// + +import Foundation +import Alamofire + +final class InventoryRepositoryImpl: InventoryRepositoryProtocol { + func getInventoryList( + page: Int, + size: Int, + categoryNames: [String], + trims: [String], + models: [String] + ) async -> AppResult> { + let dataReq = InventoryApi.getInventoryList( + page: page, + size: size, + categoryNames: categoryNames, + trims: trims, + models: models + ) + return await safeApi(dataReq, decodeTo: ApiResponse.self) + } +} diff --git a/StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift b/StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift new file mode 100644 index 0000000..f3c595e --- /dev/null +++ b/StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift @@ -0,0 +1,18 @@ +// +// InventoryRepositoryProtocol.swift +// StockMate +// +// Created by Admin on 10/15/25. +// + +import Foundation + +protocol InventoryRepositoryProtocol { + func getInventoryList( + page: Int, + size: Int, + categoryNames: [String], + trims: [String], + models: [String] + ) async -> AppResult> +} diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift new file mode 100644 index 0000000..bed009c --- /dev/null +++ b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift @@ -0,0 +1,223 @@ +// +// InventorySearchView.swift +// StockMate +// +// Created by Admin on 10/15/25. +// + + +import SwiftUI + +struct InventorySearchView: View { + @StateObject private var inventoryViewModel = InventoryViewModel() + @State private var searchText = "" + + private let categories = ["전기/램프", "엔진/미션", "하체/바디", "내장/외장", "기타소모품"] + private let trims = ["준중형/소형", "중형", "대형", "SUV", "화물/트럭/승합", "수소/전기"] + + private let trimToModels: [String: [String]] = [ + "준중형/소형": ["아반떼MD", "아반떼AD", "아반떼CN7", "I30", "엑센트", "아이오닉", "벨로스터", "캐스퍼"], + "중형": ["NF소나타", "YF소나타", "LF소나타", "DN8소나타", "그랜저TG", "그랜저HG", "그랜저IG", "그랜저GN7", "I40"], + "대형": ["제네시스BH", "에쿠스"], + "SUV": ["베뉴", "코나OS", "코나SX2", "투싼IX", "투싼TL", "투싼NX4", "싼타페CM", "싼타페DM", "싼타페TM", "싼타페MX5", "맥스크루즈", "베라크루즈", "팰리세이드LX2", "팰리세이드LX3"], + "화물/트럭/승합": ["스타렉스", "그랜드스타렉스", "스타리아", "포터2", "쏠라티", "마이티", "메가트럭", "카운티"], + "수소/전기자동차": ["아이오닉5", "아이오닉6", "아이오닉9", "넥쏘FE", "넥쏘NH2"] + ] + + private var filteredModels: [String] { + if inventoryViewModel.selectedTrims.isEmpty { + return trimToModels.values.flatMap { $0 } + } else { + return inventoryViewModel.selectedTrims.flatMap { trimToModels[$0] ?? [] } + } + } + + var body: some View { + NavigationStack { + VStack(spacing: 0) { + // 🔍 검색창 + HStack { + Image(systemName: "magnifyingglass") + .foregroundColor(.gray) + TextField("부품을 검색하세요.", text: $searchText) + .textFieldStyle(PlainTextFieldStyle()) + } + .padding() + .background(Color(.white)) + .cornerRadius(9999) + .padding(.horizontal) + .padding(.vertical) + + // 🔽 필터 버튼 + ScrollView(.horizontal, showsIndicators: false) { + HStack(spacing: 10) { + FilterMenu( + title: "카테고리", + items: categories, + selected: { inventoryViewModel.selectedCategories.contains($0) }, + onTap: { inventoryViewModel.toggleCategory($0) } + ) + FilterMenu( + title: "분류", + items: trims, + selected: { inventoryViewModel.selectedTrims.contains($0) }, + onTap: { inventoryViewModel.toggleTrim($0) } + ) + FilterMenu( + title: "모델", + items: filteredModels, + selected: { inventoryViewModel.selectedModels.contains($0) }, + onTap: { inventoryViewModel.toggleModel($0) } + ) + } + .padding(.horizontal) + .padding(.top, 2) + .padding(.bottom, 4) + } + + // 📋 재고 리스트 + ScrollView { + LazyVStack(spacing: 10) { + ForEach(inventoryViewModel.inventoryItems) { item in + InventoryCard(item: item) + .padding(.horizontal) + .onAppear { + if item.id == inventoryViewModel.inventoryItems.last?.id, + inventoryViewModel.hasMore { + Task { + await inventoryViewModel.loadInventoryList() + } + } + } + } + + if inventoryViewModel.isLoading && inventoryViewModel.hasMore { + ProgressView() + .padding(.vertical) + } + } + .padding(.top, 8) + } + } + .background(Color.Light) + .navigationTitle("재고 조회") + .task { + await inventoryViewModel.loadInventoryList(reset: true) + } + } + } +} + +// ✅ 재고 카드 (UI 스타일 적용) +struct InventoryCard: View { + let item: InventoryItem + + var body: some View { + VStack(alignment: .leading, spacing: 5) { + Text(item.categoryName) + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(.black) + + Divider() + .frame(height: 0.2) + .background(Color.textGray2) + + HStack(alignment: .center, spacing: 12) { + AsyncImage(url: URL(string: item.image)) { image in + image.resizable().scaledToFit() + } placeholder: { + Color.gray.opacity(0.2) + } + .frame(width: 64, height: 64) + .cornerRadius(10) + + VStack(alignment: .leading, spacing: 6) { + HStack { + Text(item.korName) + .font(.system(size: 14, weight: .bold)) + .foregroundColor(.black) + .lineLimit(2) + Spacer() + } + .padding(.top, 2) + + Text("\(item.trim)/\(item.model)") + .font(.system(size: 13, weight: .regular)) + .foregroundColor(.gray) + + + + } + .frame(height: 60, alignment: .top) + + VStack (alignment: .center, spacing: 6) { + if item.isLack { + Text("수량 부족") + .font(.system(size: 13, weight: .regular)) + .padding(.horizontal, 10) + .padding(.vertical, 5) + .background(Color.DangerBg) + .foregroundColor(.Danger) + .cornerRadius(12) + } else { + Text("수량 여유") + .font(.system(size: 13, weight: .regular)) + .padding(.horizontal, 10) + .padding(.vertical, 5) + .background(Color.StatusGreenBg) + .foregroundColor(.StatusGreen) + .cornerRadius(12) + } + + VStack(alignment: .leading){ + Text("현재수량: \(item.amount)개") + .font(.system(size: 11, weight: .regular)) + .foregroundColor(.textGray1) + + Text("최소수량: \(item.limitAmount)개") + .font(.system(size: 11, weight: .regular)) + .foregroundColor(.textGray1) + } + } + } + } + .padding() + .background(Color.white) + .cornerRadius(10) + .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) + } +} + +// ✅ 필터 버튼 공용 컴포넌트 +struct FilterMenu: View { + let title: String + let items: [String] + let selected: (String) -> Bool + let onTap: (String) -> Void + + var body: some View { + Menu { + ForEach(items, id: \.self) { item in + Button { + onTap(item) + } label: { + HStack { + Text(item) + if selected(item) { + Spacer() + Image(systemName: "checkmark") + } + } + } + } + } label: { + Text(title) + .font(.subheadline) + .foregroundColor(.black) + .padding(.horizontal, 12) + .padding(.vertical, 6) + .background(Color(.systemGray6)) + .cornerRadius(8) + } + } +} diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift index 96756f0..2ae4bf1 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift @@ -56,7 +56,7 @@ struct InventoryView: View { struct GridMenuView: View { let menuItems = [ - ("재고조회", Color.InvIncoming, Color.InvIncomingBg, AnyView(IncomingScanView())), + ("재고조회", Color.InvIncoming, Color.InvIncomingBg, AnyView(InventorySearchView())), ("입출고 히스토리", Color.InvUse, Color.InvUseBg, AnyView(IncomingScanView())), ("입고처리", Color.Transfer, Color.TransferBg, AnyView(IncomingScanView())), ("사용처리", Color.InvStock, Color.InvStockBg, AnyView(IncomingScanView())), diff --git a/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift b/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift new file mode 100644 index 0000000..6975c61 --- /dev/null +++ b/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift @@ -0,0 +1,104 @@ +// +// InventoryViewModel.swift +// StockMate +// +// Created by Admin on 10/15/25. +// + +import SwiftUI + +@MainActor +final class InventoryViewModel: ObservableObject { + @Published var inventoryItems: [InventoryItem] = [] + @Published var message: String = "" + @Published var shouldGoToLogin: Bool = false + + @Published var selectedCategories: [String] = [] + @Published var selectedTrims: [String] = [] + @Published var selectedModels: [String] = [] + + @Published var currentPage = 0 + @Published var isLoading = false + @Published var hasMore = true + + private let repo: InventoryRepositoryProtocol + + init(repo: InventoryRepositoryProtocol = InventoryRepositoryImpl()) { + self.repo = repo + } + + func loadInventoryList( + reset: Bool = false, + size: Int = 20 + ) async { + guard !isLoading else { return } + isLoading = true + + if reset { + currentPage = 0 + inventoryItems.removeAll() + hasMore = true + } + + let result = await repo.getInventoryList( + page: currentPage, + size: size, + categoryNames: selectedCategories, + trims: selectedTrims, + models: selectedModels + ) + + switch result { + case .success(let apiResp): + if let data = apiResp.data { + if reset { + inventoryItems = data.content + } else { + inventoryItems.append(contentsOf: data.content) + } + hasMore = currentPage + 1 < data.totalPages + currentPage += 1 + } else { + message = apiResp.message + } + case .failure(let err): + message = err.message + if err.code == 401 || err.code == 403 { + shouldGoToLogin = true + } + } + isLoading = false + } + + func resetAndLoad() async { + await loadInventoryList(reset: true) + } + + // MARK: - Filter toggle + func toggleCategory(_ name: String) { + if selectedCategories.contains(name) { + selectedCategories.removeAll { $0 == name } + } else { + selectedCategories.append(name) + } + Task { await resetAndLoad() } + } + + func toggleTrim(_ trim: String) { + if selectedTrims.contains(trim) { + selectedTrims.removeAll { $0 == trim } + } else { + selectedTrims.append(trim) + } + Task { await resetAndLoad() } + } + + func toggleModel(_ model: String) { + if selectedModels.contains(model) { + selectedModels.removeAll { $0 == model } + } else { + selectedModels.append(model) + } + Task { await resetAndLoad() } + } +} From 8392a162596471bae908b5e836011a1253a171ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Thu, 16 Oct 2025 20:25:07 +0900 Subject: [PATCH 11/85] =?UTF-8?q?[FEAT]=20=EC=9E=AC=EA=B3=A0=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=ED=83=AD=20=EB=B6=80=EC=A1=B1=EC=9E=AC=EA=B3=A0=20?= =?UTF-8?q?API=20=EC=97=B0=EB=8F=99=20=EB=B0=8F=20UI=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/components/InventoryCardView.swift | 91 ++++++ .../components/InventoryListSection.swift | 18 ++ .../feature/inventory/data/InventoryApi.swift | 9 + .../data/InventoryRepositoryImpl.swift | 9 + .../domain/InventoryRepositoryProtocol.swift | 6 + .../inventory/ui/InventorySearchView.swift | 14 +- .../feature/inventory/ui/InventoryView.swift | 271 +++++++++--------- .../viewmodel/InventoryViewModel.swift | 65 +++++ 8 files changed, 350 insertions(+), 133 deletions(-) create mode 100644 StockMate/StockMate/app/core/components/InventoryCardView.swift create mode 100644 StockMate/StockMate/app/core/components/InventoryListSection.swift diff --git a/StockMate/StockMate/app/core/components/InventoryCardView.swift b/StockMate/StockMate/app/core/components/InventoryCardView.swift new file mode 100644 index 0000000..7113417 --- /dev/null +++ b/StockMate/StockMate/app/core/components/InventoryCardView.swift @@ -0,0 +1,91 @@ +// +// InventoryCardView.swift +// StockMate +// +// Created by Admin on 10/16/25. +// + +import SwiftUI + + +struct InventoryCardView: View { + let item: InventoryItem + var showStatus: Bool = true // 필요 시 상태 표시 숨기기 옵션 + + var body: some View { + VStack(alignment: .leading, spacing: 5) { + // 상단 카테고리 + Text(item.categoryName) + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(.black) + + Divider() + .frame(height: 0.2) + .background(Color.textGray2) + + HStack(alignment: .center, spacing: 12) { + // 제품 이미지 + AsyncImage(url: URL(string: item.image)) { image in + image.resizable().scaledToFit() + } placeholder: { + Color.gray.opacity(0.2) + } + .frame(width: 64, height: 64) + .cornerRadius(10) + + // 제품명, 트림/모델 + VStack(alignment: .leading, spacing: 6) { + HStack { + Text(item.korName) + .font(.system(size: 14, weight: .bold)) + .foregroundColor(.black) + .lineLimit(2) + Spacer() + } + .padding(.top, 2) + + Text("\(item.trim) / \(item.model)") + .font(.system(size: 13, weight: .regular)) + .foregroundColor(.gray) + } + .frame(height: 60, alignment: .top) + + // 상태 표시 (옵션) + if showStatus { + VStack(alignment: .center, spacing: 6) { + if item.isLack { + Text("수량 부족") + .font(.system(size: 13, weight: .regular)) + .padding(.horizontal, 10) + .padding(.vertical, 5) + .background(Color.DangerBg) + .foregroundColor(.Danger) + .cornerRadius(12) + } else { + Text("수량 여유") + .font(.system(size: 13, weight: .regular)) + .padding(.horizontal, 10) + .padding(.vertical, 5) + .background(Color.StatusGreenBg) + .foregroundColor(.StatusGreen) + .cornerRadius(12) + } + + VStack(alignment: .leading) { + Text("현재수량: \(item.amount)개") + .font(.system(size: 11, weight: .regular)) + .foregroundColor(.textGray1) + Text("최소수량: \(item.limitAmount)개") + .font(.system(size: 11, weight: .regular)) + .foregroundColor(.textGray1) + } + } + } + } + } + .padding() + .background(Color.white) + .cornerRadius(10) + .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) + } +} diff --git a/StockMate/StockMate/app/core/components/InventoryListSection.swift b/StockMate/StockMate/app/core/components/InventoryListSection.swift new file mode 100644 index 0000000..c8d6c19 --- /dev/null +++ b/StockMate/StockMate/app/core/components/InventoryListSection.swift @@ -0,0 +1,18 @@ +// +// InventoryListSection.swift +// StockMate +// +// Created by Admin on 10/16/25. +// + +import SwiftUI + +struct InventoryListSection: View { + var body: some View { + Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + } +} + +#Preview { + InventoryListSection() +} diff --git a/StockMate/StockMate/app/feature/inventory/data/InventoryApi.swift b/StockMate/StockMate/app/feature/inventory/data/InventoryApi.swift index 1a2720c..2d250a9 100644 --- a/StockMate/StockMate/app/feature/inventory/data/InventoryApi.swift +++ b/StockMate/StockMate/app/feature/inventory/data/InventoryApi.swift @@ -63,4 +63,13 @@ enum InventoryApi { return ApiClient.shared.request(url, method: .get) } + + // ✅ 부족 재고 조회 API 추가 + static func getUnderLimitList(categoryName: String? = nil, page: Int = 0, size: Int = 10) -> DataRequest { + var url = ApiClient.baseURL + "api/v1/store/under-limit?page=\(page)&size=\(size)" + if let categoryName = categoryName, !categoryName.isEmpty { + url += "&categoryName=\(categoryName.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")" + } + return ApiClient.shared.request(url, method: .get) + } } diff --git a/StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift b/StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift index 7436a94..cb66353 100644 --- a/StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift +++ b/StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift @@ -25,4 +25,13 @@ final class InventoryRepositoryImpl: InventoryRepositoryProtocol { ) return await safeApi(dataReq, decodeTo: ApiResponse.self) } + // 부족 재고 리스트 호출 + func getUnderLimitList( + categoryName: String?, + page: Int, + size: Int + ) async -> AppResult> { + let dataReq = InventoryApi.getUnderLimitList(categoryName: categoryName, page: page, size: size) + return await safeApi(dataReq, decodeTo: ApiResponse.self) + } } diff --git a/StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift b/StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift index f3c595e..eb7a550 100644 --- a/StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift +++ b/StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift @@ -15,4 +15,10 @@ protocol InventoryRepositoryProtocol { trims: [String], models: [String] ) async -> AppResult> + + func getUnderLimitList( + categoryName: String?, + page: Int, + size: Int + ) async -> AppResult> } diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift index bed009c..0455f6f 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift @@ -79,7 +79,7 @@ struct InventorySearchView: View { ScrollView { LazyVStack(spacing: 10) { ForEach(inventoryViewModel.inventoryItems) { item in - InventoryCard(item: item) + InventoryCardView(item: item) .padding(.horizontal) .onAppear { if item.id == inventoryViewModel.inventoryItems.last?.id, @@ -89,6 +89,18 @@ struct InventorySearchView: View { } } } + + +// InventoryCard(item: item) +// .padding(.horizontal) +// .onAppear { +// if item.id == inventoryViewModel.inventoryItems.last?.id, +// inventoryViewModel.hasMore { +// Task { +// await inventoryViewModel.loadInventoryList() +// } +// } +// } } if inventoryViewModel.isLoading && inventoryViewModel.hasMore { diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift index 2ae4bf1..58ab734 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift @@ -6,165 +6,172 @@ // import SwiftUI - struct InventoryView: View { + @StateObject private var inventoryViewModel = InventoryViewModel() + @State private var showScrollToTopButton = false + @Namespace private var topID + var body: some View { NavigationStack { - VStack(alignment: .leading, spacing: 13) { - Text("재고 관리") - .font(.title2) - .bold() - .padding(.top, 13) - .frame(maxWidth: .infinity, alignment: .center) - - // 4개 버튼 영역 - GridMenuView() - - // 섹션 타이틀 - Text("얼마 남지 않았어요!") - .font(.system(size: 22, weight: .semibold)) - .foregroundColor(.black) - .padding(.horizontal, 25) - .padding(.top) - - // 공통 컴포넌트 리스트 - ScrollView { - VStack(spacing: 12) { - // 예시 데이터 - let dummyData: [(String, String, Int, Int)] = [ - ("브레이크", "현대 아이오닉5", 5, 10), - ("엔진오일", "기아 EV6", 10, 12), - ("에어필터", "현대 코나", 8, 10), - ] + ScrollViewReader { proxy in + ZStack { + ScrollView { + VStack(spacing: 0) { + // ✅ 스크롤 감지용 (맨 위) + GeometryReader { geo in + Color.clear + .onChange(of: geo.frame(in: .global).minY) { newValue in + // 👇 스크롤 시 값이 변함 + print("📏 Scroll offsetY:", newValue) // 테스트용, 화면 안정화 후 제거 + withAnimation(.easeInOut(duration: 0.25)) { + showScrollToTopButton = newValue < -150 + } + } + } + .frame(height: 0) + .id(topID) + + // 타이틀 + Text("재고 관리") + .font(.title2) + .bold() + .padding(.top, 13) + .padding(.leading, 25) + .frame(maxWidth: .infinity, alignment: .leading) + + GridMenuView() + + Text("얼마 남지 않았어요!") + .font(.system(size: 22, weight: .semibold)) + .foregroundColor(.black) + .padding(.horizontal, 25) + .padding(.top) + .frame(maxWidth: .infinity, alignment: .leading) + + LazyVStack(spacing: 12) { + ForEach(inventoryViewModel.underLimitItems) { item in + InventoryCardView(item: item) + .padding(.horizontal) + .onAppear { + if item.id == inventoryViewModel.underLimitItems.last?.id { + Task { + await inventoryViewModel.loadUnderLimitList() + } + } + } + } + + if inventoryViewModel.isLoading { + ProgressView() + .padding(.vertical, 20) + } + } + .padding(.top, 8) + .padding(.bottom, 80) + } + } - ForEach(dummyData, id: \.0) { item in - StockShortageCard( - partName: item.0, - carModel: item.1, - currentCount: item.2, - minCount: item.3 - ) + // 오른쪽 하단 플로팅 버튼 + if showScrollToTopButton { + VStack { + Spacer() + HStack { + Spacer() + Button { + withAnimation(.easeInOut) { + proxy.scrollTo(topID, anchor: .top) + } + } label: { + ZStack { + Circle() + .fill(Color.Secondary) // ✅ 배경색 + .frame(width: 50, height: 50) + Image(systemName: "arrow.up") + .font( + .system(size: 24, weight: .bold) + ) + .foregroundColor(.white) // ✅ 화살표 색 + } + } + .padding(.trailing, 20) + .padding(.bottom, 20) + } } + .transition(.opacity) + .animation(.easeInOut(duration: 0.25), value: showScrollToTopButton) } - .padding(.horizontal) + + + } + .background(Color.Light) + .task { + await inventoryViewModel.loadUnderLimitList(reset: true) } } - .background(Color.Light) } } } struct GridMenuView: View { let menuItems = [ - ("재고조회", Color.InvIncoming, Color.InvIncomingBg, AnyView(InventorySearchView())), - ("입출고 히스토리", Color.InvUse, Color.InvUseBg, AnyView(IncomingScanView())), - ("입고처리", Color.Transfer, Color.TransferBg, AnyView(IncomingScanView())), - ("사용처리", Color.InvStock, Color.InvStockBg, AnyView(IncomingScanView())), + ("재고 조회", true, "InvStock", AnyView(InventorySearchView())), + ("입출고 히스토리", false, "InvTrans", AnyView(IncomingScanView())), + ("입고 처리", false, "InvIncoming", AnyView(IncomingScanView())), + ("사용 처리", true, "InvUse", AnyView(IncomingScanView())), ] - + var body: some View { - VStack(spacing: 20) { + VStack(spacing: 15) { LazyVGrid( columns: [ - GridItem(.flexible(), spacing: 15), - GridItem(.flexible(), spacing: 15), + GridItem(.flexible(), spacing: 10), + GridItem(.flexible(), spacing: 10), ], - spacing: 15 + spacing: 10 ) { - ForEach(menuItems, id: \.0) { item in - ZStack { - // 그림자 전용 레이어 (NavigationLink 뒤에 위치) - RoundedRectangle(cornerRadius: 16) - .fill(Color.white) - .shadow( - color: .black.opacity(0.25), - radius: 4, - x: 0, - y: 4 - ) - - // 버튼 본체 - NavigationLink(destination: item.3) { - VStack(spacing: 12) { - Image("InvStock") - .renderingMode(.template) - .resizable() - .scaledToFit() - .frame(width: 24, height: 24) - .foregroundColor(item.1) - .padding(.top, 20) - - Text(item.0) - .font(.system(size: 15, weight: .semibold)) - .foregroundColor(item.1) - .padding(.bottom, 20) + NavigationLink(destination: item.3) { + VStack(alignment: .leading, spacing: 19) { + HStack { + Spacer() + ZStack { + // 타원 배경 + Rectangle() + .fill(item.1 ? Color.white.opacity(0.2) : Color.Secondary) + .frame(width: 35, height: 26) + .cornerRadius(80) + + // 아이콘 + Image(item.2) + .renderingMode(.template) + .resizable() + .scaledToFit() + .frame(width: 14, height: 14) + .foregroundColor(.white) + } } - .frame(maxWidth: .infinity, minHeight: 120) - .background(item.2.opacity(0.5)) - .overlay( - RoundedRectangle(cornerRadius: 16) - .stroke(item.1, lineWidth: 1.2) - ) - .cornerRadius(16) + + Text(item.0) + .font(.system(size: 15, weight: .semibold)) + .foregroundColor(item.1 ? .white : Color.Secondary) } - .buttonStyle(PlainButtonStyle()) + .padding(.horizontal, 20) + .padding(.vertical, 20) + .frame(height: 99) + .frame(maxWidth: .infinity) + .background( + RoundedRectangle(cornerRadius: 24) + .fill(item.1 ? Color.Secondary : Color.white) + // 카드 그림자 (Figma 스펙: y=4, blur=4, opacity=25%, black) + .shadow(color: .black.opacity(0.35), radius: 2, x: 0, y: 4) + ) } + .buttonStyle(.plain) } } - .padding(.horizontal, 10) - } - .padding() - .background(Color.White) - .padding(.horizontal) - } -} - -struct StockShortageCard: View { - let partName: String - let carModel: String - let currentCount: Int - let minCount: Int - - var body: some View { - HStack(alignment: .center, spacing: 12) { - RoundedRectangle(cornerRadius: 8) - .fill(Color.gray.opacity(0.2)) - .frame(width: 68, height: 68) - - VStack(alignment: .leading, spacing: 4) { - Text(partName) - .font(.subheadline) - .fontWeight(.semibold) - Text(carModel) - .font(.subheadline) - .foregroundColor(.textGray1) - } - Spacer() - - VStack(alignment: .leading) { - Text("수량 부족") - .font(.system(size: 14, weight: .semibold)) - .fontWeight(.regular) - .foregroundColor(.StatusRed) - .padding(.vertical, 6) - .padding(.horizontal, 12) - .background(Color.StatusRedBg) - .cornerRadius(14) - - Text("현재수량: \(currentCount)개") - .font(.caption) - .foregroundColor(.textGray1) - - Text("최소수량: \(minCount)개") - .font(.caption) - .foregroundColor(.textGray1) - } + .padding(.horizontal, 15) } - .padding(12) - .background(Color.white) - .cornerRadius(12) - .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 2) + .padding(.vertical, 17) } } diff --git a/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift b/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift index 6975c61..03e7979 100644 --- a/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift +++ b/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift @@ -20,6 +20,10 @@ final class InventoryViewModel: ObservableObject { @Published var currentPage = 0 @Published var isLoading = false @Published var hasMore = true + + @Published var underLimitItems: [InventoryItem] = [] + @Published var underLimitPage = 0 + @Published var underLimitHasMore = true private let repo: InventoryRepositoryProtocol @@ -27,6 +31,7 @@ final class InventoryViewModel: ObservableObject { self.repo = repo } + // 재고 조회 func loadInventoryList( reset: Bool = false, size: Int = 20 @@ -101,4 +106,64 @@ final class InventoryViewModel: ObservableObject { } Task { await resetAndLoad() } } + + // 부족 재고 조회 +// func loadUnderLimitList(categoryName: String? = nil) async { +// let result = await repo.getUnderLimitList( +// categoryName: categoryName, +// page: 0, +// size: 10 +// ) +// +// switch result { +// case .success(let apiResp): +// if let data = apiResp.data { +// underLimitItems = data.content +// } else { +// message = apiResp.message +// } +// case .failure(let err): +// message = err.message +// if err.code == 401 || err.code == 403 { +// shouldGoToLogin = true +// } +// } +// } + func loadUnderLimitList(reset: Bool = false, size: Int = 10) async { + guard !isLoading, underLimitHasMore else { return } + isLoading = true + + if reset { + underLimitPage = 0 + underLimitItems.removeAll() + underLimitHasMore = true + } + + // 여기서 under-limit API 호출 + let result = await repo.getUnderLimitList( + categoryName: selectedCategories.first, // 필터 적용 시 사용 + page: underLimitPage, + size: size + ) + + switch result { + case .success(let apiResp): + if let data = apiResp.data { + if reset { + underLimitItems = data.content + } else { + underLimitItems.append(contentsOf: data.content) + } + underLimitHasMore = underLimitPage + 1 < data.totalPages + underLimitPage += 1 + } + case .failure(let err): + message = err.message + if err.code == 401 || err.code == 403 { + shouldGoToLogin = true + } + } + isLoading = false + } + } From f9f066c8932bf20fae08a57fd305f5f6f775cfb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Fri, 17 Oct 2025 12:35:32 +0900 Subject: [PATCH 12/85] =?UTF-8?q?[FEAT]=20=EC=9E=AC=EA=B3=A0=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=ED=83=AD=20=EB=B6=80=ED=92=88=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20API=20=EC=97=B0=EA=B2=B0=20=EB=B0=8F=20UI?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/inventory/data/InventoryApi.swift | 8 + .../data/InventoryRepositoryImpl.swift | 12 + .../domain/InventoryRepositoryProtocol.swift | 8 + .../inventory/ui/InventorySearchView.swift | 212 ++++++++---------- .../viewmodel/InventoryViewModel.swift | 121 ++++++++-- 5 files changed, 223 insertions(+), 138 deletions(-) diff --git a/StockMate/StockMate/app/feature/inventory/data/InventoryApi.swift b/StockMate/StockMate/app/feature/inventory/data/InventoryApi.swift index 2d250a9..ed33182 100644 --- a/StockMate/StockMate/app/feature/inventory/data/InventoryApi.swift +++ b/StockMate/StockMate/app/feature/inventory/data/InventoryApi.swift @@ -72,4 +72,12 @@ enum InventoryApi { } return ApiClient.shared.request(url, method: .get) } + + // ✅ 부품 이름으로 검색 + static func findByName(name: String, page: Int, size: Int) -> DataRequest { + let encodedName = name.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "" + let url = ApiClient.baseURL + "api/v1/store/find-name?name=\(encodedName)&page=\(page)&size=\(size)" + return ApiClient.shared.request(url, method: .get) + } + } diff --git a/StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift b/StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift index cb66353..914e732 100644 --- a/StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift +++ b/StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift @@ -34,4 +34,16 @@ final class InventoryRepositoryImpl: InventoryRepositoryProtocol { let dataReq = InventoryApi.getUnderLimitList(categoryName: categoryName, page: page, size: size) return await safeApi(dataReq, decodeTo: ApiResponse.self) } + + // ✅ 이름 검색 + func findByName( + name: String, + page: Int, + size: Int + ) async -> AppResult> { + let dataReq = InventoryApi.findByName(name: name, page: page, size: size) + return await safeApi(dataReq, decodeTo: ApiResponse.self) + } + + } diff --git a/StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift b/StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift index eb7a550..3d45edd 100644 --- a/StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift +++ b/StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift @@ -21,4 +21,12 @@ protocol InventoryRepositoryProtocol { page: Int, size: Int ) async -> AppResult> + + // 이름 검색 + func findByName( + name: String, + page: Int, + size: Int + ) async -> AppResult> + } diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift index 0455f6f..ce8ac6c 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift @@ -12,6 +12,7 @@ struct InventorySearchView: View { @StateObject private var inventoryViewModel = InventoryViewModel() @State private var searchText = "" + private let categories = ["전기/램프", "엔진/미션", "하체/바디", "내장/외장", "기타소모품"] private let trims = ["준중형/소형", "중형", "대형", "SUV", "화물/트럭/승합", "수소/전기"] @@ -39,8 +40,24 @@ struct InventorySearchView: View { HStack { Image(systemName: "magnifyingglass") .foregroundColor(.gray) + TextField("부품을 검색하세요.", text: $searchText) .textFieldStyle(PlainTextFieldStyle()) + .onChange(of: searchText) { newValue in + inventoryViewModel.searchInFilteredList(keyword: newValue) + } + + if !searchText.isEmpty { + Button(action: { + searchText = "" + inventoryViewModel.isSearching = false + }) { + Image(systemName: "xmark") + .foregroundColor(.gray) + } + .buttonStyle(.plain) + .padding(.trailing,3) + } } .padding() .background(Color(.white)) @@ -48,67 +65,79 @@ struct InventorySearchView: View { .padding(.horizontal) .padding(.vertical) - // 🔽 필터 버튼 - ScrollView(.horizontal, showsIndicators: false) { HStack(spacing: 10) { FilterMenu( title: "카테고리", items: categories, - selected: { inventoryViewModel.selectedCategories.contains($0) }, + selectedItems: inventoryViewModel.selectedCategories, onTap: { inventoryViewModel.toggleCategory($0) } ) + FilterMenu( title: "분류", items: trims, - selected: { inventoryViewModel.selectedTrims.contains($0) }, + selectedItems: inventoryViewModel.selectedTrims, onTap: { inventoryViewModel.toggleTrim($0) } ) + FilterMenu( title: "모델", items: filteredModels, - selected: { inventoryViewModel.selectedModels.contains($0) }, + selectedItems: inventoryViewModel.selectedModels, onTap: { inventoryViewModel.toggleModel($0) } ) + + // 🔄 초기화 버튼 + Button(action: { + inventoryViewModel.resetFilters(with: searchText) + }) { + HStack(spacing: 4) { + Image(systemName: "arrow.counterclockwise") + .font(.system(size: 13)) + Text("초기화") + .font(.system(size: 13, weight: .medium)) + } + .foregroundColor(.blue) + .padding(.trailing, 8) + } + .frame(maxWidth: .infinity, alignment: .trailing) + } .padding(.horizontal) - .padding(.top, 2) - .padding(.bottom, 4) - } - + .padding(.bottom, 16) + // 📋 재고 리스트 ScrollView { LazyVStack(spacing: 10) { - ForEach(inventoryViewModel.inventoryItems) { item in + + ForEach( + inventoryViewModel.isSearching + ? inventoryViewModel.filteredSearchResults // ✅ 검색 + 필터링 + : inventoryViewModel.inventoryItems + ) { item in InventoryCardView(item: item) .padding(.horizontal) .onAppear { - if item.id == inventoryViewModel.inventoryItems.last?.id, - inventoryViewModel.hasMore { - Task { - await inventoryViewModel.loadInventoryList() + if inventoryViewModel.isSearching { + if item.id == inventoryViewModel.searchResults.last?.id, + inventoryViewModel.searchHasMore { + Task { await inventoryViewModel.searchByName(name: searchText) } + } + } else { + if item.id == inventoryViewModel.inventoryItems.last?.id, + inventoryViewModel.hasMore { + Task { await inventoryViewModel.loadInventoryList() } } } } - - -// InventoryCard(item: item) -// .padding(.horizontal) -// .onAppear { -// if item.id == inventoryViewModel.inventoryItems.last?.id, -// inventoryViewModel.hasMore { -// Task { -// await inventoryViewModel.loadInventoryList() -// } -// } -// } } + if inventoryViewModel.isLoading && inventoryViewModel.hasMore { ProgressView() .padding(.vertical) } } - .padding(.top, 8) } } .background(Color.Light) @@ -120,93 +149,34 @@ struct InventorySearchView: View { } } -// ✅ 재고 카드 (UI 스타일 적용) -struct InventoryCard: View { - let item: InventoryItem - - var body: some View { - VStack(alignment: .leading, spacing: 5) { - Text(item.categoryName) - .font(.system(size: 12, weight: .semibold)) - .foregroundColor(.black) - - Divider() - .frame(height: 0.2) - .background(Color.textGray2) - - HStack(alignment: .center, spacing: 12) { - AsyncImage(url: URL(string: item.image)) { image in - image.resizable().scaledToFit() - } placeholder: { - Color.gray.opacity(0.2) - } - .frame(width: 64, height: 64) - .cornerRadius(10) - - VStack(alignment: .leading, spacing: 6) { - HStack { - Text(item.korName) - .font(.system(size: 14, weight: .bold)) - .foregroundColor(.black) - .lineLimit(2) - Spacer() - } - .padding(.top, 2) - - Text("\(item.trim)/\(item.model)") - .font(.system(size: 13, weight: .regular)) - .foregroundColor(.gray) - - - - } - .frame(height: 60, alignment: .top) - - VStack (alignment: .center, spacing: 6) { - if item.isLack { - Text("수량 부족") - .font(.system(size: 13, weight: .regular)) - .padding(.horizontal, 10) - .padding(.vertical, 5) - .background(Color.DangerBg) - .foregroundColor(.Danger) - .cornerRadius(12) - } else { - Text("수량 여유") - .font(.system(size: 13, weight: .regular)) - .padding(.horizontal, 10) - .padding(.vertical, 5) - .background(Color.StatusGreenBg) - .foregroundColor(.StatusGreen) - .cornerRadius(12) - } - - VStack(alignment: .leading){ - Text("현재수량: \(item.amount)개") - .font(.system(size: 11, weight: .regular)) - .foregroundColor(.textGray1) - - Text("최소수량: \(item.limitAmount)개") - .font(.system(size: 11, weight: .regular)) - .foregroundColor(.textGray1) - } - } - } - } - .padding() - .background(Color.white) - .cornerRadius(10) - .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) - } -} - // ✅ 필터 버튼 공용 컴포넌트 struct FilterMenu: View { let title: String let items: [String] - let selected: (String) -> Bool + let selectedItems: [String] let onTap: (String) -> Void + var displayTitle: String { + if selectedItems.isEmpty { + return title + } else if selectedItems.count == 1 { + return selectedItems.first ?? title + } else { + return "\(title) \(selectedItems.count)" + } + } + + var isActive: Bool { !selectedItems.isEmpty } + + var truncatedTitle: String { + // 글자 6자까지만 표시, 이후 "..." 처리 + if displayTitle.count > 6 { + let prefix = displayTitle.prefix(5) + return "\(prefix)…" + } + return displayTitle + } + var body: some View { Menu { ForEach(items, id: \.self) { item in @@ -215,21 +185,31 @@ struct FilterMenu: View { } label: { HStack { Text(item) - if selected(item) { + if selectedItems.contains(item) { Spacer() Image(systemName: "checkmark") + .foregroundColor(.blue) } } } } } label: { - Text(title) - .font(.subheadline) - .foregroundColor(.black) - .padding(.horizontal, 12) - .padding(.vertical, 6) - .background(Color(.systemGray6)) - .cornerRadius(8) + HStack(spacing: 6) { + Text(truncatedTitle) + .font(.system(size: 13)) + .foregroundColor(isActive ? .blue : .black) + .lineLimit(1) + .truncationMode(.tail) // ✅ 안전하게 "..." 처리 + .multilineTextAlignment(.center) + + Image(systemName: "chevron.down") + .font(.system(size: 11, weight: .semibold)) + .foregroundColor(isActive ? .blue : .gray) + } + .padding(.horizontal, 12) + .padding(.vertical, 10) // ✅ 높이 늘림 + .background(isActive ? Color.blue.opacity(0.2) : Color(.systemGray6)) + .cornerRadius(8) } } } diff --git a/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift b/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift index 03e7979..ee9c06e 100644 --- a/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift +++ b/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift @@ -24,6 +24,12 @@ final class InventoryViewModel: ObservableObject { @Published var underLimitItems: [InventoryItem] = [] @Published var underLimitPage = 0 @Published var underLimitHasMore = true + + @Published var searchResults: [InventoryItem] = [] + @Published var searchPage = 0 + @Published var searchHasMore = true + @Published var isSearching = false + private let repo: InventoryRepositoryProtocol @@ -37,6 +43,7 @@ final class InventoryViewModel: ObservableObject { size: Int = 20 ) async { guard !isLoading else { return } + isSearching = false // ✅ 검색 모드 해제 isLoading = true if reset { @@ -107,28 +114,7 @@ final class InventoryViewModel: ObservableObject { Task { await resetAndLoad() } } - // 부족 재고 조회 -// func loadUnderLimitList(categoryName: String? = nil) async { -// let result = await repo.getUnderLimitList( -// categoryName: categoryName, -// page: 0, -// size: 10 -// ) -// -// switch result { -// case .success(let apiResp): -// if let data = apiResp.data { -// underLimitItems = data.content -// } else { -// message = apiResp.message -// } -// case .failure(let err): -// message = err.message -// if err.code == 401 || err.code == 403 { -// shouldGoToLogin = true -// } -// } -// } + // 부족 재고 로드 func loadUnderLimitList(reset: Bool = false, size: Int = 10) async { guard !isLoading, underLimitHasMore else { return } isLoading = true @@ -166,4 +152,95 @@ final class InventoryViewModel: ObservableObject { isLoading = false } + // ✅ 부품 이름 검색 + func searchByName(name: String, reset: Bool = false, size: Int = 20) async { + guard !isLoading else { return } + isLoading = true + isSearching = true + + if reset { + searchPage = 0 + searchResults.removeAll() + searchHasMore = true + } + + let result = await repo.findByName(name: name, page: searchPage, size: size) + + switch result { + case .success(let apiResp): + if let data = apiResp.data { + if reset { + searchResults = data.content + } else { + searchResults.append(contentsOf: data.content) + } + searchHasMore = searchPage + 1 < data.totalPages + searchPage += 1 + } + case .failure(let err): + message = err.message + if err.code == 401 || err.code == 403 { + shouldGoToLogin = true + } + } + + isLoading = false + } + + // 검색 후 필터링된 리스트 계산 + var filteredSearchResults: [InventoryItem] { + searchResults.filter { item in + // 카테고리 필터 + if !selectedCategories.isEmpty && !selectedCategories.contains(item.categoryName) { + return false + } + // 트림 필터 + if !selectedTrims.isEmpty && !selectedTrims.contains(item.trim) { + return false + } + // 모델 필터 + if !selectedModels.isEmpty && !selectedModels.contains(item.model) { + return false + } + return true + } + } + + func searchInFilteredList(keyword: String) { + guard !keyword.trimmingCharacters(in: .whitespaces).isEmpty else { + // 검색어 비면 원래 리스트 그대로 표시 + isSearching = false + return + } + + isSearching = true + searchResults = inventoryItems.filter { + //$0.name.localizedCaseInsensitiveContains(keyword) || + $0.korName.localizedCaseInsensitiveContains(keyword) // || + //$0.engName.localizedCaseInsensitiveContains(keyword) + } + } + + func resetFilters(with searchText: String) { + // 1. 필터 관련 선택 초기화 + selectedCategories.removeAll() + selectedTrims.removeAll() + selectedModels.removeAll() + + // 2. 검색어가 없을 경우 → 전체 재고 다시 + if searchText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { + isSearching = false + searchResults.removeAll() + searchPage = 0 + Task { await loadInventoryList(reset: true) } + } + // 3. 검색어가 있는 경우 → 해당 검색어로 전체 결과 다시 검색 + else { + isSearching = true + searchPage = 0 + searchResults.removeAll() + Task { await searchByName(name: searchText, reset: true) } + } + } + } From 05d737530a06f34183ecd4365be047d99f48de73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Fri, 17 Oct 2025 17:31:16 +0900 Subject: [PATCH 13/85] =?UTF-8?q?[FIX]=20=EC=9E=AC=EA=B3=A0=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=ED=83=AD=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/feature/auth/ui/LoginView.swift | 3 +-- .../inventory/ui/InventorySearchView.swift | 8 +++--- .../feature/inventory/ui/InventoryView.swift | 25 +++++++++++-------- .../viewmodel/InventoryViewModel.swift | 3 ++- .../app/navigation/MainTabView.swift | 20 ++++++++++----- 5 files changed, 37 insertions(+), 22 deletions(-) diff --git a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift index 06d745a..4409e9a 100644 --- a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift @@ -18,7 +18,6 @@ struct LoginView: View { @State private var emailError: String? = nil @State private var pwError: String? = nil -// var onLoginSuccess: () -> Void = {} var onLogin: (String, String) -> Void = { _, _ in } var onClickRegister: () -> Void = {} @@ -121,7 +120,7 @@ struct LoginView: View { // MARK: - 유효성 검사 함수 private func isValidForm() -> Bool { emailError = isValidEmail(authViewModel.email) ? nil : "이메일 형식을 확인해주세요" - //pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" + pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" return emailError == nil //&& pwError == nil } diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift index ce8ac6c..0069d00 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift @@ -22,7 +22,7 @@ struct InventorySearchView: View { "대형": ["제네시스BH", "에쿠스"], "SUV": ["베뉴", "코나OS", "코나SX2", "투싼IX", "투싼TL", "투싼NX4", "싼타페CM", "싼타페DM", "싼타페TM", "싼타페MX5", "맥스크루즈", "베라크루즈", "팰리세이드LX2", "팰리세이드LX3"], "화물/트럭/승합": ["스타렉스", "그랜드스타렉스", "스타리아", "포터2", "쏠라티", "마이티", "메가트럭", "카운티"], - "수소/전기자동차": ["아이오닉5", "아이오닉6", "아이오닉9", "넥쏘FE", "넥쏘NH2"] + "수소/전기": ["아이오닉5", "아이오닉6", "아이오닉9", "넥쏘FE", "넥쏘NH2"] ] private var filteredModels: [String] { @@ -132,8 +132,10 @@ struct InventorySearchView: View { } } - - if inventoryViewModel.isLoading && inventoryViewModel.hasMore { + if inventoryViewModel.isLoading && + (inventoryViewModel.isSearching + ? inventoryViewModel.searchHasMore + : inventoryViewModel.hasMore) { ProgressView() .padding(.vertical) } diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift index 58ab734..b0d3132 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift @@ -7,9 +7,14 @@ import SwiftUI struct InventoryView: View { + @Binding var selectedTab: Int + @Binding var tabTappedTrigger: Bool + @StateObject private var inventoryViewModel = InventoryViewModel() @State private var showScrollToTopButton = false @Namespace private var topID + + @State private var lastTabSelection = 0 var body: some View { NavigationStack { @@ -17,12 +22,12 @@ struct InventoryView: View { ZStack { ScrollView { VStack(spacing: 0) { - // ✅ 스크롤 감지용 (맨 위) + // 스크롤 감지용 (맨 위) GeometryReader { geo in Color.clear .onChange(of: geo.frame(in: .global).minY) { newValue in // 👇 스크롤 시 값이 변함 - print("📏 Scroll offsetY:", newValue) // 테스트용, 화면 안정화 후 제거 + //print("📏 Scroll offsetY:", newValue) // 테스트용, 화면 안정화 후 제거 withAnimation(.easeInOut(duration: 0.25)) { showScrollToTopButton = newValue < -150 } @@ -84,13 +89,13 @@ struct InventoryView: View { } label: { ZStack { Circle() - .fill(Color.Secondary) // ✅ 배경색 + .fill(Color.Secondary) // 배경색 .frame(width: 50, height: 50) Image(systemName: "arrow.up") .font( .system(size: 24, weight: .bold) ) - .foregroundColor(.white) // ✅ 화살표 색 + .foregroundColor(.white) // 화살표 색 } } .padding(.trailing, 20) @@ -100,13 +105,17 @@ struct InventoryView: View { .transition(.opacity) .animation(.easeInOut(duration: 0.25), value: showScrollToTopButton) } - - } .background(Color.Light) .task { await inventoryViewModel.loadUnderLimitList(reset: true) } + // 같은 탭 다시 눌릴 때 위로 스크롤 + .onChange(of: tabTappedTrigger) { _ in + withAnimation(.easeInOut) { + proxy.scrollTo(topID, anchor: .top) + } + } } } } @@ -174,7 +183,3 @@ struct GridMenuView: View { .padding(.vertical, 17) } } - -#Preview { - InventoryView() -} diff --git a/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift b/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift index ee9c06e..dd8f3e5 100644 --- a/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift +++ b/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift @@ -116,7 +116,8 @@ final class InventoryViewModel: ObservableObject { // 부족 재고 로드 func loadUnderLimitList(reset: Bool = false, size: Int = 10) async { - guard !isLoading, underLimitHasMore else { return } +// guard !isLoading, underLimitHasMore else { return } + guard !isLoading, (underLimitHasMore || reset) else { return } isLoading = true if reset { diff --git a/StockMate/StockMate/app/navigation/MainTabView.swift b/StockMate/StockMate/app/navigation/MainTabView.swift index ed4fcef..0b42bed 100644 --- a/StockMate/StockMate/app/navigation/MainTabView.swift +++ b/StockMate/StockMate/app/navigation/MainTabView.swift @@ -9,17 +9,17 @@ import SwiftUI struct MainTabView: View { @State private var selectedTab = 0 + @State private var tabTappedTrigger = false var body: some View { VStack(spacing: 0) { // 메인 화면 ZStack { switch selectedTab { - case 0: NavigationStack{ HomeView() -// UserInfoView() - } + case 0: NavigationStack{ HomeView() } case 1: NavigationStack{ OrderView() } - case 2: NavigationStack{ InventoryView() } + case 2: + NavigationStack { InventoryView(selectedTab: $selectedTab, tabTappedTrigger: $tabTappedTrigger) } case 3: NavigationStack{ ContentView() } default: NavigationStack{ HomeView() } } @@ -44,9 +44,17 @@ struct MainTabView: View { func tabButton(index: Int, icon: String, text: String) -> some View { let isSelected = selectedTab == index return Button { - withAnimation(.easeInOut) { // 탭 전환 애니메이션. 없애도 됨 - selectedTab = index + if selectedTab == index { + // ✅ 같은 탭 다시 누르면 트리거 토글 + tabTappedTrigger.toggle() + } else { + withAnimation(.easeInOut) { + selectedTab = index + } } +// withAnimation(.easeInOut) { // 탭 전환 애니메이션. 없애도 됨 +// selectedTab = index +// } } label: { VStack(spacing: 6) { Image(icon) From b20224031d6c99d0969747f529a32aa6e1bff4f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Fri, 17 Oct 2025 17:44:10 +0900 Subject: [PATCH 14/85] =?UTF-8?q?[FIX]=20=EC=9E=AC=EA=B3=A0=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=ED=8E=98=EC=9D=B4=20=EB=B2=84=EA=B7=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../inventory/ui/InventorySearchView.swift | 157 ++++++++++++------ 1 file changed, 106 insertions(+), 51 deletions(-) diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift index 0069d00..a2aa908 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift @@ -17,11 +17,54 @@ struct InventorySearchView: View { private let trims = ["준중형/소형", "중형", "대형", "SUV", "화물/트럭/승합", "수소/전기"] private let trimToModels: [String: [String]] = [ - "준중형/소형": ["아반떼MD", "아반떼AD", "아반떼CN7", "I30", "엑센트", "아이오닉", "벨로스터", "캐스퍼"], - "중형": ["NF소나타", "YF소나타", "LF소나타", "DN8소나타", "그랜저TG", "그랜저HG", "그랜저IG", "그랜저GN7", "I40"], + "준중형/소형": [ + "아반떼MD", + "아반떼AD", + "아반떼CN7", + "I30", + "엑센트", + "아이오닉", + "벨로스터", + "캐스퍼" + ], + "중형": [ + "NF소나타", + "YF소나타", + "LF소나타", + "DN8소나타", + "그랜저TG", + "그랜저HG", + "그랜저IG", + "그랜저GN7", + "I40" + ], "대형": ["제네시스BH", "에쿠스"], - "SUV": ["베뉴", "코나OS", "코나SX2", "투싼IX", "투싼TL", "투싼NX4", "싼타페CM", "싼타페DM", "싼타페TM", "싼타페MX5", "맥스크루즈", "베라크루즈", "팰리세이드LX2", "팰리세이드LX3"], - "화물/트럭/승합": ["스타렉스", "그랜드스타렉스", "스타리아", "포터2", "쏠라티", "마이티", "메가트럭", "카운티"], + "SUV": [ + "베뉴", + "코나OS", + "코나SX2", + "투싼IX", + "투싼TL", + "투싼NX4", + "싼타페CM", + "싼타페DM", + "싼타페TM", + "싼타페MX5", + "맥스크루즈", + "베라크루즈", + "팰리세이드LX2", + "팰리세이드LX3" + ], + "화물/트럭/승합": [ + "스타렉스", + "그랜드스타렉스", + "스타리아", + "포터2", + "쏠라티", + "마이티", + "메가트럭", + "카운티" + ], "수소/전기": ["아이오닉5", "아이오닉6", "아이오닉9", "넥쏘FE", "넥쏘NH2"] ] @@ -29,7 +72,8 @@ struct InventorySearchView: View { if inventoryViewModel.selectedTrims.isEmpty { return trimToModels.values.flatMap { $0 } } else { - return inventoryViewModel.selectedTrims.flatMap { trimToModels[$0] ?? [] } + return inventoryViewModel.selectedTrims + .flatMap { trimToModels[$0] ?? [] } } } @@ -44,7 +88,8 @@ struct InventorySearchView: View { TextField("부품을 검색하세요.", text: $searchText) .textFieldStyle(PlainTextFieldStyle()) .onChange(of: searchText) { newValue in - inventoryViewModel.searchInFilteredList(keyword: newValue) + inventoryViewModel + .searchInFilteredList(keyword: newValue) } if !searchText.isEmpty { @@ -65,46 +110,46 @@ struct InventorySearchView: View { .padding(.horizontal) .padding(.vertical) - HStack(spacing: 10) { - FilterMenu( - title: "카테고리", - items: categories, - selectedItems: inventoryViewModel.selectedCategories, - onTap: { inventoryViewModel.toggleCategory($0) } - ) + HStack(spacing: 10) { + FilterMenu( + title: "카테고리", + items: categories, + selectedItems: inventoryViewModel.selectedCategories, + onTap: { inventoryViewModel.toggleCategory($0) } + ) - FilterMenu( - title: "분류", - items: trims, - selectedItems: inventoryViewModel.selectedTrims, - onTap: { inventoryViewModel.toggleTrim($0) } - ) + FilterMenu( + title: "분류", + items: trims, + selectedItems: inventoryViewModel.selectedTrims, + onTap: { inventoryViewModel.toggleTrim($0) } + ) - FilterMenu( - title: "모델", - items: filteredModels, - selectedItems: inventoryViewModel.selectedModels, - onTap: { inventoryViewModel.toggleModel($0) } - ) + FilterMenu( + title: "모델", + items: filteredModels, + selectedItems: inventoryViewModel.selectedModels, + onTap: { inventoryViewModel.toggleModel($0) } + ) - // 🔄 초기화 버튼 - Button(action: { - inventoryViewModel.resetFilters(with: searchText) - }) { - HStack(spacing: 4) { - Image(systemName: "arrow.counterclockwise") - .font(.system(size: 13)) - Text("초기화") - .font(.system(size: 13, weight: .medium)) - } - .foregroundColor(.blue) - .padding(.trailing, 8) - } - .frame(maxWidth: .infinity, alignment: .trailing) - + // 🔄 초기화 버튼 + Button(action: { + inventoryViewModel.resetFilters(with: searchText) + }) { + HStack(spacing: 4) { + Image(systemName: "arrow.counterclockwise") + .font(.system(size: 13)) + Text("초기화") + .font(.system(size: 13, weight: .medium)) + } + .foregroundColor(.blue) + .padding(.trailing, 8) } - .padding(.horizontal) - .padding(.bottom, 16) + .frame(maxWidth: .infinity, alignment: .trailing) + + } + .padding(.horizontal) + .padding(.bottom, 16) // 📋 재고 리스트 ScrollView { @@ -112,30 +157,38 @@ struct InventorySearchView: View { ForEach( inventoryViewModel.isSearching - ? inventoryViewModel.filteredSearchResults // ✅ 검색 + 필터링 - : inventoryViewModel.inventoryItems + ? inventoryViewModel.filteredSearchResults // ✅ 검색 + 필터링 + : inventoryViewModel.inventoryItems ) { item in InventoryCardView(item: item) .padding(.horizontal) .onAppear { if inventoryViewModel.isSearching { - if item.id == inventoryViewModel.searchResults.last?.id, + if item.id == inventoryViewModel.filteredSearchResults.last?.id, inventoryViewModel.searchHasMore { - Task { await inventoryViewModel.searchByName(name: searchText) } + Task { + await inventoryViewModel + .searchByName( + name: searchText + ) + } } } else { if item.id == inventoryViewModel.inventoryItems.last?.id, inventoryViewModel.hasMore { - Task { await inventoryViewModel.loadInventoryList() } + Task { + await inventoryViewModel + .loadInventoryList() + } } } } } if inventoryViewModel.isLoading && - (inventoryViewModel.isSearching - ? inventoryViewModel.searchHasMore - : inventoryViewModel.hasMore) { + (inventoryViewModel.isSearching + ? inventoryViewModel.searchHasMore + : inventoryViewModel.hasMore) { ProgressView() .padding(.vertical) } @@ -210,7 +263,9 @@ struct FilterMenu: View { } .padding(.horizontal, 12) .padding(.vertical, 10) // ✅ 높이 늘림 - .background(isActive ? Color.blue.opacity(0.2) : Color(.systemGray6)) + .background( + isActive ? Color.blue.opacity(0.2) : Color(.systemGray6) + ) .cornerRadius(8) } } From 3b00a938baed69c3f936bd7dd6995b7072e18ee7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Fri, 17 Oct 2025 17:59:18 +0900 Subject: [PATCH 15/85] =?UTF-8?q?[FIX]=20=EC=9E=AC=EA=B3=A0=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=B2=84=EA=B7=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../inventory/ui/InventorySearchView.swift | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift index a2aa908..32a9c4f 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift @@ -87,15 +87,23 @@ struct InventorySearchView: View { TextField("부품을 검색하세요.", text: $searchText) .textFieldStyle(PlainTextFieldStyle()) +// .onChange(of: searchText) { newValue in +// inventoryViewModel +// .searchInFilteredList(keyword: newValue) +// } .onChange(of: searchText) { newValue in - inventoryViewModel - .searchInFilteredList(keyword: newValue) - } + let term = newValue.trimmingCharacters(in: .whitespacesAndNewlines) + if term.isEmpty { + Task { await inventoryViewModel.loadInventoryList(reset: true) } + } else { + Task { await inventoryViewModel.searchByName(name: term, reset: true) } + } + } if !searchText.isEmpty { Button(action: { searchText = "" - inventoryViewModel.isSearching = false + //inventoryViewModel.isSearching = false }) { Image(systemName: "xmark") .foregroundColor(.gray) @@ -169,7 +177,7 @@ struct InventorySearchView: View { Task { await inventoryViewModel .searchByName( - name: searchText + name: searchText.trimmingCharacters(in: .whitespacesAndNewlines) ) } } @@ -224,7 +232,7 @@ struct FilterMenu: View { var isActive: Bool { !selectedItems.isEmpty } var truncatedTitle: String { - // 글자 6자까지만 표시, 이후 "..." 처리 + // 글자 5자까지만 표시, 이후 "..." 처리 if displayTitle.count > 6 { let prefix = displayTitle.prefix(5) return "\(prefix)…" From ad06779953a1d6caecf4e5e0a51b937215ae9944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Fri, 17 Oct 2025 18:39:09 +0900 Subject: [PATCH 16/85] =?UTF-8?q?[FIX]=20=EC=9E=AC=EA=B3=A0=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=B2=84=EA=B7=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/feature/auth/ui/LoginView.swift | 2 +- .../inventory/ui/InventorySearchView.swift | 21 +++++++------------ 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift index 4409e9a..c504e96 100644 --- a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift @@ -120,7 +120,7 @@ struct LoginView: View { // MARK: - 유효성 검사 함수 private func isValidForm() -> Bool { emailError = isValidEmail(authViewModel.email) ? nil : "이메일 형식을 확인해주세요" - pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" + //pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" return emailError == nil //&& pwError == nil } diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift index 32a9c4f..8c394ad 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift @@ -87,23 +87,15 @@ struct InventorySearchView: View { TextField("부품을 검색하세요.", text: $searchText) .textFieldStyle(PlainTextFieldStyle()) -// .onChange(of: searchText) { newValue in -// inventoryViewModel -// .searchInFilteredList(keyword: newValue) -// } .onChange(of: searchText) { newValue in - let term = newValue.trimmingCharacters(in: .whitespacesAndNewlines) - if term.isEmpty { - Task { await inventoryViewModel.loadInventoryList(reset: true) } - } else { - Task { await inventoryViewModel.searchByName(name: term, reset: true) } - } - } + inventoryViewModel + .searchInFilteredList(keyword: newValue) + } if !searchText.isEmpty { Button(action: { searchText = "" - //inventoryViewModel.isSearching = false + inventoryViewModel.isSearching = false }) { Image(systemName: "xmark") .foregroundColor(.gray) @@ -177,7 +169,10 @@ struct InventorySearchView: View { Task { await inventoryViewModel .searchByName( - name: searchText.trimmingCharacters(in: .whitespacesAndNewlines) + name: searchText + .trimmingCharacters( + in: .whitespacesAndNewlines + ) ) } } From b5325df450cc05250ebf6b7cd13bb6b9a160c4cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sun, 19 Oct 2025 13:46:49 +0900 Subject: [PATCH 17/85] =?UTF-8?q?Revert=20"[FIX]=20=EC=9E=AC=EA=B3=A0?= =?UTF-8?q?=EA=B2=80=EC=83=89=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=B2=84?= =?UTF-8?q?=EA=B7=B8=20=EC=88=98=EC=A0=95"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit ad06779953a1d6caecf4e5e0a51b937215ae9944. --- .../app/feature/auth/ui/LoginView.swift | 2 +- .../inventory/ui/InventorySearchView.swift | 21 ++++++++++++------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift index c504e96..4409e9a 100644 --- a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift @@ -120,7 +120,7 @@ struct LoginView: View { // MARK: - 유효성 검사 함수 private func isValidForm() -> Bool { emailError = isValidEmail(authViewModel.email) ? nil : "이메일 형식을 확인해주세요" - //pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" + pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" return emailError == nil //&& pwError == nil } diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift index 8c394ad..32a9c4f 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift @@ -87,15 +87,23 @@ struct InventorySearchView: View { TextField("부품을 검색하세요.", text: $searchText) .textFieldStyle(PlainTextFieldStyle()) +// .onChange(of: searchText) { newValue in +// inventoryViewModel +// .searchInFilteredList(keyword: newValue) +// } .onChange(of: searchText) { newValue in - inventoryViewModel - .searchInFilteredList(keyword: newValue) - } + let term = newValue.trimmingCharacters(in: .whitespacesAndNewlines) + if term.isEmpty { + Task { await inventoryViewModel.loadInventoryList(reset: true) } + } else { + Task { await inventoryViewModel.searchByName(name: term, reset: true) } + } + } if !searchText.isEmpty { Button(action: { searchText = "" - inventoryViewModel.isSearching = false + //inventoryViewModel.isSearching = false }) { Image(systemName: "xmark") .foregroundColor(.gray) @@ -169,10 +177,7 @@ struct InventorySearchView: View { Task { await inventoryViewModel .searchByName( - name: searchText - .trimmingCharacters( - in: .whitespacesAndNewlines - ) + name: searchText.trimmingCharacters(in: .whitespacesAndNewlines) ) } } From 8185e13043cfcdb5c65c0dcd2ba34dd4b9d0aee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sun, 19 Oct 2025 13:47:03 +0900 Subject: [PATCH 18/85] =?UTF-8?q?Revert=20"[FIX]=20=EC=9E=AC=EA=B3=A0?= =?UTF-8?q?=EA=B2=80=EC=83=89=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=B2=84?= =?UTF-8?q?=EA=B7=B8=20=EC=88=98=EC=A0=95"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 3b00a938baed69c3f936bd7dd6995b7072e18ee7. --- .../inventory/ui/InventorySearchView.swift | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift index 32a9c4f..a2aa908 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift @@ -87,23 +87,15 @@ struct InventorySearchView: View { TextField("부품을 검색하세요.", text: $searchText) .textFieldStyle(PlainTextFieldStyle()) -// .onChange(of: searchText) { newValue in -// inventoryViewModel -// .searchInFilteredList(keyword: newValue) -// } .onChange(of: searchText) { newValue in - let term = newValue.trimmingCharacters(in: .whitespacesAndNewlines) - if term.isEmpty { - Task { await inventoryViewModel.loadInventoryList(reset: true) } - } else { - Task { await inventoryViewModel.searchByName(name: term, reset: true) } - } - } + inventoryViewModel + .searchInFilteredList(keyword: newValue) + } if !searchText.isEmpty { Button(action: { searchText = "" - //inventoryViewModel.isSearching = false + inventoryViewModel.isSearching = false }) { Image(systemName: "xmark") .foregroundColor(.gray) @@ -177,7 +169,7 @@ struct InventorySearchView: View { Task { await inventoryViewModel .searchByName( - name: searchText.trimmingCharacters(in: .whitespacesAndNewlines) + name: searchText ) } } @@ -232,7 +224,7 @@ struct FilterMenu: View { var isActive: Bool { !selectedItems.isEmpty } var truncatedTitle: String { - // 글자 5자까지만 표시, 이후 "..." 처리 + // 글자 6자까지만 표시, 이후 "..." 처리 if displayTitle.count > 6 { let prefix = displayTitle.prefix(5) return "\(prefix)…" From 8b8b5ba6cf53371f42757705e5822b7849c07502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Mon, 20 Oct 2025 12:40:14 +0900 Subject: [PATCH 19/85] =?UTF-8?q?[FEAT]=20=EC=9E=AC=EA=B3=A0=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/feature/auth/ui/LoginView.swift | 2 +- .../inventory/ui/InventorySearchView.swift | 126 ++++++------------ .../viewmodel/InventoryViewModel.swift | 126 +++++++++++------- 3 files changed, 121 insertions(+), 133 deletions(-) diff --git a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift index 4409e9a..06e87e3 100644 --- a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift @@ -121,7 +121,7 @@ struct LoginView: View { private func isValidForm() -> Bool { emailError = isValidEmail(authViewModel.email) ? nil : "이메일 형식을 확인해주세요" pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" - return emailError == nil //&& pwError == nil + return emailError == nil && pwError == nil } } diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift index a2aa908..5e8c205 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift @@ -12,59 +12,15 @@ struct InventorySearchView: View { @StateObject private var inventoryViewModel = InventoryViewModel() @State private var searchText = "" - + // 카테고리, 분류, 모델 private let categories = ["전기/램프", "엔진/미션", "하체/바디", "내장/외장", "기타소모품"] private let trims = ["준중형/소형", "중형", "대형", "SUV", "화물/트럭/승합", "수소/전기"] - private let trimToModels: [String: [String]] = [ - "준중형/소형": [ - "아반떼MD", - "아반떼AD", - "아반떼CN7", - "I30", - "엑센트", - "아이오닉", - "벨로스터", - "캐스퍼" - ], - "중형": [ - "NF소나타", - "YF소나타", - "LF소나타", - "DN8소나타", - "그랜저TG", - "그랜저HG", - "그랜저IG", - "그랜저GN7", - "I40" - ], + "준중형/소형": [ "아반떼MD", "아반떼AD", "아반떼CN7", "I30", "엑센트", "아이오닉", "벨로스터", "캐스퍼" ], + "중형": [ "NF소나타", "YF소나타", "LF소나타", "DN8소나타", "그랜저TG", "그랜저HG", "그랜저IG", "그랜저GN7", "I40" ], "대형": ["제네시스BH", "에쿠스"], - "SUV": [ - "베뉴", - "코나OS", - "코나SX2", - "투싼IX", - "투싼TL", - "투싼NX4", - "싼타페CM", - "싼타페DM", - "싼타페TM", - "싼타페MX5", - "맥스크루즈", - "베라크루즈", - "팰리세이드LX2", - "팰리세이드LX3" - ], - "화물/트럭/승합": [ - "스타렉스", - "그랜드스타렉스", - "스타리아", - "포터2", - "쏠라티", - "마이티", - "메가트럭", - "카운티" - ], + "SUV": [ "베뉴", "코나OS", "코나SX2", "투싼IX", "투싼TL", "투싼NX4", "싼타페CM", "싼타페DM", "싼타페TM", "싼타페MX5", "맥스크루즈", "베라크루즈", "팰리세이드LX2", "팰리세이드LX3" ], + "화물/트럭/승합": [ "스타렉스", "그랜드스타렉스", "스타리아", "포터2", "쏠라티", "마이티", "메가트럭", "카운티" ], "수소/전기": ["아이오닉5", "아이오닉6", "아이오닉9", "넥쏘FE", "넥쏘NH2"] ] @@ -87,9 +43,10 @@ struct InventorySearchView: View { TextField("부품을 검색하세요.", text: $searchText) .textFieldStyle(PlainTextFieldStyle()) - .onChange(of: searchText) { newValue in - inventoryViewModel - .searchInFilteredList(keyword: newValue) + .onSubmit { + Task { + await inventoryViewModel.searchByName(name: searchText, reset: true) + } } if !searchText.isEmpty { @@ -107,9 +64,14 @@ struct InventorySearchView: View { .padding() .background(Color(.white)) .cornerRadius(9999) + .overlay( + RoundedRectangle(cornerRadius: 9999) + .stroke(Color.gray.opacity(0.4), lineWidth: 1) + ) .padding(.horizontal) .padding(.vertical) + // 필터 및 초기화 버튼 HStack(spacing: 10) { FilterMenu( title: "카테고리", @@ -138,10 +100,9 @@ struct InventorySearchView: View { }) { HStack(spacing: 4) { Image(systemName: "arrow.counterclockwise") - .font(.system(size: 13)) Text("초기화") - .font(.system(size: 13, weight: .medium)) } + .font(.system(size: 13, weight: .medium)) .foregroundColor(.blue) .padding(.trailing, 8) } @@ -157,41 +118,32 @@ struct InventorySearchView: View { ForEach( inventoryViewModel.isSearching - ? inventoryViewModel.filteredSearchResults // ✅ 검색 + 필터링 + ? inventoryViewModel.filteredSearchResults // 검색 + 필터링 : inventoryViewModel.inventoryItems ) { item in InventoryCardView(item: item) .padding(.horizontal) .onAppear { - if inventoryViewModel.isSearching { - if item.id == inventoryViewModel.filteredSearchResults.last?.id, - inventoryViewModel.searchHasMore { - Task { - await inventoryViewModel - .searchByName( - name: searchText - ) + Task { + if inventoryViewModel.isSearching { + if item.id == inventoryViewModel.filteredSearchResults.last?.id, + inventoryViewModel.searchHasMore { + await inventoryViewModel.loadMore(searchText: searchText) } - } - } else { - if item.id == inventoryViewModel.inventoryItems.last?.id, - inventoryViewModel.hasMore { - Task { - await inventoryViewModel - .loadInventoryList() + } else { + if item.id == inventoryViewModel.inventoryItems.last?.id, + inventoryViewModel.hasMore { + await inventoryViewModel.loadMore(searchText: searchText) } } } } } - if inventoryViewModel.isLoading && - (inventoryViewModel.isSearching - ? inventoryViewModel.searchHasMore - : inventoryViewModel.hasMore) { - ProgressView() - .padding(.vertical) - } + if inventoryViewModel.isLoading { + ProgressView() + .padding(.vertical) + } } } } @@ -204,7 +156,7 @@ struct InventorySearchView: View { } } -// ✅ 필터 버튼 공용 컴포넌트 +// 필터 버튼 공용 컴포넌트 struct FilterMenu: View { let title: String let items: [String] @@ -217,7 +169,7 @@ struct FilterMenu: View { } else if selectedItems.count == 1 { return selectedItems.first ?? title } else { - return "\(title) \(selectedItems.count)" + return "\(title) (\(selectedItems.count))" } } @@ -225,8 +177,8 @@ struct FilterMenu: View { var truncatedTitle: String { // 글자 6자까지만 표시, 이후 "..." 처리 - if displayTitle.count > 6 { - let prefix = displayTitle.prefix(5) + if displayTitle.count > 8 { + let prefix = displayTitle.prefix(8) return "\(prefix)…" } return displayTitle @@ -250,19 +202,19 @@ struct FilterMenu: View { } } label: { HStack(spacing: 6) { + Image(systemName: "chevron.down") + .font(.system(size: 11, weight: .semibold)) + .foregroundColor(isActive ? .blue : .gray) + Text(truncatedTitle) .font(.system(size: 13)) .foregroundColor(isActive ? .blue : .black) .lineLimit(1) - .truncationMode(.tail) // ✅ 안전하게 "..." 처리 + .truncationMode(.tail) // 안전하게 "..." 처리 .multilineTextAlignment(.center) - - Image(systemName: "chevron.down") - .font(.system(size: 11, weight: .semibold)) - .foregroundColor(isActive ? .blue : .gray) } .padding(.horizontal, 12) - .padding(.vertical, 10) // ✅ 높이 늘림 + .padding(.vertical, 10) // 높이 늘림 .background( isActive ? Color.blue.opacity(0.2) : Color(.systemGray6) ) diff --git a/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift b/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift index dd8f3e5..b0ec3cf 100644 --- a/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift +++ b/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift @@ -9,42 +9,46 @@ import SwiftUI @MainActor final class InventoryViewModel: ObservableObject { + // ===== 기본 상태 ===== @Published var inventoryItems: [InventoryItem] = [] @Published var message: String = "" @Published var shouldGoToLogin: Bool = false - + + // ====== 필터 상태 ====== @Published var selectedCategories: [String] = [] @Published var selectedTrims: [String] = [] @Published var selectedModels: [String] = [] + + // ===== 전체 재고 페이지네이션 ===== @Published var currentPage = 0 - @Published var isLoading = false @Published var hasMore = true + // ====== 부족 재고 페이지네이션 ====== @Published var underLimitItems: [InventoryItem] = [] @Published var underLimitPage = 0 @Published var underLimitHasMore = true + // ====== 검색 관련 ====== @Published var searchResults: [InventoryItem] = [] @Published var searchPage = 0 @Published var searchHasMore = true @Published var isSearching = false - - + + // ====== 공통 상태 ====== + @Published var isLoading = false + private let repo: InventoryRepositoryProtocol init(repo: InventoryRepositoryProtocol = InventoryRepositoryImpl()) { self.repo = repo } - // 재고 조회 - func loadInventoryList( - reset: Bool = false, - size: Int = 20 - ) async { + // MARK: - 전체 재고 로드 + func loadInventoryList(reset: Bool = false, size: Int = 20) async { guard !isLoading else { return } - isSearching = false // ✅ 검색 모드 해제 isLoading = true + isSearching = false // 검색 모드 해제 if reset { currentPage = 0 @@ -81,40 +85,13 @@ final class InventoryViewModel: ObservableObject { } isLoading = false } - + + func resetAndLoad() async { await loadInventoryList(reset: true) } - - // MARK: - Filter toggle - func toggleCategory(_ name: String) { - if selectedCategories.contains(name) { - selectedCategories.removeAll { $0 == name } - } else { - selectedCategories.append(name) - } - Task { await resetAndLoad() } - } - - func toggleTrim(_ trim: String) { - if selectedTrims.contains(trim) { - selectedTrims.removeAll { $0 == trim } - } else { - selectedTrims.append(trim) - } - Task { await resetAndLoad() } - } - - func toggleModel(_ model: String) { - if selectedModels.contains(model) { - selectedModels.removeAll { $0 == model } - } else { - selectedModels.append(model) - } - Task { await resetAndLoad() } - } - // 부족 재고 로드 + // MARK: - 부족 재고 로드 func loadUnderLimitList(reset: Bool = false, size: Int = 10) async { // guard !isLoading, underLimitHasMore else { return } guard !isLoading, (underLimitHasMore || reset) else { return } @@ -153,7 +130,7 @@ final class InventoryViewModel: ObservableObject { isLoading = false } - // ✅ 부품 이름 검색 + // MARK: - 이름 검색 func searchByName(name: String, reset: Bool = false, size: Int = 20) async { guard !isLoading else { return } isLoading = true @@ -188,7 +165,7 @@ final class InventoryViewModel: ObservableObject { isLoading = false } - // 검색 후 필터링된 리스트 계산 + // MARK: - 검색 결과에서 로컬 필터링 var filteredSearchResults: [InventoryItem] { searchResults.filter { item in // 카테고리 필터 @@ -207,13 +184,13 @@ final class InventoryViewModel: ObservableObject { } } + // MARK: - 검색어 입력 시 로컬 검색 전환 func searchInFilteredList(keyword: String) { guard !keyword.trimmingCharacters(in: .whitespaces).isEmpty else { // 검색어 비면 원래 리스트 그대로 표시 isSearching = false return } - isSearching = true searchResults = inventoryItems.filter { //$0.name.localizedCaseInsensitiveContains(keyword) || @@ -221,7 +198,56 @@ final class InventoryViewModel: ObservableObject { //$0.engName.localizedCaseInsensitiveContains(keyword) } } + + // MARK: - 필터 토글 (검색모드 해제 + 전체 재로드) + func toggleCategory(_ name: String) { + if selectedCategories.contains(name) { + selectedCategories.removeAll { $0 == name } + } else { + selectedCategories.append(name) + } + // ✅ 검색 중일 때는 로컬 필터링만 다시 계산 + if isSearching { + objectWillChange.send() + } else { + Task { await resetAndLoad() } + } + +// isSearching = false +// Task { await resetAndLoad() } + } + + func toggleTrim(_ trim: String) { + if selectedTrims.contains(trim) { + selectedTrims.removeAll { $0 == trim } + } else { + selectedTrims.append(trim) + } + if isSearching { + objectWillChange.send() + } else { + Task { await resetAndLoad() } + } +// isSearching = false +// Task { await resetAndLoad() } + } + func toggleModel(_ model: String) { + if selectedModels.contains(model) { + selectedModels.removeAll { $0 == model } + } else { + selectedModels.append(model) + } + if isSearching { + objectWillChange.send() + } else { + Task { await resetAndLoad() } + } +// isSearching = false +// Task { await resetAndLoad() } + } + + // MARK: - 필터 초기화 func resetFilters(with searchText: String) { // 1. 필터 관련 선택 초기화 selectedCategories.removeAll() @@ -232,16 +258,26 @@ final class InventoryViewModel: ObservableObject { if searchText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { isSearching = false searchResults.removeAll() - searchPage = 0 + // searchPage = 0 Task { await loadInventoryList(reset: true) } } // 3. 검색어가 있는 경우 → 해당 검색어로 전체 결과 다시 검색 else { isSearching = true - searchPage = 0 + //searchPage = 0 searchResults.removeAll() Task { await searchByName(name: searchText, reset: true) } } } + + // MARK: - ✅ 무한 스크롤 로드 + func loadMore(searchText: String) async { + if isSearching { + guard !searchText.trimmingCharacters(in: .whitespaces).isEmpty else { return } + await searchByName(name: searchText) + } else { + await loadInventoryList() + } + } } From 910feb503528f58e2bee962f8d37b856c6868dc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Mon, 20 Oct 2025 12:52:22 +0900 Subject: [PATCH 20/85] =?UTF-8?q?[FIX]=20=EC=9E=AC=EA=B3=A0=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EA=B2=80=EC=83=89?= =?UTF-8?q?=EC=96=B4=20=EA=B3=B5=EB=B0=B1=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../inventory/ui/InventorySearchView.swift | 5 +++- .../viewmodel/InventoryViewModel.swift | 25 ++++++------------- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift index 5e8c205..000cc5c 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift @@ -44,8 +44,11 @@ struct InventorySearchView: View { TextField("부품을 검색하세요.", text: $searchText) .textFieldStyle(PlainTextFieldStyle()) .onSubmit { + let term = searchText.trimmingCharacters(in: .whitespacesAndNewlines) + guard !term.isEmpty else { return } Task { - await inventoryViewModel.searchByName(name: searchText, reset: true) +// await inventoryViewModel.searchByName(name: searchText, reset: true) + await inventoryViewModel.searchByName(name: term, reset: true) } } diff --git a/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift b/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift index b0ec3cf..6467147 100644 --- a/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift +++ b/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift @@ -91,7 +91,7 @@ final class InventoryViewModel: ObservableObject { await loadInventoryList(reset: true) } - // MARK: - 부족 재고 로드 + // MARK: - 부족 재고 로드 func loadUnderLimitList(reset: Bool = false, size: Int = 10) async { // guard !isLoading, underLimitHasMore else { return } guard !isLoading, (underLimitHasMore || reset) else { return } @@ -184,21 +184,6 @@ final class InventoryViewModel: ObservableObject { } } - // MARK: - 검색어 입력 시 로컬 검색 전환 - func searchInFilteredList(keyword: String) { - guard !keyword.trimmingCharacters(in: .whitespaces).isEmpty else { - // 검색어 비면 원래 리스트 그대로 표시 - isSearching = false - return - } - isSearching = true - searchResults = inventoryItems.filter { - //$0.name.localizedCaseInsensitiveContains(keyword) || - $0.korName.localizedCaseInsensitiveContains(keyword) // || - //$0.engName.localizedCaseInsensitiveContains(keyword) - } - } - // MARK: - 필터 토글 (검색모드 해제 + 전체 재로드) func toggleCategory(_ name: String) { if selectedCategories.contains(name) { @@ -266,7 +251,13 @@ final class InventoryViewModel: ObservableObject { isSearching = true //searchPage = 0 searchResults.removeAll() - Task { await searchByName(name: searchText, reset: true) } +// Task { await searchByName(name: searchText, reset: true) } + Task { + await searchByName( + name: searchText.trimmingCharacters(in: .whitespacesAndNewlines), + reset: true + ) + } } } From c562252fc6305fd97c45ba6d48ff98839d62af4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Wed, 22 Oct 2025 17:10:48 +0900 Subject: [PATCH 21/85] =?UTF-8?q?[FEAT]=20=EC=A3=BC=EB=AC=B8=EB=82=B4?= =?UTF-8?q?=EC=97=AD,=20=EC=A3=BC=EB=AC=B8=20=EC=83=81=EC=84=B8=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/OrderRequestCardView.swift | 92 ++++++ .../app/feature/auth/ui/LoginView.swift | 7 +- .../app/feature/orders/data/OrderApi.swift | 124 ++++++++ .../orders/data/OrderRepositoryImpl.swift | 62 ++++ .../domain/OrderRepositoryProtocol.swift | 23 ++ .../app/feature/orders/ui/OrderCartView.swift | 138 +++++++++ .../feature/orders/ui/OrderDetailView.swift | 268 ++++++++++++++++++ .../app/feature/orders/ui/OrderInfoView.swift | 175 ++++++++++++ .../app/feature/orders/ui/OrderListView.swift | 197 +++++++++++++ .../feature/orders/ui/OrderRequestView.swift | 169 +++++++++++ .../feature/orders/ui/OrderResultView.swift | 77 +++++ .../app/feature/orders/ui/OrderView.swift | 138 ++------- .../viewmodel/OrderDetailViewModel.swift | 31 ++ .../orders/viewmodel/OrderViewModel.swift | 47 +++ .../app/feature/user/ui/ProfileView.swift | 152 ++++++++++ 15 files changed, 1583 insertions(+), 117 deletions(-) create mode 100644 StockMate/StockMate/app/core/components/OrderRequestCardView.swift create mode 100644 StockMate/StockMate/app/feature/orders/data/OrderApi.swift create mode 100644 StockMate/StockMate/app/feature/orders/data/OrderRepositoryImpl.swift create mode 100644 StockMate/StockMate/app/feature/orders/domain/OrderRepositoryProtocol.swift create mode 100644 StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift create mode 100644 StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift create mode 100644 StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift create mode 100644 StockMate/StockMate/app/feature/orders/ui/OrderListView.swift create mode 100644 StockMate/StockMate/app/feature/orders/ui/OrderRequestView.swift create mode 100644 StockMate/StockMate/app/feature/orders/ui/OrderResultView.swift create mode 100644 StockMate/StockMate/app/feature/orders/viewmodel/OrderDetailViewModel.swift create mode 100644 StockMate/StockMate/app/feature/orders/viewmodel/OrderViewModel.swift create mode 100644 StockMate/StockMate/app/feature/user/ui/ProfileView.swift diff --git a/StockMate/StockMate/app/core/components/OrderRequestCardView.swift b/StockMate/StockMate/app/core/components/OrderRequestCardView.swift new file mode 100644 index 0000000..d399bdd --- /dev/null +++ b/StockMate/StockMate/app/core/components/OrderRequestCardView.swift @@ -0,0 +1,92 @@ +// +// OrderRequestCardView.swift +// StockMate +// +// Created by Admin on 10/20/25. +// + +import SwiftUI + +struct OrderRequestCardView: View { + let item: InventoryItem + var quantity: Int + var onAdd: () -> Void + var onRemove: () -> Void + var onCartAdd: () -> Void + + + var body: some View { + VStack(alignment: .leading, spacing: 6) { + Text(item.categoryName) + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(.black) + + Divider().frame(height: 0.2).background(Color.textGray2) + + HStack(alignment: .center, spacing: 12) { + AsyncImage(url: URL(string: item.image)) { image in + image.resizable().scaledToFit() + } placeholder: { + Color.gray.opacity(0.2) + } + .frame(width: 64, height: 64) + .cornerRadius(10) + + VStack(alignment: .leading, spacing: 6) { + Text(item.korName) + .font(.system(size: 14, weight: .bold)) + .foregroundColor(.black) + .lineLimit(2) + + Text("\(item.trim) / \(item.model)") + .font(.system(size: 13)) + .foregroundColor(.gray) + + Text("\(item.price)원") + .font(.system(size: 13, weight: .semibold)) + .foregroundColor(.black) + } + + Spacer() + + if quantity == 0 { + Button(action: onCartAdd) { + Image(systemName: "cart.badge.plus") + .font(.system(size: 18)) + .foregroundColor(.Primary) + .padding(10) + .background(Color.Primary.opacity(0.1)) + .clipShape(Circle()) + } + } else { + HStack(spacing: 10) { + Button(action: onRemove) { + Image(systemName: "minus") + .font(.system(size: 14, weight: .bold)) + .foregroundColor(.gray) + } + + Text("\(quantity)") + .font(.system(size: 15, weight: .semibold)) + .frame(width: 24) + + Button(action: onAdd) { + Image(systemName: "plus") + .font(.system(size: 14, weight: .bold)) + .foregroundColor(.Primary) + } + } + .padding(.vertical, 6) + .padding(.horizontal, 10) + .background(Color.white) + .cornerRadius(10) + .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) + } + } + } + .padding() + .background(Color.white) + .cornerRadius(14) + .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) + } +} diff --git a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift index 06e87e3..f180064 100644 --- a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift @@ -119,9 +119,10 @@ struct LoginView: View { // MARK: - 유효성 검사 함수 private func isValidForm() -> Bool { - emailError = isValidEmail(authViewModel.email) ? nil : "이메일 형식을 확인해주세요" - pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" - return emailError == nil && pwError == nil + // emailError = isValidEmail(authViewModel.email) ? nil : "이메일 형식을 확인해주세요" + //pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" + // return emailError == nil //&& pwError == nil + return true } } diff --git a/StockMate/StockMate/app/feature/orders/data/OrderApi.swift b/StockMate/StockMate/app/feature/orders/data/OrderApi.swift new file mode 100644 index 0000000..6509385 --- /dev/null +++ b/StockMate/StockMate/app/feature/orders/data/OrderApi.swift @@ -0,0 +1,124 @@ +// +// OrderApi.swift +// StockMate +// +// Created by Admin on 10/21/25. +// + +import Foundation +import Alamofire + +// MARK: - Response Models + +struct OrderListResponse: Decodable { + let status: Int + let success: Bool + let message: String + let data: OrderPageData? +} + +struct OrderDetailResponse: Decodable { + let status: Int + let success: Bool + let message: String + let data: OrderResponseItem? +} + + +struct OrderPageData: Decodable { + let totalElements: Int + let totalPages: Int + let page: Int + let size: Int + let content: [OrderResponseItem] + let last: Bool +} + +struct OrderResponseItem: Decodable, Identifiable { + var id: Int { orderId } + + let orderId: Int + let orderNumber: String + let memberId: Int + let userInfo: OrderUserInfo? + let orderItems: [OrderItem] + let etc: String? + let rejectedMessage: String? + let carrier: String? + let trackingNumber: String? + let requestedShippingDate: String? + let shippingDate: String? + let totalPrice: Int + let orderStatus: String + let createdAt: String + let updatedAt: String +} + +struct OrderUserInfo: Decodable { + let id: Int + let memberId: Int + let email: String + let owner: String + let address: String + let storeName: String + let businessNumber: String + let role: String + let verified: String + let latitude: Double + let longitude: Double +} + +struct OrderItem: Decodable { + let partId: Int + let amount: Int + let partDetail: OrderPartDetail +} + +struct OrderPartDetail: Decodable { + let id: Int + let name: String + let price: Int + let image: String + let trim: String + let model: String + let category: Int + let korName: String + let engName: String + let categoryName: String + let amount: Int +} + +// MARK: - API Call + +enum OrderApi { + // ✅ 내 주문 목록 조회 + static func getMyOrderList( + status: String? = nil, + startDate: String? = nil, + endDate: String? = nil, + page: Int = 0, + size: Int = 20 + ) -> DataRequest { + var url = ApiClient.baseURL + "api/v1/order/list/my?page=\(page)&size=\(size)" + + if let status = status, !status.isEmpty { + url += "&status=\(status.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")" + } + if let startDate = startDate, !startDate.isEmpty { + url += "&startDate=\(startDate.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")" + } + if let endDate = endDate, !endDate.isEmpty { + url += "&endDate=\(endDate.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")" + } + + return ApiClient.shared.request(url, method: .get) + } + + + // ✅ 주문 상세 조회 API + static func getOrderDetail(orderId: Int) -> DataRequest { + let url = ApiClient.baseURL + "api/v1/order/detail?orderId=\(orderId)" + return ApiClient.shared.request(url, method: .get) + } + +} diff --git a/StockMate/StockMate/app/feature/orders/data/OrderRepositoryImpl.swift b/StockMate/StockMate/app/feature/orders/data/OrderRepositoryImpl.swift new file mode 100644 index 0000000..4a987d6 --- /dev/null +++ b/StockMate/StockMate/app/feature/orders/data/OrderRepositoryImpl.swift @@ -0,0 +1,62 @@ +// +// OrderRepositoryImpl.swift +// StockMate +// +// Created by Admin on 10/21/25. +// + +import Foundation +import Alamofire + +final class OrderRepositoryImpl: OrderRepositoryProtocol { + func fetchMyOrders( + status: String?, + startDate: String?, + endDate: String?, + page: Int, + size: Int + ) async -> AppResult { + let request = OrderApi.getMyOrderList( + status: status, + startDate: startDate, + endDate: endDate, + page: page, + size: size + ) + + // safeApi 으로 전체 ApiResponse 를 디코딩 + let result = await safeApi(request, decodeTo: OrderListResponse.self) + + switch result { + case .success(let response): + // response.data 는 OrderPageData? 이므로 안전하게 꺼내서 반환 + if let pageData = response.data { + return .success(pageData) + } else { + // 서버가 data를 비워서 보냈다면 메시지로 실패 처리 + return .failure(AppError(code: response.status, message: response.message, underlying: nil)) + } + + case .failure(let error): + return .failure(error) + } + } + + + func fetchOrderDetail(orderId: Int) async -> AppResult { + let request = OrderApi.getOrderDetail(orderId: orderId) + let result = await safeApi(request, decodeTo: OrderDetailResponse.self) // ✅ 올바른 타입으로 변경 + + switch result { + case .success(let response): + if let data = response.data { + return .success(data) + } else { + return .failure(.init(code: -1, message: "주문 상세 데이터를 불러오지 못했습니다.", underlying: nil)) // ✅ 누락된 인자 채움 + } + case .failure(let error): + return .failure(error) + } + } + +} diff --git a/StockMate/StockMate/app/feature/orders/domain/OrderRepositoryProtocol.swift b/StockMate/StockMate/app/feature/orders/domain/OrderRepositoryProtocol.swift new file mode 100644 index 0000000..c981daf --- /dev/null +++ b/StockMate/StockMate/app/feature/orders/domain/OrderRepositoryProtocol.swift @@ -0,0 +1,23 @@ +// +// OrderRepositoryProtocol.swift +// StockMate +// +// Created by Admin on 10/21/25. +// + +import Foundation + +protocol OrderRepositoryProtocol { + func fetchMyOrders( + status: String?, + startDate: String?, + endDate: String?, + page: Int, + size: Int + ) async -> AppResult + + /// 주문 상세 조회 + func fetchOrderDetail( + orderId: Int + ) async -> AppResult +} diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift new file mode 100644 index 0000000..0a0939a --- /dev/null +++ b/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift @@ -0,0 +1,138 @@ +// +// OrderCartView.swift +// StockMate +// +// Created by Admin on 10/20/25. +// + +import SwiftUI + +struct OrderCartView: View { +// +// @State private var cartItems: [InventoryItem: Int] = [ +// InventoryItem( +// id: 1, +// korName: "아반떼MD", +// model: "실린더 어셈블리-브레이크 마스터", +// trim: "엔진/미션", +// price: 3000, +// image: "https://via.placeholder.com/100", +// categoryName: "엔진/미션" +// ): 1, +// InventoryItem( +// id: 2, +// korName: "소나타DN8", +// model: "브레이크 오일 탱크", +// trim: "엔진/미션", +// price: 5000, +// image: "https://via.placeholder.com/100", +// categoryName: "엔진/미션" +// ): 2 +// ] +// +// var totalPrice: Int { +// cartItems.reduce(0) { $0 + ($1.key.price * $1.value) } +// } +// + var body: some View { +// NavigationStack { +// ZStack { +// ScrollView { +// VStack(spacing: 14) { +// ForEach(Array(cartItems.keys), id: \.id) { item in +// if let quantity = cartItems[item] { +// HStack(alignment: .center, spacing: 12) { +// AsyncImage(url: URL(string: item.image)) { image in +// image.resizable().scaledToFit() +// } placeholder: { +// Color.gray.opacity(0.2) +// } +// .frame(width: 64, height: 64) +// .cornerRadius(10) +// +// VStack(alignment: .leading, spacing: 6) { +// Text(item.korName) +// .font(.system(size: 14, weight: .bold)) +// .foregroundColor(.black) +// .lineLimit(2) +// +// Text(item.model) +// .font(.system(size: 13)) +// .foregroundColor(.gray) +// +// Text("\(item.price)원") +// .font(.system(size: 13, weight: .semibold)) +// .foregroundColor(.black) +// } +// +// Spacer() +// +// HStack(spacing: 10) { +// Button { +// if quantity > 1 { +// cartItems[item] = quantity - 1 +// } else { +// cartItems.removeValue(forKey: item) +// } +// } label: { +// Image(systemName: quantity > 1 ? "minus" : "trash") +// .font(.system(size: 14, weight: .bold)) +// .foregroundColor(quantity > 1 ? .gray : .red) +// } +// +// Text("\(quantity)") +// .font(.system(size: 15, weight: .semibold)) +// .frame(width: 24) +// +// Button { +// cartItems[item] = quantity + 1 +// } label: { +// Image(systemName: "plus") +// .font(.system(size: 14, weight: .bold)) +// .foregroundColor(.Primary) +// } +// } +// .padding(.vertical, 6) +// .padding(.horizontal, 10) +// .background(Color.white) +// .cornerRadius(10) +// .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) +// } +// .padding() +// .background(Color.white) +// .cornerRadius(14) +// .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) +// .padding(.horizontal, 20) +// } +// } +// Spacer(minLength: 100) +// } +// .padding(.top, 10) +// } +// +// // 하단 결제 버튼 +// VStack { +// Spacer() +// Button { +// // 결제 액션 +// } label: { +// Text("\(totalPrice)원 결제하기") +// .font(.system(size: 16, weight: .bold)) +// .foregroundColor(.white) +// .frame(maxWidth: .infinity) +// .frame(height: 60) +// .background(Color.Primary) +// } +// } +// } +// .background(Color.Light) +// .navigationTitle("장바구니 확인") +// .navigationBarTitleDisplayMode(.inline) +// .edgesIgnoringSafeArea(.bottom) +// } + } +} + +#Preview { +// OrderCartView() +} diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift new file mode 100644 index 0000000..67ce6b8 --- /dev/null +++ b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift @@ -0,0 +1,268 @@ +// +// OrderDetailView.swift +// StockMate +// +// Created by Admin on 10/22/25. +// + +import SwiftUI + +struct OrderDetailView: View { + let orderId: Int + @StateObject private var viewModel = OrderDetailViewModel() + + var body: some View { + ScrollView { + if viewModel.isLoading { + ProgressView() + .frame(maxWidth: .infinity, maxHeight: .infinity) + } else if let order = viewModel.order { + VStack(spacing: 16) { + + // ✅ 주문 정보 + VStack(alignment: .leading, spacing: 8) { + Text(formatDate(order.createdAt)) + .font(.system(size: 15, weight: .semibold)) + Text("주문번호: \(order.orderNumber)") + .font(.system(size: 14)) + .foregroundColor(.gray) + HStack { + Text("상태:") + .font(.system(size: 14, weight: .semibold)) + Text(statusText(order.orderStatus)) + .font(.system(size: 13, weight: .semibold)) + .padding(.horizontal, 10) + .padding(.vertical, 6) + .background(statusBdColor(order.orderStatus)) + .foregroundColor(statusColor(order.orderStatus)) + .cornerRadius(12) + } + } + .frame(maxWidth: .infinity, alignment: .leading) // ✅ 이거 추가 + .padding(.all, 20) + .background(Color.white) + .cornerRadius(16) + .shadow(color: .black.opacity(0.05), radius: 3, y: 2) +// .padding(.horizontal, 20) + + + // ✅ 배송 정보 + VStack(alignment: .leading, spacing: 6) { + Text("배송정보") + .font(.system(size: 15, weight: .semibold)) + .padding(.bottom, 4) + Text(order.userInfo?.owner ?? "-") + Text(order.userInfo?.address ?? "-") + if !(order.etc ?? "").isEmpty { + Text(order.etc ?? "") + } + if let email = order.userInfo?.email { + Text(email) + .foregroundColor(.gray) + } + } + .frame(maxWidth: .infinity, alignment: .leading) // ✅ 여기도 추가 + .padding(.all, 20) + .background(Color.white) + .cornerRadius(16) + .shadow(color: .black.opacity(0.05), radius: 3, y: 2) +// .padding(.horizontal, 20) + + // ✅ 주문 상품 + OrderSectionCard { + VStack(alignment: .leading, spacing: 6) { + Text("주문 상품 \(order.orderItems.count)개") + .font(.system(size: 15, weight: .semibold)) + + VStack(alignment: .leading, spacing: 6) { + ForEach(order.orderItems, id: \.partId) { item in + Text(item.partDetail.categoryName) + .font(.system(size: 12, weight: .semibold)) + + HStack(alignment: .top, spacing: 12) { + AsyncImage(url: URL(string: item.partDetail.image)) { img in + img.resizable().scaledToFill() + } placeholder: { + Color.gray.opacity(0.1) + } + .frame(width: 60, height: 60) + .cornerRadius(4) + .clipped() + + VStack(alignment: .leading, spacing: 4) { + Text(item.partDetail.korName) + .font(.system(size: 14, weight: .semibold)) + Text("\(item.partDetail.model) / \(item.partDetail.trim) / \(formatPrice(item.partDetail.price))원 / \(item.amount)개") + .font(.caption) + .foregroundColor(.gray) + Text("\(formatPrice(item.partDetail.price * item.amount))원") + .font(.system(size: 14, weight: .bold)) + } + } + .frame(maxWidth: .infinity, alignment: .leading) // ✅ 왼쪽 정렬 강제 + } + } + .padding(.top, 8) // 위 여백만 살짝 + } + .frame(maxWidth: .infinity, alignment: .leading) // ✅ 섹션 전체도 왼쪽으로 정렬 + } + + + // ✅ 결제 정보 + OrderSectionCard { + VStack(alignment: .leading, spacing: 10) { + Text("결제 정보") + .font(.system(size: 15, weight: .semibold)) + infoRow("결제 수단", "예치금") + infoRow("상품금액", "\(formatPrice(order.totalPrice))원") + infoRow("배송비", "\(formatPrice(3000))원") + infoRow("배송희망일", formatDateOrDash(order.requestedShippingDate)) + Divider().padding(.vertical, 4) + HStack { + Text("총 결제 금액") + .font(.headline) + Spacer() + Text("\(formatPrice(order.totalPrice + 3000))원") + .font(.headline.bold()) + .foregroundColor(.Primary) + } + } + } + + // ✅ 하단 버튼 + HStack(spacing: 12) { + // 왼쪽: 영수증 확인 + Button(action: {}) { + Text("영수증 확인") + .font(.system(size: 15, weight: .semibold)) + .foregroundColor(Color.Primary) + .frame(maxWidth: .infinity) + .frame(height: 48) + .background(Color.white) + .overlay( + RoundedRectangle(cornerRadius: 10) + .stroke(Color.Primary, lineWidth: 1.5) + ) + .cornerRadius(10) + } + + // 오른쪽: 주문 취소 + Button(action: {}) { + Text("주문 취소") + .font(.system(size: 15, weight: .semibold)) + .frame(maxWidth: .infinity) + .frame(height: 48) + .background(Color(hex: "#1D4ED8")) + .foregroundColor(.white) + .cornerRadius(10) + } + } + .padding(.top, 10) + + } + .padding(.horizontal, 20) // ✅ 전체 섹션 동일 여백 + .padding(.vertical, 16) + + } else if let error = viewModel.errorMessage { + Text(error) + .foregroundColor(.red) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + } + .background(Color.Light) + .navigationTitle("주문 내역 상세") + .navigationBarTitleDisplayMode(.inline) + .task { + await viewModel.fetchOrderDetail(orderId: orderId) + } + } + + // MARK: - Helper + func infoRow(_ left: String, _ right: String) -> some View { + HStack { + Text(left) + Spacer() + Text(right) + } + .font(.system(size: 14)) + } + + func formatDate(_ isoDate: String) -> String { + let comps = isoDate.split(separator: "T").first?.split(separator: "-") ?? [] + guard comps.count == 3 else { return isoDate } + return "\(comps[0])년 \(comps[1])월 \(comps[2])일" + } + + func statusText(_ status: String) -> String { + switch status { + case "ORDER_COMPLETED": return "주문 완료" + case "PENDING_SHIPPING": return "출고 대기" + case "REJECTED": return "출고 반려" + case "SHIPPING": return "배송 중" + case "DELIVERED": return "배송 완료" + case "RECEIVED": return "입고 완료" + case "CANCELLED": return "주문 취소" + default: return "알 수 없음" + } + } + + func statusColor(_ status: String) -> Color { + switch status { + case "ORDER_COMPLETED": return .StatusGreen + case "PENDING_SHIPPING": return .InvUse + case "REJECTED": return .Danger + case "SHIPPING": return .Transfer + case "DELIVERED": return .Secondary + case "RECEIVED": return .StatusPurple + case "CANCELLED": return .gray + default: return .gray.opacity(0.6) + } + } + + func statusBdColor(_ status: String) -> Color { + switch status { + case "ORDER_COMPLETED": return .StatusGreenBg + case "PENDING_SHIPPING": return .InvUseBg + case "REJECTED": return .DangerBg + case "SHIPPING": return .TransferBg + case "DELIVERED": return .LightBlue04 + case "RECEIVED": return .StatusPurpleBg + case "CANCELLED": return Color(hex: "#EEEEEF") + default: return .gray.opacity(0.6) + } + } +} + +// ✅ 카드 레이아웃 통일용 +struct OrderSectionCard: View { + let content: Content + init(@ViewBuilder content: () -> Content) { + self.content = content() + } + + var body: some View { + VStack(alignment: .leading, spacing: 0) { + content + .padding(20) + } + .frame(maxWidth: .infinity) + .background(Color.white) + .cornerRadius(16) + .shadow(color: .black.opacity(0.05), radius: 3, y: 2) + } +} + +func formatPrice(_ value: Int) -> String { + let formatter = NumberFormatter() + formatter.numberStyle = .decimal + return formatter.string(from: NSNumber(value: value)) ?? "\(value)" +} + +func formatDateOrDash(_ isoDate: String?) -> String { + guard let isoDate = isoDate, !isoDate.isEmpty else { + return "-" + } + let comps = isoDate.split(separator: "T").first?.split(separator: "-") ?? [] + guard comps.count == 3 else { return "-" } + return "\(comps[0])년 \(comps[1])월 \(comps[2])일" +} diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift new file mode 100644 index 0000000..8fa7ed4 --- /dev/null +++ b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift @@ -0,0 +1,175 @@ +// +// OrderInfoView.swift +// StockMate +// +// Created by Admin on 10/20/25. +// + +import SwiftUI +// +//struct OrderItem: Identifiable { +// let id = UUID() +// let name: String +// let description: String +// let price: Int +// let quantity: Int +// let imageName: String +//} + +struct OrderInfoView: View { +// @State private var orderItems: [OrderItem] = [ +// OrderItem(name: "아반떼MD", description: "실린더 어셈블리·브레이크 마스터", price: 30000, quantity: 2, imageName: "carpart"), +// OrderItem(name: "아반떼MD", description: "실린더 어셈블리·브레이크 마스터", price: 30000, quantity: 2, imageName: "carpart") +// ] +// +// @State private var selectedPayment: String = "법인 카드" +// @State private var requestMessage: String = "" +// @State private var showResult = false +// +// private var totalPrice: Int { +// orderItems.reduce(0) { $0 + $1.price * $1.quantity } +// } + + var body: some View { +// NavigationStack { +// ScrollView { +// VStack(alignment: .leading, spacing: 20) { +// +// // 배송 정보 +// Section { +// VStack(alignment: .leading, spacing: 8) { +// Text("배송 정보") +// .font(.headline) +// Text("홍길동") +// Text("서울특별시 강남구 테헤란로114길") +// Text("010-1111-2222") +// .foregroundColor(.gray) +// .font(.subheadline) +// TextField("요청사항을 입력하세요", text: $requestMessage, axis: .vertical) +// .padding(10) +// .frame(height: 70) +// .background(Color(.systemGray6)) +// .cornerRadius(10) +// } +// .padding() +// .background(Color.white) +// .cornerRadius(16) +// .shadow(color: .black.opacity(0.05), radius: 3) +// } +// +// // 주문 목록 +// Section { +// VStack(alignment: .leading, spacing: 12) { +// Text("주문 목록 (\(orderItems.count))") +// .font(.headline) +// ForEach(orderItems) { item in +// HStack(alignment: .top, spacing: 12) { +// Image(systemName: "cube.box.fill") +// .resizable() +// .frame(width: 50, height: 50) +// .foregroundColor(.gray) +// VStack(alignment: .leading, spacing: 4) { +// Text(item.name) +// .font(.subheadline) +// .bold() +// Text(item.description) +// .font(.caption) +// .foregroundColor(.gray) +// Text("\(item.price * item.quantity)원") +// .font(.subheadline) +// .bold() +// .frame(maxWidth: .infinity, alignment: .trailing) +// } +// } +// Divider() +// } +// } +// .padding() +// .background(Color.white) +// .cornerRadius(16) +// .shadow(color: .black.opacity(0.05), radius: 3) +// } +// +// // 결제 수단 +// Section { +// VStack(alignment: .leading, spacing: 8) { +// Text("결제 수단") +// .font(.headline) +// RadioButtonGroup(items: ["법인 카드(잔액 ₩1,200,000)", "본사 계좌이체"], selected: $selectedPayment) +// } +// .padding() +// .background(Color.white) +// .cornerRadius(16) +// .shadow(color: .black.opacity(0.05), radius: 3) +// } +// +// // 결제 금액 +// VStack(alignment: .leading) { +// HStack { +// Text("결제 금액") +// .font(.headline) +// Spacer() +// Text("\(totalPrice)원") +// .font(.headline) +// .foregroundColor(.blue) +// } +// } +// .padding(.horizontal) +// } +// .padding() +// } +// .background(Color(.systemGray6)) +// .navigationTitle("발주 요청") +// .toolbar { +// ToolbarItem(placement: .navigationBarLeading) { +// Button(action: { /* 뒤로가기 동작 */ }) { +// Image(systemName: "chevron.left") +// .foregroundColor(.black) +// } +// } +// } +// .safeAreaInset(edge: .bottom) { +// Button { +// // 결제 API 연동 전용 더미 로직 +// withAnimation { +// showResult = true +// } +// } label: { +// Text("결제하기") +// .font(.headline) +// .frame(maxWidth: .infinity) +// .padding() +// .foregroundColor(.white) +// .background(Color.blue) +// .cornerRadius(12) +// .padding([.horizontal, .bottom]) +// } +// } +// .navigationDestination(isPresented: $showResult) { +// OrderResultView() +// } +// } + } +} + +//struct RadioButtonGroup: View { +// let items: [String] +// @Binding var selected: String +// +// var body: some View { +// VStack(alignment: .leading, spacing: 8) { +// ForEach(items, id: \.self) { item in +// HStack { +// Image(systemName: selected == item ? "circle.inset.filled" : "circle") +// .foregroundColor(selected == item ? .blue : .gray) +// .onTapGesture { selected = item } +// Text(item) +// } +// } +// } +// } +//} + +#Preview { + OrderInfoView() +} diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift new file mode 100644 index 0000000..140cd32 --- /dev/null +++ b/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift @@ -0,0 +1,197 @@ +// +// OrderListView.swift +// StockMate +// +// Created by Admin on 10/21/25. +// + +import SwiftUI + +struct OrderListView: View { + @StateObject private var orderViewModel = OrderViewModel() + + var body: some View { +// NavigationStack { + VStack(alignment: .leading, spacing: 0) { + // 타이틀 + Text("주문 내역") + .font(.title2) + .bold() + .padding(.top, 13) + .padding(.leading, 25) + .frame(maxWidth: .infinity, alignment: .leading) + + + if orderViewModel.isLoading { + ProgressView() + .frame(maxWidth: .infinity, maxHeight: .infinity) + } else if let error = orderViewModel.errorMessage { + Text(error) + .foregroundColor(.red) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } else if orderViewModel.orders.isEmpty { + Text("주문 내역이 없습니다.") + .foregroundColor(.gray) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } else { + // 날짜별로 그룹화 (최신순) + let groupedOrders = Dictionary(grouping: orderViewModel.orders) { order in + order.createdAt.split(separator: "T").first ?? "" + } + .sorted { $0.key > $1.key } + + ScrollView { + LazyVStack(alignment: .leading, spacing: 20) { + ForEach(groupedOrders, id: \.key) { date, orders in + VStack(alignment: .leading, spacing: 12) { + Text(formatDate(String(date))) + .font(.headline) + .padding(.leading, 25) + .padding(.top) + + ForEach(orders) { order in + OrderListCardView(order: order) + } + } + } + } + .padding(.bottom) + } + .padding(.top) + } + } + .background(Color.Light) + .task { + await orderViewModel.loadOrders() + } +// } + } + + func formatDate(_ dateString: String) -> String { + // yyyy-MM-dd → yyyy / MM / dd + let comps = dateString.split(separator: "-") + guard comps.count == 3 else { return dateString } + return "\(comps[0])년 \(comps[1])월 \(comps[2])일" + } +} + +struct OrderListCardView: View { + let order: OrderResponseItem + + var body: some View { + VStack(alignment: .leading, spacing: 7) { + // 상단: 주문번호 + 상세 버튼 + HStack { + Text("주문 번호: \(order.orderNumber)") + .font(.caption) + .foregroundColor(.gray) + Spacer() + NavigationLink(destination: OrderDetailView(orderId: order.id)) { + Text("주문 상세 >") + .font(.caption) + .foregroundColor(.gray) + } + } + + Divider() + + HStack(alignment: .center, spacing: 12) { + // 대표 이미지 + AsyncImage(url: URL(string: order.orderItems.first?.partDetail.image ?? "")) { image in + image.resizable().scaledToFit() + } placeholder: { + Color.gray.opacity(0.2) + } + .frame(width: 64, height: 64) + .cornerRadius(10) + + // 제품명 + 개수 + VStack(alignment: .leading, spacing: 4) { + if let first = order.orderItems.first { + Text(first.partDetail.korName) + .font(.system(size: 16, weight: .semibold)) + .foregroundColor(.black) + .lineLimit(1) + + if order.orderItems.count > 1 { + Text("외 \(order.orderItems.count - 1)개") + .font(.caption) + .foregroundColor(.gray) + } + } + } + + Spacer() + + // 상태 뱃지 + Text(statusText(order.orderStatus)) + .font(.system(size: 13, weight: .semibold)) + .padding(.horizontal, 12) + .padding(.vertical, 7) + .background(statusBdColor(order.orderStatus)) + .foregroundColor(statusColor(order.orderStatus)) + .cornerRadius(15) + } + + // 주문취소 버튼 (필요 시) + if order.orderStatus == "ORDER_COMPLETED" { + Button(action: { + // 주문취소 처리 + }) { + Text("주문 취소") + .font(.system(size: 13, weight: .semibold)) + .foregroundColor(.white) + .frame(maxWidth: .infinity) + .frame(height: 40) + .background(Color.Primary) + .cornerRadius(6) + } + .padding(.top, 6) + } + } + .padding() + .background(Color.white) + .cornerRadius(12) + .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 2) + .padding(.horizontal) + } + + func statusText(_ status: String) -> String { + switch status { + case "ORDER_COMPLETED": return "주문 완료" + case "PENDING_SHIPPING": return "출고 대기" + case "REJECTED": return "출고 반려" + case "SHIPPING": return "배송 중" + case "DELIVERED": return "배송 완료" + case "RECEIVED": return "입고 완료" + case "CANCELLED": return "주문 취소" + default: return "알 수 없음" + } + } + + func statusColor(_ status: String) -> Color { + switch status { + case "ORDER_COMPLETED": return .StatusGreen + case "PENDING_SHIPPING": return .InvUse + case "REJECTED": return .Danger + case "SHIPPING": return .Transfer + case "DELIVERED": return .Secondary + case "RECEIVED": return .StatusPurple + case "CANCELLED": return .gray + default: return .gray.opacity(0.6) + } + } + + func statusBdColor(_ status: String) -> Color { + switch status { + case "ORDER_COMPLETED": return .StatusGreenBg + case "PENDING_SHIPPING": return .InvUseBg + case "REJECTED": return .DangerBg + case "SHIPPING": return .TransferBg + case "DELIVERED": return .LightBlue04 + case "RECEIVED": return .StatusPurpleBg + case "CANCELLED": return Color(hex: "#EEEEEF") + default: return .gray.opacity(0.6) + } + } +} diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderRequestView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderRequestView.swift new file mode 100644 index 0000000..6f08d90 --- /dev/null +++ b/StockMate/StockMate/app/feature/orders/ui/OrderRequestView.swift @@ -0,0 +1,169 @@ +// +// OrderRequestView.swift +// StockMate +// +// Created by Admin on 10/20/25. +// + +import SwiftUI + +struct OrderRequestView: View { +// @StateObject private var inventoryViewModel = InventoryViewModel() +// @State private var showScrollToTopButton = false +// @Namespace private var topID +// +// @State private var cartItems: [InventoryItem: Int] = [:] // [품목: 수량] +// +// var totalPrice: Int { +// cartItems.reduce(0) { $0 + ($1.key.price * $1.value) } +// } + + var body: some View { +// NavigationStack { +// ScrollViewReader { proxy in +// ZStack { +// ScrollView { +// VStack(spacing: 0) { +// GeometryReader { geo in +// Color.clear +// .onChange(of: geo.frame(in: .global).minY) { newValue in +// withAnimation(.easeInOut(duration: 0.25)) { +// showScrollToTopButton = newValue < -150 +// } +// } +// } +// .frame(height: 0) +// .id(topID) +// +// // 상단 타이틀 +// HStack { +// Text("발주 요청") +// .font(.title3) +// .bold() +// Spacer() +// Image(systemName: "cart") +// .font(.system(size: 20)) +// .foregroundColor(.black) +// } +// .padding(.horizontal, 25) +// .padding(.top, 10) +// +// // 검색창 +// HStack { +// Image(systemName: "magnifyingglass") +// .foregroundColor(.gray) +// TextField("부품을 검색하세요.", text: .constant("")) +// } +// .padding(.horizontal) +// .padding(.vertical, 12) +// .background( +// RoundedRectangle(cornerRadius: 14) +// .stroke(Color.gray.opacity(0.3), lineWidth: 1) +// ) +// .padding(.horizontal, 20) +// .padding(.top, 10) +// +// // 부족 재고 리스트 +// VStack(alignment: .leading, spacing: 12) { +// Text("부족 재고") +// .font(.system(size: 18, weight: .semibold)) +// .foregroundColor(.black) +// .padding(.horizontal, 25) +// .padding(.top, 15) +// +// LazyVStack(spacing: 14) { +// ForEach(inventoryViewModel.underLimitItems) { item in +// OrderRequestCardView( +// item: item, +// quantity: cartItems[item] ?? 0, +// onAdd: { +// cartItems[item, default: 0] += 1 +// }, +// onRemove: { +// if let current = cartItems[item], current > 0 { +// cartItems[item] = current - 1 +// } +// }, +// onCartAdd: { +// cartItems[item, default: 0] += 1 +// } +// ) +// .padding(.horizontal, 20) +// .onAppear { +// if item.id == inventoryViewModel.underLimitItems.last?.id { +// Task { +// await inventoryViewModel.loadUnderLimitList() +// } +// } +// } +// } +// +// if inventoryViewModel.isLoading { +// ProgressView().padding(.vertical, 20) +// } +// } +// .padding(.bottom, 100) +// } +// } +// } +// +// // ✅ 하단 장바구니 버튼 +// VStack { +// Spacer() +// NavigationLink(destination: OrderCartView()) { +// HStack { +// Circle() +// .fill(Color.white) +// .frame(width: 20, height: 20) +// .overlay( +// Text("\(cartItems.count)") +// .font(.system(size: 11, weight: .bold)) +// .foregroundColor(.Primary) +// ) +// +// Text("장바구니 보기") +// .font(.system(size: 16, weight: .bold)) +// .foregroundColor(.white) +// +// Spacer() +// +// Text("\(totalPrice)원") +// .font(.system(size: 16, weight: .semibold)) +// .foregroundColor(.white) +// } +// .padding(.horizontal, 30) +// .frame(height: 60) +// .background(Color.Primary) +// // 위쪽 두 모서리만 둥글게 +// .clipShape(RoundedCorner(radius: 16, corners: [.topLeft, .topRight])) +// } +// } +// } +// .background(Color.Light) +// .edgesIgnoringSafeArea(.bottom) // 탭에 딱 맞닿게 +// .task { +// await inventoryViewModel.loadUnderLimitList(reset: true) +// } +// } +// } + } +} + +// ✅ 특정 코너만 둥글게 처리할 수 있게 하는 Shape +struct RoundedCorner: Shape { + var radius: CGFloat = 16 + var corners: UIRectCorner = .allCorners + + func path(in rect: CGRect) -> Path { + let path = UIBezierPath( + roundedRect: rect, + byRoundingCorners: corners, + cornerRadii: CGSize(width: radius, height: radius) + ) + return Path(path.cgPath) + } +} + +#Preview { + OrderRequestView() +} diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderResultView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderResultView.swift new file mode 100644 index 0000000..39fc6bf --- /dev/null +++ b/StockMate/StockMate/app/feature/orders/ui/OrderResultView.swift @@ -0,0 +1,77 @@ +// +// OrderResultView.swift +// StockMate +// +// Created by Admin on 10/20/25. +// + +import SwiftUI + +struct OrderResultView: View { + @Environment(\.dismiss) private var dismiss + + var body: some View { + VStack(spacing: 24) { + + Spacer() + + // ✅ 결제 완료 아이콘 + Image(systemName: "checkmark.circle.fill") + .resizable() + .frame(width: 80, height: 80) + .foregroundColor(.blue) + .padding(.bottom, 8) + + // ✅ 완료 문구 + Text("결제가 완료되었습니다") + .font(.title3) + .bold() + Text("주문 내역은 발주 탭에서 확인할 수 있습니다.") + .font(.subheadline) + .foregroundColor(.gray) + + Spacer() + + // ✅ 하단 버튼 + VStack(spacing: 12) { + Button { + dismiss() // 이전 화면으로 돌아가기 + } label: { + Text("확인") + .font(.headline) + .frame(maxWidth: .infinity) + .padding() + .foregroundColor(.white) + .background(Color.blue) + .cornerRadius(12) + } + + Button { + // 홈으로 이동 (추후 NavigationStack 연결 시) + } label: { + Text("홈으로 이동") + .font(.headline) + .frame(maxWidth: .infinity) + .padding() + .foregroundColor(.blue) + .background(Color.white) + .cornerRadius(12) + .overlay( + RoundedRectangle(cornerRadius: 12) + .stroke(Color.blue, lineWidth: 1) + ) + } + } + .padding(.horizontal) + + Spacer() + } + .padding() + .navigationBarBackButtonHidden(true) + .navigationTitle("결제 완료") + } +} + +#Preview { + OrderResultView() +} diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderView.swift index 95b7f90..5f43ab8 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderView.swift @@ -10,127 +10,37 @@ import SwiftUI struct OrderView: View { var body: some View { VStack(spacing: 0) { - // 상단 타이틀 - Text("발주 목록") - .font(.headline) - .padding(.top, 16) + // 타이틀 + Text("발주 요청") + .font(.title2) + .bold() + .padding(.top, 13) + .padding(.leading, 25) + .frame(maxWidth: .infinity, alignment: .leading) - // 필터 탭 버튼 -// HStack(spacing: 10) { -// ForEach(["Product", "Category", "Payment", "status"], id: \.self) { title in -// Text(title) -// .font(.system(size: 14, weight: .medium)) -// .foregroundColor(.black.opacity(0.8)) -// .padding(.horizontal, 14) -// .padding(.vertical, 8) -// .background(Color.white) -// .cornerRadius(10) -// .shadow(color: Color.black.opacity(0.05), radius: 1, x: 0, y: 1) -// } -// } -// .padding(.top, 12) - - // 주문 리스트 - ScrollView { - VStack(alignment: .leading, spacing: 16) { - OrderSection(date: "2025/09/30", orders: [ - OrderItem(image: "bolt.fill", name: "현대 아이오닉5", detail: "브레이크 등/ 5개", price: "₩300,000", status: "배송중", color: .green), - OrderItem(image: "bolt.fill", name: "현대 아이오닉5", detail: "브레이크 등/ 5개", price: "₩300,000", status: "승인완료", color: .green), - OrderItem(image: "bolt.fill", name: "현대 아이오닉5", detail: "브레이크 등/ 5개", price: "₩300,000", status: "승인완료", color: .green), - OrderItem(image: "bolt.fill", name: "현대 아이오닉5", detail: "브레이크 등/ 5개", price: "₩300,000", status: "승인 대기", color: .orange) - ]) - - OrderSection(date: "2025/09/29", orders: [ - OrderItem(image: "bolt.fill", name: "현대 아이오닉5", detail: "브레이크 등/ 5개", price: "₩300,000", status: "승인 거절", color: .red), - OrderItem(image: "bolt.fill", name: "현대 아이오닉5", detail: "브레이크 등/ 5개", price: "₩300,000", status: "입고완료", color: .blue) - ]) - } - .padding(.horizontal) - .padding(.vertical, 10) - } - - // 하단 고정 버튼 - Button(action: { - // 발주 요청 액션 - }) { - Text("발주 요청") - .font(.system(size: 16, weight: .semibold)) - .foregroundColor(.white) - .frame(maxWidth: .infinity) - .padding() - .background(Color(#colorLiteral(red: 0.215, green: 0.318, blue: 0.686, alpha: 1))) // #374EAF - .cornerRadius(12) - .padding(.horizontal) - .padding(.bottom, 12) - } - } - .background(Color(.systemGray6)) - .ignoresSafeArea(edges: .bottom) - } -} + // 🔍 검색창 + HStack { + Image(systemName: "magnifyingglass") + .foregroundColor(.gray) + + Text("부품을 검색하세요.") +// TextField("부품을 검색하세요.", text: $searchText) +// .textFieldStyle(PlainTextFieldStyle()) -struct OrderSection: View { - var date: String - var orders: [OrderItem] - - var body: some View { - VStack(alignment: .leading, spacing: 10) { - Text(date) - .font(.system(size: 13, weight: .medium)) - .foregroundColor(.gray) - - ForEach(orders, id: \.id) { order in - HStack { - Image(systemName: order.image) - .resizable() - .scaledToFit() - .frame(width: 40, height: 40) - .padding(6) - .background(Color.white) - .cornerRadius(10) - .shadow(color: .black.opacity(0.05), radius: 2) - - VStack(alignment: .leading, spacing: 2) { - Text(order.name) - .font(.system(size: 15, weight: .semibold)) - Text(order.detail) - .font(.system(size: 13)) - .foregroundColor(.gray) - } - Spacer() - - VStack(alignment: .trailing, spacing: 6) { - Text(order.price) - .font(.system(size: 15, weight: .medium)) - .foregroundColor(.purple) - Text(order.status) - .font(.system(size: 12, weight: .semibold)) - .foregroundColor(order.color) - .padding(.horizontal, 10) - .padding(.vertical, 4) - .background(order.color.opacity(0.1)) - .cornerRadius(10) - } - } - .padding() - .background(Color.white) - .cornerRadius(14) - .shadow(color: Color.black.opacity(0.03), radius: 2) } + .padding() + .background(Color(.white)) + .cornerRadius(9999) + .overlay( + RoundedRectangle(cornerRadius: 9999) + .stroke(Color.gray.opacity(0.4), lineWidth: 1) + ) + .padding(.horizontal) + .padding(.vertical) } } } -struct OrderItem: Identifiable { - let id = UUID() - var image: String - var name: String - var detail: String - var price: String - var status: String - var color: Color -} - #Preview { OrderView() } diff --git a/StockMate/StockMate/app/feature/orders/viewmodel/OrderDetailViewModel.swift b/StockMate/StockMate/app/feature/orders/viewmodel/OrderDetailViewModel.swift new file mode 100644 index 0000000..2903e8c --- /dev/null +++ b/StockMate/StockMate/app/feature/orders/viewmodel/OrderDetailViewModel.swift @@ -0,0 +1,31 @@ +// +// OrderDetailViewModel.swift +// StockMate +// +// Created by Admin on 10/22/25. +// + +import Foundation + +@MainActor +final class OrderDetailViewModel: ObservableObject { + @Published var order: OrderResponseItem? + @Published var isLoading = false + @Published var errorMessage: String? + + private let repository = OrderRepositoryImpl() + + func fetchOrderDetail(orderId: Int) async { + isLoading = true + defer { isLoading = false } + + let result = await repository.fetchOrderDetail(orderId: orderId) + + switch result { + case .success(let detail): + self.order = detail + case .failure(let error): + self.errorMessage = error.localizedDescription + } + } +} diff --git a/StockMate/StockMate/app/feature/orders/viewmodel/OrderViewModel.swift b/StockMate/StockMate/app/feature/orders/viewmodel/OrderViewModel.swift new file mode 100644 index 0000000..fdbd57f --- /dev/null +++ b/StockMate/StockMate/app/feature/orders/viewmodel/OrderViewModel.swift @@ -0,0 +1,47 @@ +// +// OrderViewModel.swift +// StockMate +// +// Created by Admin on 10/21/25. +// + +import Foundation + +@MainActor +final class OrderViewModel: ObservableObject { + @Published var orders: [OrderResponseItem] = [] + @Published var isLoading = false + @Published var errorMessage: String? + + private let repository: OrderRepositoryProtocol + + init(repository: OrderRepositoryProtocol = OrderRepositoryImpl()) { + self.repository = repository + } + + func loadOrders( + status: String? = nil, + startDate: String? = nil, + endDate: String? = nil, + page: Int = 0, + size: Int = 20 + ) async { + isLoading = true + defer { isLoading = false } + + let result = await repository.fetchMyOrders( + status: status, + startDate: startDate, + endDate: endDate, + page: page, + size: size + ) + + switch result { + case .success(let pageData): + orders = pageData.content + case .failure(let error): + errorMessage = error.message + } + } +} diff --git a/StockMate/StockMate/app/feature/user/ui/ProfileView.swift b/StockMate/StockMate/app/feature/user/ui/ProfileView.swift new file mode 100644 index 0000000..92fdf6a --- /dev/null +++ b/StockMate/StockMate/app/feature/user/ui/ProfileView.swift @@ -0,0 +1,152 @@ +// +// ProfileView().swift +// StockMate +// +// Created by Admin on 10/21/25. +// + +import SwiftUI + +struct ProfileView: View { + @StateObject private var userViewModel = UserViewModel() + + var body: some View { +// NavigationStack { + VStack(alignment: .leading, spacing: 24) { + + // MARK: - Profile Header + HStack(spacing: 16) { + ProfileCircleView(name: userViewModel.userInfo?.owner ?? "사용자", size: 50) + + VStack(alignment: .leading, spacing: 4) { + HStack { + Image("location") + .foregroundColor(.gray) + Text(userViewModel.userInfo?.storeName ?? "가게명 없음") + .foregroundColor(.gray) + .font(.subheadline) + } + Text(userViewModel.userInfo?.owner ?? "이름 없음") + .font(.title3.bold()) + .foregroundColor(Color(hex: "#2B3A1A")) + } + + Spacer() + } + .padding(.horizontal) + .padding(.top, 32) + + // MARK: - General Section + VStack(alignment: .leading, spacing: 12) { + Text("General") + .font(.system(size: 16, weight: .semibold)) + .padding(.leading) + + VStack(spacing: 10) { + SettingRow(icon: "person.crop.circle", title: "Edit Profile") + SettingRow(icon: "lock.circle", title: "Change Password") + SettingRow(icon: "bell", title: "Notifications") + SettingRow(icon: "location.circle", title: "배송 현황") + + SettingNavigationRow(icon: "bag", title: "주문 내역", destination: OrderListView()) + } + .padding(3) + .background(Color.Light) + .cornerRadius(12) + .padding(.horizontal) + } + + // MARK: - Preferences Section + VStack(alignment: .leading, spacing: 12) { + Text("Preferences") + .font(.system(size: 16, weight: .semibold)) + .padding(.leading) + + VStack(spacing: 10) { + SettingRow(icon: "shield", title: "Legal and Policies") + SettingRow(icon: "questionmark.circle", title: "Help & Support") + SettingRow(icon: "arrow.right.circle", title: "Logout", iconColor: .red, textColor: .red) + } + .padding(3) + .background(Color.Light) + .cornerRadius(12) + .padding(.horizontal) + } + + Spacer() + } + .background(Color.Light) + .navigationBarTitleDisplayMode(.inline) + .onAppear { + Task { await userViewModel.loadUserInfo() } + } +// } + } + } + + // MARK: - SettingRow (동일) +struct SettingRow: View { + var icon: String + var title: String + var iconColor: Color = .black + var textColor: Color = .primary + + var body: some View { + HStack { + Image(systemName: icon) + .font(.system(size: 18)) + .foregroundColor(iconColor) + .frame(width: 24) + + Text(title) + .font(.system(size: 16)) + .foregroundColor(textColor) + + Spacer() + Image(systemName: "chevron.right") + .font(.system(size: 14, weight: .medium)) + .foregroundColor(.gray) + } + .padding() + .background(RoundedRectangle(cornerRadius: 10).fill(Color.Light)) + .overlay(RoundedRectangle(cornerRadius: 10).stroke(Color(.systemGray4), lineWidth: 1)) + .shadow(color: .black.opacity(0.05), radius: 1, x: 0, y: 1) + } +} + +struct SettingNavigationRow: View { + var icon: String + var title: String + var iconColor: Color = .black + var textColor: Color = .primary + var destination: Destination + + var body: some View { + NavigationLink(destination: destination) { + HStack { + Image(systemName: icon) + .font(.system(size: 18)) + .foregroundColor(iconColor) + .frame(width: 24) + + Text(title) + .font(.system(size: 16)) + .foregroundColor(textColor) + + Spacer() + Image(systemName: "chevron.right") + .font(.system(size: 14, weight: .medium)) + .foregroundColor(.gray) + } + .padding() + .background(RoundedRectangle(cornerRadius: 10).fill(Color.Light)) + .overlay(RoundedRectangle(cornerRadius: 10).stroke(Color(.systemGray4), lineWidth: 1)) + .shadow(color: .black.opacity(0.05), radius: 1, x: 0, y: 1) + } + } +} + + + #Preview { + ProfileView() + } From deabaaca0b9f3db4ab1ee7d5a5c954e8f3e4dcf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Wed, 22 Oct 2025 17:31:25 +0900 Subject: [PATCH 22/85] =?UTF-8?q?[FEAT]=20=EC=9E=AC=EA=B3=A0=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=ED=83=AD=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/feature/inventory/ui/InventoryView.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift index b0d3132..35545c0 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift @@ -89,7 +89,7 @@ struct InventoryView: View { } label: { ZStack { Circle() - .fill(Color.Secondary) // 배경색 + .fill(Color.Primary) // 배경색 .frame(width: 50, height: 50) Image(systemName: "arrow.up") .font( @@ -146,7 +146,7 @@ struct GridMenuView: View { ZStack { // 타원 배경 Rectangle() - .fill(item.1 ? Color.white.opacity(0.2) : Color.Secondary) + .fill(item.1 ? Color.white.opacity(0.2) : Color.Primary) .frame(width: 35, height: 26) .cornerRadius(80) @@ -162,7 +162,7 @@ struct GridMenuView: View { Text(item.0) .font(.system(size: 15, weight: .semibold)) - .foregroundColor(item.1 ? .white : Color.Secondary) + .foregroundColor(item.1 ? .white : Color.Primary) } .padding(.horizontal, 20) .padding(.vertical, 20) @@ -170,7 +170,7 @@ struct GridMenuView: View { .frame(maxWidth: .infinity) .background( RoundedRectangle(cornerRadius: 24) - .fill(item.1 ? Color.Secondary : Color.white) + .fill(item.1 ? Color.Primary : Color.white) // 카드 그림자 (Figma 스펙: y=4, blur=4, opacity=25%, black) .shadow(color: .black.opacity(0.35), radius: 2, x: 0, y: 4) ) From 7e2cc335c279751ff751443349a7c834f10cc9e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Thu, 23 Oct 2025 23:16:44 +0900 Subject: [PATCH 23/85] =?UTF-8?q?[FEAT]=20=ED=99=88=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EC=9E=AC=EA=B3=A0=20=EB=B6=80=EC=A1=B1=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?api=20=EC=97=B0=EA=B2=B0=20=EB=B0=8F=20=EB=94=94=EC=9E=90?= =?UTF-8?q?=EC=9D=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- StockMate/StockMate/ContentView.swift | 9 ++ .../app/feature/auth/ui/HomeView.swift | 116 ++++++++++++------ .../feature/inventory/data/InventoryApi.swift | 12 ++ .../data/InventoryRepositoryImpl.swift | 7 +- .../domain/InventoryRepositoryProtocol.swift | 2 + .../inventory/ui/IncomingScanView.swift | 11 -- .../feature/inventory/ui/LackListView.swift | 103 ++++++++++++++++ .../viewmodel/InventoryViewModel.swift | 29 +++++ .../app/navigation/MainTabView.swift | 5 +- .../chair.imageset/Contents.json | 21 ++++ .../Assets.xcassets/chair.imageset/chair.svg | 3 + .../cog.imageset/Contents.json | 21 ++++ .../Assets.xcassets/cog.imageset/cog.svg | 4 + .../lightbulb.imageset/Contents.json | 21 ++++ .../lightbulb.imageset/lightbulb.svg | 3 + .../package.imageset/Contents.json | 21 ++++ .../package.imageset/package.svg | 10 ++ .../spanner.imageset/Contents.json | 21 ++++ .../spanner.imageset/spanner.svg | 10 ++ StockMate/StockMate/resources/Color.swift | 21 +++- 20 files changed, 397 insertions(+), 53 deletions(-) create mode 100644 StockMate/StockMate/app/feature/inventory/ui/LackListView.swift create mode 100644 StockMate/StockMate/resources/Assets.xcassets/chair.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/chair.imageset/chair.svg create mode 100644 StockMate/StockMate/resources/Assets.xcassets/cog.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/cog.imageset/cog.svg create mode 100644 StockMate/StockMate/resources/Assets.xcassets/lightbulb.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/lightbulb.imageset/lightbulb.svg create mode 100644 StockMate/StockMate/resources/Assets.xcassets/package.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/package.imageset/package.svg create mode 100644 StockMate/StockMate/resources/Assets.xcassets/spanner.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/spanner.imageset/spanner.svg diff --git a/StockMate/StockMate/ContentView.swift b/StockMate/StockMate/ContentView.swift index 882bebe..d576e34 100644 --- a/StockMate/StockMate/ContentView.swift +++ b/StockMate/StockMate/ContentView.swift @@ -16,6 +16,15 @@ struct ContentView: View { Text("임시 화면") } .padding() + HStack(spacing: 20) { + Image(systemName: "gearshape") + .font(.system(size: 40)) + .foregroundColor(.blue) + + Image(systemName: "lightbulb") + .font(.system(size: 40)) + .foregroundColor(.cyan) + } } } diff --git a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift index 8a8d395..7ba28a4 100644 --- a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift @@ -11,10 +11,11 @@ import SwiftUI struct HomeView: View { @EnvironmentObject var authViewModel: AuthViewModel @StateObject private var userViewModel = UserViewModel() + @StateObject private var inventoryViewModel = InventoryViewModel() var body: some View { ScrollView { - VStack(alignment: .leading, spacing: 24) { + VStack(alignment: .leading, spacing: 15) { // 상단 프로필 HStack(spacing: 16) { ProfileCircleView(name: userViewModel.userInfo?.owner ?? "사용자", size: 50) @@ -37,55 +38,41 @@ struct HomeView: View { Image("notification") .font(.system(size: 20)) .foregroundColor(.gray) + .padding(.trailing, 5) } .padding(.horizontal) - - // 검색창 + // 🔍 검색창 HStack { Image(systemName: "magnifyingglass") - .foregroundColor(Color(hex: "#4B5565")) - Text("Search for Accessories") + .foregroundColor(.gray) + + Text("부품을 검색하세요.") .foregroundColor(.gray) Spacer() } .padding() - .background( - RoundedRectangle(cornerRadius: 120) - .fill(Color(hex: "#EEF2F6")) - ) + .background(Color(.white)) + .cornerRadius(9999) .overlay( - RoundedRectangle(cornerRadius: 120) - .stroke(Color(hex: "#9AA4B2"), lineWidth: 1) + RoundedRectangle(cornerRadius: 9999) + .stroke(Color.gray.opacity(0.4), lineWidth: 1) ) .padding(.horizontal) - - // 상태 요약 카드 - HStack(spacing: 13) { - StatusItem(title: "입고", count: 77, color: .IncomingBg, icon: "incoming") - StatusItem(title: "부족", count: 33, color: .DangerBg, icon: "lack") - StatusItem(title: "승인대기", count: 27, color: .WarningBg, icon: "wait") - StatusItem(title: "반품/불량", count: 4, color: .DefectBg, icon: "defect") - StatusItem(title: "이동요청", count: 33, color: .TransferBg, icon: "transfer") - } - .padding() - .background(Color.white) - .cornerRadius(16) - .padding(.horizontal) - + lackStockSection // 도넛 차트 섹션 VStack(alignment: .leading, spacing: 18) { - Text("제목") + Text("지난달 카테고리 별 지출") .font(.headline) - .padding() + .padding(4) HStack{ DonutChartView() - .frame(height: 170) + .frame(height: 130) .padding() .background(Color.white) .cornerRadius(16) @@ -103,13 +90,12 @@ struct HomeView: View { // 막대그래프 섹션 VStack(alignment: .leading, spacing: 8) { - Text("제목") + Text("지출 현황") .font(.headline) - .padding(.top) - .padding(.leading) + .padding(4) BarChartView() - .frame(height: 200) + .frame(height: 150) // .padding() .background(Color.white) .shadow(color: .gray.opacity(0.1), radius: 4) @@ -122,11 +108,15 @@ struct HomeView: View { .padding(.vertical) } .background(Color.Light) + .task { + // 카테고리 데이터 로드 + await inventoryViewModel.loadLackCountByCategory() + } .onAppear { Task { await userViewModel.loadUserInfo() } } // 화면 디자인 시 잠시 주석처리 -// // ✅ 세션 만료 시 자동으로 로그인 뷰로 이동 + // ✅ 세션 만료 시 자동으로 로그인 뷰로 이동 // .onChange(of: userViewModel.shouldGoToLogin) { shouldGo in // if shouldGo { // print("세션 만료됨 → 로그인 화면으로 이동") @@ -134,10 +124,60 @@ struct HomeView: View { // } // } } + + private var lackStockSection: some View { + VStack(alignment: .leading, spacing: 8) { + Text("재고 부족 조회") + .font(.headline) + HStack(spacing: 13) { + ForEach(inventoryViewModel.lackCounts, id: \.id) { item in + NavigationLink { + LackListView(selectedCategory: item.categoryName) + } label: { + StatusItem( + title: item.categoryName, + count: item.count, + color: colorForCategory(item.categoryName), + icon: iconForCategory(item.categoryName) + ) + } + } + } + .padding(.vertical, 4) + } + .frame(maxWidth: .infinity, minHeight: 70) + .padding() + .background(Color.white) + .cornerRadius(16) + .padding(.horizontal) + } + + } +private func colorForCategory(_ category: String) -> Color { + switch category { + case "전기/램프": return .Hstatus1Bg + case "엔진/미션": return .Hstatus2Bg + case "하체/바디": return .Hstatus3Bg + case "내장/외장": return .Hstatus4Bg + case "기타소모품": return .Hstatus5Bg + default: return .gray.opacity(0.3) + } +} + +private func iconForCategory(_ name: String) -> String { + switch name { + case "전기/램프": return "lightbulb" + case "엔진/미션": return "cog" + case "하체/바디": return "spanner" + case "내장/외장": return "chair" + case "기타소모품": return "package" + default: return "questionmark" + } +} // MARK: - 상태 아이템 컴포넌트 struct StatusItem: View { @@ -152,13 +192,13 @@ struct StatusItem: View { .resizable() .scaledToFit() .frame(width: 25, height: 25) - .foregroundColor(.white) .padding(12) .background(color) - .clipShape(RoundedRectangle(cornerRadius: 10)) + .clipShape(RoundedRectangle(cornerRadius: 100)) Text(title) - .font(.system(size: 15, weight: .semibold)) + .font(.system(size: 13, weight: .semibold)) + .foregroundColor(.black) .padding(.top, 5) .lineLimit(1) @@ -196,8 +236,8 @@ struct DonutChartView: View { struct BarChartView: View { let values: [CGFloat] = [0.89, 0.5, 0.9, 0.3, 0.7] let colors: [Color] = [ - Color(hex: "6BE6D3"), .black, Color(hex: "7DBBFF"), Color(hex: "B899EB"), Color(hex: "71DD8C")] - // 6BE6D3 + .LightBlue04, .Primary, .LightBlue04, .LightBlue04, .LightBlue04 + ] var body: some View { HStack(alignment: .bottom, spacing: 33) { ForEach(0.. DataRequest { + let url = ApiClient.baseURL + "api/v1/store/lack-count" + return ApiClient.shared.request(url, method: .get) + } } diff --git a/StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift b/StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift index 914e732..93a8885 100644 --- a/StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift +++ b/StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift @@ -44,6 +44,11 @@ final class InventoryRepositoryImpl: InventoryRepositoryProtocol { let dataReq = InventoryApi.findByName(name: name, page: page, size: size) return await safeApi(dataReq, decodeTo: ApiResponse.self) } - + // 카테고리별 부족 재고 개수 조회 + func getLackCountByCategory() async -> AppResult> { + let dataReq = InventoryApi.getLackCountByCategory() + return await safeApi(dataReq, decodeTo: ApiResponse<[LackCountItem]>.self) + } + } diff --git a/StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift b/StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift index 3d45edd..de669ec 100644 --- a/StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift +++ b/StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift @@ -29,4 +29,6 @@ protocol InventoryRepositoryProtocol { size: Int ) async -> AppResult> + func getLackCountByCategory() async -> AppResult> + } diff --git a/StockMate/StockMate/app/feature/inventory/ui/IncomingScanView.swift b/StockMate/StockMate/app/feature/inventory/ui/IncomingScanView.swift index 706a507..83d0415 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/IncomingScanView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/IncomingScanView.swift @@ -24,17 +24,6 @@ struct IncomingScanView: View { RoundedRectangle(cornerRadius: 8) .stroke(Color.blue, lineWidth: 3) .frame(width: 220, height: 220) - .overlay( - // 모서리 강조 - ZStack { - Color.clear - .overlay( - Rectangle() - .trim(from: 0, to: 0.25) - .stroke(Color.blue, style: StrokeStyle(lineWidth: 5, lineCap: .round)) - ) - } - ) } .padding(.top, 20) diff --git a/StockMate/StockMate/app/feature/inventory/ui/LackListView.swift b/StockMate/StockMate/app/feature/inventory/ui/LackListView.swift new file mode 100644 index 0000000..b1af997 --- /dev/null +++ b/StockMate/StockMate/app/feature/inventory/ui/LackListView.swift @@ -0,0 +1,103 @@ +// +// LackListView.swift +// StockMate +// +// Created by Admin on 10/23/25. +// + +import SwiftUI + +struct LackListView: View { + @StateObject private var inventoryViewModel = InventoryViewModel() + @State private var isFirstAppear = true + + // 전달받는 초기 카테고리 + @State var selectedCategory: String + private let categories = ["전기/램프", "엔진/미션", "하체/바디", "내장/외장", "기타소모품"] + + var body: some View { + VStack(spacing: 0) { + // 상단 카테고리 탭 + HStack(spacing: 8) { + ForEach(categories, id: \.self) { category in + CategoryButton( + title: category, + isSelected: selectedCategory == category + ) { + Task { + selectedCategory = category + inventoryViewModel.selectedCategories = [category] + await inventoryViewModel.loadUnderLimitList(reset: true) + } + } + } + } + .padding(.horizontal) + .padding(.vertical, 10) + + // 리스트 + ScrollView { + LazyVStack(spacing: 12) { + ForEach(inventoryViewModel.underLimitItems) { item in + InventoryCardView(item: item) + .padding(.horizontal) + .onAppear { + // 무한 스크롤 트리거 + if item.id == inventoryViewModel.underLimitItems.last?.id { + Task { + await inventoryViewModel.loadUnderLimitList() + } + } + } + } + + if inventoryViewModel.isLoading { + ProgressView() + .padding(.vertical, 20) + } + } + .padding(.top, 8) + .padding(.bottom, 80) + } + } + .background(Color.Light) + .navigationTitle("부족 재고") + .navigationBarTitleDisplayMode(.inline) + .task { + if isFirstAppear { + isFirstAppear = false + inventoryViewModel.selectedCategories = [selectedCategory] + await inventoryViewModel.loadUnderLimitList(reset: true) + } + } + } +} + +#Preview { + LackListView(selectedCategory: "엔진/미션") + .environmentObject(InventoryViewModel()) +} + +struct CategoryButton: View { + let title: String + let isSelected: Bool + let action: () -> Void + + var body: some View { + Button(action: action) { + Text(title) + .font(.system(size: 11, weight: .regular)) + .foregroundColor(isSelected ? .Primary : .black) + .padding(.vertical, 8) + .padding(.horizontal, 12) + .background( + RoundedRectangle(cornerRadius: 16) + .fill(Color.Light) + ) + .overlay( + RoundedRectangle(cornerRadius: 16) + .stroke(isSelected ? Color.Primary : Color.GrayStroke, lineWidth: 1) + ) + } + } +} diff --git a/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift b/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift index 6467147..a9e1329 100644 --- a/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift +++ b/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift @@ -38,6 +38,10 @@ final class InventoryViewModel: ObservableObject { // ====== 공통 상태 ====== @Published var isLoading = false + // ===== 카테고리별 부족 재고 개수 ===== + @Published var lackCounts: [LackCountItem] = [] + + private let repo: InventoryRepositoryProtocol init(repo: InventoryRepositoryProtocol = InventoryRepositoryImpl()) { @@ -270,5 +274,30 @@ final class InventoryViewModel: ObservableObject { await loadInventoryList() } } + + + // MARK: - 카테고리별 부족 재고 개수 로드 + func loadLackCountByCategory() async { + guard !isLoading else { return } + isLoading = true + + let result = await repo.getLackCountByCategory() + + switch result { + case .success(let apiResp): + if let data = apiResp.data { + lackCounts = data + } else { + message = apiResp.message + } + case .failure(let err): + message = err.message + if err.code == 401 || err.code == 403 { + shouldGoToLogin = true + } + } + + isLoading = false + } } diff --git a/StockMate/StockMate/app/navigation/MainTabView.swift b/StockMate/StockMate/app/navigation/MainTabView.swift index 0b42bed..a04a3c5 100644 --- a/StockMate/StockMate/app/navigation/MainTabView.swift +++ b/StockMate/StockMate/app/navigation/MainTabView.swift @@ -20,8 +20,9 @@ struct MainTabView: View { case 1: NavigationStack{ OrderView() } case 2: NavigationStack { InventoryView(selectedTab: $selectedTab, tabTappedTrigger: $tabTappedTrigger) } - case 3: NavigationStack{ ContentView() } - default: NavigationStack{ HomeView() } +// case 3: NavigationStack{ ContentView() } + case 3: NavigationStack{ ProfileView() } + default: NavigationStack{ ContentView() } } } .frame(maxWidth: .infinity, maxHeight: .infinity) diff --git a/StockMate/StockMate/resources/Assets.xcassets/chair.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/chair.imageset/Contents.json new file mode 100644 index 0000000..ad1d744 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/chair.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "chair.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/chair.imageset/chair.svg b/StockMate/StockMate/resources/Assets.xcassets/chair.imageset/chair.svg new file mode 100644 index 0000000..ea15a1f --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/chair.imageset/chair.svg @@ -0,0 +1,3 @@ + + + diff --git a/StockMate/StockMate/resources/Assets.xcassets/cog.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/cog.imageset/Contents.json new file mode 100644 index 0000000..7124035 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/cog.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "cog.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/cog.imageset/cog.svg b/StockMate/StockMate/resources/Assets.xcassets/cog.imageset/cog.svg new file mode 100644 index 0000000..bb97be6 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/cog.imageset/cog.svg @@ -0,0 +1,4 @@ + + + + diff --git a/StockMate/StockMate/resources/Assets.xcassets/lightbulb.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/lightbulb.imageset/Contents.json new file mode 100644 index 0000000..beab311 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/lightbulb.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "lightbulb.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/lightbulb.imageset/lightbulb.svg b/StockMate/StockMate/resources/Assets.xcassets/lightbulb.imageset/lightbulb.svg new file mode 100644 index 0000000..e6ad3ff --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/lightbulb.imageset/lightbulb.svg @@ -0,0 +1,3 @@ + + + diff --git a/StockMate/StockMate/resources/Assets.xcassets/package.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/package.imageset/Contents.json new file mode 100644 index 0000000..e7f4c71 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/package.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "package.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/package.imageset/package.svg b/StockMate/StockMate/resources/Assets.xcassets/package.imageset/package.svg new file mode 100644 index 0000000..c2f544d --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/package.imageset/package.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/StockMate/StockMate/resources/Assets.xcassets/spanner.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/spanner.imageset/Contents.json new file mode 100644 index 0000000..36cb254 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/spanner.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "spanner.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/spanner.imageset/spanner.svg b/StockMate/StockMate/resources/Assets.xcassets/spanner.imageset/spanner.svg new file mode 100644 index 0000000..8203013 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/spanner.imageset/spanner.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/StockMate/StockMate/resources/Color.swift b/StockMate/StockMate/resources/Color.swift index f98469f..f927e60 100644 --- a/StockMate/StockMate/resources/Color.swift +++ b/StockMate/StockMate/resources/Color.swift @@ -28,12 +28,12 @@ extension Color { // Etc static let Gray = Color(hex: "#ABABAB") + static let GrayStroke = Color(hex: "#DBDBDB") static let White = Color(hex: "#FFFFFF") static let Dark = Color(hex: "#04150C") static let Light = Color(hex: "#F7F7F7") - // Text static let textBlack = Color(hex: "#152C07") static let textGray1 = Color(hex: "#5D5C5D") @@ -77,6 +77,25 @@ extension Color { static let StatusRedBg = Color(hex: "#FDE0DD") static let StatusPurpleBg = Color(hex: "#E9E6FF") + + + //DFF6FC + // Home Status + static let Hstatus1 = Color(hex: "#DFF6FC") + static let Hstatus2 = Color(hex: "#DBDFF3") + static let Hstatus3 = Color(hex: "#EB5032") + static let Hstatus4 = Color(hex: "#8DDB55") + static let Hstatus5 = Color(hex: "#8892A2") + + // Home Status Background + static let Hstatus1Bg = Color(hex: "#DFF6FC") + static let Hstatus2Bg = Color(hex: "#DBDFF3") + static let Hstatus3Bg = Color(hex: "#FCE3DE") + static let Hstatus4Bg = Color(hex: "#EDF9E4") + static let Hstatus5Bg = Color(hex: "#ECEEF0") + + + // 투명도 포함 예시 static let boxBgWhite = Color(hex: "#40FFFFFF") // 투명도 포함 } From 26a5420a6af1cae6112fc9f691af9d3f32ebd9b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sun, 26 Oct 2025 00:49:52 +0900 Subject: [PATCH 24/85] =?UTF-8?q?[FEAT]=20=EB=B0=9C=EC=A3=BC=ED=83=AD=20?= =?UTF-8?q?=EC=9E=A5=EB=B0=94=EA=B5=AC=EB=8B=88=20api=20=EC=97=B0=EB=8F=99?= =?UTF-8?q?=20=EB=B0=8F=20=EC=9E=A5=EB=B0=94=EA=B5=AC=EB=8B=88=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/core/components/CartSummaryBar.swift | 50 ++++++ .../components/OrderRequestCardView.swift | 160 ++++++++++-------- .../app/core/components/RoundedCorner.swift | 22 +++ .../app/feature/auth/ui/HomeView.swift | 38 +++-- .../app/feature/cart/data/CartApi.swift | 80 +++++++++ .../cart/data/CartRepositoryImpl.swift | 37 ++++ .../cart/domain/CartRepositoryProtocol.swift | 24 +++ .../cart/viewmodel/CartViewModel.swift | 135 +++++++++++++++ .../domain/InventoryRepositoryProtocol.swift | 1 + .../app/feature/orders/ui/OrderListView.swift | 9 +- .../feature/orders/ui/OrderRequestView.swift | 28 +-- .../app/feature/orders/ui/OrderView.swift | 136 ++++++++++++--- 12 files changed, 589 insertions(+), 131 deletions(-) create mode 100644 StockMate/StockMate/app/core/components/CartSummaryBar.swift create mode 100644 StockMate/StockMate/app/core/components/RoundedCorner.swift create mode 100644 StockMate/StockMate/app/feature/cart/data/CartApi.swift create mode 100644 StockMate/StockMate/app/feature/cart/data/CartRepositoryImpl.swift create mode 100644 StockMate/StockMate/app/feature/cart/domain/CartRepositoryProtocol.swift create mode 100644 StockMate/StockMate/app/feature/cart/viewmodel/CartViewModel.swift diff --git a/StockMate/StockMate/app/core/components/CartSummaryBar.swift b/StockMate/StockMate/app/core/components/CartSummaryBar.swift new file mode 100644 index 0000000..6c918d4 --- /dev/null +++ b/StockMate/StockMate/app/core/components/CartSummaryBar.swift @@ -0,0 +1,50 @@ +// +// CartSummaryBar.swift +// StockMate +// +// Created by Admin on 10/26/25. +// + +import Foundation +import SwiftUI + +struct CartSummaryBar: View { + @ObservedObject var cartVM: CartViewModel + + var body: some View { + VStack { + Spacer() + + if let cart = cartVM.cart, + !cart.items.isEmpty { + NavigationLink(destination: OrderCartView()) { + HStack(spacing: 12) { + Circle() + .fill(Color.white) + .frame(width: 20, height: 20) + .overlay( + Text("\(cart.items.count)") + .font(.system(size: 11, weight: .bold)) + .foregroundColor(.Primary) + ) + + Text("장바구니 보기") + .font(.system(size: 16, weight: .bold)) + .foregroundColor(.white) + + Spacer() + + Text("\(cart.totalPrice ?? 0)원") + .font(.system(size: 16, weight: .semibold)) + .foregroundColor(.white) + } + .padding(.horizontal, 30) + .frame(height: 60) + .background(Color.Primary) + .clipShape(RoundedCorner(radius: 16, corners: [.topLeft, .topRight])) + } + } + } + .ignoresSafeArea(edges: .bottom) + } +} diff --git a/StockMate/StockMate/app/core/components/OrderRequestCardView.swift b/StockMate/StockMate/app/core/components/OrderRequestCardView.swift index d399bdd..aa29bef 100644 --- a/StockMate/StockMate/app/core/components/OrderRequestCardView.swift +++ b/StockMate/StockMate/app/core/components/OrderRequestCardView.swift @@ -9,84 +9,110 @@ import SwiftUI struct OrderRequestCardView: View { let item: InventoryItem - var quantity: Int - var onAdd: () -> Void - var onRemove: () -> Void - var onCartAdd: () -> Void + let quantity: Int + let onIncrease: () -> Void + let onDecrease: () -> Void + let onAddToCart: () -> Void + let onRemoveFromCart: () -> Void - var body: some View { VStack(alignment: .leading, spacing: 6) { - Text(item.categoryName) - .font(.system(size: 12, weight: .semibold)) - .foregroundColor(.black) + Text(item.categoryName) + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(.black) - Divider().frame(height: 0.2).background(Color.textGray2) + Divider().frame(height: 0.2).background(Color.textGray2) - HStack(alignment: .center, spacing: 12) { - AsyncImage(url: URL(string: item.image)) { image in - image.resizable().scaledToFit() - } placeholder: { - Color.gray.opacity(0.2) - } - .frame(width: 64, height: 64) - .cornerRadius(10) + HStack(alignment: .center, spacing: 12) { + AsyncImage(url: URL(string: item.image)) { image in + image.resizable().scaledToFit() + } placeholder: { + Color.gray.opacity(0.2) + } + .frame(width: 64, height: 64) + .cornerRadius(10) - VStack(alignment: .leading, spacing: 6) { - Text(item.korName) - .font(.system(size: 14, weight: .bold)) - .foregroundColor(.black) - .lineLimit(2) + VStack(alignment: .leading, spacing: 6) { + Text(item.korName) + .font(.system(size: 14, weight: .bold)) + .foregroundColor(.black) + .lineLimit(2) - Text("\(item.trim) / \(item.model)") - .font(.system(size: 13)) - .foregroundColor(.gray) + Text("\(item.trim) / \(item.model)") + .font(.system(size: 13)) + .foregroundColor(.gray) - Text("\(item.price)원") - .font(.system(size: 13, weight: .semibold)) - .foregroundColor(.black) - } + Text("\(item.price)원") + .font(.system(size: 13, weight: .semibold)) + .foregroundColor(.black) + } - Spacer() + Spacer() - if quantity == 0 { - Button(action: onCartAdd) { - Image(systemName: "cart.badge.plus") - .font(.system(size: 18)) - .foregroundColor(.Primary) - .padding(10) - .background(Color.Primary.opacity(0.1)) - .clipShape(Circle()) - } - } else { - HStack(spacing: 10) { - Button(action: onRemove) { - Image(systemName: "minus") - .font(.system(size: 14, weight: .bold)) - .foregroundColor(.gray) - } + // 🪄 수량에 따른 3단계 분기 + if quantity == 0 { + Button(action: onAddToCart) { + Image(systemName: "cart.badge.plus") + .font(.system(size: 18)) + .foregroundColor(.Primary) + .padding(10) + .background(Color.Primary.opacity(0.1)) + .clipShape(Circle()) + } - Text("\(quantity)") - .font(.system(size: 15, weight: .semibold)) - .frame(width: 24) + } else if quantity == 1 { + HStack(spacing: 10) { + Button(action: onRemoveFromCart) { + Image(systemName: "trash") + .font(.system(size: 14, weight: .bold)) + .foregroundColor(.red) + } + + Text("1") + .font(.system(size: 15, weight: .semibold)) + .frame(width: 24) - Button(action: onAdd) { - Image(systemName: "plus") - .font(.system(size: 14, weight: .bold)) - .foregroundColor(.Primary) - } - } - .padding(.vertical, 6) - .padding(.horizontal, 10) - .background(Color.white) - .cornerRadius(10) - .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) - } - } - } - .padding() - .background(Color.white) - .cornerRadius(14) - .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) + Button(action: onIncrease) { + Image(systemName: "plus") + .font(.system(size: 14, weight: .bold)) + .foregroundColor(.Primary) + } + } + .padding(.vertical, 6) + .padding(.horizontal, 10) + .background(Color.white) + .cornerRadius(10) + .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) + + } else { + HStack(spacing: 10) { + Button(action: onDecrease) { + Image(systemName: "minus") + .font(.system(size: 14, weight: .bold)) + .foregroundColor(.gray) + } + + Text("\(quantity)") + .font(.system(size: 15, weight: .semibold)) + .frame(width: 24) + + Button(action: onIncrease) { + Image(systemName: "plus") + .font(.system(size: 14, weight: .bold)) + .foregroundColor(.Primary) + } + } + .padding(.vertical, 6) + .padding(.horizontal, 10) + .background(Color.white) + .cornerRadius(10) + .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) + } + } + } + .padding() + .background(Color.white) + .cornerRadius(14) + .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) } } diff --git a/StockMate/StockMate/app/core/components/RoundedCorner.swift b/StockMate/StockMate/app/core/components/RoundedCorner.swift new file mode 100644 index 0000000..b93b448 --- /dev/null +++ b/StockMate/StockMate/app/core/components/RoundedCorner.swift @@ -0,0 +1,22 @@ +// +// RoundedCorner.swift +// StockMate +// +// Created by Admin on 10/26/25. +// + +import SwiftUI + +struct RoundedCorner: Shape { + var radius: CGFloat = .infinity + var corners: UIRectCorner = .allCorners + + func path(in rect: CGRect) -> Path { + let path = UIBezierPath( + roundedRect: rect, + byRoundingCorners: corners, + cornerRadii: CGSize(width: radius, height: radius) + ) + return Path(path.cgPath) + } +} diff --git a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift index 7ba28a4..1094e52 100644 --- a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift @@ -20,8 +20,8 @@ struct HomeView: View { HStack(spacing: 16) { ProfileCircleView(name: userViewModel.userInfo?.owner ?? "사용자", size: 50) - VStack(alignment: .leading, spacing: 4) { - HStack { + VStack(alignment: .leading, spacing: 1) { + HStack(spacing: 2) { Image("location") .foregroundColor(.gray) Text(userViewModel.userInfo?.storeName ?? "가게명 없음") @@ -43,23 +43,25 @@ struct HomeView: View { .padding(.horizontal) // 🔍 검색창 - HStack { - Image(systemName: "magnifyingglass") - .foregroundColor(.gray) - - Text("부품을 검색하세요.") - .foregroundColor(.gray) - Spacer() + NavigationLink(destination: InventorySearchView()) { + HStack { + Image(systemName: "magnifyingglass") + .foregroundColor(.gray) + + Text("부품을 검색하세요.") + .foregroundColor(.gray) + Spacer() + } + .padding() + .background(Color(.white)) + .cornerRadius(9999) + .overlay( + RoundedRectangle(cornerRadius: 9999) + .stroke(Color.gray.opacity(0.4), lineWidth: 1) + ) + .padding(.horizontal) } - .padding() - .background(Color(.white)) - .cornerRadius(9999) - .overlay( - RoundedRectangle(cornerRadius: 9999) - .stroke(Color.gray.opacity(0.4), lineWidth: 1) - ) - .padding(.horizontal) - + .buttonStyle(.plain) lackStockSection diff --git a/StockMate/StockMate/app/feature/cart/data/CartApi.swift b/StockMate/StockMate/app/feature/cart/data/CartApi.swift new file mode 100644 index 0000000..0ad1e1d --- /dev/null +++ b/StockMate/StockMate/app/feature/cart/data/CartApi.swift @@ -0,0 +1,80 @@ +// +// CartApi.swift +// StockMate +// +// Created by Admin on 10/25/25. +// + +import Foundation +import Alamofire + +// === Response === +// 빈 데이터 응답용 +struct VoidData: Codable {} + +// get/put/post 응답, put,post는 totalPrice가 없음 -> optional +struct CartData: Decodable { + let cartId: Int + let memberId: Int + let items: [CartItem] + let totalPrice: Int? +} + +// put,post는 cartItemId,partId,amount만 필요 -> optional +struct CartItem: Decodable, Identifiable { + let cartItemId: Int + let partId: Int + var amount: Int + let partName: String? + let categoryName: String? + let brand: String? + let model: String? + let trim: String? + let price: Int? + let stock: Int? + + var id: Int { cartItemId } +} + + +// === Request === put,post만 요청값이 있음 +struct CartUpdateRequest: Encodable { + let items: [CartUpdateItem] +} + +struct CartUpdateItem: Encodable { + let partId: Int + let amount: Int +} + +enum CartApi { + // GET - 장바구니 조회 + static func fetchCart() -> DataRequest { + let url = ApiClient.baseURL + "api/v1/order/cart" + return ApiClient.shared.request(url, method: .get) + } + + // POST - 장바구니 등록 (추가) + static func addToCart(_ body: CartUpdateRequest) -> DataRequest { + let url = ApiClient.baseURL + "api/v1/order/cart" + return ApiClient.shared.request(url, + method: .post, + parameters: body, + encoder: JSONParameterEncoder.default) + } + + // PUT - 장바구니 수정 (전체 덮어쓰기 형태) + static func updateCart(_ body: CartUpdateRequest) -> DataRequest { + let url = ApiClient.baseURL + "api/v1/order/cart" + return ApiClient.shared.request(url, + method: .put, + parameters: body, + encoder: JSONParameterEncoder.default) + } + + // DELETE - 장바구니 전체 비우기 (파라미터 없음) + static func clearCart() -> DataRequest { + let url = ApiClient.baseURL + "api/v1/order/cart" + return ApiClient.shared.request(url, method: .delete) + } +} diff --git a/StockMate/StockMate/app/feature/cart/data/CartRepositoryImpl.swift b/StockMate/StockMate/app/feature/cart/data/CartRepositoryImpl.swift new file mode 100644 index 0000000..9a7c98f --- /dev/null +++ b/StockMate/StockMate/app/feature/cart/data/CartRepositoryImpl.swift @@ -0,0 +1,37 @@ +// +// CartRepositoryImpl.swift +// StockMate +// +// Created by Admin on 10/25/25. +// + + +import Foundation +import Alamofire + +final class CartRepositoryImpl: CartRepositoryProtocol { + + func fetchCart() async -> AppResult> { + let req = CartApi.fetchCart() + return await safeApi(req, decodeTo: ApiResponse.self) + } + + func addToCart( + request: CartUpdateRequest + ) async -> AppResult> { + let req = CartApi.addToCart(request) + return await safeApi(req, decodeTo: ApiResponse.self) + } + + func updateCart( + request: CartUpdateRequest + ) async -> AppResult> { + let req = CartApi.updateCart(request) + return await safeApi(req, decodeTo: ApiResponse.self) + } + + func clearCart() async -> AppResult> { + let req = CartApi.clearCart() + return await safeApi(req, decodeTo: ApiResponse.self) + } +} diff --git a/StockMate/StockMate/app/feature/cart/domain/CartRepositoryProtocol.swift b/StockMate/StockMate/app/feature/cart/domain/CartRepositoryProtocol.swift new file mode 100644 index 0000000..82b44a0 --- /dev/null +++ b/StockMate/StockMate/app/feature/cart/domain/CartRepositoryProtocol.swift @@ -0,0 +1,24 @@ +// +// CartRepositoryProtocol.swift +// StockMate +// +// Created by Admin on 10/25/25. +// + +import Foundation +import Alamofire + +protocol CartRepositoryProtocol { + func fetchCart() async -> AppResult> + + func addToCart( + request: CartUpdateRequest + ) async -> AppResult> + + func updateCart( + request: CartUpdateRequest + ) async -> AppResult> + + func clearCart() async -> AppResult> +} + diff --git a/StockMate/StockMate/app/feature/cart/viewmodel/CartViewModel.swift b/StockMate/StockMate/app/feature/cart/viewmodel/CartViewModel.swift new file mode 100644 index 0000000..e7355d5 --- /dev/null +++ b/StockMate/StockMate/app/feature/cart/viewmodel/CartViewModel.swift @@ -0,0 +1,135 @@ +// +// CartViewModel.swift +// StockMate +// +// Created by Admin on 10/25/25. +// + +import Foundation + +@MainActor +final class CartViewModel: ObservableObject { + + @Published var cart: CartData? + @Published var items: [CartItem] = [] + @Published var message: String = "" + @Published var isLoading: Bool = false + @Published var shouldGoToLogin: Bool = false // 401 대응 + + private let repository: CartRepositoryProtocol + + init(repository: CartRepositoryProtocol = CartRepositoryImpl()) { + self.repository = repository + } + + // MARK: - Fetch + func fetchCart() async { + isLoading = true + defer { isLoading = false } + + switch await repository.fetchCart() { + case .success(let response): + self.cart = response.data + self.items = response.data?.items ?? [] + case .failure(let error): + handleError(error) + } + } + + // MARK: - Add + func addToCart(partId: Int, amount: Int) async { + let req = CartUpdateRequest(items: [CartUpdateItem(partId: partId, amount: amount)]) + switch await repository.addToCart(request: req) { + case .success(let response): + self.cart = response.data + self.items = response.data?.items ?? [] + case .failure(let error): + handleError(error) + } + } + + // MARK: - Update Quantity (전체 덮어쓰기) + func updateCart() async { + let requestItems = items.map { CartUpdateItem(partId: $0.partId, amount: $0.amount) } + let req = CartUpdateRequest(items: requestItems) + + switch await repository.updateCart(request: req) { + case .success(let response): + self.cart = response.data + case .failure(let error): + handleError(error) + } + } + + // MARK: - Clear Cart + func clearCart() async { + switch await repository.clearCart() { + case .success: + self.cart = nil + self.items = [] + case .failure(let error): + handleError(error) + } + } + + // MARK: - Private + private func handleError(_ error: AppError) { + message = error.message + if error.code == 401 { + shouldGoToLogin = true + } + } + + + func quantity(for partId: Int) -> Int { + return items.first(where: { $0.partId == partId })?.amount ?? 0 + } + + func increaseQuantity(for partId: Int) async { + if let index = items.firstIndex(where: { $0.partId == partId }) { + items[index].amount += 1 + } else { + let newItem = CartItem( + cartItemId: 0, + partId: partId, + amount: 1, + partName: nil, + categoryName: nil, + brand: nil, + model: nil, + trim: nil, + price: nil, + stock: nil + ) + items.append(newItem) + } + await syncCart() + } + + func decreaseQuantity(for partId: Int) async { + guard let index = items.firstIndex(where: { $0.partId == partId }) else { return } + + if items[index].amount > 1 { + items[index].amount -= 1 + } else { + items.remove(at: index) + } + await syncCart() + } + + + // 외부에서 호출하는 기존 updateCart()는 그대로 유지 + private func syncCart() async { + let updates = items.map { + CartUpdateItem(partId: $0.partId, amount: $0.amount) + } + let request = CartUpdateRequest(items: updates) + let response = await repository.updateCart(request: request) + + if case .success(let result) = response, let data = result.data { + self.items = data.items // 서버 최신 값으로 맞추기 + } + } + + +} diff --git a/StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift b/StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift index de669ec..fe2a090 100644 --- a/StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift +++ b/StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift @@ -6,6 +6,7 @@ // import Foundation +import Alamofire protocol InventoryRepositoryProtocol { func getInventoryList( diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift index 140cd32..893218e 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift @@ -13,14 +13,6 @@ struct OrderListView: View { var body: some View { // NavigationStack { VStack(alignment: .leading, spacing: 0) { - // 타이틀 - Text("주문 내역") - .font(.title2) - .bold() - .padding(.top, 13) - .padding(.leading, 25) - .frame(maxWidth: .infinity, alignment: .leading) - if orderViewModel.isLoading { ProgressView() @@ -61,6 +53,7 @@ struct OrderListView: View { } } .background(Color.Light) + .navigationTitle("주문 내역") .task { await orderViewModel.loadOrders() } diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderRequestView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderRequestView.swift index 6f08d90..12f9b6e 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderRequestView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderRequestView.swift @@ -149,20 +149,20 @@ struct OrderRequestView: View { } } -// ✅ 특정 코너만 둥글게 처리할 수 있게 하는 Shape -struct RoundedCorner: Shape { - var radius: CGFloat = 16 - var corners: UIRectCorner = .allCorners - - func path(in rect: CGRect) -> Path { - let path = UIBezierPath( - roundedRect: rect, - byRoundingCorners: corners, - cornerRadii: CGSize(width: radius, height: radius) - ) - return Path(path.cgPath) - } -} +//// ✅ 특정 코너만 둥글게 처리할 수 있게 하는 Shape +//struct RoundedCorner: Shape { +// var radius: CGFloat = 16 +// var corners: UIRectCorner = .allCorners +// +// func path(in rect: CGRect) -> Path { +// let path = UIBezierPath( +// roundedRect: rect, +// byRoundingCorners: corners, +// cornerRadii: CGSize(width: radius, height: radius) +// ) +// return Path(path.cgPath) +// } +//} #Preview { OrderRequestView() diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderView.swift index 5f43ab8..ddeaf61 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderView.swift @@ -8,39 +8,127 @@ import SwiftUI struct OrderView: View { + @StateObject private var inventoryVM = InventoryViewModel() + @StateObject private var cartVM = CartViewModel() + var body: some View { - VStack(spacing: 0) { - // 타이틀 - Text("발주 요청") - .font(.title2) - .bold() - .padding(.top, 13) - .padding(.leading, 25) - .frame(maxWidth: .infinity, alignment: .leading) + NavigationStack { + ZStack{ + - // 🔍 검색창 - HStack { - Image(systemName: "magnifyingglass") - .foregroundColor(.gray) + ScrollView { + // 타이틀 + Text("재고 관리") + .font(.title2) + .bold() + .padding(.top, 13) + .padding(.leading, 25) + .frame(maxWidth: .infinity, alignment: .leading) + + Text("직접 발주") + .font(.system(size: 14)) + .bold() + .padding(.top, 13) + .padding(.leading, 25) + .frame(maxWidth: .infinity, alignment: .leading) + + // 🔍 검색창 + NavigationLink(destination: InventorySearchView()) { + HStack { + Image(systemName: "magnifyingglass") + .foregroundColor(.gray) + + Text("부품을 검색하세요.") + .foregroundColor(.gray) + Spacer() + } + .padding() + .background(Color(.white)) + .cornerRadius(9999) + .overlay( + RoundedRectangle(cornerRadius: 9999) + .stroke(Color.gray.opacity(0.4), lineWidth: 1) + ) + .padding(.horizontal) + } + .buttonStyle(.plain) + + // 타이틀 + Text("부족 재고") + .font(.system(size: 14)) + .bold() + .padding(.top, 13) + .padding(.leading, 25) + .frame(maxWidth: .infinity, alignment: .leading) + + LazyVStack(alignment: .leading, spacing: 14) { + ForEach(inventoryVM.underLimitItems) { item in + + let qty = cartVM.quantity(for: item.id) + + OrderRequestCardView( + item: item, + quantity: qty, + onIncrease: { + Task { + await cartVM.increaseQuantity(for: item.id) + } + }, + onDecrease: { + Task { + await cartVM.decreaseQuantity(for: item.id) + } + }, + onAddToCart: { + Task { + await cartVM.addToCart(partId: item.id, amount: 1) + } + }, + onRemoveFromCart: { + Task { + await cartVM.decreaseQuantity(for: item.id) + } + } + ) + .onAppear { + if item.id == inventoryVM.underLimitItems.last?.id { + Task { await inventoryVM.loadUnderLimitList() } + } + } + } + + if inventoryVM.isLoading { + ProgressView().padding() + } + } + .padding(.horizontal) + + + + + } + .background(Color.Light) + .task { + await inventoryVM.loadUnderLimitList(reset: true) + await cartVM.fetchCart() + } + .alert(cartVM.message, isPresented: .constant(!cartVM.message.isEmpty)) { + Button("확인") { cartVM.message = "" } + } - Text("부품을 검색하세요.") -// TextField("부품을 검색하세요.", text: $searchText) -// .textFieldStyle(PlainTextFieldStyle()) + // OrderView 내부 ScrollView 아래에 overlay 혹은 bottomBar + VStack { + Spacer() + CartSummaryBar(cartVM: cartVM) + } + .ignoresSafeArea(edges: .bottom) } - .padding() - .background(Color(.white)) - .cornerRadius(9999) - .overlay( - RoundedRectangle(cornerRadius: 9999) - .stroke(Color.gray.opacity(0.4), lineWidth: 1) - ) - .padding(.horizontal) - .padding(.vertical) } } } + #Preview { OrderView() } From a71c9e72758654a5277d69fb5a789124e958a478 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Mon, 27 Oct 2025 11:30:20 +0900 Subject: [PATCH 25/85] =?UTF-8?q?[REFAC]=20=EB=B0=9C=EC=A3=BC=ED=83=AD=20?= =?UTF-8?q?=EC=9E=A5=EB=B0=94=EA=B5=AC=EB=8B=88=20=EB=B3=B4=EA=B8=B0=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/feature/cart/data/CartApi.swift | 4 +- .../cart/data/CartRepositoryImpl.swift | 1 + .../cart/viewmodel/CartViewModel.swift | 74 +++++++++++++++---- .../app/feature/orders/ui/OrderView.swift | 37 ++++------ 4 files changed, 77 insertions(+), 39 deletions(-) diff --git a/StockMate/StockMate/app/feature/cart/data/CartApi.swift b/StockMate/StockMate/app/feature/cart/data/CartApi.swift index 0ad1e1d..231f8dd 100644 --- a/StockMate/StockMate/app/feature/cart/data/CartApi.swift +++ b/StockMate/StockMate/app/feature/cart/data/CartApi.swift @@ -33,7 +33,9 @@ struct CartItem: Decodable, Identifiable { let price: Int? let stock: Int? - var id: Int { cartItemId } +// var id: Int { cartItemId } + var id: Int { partId } + } diff --git a/StockMate/StockMate/app/feature/cart/data/CartRepositoryImpl.swift b/StockMate/StockMate/app/feature/cart/data/CartRepositoryImpl.swift index 9a7c98f..479f1d2 100644 --- a/StockMate/StockMate/app/feature/cart/data/CartRepositoryImpl.swift +++ b/StockMate/StockMate/app/feature/cart/data/CartRepositoryImpl.swift @@ -34,4 +34,5 @@ final class CartRepositoryImpl: CartRepositoryProtocol { let req = CartApi.clearCart() return await safeApi(req, decodeTo: ApiResponse.self) } + } diff --git a/StockMate/StockMate/app/feature/cart/viewmodel/CartViewModel.swift b/StockMate/StockMate/app/feature/cart/viewmodel/CartViewModel.swift index e7355d5..f4bc314 100644 --- a/StockMate/StockMate/app/feature/cart/viewmodel/CartViewModel.swift +++ b/StockMate/StockMate/app/feature/cart/viewmodel/CartViewModel.swift @@ -40,9 +40,9 @@ final class CartViewModel: ObservableObject { func addToCart(partId: Int, amount: Int) async { let req = CartUpdateRequest(items: [CartUpdateItem(partId: partId, amount: amount)]) switch await repository.addToCart(request: req) { - case .success(let response): - self.cart = response.data - self.items = response.data?.items ?? [] + case .success: + await fetchCart() + updateLocalCartState() case .failure(let error): handleError(error) } @@ -50,6 +50,12 @@ final class CartViewModel: ObservableObject { // MARK: - Update Quantity (전체 덮어쓰기) func updateCart() async { + // ✅ items가 비면 clearCart 호출하고 return + if items.isEmpty { + await clearCart() + return + } + let requestItems = items.map { CartUpdateItem(partId: $0.partId, amount: $0.amount) } let req = CartUpdateRequest(items: requestItems) @@ -75,21 +81,48 @@ final class CartViewModel: ObservableObject { // MARK: - Private private func handleError(_ error: AppError) { message = error.message + print("🚨 Error (\(error.code)): \(error.message)") + if error.code == 401 { shouldGoToLogin = true } } - func quantity(for partId: Int) -> Int { - return items.first(where: { $0.partId == partId })?.amount ?? 0 - } + return items.first(where: { $0.partId == partId })?.amount ?? 0 + } + + private func updateLocalCartState() { + let total = items.reduce(0) { result, item in + result + (item.price ?? 0) * item.amount + } + + if let cart = cart { + self.cart = CartData( + cartId: cart.cartId, + memberId: cart.memberId, + items: self.items, + totalPrice: total + ) + } else { + // fallback: cart가 nil일 수 있는 초기 로드 상황 대비 + self.cart = CartData( + cartId: -1, + memberId: -1, + items: self.items, + totalPrice: total + ) + } + + objectWillChange.send() // 중요! SwiftUI에게 “바뀌었어!” 알림 + } + func increaseQuantity(for partId: Int) async { if let index = items.firstIndex(where: { $0.partId == partId }) { items[index].amount += 1 } else { - let newItem = CartItem( + items.append(CartItem( cartItemId: 0, partId: partId, amount: 1, @@ -100,36 +133,45 @@ final class CartViewModel: ObservableObject { trim: nil, price: nil, stock: nil - ) - items.append(newItem) + )) } + await syncCart() + updateLocalCartState() } func decreaseQuantity(for partId: Int) async { - guard let index = items.firstIndex(where: { $0.partId == partId }) else { return } + guard let index = items.firstIndex(where: { $0.partId == partId }) else { + return + } if items[index].amount > 1 { items[index].amount -= 1 } else { items.remove(at: index) } + await syncCart() + updateLocalCartState() } - - // 외부에서 호출하는 기존 updateCart()는 그대로 유지 private func syncCart() async { + if items.isEmpty { + // ✅ 장바구니가 빈 경우는 clearCart 호출 + await clearCart() + return + } let updates = items.map { CartUpdateItem(partId: $0.partId, amount: $0.amount) } let request = CartUpdateRequest(items: updates) - let response = await repository.updateCart(request: request) - if case .success(let result) = response, let data = result.data { - self.items = data.items // 서버 최신 값으로 맞추기 + switch await repository.updateCart(request: request) { + case .success: + await fetchCart() + case .failure(let error): + handleError(error) } } - } diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderView.swift index ddeaf61..ab93776 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderView.swift @@ -8,14 +8,13 @@ import SwiftUI struct OrderView: View { - @StateObject private var inventoryVM = InventoryViewModel() - @StateObject private var cartVM = CartViewModel() + @StateObject private var inventoryViewModel = InventoryViewModel() + @StateObject private var cartViewModel = CartViewModel() var body: some View { NavigationStack { ZStack{ - ScrollView { // 타이틀 Text("재고 관리") @@ -62,64 +61,58 @@ struct OrderView: View { .frame(maxWidth: .infinity, alignment: .leading) LazyVStack(alignment: .leading, spacing: 14) { - ForEach(inventoryVM.underLimitItems) { item in + ForEach(inventoryViewModel.underLimitItems) { item in - let qty = cartVM.quantity(for: item.id) + let qty = cartViewModel.quantity(for: item.id) OrderRequestCardView( item: item, quantity: qty, onIncrease: { Task { - await cartVM.increaseQuantity(for: item.id) + await cartViewModel.increaseQuantity(for: item.id) } }, onDecrease: { Task { - await cartVM.decreaseQuantity(for: item.id) + await cartViewModel.decreaseQuantity(for: item.id) } }, onAddToCart: { Task { - await cartVM.addToCart(partId: item.id, amount: 1) + await cartViewModel.addToCart(partId: item.id, amount: 1) } }, onRemoveFromCart: { Task { - await cartVM.decreaseQuantity(for: item.id) + await cartViewModel.decreaseQuantity(for: item.id) } } ) .onAppear { - if item.id == inventoryVM.underLimitItems.last?.id { - Task { await inventoryVM.loadUnderLimitList() } + if item.id == inventoryViewModel.underLimitItems.last?.id { + Task { await inventoryViewModel.loadUnderLimitList() } } } } - if inventoryVM.isLoading { + if inventoryViewModel.isLoading { ProgressView().padding() } } .padding(.horizontal) - - - } .background(Color.Light) .task { - await inventoryVM.loadUnderLimitList(reset: true) - await cartVM.fetchCart() - } - .alert(cartVM.message, isPresented: .constant(!cartVM.message.isEmpty)) { - Button("확인") { cartVM.message = "" } + await inventoryViewModel.loadUnderLimitList(reset: true) + await cartViewModel.fetchCart() } - // OrderView 내부 ScrollView 아래에 overlay 혹은 bottomBar + // OrderView 내부 ScrollView 아래 장바구니 확인 버튼 VStack { Spacer() - CartSummaryBar(cartVM: cartVM) + CartSummaryBar(cartVM: cartViewModel) } .ignoresSafeArea(edges: .bottom) From 96e82275dfc7c75b67df893ce716943bfc3d66a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Mon, 27 Oct 2025 12:34:29 +0900 Subject: [PATCH 26/85] =?UTF-8?q?[FEAT]=20=EC=A7=81=EC=A0=91=EB=B0=9C?= =?UTF-8?q?=EC=A3=BC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../orders/ui/OrderRequestSearchView.swift | 183 +++++++++++++++ .../app/feature/orders/ui/OrderView.swift | 208 +++++++++--------- .../app/navigation/MainTabView.swift | 5 +- 3 files changed, 294 insertions(+), 102 deletions(-) create mode 100644 StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift new file mode 100644 index 0000000..32699ff --- /dev/null +++ b/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift @@ -0,0 +1,183 @@ +// +// OrderRequestSearchView.swift +// StockMate +// +// Created by Admin on 10/27/25. +// + +import SwiftUI + +struct OrderRequestSearchView: View { + @ObservedObject var cartViewModel: CartViewModel + + @StateObject var inventoryViewModel = InventoryViewModel() + @State private var searchText = "" + + // 카테고리, 분류, 모델 + private let categories = ["전기/램프", "엔진/미션", "하체/바디", "내장/외장", "기타소모품"] + private let trims = ["준중형/소형", "중형", "대형", "SUV", "화물/트럭/승합", "수소/전기"] + private let trimToModels: [String: [String]] = [ + "준중형/소형": [ "아반떼MD", "아반떼AD", "아반떼CN7", "I30", "엑센트", "아이오닉", "벨로스터", "캐스퍼" ], + "중형": [ "NF소나타", "YF소나타", "LF소나타", "DN8소나타", "그랜저TG", "그랜저HG", "그랜저IG", "그랜저GN7", "I40" ], + "대형": ["제네시스BH", "에쿠스"], + "SUV": [ "베뉴", "코나OS", "코나SX2", "투싼IX", "투싼TL", "투싼NX4", "싼타페CM", "싼타페DM", "싼타페TM", "싼타페MX5", "맥스크루즈", "베라크루즈", "팰리세이드LX2", "팰리세이드LX3" ], + "화물/트럭/승합": [ "스타렉스", "그랜드스타렉스", "스타리아", "포터2", "쏠라티", "마이티", "메가트럭", "카운티" ], + "수소/전기": ["아이오닉5", "아이오닉6", "아이오닉9", "넥쏘FE", "넥쏘NH2"] + ] + + private var filteredModels: [String] { + if inventoryViewModel.selectedTrims.isEmpty { + return trimToModels.values.flatMap { $0 } + } else { + return inventoryViewModel.selectedTrims + .flatMap { trimToModels[$0] ?? [] } + } + } + + var body: some View { + ZStack { + VStack(spacing: 0) { + // 🔍 검색창 + HStack { + Image(systemName: "magnifyingglass") + .foregroundColor(.gray) + + TextField("부품을 검색하세요.", text: $searchText) + .textFieldStyle(PlainTextFieldStyle()) + .onSubmit { + let term = searchText.trimmingCharacters( + in: .whitespacesAndNewlines + ) + guard !term.isEmpty else { return } + Task { + await inventoryViewModel + .searchByName(name: term, reset: true) + } + } + + if !searchText.isEmpty { + Button(action: { + searchText = "" + inventoryViewModel.isSearching = false + }) { + Image(systemName: "xmark") + .foregroundColor(.gray) + } + .buttonStyle(.plain) + .padding(.trailing,3) + } + } + .padding() + .background(Color(.white)) + .cornerRadius(9999) + .overlay( + RoundedRectangle(cornerRadius: 9999) + .stroke(Color.gray.opacity(0.4), lineWidth: 1) + ) + .padding(.horizontal) + .padding(.vertical) + + // 필터 및 초기화 버튼 + HStack(spacing: 10) { + FilterMenu( + title: "카테고리", + items: categories, + selectedItems: inventoryViewModel.selectedCategories, + onTap: { inventoryViewModel.toggleCategory($0) } + ) + + FilterMenu( + title: "분류", + items: trims, + selectedItems: inventoryViewModel.selectedTrims, + onTap: { inventoryViewModel.toggleTrim($0) } + ) + + FilterMenu( + title: "모델", + items: filteredModels, + selectedItems: inventoryViewModel.selectedModels, + onTap: { inventoryViewModel.toggleModel($0) } + ) + + // 🔄 초기화 버튼 + Button(action: { + inventoryViewModel.resetFilters(with: searchText) + }) { + HStack(spacing: 4) { + Image(systemName: "arrow.counterclockwise") + Text("초기화") + } + .font(.system(size: 13, weight: .medium)) + .foregroundColor(.blue) + .padding(.trailing, 8) + } + .frame(maxWidth: .infinity, alignment: .trailing) + + } + .padding(.horizontal) + .padding(.bottom, 16) + + // 📋 재고 리스트 + ScrollView { + LazyVStack(spacing: 10) { + + ForEach( + inventoryViewModel.isSearching + ? inventoryViewModel.filteredSearchResults // 검색 + 필터링 + : inventoryViewModel.inventoryItems + ) { item in + let qty = cartViewModel.quantity(for: item.id) + + // InventoryCardView(item: item) + OrderRequestCardView( + item: item, + quantity: qty, + onIncrease: { Task { await cartViewModel.increaseQuantity(for: item.id) } }, + onDecrease: { Task { await cartViewModel.decreaseQuantity(for: item.id) }}, + onAddToCart: { Task { await cartViewModel.addToCart(partId: item.id, amount: 1) }}, + onRemoveFromCart: { Task { await cartViewModel.decreaseQuantity(for: item.id) }} + ) + .padding(.horizontal) + .onAppear { + Task { + if inventoryViewModel.isSearching { + if item.id == inventoryViewModel.filteredSearchResults.last?.id, + inventoryViewModel.searchHasMore { + await inventoryViewModel .loadMore(searchText: searchText) + } + } else { + if item.id == inventoryViewModel.inventoryItems.last?.id, + inventoryViewModel.hasMore, + !inventoryViewModel.isLoading + { + await inventoryViewModel.loadMore(searchText: searchText) + } + } + } + } + } + + if inventoryViewModel.isLoading { + ProgressView() + .padding(.vertical) + } + } + } + } + .background(Color.Light) + .navigationTitle("재고 조회") + .task { + await inventoryViewModel.loadInventoryList(reset: true) + await cartViewModel.fetchCart() + } + + VStack { + Spacer() + CartSummaryBar(cartVM: cartViewModel) + } + .ignoresSafeArea(edges: .bottom) + + } + } +} diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderView.swift index ab93776..2487705 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderView.swift @@ -8,120 +8,126 @@ import SwiftUI struct OrderView: View { - @StateObject private var inventoryViewModel = InventoryViewModel() - @StateObject private var cartViewModel = CartViewModel() - + @StateObject var inventoryViewModel = InventoryViewModel() + @ObservedObject var cartViewModel: CartViewModel + var body: some View { - NavigationStack { - ZStack{ + ZStack{ + ScrollView { + // 타이틀 + Text("재고 관리") + .font(.title2) + .bold() + .padding(.top, 13) + .padding(.leading, 25) + .frame(maxWidth: .infinity, alignment: .leading) - ScrollView { - // 타이틀 - Text("재고 관리") - .font(.title2) - .bold() - .padding(.top, 13) - .padding(.leading, 25) - .frame(maxWidth: .infinity, alignment: .leading) - - Text("직접 발주") - .font(.system(size: 14)) - .bold() - .padding(.top, 13) - .padding(.leading, 25) - .frame(maxWidth: .infinity, alignment: .leading) - - // 🔍 검색창 - NavigationLink(destination: InventorySearchView()) { - HStack { - Image(systemName: "magnifyingglass") - .foregroundColor(.gray) - - Text("부품을 검색하세요.") - .foregroundColor(.gray) - Spacer() - } - .padding() - .background(Color(.white)) - .cornerRadius(9999) - .overlay( - RoundedRectangle(cornerRadius: 9999) - .stroke(Color.gray.opacity(0.4), lineWidth: 1) - ) - .padding(.horizontal) + Text("직접 발주") + .font(.system(size: 14)) + .bold() + .padding(.top, 13) + .padding(.leading, 25) + .frame(maxWidth: .infinity, alignment: .leading) + + // 🔍 검색창 + NavigationLink(destination: + OrderRequestSearchView( + cartViewModel: cartViewModel + //inventoryViewModel: inventoryViewModel + ) + ) { + HStack { + Image(systemName: "magnifyingglass") + .foregroundColor(.gray) + + Text("부품을 검색하세요.") + .foregroundColor(.gray) + Spacer() } - .buttonStyle(.plain) - - // 타이틀 - Text("부족 재고") - .font(.system(size: 14)) - .bold() - .padding(.top, 13) - .padding(.leading, 25) - .frame(maxWidth: .infinity, alignment: .leading) - - LazyVStack(alignment: .leading, spacing: 14) { - ForEach(inventoryViewModel.underLimitItems) { item in - - let qty = cartViewModel.quantity(for: item.id) - - OrderRequestCardView( - item: item, - quantity: qty, - onIncrease: { - Task { - await cartViewModel.increaseQuantity(for: item.id) - } - }, - onDecrease: { - Task { - await cartViewModel.decreaseQuantity(for: item.id) - } - }, - onAddToCart: { - Task { - await cartViewModel.addToCart(partId: item.id, amount: 1) - } - }, - onRemoveFromCart: { - Task { - await cartViewModel.decreaseQuantity(for: item.id) - } + .padding() + .background(Color(.white)) + .cornerRadius(9999) + .overlay( + RoundedRectangle(cornerRadius: 9999) + .stroke(Color.gray.opacity(0.4), lineWidth: 1) + ) + .padding(.horizontal) + } + .buttonStyle(.plain) + + // 타이틀 + Text("부족 재고") + .font(.system(size: 14)) + .bold() + .padding(.top, 13) + .padding(.leading, 25) + .frame(maxWidth: .infinity, alignment: .leading) + + LazyVStack(alignment: .leading, spacing: 14) { + ForEach(inventoryViewModel.underLimitItems) { item in + + let qty = cartViewModel.quantity(for: item.id) + + OrderRequestCardView( + item: item, + quantity: qty, + onIncrease: { + Task { + await cartViewModel.increaseQuantity(for: item.id) + } + }, + onDecrease: { + Task { + await cartViewModel.decreaseQuantity(for: item.id) + } + }, + onAddToCart: { + Task { + await cartViewModel.addToCart(partId: item.id, amount: 1) } - ) - .onAppear { - if item.id == inventoryViewModel.underLimitItems.last?.id { - Task { await inventoryViewModel.loadUnderLimitList() } + }, + onRemoveFromCart: { + Task { + await cartViewModel.decreaseQuantity(for: item.id) } } - } - - if inventoryViewModel.isLoading { - ProgressView().padding() + ) + .onAppear { + if item.id == inventoryViewModel.underLimitItems.last?.id { + Task { await inventoryViewModel.loadUnderLimitList() } + } } } - .padding(.horizontal) + if inventoryViewModel.isLoading { + ProgressView().padding() + } } - .background(Color.Light) - .task { - await inventoryViewModel.loadUnderLimitList(reset: true) - await cartViewModel.fetchCart() - } + .padding(.horizontal) - // OrderView 내부 ScrollView 아래 장바구니 확인 버튼 - VStack { - Spacer() - CartSummaryBar(cartVM: cartViewModel) - } - .ignoresSafeArea(edges: .bottom) - } + .background(Color.Light) + .task { + if inventoryViewModel.underLimitItems.isEmpty { + await inventoryViewModel.loadUnderLimitList(reset: true) + } +// await inventoryViewModel.loadUnderLimitList(reset: true) + await cartViewModel.fetchCart() + } + + // OrderView 내부 ScrollView 아래 장바구니 확인 버튼 + VStack { + Spacer() + CartSummaryBar(cartVM: cartViewModel) + } + .ignoresSafeArea(edges: .bottom) + } + } } - -#Preview { - OrderView() -} +// +//#Preview { +// OrderView() +//} diff --git a/StockMate/StockMate/app/navigation/MainTabView.swift b/StockMate/StockMate/app/navigation/MainTabView.swift index a04a3c5..92e6ab7 100644 --- a/StockMate/StockMate/app/navigation/MainTabView.swift +++ b/StockMate/StockMate/app/navigation/MainTabView.swift @@ -8,6 +8,8 @@ import SwiftUI struct MainTabView: View { + @StateObject var cartVM = CartViewModel() + @State private var selectedTab = 0 @State private var tabTappedTrigger = false @@ -17,7 +19,8 @@ struct MainTabView: View { ZStack { switch selectedTab { case 0: NavigationStack{ HomeView() } - case 1: NavigationStack{ OrderView() } + case 1: NavigationStack{ OrderView(cartViewModel: cartVM) } //, inventoryViewModel: inventoryVM) } +// case 1: NavigationStack{ OrderView() } case 2: NavigationStack { InventoryView(selectedTab: $selectedTab, tabTappedTrigger: $tabTappedTrigger) } // case 3: NavigationStack{ ContentView() } From 70fc04f24abc6d2a09662783455a9a432eaefd38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Mon, 27 Oct 2025 15:15:25 +0900 Subject: [PATCH 27/85] =?UTF-8?q?[FEAT]=20=EC=9E=A5=EB=B0=94=EA=B5=AC?= =?UTF-8?q?=EB=8B=88=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/core/components/CartCard.swift | 128 +++++++++++++ .../app/core/components/CartSummaryBar.swift | 2 +- .../app/feature/cart/data/CartApi.swift | 1 + .../cart/viewmodel/CartViewModel.swift | 3 +- .../app/feature/orders/ui/OrderCartView.swift | 173 +++++------------- .../orders/ui/OrderRequestSearchView.swift | 3 +- .../app/feature/orders/ui/OrderView.swift | 1 - 7 files changed, 182 insertions(+), 129 deletions(-) create mode 100644 StockMate/StockMate/app/core/components/CartCard.swift diff --git a/StockMate/StockMate/app/core/components/CartCard.swift b/StockMate/StockMate/app/core/components/CartCard.swift new file mode 100644 index 0000000..1d959d0 --- /dev/null +++ b/StockMate/StockMate/app/core/components/CartCard.swift @@ -0,0 +1,128 @@ +// +// CartCard.swift +// StockMate +// +// Created by Admin on 10/27/25. +// + +import SwiftUI + +struct CartCard: View { + let item: CartItem + let quantity: Int + let onIncrease: () -> Void + let onDecrease: () -> Void + let onAddToCart: (() -> Void)? + let onRemoveFromCart: () -> Void + + var body: some View { + VStack(alignment: .leading, spacing: 6) { + Text(item.categoryName ?? "") + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(.black) + + Divider().frame(height: 0.2).background(Color.textGray2) + + HStack(alignment: .center, spacing: 12) { + AsyncImage(url: URL(string: item.image ?? "")) { image in + image.resizable().scaledToFit() + } placeholder: { + Color.gray.opacity(0.2) + } + .frame(width: 64, height: 64) + .cornerRadius(10) + + VStack(alignment: .leading, spacing: 6) { + Text(item.brand ?? "") + .font(.system(size: 14, weight: .bold)) + .foregroundColor(.black) + .lineLimit(2) + + Text("\((item.trim ?? "")) / \((item.model ?? ""))") + .font(.system(size: 13)) + .foregroundColor(.gray) + .lineLimit(1) + + Text("\(item.price ?? 0)원") + .font(.system(size: 13, weight: .semibold)) + .foregroundColor(.black) + +// Text("\(item.trim) / \(item.model)") +// .font(.system(size: 13)) +// .foregroundColor(.gray) +// +// Text("\(item.price)원") +// .font(.system(size: 13, weight: .semibold)) +// .foregroundColor(.black) + } + + Spacer() + + // 🪄 수량에 따른 3단계 분기 + if quantity == 0 { + if let onAddToCart = onAddToCart { + Button(action: onAddToCart) { + Image(systemName: "cart.badge.plus") + .font(.system(size: 18)) + .foregroundColor(.Primary) + .padding(10) + .background(Color.Primary.opacity(0.1)) + .clipShape(Circle()) + } + } + } else if quantity == 1 { + HStack(spacing: 10) { + Button(action: onRemoveFromCart) { + Image(systemName: "trash") + .font(.system(size: 14, weight: .bold)) + .foregroundColor(.red) + } + + Text("1") + .font(.system(size: 15, weight: .semibold)) + .frame(width: 24) + + Button(action: onIncrease) { + Image(systemName: "plus") + .font(.system(size: 14, weight: .bold)) + .foregroundColor(.Primary) + } + } + .padding(.vertical, 6) + .padding(.horizontal, 10) + .background(Color.white) + .cornerRadius(10) + .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) + + } else { + HStack(spacing: 10) { + Button(action: onDecrease) { + Image(systemName: "minus") + .font(.system(size: 14, weight: .bold)) + .foregroundColor(.gray) + } + + Text("\(quantity)") + .font(.system(size: 15, weight: .semibold)) + .frame(width: 24) + + Button(action: onIncrease) { + Image(systemName: "plus") + .font(.system(size: 14, weight: .bold)) + .foregroundColor(.Primary) + } + } + .padding(.vertical, 6) + .padding(.horizontal, 10) + .background(Color.white) + .cornerRadius(10) + .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) + } + } + } + .padding() + .background(Color.white) + .cornerRadius(14) + .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) + } +} diff --git a/StockMate/StockMate/app/core/components/CartSummaryBar.swift b/StockMate/StockMate/app/core/components/CartSummaryBar.swift index 6c918d4..b4aea61 100644 --- a/StockMate/StockMate/app/core/components/CartSummaryBar.swift +++ b/StockMate/StockMate/app/core/components/CartSummaryBar.swift @@ -17,7 +17,7 @@ struct CartSummaryBar: View { if let cart = cartVM.cart, !cart.items.isEmpty { - NavigationLink(destination: OrderCartView()) { + NavigationLink(destination: OrderCartView(cartViewModel: cartVM)) { HStack(spacing: 12) { Circle() .fill(Color.white) diff --git a/StockMate/StockMate/app/feature/cart/data/CartApi.swift b/StockMate/StockMate/app/feature/cart/data/CartApi.swift index 231f8dd..9a717b4 100644 --- a/StockMate/StockMate/app/feature/cart/data/CartApi.swift +++ b/StockMate/StockMate/app/feature/cart/data/CartApi.swift @@ -32,6 +32,7 @@ struct CartItem: Decodable, Identifiable { let trim: String? let price: Int? let stock: Int? + let image: String? // var id: Int { cartItemId } var id: Int { partId } diff --git a/StockMate/StockMate/app/feature/cart/viewmodel/CartViewModel.swift b/StockMate/StockMate/app/feature/cart/viewmodel/CartViewModel.swift index f4bc314..28bf1fd 100644 --- a/StockMate/StockMate/app/feature/cart/viewmodel/CartViewModel.swift +++ b/StockMate/StockMate/app/feature/cart/viewmodel/CartViewModel.swift @@ -132,7 +132,8 @@ final class CartViewModel: ObservableObject { model: nil, trim: nil, price: nil, - stock: nil + stock: nil, + image: nil )) } diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift index 0a0939a..acf2d76 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift @@ -8,131 +8,56 @@ import SwiftUI struct OrderCartView: View { -// -// @State private var cartItems: [InventoryItem: Int] = [ -// InventoryItem( -// id: 1, -// korName: "아반떼MD", -// model: "실린더 어셈블리-브레이크 마스터", -// trim: "엔진/미션", -// price: 3000, -// image: "https://via.placeholder.com/100", -// categoryName: "엔진/미션" -// ): 1, -// InventoryItem( -// id: 2, -// korName: "소나타DN8", -// model: "브레이크 오일 탱크", -// trim: "엔진/미션", -// price: 5000, -// image: "https://via.placeholder.com/100", -// categoryName: "엔진/미션" -// ): 2 -// ] -// -// var totalPrice: Int { -// cartItems.reduce(0) { $0 + ($1.key.price * $1.value) } -// } -// + @ObservedObject var cartViewModel: CartViewModel + var body: some View { -// NavigationStack { -// ZStack { -// ScrollView { -// VStack(spacing: 14) { -// ForEach(Array(cartItems.keys), id: \.id) { item in -// if let quantity = cartItems[item] { -// HStack(alignment: .center, spacing: 12) { -// AsyncImage(url: URL(string: item.image)) { image in -// image.resizable().scaledToFit() -// } placeholder: { -// Color.gray.opacity(0.2) -// } -// .frame(width: 64, height: 64) -// .cornerRadius(10) -// -// VStack(alignment: .leading, spacing: 6) { -// Text(item.korName) -// .font(.system(size: 14, weight: .bold)) -// .foregroundColor(.black) -// .lineLimit(2) -// -// Text(item.model) -// .font(.system(size: 13)) -// .foregroundColor(.gray) -// -// Text("\(item.price)원") -// .font(.system(size: 13, weight: .semibold)) -// .foregroundColor(.black) -// } -// -// Spacer() -// -// HStack(spacing: 10) { -// Button { -// if quantity > 1 { -// cartItems[item] = quantity - 1 -// } else { -// cartItems.removeValue(forKey: item) -// } -// } label: { -// Image(systemName: quantity > 1 ? "minus" : "trash") -// .font(.system(size: 14, weight: .bold)) -// .foregroundColor(quantity > 1 ? .gray : .red) -// } -// -// Text("\(quantity)") -// .font(.system(size: 15, weight: .semibold)) -// .frame(width: 24) -// -// Button { -// cartItems[item] = quantity + 1 -// } label: { -// Image(systemName: "plus") -// .font(.system(size: 14, weight: .bold)) -// .foregroundColor(.Primary) -// } -// } -// .padding(.vertical, 6) -// .padding(.horizontal, 10) -// .background(Color.white) -// .cornerRadius(10) -// .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) -// } -// .padding() -// .background(Color.white) -// .cornerRadius(14) -// .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) -// .padding(.horizontal, 20) -// } -// } -// Spacer(minLength: 100) -// } -// .padding(.top, 10) -// } -// -// // 하단 결제 버튼 -// VStack { -// Spacer() -// Button { -// // 결제 액션 -// } label: { -// Text("\(totalPrice)원 결제하기") -// .font(.system(size: 16, weight: .bold)) -// .foregroundColor(.white) -// .frame(maxWidth: .infinity) -// .frame(height: 60) -// .background(Color.Primary) -// } -// } -// } -// .background(Color.Light) -// .navigationTitle("장바구니 확인") -// .navigationBarTitleDisplayMode(.inline) -// .edgesIgnoringSafeArea(.bottom) -// } + VStack(spacing: 0) { + + ScrollView { + LazyVStack(spacing: 16) { + ForEach(cartViewModel.items) { cartItem in + + CartCard( + item: cartItem, + quantity: cartItem.amount, + onIncrease: { + Task { await cartViewModel.increaseQuantity(for: cartItem.partId) } + }, + onDecrease: { + Task { await cartViewModel.decreaseQuantity(for: cartItem.partId) } + }, + onAddToCart: nil, + onRemoveFromCart: { + Task { + await cartViewModel.decreaseQuantity(for: cartItem.partId) + } + } + ) + .padding(.horizontal) + } + } + .padding(.vertical) + } + .background(Color.Light) + + Button { + // TODO: 결제 처리 + } label: { + Text("\(cartViewModel.cart?.totalPrice ?? 0)원 결제하기") + .font(.system(size: 16, weight: .bold)) + .foregroundColor(.white) + .frame(maxWidth: .infinity) + .frame(height: 60) + .background(Color.Primary) + } + } + .background(Color.Light) + .navigationTitle("장바구니 확인") + .navigationBarTitleDisplayMode(.inline) + .task { + await cartViewModel.fetchCart() + } + .edgesIgnoringSafeArea(.bottom) } } -#Preview { -// OrderCartView() -} diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift index 32699ff..d75fea6 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift @@ -129,7 +129,6 @@ struct OrderRequestSearchView: View { ) { item in let qty = cartViewModel.quantity(for: item.id) - // InventoryCardView(item: item) OrderRequestCardView( item: item, quantity: qty, @@ -166,7 +165,7 @@ struct OrderRequestSearchView: View { } } .background(Color.Light) - .navigationTitle("재고 조회") + .navigationTitle("직접 발주") .task { await inventoryViewModel.loadInventoryList(reset: true) await cartViewModel.fetchCart() diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderView.swift index 2487705..c238a30 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderView.swift @@ -111,7 +111,6 @@ struct OrderView: View { if inventoryViewModel.underLimitItems.isEmpty { await inventoryViewModel.loadUnderLimitList(reset: true) } -// await inventoryViewModel.loadUnderLimitList(reset: true) await cartViewModel.fetchCart() } From bb0d3b81f5040dbb385e388897b89eb6c6fa2ae3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Mon, 27 Oct 2025 19:36:59 +0900 Subject: [PATCH 28/85] =?UTF-8?q?[FEAT]=20=EC=A3=BC=EB=AC=B8=20=EA=B2=B0?= =?UTF-8?q?=EC=A0=9C=20UI=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/core/components/CartInfoCard.swift | 86 ++++ .../app/feature/orders/ui/OrderCartView.swift | 6 +- .../app/feature/orders/ui/OrderInfoView.swift | 460 ++++++++++++------ .../feature/orders/ui/OrderRequestView.swift | 169 ------- 4 files changed, 391 insertions(+), 330 deletions(-) create mode 100644 StockMate/StockMate/app/core/components/CartInfoCard.swift delete mode 100644 StockMate/StockMate/app/feature/orders/ui/OrderRequestView.swift diff --git a/StockMate/StockMate/app/core/components/CartInfoCard.swift b/StockMate/StockMate/app/core/components/CartInfoCard.swift new file mode 100644 index 0000000..d38e0f1 --- /dev/null +++ b/StockMate/StockMate/app/core/components/CartInfoCard.swift @@ -0,0 +1,86 @@ +// +// CartInfoCard.swift +// StockMate +// +// Created by Admin on 10/27/25. +// + +import SwiftUI + +struct CartInfoCard: View { + let item: CartItem + let quantity: Int + + var body: some View { + VStack(alignment: .leading, spacing: 6) { + Text(item.categoryName ?? "") + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(.black) + + Divider().frame(height: 0.2).background(Color.textGray2) + + HStack(alignment: .center, spacing: 12) { + AsyncImage(url: URL(string: item.image ?? "")) { image in + image.resizable().scaledToFit() + } placeholder: { + Color.gray.opacity(0.2) + } + .frame(width: 64, height: 64) + .cornerRadius(10) + + + VStack(alignment: .leading, spacing: 6) { + Text(item.brand ?? "") + .font(.system(size: 14, weight: .bold)) + .foregroundColor(.black) + .lineLimit(2) + + HStack{ + Text("\((item.trim ?? "")) / \((item.model ?? ""))") + .font(.system(size: 13)) + .foregroundColor(.gray) + .lineLimit(1) + + Spacer() + + Text("\((item.price ?? 0) * item.amount)원") + .font(.system(size: 13, weight: .semibold)) + .foregroundColor(.black) + .lineLimit(1) + + } + Text("\(item.price ?? 0)원 / \(item.amount)개") + .font(.system(size: 13)) + .foregroundColor(.gray) + .lineLimit(1) + } + } + } + .padding() + .background(Color.white) + .cornerRadius(14) +// .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) + } +} + +#Preview { + CartInfoCard( + item: CartItem( + cartItemId: 1, + partId: 101, + amount: 2, + partName: "실린더 어셈블리 브레이크 마스터", + categoryName: "엔진/미션", + brand: "실린더 어셈블리 브레이크 마스터", + model: "아반떼 MD", + trim: "중형", + price: 60000, + stock: 30, + image: "" + ), + quantity: 2 + ) + .previewLayout(.sizeThatFits) + .padding() + .background(Color.Light) +} diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift index acf2d76..b39a506 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift @@ -40,9 +40,8 @@ struct OrderCartView: View { } .background(Color.Light) - Button { - // TODO: 결제 처리 - } label: { + + NavigationLink(destination: OrderInfoView(cartViewModel: cartViewModel)) { Text("\(cartViewModel.cart?.totalPrice ?? 0)원 결제하기") .font(.system(size: 16, weight: .bold)) .foregroundColor(.white) @@ -50,6 +49,7 @@ struct OrderCartView: View { .frame(height: 60) .background(Color.Primary) } + } .background(Color.Light) .navigationTitle("장바구니 확인") diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift index 8fa7ed4..52d52ba 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift @@ -6,170 +6,314 @@ // import SwiftUI -// -//struct OrderItem: Identifiable { -// let id = UUID() -// let name: String -// let description: String -// let price: Int -// let quantity: Int -// let imageName: String -//} + +enum PaymentType: String { + case deposit = "DEPOSIT" + case card = "CARD" +} + +enum ShippingDateOption { + case today + case tomorrow + case specific(Date) +} + struct OrderInfoView: View { -// @State private var orderItems: [OrderItem] = [ -// OrderItem(name: "아반떼MD", description: "실린더 어셈블리·브레이크 마스터", price: 30000, quantity: 2, imageName: "carpart"), -// OrderItem(name: "아반떼MD", description: "실린더 어셈블리·브레이크 마스터", price: 30000, quantity: 2, imageName: "carpart") -// ] -// -// @State private var selectedPayment: String = "법인 카드" -// @State private var requestMessage: String = "" -// @State private var showResult = false -// -// private var totalPrice: Int { -// orderItems.reduce(0) { $0 + $1.price * $1.quantity } -// } + @ObservedObject var cartViewModel: CartViewModel + + @State private var paymentType: PaymentType = .deposit + @State private var shippingDateOption: ShippingDateOption = .today + @State private var specificDate = Date() // datePicker용 + @State private var requestMessage: String = "" + + @State private var showResult = false + var body: some View { -// NavigationStack { -// ScrollView { -// VStack(alignment: .leading, spacing: 20) { -// -// // 배송 정보 -// Section { -// VStack(alignment: .leading, spacing: 8) { -// Text("배송 정보") -// .font(.headline) -// Text("홍길동") -// Text("서울특별시 강남구 테헤란로114길") -// Text("010-1111-2222") -// .foregroundColor(.gray) -// .font(.subheadline) -// TextField("요청사항을 입력하세요", text: $requestMessage, axis: .vertical) -// .padding(10) -// .frame(height: 70) -// .background(Color(.systemGray6)) -// .cornerRadius(10) -// } -// .padding() -// .background(Color.white) -// .cornerRadius(16) -// .shadow(color: .black.opacity(0.05), radius: 3) -// } -// -// // 주문 목록 -// Section { -// VStack(alignment: .leading, spacing: 12) { -// Text("주문 목록 (\(orderItems.count))") -// .font(.headline) -// ForEach(orderItems) { item in -// HStack(alignment: .top, spacing: 12) { -// Image(systemName: "cube.box.fill") -// .resizable() -// .frame(width: 50, height: 50) -// .foregroundColor(.gray) -// VStack(alignment: .leading, spacing: 4) { -// Text(item.name) -// .font(.subheadline) -// .bold() -// Text(item.description) -// .font(.caption) -// .foregroundColor(.gray) -// Text("\(item.price * item.quantity)원") -// .font(.subheadline) -// .bold() -// .frame(maxWidth: .infinity, alignment: .trailing) -// } -// } -// Divider() -// } -// } -// .padding() -// .background(Color.white) -// .cornerRadius(16) -// .shadow(color: .black.opacity(0.05), radius: 3) -// } -// -// // 결제 수단 -// Section { -// VStack(alignment: .leading, spacing: 8) { -// Text("결제 수단") -// .font(.headline) -// RadioButtonGroup(items: ["법인 카드(잔액 ₩1,200,000)", "본사 계좌이체"], selected: $selectedPayment) -// } -// .padding() -// .background(Color.white) -// .cornerRadius(16) -// .shadow(color: .black.opacity(0.05), radius: 3) -// } -// -// // 결제 금액 -// VStack(alignment: .leading) { -// HStack { -// Text("결제 금액") -// .font(.headline) -// Spacer() -// Text("\(totalPrice)원") -// .font(.headline) -// .foregroundColor(.blue) -// } -// } -// .padding(.horizontal) -// } -// .padding() -// } -// .background(Color(.systemGray6)) -// .navigationTitle("발주 요청") -// .toolbar { -// ToolbarItem(placement: .navigationBarLeading) { -// Button(action: { /* 뒤로가기 동작 */ }) { -// Image(systemName: "chevron.left") -// .foregroundColor(.black) -// } -// } -// } -// .safeAreaInset(edge: .bottom) { -// Button { -// // 결제 API 연동 전용 더미 로직 -// withAnimation { -// showResult = true -// } -// } label: { -// Text("결제하기") -// .font(.headline) -// .frame(maxWidth: .infinity) -// .padding() -// .foregroundColor(.white) -// .background(Color.blue) -// .cornerRadius(12) -// .padding([.horizontal, .bottom]) -// } -// } -// .navigationDestination(isPresented: $showResult) { -// OrderResultView() -// } -// } + VStack(spacing: 0) { + ScrollView { + VStack(alignment: .leading, spacing: 10) { + + // 배송 정보 + Text("배송 정보") + .font(.headline) + .padding(.leading ,5) + VStack(alignment: .leading, spacing: 8) { + Text("홍길동") + .font(.system(size: 15, weight: .medium)) + + Text("서울특별시 강남구 테헤란로114길") + .font(.system(size: 14, weight: .medium)) + .foregroundColor(.textGray1) + + Text("010-1111-2222") + .font(.system(size: 14, weight: .regular)) + .foregroundColor(.textGray1) + + Text("요청사항") + .font(.system(size: 14, weight: .medium)) + .padding(.top, 5) + + TextField("요청사항을 입력하세요", text: $requestMessage, axis: .vertical) + .font(.system(size: 14, weight: .regular)) + .padding(10) + .frame(height: 70) + .background( + RoundedRectangle(cornerRadius: 10) + .fill(Color.white) + ) + .overlay( + RoundedRectangle(cornerRadius: 10) + .stroke(Color(.systemGray4), lineWidth: 1) + ) + } + .padding() + .background(Color.white) + .cornerRadius(16) + + Text("주문 목록 ") + .font(.headline) + .padding(.top) + .padding(.leading ,5) + // 주문 목록 + Section { + LazyVStack(spacing: 8) { + ForEach(cartViewModel.items) { cartItem in + CartInfoCard( + item: cartItem, + quantity: cartItem.amount + ) + .padding(.horizontal, 5) + } + } + } + .background(Color.white) + .cornerRadius(16) + + Text("결제 수단") + .font(.headline) + .padding(.top) + .padding(.leading ,5) + // 결제 수단 + VStack(alignment: .leading) { + HStack{ + RadioButtonRow( + title: "예치금 (잔액 ₩1,200,000)", + selected: paymentType == .deposit + ) { + paymentType = .deposit + } + .padding(.bottom, 5) + Spacer() + } + + HStack{ + RadioButtonRow( + title: "직접 결제", + selected: paymentType == .card + ) { + paymentType = .card + } + Spacer() + } + + } + .padding() + .frame(maxWidth: .infinity) + .background(Color.white) + .cornerRadius(16) + + Text("배송 요청일") + .font(.headline) + .padding(.top) + .padding(.leading ,5) + + // 배송 요청일 + VStack(alignment: .leading, spacing: 5) { + HStack{ + RadioButtonRow( + title: "오늘", + selected: { + if case .today = shippingDateOption { + return true + } + return false + }() + ) { + shippingDateOption = .today + } + .frame(height: 35) + + Spacer() + } + + HStack{ + RadioButtonRow( + title: "내일", + selected: { + if case .tomorrow = shippingDateOption { + return true + } + return false + }() + ) { + shippingDateOption = .tomorrow + } + .frame(height: 35) + + Spacer() + } + + HStack{ + RadioButtonRow( + title: "날짜 선택", + selected: { + if case .specific(_) = shippingDateOption { + return true + } + return false + }() + ) { + shippingDateOption = .specific(specificDate) + } + + + if case .specific(_) = shippingDateOption { + CustomDatePickerField(date: Binding( + get: { specificDate }, + set: { newValue in + specificDate = newValue + shippingDateOption = .specific(newValue) + } + )) + + } + + Spacer() + } + .frame(height: 35) + + } + .padding() + .frame(maxWidth: .infinity) + .background(Color.white) + .cornerRadius(16) + .padding(.bottom, 8) + + // 총 결제 금액 + HStack { + Text("결제금액") + .font(.system(size: 16, weight: .semibold)) + + Spacer() + + Text("\(cartViewModel.cart?.totalPrice ?? 0)원") + .font(.system(size: 16, weight: .bold)) + .foregroundColor(.Primary) + } + .padding() + .background(Color.white) + .cornerRadius(10) + } + + + } + .padding(.horizontal) + .padding(.top) + + // 결제하기 버튼 + Button { + print("결제 진행!") + } label: { + Text("결제하기") + .font(.system(size: 16, weight: .bold)) + .foregroundColor(.white) + .frame(maxWidth: .infinity) + .frame(height: 70) + .background(Color.Primary) + } + } + .background(Color.Light) + .navigationTitle("주문/결제") + .navigationBarTitleDisplayMode(.inline) + .task { + await cartViewModel.fetchCart() + } + .edgesIgnoringSafeArea(.bottom) + + } +} + +struct RadioButtonRow: View { + let title: String + var selected: Bool + var action: () -> Void + + var body: some View { + HStack { + Image(systemName: selected ? "circle.inset.filled" : "circle") + .foregroundColor(selected ? .Primary : .gray) + Text(title) +// Spacer() + } + .onTapGesture { action() } } } -//struct RadioButtonGroup: View { -// let items: [String] -// @Binding var selected: String -// -// var body: some View { -// VStack(alignment: .leading, spacing: 8) { -// ForEach(items, id: \.self) { item in -// HStack { -// Image(systemName: selected == item ? "circle.inset.filled" : "circle") -// .foregroundColor(selected == item ? .blue : .gray) -// .onTapGesture { selected = item } -// Text(item) -// } -// } -// } -// } -//} -#Preview { - OrderInfoView() +struct CustomDatePickerField: View { + @Binding var date: Date + @State private var showPicker: Bool = false + + // 날짜 포맷 변환용 Formatter + private var dateFormatter: DateFormatter { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd" + return formatter + } + + var body: some View { + Button { + showPicker.toggle() + } label: { + HStack { + Text(dateFormatter.string(from: date)) + .font(.system(size: 14)) + .foregroundColor(.black) + + Spacer() + + Image(systemName: "calendar") + .foregroundColor(.gray) + } + .padding(10) + .frame(width: 140, height: 30) + .background( + RoundedRectangle(cornerRadius: 4) + .stroke(Color(.systemGray4)) + ) + } + .sheet(isPresented: $showPicker) { + VStack { + DatePicker( + "", + selection: $date, + in: Date()..., + displayedComponents: [.date] + ) + .datePickerStyle(.graphical) + .padding() + + Button("완료") { + showPicker = false + } + .font(.headline) + .padding() + .frame(maxWidth: .infinity) + } + .presentationDetents([.medium]) + } + } } + diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderRequestView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderRequestView.swift deleted file mode 100644 index 12f9b6e..0000000 --- a/StockMate/StockMate/app/feature/orders/ui/OrderRequestView.swift +++ /dev/null @@ -1,169 +0,0 @@ -// -// OrderRequestView.swift -// StockMate -// -// Created by Admin on 10/20/25. -// - -import SwiftUI - -struct OrderRequestView: View { -// @StateObject private var inventoryViewModel = InventoryViewModel() -// @State private var showScrollToTopButton = false -// @Namespace private var topID -// -// @State private var cartItems: [InventoryItem: Int] = [:] // [품목: 수량] -// -// var totalPrice: Int { -// cartItems.reduce(0) { $0 + ($1.key.price * $1.value) } -// } - - var body: some View { -// NavigationStack { -// ScrollViewReader { proxy in -// ZStack { -// ScrollView { -// VStack(spacing: 0) { -// GeometryReader { geo in -// Color.clear -// .onChange(of: geo.frame(in: .global).minY) { newValue in -// withAnimation(.easeInOut(duration: 0.25)) { -// showScrollToTopButton = newValue < -150 -// } -// } -// } -// .frame(height: 0) -// .id(topID) -// -// // 상단 타이틀 -// HStack { -// Text("발주 요청") -// .font(.title3) -// .bold() -// Spacer() -// Image(systemName: "cart") -// .font(.system(size: 20)) -// .foregroundColor(.black) -// } -// .padding(.horizontal, 25) -// .padding(.top, 10) -// -// // 검색창 -// HStack { -// Image(systemName: "magnifyingglass") -// .foregroundColor(.gray) -// TextField("부품을 검색하세요.", text: .constant("")) -// } -// .padding(.horizontal) -// .padding(.vertical, 12) -// .background( -// RoundedRectangle(cornerRadius: 14) -// .stroke(Color.gray.opacity(0.3), lineWidth: 1) -// ) -// .padding(.horizontal, 20) -// .padding(.top, 10) -// -// // 부족 재고 리스트 -// VStack(alignment: .leading, spacing: 12) { -// Text("부족 재고") -// .font(.system(size: 18, weight: .semibold)) -// .foregroundColor(.black) -// .padding(.horizontal, 25) -// .padding(.top, 15) -// -// LazyVStack(spacing: 14) { -// ForEach(inventoryViewModel.underLimitItems) { item in -// OrderRequestCardView( -// item: item, -// quantity: cartItems[item] ?? 0, -// onAdd: { -// cartItems[item, default: 0] += 1 -// }, -// onRemove: { -// if let current = cartItems[item], current > 0 { -// cartItems[item] = current - 1 -// } -// }, -// onCartAdd: { -// cartItems[item, default: 0] += 1 -// } -// ) -// .padding(.horizontal, 20) -// .onAppear { -// if item.id == inventoryViewModel.underLimitItems.last?.id { -// Task { -// await inventoryViewModel.loadUnderLimitList() -// } -// } -// } -// } -// -// if inventoryViewModel.isLoading { -// ProgressView().padding(.vertical, 20) -// } -// } -// .padding(.bottom, 100) -// } -// } -// } -// -// // ✅ 하단 장바구니 버튼 -// VStack { -// Spacer() -// NavigationLink(destination: OrderCartView()) { -// HStack { -// Circle() -// .fill(Color.white) -// .frame(width: 20, height: 20) -// .overlay( -// Text("\(cartItems.count)") -// .font(.system(size: 11, weight: .bold)) -// .foregroundColor(.Primary) -// ) -// -// Text("장바구니 보기") -// .font(.system(size: 16, weight: .bold)) -// .foregroundColor(.white) -// -// Spacer() -// -// Text("\(totalPrice)원") -// .font(.system(size: 16, weight: .semibold)) -// .foregroundColor(.white) -// } -// .padding(.horizontal, 30) -// .frame(height: 60) -// .background(Color.Primary) -// // 위쪽 두 모서리만 둥글게 -// .clipShape(RoundedCorner(radius: 16, corners: [.topLeft, .topRight])) -// } -// } -// } -// .background(Color.Light) -// .edgesIgnoringSafeArea(.bottom) // 탭에 딱 맞닿게 -// .task { -// await inventoryViewModel.loadUnderLimitList(reset: true) -// } -// } -// } - } -} - -//// ✅ 특정 코너만 둥글게 처리할 수 있게 하는 Shape -//struct RoundedCorner: Shape { -// var radius: CGFloat = 16 -// var corners: UIRectCorner = .allCorners -// -// func path(in rect: CGRect) -> Path { -// let path = UIBezierPath( -// roundedRect: rect, -// byRoundingCorners: corners, -// cornerRadii: CGSize(width: radius, height: radius) -// ) -// return Path(path.cgPath) -// } -//} - -#Preview { - OrderRequestView() -} From 126e853706fb20c26622c1bd5665ad8322223237 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Tue, 28 Oct 2025 09:38:40 +0900 Subject: [PATCH 29/85] =?UTF-8?q?[FEAT]=20=EC=A3=BC=EB=AC=B8=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20API=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/feature/orders/data/OrderApi.swift | 39 +++- .../orders/data/OrderRepositoryImpl.swift | 38 ++++ .../domain/OrderRepositoryProtocol.swift | 3 + .../app/feature/orders/ui/OrderInfoView.swift | 209 ++++++++++++------ .../orders/viewmodel/OrderViewModel.swift | 42 ++++ 5 files changed, 266 insertions(+), 65 deletions(-) diff --git a/StockMate/StockMate/app/feature/orders/data/OrderApi.swift b/StockMate/StockMate/app/feature/orders/data/OrderApi.swift index 6509385..46c91ba 100644 --- a/StockMate/StockMate/app/feature/orders/data/OrderApi.swift +++ b/StockMate/StockMate/app/feature/orders/data/OrderApi.swift @@ -88,10 +88,29 @@ struct OrderPartDetail: Decodable { let amount: Int } + +// MARK: - 주문 생성 Request +struct OrderRequest: Encodable { + let orderItems: [OrderItems] + let requestedShippingDate: String + let paymentType: String + let etc: String +} + +struct OrderItems: Encodable { + let partId: Int + let amount: Int +} + + + + + + // MARK: - API Call enum OrderApi { - // ✅ 내 주문 목록 조회 + // ✅ 내 주문 리스트 조회 API static func getMyOrderList( status: String? = nil, startDate: String? = nil, @@ -110,7 +129,7 @@ enum OrderApi { if let endDate = endDate, !endDate.isEmpty { url += "&endDate=\(endDate.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")" } - + return ApiClient.shared.request(url, method: .get) } @@ -120,5 +139,19 @@ enum OrderApi { let url = ApiClient.baseURL + "api/v1/order/detail?orderId=\(orderId)" return ApiClient.shared.request(url, method: .get) } - + + + // ✅ 주문 생성 API + static func createOrder(_ requestBody: OrderRequest) -> DataRequest { + let url = ApiClient.baseURL + "api/v1/order" + return ApiClient.shared.request( + url, + method: .post, + parameters: requestBody, + encoder: JSONParameterEncoder.default + ) + } + + + } diff --git a/StockMate/StockMate/app/feature/orders/data/OrderRepositoryImpl.swift b/StockMate/StockMate/app/feature/orders/data/OrderRepositoryImpl.swift index 4a987d6..8307c62 100644 --- a/StockMate/StockMate/app/feature/orders/data/OrderRepositoryImpl.swift +++ b/StockMate/StockMate/app/feature/orders/data/OrderRepositoryImpl.swift @@ -58,5 +58,43 @@ final class OrderRepositoryImpl: OrderRepositoryProtocol { return .failure(error) } } + +// func createOrder(request: OrderRequest) async -> AppResult { +// let request = OrderApi.createOrder(request) +// +// let result = await safeApi(request, decodeTo: ApiResponse.self) +// +// switch result { +// case .success(let response): +// if let data = response.data { +// return .success(data) +// } else { +// return .failure(.init(code: response.status, message: response.message, underlying: nil)) +// } +// case .failure(let error): +// return .failure(error) +// } +// } + func createOrder(request: OrderRequest) async -> AppResult { + let request = OrderApi.createOrder(request) + let result = await safeApi(request, decodeTo: ApiResponse.self) + + switch result { + case .success(let response): + return .success(response.data ?? "success") + case .failure(let error): + return .failure(error) + } +// switch result { +// case .success(let response): +// if let data = response.data { +// return .success(data) +// } else { +// return .failure(.init(code: -1, message: "주문 결과가 비었습니다.", underlying: nil)) +// } +// case .failure(let error): +// return .failure(error) +// } + } } diff --git a/StockMate/StockMate/app/feature/orders/domain/OrderRepositoryProtocol.swift b/StockMate/StockMate/app/feature/orders/domain/OrderRepositoryProtocol.swift index c981daf..dd1cfec 100644 --- a/StockMate/StockMate/app/feature/orders/domain/OrderRepositoryProtocol.swift +++ b/StockMate/StockMate/app/feature/orders/domain/OrderRepositoryProtocol.swift @@ -20,4 +20,7 @@ protocol OrderRepositoryProtocol { func fetchOrderDetail( orderId: Int ) async -> AppResult + + // 주문 생성 + func createOrder(request: OrderRequest) async -> AppResult } diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift index 52d52ba..bd989fe 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift @@ -22,13 +22,36 @@ enum ShippingDateOption { struct OrderInfoView: View { @ObservedObject var cartViewModel: CartViewModel + @StateObject var orderViewModel = OrderViewModel() + @State private var paymentType: PaymentType = .deposit @State private var shippingDateOption: ShippingDateOption = .today @State private var specificDate = Date() // datePicker용 @State private var requestMessage: String = "" @State private var showResult = false - + @State private var navigateToSuccessPage = false + + func formattedShippingDate() -> String { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd" + formatter.locale = Locale(identifier: "ko_KR") + + switch shippingDateOption { + case .today: + return formatter.string(from: Date()) + case .tomorrow: + return formatter.string(from: Calendar.current.date(byAdding: .day, value: 1, to: Date())!) + case .specific(let date): + return formatter.string(from: date) + } + } + + func makeOrderItems() -> [OrderItems] { + return cartViewModel.items.map { + OrderItems(partId: $0.id, amount: $0.amount) + } + } var body: some View { VStack(spacing: 0) { @@ -36,60 +59,71 @@ struct OrderInfoView: View { VStack(alignment: .leading, spacing: 10) { // 배송 정보 - Text("배송 정보") - .font(.headline) - .padding(.leading ,5) - VStack(alignment: .leading, spacing: 8) { - Text("홍길동") - .font(.system(size: 15, weight: .medium)) + Text("배송 정보") + .font(.headline) + .padding(.leading ,5) + VStack(alignment: .leading, spacing: 8) { + Text("홍길동") + .font(.system(size: 15, weight: .medium)) - Text("서울특별시 강남구 테헤란로114길") - .font(.system(size: 14, weight: .medium)) - .foregroundColor(.textGray1) + Text("서울특별시 강남구 테헤란로114길") + .font(.system(size: 14, weight: .medium)) + .foregroundColor(.textGray1) - Text("010-1111-2222") - .font(.system(size: 14, weight: .regular)) - .foregroundColor(.textGray1) + Text("010-1111-2222") + .font(.system(size: 14, weight: .regular)) + .foregroundColor(.textGray1) - Text("요청사항") - .font(.system(size: 14, weight: .medium)) - .padding(.top, 5) - - TextField("요청사항을 입력하세요", text: $requestMessage, axis: .vertical) - .font(.system(size: 14, weight: .regular)) - .padding(10) - .frame(height: 70) - .background( - RoundedRectangle(cornerRadius: 10) - .fill(Color.white) - ) - .overlay( - RoundedRectangle(cornerRadius: 10) - .stroke(Color(.systemGray4), lineWidth: 1) - ) + Text("요청사항") + .font(.system(size: 14, weight: .medium)) + .padding(.top, 5) + + ZStack(alignment: .topLeading) { + if requestMessage.isEmpty { + Text("요청사항을 입력하세요") + .foregroundColor(.gray) + .font(.system(size: 14)) + .padding(.top, 12) + .padding(.leading, 10) + } + + TextEditor(text: $requestMessage) + .font(.system(size: 14)) + .padding(.top, 4) + .padding(.leading, 6) + .scrollContentBackground(.hidden) + .background(Color.clear) } - .padding() + .frame(height: 70) .background(Color.white) - .cornerRadius(16) + .overlay( + RoundedRectangle(cornerRadius: 10) + .stroke(Color(.systemGray4), lineWidth: 1) + ) + + } + .padding() + .background(Color.white) + .cornerRadius(16) - Text("주문 목록 ") - .font(.headline) - .padding(.top) - .padding(.leading ,5) - // 주문 목록 - Section { - LazyVStack(spacing: 8) { - ForEach(cartViewModel.items) { cartItem in - CartInfoCard( - item: cartItem, - quantity: cartItem.amount - ) - .padding(.horizontal, 5) - } + Text("주문 목록 ") + .font(.headline) + .padding(.top) + .padding(.leading ,5) + // 주문 목록 + Section { + LazyVStack(spacing: 8) { + ForEach(cartViewModel.items) { cartItem in + CartInfoCard( + item: cartItem, + quantity: cartItem.amount + ) + .padding(.horizontal, 5) } } - .background(Color.white) - .cornerRadius(16) + } + .background(Color.white) + .cornerRadius(16) Text("결제 수단") .font(.headline) @@ -187,9 +221,7 @@ struct OrderInfoView: View { shippingDateOption = .specific(newValue) } )) - } - Spacer() } .frame(height: 35) @@ -216,15 +248,27 @@ struct OrderInfoView: View { .background(Color.white) .cornerRadius(10) } - - } - .padding(.horizontal) - .padding(.top) + } + .padding(.horizontal) + .padding(.top) - // 결제하기 버튼 + // 결제하기 버튼 + VStack { + // 결제 버튼 Button { print("결제 진행!") + + Task { + let orderRequest = OrderRequest( + orderItems: makeOrderItems(), + requestedShippingDate: formattedShippingDate(), + paymentType: paymentType.rawValue, + etc: requestMessage + ) + + await orderViewModel.createOrder(request: orderRequest) + } } label: { Text("결제하기") .font(.system(size: 16, weight: .bold)) @@ -233,16 +277,57 @@ struct OrderInfoView: View { .frame(height: 70) .background(Color.Primary) } + + // ✅ 성공 시 이동을 위한 NavigationLink + NavigationLink( + destination: OrderResultView(), + isActive: $navigateToSuccessPage + ) { EmptyView() } } - .background(Color.Light) - .navigationTitle("주문/결제") - .navigationBarTitleDisplayMode(.inline) - .task { - await cartViewModel.fetchCart() + // ✅ 주문 성공 감지 후 처리 + .onChange(of: orderViewModel.isOrderSuccess) { success in + if success { + Task { + await cartViewModel.clearCart() // 장바구니 비우기 + } + navigateToSuccessPage = true // 페이지 이동 + } } - .edgesIgnoringSafeArea(.bottom) - + +// Button { +// print("결제 진행!") +// Task { +// let orderRequest = OrderRequest( +// orderItems: makeOrderItems(), +// requestedShippingDate: formattedShippingDate(), +// paymentType: paymentType.rawValue, +// etc: requestMessage +// ) +// +// let result = await orderViewModel.createOrder(request: orderRequest) +// if result { +// showResult = true +// } +// } +// } label: { +// Text("결제하기") +// .font(.system(size: 16, weight: .bold)) +// .foregroundColor(.white) +// .frame(maxWidth: .infinity) +// .frame(height: 70) +// .background(Color.Primary) +// } + + } + .background(Color.Light) + .navigationTitle("주문/결제") + .navigationBarTitleDisplayMode(.inline) + .task { + await cartViewModel.fetchCart() } + .edgesIgnoringSafeArea(.bottom) + + } } struct RadioButtonRow: View { @@ -255,7 +340,7 @@ struct RadioButtonRow: View { Image(systemName: selected ? "circle.inset.filled" : "circle") .foregroundColor(selected ? .Primary : .gray) Text(title) -// Spacer() + // Spacer() } .onTapGesture { action() } } diff --git a/StockMate/StockMate/app/feature/orders/viewmodel/OrderViewModel.swift b/StockMate/StockMate/app/feature/orders/viewmodel/OrderViewModel.swift index fdbd57f..f957640 100644 --- a/StockMate/StockMate/app/feature/orders/viewmodel/OrderViewModel.swift +++ b/StockMate/StockMate/app/feature/orders/viewmodel/OrderViewModel.swift @@ -13,6 +13,8 @@ final class OrderViewModel: ObservableObject { @Published var isLoading = false @Published var errorMessage: String? + @Published var isOrderSuccess: Bool = false + private let repository: OrderRepositoryProtocol init(repository: OrderRepositoryProtocol = OrderRepositoryImpl()) { @@ -44,4 +46,44 @@ final class OrderViewModel: ObservableObject { errorMessage = error.message } } + + // 주문 생성 + func createOrder(request: OrderRequest) async -> Bool { + isLoading = true + defer { isLoading = false } + + let result = await repository.createOrder(request: request) + + switch result { + case .success(_): + isOrderSuccess = true + return true + case .failure(let error): + errorMessage = error.message + print("❌ 주문 실패:", error.message) + return false + } + } +// func createOrder( +// items: [OrderItems], +// requestedDate: String, +// payment: String, +// etc: String +// ) async { +// let requestBody = OrderRequest( +// orderItems: items, +// requestedShippingDate: requestedDate, +// paymentType: payment, +// etc: etc +// ) +// +// let result = await repository.createOrder(request: requestBody) +// +// switch result { +// case .success(let orderNumber): +// print("✅ 주문 성공:", orderNumber) +// case .failure(let error): +// print("❌ 주문 실패:", error.message) +// } +// } } From f50860e8f8fc2ded5fd129ef5c848ef4a8be32e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Tue, 28 Oct 2025 12:20:09 +0900 Subject: [PATCH 30/85] =?UTF-8?q?[FEAT]=20=EC=A3=BC=EB=AC=B8=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20API=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/feature/orders/data/OrderApi.swift | 22 +- .../orders/data/OrderRepositoryImpl.swift | 49 +- .../domain/OrderRepositoryProtocol.swift | 6 +- .../feature/orders/ui/OrderDetailView.swift | 9 +- .../app/feature/orders/ui/OrderInfoView.swift | 477 ++++++++---------- .../app/feature/orders/ui/OrderListView.swift | 8 +- .../orders/viewmodel/OrderViewModel.swift | 86 ++-- 7 files changed, 336 insertions(+), 321 deletions(-) diff --git a/StockMate/StockMate/app/feature/orders/data/OrderApi.swift b/StockMate/StockMate/app/feature/orders/data/OrderApi.swift index 46c91ba..c8ca332 100644 --- a/StockMate/StockMate/app/feature/orders/data/OrderApi.swift +++ b/StockMate/StockMate/app/feature/orders/data/OrderApi.swift @@ -102,9 +102,13 @@ struct OrderItems: Encodable { let amount: Int } - - - +// Response +struct OrderCreateResponseData: Decodable { + let orderId: Int + let orderNumber: String + let totalPrice: Int + let orderStatus: String +} // MARK: - API Call @@ -151,7 +155,13 @@ enum OrderApi { encoder: JSONParameterEncoder.default ) } - - - + + // ✅ 주문 취소 API + static func cancelOrder(orderId: Int) -> DataRequest { + let url = ApiClient.baseURL + "api/v1/order/\(orderId)/cancel" + print("🚀 CancelOrder URL:", url) + return ApiClient.shared.request(url, method: .put) + .validate() // ✅ 서버 상태코드 확인 + } + } diff --git a/StockMate/StockMate/app/feature/orders/data/OrderRepositoryImpl.swift b/StockMate/StockMate/app/feature/orders/data/OrderRepositoryImpl.swift index 8307c62..fdffb34 100644 --- a/StockMate/StockMate/app/feature/orders/data/OrderRepositoryImpl.swift +++ b/StockMate/StockMate/app/feature/orders/data/OrderRepositoryImpl.swift @@ -75,26 +75,49 @@ final class OrderRepositoryImpl: OrderRepositoryProtocol { // return .failure(error) // } // } - func createOrder(request: OrderRequest) async -> AppResult { + +// func createOrder(request: OrderRequest) async -> AppResult { +//// func createOrder(request: OrderRequest) async -> AppResult { +// let request = OrderApi.createOrder(request) +// let result = await safeApi(request, decodeTo: ApiResponse.self) +// +// switch result { +// case .success(let response): +// return .success(response.data ?? "success") +// case .failure(let error): +// return .failure(error) +// } +// } + + func createOrder(request: OrderRequest) async -> AppResult { let request = OrderApi.createOrder(request) + + let result = await safeApi(request, decodeTo: ApiResponse.self) + + switch result { + case .success(let response): + if let data = response.data { + return .success(data) + } else { + return .failure(.init(code: response.status, message: response.message, underlying: nil)) + } + case .failure(let error): + return .failure(error) + } + } + + + func cancelOrder(orderId: Int) async -> AppResult { + let request = OrderApi.cancelOrder(orderId: orderId) let result = await safeApi(request, decodeTo: ApiResponse.self) - + switch result { case .success(let response): + print("✅ 취소 성공:", response) return .success(response.data ?? "success") case .failure(let error): return .failure(error) } - -// switch result { -// case .success(let response): -// if let data = response.data { -// return .success(data) -// } else { -// return .failure(.init(code: -1, message: "주문 결과가 비었습니다.", underlying: nil)) -// } -// case .failure(let error): -// return .failure(error) -// } } + } diff --git a/StockMate/StockMate/app/feature/orders/domain/OrderRepositoryProtocol.swift b/StockMate/StockMate/app/feature/orders/domain/OrderRepositoryProtocol.swift index dd1cfec..269b8dd 100644 --- a/StockMate/StockMate/app/feature/orders/domain/OrderRepositoryProtocol.swift +++ b/StockMate/StockMate/app/feature/orders/domain/OrderRepositoryProtocol.swift @@ -22,5 +22,9 @@ protocol OrderRepositoryProtocol { ) async -> AppResult // 주문 생성 - func createOrder(request: OrderRequest) async -> AppResult +// func createOrder(request: OrderRequest) async -> AppResult + func createOrder(request: OrderRequest) async -> AppResult + + + func cancelOrder(orderId: Int) async -> AppResult } diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift index 67ce6b8..6ecc766 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift @@ -9,6 +9,7 @@ import SwiftUI struct OrderDetailView: View { let orderId: Int + @ObservedObject var orderViewModel: OrderViewModel @StateObject private var viewModel = OrderDetailViewModel() var body: some View { @@ -147,7 +148,13 @@ struct OrderDetailView: View { } // 오른쪽: 주문 취소 - Button(action: {}) { + Button(action: { + // 주문취소 처리 + Task { + await orderViewModel.cancelOrder(orderId: orderId) + } + + }) { Text("주문 취소") .font(.system(size: 15, weight: .semibold)) .frame(maxWidth: .infinity) diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift index bd989fe..d813947 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift @@ -4,7 +4,6 @@ // // Created by Admin on 10/20/25. // - import SwiftUI enum PaymentType: String { @@ -18,20 +17,27 @@ enum ShippingDateOption { case specific(Date) } - struct OrderInfoView: View { @ObservedObject var cartViewModel: CartViewModel - @StateObject var orderViewModel = OrderViewModel() @State private var paymentType: PaymentType = .deposit @State private var shippingDateOption: ShippingDateOption = .today - @State private var specificDate = Date() // datePicker용 + @State private var specificDate = Date() @State private var requestMessage: String = "" - - @State private var showResult = false + @State private var navigateToSuccessPage = false + private var destinationView: some View { + Group { + if let id = orderViewModel.createdOrderId { + OrderDetailView(orderId: id, orderViewModel: orderViewModel) + } else { + EmptyView() + } + } + } + func formattedShippingDate() -> String { let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd" @@ -46,287 +52,224 @@ struct OrderInfoView: View { return formatter.string(from: date) } } - + func makeOrderItems() -> [OrderItems] { - return cartViewModel.items.map { - OrderItems(partId: $0.id, amount: $0.amount) - } - } + return cartViewModel.items.map { + OrderItems(partId: $0.id, amount: $0.amount) + } + } var body: some View { VStack(spacing: 0) { ScrollView { - VStack(alignment: .leading, spacing: 10) { - - // 배송 정보 - Text("배송 정보") - .font(.headline) - .padding(.leading ,5) - VStack(alignment: .leading, spacing: 8) { - Text("홍길동") - .font(.system(size: 15, weight: .medium)) - - Text("서울특별시 강남구 테헤란로114길") - .font(.system(size: 14, weight: .medium)) - .foregroundColor(.textGray1) - - Text("010-1111-2222") - .font(.system(size: 14, weight: .regular)) - .foregroundColor(.textGray1) - - Text("요청사항") - .font(.system(size: 14, weight: .medium)) - .padding(.top, 5) - - ZStack(alignment: .topLeading) { - if requestMessage.isEmpty { - Text("요청사항을 입력하세요") - .foregroundColor(.gray) - .font(.system(size: 14)) - .padding(.top, 12) - .padding(.leading, 10) - } - - TextEditor(text: $requestMessage) - .font(.system(size: 14)) - .padding(.top, 4) - .padding(.leading, 6) - .scrollContentBackground(.hidden) - .background(Color.clear) - } - .frame(height: 70) - .background(Color.white) - .overlay( - RoundedRectangle(cornerRadius: 10) - .stroke(Color(.systemGray4), lineWidth: 1) - ) - - } - .padding() - .background(Color.white) - .cornerRadius(16) - - Text("주문 목록 ") - .font(.headline) - .padding(.top) - .padding(.leading ,5) - // 주문 목록 - Section { - LazyVStack(spacing: 8) { - ForEach(cartViewModel.items) { cartItem in - CartInfoCard( - item: cartItem, - quantity: cartItem.amount - ) - .padding(.horizontal, 5) - } - } - } - .background(Color.white) - .cornerRadius(16) - - Text("결제 수단") - .font(.headline) - .padding(.top) - .padding(.leading ,5) - // 결제 수단 - VStack(alignment: .leading) { - HStack{ - RadioButtonRow( - title: "예치금 (잔액 ₩1,200,000)", - selected: paymentType == .deposit - ) { - paymentType = .deposit - } - .padding(.bottom, 5) - Spacer() - } - - HStack{ - RadioButtonRow( - title: "직접 결제", - selected: paymentType == .card - ) { - paymentType = .card - } - Spacer() - } + contentView + } + .padding(.horizontal) + .padding(.top) + + bottomOrderButton + } + .background(Color.Light) + .navigationTitle("주문/결제") + .navigationBarTitleDisplayMode(.inline) + .task { await cartViewModel.fetchCart() } + .edgesIgnoringSafeArea(.bottom) + .onChange(of: orderViewModel.isOrderSuccess) { success in + if success { + Task { + await cartViewModel.clearCart() + navigateToSuccessPage = true + } + } + } + } +} - } - .padding() - .frame(maxWidth: .infinity) - .background(Color.white) - .cornerRadius(16) - - Text("배송 요청일") - .font(.headline) - .padding(.top) - .padding(.leading ,5) +// MARK: - UI 구성 View +extension OrderInfoView { + + private var contentView: some View { + VStack(alignment: .leading, spacing: 10) { + shippingInfoSection + orderListSection + paymentSection + shippingDateSection + totalPriceSection + } + } + + private var shippingInfoSection: some View { + VStack(alignment: .leading, spacing: 8) { + Text("배송 정보") + .font(.headline) + + VStack(alignment: .leading, spacing: 8) { + Text("홍길동").font(.system(size: 15, weight: .medium)) + Text("서울특별시 강남구 테헤란로114길") + .font(.system(size: 14)) + .foregroundColor(.textGray1) + Text("010-1111-2222") + .font(.system(size: 14)) + .foregroundColor(.textGray1) - // 배송 요청일 - VStack(alignment: .leading, spacing: 5) { - HStack{ - RadioButtonRow( - title: "오늘", - selected: { - if case .today = shippingDateOption { - return true - } - return false - }() - ) { - shippingDateOption = .today - } - .frame(height: 35) - - Spacer() - } - - HStack{ - RadioButtonRow( - title: "내일", - selected: { - if case .tomorrow = shippingDateOption { - return true - } - return false - }() - ) { - shippingDateOption = .tomorrow - } - .frame(height: 35) - - Spacer() - } - - HStack{ - RadioButtonRow( - title: "날짜 선택", - selected: { - if case .specific(_) = shippingDateOption { - return true - } - return false - }() - ) { - shippingDateOption = .specific(specificDate) - } - - - if case .specific(_) = shippingDateOption { - CustomDatePickerField(date: Binding( - get: { specificDate }, - set: { newValue in - specificDate = newValue - shippingDateOption = .specific(newValue) - } - )) - } - Spacer() - } - .frame(height: 35) - + Text("요청사항") + .font(.system(size: 14, weight: .medium)) + .padding(.top, 5) + + ZStack(alignment: .topLeading) { + if requestMessage.isEmpty { + Text("요청사항을 입력하세요") + .foregroundColor(.gray) + .font(.system(size: 14)) + .padding(.top, 12) + .padding(.leading, 10) } - .padding() - .frame(maxWidth: .infinity) - .background(Color.white) - .cornerRadius(16) - .padding(.bottom, 8) - // 총 결제 금액 - HStack { - Text("결제금액") - .font(.system(size: 16, weight: .semibold)) - - Spacer() - - Text("\(cartViewModel.cart?.totalPrice ?? 0)원") - .font(.system(size: 16, weight: .bold)) - .foregroundColor(.Primary) - } - .padding() - .background(Color.white) - .cornerRadius(10) + TextEditor(text: $requestMessage) + .font(.system(size: 14)) + .padding(.top, 4) + .padding(.leading, 6) + .scrollContentBackground(.hidden) + .background(Color.clear) } - + .frame(height: 70) + .background(Color.white) + .overlay(RoundedRectangle(cornerRadius: 10) + .stroke(Color(.systemGray4), lineWidth: 1)) } - .padding(.horizontal) - .padding(.top) + .padding() + .background(Color.white) + .cornerRadius(16) + } + .padding(.leading, 5) + } + + private var orderListSection: some View { + VStack(alignment: .leading) { + Text("주문 목록") + .font(.headline) + .padding(.leading, 5) - // 결제하기 버튼 - VStack { - // 결제 버튼 - Button { - print("결제 진행!") - - Task { - let orderRequest = OrderRequest( - orderItems: makeOrderItems(), - requestedShippingDate: formattedShippingDate(), - paymentType: paymentType.rawValue, - etc: requestMessage - ) - - await orderViewModel.createOrder(request: orderRequest) + Section { + LazyVStack(spacing: 8) { + ForEach(cartViewModel.items) { cartItem in + CartInfoCard(item: cartItem, quantity: cartItem.amount) + .padding(.horizontal, 5) } - } label: { - Text("결제하기") - .font(.system(size: 16, weight: .bold)) - .foregroundColor(.white) - .frame(maxWidth: .infinity) - .frame(height: 70) - .background(Color.Primary) } - - // ✅ 성공 시 이동을 위한 NavigationLink - NavigationLink( - destination: OrderResultView(), - isActive: $navigateToSuccessPage - ) { EmptyView() } } - // ✅ 주문 성공 감지 후 처리 - .onChange(of: orderViewModel.isOrderSuccess) { success in - if success { - Task { - await cartViewModel.clearCart() // 장바구니 비우기 - } - navigateToSuccessPage = true // 페이지 이동 + .background(Color.white) + .cornerRadius(16) + } + } + + private var paymentSection: some View { + VStack(alignment: .leading) { + Text("결제 수단") + .font(.headline) + .padding(.leading, 5) + + VStack(alignment: .leading, spacing: 5) { + RadioButtonRow(title: "예치금 (잔액 ₩1,200,000)", selected: paymentType == .deposit) { + paymentType = .deposit + } + RadioButtonRow(title: "직접 결제", selected: paymentType == .card) { + paymentType = .card } } - -// Button { -// print("결제 진행!") -// Task { -// let orderRequest = OrderRequest( -// orderItems: makeOrderItems(), -// requestedShippingDate: formattedShippingDate(), -// paymentType: paymentType.rawValue, -// etc: requestMessage -// ) -// -// let result = await orderViewModel.createOrder(request: orderRequest) -// if result { -// showResult = true -// } -// } -// } label: { -// Text("결제하기") -// .font(.system(size: 16, weight: .bold)) -// .foregroundColor(.white) -// .frame(maxWidth: .infinity) -// .frame(height: 70) -// .background(Color.Primary) -// } + .padding() + .frame(maxWidth: .infinity) + .background(Color.white) + .cornerRadius(16) + } + } + + private var shippingDateSection: some View { + VStack(alignment: .leading) { + Text("배송 요청일") + .font(.headline) + .padding(.leading, 5) + VStack(alignment: .leading, spacing: 5) { + RadioButtonRow(title: "오늘", selected: { + if case .today = shippingDateOption { return true } + return false + }()) { + shippingDateOption = .today + } + + RadioButtonRow(title: "내일", selected: { + if case .tomorrow = shippingDateOption { return true } + return false + }()) { + shippingDateOption = .tomorrow + } + + HStack { + RadioButtonRow(title: "날짜 선택", selected: { + if case .specific(_) = shippingDateOption { return true } + return false + }()) { + shippingDateOption = .specific(specificDate) + } + + if case .specific(_) = shippingDateOption { + CustomDatePickerField(date: Binding { + specificDate + } set: { newValue in + specificDate = newValue + shippingDateOption = .specific(newValue) + }) + } + } + .frame(height: 35) + + } + .padding() + .background(Color.white) + .cornerRadius(16) } - .background(Color.Light) - .navigationTitle("주문/결제") - .navigationBarTitleDisplayMode(.inline) - .task { - await cartViewModel.fetchCart() + } + + private var totalPriceSection: some View { + HStack { + Text("결제금액") + .font(.system(size: 16, weight: .semibold)) + Spacer() + Text("\(cartViewModel.cart?.totalPrice ?? 0)원") + .font(.system(size: 16, weight: .bold)) + .foregroundColor(.Primary) + } + .padding() + .background(Color.white) + .cornerRadius(10) + } + + private var bottomOrderButton: some View { + VStack { + Button { + Task { + let orderRequest = OrderRequest( + orderItems: makeOrderItems(), + requestedShippingDate: formattedShippingDate(), + paymentType: paymentType.rawValue, + etc: requestMessage + ) + await orderViewModel.createOrder(request: orderRequest) + } + } label: { + Text("결제하기") + .font(.system(size: 16, weight: .bold)) + .foregroundColor(.white) + .frame(maxWidth: .infinity) + .frame(height: 70) + .background(Color.Primary) + } + + NavigationLink(destination: destinationView, isActive: $navigateToSuccessPage) { + EmptyView() + } } - .edgesIgnoringSafeArea(.bottom) - } } diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift index 893218e..0545895 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift @@ -42,7 +42,7 @@ struct OrderListView: View { .padding(.top) ForEach(orders) { order in - OrderListCardView(order: order) + OrderListCardView(order: order, orderViewModel: orderViewModel) } } } @@ -70,6 +70,7 @@ struct OrderListView: View { struct OrderListCardView: View { let order: OrderResponseItem + @ObservedObject var orderViewModel: OrderViewModel var body: some View { VStack(alignment: .leading, spacing: 7) { @@ -79,7 +80,7 @@ struct OrderListCardView: View { .font(.caption) .foregroundColor(.gray) Spacer() - NavigationLink(destination: OrderDetailView(orderId: order.id)) { + NavigationLink(destination: OrderDetailView(orderId: order.id, orderViewModel: orderViewModel)) { Text("주문 상세 >") .font(.caption) .foregroundColor(.gray) @@ -130,6 +131,9 @@ struct OrderListCardView: View { if order.orderStatus == "ORDER_COMPLETED" { Button(action: { // 주문취소 처리 + Task { + await orderViewModel.cancelOrder(orderId: order.id) + } }) { Text("주문 취소") .font(.system(size: 13, weight: .semibold)) diff --git a/StockMate/StockMate/app/feature/orders/viewmodel/OrderViewModel.swift b/StockMate/StockMate/app/feature/orders/viewmodel/OrderViewModel.swift index f957640..b80fd15 100644 --- a/StockMate/StockMate/app/feature/orders/viewmodel/OrderViewModel.swift +++ b/StockMate/StockMate/app/feature/orders/viewmodel/OrderViewModel.swift @@ -14,6 +14,9 @@ final class OrderViewModel: ObservableObject { @Published var errorMessage: String? @Published var isOrderSuccess: Bool = false + @Published var isOrderCanceled: Bool = false + + @Published var createdOrderId: Int? private let repository: OrderRepositoryProtocol @@ -48,42 +51,63 @@ final class OrderViewModel: ObservableObject { } // 주문 생성 - func createOrder(request: OrderRequest) async -> Bool { - isLoading = true - defer { isLoading = false } - + func createOrder(request: OrderRequest) async { let result = await repository.createOrder(request: request) + + switch result { + case .success(let response): + self.createdOrderId = response.orderId + self.isOrderSuccess = true + + case .failure(let error): + print("❌ 주문 실패:", error.message) + self.errorMessage = error.message + } + +// switch result { +// case .success(let data): +// let id = data.orderId +// DispatchQueue.main.async { +// self.createdOrderId = id +// self.isOrderSuccess = true +// } +// +// case .failure(let error): +// print("❌ 주문 실패:", error.message) +// self.errorMessage = error.message +// } + } + +// func createOrder(request: OrderRequest) async -> Bool { +// isLoading = true +// defer { isLoading = false } +// +// let result = await repository.createOrder(request: request) +// +// switch result { +// case .success(_): +// isOrderSuccess = true +// return true +// case .failure(let error): +// errorMessage = error.message +// print("❌ 주문 실패:", error.message) +// return false +// } +// } + + + func cancelOrder(orderId: Int) async { + isLoading = true + let result = await repository.cancelOrder(orderId: orderId) switch result { - case .success(_): - isOrderSuccess = true - return true + case .success: + await loadOrders() // ✅ 취소 후 즉시 UI 새로고침 case .failure(let error): errorMessage = error.message - print("❌ 주문 실패:", error.message) - return false } + isLoading = false } -// func createOrder( -// items: [OrderItems], -// requestedDate: String, -// payment: String, -// etc: String -// ) async { -// let requestBody = OrderRequest( -// orderItems: items, -// requestedShippingDate: requestedDate, -// paymentType: payment, -// etc: etc -// ) -// -// let result = await repository.createOrder(request: requestBody) -// -// switch result { -// case .success(let orderNumber): -// print("✅ 주문 성공:", orderNumber) -// case .failure(let error): -// print("❌ 주문 실패:", error.message) -// } -// } + + } From a8e18bf91fa4a761ef35eeac488bbe13409afbad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Tue, 28 Oct 2025 18:52:21 +0900 Subject: [PATCH 31/85] =?UTF-8?q?[FEAT]=20=EC=98=81=EC=88=98=EC=A6=9D=20UI?= =?UTF-8?q?=20=EB=94=94=EC=9E=90=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/feature/orders/data/OrderApi.swift | 1 + .../orders/data/OrderRepositoryImpl.swift | 30 --- .../domain/OrderRepositoryProtocol.swift | 1 - .../feature/orders/ui/OrderDetailView.swift | 11 +- .../app/feature/orders/ui/OrderInfoView.swift | 50 +++-- .../app/feature/orders/ui/ReceiptView.swift | 175 ++++++++++++++++++ .../app/navigation/MainTabView.swift | 3 +- 7 files changed, 219 insertions(+), 52 deletions(-) create mode 100644 StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift diff --git a/StockMate/StockMate/app/feature/orders/data/OrderApi.swift b/StockMate/StockMate/app/feature/orders/data/OrderApi.swift index c8ca332..7032db0 100644 --- a/StockMate/StockMate/app/feature/orders/data/OrderApi.swift +++ b/StockMate/StockMate/app/feature/orders/data/OrderApi.swift @@ -42,6 +42,7 @@ struct OrderResponseItem: Decodable, Identifiable { let memberId: Int let userInfo: OrderUserInfo? let orderItems: [OrderItem] + let paymentType: String? let etc: String? let rejectedMessage: String? let carrier: String? diff --git a/StockMate/StockMate/app/feature/orders/data/OrderRepositoryImpl.swift b/StockMate/StockMate/app/feature/orders/data/OrderRepositoryImpl.swift index fdffb34..0f4d42f 100644 --- a/StockMate/StockMate/app/feature/orders/data/OrderRepositoryImpl.swift +++ b/StockMate/StockMate/app/feature/orders/data/OrderRepositoryImpl.swift @@ -59,36 +59,6 @@ final class OrderRepositoryImpl: OrderRepositoryProtocol { } } -// func createOrder(request: OrderRequest) async -> AppResult { -// let request = OrderApi.createOrder(request) -// -// let result = await safeApi(request, decodeTo: ApiResponse.self) -// -// switch result { -// case .success(let response): -// if let data = response.data { -// return .success(data) -// } else { -// return .failure(.init(code: response.status, message: response.message, underlying: nil)) -// } -// case .failure(let error): -// return .failure(error) -// } -// } - -// func createOrder(request: OrderRequest) async -> AppResult { -//// func createOrder(request: OrderRequest) async -> AppResult { -// let request = OrderApi.createOrder(request) -// let result = await safeApi(request, decodeTo: ApiResponse.self) -// -// switch result { -// case .success(let response): -// return .success(response.data ?? "success") -// case .failure(let error): -// return .failure(error) -// } -// } - func createOrder(request: OrderRequest) async -> AppResult { let request = OrderApi.createOrder(request) diff --git a/StockMate/StockMate/app/feature/orders/domain/OrderRepositoryProtocol.swift b/StockMate/StockMate/app/feature/orders/domain/OrderRepositoryProtocol.swift index 269b8dd..9b98ce6 100644 --- a/StockMate/StockMate/app/feature/orders/domain/OrderRepositoryProtocol.swift +++ b/StockMate/StockMate/app/feature/orders/domain/OrderRepositoryProtocol.swift @@ -22,7 +22,6 @@ protocol OrderRepositoryProtocol { ) async -> AppResult // 주문 생성 -// func createOrder(request: OrderRequest) async -> AppResult func createOrder(request: OrderRequest) async -> AppResult diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift index 6ecc766..1d8054e 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift @@ -114,16 +114,21 @@ struct OrderDetailView: View { VStack(alignment: .leading, spacing: 10) { Text("결제 정보") .font(.system(size: 15, weight: .semibold)) - infoRow("결제 수단", "예치금") + + if order.paymentType == "DEPOSIT" { + infoRow("결제 수단", "예치금") + } else { + infoRow("결제 수단", "카드 결제") + } + infoRow("상품금액", "\(formatPrice(order.totalPrice))원") - infoRow("배송비", "\(formatPrice(3000))원") infoRow("배송희망일", formatDateOrDash(order.requestedShippingDate)) Divider().padding(.vertical, 4) HStack { Text("총 결제 금액") .font(.headline) Spacer() - Text("\(formatPrice(order.totalPrice + 3000))원") + Text("\(formatPrice(order.totalPrice))원") .font(.headline.bold()) .foregroundColor(.Primary) } diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift index d813947..d191506 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift @@ -89,7 +89,7 @@ struct OrderInfoView: View { extension OrderInfoView { private var contentView: some View { - VStack(alignment: .leading, spacing: 10) { + VStack(alignment: .leading, spacing: 17) { shippingInfoSection orderListSection paymentSection @@ -170,12 +170,20 @@ extension OrderInfoView { .padding(.leading, 5) VStack(alignment: .leading, spacing: 5) { - RadioButtonRow(title: "예치금 (잔액 ₩1,200,000)", selected: paymentType == .deposit) { - paymentType = .deposit + HStack{ + RadioButtonRow(title: "예치금 (잔액 ₩1,200,000)", selected: paymentType == .deposit) { + paymentType = .deposit + } + Spacer() } - RadioButtonRow(title: "직접 결제", selected: paymentType == .card) { - paymentType = .card + .frame(height: 35) + HStack{ + RadioButtonRow(title: "직접 결제", selected: paymentType == .card) { + paymentType = .card + } + Spacer() } + .frame(height: 35) } .padding() .frame(maxWidth: .infinity) @@ -191,27 +199,35 @@ extension OrderInfoView { .padding(.leading, 5) VStack(alignment: .leading, spacing: 5) { - RadioButtonRow(title: "오늘", selected: { - if case .today = shippingDateOption { return true } - return false - }()) { - shippingDateOption = .today + HStack { + RadioButtonRow(title: "오늘", selected: { + if case .today = shippingDateOption { return true } + return false + }()) { + shippingDateOption = .today + } + Spacer() } - - RadioButtonRow(title: "내일", selected: { - if case .tomorrow = shippingDateOption { return true } - return false - }()) { - shippingDateOption = .tomorrow + .frame(height: 35) + HStack { + RadioButtonRow(title: "내일", selected: { + if case .tomorrow = shippingDateOption { return true } + return false + }()) { + shippingDateOption = .tomorrow + } + Spacer() } + .frame(height: 35) - HStack { + HStack (spacing: 30){ RadioButtonRow(title: "날짜 선택", selected: { if case .specific(_) = shippingDateOption { return true } return false }()) { shippingDateOption = .specific(specificDate) } + .padding(.trailing, 5) if case .specific(_) = shippingDateOption { CustomDatePickerField(date: Binding { diff --git a/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift b/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift new file mode 100644 index 0000000..d82fbc1 --- /dev/null +++ b/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift @@ -0,0 +1,175 @@ +// +// ReceiptView.swift +// StockMate +// +// Created by Admin on 10/28/25. +// + +import SwiftUI +import PDFKit + +enum PDFType { + case a4 + case receipt80mm +} + +struct ReceiptView: View { + @State var paymentType = "예치금" + @State var approvalNumber = "202510300743" + @State var date = "2025/10/30 07:43:54" + @State var orderNumber = "SMO-2" + @State var itemName = "배터리-트랜스미터" + @State var quantity = 1 + @State var price = 5273 + @State var sellerName = "박시영" + @State var businessNumber = "888777776666" + @State var phone = "010-2596-2352" + @State var address = "서울특별시 성동구 동일로 259 3층" + + var vat: Int { Int(Double(price) * 0.1) } + var total: Int { price + vat } + + var body: some View { + ScrollView { + VStack(alignment: .leading) { + receiptContent + + HStack { + pdfButton(type: .a4, title: "A4 PDF") + pdfButton(type: .receipt80mm, title: "영수증 PDF") + } + .padding(.horizontal) + .padding(.bottom) + } + .background(Color.white) + .cornerRadius(12) + .padding() + } + .background(Color.Light) + .navigationTitle("영수증") + .navigationBarTitleDisplayMode(.inline) + + } + + private var receiptContent: some View { + VStack(alignment: .leading, spacing: 16) { + section("결제 정보") { + Divider() + row("거래종류", paymentType) + row("승인번호", approvalNumber) + row("거래일시", date) + } + .padding(4) + .padding(.top,5) + + section("구매정보") { + Divider() + row("주문번호", orderNumber) + row("상품명", "\(itemName), \(quantity)개") + row("공급가액", "\(price)원") + row("부가세액", "\(vat)원") + row("합계금액", "\(total)원", highlight: true) + } + .padding(4) + .padding(.top) + + section("판매자 정보") { + Divider() + row("대표자명", sellerName) + row("사업자등록번호", businessNumber) + row("전화번호", phone) + row("사업장주소", address) + } + .padding(4) + .padding(.top) + + Divider() + + Text(""" + • 비현금성으로 지급되는 예치금 사용 금액의 경우 현금 영수증 발행 대상에서 + 제외될 수 있습니다. + • 발생 정보는 구매확정 또는 거래 완료 이후 전달되기 때문에 국세청 사이트에서 + 즉시 확인되지 않을 수 있습니다. + • 이 영수증은 조세특례제한법 제 126조 3항에 의거 연말정산 시 소득공제혜택 + 부여 목적으로 발행됩니다. (국세청 회원가입 필수) + • 현금 영수증은 구매확정 또는 거래 완료 후 48시간 내로 국세청에서 확인 작업 + 후 최종 확정됩니다. + • 국세청 확인: 홈택스 홈페이지(https://www.hometax.go.kr/) 또는 국세청 + 상담센터(현금영수증 문의 ☎️126-1-1) + """) + .font(.system(size: 10.5)) + .foregroundColor(.textGray1) + .multilineTextAlignment(.leading) + .frame(maxWidth: .infinity, alignment: .leading) + .lineSpacing(4) + .padding(.leading, 2) // ✅ 총알 뒤 문장 들여쓰기 추가 + + } + .padding() + } + + private func section(_ title: String, @ViewBuilder content: () -> some View) -> some View { + VStack(alignment: .leading, spacing: 8) { + Text(title) + .font(.headline) + content() + } + .font(.subheadline) + } + + private func row(_ key: String, _ value: String, highlight: Bool = false) -> some View { + HStack { + Text(key) + Spacer() + Text(value) + .fontWeight(highlight ? .bold : .regular) + .foregroundColor(highlight ? .blue : .primary) + } + } + + private func pdfButton(type: PDFType, title: String) -> some View { + Button(action: { + generatePDF(type: type) + }) { + Text(title) + .frame(maxWidth: .infinity) + .padding() + .background(Color.blue) + .foregroundColor(.white) + .cornerRadius(8) + } + } + + private func generatePDF(type: PDFType) { + let content = receiptContent + let renderer = ImageRenderer(content: content) + + let width: CGFloat + let height: CGFloat = 2000 + + switch type { + case .a4: width = 595.2 // A4 width in pt + case .receipt80mm: width = 226.77 // 80mm in pt + } + + renderer.scale = UIScreen.main.scale + + if let image = renderer.uiImage { + let pdfDoc = PDFDocument() + let pdfPage = PDFPage(image: image) + pdfDoc.insert(pdfPage!, at: 0) + + let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent("receipt.pdf") + if pdfDoc.write(to: tempURL) { + let av = UIActivityViewController(activityItems: [tempURL], applicationActivities: nil) + UIApplication.shared.windows.first?.rootViewController?.present(av, animated: true) + } + } + } +} + +struct ReceiptView_Previews: PreviewProvider { + static var previews: some View { + ReceiptView() + } +} diff --git a/StockMate/StockMate/app/navigation/MainTabView.swift b/StockMate/StockMate/app/navigation/MainTabView.swift index 92e6ab7..5c978d8 100644 --- a/StockMate/StockMate/app/navigation/MainTabView.swift +++ b/StockMate/StockMate/app/navigation/MainTabView.swift @@ -24,7 +24,8 @@ struct MainTabView: View { case 2: NavigationStack { InventoryView(selectedTab: $selectedTab, tabTappedTrigger: $tabTappedTrigger) } // case 3: NavigationStack{ ContentView() } - case 3: NavigationStack{ ProfileView() } + case 3: NavigationStack{ ReceiptView() } +// case 3: NavigationStack{ ProfileView() } default: NavigationStack{ ContentView() } } } From 861febc42a7d5bb0310972e390f9c08aab1bfbc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Wed, 29 Oct 2025 09:18:32 +0900 Subject: [PATCH 32/85] =?UTF-8?q?[CHORE]=20=ED=8C=8C=EC=9D=BC=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- StockMate/StockMate/app/feature/auth/ui/LoginView.swift | 1 + StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift | 4 +++- StockMate/StockMate/app/navigation/MainTabView.swift | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift index f180064..88d62af 100644 --- a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift @@ -118,6 +118,7 @@ struct LoginView: View { } // MARK: - 유효성 검사 함수 + //테스트 계정 사용 위해 잠시 주석처리 private func isValidForm() -> Bool { // emailError = isValidEmail(authViewModel.email) ? nil : "이메일 형식을 확인해주세요" //pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" diff --git a/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift b/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift index d82fbc1..82715aa 100644 --- a/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift @@ -12,8 +12,10 @@ enum PDFType { case a4 case receipt80mm } - +// TODO: API 연결 후 주문 상세 페이지와 연결 struct ReceiptView: View { + + @State var paymentType = "예치금" @State var approvalNumber = "202510300743" @State var date = "2025/10/30 07:43:54" diff --git a/StockMate/StockMate/app/navigation/MainTabView.swift b/StockMate/StockMate/app/navigation/MainTabView.swift index 5c978d8..b5bf61c 100644 --- a/StockMate/StockMate/app/navigation/MainTabView.swift +++ b/StockMate/StockMate/app/navigation/MainTabView.swift @@ -24,8 +24,8 @@ struct MainTabView: View { case 2: NavigationStack { InventoryView(selectedTab: $selectedTab, tabTappedTrigger: $tabTappedTrigger) } // case 3: NavigationStack{ ContentView() } - case 3: NavigationStack{ ReceiptView() } -// case 3: NavigationStack{ ProfileView() } +// case 3: NavigationStack{ ReceiptView() } + case 3: NavigationStack{ ProfileView() } default: NavigationStack{ ContentView() } } } From 234e9e21f4969b4096652ceacf79a8d7a6dc6f09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Wed, 29 Oct 2025 10:00:10 +0900 Subject: [PATCH 33/85] =?UTF-8?q?[FIX]=20=EC=A3=BC=EC=84=9D=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20=EB=B0=8F=20=EC=9C=84=ED=97=98=EC=9A=94=EC=86=8C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/feature/auth/ui/LoginView.swift | 8 ++--- .../feature/inventory/ui/LackListView.swift | 5 ---- .../app/feature/orders/ui/OrderInfoView.swift | 4 ++- .../app/feature/orders/ui/OrderListView.swift | 5 +++- .../app/feature/orders/ui/ReceiptView.swift | 7 ++++- .../orders/viewmodel/OrderViewModel.swift | 30 ------------------- 6 files changed, 16 insertions(+), 43 deletions(-) diff --git a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift index 88d62af..06e87e3 100644 --- a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift @@ -118,12 +118,10 @@ struct LoginView: View { } // MARK: - 유효성 검사 함수 - //테스트 계정 사용 위해 잠시 주석처리 private func isValidForm() -> Bool { - // emailError = isValidEmail(authViewModel.email) ? nil : "이메일 형식을 확인해주세요" - //pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" - // return emailError == nil //&& pwError == nil - return true + emailError = isValidEmail(authViewModel.email) ? nil : "이메일 형식을 확인해주세요" + pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" + return emailError == nil && pwError == nil } } diff --git a/StockMate/StockMate/app/feature/inventory/ui/LackListView.swift b/StockMate/StockMate/app/feature/inventory/ui/LackListView.swift index b1af997..b9ca69a 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/LackListView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/LackListView.swift @@ -73,11 +73,6 @@ struct LackListView: View { } } -#Preview { - LackListView(selectedCategory: "엔진/미션") - .environmentObject(InventoryViewModel()) -} - struct CategoryButton: View { let title: String let isSelected: Bool diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift index d191506..b167671 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift @@ -47,7 +47,9 @@ struct OrderInfoView: View { case .today: return formatter.string(from: Date()) case .tomorrow: - return formatter.string(from: Calendar.current.date(byAdding: .day, value: 1, to: Date())!) + let tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: Date()) ?? Date() + return formatter.string(from: tomorrow) +// return formatter.string(from: Calendar.current.date(byAdding: .day, value: 1, to: Date())!) case .specific(let date): return formatter.string(from: date) } diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift index 0545895..0dc04a5 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift @@ -27,8 +27,11 @@ struct OrderListView: View { .frame(maxWidth: .infinity, maxHeight: .infinity) } else { // 날짜별로 그룹화 (최신순) +// let groupedOrders = Dictionary(grouping: orderViewModel.orders) { order in +// order.createdAt.split(separator: "T").first ?? "" +// } let groupedOrders = Dictionary(grouping: orderViewModel.orders) { order in - order.createdAt.split(separator: "T").first ?? "" + order.createdAt.split(separator: "T").first.map(String.init) ?? "" } .sorted { $0.key > $1.key } diff --git a/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift b/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift index 82715aa..59aec01 100644 --- a/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift @@ -164,7 +164,12 @@ struct ReceiptView: View { let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent("receipt.pdf") if pdfDoc.write(to: tempURL) { let av = UIActivityViewController(activityItems: [tempURL], applicationActivities: nil) - UIApplication.shared.windows.first?.rootViewController?.present(av, animated: true) + if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, + let rootVC = windowScene.windows.first?.rootViewController { + rootVC.present(av, animated: true) + } +// let av = UIActivityViewController(activityItems: [tempURL], applicationActivities: nil) +// UIApplication.shared.windows.first?.rootViewController?.present(av, animated: true) } } } diff --git a/StockMate/StockMate/app/feature/orders/viewmodel/OrderViewModel.swift b/StockMate/StockMate/app/feature/orders/viewmodel/OrderViewModel.swift index b80fd15..dccd03f 100644 --- a/StockMate/StockMate/app/feature/orders/viewmodel/OrderViewModel.swift +++ b/StockMate/StockMate/app/feature/orders/viewmodel/OrderViewModel.swift @@ -63,38 +63,8 @@ final class OrderViewModel: ObservableObject { print("❌ 주문 실패:", error.message) self.errorMessage = error.message } - -// switch result { -// case .success(let data): -// let id = data.orderId -// DispatchQueue.main.async { -// self.createdOrderId = id -// self.isOrderSuccess = true -// } -// -// case .failure(let error): -// print("❌ 주문 실패:", error.message) -// self.errorMessage = error.message -// } } -// func createOrder(request: OrderRequest) async -> Bool { -// isLoading = true -// defer { isLoading = false } -// -// let result = await repository.createOrder(request: request) -// -// switch result { -// case .success(_): -// isOrderSuccess = true -// return true -// case .failure(let error): -// errorMessage = error.message -// print("❌ 주문 실패:", error.message) -// return false -// } -// } - func cancelOrder(orderId: Int) async { isLoading = true From 232e53d8a7f8c642a3ea5d1d6f07f5267623d6a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Wed, 29 Oct 2025 15:14:37 +0900 Subject: [PATCH 34/85] =?UTF-8?q?[FEAT]=20=EC=A3=BC=EB=AC=B8=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=B0=8F=20=EC=98=81?= =?UTF-8?q?=EC=88=98=EC=A6=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- StockMate/.DS_Store | Bin 6148 -> 6148 bytes .../feature/orders/ui/OrderDetailView.swift | 107 ++++++--- .../app/feature/orders/ui/ReceiptView.swift | 214 ++++++++++++------ 3 files changed, 213 insertions(+), 108 deletions(-) diff --git a/StockMate/.DS_Store b/StockMate/.DS_Store index 8b131bc6d6be2f3fa147df58dc1424275991ebff..9b2b289a6cc938aeb964150a839318a8b9520a06 100644 GIT binary patch delta 70 zcmZoMXffCj%gW4>X)`&2)r{j&>B_Ag;l~|;9G%IhSh(Bd!1Fbq!4&n;j80cL}Z Ng`XHVvvd6A2LNgN7oq?F diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift index 1d8054e..3fd73db 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift @@ -21,15 +21,16 @@ struct OrderDetailView: View { VStack(spacing: 16) { // ✅ 주문 정보 - VStack(alignment: .leading, spacing: 8) { + VStack(alignment: .leading, spacing: 6) { Text(formatDate(order.createdAt)) .font(.system(size: 15, weight: .semibold)) - Text("주문번호: \(order.orderNumber)") - .font(.system(size: 14)) - .foregroundColor(.gray) + .padding(.bottom, 4) + + infoRow("주문번호", order.orderNumber) HStack { - Text("상태:") - .font(.system(size: 14, weight: .semibold)) + Text("상태") + .font(.system(size: 14)) + Spacer() Text(statusText(order.orderStatus)) .font(.system(size: 13, weight: .semibold)) .padding(.horizontal, 10) @@ -37,14 +38,15 @@ struct OrderDetailView: View { .background(statusBdColor(order.orderStatus)) .foregroundColor(statusColor(order.orderStatus)) .cornerRadius(12) + .padding(.leading,4) } + } .frame(maxWidth: .infinity, alignment: .leading) // ✅ 이거 추가 .padding(.all, 20) .background(Color.white) .cornerRadius(16) .shadow(color: .black.opacity(0.05), radius: 3, y: 2) -// .padding(.horizontal, 20) // ✅ 배송 정보 @@ -52,22 +54,29 @@ struct OrderDetailView: View { Text("배송정보") .font(.system(size: 15, weight: .semibold)) .padding(.bottom, 4) - Text(order.userInfo?.owner ?? "-") - Text(order.userInfo?.address ?? "-") - if !(order.etc ?? "").isEmpty { - Text(order.etc ?? "") - } - if let email = order.userInfo?.email { - Text(email) - .foregroundColor(.gray) - } + + infoRow("주문자명", order.userInfo?.owner ?? "-") + infoRow("주소", order.userInfo?.address ?? "-") + // ✅ 운송장정보 안전 처리 + let trackingText: String = { + if let carrier = order.carrier, + let trackingNo = order.trackingNumber, + !carrier.isEmpty, + !trackingNo.isEmpty { + return "\(carrier): \(trackingNo)" + } + return "-" + }() + infoRow("운송장번호", trackingText) + + infoRow("요청사항", order.etc ?? "") } .frame(maxWidth: .infinity, alignment: .leading) // ✅ 여기도 추가 .padding(.all, 20) .background(Color.white) .cornerRadius(16) .shadow(color: .black.opacity(0.05), radius: 3, y: 2) -// .padding(.horizontal, 20) + // ✅ 주문 상품 OrderSectionCard { @@ -138,7 +147,8 @@ struct OrderDetailView: View { // ✅ 하단 버튼 HStack(spacing: 12) { // 왼쪽: 영수증 확인 - Button(action: {}) { + // 왼쪽: 영수증 확인 + NavigationLink(destination: ReceiptView(orderId: order.id)) { Text("영수증 확인") .font(.system(size: 15, weight: .semibold)) .foregroundColor(Color.Primary) @@ -152,24 +162,55 @@ struct OrderDetailView: View { .cornerRadius(10) } - // 오른쪽: 주문 취소 - Button(action: { - // 주문취소 처리 - Task { - await orderViewModel.cancelOrder(orderId: orderId) + + // 오른쪽 버튼: 주문 상태에 따라 변경 + if order.orderStatus == "ORDER_COMPLETED" { + // 주문 완료 → "주문취소" 버튼 + Button(action: { + Task { + await orderViewModel.cancelOrder(orderId: orderId) + } + }) { + Text("주문 취소") + .font(.system(size: 15, weight: .semibold)) + .frame(maxWidth: .infinity) + .frame(height: 48) + .background(Color(hex: "#1D4ED8")) + .foregroundColor(.white) + .cornerRadius(10) } - - }) { - Text("주문 취소") - .font(.system(size: 15, weight: .semibold)) - .frame(maxWidth: .infinity) - .frame(height: 48) - .background(Color(hex: "#1D4ED8")) - .foregroundColor(.white) - .cornerRadius(10) + + } else if order.orderStatus == "ORDER_COMPLETED" { + // 배송 완료 → "입고 하기" 버튼 + Button(action: { + // TODO: 입고 처리 버튼 + }) { + Text("입고 처리") + .font(.system(size: 15, weight: .semibold)) + .frame(maxWidth: .infinity) + .frame(height: 48) + .background(Color(hex: "#1D4ED8")) + .foregroundColor(.white) + .cornerRadius(10) + } + + } else { + // 👉 나머지 상태 → "재주문하기" 버튼 + Button(action: { + // TODO: 평가 액션 처리 + }) { + Text("재주문하기") + .font(.system(size: 15, weight: .semibold)) + .frame(maxWidth: .infinity) + .frame(height: 48) + .background(Color(hex: "#1D4ED8")) + .foregroundColor(.white) + .cornerRadius(10) + } + } } - .padding(.top, 10) + .padding(.top, 5) } .padding(.horizontal, 20) // ✅ 전체 섹션 동일 여백 diff --git a/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift b/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift index 59aec01..9e690f1 100644 --- a/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift @@ -7,70 +7,106 @@ import SwiftUI import PDFKit +import UIKit enum PDFType { case a4 case receipt80mm } -// TODO: API 연결 후 주문 상세 페이지와 연결 + struct ReceiptView: View { + let orderId: Int + @StateObject private var detailViewModel = OrderDetailViewModel() - @State var paymentType = "예치금" - @State var approvalNumber = "202510300743" - @State var date = "2025/10/30 07:43:54" - @State var orderNumber = "SMO-2" - @State var itemName = "배터리-트랜스미터" - @State var quantity = 1 - @State var price = 5273 - @State var sellerName = "박시영" - @State var businessNumber = "888777776666" - @State var phone = "010-2596-2352" - @State var address = "서울특별시 성동구 동일로 259 3층" - - var vat: Int { Int(Double(price) * 0.1) } - var total: Int { price + vat } + @State var sellerName = "홍길동" + @State var businessNumber = "215-87-12345" // 형식만 맞춘 랜덤번호 + @State var phone = "02-567-8901" + @State var address = "서울특별시 금천구 가산동 459-9" + var body: some View { ScrollView { - VStack(alignment: .leading) { - receiptContent - - HStack { - pdfButton(type: .a4, title: "A4 PDF") - pdfButton(type: .receipt80mm, title: "영수증 PDF") + if detailViewModel.isLoading { + ProgressView() + .frame(maxWidth: .infinity, maxHeight: .infinity) + } else if let order = detailViewModel.order { + VStack(alignment: .leading) { + receiptContent(order: order) + + Button { + generatePDF(type: .receipt80mm, order: order) + } label: { + Text("영수증 PDF 저장") + .frame(maxWidth: .infinity) + .padding() + .background(Color.blue) + .foregroundColor(.white) + .cornerRadius(8) + } + .padding(.horizontal) + .padding(.bottom) + } - .padding(.horizontal) - .padding(.bottom) + .background(Color.white) + .cornerRadius(12) + .padding() } - .background(Color.white) - .cornerRadius(12) - .padding() } .background(Color.Light) .navigationTitle("영수증") .navigationBarTitleDisplayMode(.inline) + .task { + await detailViewModel.fetchOrderDetail(orderId: orderId) + } } - - private var receiptContent: some View { - VStack(alignment: .leading, spacing: 16) { + + private func receiptContent(order: OrderResponseItem) -> some View { + let total = order.totalPrice + let vat = Int(Double(total) * 10 / 110) // 부가세액 + let supplyPrice = total - vat // 공급가액 + + return VStack(alignment: .leading, spacing: 16) { section("결제 정보") { Divider() - row("거래종류", paymentType) - row("승인번호", approvalNumber) - row("거래일시", date) + if order.paymentType == "DEPOSIT" { + row("거래종류", "예치금") + } else { + row("결제수단", "신용카드") + } + row("승인번호", formattedApprovalNumber(order.createdAt)) + row("거래일시", formattedDate(order.createdAt)) + } .padding(4) .padding(.top,5) section("구매정보") { Divider() - row("주문번호", orderNumber) - row("상품명", "\(itemName), \(quantity)개") - row("공급가액", "\(price)원") - row("부가세액", "\(vat)원") - row("합계금액", "\(total)원", highlight: true) + VStack(spacing: 8){ + row("주문번호", order.orderNumber) + VStack{ + // 상품명 라벨과 첫 번째 상품 같은 라인 + if let first = order.orderItems.first { + HStack { + Text("상품명") + Spacer() + Text("\(first.partDetail.korName) \(first.amount)개") + } + } + // 나머지는 label 없이 아래에 + ForEach(order.orderItems.dropFirst(), id: \.partId) { item in + HStack { + Spacer() // label 영역만큼 들여쓰기 효과 + Text("\(item.partDetail.korName) \(item.amount)개") + } + } + } + row("공급가액", "\(formatPrice(supplyPrice))원") + row("부가세액", "\(formatPrice(vat))원") + row("합계금액", "\(formatPrice(total))원", highlight: true) + } } .padding(4) .padding(.top) @@ -104,7 +140,7 @@ struct ReceiptView: View { .multilineTextAlignment(.leading) .frame(maxWidth: .infinity, alignment: .leading) .lineSpacing(4) - .padding(.leading, 2) // ✅ 총알 뒤 문장 들여쓰기 추가 + .padding(.leading, 2) // 문장 들여쓰기 추가 } .padding() @@ -129,54 +165,82 @@ struct ReceiptView: View { } } - private func pdfButton(type: PDFType, title: String) -> some View { - Button(action: { - generatePDF(type: type) - }) { - Text(title) - .frame(maxWidth: .infinity) - .padding() - .background(Color.blue) - .foregroundColor(.white) - .cornerRadius(8) - } - } - - private func generatePDF(type: PDFType) { - let content = receiptContent - let renderer = ImageRenderer(content: content) + private func generatePDF(type: PDFType, order: OrderResponseItem) { + let view = receiptContent(order: order) // ✅ 실제 View 생성 + let renderer = ImageRenderer(content: view) let width: CGFloat - let height: CGFloat = 2000 - switch type { case .a4: width = 595.2 // A4 width in pt case .receipt80mm: width = 226.77 // 80mm in pt } renderer.scale = UIScreen.main.scale + + // ✅ cgImage 기반 안전 처리 + if let cgImage = renderer.cgImage { + let uiImage = UIImage(cgImage: cgImage) + let pdfDoc = PDFDocument() + if let pdfPage = PDFPage(image: uiImage) { + pdfDoc.insert(pdfPage, at: 0) + } + + // ✅ 주문번호 기반 파일명 + let fileName = "receipt_\(order.orderNumber).pdf" + let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(fileName) + + if pdfDoc.write(to: tempURL) { + let av = UIActivityViewController(activityItems: [tempURL], applicationActivities: nil) + + if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, + let rootVC = windowScene.windows.first?.rootViewController { + av.popoverPresentationController?.sourceView = rootVC.view + rootVC.present(av, animated: true) + } + } + } + + } +} - if let image = renderer.uiImage { - let pdfDoc = PDFDocument() - let pdfPage = PDFPage(image: image) - pdfDoc.insert(pdfPage!, at: 0) - - let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent("receipt.pdf") - if pdfDoc.write(to: tempURL) { - let av = UIActivityViewController(activityItems: [tempURL], applicationActivities: nil) - if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, - let rootVC = windowScene.windows.first?.rootViewController { - rootVC.present(av, animated: true) - } -// let av = UIActivityViewController(activityItems: [tempURL], applicationActivities: nil) -// UIApplication.shared.windows.first?.rootViewController?.present(av, animated: true) - } - } +//struct ReceiptView_Previews: PreviewProvider { +// static var previews: some View { +// ReceiptView() +// } +//} + +func formattedDate(_ timestamp: String) -> String { + let inputFormatter = DateFormatter() + inputFormatter.locale = Locale(identifier: "ko_KR") + inputFormatter.timeZone = TimeZone(abbreviation: "UTC") + inputFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS" + + guard let date = inputFormatter.date(from: timestamp) else { + return timestamp } + + let outputFormatter = DateFormatter() + outputFormatter.locale = Locale(identifier: "ko_KR") + outputFormatter.timeZone = TimeZone.current + outputFormatter.dateFormat = "yyyy/MM/dd HH:mm:ss" + + return outputFormatter.string(from: date) } -struct ReceiptView_Previews: PreviewProvider { - static var previews: some View { - ReceiptView() +func formattedApprovalNumber(_ timestamp: String) -> String { + let inputFormatter = DateFormatter() + inputFormatter.locale = Locale(identifier: "ko_KR") + inputFormatter.timeZone = TimeZone(abbreviation: "UTC") + inputFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS" + + guard let date = inputFormatter.date(from: timestamp) else { + return timestamp } + + let outputFormatter = DateFormatter() + outputFormatter.locale = Locale(identifier: "ko_KR") + outputFormatter.timeZone = TimeZone.current + outputFormatter.dateFormat = "yyyyMMddHHmm" // ✅ 승인번호 포맷 + + return outputFormatter.string(from: date) } From 4405f9b673f5ee80c4224fce9bbc7375a03ed1f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Thu, 30 Oct 2025 16:01:43 +0900 Subject: [PATCH 35/85] =?UTF-8?q?[FEAT]=20=EC=A3=BC=EB=AC=B8=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=98=88=EC=B9=98?= =?UTF-8?q?=EA=B8=88=20=EC=B6=A9=EC=A0=84=20API=20=EC=97=B0=EB=8F=99,?= =?UTF-8?q?=EB=B0=B0=EC=86=A1=EC=A0=95=EB=B3=B4=20UI=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20=EB=B0=8F=20=EB=B0=B0=EC=86=A1=EC=83=81=ED=83=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- StockMate/StockMate/ContentView.swift | 50 ++++++-- .../feature/cart/ui/DeliveryStatusView.swift | 106 ++++++++++++++++ .../feature/orders/ui/OrderDetailView.swift | 58 ++++++++- .../app/feature/orders/ui/OrderInfoView.swift | 100 +++++++++++---- .../app/feature/payment/data/PaymentApi.swift | 40 ++++++ .../payment/data/PaymentRepositoryImpl.swift | 48 ++++++++ .../domain/PaymentRepositoryProtocol.swift | 14 +++ .../payment/ui/DepositChargeView.swift | 115 ++++++++++++++++++ .../payment/viewmodel/DepositViewModel.swift | 46 +++++++ .../check.imageset/Contents.json | 21 ++++ .../Assets.xcassets/check.imageset/check.svg | 8 ++ .../deposit_background.imageset/Contents.json | 23 ++++ .../deposit_background@1x.png | Bin 0 -> 3184 bytes .../deposit_background@2x.png | Bin 0 -> 6499 bytes .../deposit_background@3x.png | Bin 0 -> 10386 bytes .../dottedline.imageset/Contents.json | 21 ++++ .../dottedline.imageset/dottedline.svg | 6 + .../hourglass.imageset/Contents.json | 21 ++++ .../hourglass.imageset/hourglass.svg | 8 ++ .../pindrop.imageset/Contents.json | 21 ++++ .../pindrop.imageset/pindrop.svg | 9 ++ .../rocket.imageset/Contents.json | 21 ++++ .../rocket.imageset/rocket.svg | 8 ++ .../uploadprogress.imageset/Contents.json | 21 ++++ .../uploadprogress.svg | 8 ++ StockMate/StockMate/resources/Color.swift | 2 + 26 files changed, 735 insertions(+), 40 deletions(-) create mode 100644 StockMate/StockMate/app/feature/cart/ui/DeliveryStatusView.swift create mode 100644 StockMate/StockMate/app/feature/payment/data/PaymentApi.swift create mode 100644 StockMate/StockMate/app/feature/payment/data/PaymentRepositoryImpl.swift create mode 100644 StockMate/StockMate/app/feature/payment/domain/PaymentRepositoryProtocol.swift create mode 100644 StockMate/StockMate/app/feature/payment/ui/DepositChargeView.swift create mode 100644 StockMate/StockMate/app/feature/payment/viewmodel/DepositViewModel.swift create mode 100644 StockMate/StockMate/resources/Assets.xcassets/check.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/check.imageset/check.svg create mode 100644 StockMate/StockMate/resources/Assets.xcassets/deposit_background.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/deposit_background.imageset/deposit_background@1x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/deposit_background.imageset/deposit_background@2x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/deposit_background.imageset/deposit_background@3x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/dottedline.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/dottedline.imageset/dottedline.svg create mode 100644 StockMate/StockMate/resources/Assets.xcassets/hourglass.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/hourglass.imageset/hourglass.svg create mode 100644 StockMate/StockMate/resources/Assets.xcassets/pindrop.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/pindrop.imageset/pindrop.svg create mode 100644 StockMate/StockMate/resources/Assets.xcassets/rocket.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/rocket.imageset/rocket.svg create mode 100644 StockMate/StockMate/resources/Assets.xcassets/uploadprogress.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/uploadprogress.imageset/uploadprogress.svg diff --git a/StockMate/StockMate/ContentView.swift b/StockMate/StockMate/ContentView.swift index d576e34..ccb290e 100644 --- a/StockMate/StockMate/ContentView.swift +++ b/StockMate/StockMate/ContentView.swift @@ -6,28 +6,52 @@ // import SwiftUI +// +//struct DottedLine: View { +// var body: some View { +// Rectangle() +// .fill(Color.clear) +// .frame(height: 1) +// .overlay( +// Rectangle() +// .stroke(style: StrokeStyle(lineWidth: 1, dash: [3])) // <- 점선 패턴 +// .foregroundColor(.gray) +// ) +// } +//} struct ContentView: View { var body: some View { VStack { - Image(systemName: "globe") - .imageScale(.large) - .foregroundStyle(.tint) - Text("임시 화면") + Text("위쪽") +// DottedLine() + Text("아래쪽") } .padding() - HStack(spacing: 20) { - Image(systemName: "gearshape") - .font(.system(size: 40)) - .foregroundColor(.blue) - - Image(systemName: "lightbulb") - .font(.system(size: 40)) - .foregroundColor(.cyan) - } } } +//struct ContentView: View { +// var body: some View { +// VStack { +// Image(systemName: "globe") +// .imageScale(.large) +// .foregroundStyle(.tint) +// Text("임시 화면") +// } +// .padding() +// HStack(spacing: 20) { +// Image(systemName: "gearshape") +// .font(.system(size: 40)) +// .foregroundColor(.blue) +// +// Image(systemName: "lightbulb") +// .font(.system(size: 40)) +// .foregroundColor(.cyan) +// } +// } +//} + #Preview { ContentView() } diff --git a/StockMate/StockMate/app/feature/cart/ui/DeliveryStatusView.swift b/StockMate/StockMate/app/feature/cart/ui/DeliveryStatusView.swift new file mode 100644 index 0000000..2825687 --- /dev/null +++ b/StockMate/StockMate/app/feature/cart/ui/DeliveryStatusView.swift @@ -0,0 +1,106 @@ +// +// DeliveryStatusView.swift +// StockMate +// +// Created by Admin on 10/30/25. +// + +import SwiftUI + +struct DeliveryStep { + let title: String + let iconName: String // Asset 이름 +} + + +struct DeliveryStatusView: View { + let steps: [DeliveryStep] = [ + DeliveryStep(title: "결제완료", iconName: "check"), + DeliveryStep(title: "승인대기중", iconName: "hourglass"), + DeliveryStep(title: "상품준비중", iconName: "uploadprogress"), + DeliveryStep(title: "배송중", iconName: "rocket"), + DeliveryStep(title: "배송완료", iconName: "pindrop") + ] + + let currentStep: Int + + var body: some View { + GeometryReader { geo in + HStack(alignment: .center, spacing: 0) { + ForEach(0.. String { switch status { - case "ORDER_COMPLETED": return "주문 완료" + case "ORDER_COMPLETED": return "주문 완료" // + case "PAY_COMPLETED": return "결제 완료" + case "PENDING_APPROVAL": return "승인 대기" // + case "FAILED": return "결제 실패" case "PENDING_SHIPPING": return "출고 대기" - case "REJECTED": return "출고 반려" - case "SHIPPING": return "배송 중" + case "SHIPPING": return "배송중" + case "PENDING_RECEIVING": return "입고 대기" + case "REJECTED": return "주문 반려" case "DELIVERED": return "배송 완료" case "RECEIVED": return "입고 완료" + case "REFUNDED": return "환불 완료" + case "REFUND_REJECTED": return "환불 반려" case "CANCELLED": return "주문 취소" default: return "알 수 없음" } @@ -262,12 +275,18 @@ struct OrderDetailView: View { func statusColor(_ status: String) -> Color { switch status { case "ORDER_COMPLETED": return .StatusGreen + case "PAY_COMPLETED": return .StatusGreen + case "PENDING_APPROVAL": return .Warning + case "FAILED": return .Danger case "PENDING_SHIPPING": return .InvUse - case "REJECTED": return .Danger case "SHIPPING": return .Transfer + case "PENDING_RECEIVING": return .Secondary + case "REJECTED": return .Danger case "DELIVERED": return .Secondary case "RECEIVED": return .StatusPurple - case "CANCELLED": return .gray + case "REFUNDED": return .Gray + case "REFUND_REJECTED": return .Gray + case "CANCELLED": return .Gray default: return .gray.opacity(0.6) } } @@ -275,11 +294,17 @@ struct OrderDetailView: View { func statusBdColor(_ status: String) -> Color { switch status { case "ORDER_COMPLETED": return .StatusGreenBg + case "PAY_COMPLETED": return .StatusGreenBg + case "PENDING_APPROVAL": return .WarningBg + case "FAILED": return .DangerBg case "PENDING_SHIPPING": return .InvUseBg - case "REJECTED": return .DangerBg case "SHIPPING": return .TransferBg + case "PENDING_RECEIVING": return .LightBlue04 + case "REJECTED": return .DangerBg case "DELIVERED": return .LightBlue04 case "RECEIVED": return .StatusPurpleBg + case "REFUNDED": return Color(hex: "#EEEEEF") + case "REFUND_REJECTED": return Color(hex: "#EEEEEF") case "CANCELLED": return Color(hex: "#EEEEEF") default: return .gray.opacity(0.6) } @@ -319,3 +344,24 @@ func formatDateOrDash(_ isoDate: String?) -> String { guard comps.count == 3 else { return "-" } return "\(comps[0])년 \(comps[1])월 \(comps[2])일" } + +func deliveryStep(for status: String) -> Int { + //6 -> 전체 회색 + //4 -> 전체 파란색 + switch status { + case "ORDER_COMPLETED": return 0 // 주문 완료 + case "PAY_COMPLETED": return 0 // 결제 완료 + case "PENDING_APPROVAL": return 1 // 승인 대기 + case "FAILED": return 6 // 결제 실패 + case "PENDING_SHIPPING": return 2 // 출고 대기 + case "SHIPPING": return 3 // 배송중 + case "PENDING_RECEIVING": return 4 // 입고 대기 + case "REJECTED": return 6 // 승인 반려 + case "DELIVERED": return 4 // 배송 완료 + case "RECEIVED": return 4 // 입고 완료 + case "REFUNDED": return 6 // 환불 완료 + case "REFUND_REJECTED": return 6 // 환불 반려 + case "CANCELLED": return 6 // 주문 취소 + default: return 6 + } +} diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift index b167671..701c358 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift @@ -20,6 +20,7 @@ enum ShippingDateOption { struct OrderInfoView: View { @ObservedObject var cartViewModel: CartViewModel @StateObject var orderViewModel = OrderViewModel() + @StateObject private var depositViewModel = DepositViewModel() @State private var paymentType: PaymentType = .deposit @State private var shippingDateOption: ShippingDateOption = .today @@ -74,7 +75,10 @@ struct OrderInfoView: View { .background(Color.Light) .navigationTitle("주문/결제") .navigationBarTitleDisplayMode(.inline) - .task { await cartViewModel.fetchCart() } + .task { + await cartViewModel.fetchCart() + await depositViewModel.fetchDepositAmount() + } .edgesIgnoringSafeArea(.bottom) .onChange(of: orderViewModel.isOrderSuccess) { success in if success { @@ -84,7 +88,16 @@ struct OrderInfoView: View { } } } + // ✅ 충전 bottom sheet 연결 + .sheet(isPresented: $depositViewModel.showChargeSheet) { + DepositChargeView(viewModel: depositViewModel) + .presentationDetents([.fraction(0.80)]) // 시트 높이 85% +// .presentationDragIndicator(.visible) + } + } + + } // MARK: - UI 구성 View @@ -164,35 +177,80 @@ extension OrderInfoView { .cornerRadius(16) } } - private var paymentSection: some View { - VStack(alignment: .leading) { - Text("결제 수단") - .font(.headline) - .padding(.leading, 5) + ZStack { + // 배경 이미지 적용 + Image("deposit_background") // ← 에셋에 넣은 이미지 이름 + .resizable() + .scaledToFill() + .frame(height: 185) + .clipped() + .cornerRadius(16.39) - VStack(alignment: .leading, spacing: 5) { - HStack{ - RadioButtonRow(title: "예치금 (잔액 ₩1,200,000)", selected: paymentType == .deposit) { - paymentType = .deposit + VStack(alignment: .leading, spacing: 12) { + VStack (alignment: .leading, spacing: 13){ + HStack { + Text("사용 가능 예치금") + .font(.system(size: 17, weight: .bold)) + .padding(.leading, 5) + .padding(.top, 25) + .foregroundColor(Color.white) } - Spacer() - } - .frame(height: 35) - HStack{ - RadioButtonRow(title: "직접 결제", selected: paymentType == .card) { - paymentType = .card + + HStack { + // 예치금 금액 표시 + if depositViewModel.isLoading { + ProgressView() + .tint(.white) + } else { + Text("₩\(formatPrice(depositViewModel.balance))") + .font(.system(size: 26, weight: .bold)) + .foregroundColor(Color.white) + } + + // Text("₩\(cartViewModel.depositBalance?.formatted() ?? "0")") + // .font(.system(size: 22, weight: .bold)) } + } + + Spacer() + + HStack { Spacer() + Button { + depositViewModel.showChargeSheet = true // <-- $ 없이 할당 + } label: { + Text("충전") + .foregroundColor(Color.Primary) + .font(.system(size: 14, weight: .bold)) + .padding(.vertical, 6) + .padding(.horizontal, 14) + .background(Color.white) + .cornerRadius(20) + } + .padding(.trailing, 5) + +// Button { +// depositViewModel.showChargeSheet = true +// } label: { +// Text("충전") +// .foregroundColor(Color.Primary) +// .font(.system(size: 14, weight: .bold)) +// .padding(.vertical, 6) +// .padding(.horizontal, 14) +// .background(Color.white) +// .cornerRadius(20) +// } +// .padding(.trailing, 5) } - .frame(height: 35) + .padding(.bottom, 25) } - .padding() - .frame(maxWidth: .infinity) - .background(Color.white) - .cornerRadius(16) + .frame(height: 185) + .padding(20) } + .frame(maxWidth: .infinity) } + private var shippingDateSection: some View { VStack(alignment: .leading) { diff --git a/StockMate/StockMate/app/feature/payment/data/PaymentApi.swift b/StockMate/StockMate/app/feature/payment/data/PaymentApi.swift new file mode 100644 index 0000000..53346c3 --- /dev/null +++ b/StockMate/StockMate/app/feature/payment/data/PaymentApi.swift @@ -0,0 +1,40 @@ +// +// PaymentApi.swift +// StockMate +// +// Created by Admin on 10/29/25. +// + + +import Foundation +import Alamofire + + +struct PaymentAmount: Decodable { + let id: Int + let balance: Int + let userId: Int +} + + +enum PaymentApi { + // 예치금 조회 + static func getPaymentAmount() -> DataRequest { + let url = ApiClient.baseURL + "api/v1/payment/amount" + return ApiClient.shared.request(url, method: .get) + } + + // 예치금 충전 + static func chargeDeposit(amount: Int) -> DataRequest { + let url = ApiClient.baseURL + "api/v1/payment/charge" + let params: [String: Any] = [ + "amount": amount + ] + return ApiClient.shared.request( + url, + method: .post, + parameters: params, + encoding: URLEncoding.queryString + ) + } +} diff --git a/StockMate/StockMate/app/feature/payment/data/PaymentRepositoryImpl.swift b/StockMate/StockMate/app/feature/payment/data/PaymentRepositoryImpl.swift new file mode 100644 index 0000000..625b887 --- /dev/null +++ b/StockMate/StockMate/app/feature/payment/data/PaymentRepositoryImpl.swift @@ -0,0 +1,48 @@ +// +// PaymentRepositoryImpl.swift +// StockMate +// +// Created by Admin on 10/29/25. +// + + +import Foundation +import Alamofire + + +final class PaymentRepositoryImpl: PaymentRepositoryProtocol { + + func fetchDepositAmount() async -> AppResult { + let request = PaymentApi.getPaymentAmount() + let result = await safeApi(request, decodeTo: ApiResponse.self) + + switch result { + case .success(let response): + if let data = response.data { + return .success(data) + } else { + return .failure(.init( + code: response.status, + message: response.message, + underlying: nil + )) + } + + case .failure(let error): + return .failure(error) + } + } + + func chargeDeposit(amount: Int) async -> AppResult { + let request = PaymentApi.chargeDeposit(amount: amount) + let result = await safeApi(request, decodeTo: ApiResponse.self) + + switch result { + case .success(let response): + return .success(response.data ?? response.message) + + case .failure(let error): + return .failure(error) + } + } +} diff --git a/StockMate/StockMate/app/feature/payment/domain/PaymentRepositoryProtocol.swift b/StockMate/StockMate/app/feature/payment/domain/PaymentRepositoryProtocol.swift new file mode 100644 index 0000000..150ab26 --- /dev/null +++ b/StockMate/StockMate/app/feature/payment/domain/PaymentRepositoryProtocol.swift @@ -0,0 +1,14 @@ +// +// PaymentRepositoryProtocol.swift +// StockMate +// +// Created by Admin on 10/29/25. +// + +import Foundation +import Alamofire + +protocol PaymentRepositoryProtocol { + func fetchDepositAmount() async -> AppResult + func chargeDeposit(amount: Int) async -> AppResult +} diff --git a/StockMate/StockMate/app/feature/payment/ui/DepositChargeView.swift b/StockMate/StockMate/app/feature/payment/ui/DepositChargeView.swift new file mode 100644 index 0000000..77987f5 --- /dev/null +++ b/StockMate/StockMate/app/feature/payment/ui/DepositChargeView.swift @@ -0,0 +1,115 @@ +import SwiftUI + +struct DepositChargeView: View { + @Environment(\.dismiss) private var dismiss + @ObservedObject var viewModel: DepositViewModel + @State private var amountText: String = "" + @State private var isCharging: Bool = false + + let keypad: [[String]] = [ + ["1","2","3"], + ["4","5","6"], + ["7","8","9"], + ["00","0","⌫"] + ] + + private var formattedNumberString: String { + if let value = Int(amountText) { + return value.formatted(.number) + } + return "0" + } + + private var formattedAmount: String { + return formattedNumberString + "원" + } + + func buttonAction(_ val: String) { + if val == "⌫" { + if !amountText.isEmpty { amountText.removeLast() } + } else { + amountText.append(val) + } + } + + var body: some View { + VStack(spacing: 20) { + + // Title + Text("예치금 충전") + .font(.system(size: 20, weight: .semibold)) + .padding(.top, 35) + + // 금액 + Text(formattedAmount) + .font(.system(size: 38, weight: .bold)) + .padding(.top, 35) + + // 아래 작은 라벨 +// Text("\(formattedNumberString)원") +// .font(.system(size: 15)) +// .foregroundColor(.textGray1) +// .padding(.horizontal, 10) +// .padding(.vertical, 4) +// .background(Color.white.opacity(0.9)) +// .cornerRadius(12) + + Spacer().frame(height: 80) + + // 키패드 + LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 0), count: 3), + spacing: 1) { + ForEach(keypad.flatMap { $0 }, id: \.self) { key in + Button { + buttonAction(key) + } label: { + Text(key) + .font(.system(size: 20)) + .frame(width: 50, height: 45) + .foregroundColor(Color.black) +// .background(Color.red.opacity(0.5)) + .padding(.horizontal, 4) + .background(Color.white) + } + } + } + .padding(.horizontal,16) + .padding(.top, 28) + + + // 충전 버튼 + Button { + Task { + guard let amount = Int(amountText), amount > 0 else { return } + isCharging = true + let success = await viewModel.chargeDeposit(amount: amount) + isCharging = false + if success { + viewModel.showChargeSheet = false + dismiss() + } + } + } label: { + if isCharging { + ProgressView() + .tint(.white) + .frame(height: 56) + .frame(maxWidth: .infinity) + } else { + Text("충전") + .font(.system(size: 18, weight: .bold)) + .foregroundColor(.white) + .frame(height: 59) + .frame(maxWidth: .infinity) + } + } + .background(Color.Primary) + .cornerRadius(28) + .padding(.top, 28) + .padding(.horizontal, 10) + } + .padding(.horizontal, 20) +// .padding(.top, 20) + .background(Color.white) + } +} diff --git a/StockMate/StockMate/app/feature/payment/viewmodel/DepositViewModel.swift b/StockMate/StockMate/app/feature/payment/viewmodel/DepositViewModel.swift new file mode 100644 index 0000000..27d6009 --- /dev/null +++ b/StockMate/StockMate/app/feature/payment/viewmodel/DepositViewModel.swift @@ -0,0 +1,46 @@ +// +// PaymentViewModel.swift +// StockMate +// +// Created by Admin on 10/29/25. +// + +import Foundation + +@MainActor +final class DepositViewModel: ObservableObject { + private let repository: PaymentRepositoryProtocol = PaymentRepositoryImpl() + + @Published var balance: Int = 0 + @Published var isLoading: Bool = false + @Published var showChargeSheet: Bool = false + + /// ✅ 예치금 조회 + func fetchDepositAmount() async { + isLoading = true + + let result = await repository.fetchDepositAmount() + + isLoading = false + + switch result { + case .success(let data): + self.balance = data.balance + case .failure(let error): + print("❌ 예치금 조회 실패:", error.message) + } + } + + /// ✅ 예치금 충전 + func chargeDeposit(amount: Int) async -> Bool { + let result = await repository.chargeDeposit(amount: amount) + switch result { + case .success(_): + await fetchDepositAmount() + return true + case .failure(let error): + print("❌ 충전 실패:", error.message) + return false + } + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/check.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/check.imageset/Contents.json new file mode 100644 index 0000000..17203cc --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/check.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "check.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/check.imageset/check.svg b/StockMate/StockMate/resources/Assets.xcassets/check.imageset/check.svg new file mode 100644 index 0000000..8bdc85f --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/check.imageset/check.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/StockMate/StockMate/resources/Assets.xcassets/deposit_background.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/deposit_background.imageset/Contents.json new file mode 100644 index 0000000..7c4374c --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/deposit_background.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "deposit_background@1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "deposit_background@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "deposit_background@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/deposit_background.imageset/deposit_background@1x.png b/StockMate/StockMate/resources/Assets.xcassets/deposit_background.imageset/deposit_background@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..b988925e9908e624cb8ec64cfc26bfdc0234a1f7 GIT binary patch literal 3184 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 al1N-gzcYW=1NfT=u(5JDRC9ou^nU<3qIA6g literal 0 HcmV?d00001 diff --git a/StockMate/StockMate/resources/Assets.xcassets/deposit_background.imageset/deposit_background@3x.png b/StockMate/StockMate/resources/Assets.xcassets/deposit_background.imageset/deposit_background@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..84218d3225a008f947b7d4f122a5683c69dc628e GIT binary patch literal 10386 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%0ABU9Fo%ljc6 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 literal 0 HcmV?d00001 diff --git a/StockMate/StockMate/resources/Assets.xcassets/dottedline.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/dottedline.imageset/Contents.json new file mode 100644 index 0000000..ab174a4 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/dottedline.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "dottedline.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/dottedline.imageset/dottedline.svg b/StockMate/StockMate/resources/Assets.xcassets/dottedline.imageset/dottedline.svg new file mode 100644 index 0000000..5f4b4b2 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/dottedline.imageset/dottedline.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/StockMate/StockMate/resources/Assets.xcassets/hourglass.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/hourglass.imageset/Contents.json new file mode 100644 index 0000000..9e19804 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/hourglass.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "hourglass.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/hourglass.imageset/hourglass.svg b/StockMate/StockMate/resources/Assets.xcassets/hourglass.imageset/hourglass.svg new file mode 100644 index 0000000..f229793 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/hourglass.imageset/hourglass.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/StockMate/StockMate/resources/Assets.xcassets/pindrop.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/pindrop.imageset/Contents.json new file mode 100644 index 0000000..b922f9e --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/pindrop.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "pindrop.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/pindrop.imageset/pindrop.svg b/StockMate/StockMate/resources/Assets.xcassets/pindrop.imageset/pindrop.svg new file mode 100644 index 0000000..f9cfacb --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/pindrop.imageset/pindrop.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/StockMate/StockMate/resources/Assets.xcassets/rocket.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/rocket.imageset/Contents.json new file mode 100644 index 0000000..288431e --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/rocket.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "rocket.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/rocket.imageset/rocket.svg b/StockMate/StockMate/resources/Assets.xcassets/rocket.imageset/rocket.svg new file mode 100644 index 0000000..b63f40e --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/rocket.imageset/rocket.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/StockMate/StockMate/resources/Assets.xcassets/uploadprogress.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/uploadprogress.imageset/Contents.json new file mode 100644 index 0000000..e776e0e --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/uploadprogress.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "uploadprogress.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/uploadprogress.imageset/uploadprogress.svg b/StockMate/StockMate/resources/Assets.xcassets/uploadprogress.imageset/uploadprogress.svg new file mode 100644 index 0000000..3e65143 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/uploadprogress.imageset/uploadprogress.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/StockMate/StockMate/resources/Color.swift b/StockMate/StockMate/resources/Color.swift index f927e60..94172ee 100644 --- a/StockMate/StockMate/resources/Color.swift +++ b/StockMate/StockMate/resources/Color.swift @@ -95,6 +95,8 @@ extension Color { static let Hstatus5Bg = Color(hex: "#ECEEF0") + static let Grayline = Color(hex: "#ECECED") + static let Grayline2 = Color(hex: "#DDDDDD") // 투명도 포함 예시 static let boxBgWhite = Color(hex: "#40FFFFFF") // 투명도 포함 From e01f5cc45b863c288f757bb38823720da2520758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Thu, 30 Oct 2025 16:18:54 +0900 Subject: [PATCH 36/85] =?UTF-8?q?[CHORE]=20svg=20=ED=8C=8C=EC=9D=BC=20sing?= =?UTF-8?q?le=20scale=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Assets.xcassets/chair.imageset/Contents.json | 11 +---------- .../Assets.xcassets/check.imageset/Contents.json | 11 +---------- .../Assets.xcassets/cog.imageset/Contents.json | 11 +---------- .../Assets.xcassets/dottedline.imageset/Contents.json | 11 +---------- .../Assets.xcassets/hourglass.imageset/Contents.json | 11 +---------- .../Assets.xcassets/lightbulb.imageset/Contents.json | 11 +---------- .../Assets.xcassets/location.imageset/Contents.json | 11 +---------- .../notification.imageset/Contents.json | 11 +---------- .../Assets.xcassets/package.imageset/Contents.json | 11 +---------- .../Assets.xcassets/pindrop.imageset/Contents.json | 11 +---------- .../Assets.xcassets/rocket.imageset/Contents.json | 11 +---------- .../Assets.xcassets/spanner.imageset/Contents.json | 11 +---------- .../Assets.xcassets/tabHome.imageset/Contents.json | 11 +---------- .../tabInventory.imageset/Contents.json | 11 +---------- .../Assets.xcassets/tabPackage.imageset/Contents.json | 11 +---------- .../Assets.xcassets/tabProfile.imageset/Contents.json | 11 +---------- .../uploadprogress.imageset/Contents.json | 11 +---------- 17 files changed, 17 insertions(+), 170 deletions(-) diff --git a/StockMate/StockMate/resources/Assets.xcassets/chair.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/chair.imageset/Contents.json index ad1d744..052b5da 100644 --- a/StockMate/StockMate/resources/Assets.xcassets/chair.imageset/Contents.json +++ b/StockMate/StockMate/resources/Assets.xcassets/chair.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "filename" : "chair.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "idiom" : "universal" } ], "info" : { diff --git a/StockMate/StockMate/resources/Assets.xcassets/check.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/check.imageset/Contents.json index 17203cc..b09981a 100644 --- a/StockMate/StockMate/resources/Assets.xcassets/check.imageset/Contents.json +++ b/StockMate/StockMate/resources/Assets.xcassets/check.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "filename" : "check.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "idiom" : "universal" } ], "info" : { diff --git a/StockMate/StockMate/resources/Assets.xcassets/cog.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/cog.imageset/Contents.json index 7124035..bd8698d 100644 --- a/StockMate/StockMate/resources/Assets.xcassets/cog.imageset/Contents.json +++ b/StockMate/StockMate/resources/Assets.xcassets/cog.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "filename" : "cog.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "idiom" : "universal" } ], "info" : { diff --git a/StockMate/StockMate/resources/Assets.xcassets/dottedline.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/dottedline.imageset/Contents.json index ab174a4..3551fde 100644 --- a/StockMate/StockMate/resources/Assets.xcassets/dottedline.imageset/Contents.json +++ b/StockMate/StockMate/resources/Assets.xcassets/dottedline.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "filename" : "dottedline.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "idiom" : "universal" } ], "info" : { diff --git a/StockMate/StockMate/resources/Assets.xcassets/hourglass.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/hourglass.imageset/Contents.json index 9e19804..900b128 100644 --- a/StockMate/StockMate/resources/Assets.xcassets/hourglass.imageset/Contents.json +++ b/StockMate/StockMate/resources/Assets.xcassets/hourglass.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "filename" : "hourglass.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "idiom" : "universal" } ], "info" : { diff --git a/StockMate/StockMate/resources/Assets.xcassets/lightbulb.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/lightbulb.imageset/Contents.json index beab311..e82017e 100644 --- a/StockMate/StockMate/resources/Assets.xcassets/lightbulb.imageset/Contents.json +++ b/StockMate/StockMate/resources/Assets.xcassets/lightbulb.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "filename" : "lightbulb.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "idiom" : "universal" } ], "info" : { diff --git a/StockMate/StockMate/resources/Assets.xcassets/location.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/location.imageset/Contents.json index 306ca5c..be50dad 100644 --- a/StockMate/StockMate/resources/Assets.xcassets/location.imageset/Contents.json +++ b/StockMate/StockMate/resources/Assets.xcassets/location.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "filename" : "location.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "idiom" : "universal" } ], "info" : { diff --git a/StockMate/StockMate/resources/Assets.xcassets/notification.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/notification.imageset/Contents.json index acfc443..5dabea1 100644 --- a/StockMate/StockMate/resources/Assets.xcassets/notification.imageset/Contents.json +++ b/StockMate/StockMate/resources/Assets.xcassets/notification.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "filename" : "notification.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "idiom" : "universal" } ], "info" : { diff --git a/StockMate/StockMate/resources/Assets.xcassets/package.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/package.imageset/Contents.json index e7f4c71..f289ed5 100644 --- a/StockMate/StockMate/resources/Assets.xcassets/package.imageset/Contents.json +++ b/StockMate/StockMate/resources/Assets.xcassets/package.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "filename" : "package.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "idiom" : "universal" } ], "info" : { diff --git a/StockMate/StockMate/resources/Assets.xcassets/pindrop.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/pindrop.imageset/Contents.json index b922f9e..01ce80a 100644 --- a/StockMate/StockMate/resources/Assets.xcassets/pindrop.imageset/Contents.json +++ b/StockMate/StockMate/resources/Assets.xcassets/pindrop.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "filename" : "pindrop.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "idiom" : "universal" } ], "info" : { diff --git a/StockMate/StockMate/resources/Assets.xcassets/rocket.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/rocket.imageset/Contents.json index 288431e..a61a5d8 100644 --- a/StockMate/StockMate/resources/Assets.xcassets/rocket.imageset/Contents.json +++ b/StockMate/StockMate/resources/Assets.xcassets/rocket.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "filename" : "rocket.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "idiom" : "universal" } ], "info" : { diff --git a/StockMate/StockMate/resources/Assets.xcassets/spanner.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/spanner.imageset/Contents.json index 36cb254..7fcd63e 100644 --- a/StockMate/StockMate/resources/Assets.xcassets/spanner.imageset/Contents.json +++ b/StockMate/StockMate/resources/Assets.xcassets/spanner.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "filename" : "spanner.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "idiom" : "universal" } ], "info" : { diff --git a/StockMate/StockMate/resources/Assets.xcassets/tabHome.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/tabHome.imageset/Contents.json index e847277..a1b0717 100644 --- a/StockMate/StockMate/resources/Assets.xcassets/tabHome.imageset/Contents.json +++ b/StockMate/StockMate/resources/Assets.xcassets/tabHome.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "filename" : "tabHome.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "idiom" : "universal" } ], "info" : { diff --git a/StockMate/StockMate/resources/Assets.xcassets/tabInventory.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/tabInventory.imageset/Contents.json index 3421138..53286b8 100644 --- a/StockMate/StockMate/resources/Assets.xcassets/tabInventory.imageset/Contents.json +++ b/StockMate/StockMate/resources/Assets.xcassets/tabInventory.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "filename" : "tabInventory.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "idiom" : "universal" } ], "info" : { diff --git a/StockMate/StockMate/resources/Assets.xcassets/tabPackage.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/tabPackage.imageset/Contents.json index c6df33b..d90fcd3 100644 --- a/StockMate/StockMate/resources/Assets.xcassets/tabPackage.imageset/Contents.json +++ b/StockMate/StockMate/resources/Assets.xcassets/tabPackage.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "filename" : "tabPackage.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "idiom" : "universal" } ], "info" : { diff --git a/StockMate/StockMate/resources/Assets.xcassets/tabProfile.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/tabProfile.imageset/Contents.json index 9bea2a3..25e0b53 100644 --- a/StockMate/StockMate/resources/Assets.xcassets/tabProfile.imageset/Contents.json +++ b/StockMate/StockMate/resources/Assets.xcassets/tabProfile.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "filename" : "tabProfile.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "idiom" : "universal" } ], "info" : { diff --git a/StockMate/StockMate/resources/Assets.xcassets/uploadprogress.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/uploadprogress.imageset/Contents.json index e776e0e..a1bff87 100644 --- a/StockMate/StockMate/resources/Assets.xcassets/uploadprogress.imageset/Contents.json +++ b/StockMate/StockMate/resources/Assets.xcassets/uploadprogress.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "filename" : "uploadprogress.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "idiom" : "universal" } ], "info" : { From 23e2adf24e3f78e45997f7cb8cf3c98376841dd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Fri, 31 Oct 2025 10:22:42 +0900 Subject: [PATCH 37/85] =?UTF-8?q?[FIX]=20=EC=BD=94=EB=93=9C=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- StockMate/StockMate/ContentView.swift | 50 ++----- .../feature/orders/ui/OrderDetailView.swift | 138 +++++++++--------- .../app/feature/orders/ui/OrderInfoView.swift | 13 -- .../app/feature/orders/ui/OrderListView.swift | 39 ----- .../payment/ui/DepositChargeView.swift | 2 + 5 files changed, 86 insertions(+), 156 deletions(-) diff --git a/StockMate/StockMate/ContentView.swift b/StockMate/StockMate/ContentView.swift index ccb290e..d576e34 100644 --- a/StockMate/StockMate/ContentView.swift +++ b/StockMate/StockMate/ContentView.swift @@ -6,52 +6,28 @@ // import SwiftUI -// -//struct DottedLine: View { -// var body: some View { -// Rectangle() -// .fill(Color.clear) -// .frame(height: 1) -// .overlay( -// Rectangle() -// .stroke(style: StrokeStyle(lineWidth: 1, dash: [3])) // <- 점선 패턴 -// .foregroundColor(.gray) -// ) -// } -//} struct ContentView: View { var body: some View { VStack { - Text("위쪽") -// DottedLine() - Text("아래쪽") + Image(systemName: "globe") + .imageScale(.large) + .foregroundStyle(.tint) + Text("임시 화면") } .padding() + HStack(spacing: 20) { + Image(systemName: "gearshape") + .font(.system(size: 40)) + .foregroundColor(.blue) + + Image(systemName: "lightbulb") + .font(.system(size: 40)) + .foregroundColor(.cyan) + } } } -//struct ContentView: View { -// var body: some View { -// VStack { -// Image(systemName: "globe") -// .imageScale(.large) -// .foregroundStyle(.tint) -// Text("임시 화면") -// } -// .padding() -// HStack(spacing: 20) { -// Image(systemName: "gearshape") -// .font(.system(size: 40)) -// .foregroundColor(.blue) -// -// Image(systemName: "lightbulb") -// .font(.system(size: 40)) -// .foregroundColor(.cyan) -// } -// } -//} - #Preview { ContentView() } diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift index c47aec8..c701163 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift @@ -153,7 +153,6 @@ struct OrderDetailView: View { // ✅ 하단 버튼 HStack(spacing: 12) { - // 왼쪽: 영수증 확인 // 왼쪽: 영수증 확인 NavigationLink(destination: ReceiptView(orderId: order.id)) { Text("영수증 확인") @@ -171,8 +170,10 @@ struct OrderDetailView: View { // 오른쪽 버튼: 주문 상태에 따라 변경 - if order.orderStatus == "ORDER_COMPLETED" { - // 주문 완료 → "주문취소" 버튼 + if order.orderStatus == "ORDER_COMPLETED" || + order.orderStatus == "PAY_COMPLETED" || + order.orderStatus == "PENDING_APPROVAL" { + // "주문취소" → 주문완료/결제완료/승인대기 Button(action: { Task { await orderViewModel.cancelOrder(orderId: orderId) @@ -187,8 +188,9 @@ struct OrderDetailView: View { .cornerRadius(10) } - } else if order.orderStatus == "ORDER_COMPLETED" { - // 배송 완료 → "입고 하기" 버튼 + } else if order.orderStatus == "PENDING_RECEIVING" || + order.orderStatus == "DELIVERED" { + // "입고 하기" → 입고대기/배송완료 Button(action: { // TODO: 입고 처리 버튼 }) { @@ -247,68 +249,7 @@ struct OrderDetailView: View { .font(.system(size: 14)) } - func formatDate(_ isoDate: String) -> String { - let comps = isoDate.split(separator: "T").first?.split(separator: "-") ?? [] - guard comps.count == 3 else { return isoDate } - return "\(comps[0])년 \(comps[1])월 \(comps[2])일" - } - - func statusText(_ status: String) -> String { - switch status { - case "ORDER_COMPLETED": return "주문 완료" // - case "PAY_COMPLETED": return "결제 완료" - case "PENDING_APPROVAL": return "승인 대기" // - case "FAILED": return "결제 실패" - case "PENDING_SHIPPING": return "출고 대기" - case "SHIPPING": return "배송중" - case "PENDING_RECEIVING": return "입고 대기" - case "REJECTED": return "주문 반려" - case "DELIVERED": return "배송 완료" - case "RECEIVED": return "입고 완료" - case "REFUNDED": return "환불 완료" - case "REFUND_REJECTED": return "환불 반려" - case "CANCELLED": return "주문 취소" - default: return "알 수 없음" - } - } - - func statusColor(_ status: String) -> Color { - switch status { - case "ORDER_COMPLETED": return .StatusGreen - case "PAY_COMPLETED": return .StatusGreen - case "PENDING_APPROVAL": return .Warning - case "FAILED": return .Danger - case "PENDING_SHIPPING": return .InvUse - case "SHIPPING": return .Transfer - case "PENDING_RECEIVING": return .Secondary - case "REJECTED": return .Danger - case "DELIVERED": return .Secondary - case "RECEIVED": return .StatusPurple - case "REFUNDED": return .Gray - case "REFUND_REJECTED": return .Gray - case "CANCELLED": return .Gray - default: return .gray.opacity(0.6) - } - } - - func statusBdColor(_ status: String) -> Color { - switch status { - case "ORDER_COMPLETED": return .StatusGreenBg - case "PAY_COMPLETED": return .StatusGreenBg - case "PENDING_APPROVAL": return .WarningBg - case "FAILED": return .DangerBg - case "PENDING_SHIPPING": return .InvUseBg - case "SHIPPING": return .TransferBg - case "PENDING_RECEIVING": return .LightBlue04 - case "REJECTED": return .DangerBg - case "DELIVERED": return .LightBlue04 - case "RECEIVED": return .StatusPurpleBg - case "REFUNDED": return Color(hex: "#EEEEEF") - case "REFUND_REJECTED": return Color(hex: "#EEEEEF") - case "CANCELLED": return Color(hex: "#EEEEEF") - default: return .gray.opacity(0.6) - } - } + } // ✅ 카드 레이아웃 통일용 @@ -365,3 +306,66 @@ func deliveryStep(for status: String) -> Int { default: return 6 } } + +func formatDate(_ isoDate: String) -> String { + let comps = isoDate.split(separator: "T").first?.split(separator: "-") ?? [] + guard comps.count == 3 else { return isoDate } + return "\(comps[0])년 \(comps[1])월 \(comps[2])일" +} + +func statusText(_ status: String) -> String { + switch status { + case "ORDER_COMPLETED": return "주문 완료" // 주문 완료 + case "PAY_COMPLETED": return "결제 완료" // 결제 완료 + case "PENDING_APPROVAL": return "승인 대기" // 승인대기 + case "FAILED": return "결제 실패" // 결제 실패 + case "PENDING_SHIPPING": return "출고 대기" // 출고 대기 + case "SHIPPING": return "배송중" // 배송중 + case "PENDING_RECEIVING": return "배송 완료" // 입고대기 + case "REJECTED": return "승인 반려" // 이론상 출고 반려 + case "DELIVERED": return "배송 완료" // 배송 완료 + case "RECEIVED": return "입고 완료" // 입고 완료 + case "REFUNDED": return "환불 완료" // 환불 완료 + case "REFUND_REJECTED": return "환불 반려" // 환불 반려 + case "CANCELLED": return "주문 취소" // 주문 취소 + default: return "알 수 없음" + } +} + +func statusColor(_ status: String) -> Color { + switch status { + case "ORDER_COMPLETED": return .StatusGreen + case "PAY_COMPLETED": return .StatusGreen + case "PENDING_APPROVAL": return .Warning + case "FAILED": return .Danger + case "PENDING_SHIPPING": return .InvUse + case "SHIPPING": return .Transfer + case "PENDING_RECEIVING": return .Secondary + case "REJECTED": return .Danger + case "DELIVERED": return .Secondary + case "RECEIVED": return .StatusPurple + case "REFUNDED": return .Gray + case "REFUND_REJECTED": return .Gray + case "CANCELLED": return .Gray + default: return .gray.opacity(0.6) + } +} + +func statusBdColor(_ status: String) -> Color { + switch status { + case "ORDER_COMPLETED": return .StatusGreenBg + case "PAY_COMPLETED": return .StatusGreenBg + case "PENDING_APPROVAL": return .WarningBg + case "FAILED": return .DangerBg + case "PENDING_SHIPPING": return .InvUseBg + case "SHIPPING": return .TransferBg + case "PENDING_RECEIVING": return .LightBlue04 + case "REJECTED": return .DangerBg + case "DELIVERED": return .LightBlue04 + case "RECEIVED": return .StatusPurpleBg + case "REFUNDED": return Color(hex: "#EEEEEF") + case "REFUND_REJECTED": return Color(hex: "#EEEEEF") + case "CANCELLED": return Color(hex: "#EEEEEF") + default: return .gray.opacity(0.6) + } +} diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift index 701c358..f32ac74 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift @@ -229,19 +229,6 @@ extension OrderInfoView { .cornerRadius(20) } .padding(.trailing, 5) - -// Button { -// depositViewModel.showChargeSheet = true -// } label: { -// Text("충전") -// .foregroundColor(Color.Primary) -// .font(.system(size: 14, weight: .bold)) -// .padding(.vertical, 6) -// .padding(.horizontal, 14) -// .background(Color.white) -// .cornerRadius(20) -// } -// .padding(.trailing, 5) } .padding(.bottom, 25) } diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift index 0dc04a5..a6e9aa5 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift @@ -155,43 +155,4 @@ struct OrderListCardView: View { .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 2) .padding(.horizontal) } - - func statusText(_ status: String) -> String { - switch status { - case "ORDER_COMPLETED": return "주문 완료" - case "PENDING_SHIPPING": return "출고 대기" - case "REJECTED": return "출고 반려" - case "SHIPPING": return "배송 중" - case "DELIVERED": return "배송 완료" - case "RECEIVED": return "입고 완료" - case "CANCELLED": return "주문 취소" - default: return "알 수 없음" - } - } - - func statusColor(_ status: String) -> Color { - switch status { - case "ORDER_COMPLETED": return .StatusGreen - case "PENDING_SHIPPING": return .InvUse - case "REJECTED": return .Danger - case "SHIPPING": return .Transfer - case "DELIVERED": return .Secondary - case "RECEIVED": return .StatusPurple - case "CANCELLED": return .gray - default: return .gray.opacity(0.6) - } - } - - func statusBdColor(_ status: String) -> Color { - switch status { - case "ORDER_COMPLETED": return .StatusGreenBg - case "PENDING_SHIPPING": return .InvUseBg - case "REJECTED": return .DangerBg - case "SHIPPING": return .TransferBg - case "DELIVERED": return .LightBlue04 - case "RECEIVED": return .StatusPurpleBg - case "CANCELLED": return Color(hex: "#EEEEEF") - default: return .gray.opacity(0.6) - } - } } diff --git a/StockMate/StockMate/app/feature/payment/ui/DepositChargeView.swift b/StockMate/StockMate/app/feature/payment/ui/DepositChargeView.swift index 77987f5..538412f 100644 --- a/StockMate/StockMate/app/feature/payment/ui/DepositChargeView.swift +++ b/StockMate/StockMate/app/feature/payment/ui/DepositChargeView.swift @@ -79,6 +79,7 @@ struct DepositChargeView: View { // 충전 버튼 Button { + guard !isCharging else { return } Task { guard let amount = Int(amountText), amount > 0 else { return } isCharging = true @@ -107,6 +108,7 @@ struct DepositChargeView: View { .cornerRadius(28) .padding(.top, 28) .padding(.horizontal, 10) + .disabled(isCharging) } .padding(.horizontal, 20) // .padding(.top, 20) From 259efb0655ffe0af710227bbd70295fb95512421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Fri, 31 Oct 2025 11:27:12 +0900 Subject: [PATCH 38/85] =?UTF-8?q?[REFAC]=20=EC=A3=BC=EB=AC=B8=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=ED=99=94=EB=A9=B4=20=EB=B0=B0=EC=B9=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/orders/ui/OrderDetailView.swift | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift index c701163..a963578 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift @@ -25,27 +25,35 @@ struct OrderDetailView: View { } .frame(maxWidth: .infinity, alignment: .center) - - // ✅ 주문 정보 + VStack(alignment: .leading, spacing: 6) { Text(formatDate(order.createdAt)) .font(.system(size: 15, weight: .semibold)) .padding(.bottom, 4) - infoRow("주문번호", order.orderNumber) - - HStack { - Text("상태") - .font(.system(size: 14)) - Spacer() - Text(statusText(order.orderStatus)) - .font(.system(size: 13, weight: .semibold)) - .padding(.horizontal, 10) - .padding(.vertical, 6) - .background(statusBdColor(order.orderStatus)) - .foregroundColor(statusColor(order.orderStatus)) - .cornerRadius(12) - .padding(.leading,4) + // ✅ 주문 정보 + HStack(alignment: .top, spacing: 6){ + VStack(alignment: .leading){ + Text("주문번호") + .font(.system(size: 14)) + .padding(.bottom, 4) + + Text("상태") + .font(.system(size: 14)) + } + + VStack(alignment: .leading){ + Text(order.orderNumber) + .font(.system(size: 14)) + + Text(statusText(order.orderStatus)) + .font(.system(size: 13, weight: .semibold)) + .padding(.horizontal, 10) + .padding(.vertical, 6) + .background(statusBdColor(order.orderStatus)) + .foregroundColor(statusColor(order.orderStatus)) + .cornerRadius(12) + }.padding(.leading) } } From 3c1d65bac679569b4935292d5345de18087c6535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Fri, 31 Oct 2025 15:03:31 +0900 Subject: [PATCH 39/85] =?UTF-8?q?[REFAC]=20=EC=A3=BC=EB=AC=B8=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20API=20=EC=9D=91=EB=8B=B5=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- StockMate/StockMate/app/feature/auth/ui/LoginView.swift | 7 ++++--- .../StockMate/app/feature/orders/data/OrderApi.swift | 9 ++++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift index 06e87e3..72b16ad 100644 --- a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift @@ -119,9 +119,10 @@ struct LoginView: View { // MARK: - 유효성 검사 함수 private func isValidForm() -> Bool { - emailError = isValidEmail(authViewModel.email) ? nil : "이메일 형식을 확인해주세요" - pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" - return emailError == nil && pwError == nil +// emailError = isValidEmail(authViewModel.email) ? nil : "이메일 형식을 확인해주세요" +// pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" +// return emailError == nil && pwError == nil + return true } } diff --git a/StockMate/StockMate/app/feature/orders/data/OrderApi.swift b/StockMate/StockMate/app/feature/orders/data/OrderApi.swift index 7032db0..3b073f1 100644 --- a/StockMate/StockMate/app/feature/orders/data/OrderApi.swift +++ b/StockMate/StockMate/app/feature/orders/data/OrderApi.swift @@ -104,14 +104,21 @@ struct OrderItems: Encodable { } // Response +//struct OrderCreateResponseData: Decodable { +// let orderId: Int +// let orderNumber: String +// let totalPrice: Int +// let orderStatus: String +//} struct OrderCreateResponseData: Decodable { let orderId: Int let orderNumber: String let totalPrice: Int - let orderStatus: String + let paymentType: String // ✅ 서버 필드와 맞춤 } + // MARK: - API Call enum OrderApi { From 2f74924d04196d41a6d153544e7a0b5fa84e3329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Fri, 31 Oct 2025 18:06:19 +0900 Subject: [PATCH 40/85] =?UTF-8?q?[FEAT]=20=EC=9E=85=EA=B3=A0/=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=20QR=20=EC=B2=98=EB=A6=AC=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- StockMate/StockMate.xcodeproj/project.pbxproj | 17 +++ StockMate/StockMate/ContentView.swift | 40 ++++++ StockMate/StockMate/Info.plist | 5 + .../inventory/ui/IncomingScanView.swift | 124 ++++++++++++------ .../feature/inventory/ui/InventoryView.swift | 2 +- .../inventory/ui/OutgoingScanView.swift | 120 +++++++++++++++++ .../app/feature/orders/data/OrderApi.swift | 18 ++- .../orders/data/OrderRepositoryImpl.swift | 13 ++ .../domain/OrderRepositoryProtocol.swift | 5 +- .../feature/orders/ui/OrderDetailView.swift | 58 +++++--- .../orders/viewmodel/OrderViewModel.swift | 29 ++++ .../app/feature/parts/data/PartApi.swift | 32 +++++ .../parts/data/PartRepositoryImpl.swift | 16 +++ .../parts/domain/PartRepositoryProtocol.swift | 12 ++ .../app/feature/parts/ui/QRScannerView.swift | 36 +++++ .../parts/ui/QRScannerViewController.swift | 69 ++++++++++ .../parts/viewmodel/PartViewModel.swift | 44 +++++++ 17 files changed, 582 insertions(+), 58 deletions(-) create mode 100644 StockMate/StockMate/Info.plist create mode 100644 StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift create mode 100644 StockMate/StockMate/app/feature/parts/data/PartApi.swift create mode 100644 StockMate/StockMate/app/feature/parts/data/PartRepositoryImpl.swift create mode 100644 StockMate/StockMate/app/feature/parts/domain/PartRepositoryProtocol.swift create mode 100644 StockMate/StockMate/app/feature/parts/ui/QRScannerView.swift create mode 100644 StockMate/StockMate/app/feature/parts/ui/QRScannerViewController.swift create mode 100644 StockMate/StockMate/app/feature/parts/viewmodel/PartViewModel.swift diff --git a/StockMate/StockMate.xcodeproj/project.pbxproj b/StockMate/StockMate.xcodeproj/project.pbxproj index 76ad704..788b909 100644 --- a/StockMate/StockMate.xcodeproj/project.pbxproj +++ b/StockMate/StockMate.xcodeproj/project.pbxproj @@ -14,9 +14,22 @@ 84BB0A132E91FE0E00A08CD6 /* StockMate.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = StockMate.app; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 84BB0D3C2EB49C0900A08CD6 /* Exceptions for "StockMate" folder in "StockMate" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + target = 84BB0A122E91FE0E00A08CD6 /* StockMate */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + /* Begin PBXFileSystemSynchronizedRootGroup section */ 84BB0A152E91FE0E00A08CD6 /* StockMate */ = { isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + 84BB0D3C2EB49C0900A08CD6 /* Exceptions for "StockMate" folder in "StockMate" target */, + ); path = StockMate; sourceTree = ""; }; @@ -263,6 +276,8 @@ DEVELOPMENT_TEAM = 35TSG7VB2B; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = StockMate/Info.plist; + INFOPLIST_KEY_NSCameraUsageDescription = "QR 코드를 스캔하기 위해 카메라 접근이 필요합니다."; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -291,6 +306,8 @@ DEVELOPMENT_TEAM = 35TSG7VB2B; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = StockMate/Info.plist; + INFOPLIST_KEY_NSCameraUsageDescription = "QR 코드를 스캔하기 위해 카메라 접근이 필요합니다."; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; diff --git a/StockMate/StockMate/ContentView.swift b/StockMate/StockMate/ContentView.swift index d576e34..4d36f82 100644 --- a/StockMate/StockMate/ContentView.swift +++ b/StockMate/StockMate/ContentView.swift @@ -7,6 +7,46 @@ import SwiftUI +//struct ContentView: View { +// @State private var showingScanner = false +// @State private var scannedCode: String? = nil +// +// var body: some View { +// NavigationView { +// VStack(spacing: 20) { +// if let code = scannedCode { +// Text("스캔 결과:") +// .font(.headline) +// Text(code) +// .font(.body) +// .multilineTextAlignment(.center) +// .padding() +// .background(Color(.systemGray6)) +// .cornerRadius(8) +// } else { +// Text("아직 스캔된 코드가 없습니다.") +// .foregroundColor(.secondary) +// } +// +// Button("QR 스캔 시작") { +// // 카메라 권한 체크는 시스템이 자동으로 권한 알림을 띄우므로 +// // 필요하면 권한 상태 확인 로직 추가 가능 +// showingScanner = true +// } +// .buttonStyle(.borderedProminent) +// .padding(.top) +// +// Spacer() +// } +// .padding() +// .navigationTitle("QR 스캐너 예제") +// .sheet(isPresented: $showingScanner) { +// QRScannerView(isPresented: $showingScanner, scannedCode: $scannedCode) +// .edgesIgnoringSafeArea(.all) +// } +// } +// } +//} struct ContentView: View { var body: some View { VStack { diff --git a/StockMate/StockMate/Info.plist b/StockMate/StockMate/Info.plist new file mode 100644 index 0000000..0c67376 --- /dev/null +++ b/StockMate/StockMate/Info.plist @@ -0,0 +1,5 @@ + + + + + diff --git a/StockMate/StockMate/app/feature/inventory/ui/IncomingScanView.swift b/StockMate/StockMate/app/feature/inventory/ui/IncomingScanView.swift index 83d0415..18e375a 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/IncomingScanView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/IncomingScanView.swift @@ -8,52 +8,100 @@ import SwiftUI struct IncomingScanView: View { + @Environment(\.dismiss) private var dismiss + @State private var scannedCode: String? = nil + @State private var showAlert = false + @State private var alertMessage = "" + + @StateObject private var orderViewModel = OrderViewModel() // ✅ 뷰모델 추가 + var body: some View { - VStack(spacing: 30) { - // 상단 타이틀 - Text("입고 부품의 QR을 스캔해주세요") - .font(.headline) - .padding(.top, 30) - - // 스캔 영역 - ZStack { - RoundedRectangle(cornerRadius: 12) - .fill(Color.gray.opacity(0.2)) - .frame(width: 250, height: 250) - - RoundedRectangle(cornerRadius: 8) - .stroke(Color.blue, lineWidth: 3) - .frame(width: 220, height: 220) + ZStack { + // ✅ 1. 카메라 화면 (QR 스캐너) + QRScannerView(scannedCode: $scannedCode) + .ignoresSafeArea() + + // ✅ 2. 스캔 영역 가이드 박스 + VStack { + Text("입고 부품의 QR을 스캔해주세요") + .font(.headline) + .padding(.top, 60) + .foregroundColor(.white) + .shadow(radius: 2) + + Spacer() + + ZStack { + RoundedRectangle(cornerRadius: 12) + .fill(Color.clear) + .frame(width: 250, height: 250) + + RoundedRectangle(cornerRadius: 8) + .stroke(Color.blue, lineWidth: 3) + .frame(width: 220, height: 220) + } + .padding(.bottom, 180) + + Spacer() + + // ✅ 직접 등록 버튼 + Button(action: { + dismiss() + }) { + Text("직접 등록 하기") + .fontWeight(.semibold) + .foregroundColor(.black) + .frame(maxWidth: .infinity) + .padding() + .background(Color.white) + .cornerRadius(10) + .shadow(color: .gray.opacity(0.3), radius: 2, x: 0, y: 2) + } + .padding(.horizontal, 40) + .padding(.bottom, 40) } - .padding(.top, 20) - - Spacer() - - // 직접 등록 버튼 - Button(action: { - print("직접 등록 tapped") - }) { - Text("직접 등록 하기") - .fontWeight(.semibold) - .foregroundColor(.black) - .frame(maxWidth: .infinity) + + // ✅ 로딩 표시 + if orderViewModel.isLoading { + Color.black.opacity(0.3).ignoresSafeArea() + ProgressView("입고 처리 중...") .padding() - .background(Color.white) + .background(.ultraThinMaterial) .cornerRadius(10) - .shadow(color: .gray.opacity(0.3), radius: 2, x: 0, y: 2) } - .padding(.horizontal, 40) - .padding(.bottom, 40) } - .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(Color.gray.opacity(0.1)) + .alert("입고 처리 결과", isPresented: $showAlert) { + Button("확인") { + dismiss() + } + } message: { + Text(alertMessage) + } + .onChange(of: scannedCode) { newValue in + guard let code = newValue, !code.isEmpty else { return } + Task { + await handleScannedCode(code) + } + } .navigationTitle("입고 부품 등록") .navigationBarTitleDisplayMode(.inline) } -} - -#Preview { - NavigationStack { - IncomingScanView() + + private func handleScannedCode(_ code: String) async { + await MainActor.run { + orderViewModel.isLoading = true + } + + let result = await orderViewModel.receiveOrder(orderNumber: code) + await MainActor.run { + orderViewModel.isLoading = false + switch result { + case .success(let message): + alertMessage = message + case .failure(let error): + alertMessage = error.message + } + showAlert = true + } } } diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift index 35545c0..9641e2e 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift @@ -126,7 +126,7 @@ struct GridMenuView: View { ("재고 조회", true, "InvStock", AnyView(InventorySearchView())), ("입출고 히스토리", false, "InvTrans", AnyView(IncomingScanView())), ("입고 처리", false, "InvIncoming", AnyView(IncomingScanView())), - ("사용 처리", true, "InvUse", AnyView(IncomingScanView())), + ("사용 처리", true, "InvUse", AnyView(OutgoingScanView())), ] var body: some View { diff --git a/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift b/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift new file mode 100644 index 0000000..506c0fa --- /dev/null +++ b/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift @@ -0,0 +1,120 @@ +// +// OutgoingScanView.swift +// StockMate +// +// Created by Admin on 10/31/25. +// + + +import SwiftUI + +struct OutgoingScanView: View { + @Environment(\.dismiss) private var dismiss + @State private var scannedCode: String? = nil + @State private var showAlert = false + @State private var alertMessage = "" + + @StateObject private var partViewModel = PartViewModel() // ✅ ViewModel 추가 + + var body: some View { + ZStack { + // ✅ 카메라 미리보기 (QR 스캐너) + QRScannerView(scannedCode: $scannedCode) + .ignoresSafeArea() + + // ✅ 스캔 가이드 및 UI 오버레이 + VStack { + Text("사용할 부품의 QR을 스캔해주세요") + .font(.headline) + .padding(.top, 60) + .foregroundColor(.white) + .shadow(radius: 2) + + Spacer() + + // 📷 스캔 박스 + ZStack { + RoundedRectangle(cornerRadius: 12) + .fill(Color.clear) + .frame(width: 250, height: 250) + + RoundedRectangle(cornerRadius: 8) + .stroke(Color.green, lineWidth: 3) + .frame(width: 220, height: 220) + } + .padding(.bottom, 180) + + Spacer() + + // 📦 직접 입력 버튼 + Button(action: { + dismiss() + }) { + Text("직접 입력 하기") + .fontWeight(.semibold) + .foregroundColor(.black) + .frame(maxWidth: .infinity) + .padding() + .background(Color.white) + .cornerRadius(10) + .shadow(color: .gray.opacity(0.3), radius: 2, x: 0, y: 2) + } + .padding(.horizontal, 40) + .padding(.bottom, 40) + } + + // ✅ 로딩 인디케이터 + if partViewModel.isLoading { + Color.black.opacity(0.3).ignoresSafeArea() + ProgressView("부품 사용 처리 중...") + .padding() + .background(.ultraThinMaterial) + .cornerRadius(10) + } + } + // ✅ 알림창 + .alert("부품 사용 결과", isPresented: $showAlert) { + Button("확인") { + dismiss() + } + } message: { + Text(alertMessage) + } + // ✅ QR 스캔 이벤트 발생 시 + .onChange(of: scannedCode) { newValue in + guard let code = newValue, !code.isEmpty else { return } + Task { + await handleScannedCode(code) + } + } + .navigationTitle("부품 사용 처리") + .navigationBarTitleDisplayMode(.inline) + } + + // ✅ 스캔된 코드로 출고 API 호출 + private func handleScannedCode(_ code: String) async { + await MainActor.run { + partViewModel.isLoading = true + } + + let request = [ReleaseItemRequest(partCode: code, quantity: 1)] // 기본 1개로 설정 + let result = await partViewModel.releaseParts(items: request) + + await MainActor.run { + partViewModel.isLoading = false + switch result { + case .success(let message): + alertMessage = message + case .failure(let error): + alertMessage = error.message + } + showAlert = true + } + } +} + +#Preview { + NavigationStack { + OutgoingScanView() + } +} diff --git a/StockMate/StockMate/app/feature/orders/data/OrderApi.swift b/StockMate/StockMate/app/feature/orders/data/OrderApi.swift index 3b073f1..ef3d651 100644 --- a/StockMate/StockMate/app/feature/orders/data/OrderApi.swift +++ b/StockMate/StockMate/app/feature/orders/data/OrderApi.swift @@ -110,6 +110,7 @@ struct OrderItems: Encodable { // let totalPrice: Int // let orderStatus: String //} + struct OrderCreateResponseData: Decodable { let orderId: Int let orderNumber: String @@ -117,6 +118,11 @@ struct OrderCreateResponseData: Decodable { let paymentType: String // ✅ 서버 필드와 맞춤 } +// ✅ 입고 처리 요청 API +struct ReceiveOrderRequest: Encodable { + let orderNumber: String +} + // MARK: - API Call @@ -171,5 +177,15 @@ enum OrderApi { return ApiClient.shared.request(url, method: .put) .validate() // ✅ 서버 상태코드 확인 } - + + // ✅ 입고 처리 API + static func receiveOrder(_ requestBody: ReceiveOrderRequest) -> DataRequest { + let url = ApiClient.baseURL + "api/v1/order/receive" + return ApiClient.shared.request( + url, + method: .post, + parameters: requestBody, + encoder: JSONParameterEncoder.default + ) + } } diff --git a/StockMate/StockMate/app/feature/orders/data/OrderRepositoryImpl.swift b/StockMate/StockMate/app/feature/orders/data/OrderRepositoryImpl.swift index 0f4d42f..c670736 100644 --- a/StockMate/StockMate/app/feature/orders/data/OrderRepositoryImpl.swift +++ b/StockMate/StockMate/app/feature/orders/data/OrderRepositoryImpl.swift @@ -90,4 +90,17 @@ final class OrderRepositoryImpl: OrderRepositoryProtocol { } } + func receiveOrder(orderNumber: String) async -> AppResult { + let request = OrderApi.receiveOrder(.init(orderNumber: orderNumber)) + let result = await safeApi(request, decodeTo: ApiResponse.self) + + switch result { + case .success(let response): + print("✅ 입고 처리 성공:", response) + return .success(response.data ?? "입고 처리가 완료되었습니다.") + case .failure(let error): + print("❌ 입고 처리 실패:", error.message) + return .failure(error) + } + } } diff --git a/StockMate/StockMate/app/feature/orders/domain/OrderRepositoryProtocol.swift b/StockMate/StockMate/app/feature/orders/domain/OrderRepositoryProtocol.swift index 9b98ce6..81be290 100644 --- a/StockMate/StockMate/app/feature/orders/domain/OrderRepositoryProtocol.swift +++ b/StockMate/StockMate/app/feature/orders/domain/OrderRepositoryProtocol.swift @@ -24,6 +24,9 @@ protocol OrderRepositoryProtocol { // 주문 생성 func createOrder(request: OrderRequest) async -> AppResult - + // 주문 취소 func cancelOrder(orderId: Int) async -> AppResult + + // 입고 처리 + func receiveOrder(orderNumber: String) async -> AppResult } diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift index a963578..0e4c5de 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift @@ -25,27 +25,21 @@ struct OrderDetailView: View { } .frame(maxWidth: .infinity, alignment: .center) - + + // ✅ 주문 정보 VStack(alignment: .leading, spacing: 6) { Text(formatDate(order.createdAt)) .font(.system(size: 15, weight: .semibold)) .padding(.bottom, 4) - // ✅ 주문 정보 + infoRow("주문번호", order.orderNumber) + .padding(.bottom, 4) + + HStack(alignment: .top, spacing: 6){ - VStack(alignment: .leading){ - Text("주문번호") - .font(.system(size: 14)) - .padding(.bottom, 4) - Text("상태") .font(.system(size: 14)) - } - - VStack(alignment: .leading){ - Text(order.orderNumber) - .font(.system(size: 14)) - + Spacer() Text(statusText(order.orderStatus)) .font(.system(size: 13, weight: .semibold)) .padding(.horizontal, 10) @@ -53,7 +47,6 @@ struct OrderDetailView: View { .background(statusBdColor(order.orderStatus)) .foregroundColor(statusColor(order.orderStatus)) .cornerRadius(12) - }.padding(.leading) } } @@ -63,7 +56,23 @@ struct OrderDetailView: View { .cornerRadius(16) .shadow(color: .black.opacity(0.05), radius: 3, y: 2) - + // 승인 반려인 경우에만 반려메세지 칸 생성 + if order.orderStatus == "REJECTED" { + VStack(alignment: .leading, spacing: 6) { + Text("반려 메세지") + .font(.system(size: 15, weight: .semibold)) + .padding(.bottom, 4) + Text(order.rejectedMessage ?? "-") + .font(.system(size: 14)) + + } + .frame(maxWidth: .infinity, alignment: .leading) // ✅ 여기도 추가 + .padding(.all, 20) + .background(Color.white) + .cornerRadius(16) + .shadow(color: .black.opacity(0.05), radius: 3, y: 2) + } + // ✅ 배송 정보 VStack(alignment: .leading, spacing: 6) { Text("배송정보") @@ -83,15 +92,30 @@ struct OrderDetailView: View { return "-" }() infoRow("운송장번호", trackingText) + } + .frame(maxWidth: .infinity, alignment: .leading) // ✅ 여기도 추가 + .padding(.all, 20) + .background(Color.white) + .cornerRadius(16) + .shadow(color: .black.opacity(0.05), radius: 3, y: 2) + + + // 요청사항 따로 빼기 + VStack(alignment: .leading, spacing: 6) { + Text("요청사항") + .font(.system(size: 15, weight: .semibold)) + .padding(.bottom, 4) + + Text(order.etc ?? "") + .font(.system(size: 14)) - infoRow("요청사항", order.etc ?? "") } .frame(maxWidth: .infinity, alignment: .leading) // ✅ 여기도 추가 .padding(.all, 20) .background(Color.white) .cornerRadius(16) .shadow(color: .black.opacity(0.05), radius: 3, y: 2) - + // ✅ 주문 상품 OrderSectionCard { diff --git a/StockMate/StockMate/app/feature/orders/viewmodel/OrderViewModel.swift b/StockMate/StockMate/app/feature/orders/viewmodel/OrderViewModel.swift index dccd03f..447274b 100644 --- a/StockMate/StockMate/app/feature/orders/viewmodel/OrderViewModel.swift +++ b/StockMate/StockMate/app/feature/orders/viewmodel/OrderViewModel.swift @@ -78,6 +78,35 @@ final class OrderViewModel: ObservableObject { } isLoading = false } + + func receiveOrder(orderNumber: String) async -> AppResult { + isLoading = true + defer { isLoading = false } + + let result = await repository.receiveOrder(orderNumber: orderNumber) + switch result { + case .success(let message): + print("✅ 입고 처리 성공:", message) + await loadOrders() + return .success(message) + case .failure(let error): + errorMessage = error.message + return .failure(error) + } + } +// func receiveOrder(orderNumber: String) async { +// isLoading = true +// defer { isLoading = false } +// +// let result = await repository.receiveOrder(orderNumber: orderNumber) +// switch result { +// case .success(let message): +// print("✅ 입고 처리 성공:", message) +// await loadOrders() // 리스트 갱신 +// case .failure(let error): +// errorMessage = error.message +// } +// } } diff --git a/StockMate/StockMate/app/feature/parts/data/PartApi.swift b/StockMate/StockMate/app/feature/parts/data/PartApi.swift new file mode 100644 index 0000000..9c5070d --- /dev/null +++ b/StockMate/StockMate/app/feature/parts/data/PartApi.swift @@ -0,0 +1,32 @@ +// +// PartApi.swift +// StockMate +// +// Created by Admin on 10/31/25. +// + + +import Foundation +import Alamofire + +// ✅ 요청 모델 +struct ReleaseItemRequest: Encodable { + let partCode: String + let quantity: Int +} + +// ✅ API 정의 +enum PartApi { + static func releaseParts(items: [ReleaseItemRequest]) -> DataRequest { + let url = ApiClient.baseURL + "api/v1/store/release" + let body: [String: Any] = [ + "items": items.map { ["partCode": $0.partCode, "quantity": $0.quantity] } + ] + return ApiClient.shared.request( + url, + method: .post, + parameters: body, + encoding: JSONEncoding.default + ) + } +} diff --git a/StockMate/StockMate/app/feature/parts/data/PartRepositoryImpl.swift b/StockMate/StockMate/app/feature/parts/data/PartRepositoryImpl.swift new file mode 100644 index 0000000..7ac1c08 --- /dev/null +++ b/StockMate/StockMate/app/feature/parts/data/PartRepositoryImpl.swift @@ -0,0 +1,16 @@ +// +// PartRepositoryImpl.swift +// StockMate +// +// Created by Admin on 10/31/25. +// + +import Foundation +import Alamofire + +final class PartRepositoryImpl: PartRepositoryProtocol { + func releaseParts(items: [ReleaseItemRequest]) async -> AppResult> { + let dataReq = PartApi.releaseParts(items: items) + return await safeApi(dataReq, decodeTo: ApiResponse.self) + } +} diff --git a/StockMate/StockMate/app/feature/parts/domain/PartRepositoryProtocol.swift b/StockMate/StockMate/app/feature/parts/domain/PartRepositoryProtocol.swift new file mode 100644 index 0000000..f3a939e --- /dev/null +++ b/StockMate/StockMate/app/feature/parts/domain/PartRepositoryProtocol.swift @@ -0,0 +1,12 @@ +// +// PartRepositoryProtocol.swift +// StockMate +// +// Created by Admin on 10/31/25. +// + +import Foundation + +protocol PartRepositoryProtocol { + func releaseParts(items: [ReleaseItemRequest]) async -> AppResult> +} diff --git a/StockMate/StockMate/app/feature/parts/ui/QRScannerView.swift b/StockMate/StockMate/app/feature/parts/ui/QRScannerView.swift new file mode 100644 index 0000000..761468f --- /dev/null +++ b/StockMate/StockMate/app/feature/parts/ui/QRScannerView.swift @@ -0,0 +1,36 @@ +// +// QRScannerView.swift +// StockMate +// +// Created by Admin on 10/31/25. +// + +import SwiftUI + +struct QRScannerView: UIViewControllerRepresentable { + @Binding var scannedCode: String? + + func makeUIViewController(context: Context) -> QRScannerViewController { + let controller = QRScannerViewController() + controller.delegate = context.coordinator + return controller + } + + func updateUIViewController(_ uiViewController: QRScannerViewController, context: Context) {} + + func makeCoordinator() -> Coordinator { + Coordinator(self) + } + + final class Coordinator: NSObject, QRScannerDelegate { + let parent: QRScannerView + + init(_ parent: QRScannerView) { + self.parent = parent + } + + func didScanQRCode(_ code: String) { + parent.scannedCode = code + } + } +} diff --git a/StockMate/StockMate/app/feature/parts/ui/QRScannerViewController.swift b/StockMate/StockMate/app/feature/parts/ui/QRScannerViewController.swift new file mode 100644 index 0000000..f338a55 --- /dev/null +++ b/StockMate/StockMate/app/feature/parts/ui/QRScannerViewController.swift @@ -0,0 +1,69 @@ +// +// QRScannerViewController.swift +// StockMate +// +// Created by Admin on 10/31/25. +// + +import UIKit +import AVFoundation + +protocol QRScannerDelegate: AnyObject { + func didScanQRCode(_ code: String) +} + +final class QRScannerViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate { + weak var delegate: QRScannerDelegate? + + private var captureSession: AVCaptureSession! + private var previewLayer: AVCaptureVideoPreviewLayer! + + override func viewDidLoad() { + super.viewDidLoad() + setupCamera() + } + + private func setupCamera() { + captureSession = AVCaptureSession() + + guard let videoCaptureDevice = AVCaptureDevice.default(for: .video), + let videoInput = try? AVCaptureDeviceInput(device: videoCaptureDevice), + captureSession.canAddInput(videoInput) + else { + print("카메라 입력 불가") + return + } + + captureSession.addInput(videoInput) + + let metadataOutput = AVCaptureMetadataOutput() + guard captureSession.canAddOutput(metadataOutput) else { + print("출력 연결 불가") + return + } + + captureSession.addOutput(metadataOutput) + metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main) + metadataOutput.metadataObjectTypes = [.qr] + + previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) + previewLayer.frame = view.layer.bounds + previewLayer.videoGravity = .resizeAspectFill + view.layer.addSublayer(previewLayer) + + // ⚠️ 백그라운드에서 실행 + DispatchQueue.global(qos: .userInitiated).async { + self.captureSession.startRunning() + } + } + + + func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) { + if let metadataObject = metadataObjects.first as? AVMetadataMachineReadableCodeObject, + let stringValue = metadataObject.stringValue { + captureSession.stopRunning() + delegate?.didScanQRCode(stringValue) + } + } +} + diff --git a/StockMate/StockMate/app/feature/parts/viewmodel/PartViewModel.swift b/StockMate/StockMate/app/feature/parts/viewmodel/PartViewModel.swift new file mode 100644 index 0000000..fc3fecd --- /dev/null +++ b/StockMate/StockMate/app/feature/parts/viewmodel/PartViewModel.swift @@ -0,0 +1,44 @@ +// +// PartViewModel.swift +// StockMate +// +// Created by Admin on 10/31/25. +// + + +import SwiftUI + +@MainActor +final class PartViewModel: ObservableObject { + @Published var isLoading = false + @Published var message: String = "" + @Published var shouldGoToLogin = false + + private let repo: PartRepositoryProtocol + + init(repo: PartRepositoryProtocol = PartRepositoryImpl()) { + self.repo = repo + } + + func releaseParts(items: [ReleaseItemRequest]) async -> AppResult { + isLoading = true + defer { isLoading = false } + + let result = await repo.releaseParts(items: items) + switch result { + case .success(let apiResp): + message = apiResp.message + if let data = apiResp.data { + return .success(data) + } else { + return .failure(AppError(code: apiResp.status, message: apiResp.message, underlying: nil)) + } + case .failure(let err): + message = err.message + if err.code == 401 || err.code == 403 { + shouldGoToLogin = true + } + return .failure(err) + } + } +} \ No newline at end of file From 574be77383ee8c8afd9fc8247e4795854cfdb2e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sat, 1 Nov 2025 17:35:59 +0900 Subject: [PATCH 41/85] =?UTF-8?q?[CHORE]=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=A0=95=EA=B7=9C=EC=8B=9D=20=ED=99=9C=EC=84=B1=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- StockMate/StockMate/app/feature/auth/ui/LoginView.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift index 72b16ad..27236a7 100644 --- a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift @@ -119,9 +119,9 @@ struct LoginView: View { // MARK: - 유효성 검사 함수 private func isValidForm() -> Bool { -// emailError = isValidEmail(authViewModel.email) ? nil : "이메일 형식을 확인해주세요" -// pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" -// return emailError == nil && pwError == nil + emailError = isValidEmail(authViewModel.email) ? nil : "이메일 형식을 확인해주세요" + pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" + return emailError == nil && pwError == nil return true } From 484984b26322ee4c245bade20d100036dcd0476b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sun, 2 Nov 2025 15:20:26 +0900 Subject: [PATCH 42/85] =?UTF-8?q?[FEAT]=20=EC=9E=85=EC=B6=9C=EA=B3=A0=20?= =?UTF-8?q?=ED=9E=88=EC=8A=A4=ED=86=A0=EB=A6=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/dashboard/data/HistoryApi.swift | 73 ++++++++ .../data/HistoryRepositoryImpl.swift | 16 ++ .../domain/HistoryRepositoryProtocol.swift | 13 ++ .../dashboard/ui/InOutHistoryView.swift | 167 ++++++++++++++++++ .../dashboard/ui/ReleaseDetailView.swift | 156 ++++++++++++++++ .../viewmodel/HistoryViewModel.swift | 62 +++++++ .../feature/inventory/ui/InventoryView.swift | 2 +- .../app/feature/orders/ui/OrderListView.swift | 3 - 8 files changed, 488 insertions(+), 4 deletions(-) create mode 100644 StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift create mode 100644 StockMate/StockMate/app/feature/dashboard/data/HistoryRepositoryImpl.swift create mode 100644 StockMate/StockMate/app/feature/dashboard/domain/HistoryRepositoryProtocol.swift create mode 100644 StockMate/StockMate/app/feature/dashboard/ui/InOutHistoryView.swift create mode 100644 StockMate/StockMate/app/feature/dashboard/ui/ReleaseDetailView.swift create mode 100644 StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift diff --git a/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift b/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift new file mode 100644 index 0000000..1690fa7 --- /dev/null +++ b/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift @@ -0,0 +1,73 @@ +// +// HistoryApi.swift +// StockMate +// +// Created by Admin on 11/1/25. +// + +import Foundation +import Alamofire + +// MARK: - 입출고 히스토리 데이터 구조 +struct HistoryPageData: Decodable { + let totalElements: Int + let totalPages: Int + let currentPage: Int + let pageSize: Int + let content: [HistoryItem] + let last: Bool +} + +struct HistoryItem: Decodable, Identifiable { + let id: Int + let memberId: Int + let orderId: Int? + let orderNumber: String? + let message: String + let status: String + let type: String + let createdAt: String + let updatedAt: String + let userInfo: HistoryUserInfo? + let items: [HistoryPart] +} + +struct HistoryUserInfo: Decodable { + let id: Int + let memberId: Int + let email: String + let owner: String + let address: String + let storeName: String + let businessNumber: String + let role: String + let verified: String + let latitude: Double + let longitude: Double +} + +struct HistoryPart: Decodable, Identifiable { + let id: Int + let name: String + let price: Int + let image: String + let trim: String + let model: String + let category: Int + let korName: String + let engName: String + let categoryName: String + let amount: Int + let code: String + let location: String + let cost: Int + let historyQuantity: Int +} +// MARK: - API +enum HistoryApi { + // ✅ 가맹점별 입출고 히스토리 조회 + static func getInOutHistory(page: Int = 0, size: Int = 20) -> DataRequest { + let url = ApiClient.baseURL + "api/v1/information/order-history/my?page=\(page)&size=\(size)" + return ApiClient.shared.request(url, method: .get) + } +} diff --git a/StockMate/StockMate/app/feature/dashboard/data/HistoryRepositoryImpl.swift b/StockMate/StockMate/app/feature/dashboard/data/HistoryRepositoryImpl.swift new file mode 100644 index 0000000..694de1a --- /dev/null +++ b/StockMate/StockMate/app/feature/dashboard/data/HistoryRepositoryImpl.swift @@ -0,0 +1,16 @@ +// +// HistoryRepositoryImpl.swift +// StockMate +// +// Created by Admin on 11/1/25. +// + +import Foundation +import Alamofire + +final class HistoryRepositoryImpl: HistoryRepositoryProtocol { + func getInOutHistory(page: Int, size: Int) async -> AppResult> { + let request = HistoryApi.getInOutHistory(page: page, size: size) + return await safeApi(request, decodeTo: ApiResponse.self) + } +} diff --git a/StockMate/StockMate/app/feature/dashboard/domain/HistoryRepositoryProtocol.swift b/StockMate/StockMate/app/feature/dashboard/domain/HistoryRepositoryProtocol.swift new file mode 100644 index 0000000..0816b5d --- /dev/null +++ b/StockMate/StockMate/app/feature/dashboard/domain/HistoryRepositoryProtocol.swift @@ -0,0 +1,13 @@ +// +// HistoryRepositoryProtocol.swift +// StockMate +// +// Created by Admin on 11/1/25. +// + +import Foundation +import Alamofire + +protocol HistoryRepositoryProtocol { + func getInOutHistory(page: Int, size: Int) async -> AppResult> +} diff --git a/StockMate/StockMate/app/feature/dashboard/ui/InOutHistoryView.swift b/StockMate/StockMate/app/feature/dashboard/ui/InOutHistoryView.swift new file mode 100644 index 0000000..43be7ab --- /dev/null +++ b/StockMate/StockMate/app/feature/dashboard/ui/InOutHistoryView.swift @@ -0,0 +1,167 @@ +// +// InOutHistoryView.swift +// StockMate +// +// Created by Admin on 11/1/25. +// + +import SwiftUI + +struct InOutHistoryView: View { + @StateObject private var viewModel = HistoryViewModel() + + var body: some View { + VStack { + if viewModel.isLoading { + ProgressView("불러오는 중...") + .frame(maxWidth: .infinity, maxHeight: .infinity) + } else if let error = viewModel.errorMessage { + Text(error) + .foregroundColor(.red) + .multilineTextAlignment(.center) + .padding() + } else if viewModel.histories.isEmpty { + Text("입출고 내역이 없습니다.") + .foregroundColor(.gray) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } else { + // ✅ 날짜별로 그룹화 (최신순) + let groupedHistories = Dictionary(grouping: viewModel.histories) { history in + history.createdAt.split(separator: "T").first.map(String.init) ?? "" + } + .sorted { $0.key > $1.key } // 최신순 정렬 + + ScrollView { + LazyVStack(alignment: .leading, spacing: 20) { + ForEach(groupedHistories, id: \.key) { date, histories in + VStack(alignment: .leading, spacing: 12) { + // ✅ 날짜 헤더 + Text(formatDate(String(date))) + .font(.headline) + .padding(.leading, 25) + .padding(.top) + + // ✅ 해당 날짜의 히스토리 카드들 + ForEach(histories) { history in + InOutHistoryCard(history: history) + } + } + } + } + .padding(.bottom) + } + .padding(.top) + } + } + .background(Color.Light) + .navigationTitle("입출고 내역") + .task { + await viewModel.fetchInOutHistory() + } + } + + func formatDate(_ dateString: String) -> String { + // yyyy-MM-dd → yyyy년 MM월 dd일 + let comps = dateString.split(separator: "-") + guard comps.count == 3 else { return dateString } + return "\(comps[0])년 \(comps[1])월 \(comps[2])일" + } +} + + +struct InOutHistoryCard: View { + let history: HistoryItem + + var body: some View { + HStack(spacing: 10) { + if let firstItem = history.items.first { + HStack(alignment: .center, spacing: 12) { + AsyncImage(url: URL(string: firstItem.image)) { image in + image.resizable().scaledToFill() + } placeholder: { + Color.gray.opacity(0.2) + } + .frame(width: 64, height: 64) + .cornerRadius(10) + + VStack(alignment: .leading, spacing: 4) { + Text(firstItem.korName) + .font(.headline) + .lineLimit(1) + if history.items.count > 1 { + Text("외 \(history.items.count - 1)개 품목") + .font(.caption) + .foregroundColor(.gray) + } + } + } + } + + // 입출고 상태 + Text(statusText(history.status)) + .font(.caption) + .padding(.horizontal, 8) + .padding(.vertical, 4) + .background(statusBgColor(history.status)) + .foregroundColor(statusTextColor(history.status)) + .cornerRadius(8) + + if history.status == "RECEIVED", let orderId = history.orderId { + NavigationLink( + destination: OrderDetailView(orderId: orderId, orderViewModel: OrderViewModel()) + ) { + + Image(systemName: "chevron.right") + .font(.system(size: 14, weight: .medium)) + .foregroundColor(.gray) + + } + .buttonStyle(.plain) + } else { + NavigationLink(destination: ReleaseDetailView(history: history)) { + Image(systemName: "chevron.right") + .font(.system(size: 14, weight: .medium)) + .foregroundColor(.gray) + } + .buttonStyle(.plain) + } + } + .padding() + .background(Color.white) + .cornerRadius(12) + .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 2) + .padding(.horizontal) + } + + func formatDate(_ iso: String) -> String { + String(iso.prefix(10)).replacingOccurrences(of: "-", with: ".") + } + + func statusText(_ status: String) -> String { + switch status { + case "RECEIVED": return "입고 완료" + case "RELEASED": return "출고 완료" + default: return status + } + } + + func statusTextColor(_ status: String) -> Color { + switch status { + case "RELEASED": return .StatusGreen + case "RECEIVED": return .StatusPurple + default: return .gray + } + } + + func statusBgColor(_ status: String) -> Color { + switch status { + case "RELEASED": return .StatusGreenBg + case "RECEIVED": return .StatusPurpleBg + default: return Color.gray.opacity(0.15) + } + } +} + +#Preview { + InOutHistoryView() +} diff --git a/StockMate/StockMate/app/feature/dashboard/ui/ReleaseDetailView.swift b/StockMate/StockMate/app/feature/dashboard/ui/ReleaseDetailView.swift new file mode 100644 index 0000000..9d4e522 --- /dev/null +++ b/StockMate/StockMate/app/feature/dashboard/ui/ReleaseDetailView.swift @@ -0,0 +1,156 @@ +// +// ReleaseDetailView.swift +// StockMate +// +// Created by Admin on 11/2/25. +// + +import SwiftUI + +struct ReleaseDetailView: View { + let history: HistoryItem + + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 20) { + // ✅ 부품 리스트 + VStack(alignment: .leading, spacing: 7) { + Text("출고 품목 (\(history.items.count)개)") + .font(.system(size: 17, weight: .semibold)) + .padding(.leading) + + // ✅ 처리일자 포맷팅 + Text("처리일자: \(formattedDate3(history.createdAt))") + .font(.system(size: 15)) + .foregroundColor(Color.textGray1) + .padding(.leading) + + ForEach(history.items) { part in + ReleasePartCard(part: part) + } + } + } + .padding(.vertical) + } + .background(Color.Light) + .navigationTitle("출고 상세") + .navigationBarTitleDisplayMode(.inline) + } +// func formatDate(_ iso: String) -> String { +// String(iso.prefix(10)).replacingOccurrences(of: "-", with: ".") +// } +} + +struct ReleasePartCard: View { + let part: HistoryPart + + var body: some View { + VStack(alignment: .leading, spacing: 5){ + // 상단 카테고리 + Text(part.categoryName) + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(.black) + + Divider() + .frame(height: 0.2) + .background(Color.textGray2) + + HStack(alignment: .center, spacing: 12) { + AsyncImage(url: URL(string: part.image)) { image in + image.resizable().scaledToFill() + } placeholder: { + Color.gray.opacity(0.2) + } + .frame(width: 64, height: 64) + .cornerRadius(10) + + VStack(alignment: .leading, spacing: 4) { + Text(part.korName) + .font(.system(size: 14, weight: .bold)) + .lineLimit(1) + + Text("\(part.model) / \(part.trim) / \(part.price)원 / \(part.amount)개") + .font(.system(size: 12)) + .foregroundColor(.gray) + + Text("\(part.price * part.amount)원") + .font(.system(size: 12)) + .foregroundColor(.black) + } + + Spacer() + + + } + } + .padding() + .background(Color.white) + .cornerRadius(12) + .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 2) + .padding(.horizontal) + } +} + +#Preview { + ReleaseDetailView(history: HistoryItem( + id: 1, + memberId: 1, + orderId: 2, + orderNumber: "ORD-1234", + message: "출고 완료", + status: "RELEASED", + type: "RELEASE", + createdAt: "2025-11-01T12:30:00", + updatedAt: "2025-11-01T12:40:00", + userInfo: nil, + items: [ + HistoryPart(id: 1, name: "partA", price: 1000, image: "", trim: "basic", model: "A1", category: 1, korName: "부품A", engName: "PartA", categoryName: "카테고리A", amount: 5, code: "P001", location: "A-01", cost: 500, historyQuantity: 3) + ] + )) +} + +func formattedDate3(_ timestamp: String) -> String { + // 가능한 입력 포맷들 (서버별 변형 대응) + let inputFormats = [ + "yyyy-MM-dd'T'HH:mm:ss.SSSSSS", + "yyyy-MM-dd'T'HH:mm:ss.SSSSSSS", + "yyyy-MM-dd'T'HH:mm:ss.SSS", + "yyyy-MM-dd'T'HH:mm:ss", + "yyyy-MM-dd'T'HH:mm:ssZ", + "yyyy-MM-dd'T'HH:mm:ss.SSSZ" + ] + + let trimmed = timestamp.trimmingCharacters(in: .whitespacesAndNewlines) + let parser = DateFormatter() + parser.locale = Locale(identifier: "en_US_POSIX") + parser.timeZone = TimeZone(abbreviation: "UTC") + + var date: Date? = nil + + for format in inputFormats { + parser.dateFormat = format + if let parsed = parser.date(from: trimmed) { + date = parsed + break + } + } + + // ISO8601 fallback + if date == nil { + let iso = ISO8601DateFormatter() + iso.formatOptions = [.withInternetDateTime, .withFractionalSeconds] + date = iso.date(from: trimmed) ?? ISO8601DateFormatter().date(from: trimmed) + } + + guard let finalDate = date else { + return timestamp // 파싱 실패 시 원본 반환 + } + + let output = DateFormatter() + output.locale = Locale(identifier: "ko_KR") + output.timeZone = TimeZone.current + output.dateFormat = "yyyy.MM.dd HH:mm:ss" + + return output.string(from: finalDate) +} + diff --git a/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift b/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift new file mode 100644 index 0000000..28c8eee --- /dev/null +++ b/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift @@ -0,0 +1,62 @@ +// +// HistoryViewModel.swift +// StockMate +// +// Created by Admin on 11/1/25. +// + +import Foundation +import Alamofire + +@MainActor +final class HistoryViewModel: ObservableObject { + @Published var histories: [HistoryItem] = [] + @Published var isLoading = false + @Published var errorMessage: String? = nil // ✅ 추가 + @Published var currentPage = 0 + @Published var totalPages = 1 + + private let repository: HistoryRepositoryProtocol + + init(repository: HistoryRepositoryProtocol = HistoryRepositoryImpl()) { + self.repository = repository + } + + /// ✅ 입출고 히스토리 불러오기 + func fetchInOutHistory(page: Int = 0, size: Int = 20) async { + guard !isLoading else { return } + isLoading = true + defer { isLoading = false } + + let result = await repository.getInOutHistory(page: page, size: size) // ✅ 오타도 수정됨 (getInout → getInOut) + switch result { + case .success(let response): + if let data = response.data { + if page == 0 { + histories = data.content + } else { + histories.append(contentsOf: data.content) + } + currentPage = data.currentPage + totalPages = data.totalPages + errorMessage = nil + } else { + errorMessage = "데이터가 없습니다." + } + case .failure(let error): + errorMessage = error.localizedDescription + print("❌ 입출고 히스토리 조회 실패:", error) + } + } + + /// ✅ 다음 페이지 로드 (무한 스크롤 등) + func loadMoreIfNeeded(currentItem item: HistoryItem?) async { + guard let item = item else { return } + let thresholdIndex = histories.index(histories.endIndex, offsetBy: -5) + if histories.firstIndex(where: { $0.id == item.id }) == thresholdIndex { + if currentPage + 1 < totalPages { + await fetchInOutHistory(page: currentPage + 1) + } + } + } +} diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift index 9641e2e..702a971 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift @@ -124,7 +124,7 @@ struct InventoryView: View { struct GridMenuView: View { let menuItems = [ ("재고 조회", true, "InvStock", AnyView(InventorySearchView())), - ("입출고 히스토리", false, "InvTrans", AnyView(IncomingScanView())), + ("입출고 히스토리", false, "InvTrans", AnyView(InOutHistoryView())), ("입고 처리", false, "InvIncoming", AnyView(IncomingScanView())), ("사용 처리", true, "InvUse", AnyView(OutgoingScanView())), ] diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift index a6e9aa5..7cd04b1 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift @@ -27,9 +27,6 @@ struct OrderListView: View { .frame(maxWidth: .infinity, maxHeight: .infinity) } else { // 날짜별로 그룹화 (최신순) -// let groupedOrders = Dictionary(grouping: orderViewModel.orders) { order in -// order.createdAt.split(separator: "T").first ?? "" -// } let groupedOrders = Dictionary(grouping: orderViewModel.orders) { order in order.createdAt.split(separator: "T").first.map(String.init) ?? "" } From ab2d4fab07d202f952c18dbe8752c72efd690c13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sun, 2 Nov 2025 16:52:35 +0900 Subject: [PATCH 43/85] =?UTF-8?q?[FIX]=20=EC=A3=BC=EB=AC=B8=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20enum=20=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B8=20=EB=A7=A4=ED=95=91=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/feature/auth/ui/LoginView.swift | 6 +- .../feature/cart/ui/DeliveryStatusView.swift | 2 +- .../dashboard/ui/InOutHistoryView.swift | 4 +- .../dashboard/ui/ReleaseDetailView.swift | 4 +- .../inventory/ui/OutgoingScanView.swift | 34 +++++- .../app/feature/orders/data/OrderApi.swift | 8 -- .../feature/orders/ui/OrderDetailView.swift | 100 +++++++++++------- .../app/feature/parts/data/PartApi.swift | 8 +- .../flag.imageset/Contents.json | 12 +++ .../Assets.xcassets/flag.imageset/flag.svg | 3 + 10 files changed, 121 insertions(+), 60 deletions(-) create mode 100644 StockMate/StockMate/resources/Assets.xcassets/flag.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/flag.imageset/flag.svg diff --git a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift index 27236a7..72b16ad 100644 --- a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift @@ -119,9 +119,9 @@ struct LoginView: View { // MARK: - 유효성 검사 함수 private func isValidForm() -> Bool { - emailError = isValidEmail(authViewModel.email) ? nil : "이메일 형식을 확인해주세요" - pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" - return emailError == nil && pwError == nil +// emailError = isValidEmail(authViewModel.email) ? nil : "이메일 형식을 확인해주세요" +// pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" +// return emailError == nil && pwError == nil return true } diff --git a/StockMate/StockMate/app/feature/cart/ui/DeliveryStatusView.swift b/StockMate/StockMate/app/feature/cart/ui/DeliveryStatusView.swift index 2825687..6a9f189 100644 --- a/StockMate/StockMate/app/feature/cart/ui/DeliveryStatusView.swift +++ b/StockMate/StockMate/app/feature/cart/ui/DeliveryStatusView.swift @@ -16,8 +16,8 @@ struct DeliveryStep { struct DeliveryStatusView: View { let steps: [DeliveryStep] = [ DeliveryStep(title: "결제완료", iconName: "check"), - DeliveryStep(title: "승인대기중", iconName: "hourglass"), DeliveryStep(title: "상품준비중", iconName: "uploadprogress"), + DeliveryStep(title: "배송시작", iconName: "flag"), DeliveryStep(title: "배송중", iconName: "rocket"), DeliveryStep(title: "배송완료", iconName: "pindrop") ] diff --git a/StockMate/StockMate/app/feature/dashboard/ui/InOutHistoryView.swift b/StockMate/StockMate/app/feature/dashboard/ui/InOutHistoryView.swift index 43be7ab..12998e9 100644 --- a/StockMate/StockMate/app/feature/dashboard/ui/InOutHistoryView.swift +++ b/StockMate/StockMate/app/feature/dashboard/ui/InOutHistoryView.swift @@ -86,7 +86,7 @@ struct InOutHistoryCard: View { VStack(alignment: .leading, spacing: 4) { Text(firstItem.korName) - .font(.headline) + .font(.system(size: 15)) .lineLimit(1) if history.items.count > 1 { Text("외 \(history.items.count - 1)개 품목") @@ -97,6 +97,8 @@ struct InOutHistoryCard: View { } } + Spacer() + // 입출고 상태 Text(statusText(history.status)) .font(.caption) diff --git a/StockMate/StockMate/app/feature/dashboard/ui/ReleaseDetailView.swift b/StockMate/StockMate/app/feature/dashboard/ui/ReleaseDetailView.swift index 9d4e522..af44590 100644 --- a/StockMate/StockMate/app/feature/dashboard/ui/ReleaseDetailView.swift +++ b/StockMate/StockMate/app/feature/dashboard/ui/ReleaseDetailView.swift @@ -69,11 +69,11 @@ struct ReleasePartCard: View { .font(.system(size: 14, weight: .bold)) .lineLimit(1) - Text("\(part.model) / \(part.trim) / \(part.price)원 / \(part.amount)개") + Text("\(part.model) / \(part.trim) / \(part.price)원 / \(part.historyQuantity)개") .font(.system(size: 12)) .foregroundColor(.gray) - Text("\(part.price * part.amount)원") + Text("\(part.price * part.historyQuantity)원") .font(.system(size: 12)) .foregroundColor(.black) } diff --git a/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift b/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift index 506c0fa..12a7502 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift @@ -92,12 +92,43 @@ struct OutgoingScanView: View { } // ✅ 스캔된 코드로 출고 API 호출 +// private func handleScannedCode(_ code: String) async { +// await MainActor.run { +// partViewModel.isLoading = true +// } +// +// let request = [ReleaseItemRequest(partCode: code, quantity: 1)] // 기본 1개로 설정 +// let result = await partViewModel.releaseParts(items: request) +// +// await MainActor.run { +// partViewModel.isLoading = false +// switch result { +// case .success(let message): +// alertMessage = message +// case .failure(let error): +// alertMessage = error.message +// } +// showAlert = true +// } +// } + // ✅ 스캔된 코드로 출고 API 호출 private func handleScannedCode(_ code: String) async { await MainActor.run { partViewModel.isLoading = true } - let request = [ReleaseItemRequest(partCode: code, quantity: 1)] // 기본 1개로 설정 + // ✅ 문자열 → Int 변환 (QR 코드가 숫자 아닐 경우 예외 처리) + guard let partId = Int(code) else { + await MainActor.run { + partViewModel.isLoading = false + alertMessage = "잘못된 QR 코드입니다. (숫자형 ID가 아닙니다)" + showAlert = true + } + return + } + + // ✅ 요청 생성 및 API 호출 + let request = [ReleaseItemRequest(partId: partId, quantity: 1)] // 기본 1개 사용 let result = await partViewModel.releaseParts(items: request) await MainActor.run { @@ -111,6 +142,7 @@ struct OutgoingScanView: View { showAlert = true } } + } #Preview { diff --git a/StockMate/StockMate/app/feature/orders/data/OrderApi.swift b/StockMate/StockMate/app/feature/orders/data/OrderApi.swift index ef3d651..211e3a5 100644 --- a/StockMate/StockMate/app/feature/orders/data/OrderApi.swift +++ b/StockMate/StockMate/app/feature/orders/data/OrderApi.swift @@ -103,14 +103,6 @@ struct OrderItems: Encodable { let amount: Int } -// Response -//struct OrderCreateResponseData: Decodable { -// let orderId: Int -// let orderNumber: String -// let totalPrice: Int -// let orderStatus: String -//} - struct OrderCreateResponseData: Decodable { let orderId: Int let orderNumber: String diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift index 0e4c5de..4ab38d1 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift @@ -203,8 +203,7 @@ struct OrderDetailView: View { // 오른쪽 버튼: 주문 상태에 따라 변경 if order.orderStatus == "ORDER_COMPLETED" || - order.orderStatus == "PAY_COMPLETED" || - order.orderStatus == "PENDING_APPROVAL" { + order.orderStatus == "PAY_COMPLETED" { // "주문취소" → 주문완료/결제완료/승인대기 Button(action: { Task { @@ -220,8 +219,7 @@ struct OrderDetailView: View { .cornerRadius(10) } - } else if order.orderStatus == "PENDING_RECEIVING" || - order.orderStatus == "DELIVERED" { + } else if order.orderStatus == "SHIPPING" { // "입고 하기" → 입고대기/배송완료 Button(action: { // TODO: 입고 처리 버튼 @@ -319,22 +317,30 @@ func formatDateOrDash(_ isoDate: String?) -> String { } func deliveryStep(for status: String) -> Int { - //6 -> 전체 회색 - //4 -> 전체 파란색 + // 6 -> 전체 회색 + // 4 -> 전체 파란색 switch status { case "ORDER_COMPLETED": return 0 // 주문 완료 - case "PAY_COMPLETED": return 0 // 결제 완료 - case "PENDING_APPROVAL": return 1 // 승인 대기 + + // 결제 후 결과에 따라 결제 실패 or 완료 case "FAILED": return 6 // 결제 실패 + case "PAY_COMPLETED": return 0 // 결제 완료 + + // 결제 완료 상태에서 지점이 주문 취소 + case "CANCELLED": return 6 // 주문 취소 + + // 본사에서 "결제 완료"에 대해서 주문을 반려 or 승인 + case "REJECTED": return 6 // 주문 반려 + case "APPROVAL_ORDER": return 1 // 주문 승인 + + // 창고관리자가 "주문 승인"에 대해서 송장(인보이스)를 뽑으면 출고 대기 case "PENDING_SHIPPING": return 2 // 출고 대기 + + // 창고관리자가 QR을 스캔하여 출고처리 하면 배송중 case "SHIPPING": return 3 // 배송중 - case "PENDING_RECEIVING": return 4 // 입고 대기 - case "REJECTED": return 6 // 승인 반려 - case "DELIVERED": return 4 // 배송 완료 - case "RECEIVED": return 4 // 입고 완료 - case "REFUNDED": return 6 // 환불 완료 - case "REFUND_REJECTED": return 6 // 환불 반려 - case "CANCELLED": return 6 // 주문 취소 + + // 지점에서 QR을 스캔하여 입고 완료 처리 + case "RECEIVED": return 4 // 입고 완료 default: return 6 } } @@ -345,21 +351,31 @@ func formatDate(_ isoDate: String) -> String { return "\(comps[0])년 \(comps[1])월 \(comps[2])일" } +// 0: 초록, 1: 빨강, 2: 주황, 3: 노랑, 4: 파랑, 5: 보라 + func statusText(_ status: String) -> String { switch status { case "ORDER_COMPLETED": return "주문 완료" // 주문 완료 - case "PAY_COMPLETED": return "결제 완료" // 결제 완료 - case "PENDING_APPROVAL": return "승인 대기" // 승인대기 + + // 결제 후 결과에 따라 결제 실패 or 완료 case "FAILED": return "결제 실패" // 결제 실패 + case "PAY_COMPLETED": return "결제 완료" // 결제 완료 + + // 결제 완료 상태에서 지점이 주문 취소 + case "CANCELLED": return "주문 취소" // 주문 취소 + + // 본사에서 "결제 완료"에 대해서 주문을 반려 or 승인 + case "REJECTED": return "결제 실패" // 주문 반려 + case "APPROVAL_ORDER": return "출고 대기" // 주문 승인 + + // 창고관리자가 "주문 승인"에 대해서 송장(인보이스)를 뽑으면 출고 대기 case "PENDING_SHIPPING": return "출고 대기" // 출고 대기 + + // 창고관리자가 QR을 스캔하여 출고처리 하면 배송중 case "SHIPPING": return "배송중" // 배송중 - case "PENDING_RECEIVING": return "배송 완료" // 입고대기 - case "REJECTED": return "승인 반려" // 이론상 출고 반려 - case "DELIVERED": return "배송 완료" // 배송 완료 + + // 지점에서 QR을 스캔하여 입고 완료 처리 case "RECEIVED": return "입고 완료" // 입고 완료 - case "REFUNDED": return "환불 완료" // 환불 완료 - case "REFUND_REJECTED": return "환불 반려" // 환불 반려 - case "CANCELLED": return "주문 취소" // 주문 취소 default: return "알 수 없음" } } @@ -367,18 +383,19 @@ func statusText(_ status: String) -> String { func statusColor(_ status: String) -> Color { switch status { case "ORDER_COMPLETED": return .StatusGreen - case "PAY_COMPLETED": return .StatusGreen - case "PENDING_APPROVAL": return .Warning + case "FAILED": return .Danger - case "PENDING_SHIPPING": return .InvUse - case "SHIPPING": return .Transfer - case "PENDING_RECEIVING": return .Secondary + case "PAY_COMPLETED": return .StatusGreen + + case "CANCELLED": return .Danger + case "REJECTED": return .Danger - case "DELIVERED": return .Secondary + case "APPROVAL_ORDER": return .Warning + + case "PENDING_SHIPPING": return .InvUse + case "SHIPPING": return .Secondary + case "RECEIVED": return .StatusPurple - case "REFUNDED": return .Gray - case "REFUND_REJECTED": return .Gray - case "CANCELLED": return .Gray default: return .gray.opacity(0.6) } } @@ -386,18 +403,19 @@ func statusColor(_ status: String) -> Color { func statusBdColor(_ status: String) -> Color { switch status { case "ORDER_COMPLETED": return .StatusGreenBg - case "PAY_COMPLETED": return .StatusGreenBg - case "PENDING_APPROVAL": return .WarningBg + case "FAILED": return .DangerBg - case "PENDING_SHIPPING": return .InvUseBg - case "SHIPPING": return .TransferBg - case "PENDING_RECEIVING": return .LightBlue04 + case "PAY_COMPLETED": return .StatusGreenBg + + case "CANCELLED": return .DangerBg + case "REJECTED": return .DangerBg - case "DELIVERED": return .LightBlue04 + case "APPROVAL_ORDER": return .WarningBg + + case "PENDING_SHIPPING": return .InvUseBg + case "SHIPPING": return .LightBlue04 + case "RECEIVED": return .StatusPurpleBg - case "REFUNDED": return Color(hex: "#EEEEEF") - case "REFUND_REJECTED": return Color(hex: "#EEEEEF") - case "CANCELLED": return Color(hex: "#EEEEEF") default: return .gray.opacity(0.6) } } diff --git a/StockMate/StockMate/app/feature/parts/data/PartApi.swift b/StockMate/StockMate/app/feature/parts/data/PartApi.swift index 9c5070d..67e835e 100644 --- a/StockMate/StockMate/app/feature/parts/data/PartApi.swift +++ b/StockMate/StockMate/app/feature/parts/data/PartApi.swift @@ -9,9 +9,9 @@ import Foundation import Alamofire -// ✅ 요청 모델 +// ✅ 요청 모델 (partCode → partId 로 변경) struct ReleaseItemRequest: Encodable { - let partCode: String + let partId: Int let quantity: Int } @@ -20,7 +20,9 @@ enum PartApi { static func releaseParts(items: [ReleaseItemRequest]) -> DataRequest { let url = ApiClient.baseURL + "api/v1/store/release" let body: [String: Any] = [ - "items": items.map { ["partCode": $0.partCode, "quantity": $0.quantity] } +// "items": items.map { ["partId": $0.partId, "quantity": $0.quantity] } + "items": items.map { ["partId": $0.partId, "quantity": $0.quantity] } + ] return ApiClient.shared.request( url, diff --git a/StockMate/StockMate/resources/Assets.xcassets/flag.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/flag.imageset/Contents.json new file mode 100644 index 0000000..02f005f --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/flag.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "flag.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/flag.imageset/flag.svg b/StockMate/StockMate/resources/Assets.xcassets/flag.imageset/flag.svg new file mode 100644 index 0000000..3c142bb --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/flag.imageset/flag.svg @@ -0,0 +1,3 @@ + + + From f8330b06fd7eef7ce1dd287cc11752765c259a13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sun, 2 Nov 2025 21:04:48 +0900 Subject: [PATCH 44/85] =?UTF-8?q?[FEAT]=20=ED=99=88=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EC=A7=80=EC=B6=9C=ED=98=84=ED=99=A9=20=EC=A4=91=EA=B0=84=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/core/components/BarChartView.swift | 96 +++++++++++++++++++ .../app/feature/auth/ui/HomeView.swift | 47 +++++---- .../app/feature/payment/data/PaymentApi.swift | 13 +++ .../payment/data/PaymentRepositoryImpl.swift | 16 ++++ .../domain/PaymentRepositoryProtocol.swift | 3 + .../viewmodel/DashboardViewModel.swift | 48 ++++++++++ 6 files changed, 199 insertions(+), 24 deletions(-) create mode 100644 StockMate/StockMate/app/core/components/BarChartView.swift create mode 100644 StockMate/StockMate/app/feature/payment/viewmodel/DashboardViewModel.swift diff --git a/StockMate/StockMate/app/core/components/BarChartView.swift b/StockMate/StockMate/app/core/components/BarChartView.swift new file mode 100644 index 0000000..96184e3 --- /dev/null +++ b/StockMate/StockMate/app/core/components/BarChartView.swift @@ -0,0 +1,96 @@ +// +// BarChartView.swift +// StockMate +// +// Created by Admin on 11/2/25. +// + +import SwiftUI + +struct BarChartView: View { + let values: [CGFloat] // 각 월별 비율값 (0~1) + let labels: [String] // 예: ["06", "07", "08", "09", "10"] + let amounts: [Int] // 예: [230000, 250000, 310000, 280000, 400000] + @Binding var selectedMonth: String? + + var body: some View { + // ✅ 최신월이 오른쪽에 오도록 역순 정렬 + let reversedValues = Array(values.reversed()) + let reversedLabels = Array(labels.reversed()) + let reversedAmounts = Array(amounts.reversed()) + + // ✅ "07" → "7월" 형식 변환 + let displayLabels = reversedLabels.map { label in + if let monthInt = Int(label) { + return "\(monthInt)월" + } else { + return label + } + } + + // ✅ 기본 선택: 최신월 + let defaultMonth = displayLabels.first ?? "" + let activeMonth = selectedMonth ?? defaultMonth + + VStack(alignment: .leading, spacing: 14) { + // 제목 + Text("월간 지출 현황") + .font(.headline) + .foregroundColor(.black) + .padding(.horizontal, 4) + + // ✅ 막대 그래프 + GeometryReader { geometry in + let totalWidth = geometry.size.width + let barCount = CGFloat(reversedValues.count) + let barWidth: CGFloat = 28 + let spacing = max((totalWidth - (barWidth * barCount)) / (barCount + 1), 6) + + HStack(alignment: .bottom, spacing: spacing) { + ForEach(reversedValues.indices, id: \.self) { i in + VStack { + RoundedRectangle(cornerRadius: 6) + .fill(activeMonth == displayLabels[i] ? Color.Primary : Color.LightBlue04) + .frame(width: barWidth, height: 150 * reversedValues[i]) + .onTapGesture { + // 선택/해제 처리 + selectedMonth = (selectedMonth == displayLabels[i]) ? nil : displayLabels[i] + } + + Text(displayLabels[i]) + .font(.caption2) + .foregroundColor(.black) + .padding(.top, 4) + } + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom) + } + .frame(height: 180) + .padding(.vertical) + + Divider() + + // ✅ 하단 "n월 지출금액 ooo원" 표시 + if let index = displayLabels.firstIndex(of: activeMonth) { + HStack { + Text("\(displayLabels[index]) 지출금액") + .font(.subheadline) + .foregroundColor(.gray) + Spacer() + Text("\(reversedAmounts[index].formatted())원") + .font(.headline) + .foregroundColor(Color.Primary) + } + .padding(.top, 6) + } + } + .frame(maxWidth: .infinity) + // ✅ 초기 로드 시 최신월 자동 선택 + .onAppear { + if selectedMonth == nil { + selectedMonth = defaultMonth + } + } + } +} diff --git a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift index 1094e52..2290203 100644 --- a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift @@ -12,7 +12,12 @@ struct HomeView: View { @EnvironmentObject var authViewModel: AuthViewModel @StateObject private var userViewModel = UserViewModel() @StateObject private var inventoryViewModel = InventoryViewModel() + @StateObject private var dashboardViewModel = DashboardViewModel() + @State private var selectedMonth: String? = nil // ✅ 추가 +// @State private var selectedMonthIndex: Int? = nil + + var body: some View { ScrollView { VStack(alignment: .leading, spacing: 15) { @@ -88,19 +93,29 @@ struct HomeView: View { .cornerRadius(16) .padding(.horizontal) - - // 막대그래프 섹션 VStack(alignment: .leading, spacing: 8) { - Text("지출 현황") + Text("원간 지출 현황") .font(.headline) .padding(4) - BarChartView() - .frame(height: 150) -// .padding() + if dashboardViewModel.isLoading { + ProgressView("데이터 불러오는 중...") + .frame(height: 150) + } else if dashboardViewModel.monthlySpendings.isEmpty { + Text("최근 지출 내역이 없습니다.") + .foregroundColor(.gray) + .frame(height: 150) + } else { + BarChartView( + values: dashboardViewModel.spendingRatios, + labels: dashboardViewModel.monthLabels, + amounts: dashboardViewModel.monthlySpendings.map { $0.totalAmount }, selectedMonth: $selectedMonth + ) + .frame(height: 200) .background(Color.white) - .shadow(color: .gray.opacity(0.1), radius: 4) + .cornerRadius(16) + } } .padding() .background(Color.white) @@ -113,6 +128,7 @@ struct HomeView: View { .task { // 카테고리 데이터 로드 await inventoryViewModel.loadLackCountByCategory() + await dashboardViewModel.fetchMonthlySpending() // ✅ 추가 } .onAppear { Task { await userViewModel.loadUserInfo() } @@ -234,23 +250,6 @@ struct DonutChartView: View { } } -// MARK: - 막대그래프 (더미) -struct BarChartView: View { - let values: [CGFloat] = [0.89, 0.5, 0.9, 0.3, 0.7] - let colors: [Color] = [ - .LightBlue04, .Primary, .LightBlue04, .LightBlue04, .LightBlue04 - ] - var body: some View { - HStack(alignment: .bottom, spacing: 33) { - ForEach(0.. DataRequest { + let url = ApiClient.baseURL + "api/v1/payment/monthly-spending" + return ApiClient.shared.request(url, method: .get) + } } diff --git a/StockMate/StockMate/app/feature/payment/data/PaymentRepositoryImpl.swift b/StockMate/StockMate/app/feature/payment/data/PaymentRepositoryImpl.swift index 625b887..7117d5b 100644 --- a/StockMate/StockMate/app/feature/payment/data/PaymentRepositoryImpl.swift +++ b/StockMate/StockMate/app/feature/payment/data/PaymentRepositoryImpl.swift @@ -45,4 +45,20 @@ final class PaymentRepositoryImpl: PaymentRepositoryProtocol { return .failure(error) } } + + // ✅ 최근 5개월 소비 내역 조회 + func fetchMonthlySpending() async -> AppResult<[MonthlySpending]> { + let request = PaymentApi.getMonthlySpending() + let result = await safeApi(request, decodeTo: ApiResponse<[MonthlySpending]>.self) + switch result { + case .success(let response): + if let data = response.data { + return .success(data) + } else { + return .failure(.init(code: response.status, message: response.message, underlying: nil)) + } + case .failure(let error): + return .failure(error) + } + } } diff --git a/StockMate/StockMate/app/feature/payment/domain/PaymentRepositoryProtocol.swift b/StockMate/StockMate/app/feature/payment/domain/PaymentRepositoryProtocol.swift index 150ab26..fff5558 100644 --- a/StockMate/StockMate/app/feature/payment/domain/PaymentRepositoryProtocol.swift +++ b/StockMate/StockMate/app/feature/payment/domain/PaymentRepositoryProtocol.swift @@ -11,4 +11,7 @@ import Alamofire protocol PaymentRepositoryProtocol { func fetchDepositAmount() async -> AppResult func chargeDeposit(amount: Int) async -> AppResult + + // ✅ 최근 5개월 소비 내역 조회 + func fetchMonthlySpending() async -> AppResult<[MonthlySpending]> } diff --git a/StockMate/StockMate/app/feature/payment/viewmodel/DashboardViewModel.swift b/StockMate/StockMate/app/feature/payment/viewmodel/DashboardViewModel.swift new file mode 100644 index 0000000..0741759 --- /dev/null +++ b/StockMate/StockMate/app/feature/payment/viewmodel/DashboardViewModel.swift @@ -0,0 +1,48 @@ +// +// DashboardViewModel.swift +// StockMate +// +// Created by Admin on 11/2/25. +// + +import Foundation + +@MainActor +final class DashboardViewModel: ObservableObject { + private let repo: PaymentRepositoryProtocol = PaymentRepositoryImpl() + + @Published var monthlySpendings: [MonthlySpending] = [] + @Published var isLoading = false + + /// ✅ 최근 5개월 소비 내역 조회 + func fetchMonthlySpending() async { + isLoading = true + let result = await repo.fetchMonthlySpending() + isLoading = false + + switch result { + case .success(let data): + monthlySpendings = data + case .failure(let err): + print("❌ 월별 소비 내역 조회 실패:", err.message) + } + } + + /// ✅ 막대그래프 비율 계산 + var spendingRatios: [CGFloat] { + guard let max = monthlySpendings.map({ $0.totalAmount }).max(), max > 0 else { return [] } + return monthlySpendings.map { CGFloat($0.totalAmount) / CGFloat(max) } + } + + /// ✅ 월 라벨 (예: "10월", "11월") + var monthLabels: [String] { + monthlySpendings.map { month in + // "2025-10" → "10월" + if month.month.count >= 7 { + return String(month.month.suffix(2)) + "월" + } else { + return month.month + } + } + } +} From 79ca35ecd8a65c3b68dc282df1200350bf0053bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Mon, 3 Nov 2025 12:18:02 +0900 Subject: [PATCH 45/85] =?UTF-8?q?[FEAT]=20=ED=99=88=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=B4=EC=B0=A8=ED=8A=B8=20=EC=A4=91=EA=B0=84=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/core/components/BarChartView.swift | 27 +++--- .../app/core/components/ChartEx.swift | 69 ++++++++++++++ .../app/core/components/DonutChartView.swift | 90 +++++++++++++++++++ .../app/feature/auth/ui/HomeView.swift | 74 ++++++++------- .../feature/dashboard/data/HistoryApi.swift | 2 + .../app/feature/payment/data/PaymentApi.swift | 13 +++ .../payment/data/PaymentRepositoryImpl.swift | 17 ++++ .../domain/PaymentRepositoryProtocol.swift | 4 + .../viewmodel/DashboardViewModel.swift | 23 ++++- StockMate/StockMate/resources/Color.swift | 4 +- 10 files changed, 270 insertions(+), 53 deletions(-) create mode 100644 StockMate/StockMate/app/core/components/ChartEx.swift create mode 100644 StockMate/StockMate/app/core/components/DonutChartView.swift diff --git a/StockMate/StockMate/app/core/components/BarChartView.swift b/StockMate/StockMate/app/core/components/BarChartView.swift index 96184e3..a06f145 100644 --- a/StockMate/StockMate/app/core/components/BarChartView.swift +++ b/StockMate/StockMate/app/core/components/BarChartView.swift @@ -29,18 +29,13 @@ struct BarChartView: View { } // ✅ 기본 선택: 최신월 - let defaultMonth = displayLabels.first ?? "" + let defaultMonth = displayLabels.last ?? "" let activeMonth = selectedMonth ?? defaultMonth VStack(alignment: .leading, spacing: 14) { - // 제목 - Text("월간 지출 현황") - .font(.headline) - .foregroundColor(.black) - .padding(.horizontal, 4) - // ✅ 막대 그래프 GeometryReader { geometry in + let chartHeight = geometry.size.height * 0.85 // 상하 여백 고려 let totalWidth = geometry.size.width let barCount = CGFloat(reversedValues.count) let barWidth: CGFloat = 28 @@ -49,11 +44,11 @@ struct BarChartView: View { HStack(alignment: .bottom, spacing: spacing) { ForEach(reversedValues.indices, id: \.self) { i in VStack { - RoundedRectangle(cornerRadius: 6) + RoundedRectangle(cornerRadius: 8) .fill(activeMonth == displayLabels[i] ? Color.Primary : Color.LightBlue04) - .frame(width: barWidth, height: 150 * reversedValues[i]) + // ✅ 막대 높이를 geometry 기준으로 조정 + .frame(width: barWidth, height: chartHeight * reversedValues[i]) .onTapGesture { - // 선택/해제 처리 selectedMonth = (selectedMonth == displayLabels[i]) ? nil : displayLabels[i] } @@ -66,23 +61,23 @@ struct BarChartView: View { } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom) } - .frame(height: 180) - .padding(.vertical) + .frame(height: 140) // ← 전체 그래프 영역 높이 확장 + .padding(.vertical, 8) Divider() // ✅ 하단 "n월 지출금액 ooo원" 표시 if let index = displayLabels.firstIndex(of: activeMonth) { HStack { - Text("\(displayLabels[index]) 지출금액") - .font(.subheadline) - .foregroundColor(.gray) + Text("\(displayLabels[index]) 지출 현황") + .font(.system(size: 17, weight: .medium)) Spacer() Text("\(reversedAmounts[index].formatted())원") - .font(.headline) + .font(.system(size: 18, weight: .bold)) .foregroundColor(Color.Primary) } .padding(.top, 6) + .padding(.horizontal,4) } } .frame(maxWidth: .infinity) diff --git a/StockMate/StockMate/app/core/components/ChartEx.swift b/StockMate/StockMate/app/core/components/ChartEx.swift new file mode 100644 index 0000000..e8195c2 --- /dev/null +++ b/StockMate/StockMate/app/core/components/ChartEx.swift @@ -0,0 +1,69 @@ +// +// ChartEx.swift +// StockMate +// +// Created by Admin on 11/3/25. +// + +//import SwiftUI +//import Charts +// +//struct ChartEx: View { +// let data: [CategorySpending] +// +// init(data: [CategorySpending]) { +// self.data = data +// } +// +// private var total: Double { +// Double(data.map { $0.totalAmount }.reduce(0, +)) +// } +// +// var body: some View { +// VStack(alignment: .leading, spacing: 16) { +// Text("카테고리별 지출 현황") +// .font(.headline) +// .foregroundStyle(.primary) +// +// Chart { +// ForEach(data, id: \.categoryName) { item in +// SectorMark( +// angle: .value("지출 비율", item.totalAmount), +// innerRadius: .ratio(0.65), +// angularInset: 2.0 +// ) +// .foregroundStyle(by: .value("카테고리", item.categoryName)) +// .cornerRadius(8.0) +// .annotation(position: .overlay) { +// if total > 0 { +// let percent = (Double(item.totalAmount) / total) * 100 +// Text("\(Int(percent))%") +// .font(.caption) +// .foregroundStyle(.white) +// } +// } +// } +// } +// .chartLegend(position: .bottom, alignment: .leading) +// .frame(height: 260) +// +// HStack { +// Text("총합: \(Int(total).formatted())원") +// .font(.subheadline) +// .foregroundColor(.gray) +// Spacer() +// } +// } +// .padding() +// } +//} +// +//#Preview { +// ChartEx(data: [ +// CategorySpending(categoryName: "식비", totalAmount: 120000), +// CategorySpending(categoryName: "교통", totalAmount: 85000), +// CategorySpending(categoryName: "문화", totalAmount: 45000), +// CategorySpending(categoryName: "쇼핑", totalAmount: 155000), +// CategorySpending(categoryName: "기타", totalAmount: 30000) +// ]) +//} diff --git a/StockMate/StockMate/app/core/components/DonutChartView.swift b/StockMate/StockMate/app/core/components/DonutChartView.swift new file mode 100644 index 0000000..826a000 --- /dev/null +++ b/StockMate/StockMate/app/core/components/DonutChartView.swift @@ -0,0 +1,90 @@ +// +// DonutChartView.swift +// StockMate +// +// Created by Admin on 11/3/25. +// + +import SwiftUI +import Charts + +struct DonutChartView: View { + let data: [CategorySpending] + + var total: Double { + Double(data.map { $0.totalAmount }.reduce(0, +)) + } + + // ✅ 각 항목별 비율 계산 + var percentages: [Double] { + data.map { total == 0 ? 0 : (Double($0.totalAmount) / total * 100) } + } + + var colors: [Color] = [ + Color.Hstatus1, + Color.Hstatus2, + Color.Hstatus3, + Color.Hstatus4, + Color.Hstatus5 + ] + + var body: some View { + HStack(alignment: .center, spacing: 24) { + // ✅ 도넛 차트 + if total == 0 { + Text("데이터 없음") + .foregroundColor(.gray) + } else { + Chart { + ForEach(Array(data.enumerated()), id: \.offset) { index, item in + SectorMark( + angle: .value("지출", item.totalAmount), + innerRadius: .ratio(0.56), + angularInset: 1.9 + ) + .foregroundStyle(colors[index % colors.count]) + .cornerRadius(8.0) + // ✅ 도넛 안쪽에 비율 표시 + .annotation(position: .overlay) { + let percentage = percentages[index] + Text("\(percentage, specifier: "%.1f")%") + .font(.system(size: 10, weight: .semibold)) + .foregroundColor(.black) + .offset(y: -2) + } + } + } + .frame(height: 200) + .chartLegend(.hidden) // 기본 범례 숨김 + } + + // ✅ 오른쪽 커스텀 범례 + VStack(alignment: .leading, spacing: 8) { + ForEach(Array(data.enumerated()), id: \.offset) { index, item in + let percentage = percentages[index] + HStack(spacing: 8) { + Circle() + .fill(colors[index % colors.count]) + .frame(width: 10, height: 10) + Text(item.categoryName) + .font(.system(size: 12, weight: .medium)) + .frame(width: 70, alignment: .leading) + Text("\(percentage, specifier: "%.1f")%") + .font(.system(size: 12)) + .foregroundColor(.black) + } + } + } + } + } +} + +#Preview { + DonutChartView(data: [ + CategorySpending(categoryName: "전기/램프", totalAmount: 450000), + CategorySpending(categoryName: "엔진/미션", totalAmount: 300000), + CategorySpending(categoryName: "하체/바디", totalAmount: 150000), + CategorySpending(categoryName: "내장/외장", totalAmount: 100000), + CategorySpending(categoryName: "기타소모품", totalAmount: 50000) + ]) +} diff --git a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift index 2290203..2e641a2 100644 --- a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift @@ -12,7 +12,8 @@ struct HomeView: View { @EnvironmentObject var authViewModel: AuthViewModel @StateObject private var userViewModel = UserViewModel() @StateObject private var inventoryViewModel = InventoryViewModel() - @StateObject private var dashboardViewModel = DashboardViewModel() + @EnvironmentObject var dashboardViewModel: DashboardViewModel +// @StateObject private var dashboardViewModel = DashboardViewModel() @State private var selectedMonth: String? = nil // ✅ 추가 // @State private var selectedMonthIndex: Int? = nil @@ -70,20 +71,29 @@ struct HomeView: View { lackStockSection - // 도넛 차트 섹션 VStack(alignment: .leading, spacing: 18) { Text("지난달 카테고리 별 지출") .font(.headline) .padding(4) - HStack{ - DonutChartView() - .frame(height: 130) - .padding() - .background(Color.white) - .cornerRadius(16) - .shadow(color: .gray.opacity(0.1), radius: 4) + HStack { + if dashboardViewModel.isLoading { + ProgressView("불러오는 중...") + .frame(height: 130) +// } else if dashboardViewModel.categorySpendings.isEmpty { +// Text("최근 지출 내역이 없습니다.") +// .foregroundColor(.gray) +// .frame(height: 150) + } else { + DonutChartView(data: dashboardViewModel.categorySpendings) + + .frame(height: 180) + .padding() + .background(Color.white) + .cornerRadius(16) + .shadow(color: .gray.opacity(0.1), radius: 4) + } Spacer() } @@ -92,10 +102,11 @@ struct HomeView: View { .background(Color.white) .cornerRadius(16) .padding(.horizontal) + // 막대그래프 섹션 VStack(alignment: .leading, spacing: 8) { - Text("원간 지출 현황") + Text("월간 지출 현황") .font(.headline) .padding(4) @@ -112,7 +123,7 @@ struct HomeView: View { labels: dashboardViewModel.monthLabels, amounts: dashboardViewModel.monthlySpendings.map { $0.totalAmount }, selectedMonth: $selectedMonth ) - .frame(height: 200) + .frame(height: 220) .background(Color.white) .cornerRadius(16) } @@ -122,13 +133,14 @@ struct HomeView: View { .cornerRadius(16) .padding(.horizontal) } - .padding(.vertical) + .padding(.vertical,5) } .background(Color.Light) .task { // 카테고리 데이터 로드 await inventoryViewModel.loadLackCountByCategory() await dashboardViewModel.fetchMonthlySpending() // ✅ 추가 + await dashboardViewModel.fetchCategorySpending() // ✅ 추가 } .onAppear { Task { await userViewModel.loadUserInfo() } @@ -228,29 +240,23 @@ struct StatusItem: View { } } -// MARK: - 도넛 차트 (더미) -struct DonutChartView: View { - var body: some View { - ZStack { - Circle() - .trim(from: 0, to: 0.521) - .stroke(Color.black, lineWidth: 40) - Circle() - .trim(from: 0.521, to: 0.749) - .stroke(Color(hex: "#7DBBFF"), lineWidth: 40) - Circle() - .trim(from: 0.749, to: 0.888) - .stroke(Color(hex: "#71DD8C"), lineWidth: 40) - Circle() - .trim(from: 0.888, to: 1) - .stroke(Color(hex: "#A0BCE8"), lineWidth: 40) - } - .rotationEffect(.degrees(-89.9)) - .padding() - } -} +//#Preview { +// HomeView() +//} + #Preview { - HomeView() + let dashboardVM = DashboardViewModel() + dashboardVM.categorySpendings = [ + CategorySpending(categoryName: "전기/램프", totalAmount: 450000), + CategorySpending(categoryName: "엔진/미션", totalAmount: 300000), + CategorySpending(categoryName: "하체/바디", totalAmount: 150000), + CategorySpending(categoryName: "내장/외장", totalAmount: 100000), + CategorySpending(categoryName: "기타소모품", totalAmount: 50000) + ] + + return HomeView() + .environmentObject(AuthViewModel()) + .environmentObject(dashboardVM) // ✅ 이제 진짜 연결됨! } diff --git a/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift b/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift index 1690fa7..b0e93c1 100644 --- a/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift +++ b/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift @@ -63,6 +63,8 @@ struct HistoryPart: Decodable, Identifiable { let cost: Int let historyQuantity: Int } + + // MARK: - API enum HistoryApi { // ✅ 가맹점별 입출고 히스토리 조회 diff --git a/StockMate/StockMate/app/feature/payment/data/PaymentApi.swift b/StockMate/StockMate/app/feature/payment/data/PaymentApi.swift index b9a1791..c2db3ef 100644 --- a/StockMate/StockMate/app/feature/payment/data/PaymentApi.swift +++ b/StockMate/StockMate/app/feature/payment/data/PaymentApi.swift @@ -23,6 +23,12 @@ struct MonthlySpending: Decodable, Identifiable { let totalAmount: Int } +struct CategorySpending: Decodable, Identifiable { + var id: String { categoryName } + let categoryName: String + let totalAmount: Int +} + enum PaymentApi { // 예치금 조회 @@ -50,4 +56,11 @@ enum PaymentApi { let url = ApiClient.baseURL + "api/v1/payment/monthly-spending" return ApiClient.shared.request(url, method: .get) } + + + // ✅ 지난달 카테고리별 지출금액 조회 + static func getCategorySpending() -> DataRequest { + let url = ApiClient.baseURL + "api/v1/order/category-spend" + return ApiClient.shared.request(url, method: .get) + } } diff --git a/StockMate/StockMate/app/feature/payment/data/PaymentRepositoryImpl.swift b/StockMate/StockMate/app/feature/payment/data/PaymentRepositoryImpl.swift index 7117d5b..6024082 100644 --- a/StockMate/StockMate/app/feature/payment/data/PaymentRepositoryImpl.swift +++ b/StockMate/StockMate/app/feature/payment/data/PaymentRepositoryImpl.swift @@ -61,4 +61,21 @@ final class PaymentRepositoryImpl: PaymentRepositoryProtocol { return .failure(error) } } + + // ✅ 지난달 카테고리별 지출 + func fetchCategorySpending() async -> AppResult<[CategorySpending]> { + let request = PaymentApi.getCategorySpending() + let result = await safeApi(request, decodeTo: ApiResponse<[CategorySpending]>.self) + switch result { + case .success(let response): + if let data = response.data { + return .success(data) + } else { + return .failure(.init(code: response.status, message: response.message, underlying: nil)) + } + case .failure(let error): + return .failure(error) + } + } + } diff --git a/StockMate/StockMate/app/feature/payment/domain/PaymentRepositoryProtocol.swift b/StockMate/StockMate/app/feature/payment/domain/PaymentRepositoryProtocol.swift index fff5558..fe22926 100644 --- a/StockMate/StockMate/app/feature/payment/domain/PaymentRepositoryProtocol.swift +++ b/StockMate/StockMate/app/feature/payment/domain/PaymentRepositoryProtocol.swift @@ -14,4 +14,8 @@ protocol PaymentRepositoryProtocol { // ✅ 최근 5개월 소비 내역 조회 func fetchMonthlySpending() async -> AppResult<[MonthlySpending]> + + // 지난달 카테고리별 지출 + func fetchCategorySpending() async -> AppResult<[CategorySpending]> + } diff --git a/StockMate/StockMate/app/feature/payment/viewmodel/DashboardViewModel.swift b/StockMate/StockMate/app/feature/payment/viewmodel/DashboardViewModel.swift index 0741759..8ef7136 100644 --- a/StockMate/StockMate/app/feature/payment/viewmodel/DashboardViewModel.swift +++ b/StockMate/StockMate/app/feature/payment/viewmodel/DashboardViewModel.swift @@ -12,6 +12,8 @@ final class DashboardViewModel: ObservableObject { private let repo: PaymentRepositoryProtocol = PaymentRepositoryImpl() @Published var monthlySpendings: [MonthlySpending] = [] + @Published var categorySpendings: [CategorySpending] = [] + @Published var isLoading = false /// ✅ 최근 5개월 소비 내역 조회 @@ -28,6 +30,20 @@ final class DashboardViewModel: ObservableObject { } } + // ✅ 지난달 카테고리별 지출 금액 조회 + func fetchCategorySpending() async { + isLoading = true + let result = await repo.fetchCategorySpending() + isLoading = false + + switch result { + case .success(let data): + categorySpendings = data + case .failure(let err): + print("❌ 카테고리별 지출 금액 조회 실패:", err.message) + } + } + /// ✅ 막대그래프 비율 계산 var spendingRatios: [CGFloat] { guard let max = monthlySpendings.map({ $0.totalAmount }).max(), max > 0 else { return [] } @@ -39,7 +55,12 @@ final class DashboardViewModel: ObservableObject { monthlySpendings.map { month in // "2025-10" → "10월" if month.month.count >= 7 { - return String(month.month.suffix(2)) + "월" + let suffix = String(month.month.suffix(2)) + if let monthInt = Int(suffix) { + return "\(monthInt)월" + } else { + return suffix + "월" + } } else { return month.month } diff --git a/StockMate/StockMate/resources/Color.swift b/StockMate/StockMate/resources/Color.swift index 94172ee..44ecf7b 100644 --- a/StockMate/StockMate/resources/Color.swift +++ b/StockMate/StockMate/resources/Color.swift @@ -81,8 +81,8 @@ extension Color { //DFF6FC // Home Status - static let Hstatus1 = Color(hex: "#DFF6FC") - static let Hstatus2 = Color(hex: "#DBDFF3") + static let Hstatus1 = Color(hex: "#08C2EB") + static let Hstatus2 = Color(hex: "#1F40AE") static let Hstatus3 = Color(hex: "#EB5032") static let Hstatus4 = Color(hex: "#8DDB55") static let Hstatus5 = Color(hex: "#8892A2") From 40ef1dba27e418845734c333eea23a159e88d206 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Mon, 3 Nov 2025 12:34:24 +0900 Subject: [PATCH 46/85] =?UTF-8?q?[FEAT]=20=ED=99=88=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EB=8C=80=EC=8B=9C=EB=B3=B4=EB=93=9C=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/core/components/ChartEx.swift | 69 ------------------- 1 file changed, 69 deletions(-) delete mode 100644 StockMate/StockMate/app/core/components/ChartEx.swift diff --git a/StockMate/StockMate/app/core/components/ChartEx.swift b/StockMate/StockMate/app/core/components/ChartEx.swift deleted file mode 100644 index e8195c2..0000000 --- a/StockMate/StockMate/app/core/components/ChartEx.swift +++ /dev/null @@ -1,69 +0,0 @@ -// -// ChartEx.swift -// StockMate -// -// Created by Admin on 11/3/25. -// - -//import SwiftUI -//import Charts -// -//struct ChartEx: View { -// let data: [CategorySpending] -// -// init(data: [CategorySpending]) { -// self.data = data -// } -// -// private var total: Double { -// Double(data.map { $0.totalAmount }.reduce(0, +)) -// } -// -// var body: some View { -// VStack(alignment: .leading, spacing: 16) { -// Text("카테고리별 지출 현황") -// .font(.headline) -// .foregroundStyle(.primary) -// -// Chart { -// ForEach(data, id: \.categoryName) { item in -// SectorMark( -// angle: .value("지출 비율", item.totalAmount), -// innerRadius: .ratio(0.65), -// angularInset: 2.0 -// ) -// .foregroundStyle(by: .value("카테고리", item.categoryName)) -// .cornerRadius(8.0) -// .annotation(position: .overlay) { -// if total > 0 { -// let percent = (Double(item.totalAmount) / total) * 100 -// Text("\(Int(percent))%") -// .font(.caption) -// .foregroundStyle(.white) -// } -// } -// } -// } -// .chartLegend(position: .bottom, alignment: .leading) -// .frame(height: 260) -// -// HStack { -// Text("총합: \(Int(total).formatted())원") -// .font(.subheadline) -// .foregroundColor(.gray) -// Spacer() -// } -// } -// .padding() -// } -//} -// -//#Preview { -// ChartEx(data: [ -// CategorySpending(categoryName: "식비", totalAmount: 120000), -// CategorySpending(categoryName: "교통", totalAmount: 85000), -// CategorySpending(categoryName: "문화", totalAmount: 45000), -// CategorySpending(categoryName: "쇼핑", totalAmount: 155000), -// CategorySpending(categoryName: "기타", totalAmount: 30000) -// ]) -//} From 72deb00aed3b47b6b1b992e0113414803f2dcd00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Mon, 3 Nov 2025 12:35:07 +0900 Subject: [PATCH 47/85] =?UTF-8?q?[FEAT]=20=ED=99=88=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EB=8C=80=EC=8B=9C=EB=B3=B4=EB=93=9C=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/core/components/DonutChartView.swift | 33 +++++++++--- .../app/feature/auth/ui/HomeView.swift | 50 +++++++++---------- 2 files changed, 49 insertions(+), 34 deletions(-) diff --git a/StockMate/StockMate/app/core/components/DonutChartView.swift b/StockMate/StockMate/app/core/components/DonutChartView.swift index 826a000..2dd37c6 100644 --- a/StockMate/StockMate/app/core/components/DonutChartView.swift +++ b/StockMate/StockMate/app/core/components/DonutChartView.swift @@ -28,6 +28,15 @@ struct DonutChartView: View { Color.Hstatus5 ] + var gradients: [AngularGradient] = [ + AngularGradient(gradient: Gradient(colors: [.pink, .orange]), center: .center), + AngularGradient(gradient: Gradient(colors: [.blue, .teal]), center: .center), + AngularGradient(gradient: Gradient(colors: [.green, .mint]), center: .center), + AngularGradient(gradient: Gradient(colors: [.purple, .indigo]), center: .center), + AngularGradient(gradient: Gradient(colors: [.gray, .black]), center: .center) + ] + + var body: some View { HStack(alignment: .center, spacing: 24) { // ✅ 도넛 차트 @@ -39,35 +48,45 @@ struct DonutChartView: View { ForEach(Array(data.enumerated()), id: \.offset) { index, item in SectorMark( angle: .value("지출", item.totalAmount), - innerRadius: .ratio(0.56), + innerRadius: .ratio(0.49), angularInset: 1.9 ) - .foregroundStyle(colors[index % colors.count]) + .foregroundStyle( + LinearGradient( + gradient: Gradient(colors: [ + colors[index % colors.count], + colors[index % colors.count].opacity(0.5) + ]), + startPoint: .topLeading, + endPoint: .bottomTrailing + ) + ) +// .foregroundStyle(colors[index % colors.count]) .cornerRadius(8.0) // ✅ 도넛 안쪽에 비율 표시 .annotation(position: .overlay) { let percentage = percentages[index] Text("\(percentage, specifier: "%.1f")%") - .font(.system(size: 10, weight: .semibold)) + .font(.system(size: 10, weight: .light)) .foregroundColor(.black) .offset(y: -2) } } } - .frame(height: 200) + .frame(height: 150) .chartLegend(.hidden) // 기본 범례 숨김 } // ✅ 오른쪽 커스텀 범례 - VStack(alignment: .leading, spacing: 8) { + VStack(alignment: .leading, spacing: 18) { ForEach(Array(data.enumerated()), id: \.offset) { index, item in let percentage = percentages[index] - HStack(spacing: 8) { + HStack(spacing: 7) { Circle() .fill(colors[index % colors.count]) .frame(width: 10, height: 10) Text(item.categoryName) - .font(.system(size: 12, weight: .medium)) + .font(.system(size: 12, weight: .light)) .frame(width: 70, alignment: .leading) Text("\(percentage, specifier: "%.1f")%") .font(.system(size: 12)) diff --git a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift index 2e641a2..4a0b4d6 100644 --- a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift @@ -12,11 +12,10 @@ struct HomeView: View { @EnvironmentObject var authViewModel: AuthViewModel @StateObject private var userViewModel = UserViewModel() @StateObject private var inventoryViewModel = InventoryViewModel() - @EnvironmentObject var dashboardViewModel: DashboardViewModel -// @StateObject private var dashboardViewModel = DashboardViewModel() +// @EnvironmentObject var dashboardViewModel: DashboardViewModel //preview 용 + @StateObject private var dashboardViewModel = DashboardViewModel() @State private var selectedMonth: String? = nil // ✅ 추가 -// @State private var selectedMonthIndex: Int? = nil var body: some View { @@ -81,18 +80,15 @@ struct HomeView: View { if dashboardViewModel.isLoading { ProgressView("불러오는 중...") .frame(height: 130) -// } else if dashboardViewModel.categorySpendings.isEmpty { -// Text("최근 지출 내역이 없습니다.") -// .foregroundColor(.gray) -// .frame(height: 150) + } else if dashboardViewModel.categorySpendings.isEmpty { + Text("최근 지출 내역이 없습니다.") + .foregroundColor(.gray) + .frame(height: 150) } else { DonutChartView(data: dashboardViewModel.categorySpendings) - - .frame(height: 180) - .padding() - .background(Color.white) - .cornerRadius(16) - .shadow(color: .gray.opacity(0.1), radius: 4) + .frame(height: 155) + .background(Color.white) + .cornerRadius(16) } Spacer() @@ -246,17 +242,17 @@ struct StatusItem: View { // HomeView() //} -#Preview { - let dashboardVM = DashboardViewModel() - dashboardVM.categorySpendings = [ - CategorySpending(categoryName: "전기/램프", totalAmount: 450000), - CategorySpending(categoryName: "엔진/미션", totalAmount: 300000), - CategorySpending(categoryName: "하체/바디", totalAmount: 150000), - CategorySpending(categoryName: "내장/외장", totalAmount: 100000), - CategorySpending(categoryName: "기타소모품", totalAmount: 50000) - ] - - return HomeView() - .environmentObject(AuthViewModel()) - .environmentObject(dashboardVM) // ✅ 이제 진짜 연결됨! -} +//#Preview { +// let dashboardVM = DashboardViewModel() +// dashboardVM.categorySpendings = [ +// CategorySpending(categoryName: "전기/램프", totalAmount: 450000), +// CategorySpending(categoryName: "엔진/미션", totalAmount: 300000), +// CategorySpending(categoryName: "하체/바디", totalAmount: 150000), +// CategorySpending(categoryName: "내장/외장", totalAmount: 100000), +// CategorySpending(categoryName: "기타소모품", totalAmount: 50000) +// ] +// +// return HomeView() +// .environmentObject(AuthViewModel()) +// .environmentObject(dashboardVM) // ✅ 이제 진짜 연결됨! +//} From 92a5c275091a494b6848877067795ddc2a77b0ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Mon, 3 Nov 2025 16:39:54 +0900 Subject: [PATCH 48/85] =?UTF-8?q?[FEAT]=20=EC=97=90=EC=B9=98=EA=B8=88=20?= =?UTF-8?q?=ED=9E=88=EC=8A=A4=ED=86=A0=EB=A6=AC=20=EC=9E=84=EC=8B=9C=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- StockMate/.DS_Store | Bin 6148 -> 6148 bytes .../app/feature/auth/ui/HomeView.swift | 2 +- .../feature/dashboard/data/HistoryApi.swift | 39 +++++ .../data/HistoryRepositoryImpl.swift | 6 + .../domain/HistoryRepositoryProtocol.swift | 3 + .../dashboard/ui/DepositHistoryView.swift | 135 ++++++++++++++++++ .../viewmodel/HistoryViewModel.swift | 45 ++++++ .../app/feature/orders/data/OrderApi.swift | 34 +++++ .../app/feature/user/ui/ProfileView.swift | 36 ++--- .../bag.imageset/Contents.json | 21 +++ .../Assets.xcassets/bag.imageset/bag.svg | 3 + .../exchange.imageset/Contents.json | 12 ++ .../exchange.imageset/exchange.svg | 9 ++ .../lock.imageset/Contents.json | 12 ++ .../Assets.xcassets/lock.imageset/lock.svg | 7 + .../logout.imageset/Contents.json | 12 ++ .../logout.imageset/logout.svg | 5 + .../notification 1.imageset/Contents.json | 12 ++ .../notification 1.imageset/notification.svg | 5 + .../receipt.imageset/Contents.json | 12 ++ .../receipt.imageset/receipt.svg | 8 ++ .../user.imageset/Contents.json | 12 ++ .../Assets.xcassets/user.imageset/user.svg | 4 + 23 files changed, 405 insertions(+), 29 deletions(-) create mode 100644 StockMate/StockMate/app/feature/dashboard/ui/DepositHistoryView.swift create mode 100644 StockMate/StockMate/resources/Assets.xcassets/bag.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/bag.imageset/bag.svg create mode 100644 StockMate/StockMate/resources/Assets.xcassets/exchange.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/exchange.imageset/exchange.svg create mode 100644 StockMate/StockMate/resources/Assets.xcassets/lock.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/lock.imageset/lock.svg create mode 100644 StockMate/StockMate/resources/Assets.xcassets/logout.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/logout.imageset/logout.svg create mode 100644 StockMate/StockMate/resources/Assets.xcassets/notification 1.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/notification 1.imageset/notification.svg create mode 100644 StockMate/StockMate/resources/Assets.xcassets/receipt.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/receipt.imageset/receipt.svg create mode 100644 StockMate/StockMate/resources/Assets.xcassets/user.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/user.imageset/user.svg diff --git a/StockMate/.DS_Store b/StockMate/.DS_Store index 9b2b289a6cc938aeb964150a839318a8b9520a06..96d21b81f6d7b95cdc550b0bdfe354e75ac4fec1 100644 GIT binary patch delta 69 zcmZoMXffCj$I7(m{Nw~yGmeK#QZ)RcjynQ5I+IVaav=+7@fTzm1}Ep|7BGMStH8#> MPmG({IsWnk0B6e=a{vGU delta 69 zcmZoMXffCj$I6sxGdY3PjN?)1%B>yY#~pzjoyn(IxsV04_zN-&gOl@f3m8Cv<-x|n MPmG({IsWnk0EhY+EC2ui diff --git a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift index 4a0b4d6..ddd982c 100644 --- a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift @@ -81,7 +81,7 @@ struct HomeView: View { ProgressView("불러오는 중...") .frame(height: 130) } else if dashboardViewModel.categorySpendings.isEmpty { - Text("최근 지출 내역이 없습니다.") + Text("지난달 지출 내역이 없습니다.") .foregroundColor(.gray) .frame(height: 150) } else { diff --git a/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift b/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift index b0e93c1..8882e6b 100644 --- a/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift +++ b/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift @@ -65,6 +65,39 @@ struct HistoryPart: Decodable, Identifiable { } +// MARK: - 예치금 거래내역 데이터 구조 +struct PaymentTransactionPageData: Decodable { + let content: [PaymentTransactionItem] + let page: Int + let size: Int + let totalElements: Int + let totalPages: Int + let hasNext: Bool + let hasPrevious: Bool + let last: Bool + let first: Bool +} + +//struct PaymentTransactionItem: Decodable, Identifiable { +// var id: UUID { UUID() } // 서버에서 id 제공 안하므로 로컬에서 생성 +// let transactionType: String // "CHARGE" or "PAY" +// let transactionTime: String +// let totalAmount: Int +// let orderId: Int +// let balance: Int +//} + +struct PaymentTransactionItem: Decodable, Identifiable { + var id: UUID { UUID() } // 서버에서 id 제공 안하므로 로컬 생성 + let transactionType: String // "CHARGE" or "PAY" + let transactionTime: String? // ✅ null 허용 + let totalAmount: Int + let orderId: Int? // ✅ null 허용 + let balance: Int +} + + + // MARK: - API enum HistoryApi { // ✅ 가맹점별 입출고 히스토리 조회 @@ -72,4 +105,10 @@ enum HistoryApi { let url = ApiClient.baseURL + "api/v1/information/order-history/my?page=\(page)&size=\(size)" return ApiClient.shared.request(url, method: .get) } + + // ✅ 예치금 거래내역 조회 + static func getPaymentTransaction(page: Int = 0, size: Int = 20) -> DataRequest { + let url = ApiClient.baseURL + "api/v1/payment/transaction?page=\(page)&size=\(size)" + return ApiClient.shared.request(url, method: .get) + } } diff --git a/StockMate/StockMate/app/feature/dashboard/data/HistoryRepositoryImpl.swift b/StockMate/StockMate/app/feature/dashboard/data/HistoryRepositoryImpl.swift index 694de1a..1c8dfe4 100644 --- a/StockMate/StockMate/app/feature/dashboard/data/HistoryRepositoryImpl.swift +++ b/StockMate/StockMate/app/feature/dashboard/data/HistoryRepositoryImpl.swift @@ -13,4 +13,10 @@ final class HistoryRepositoryImpl: HistoryRepositoryProtocol { let request = HistoryApi.getInOutHistory(page: page, size: size) return await safeApi(request, decodeTo: ApiResponse.self) } + + // ✅ 예치금 거래내역 조회 + func getPaymentTransaction(page: Int, size: Int) async -> AppResult> { + let request = HistoryApi.getPaymentTransaction(page: page, size: size) + return await safeApi(request, decodeTo: ApiResponse.self) + } } diff --git a/StockMate/StockMate/app/feature/dashboard/domain/HistoryRepositoryProtocol.swift b/StockMate/StockMate/app/feature/dashboard/domain/HistoryRepositoryProtocol.swift index 0816b5d..fddd5cc 100644 --- a/StockMate/StockMate/app/feature/dashboard/domain/HistoryRepositoryProtocol.swift +++ b/StockMate/StockMate/app/feature/dashboard/domain/HistoryRepositoryProtocol.swift @@ -10,4 +10,7 @@ import Alamofire protocol HistoryRepositoryProtocol { func getInOutHistory(page: Int, size: Int) async -> AppResult> + + // ✅ 예치금 거래내역 조회 + func getPaymentTransaction(page: Int, size: Int) async -> AppResult> } diff --git a/StockMate/StockMate/app/feature/dashboard/ui/DepositHistoryView.swift b/StockMate/StockMate/app/feature/dashboard/ui/DepositHistoryView.swift new file mode 100644 index 0000000..97f8c6c --- /dev/null +++ b/StockMate/StockMate/app/feature/dashboard/ui/DepositHistoryView.swift @@ -0,0 +1,135 @@ +// +// DepositHistoryView.swift +// StockMate +// +// Created by Admin on 11/3/25. +// + +import SwiftUI + +struct DepositHistoryView: View { + @StateObject private var viewModel = HistoryViewModel() + + var body: some View { + VStack { + // 본문 스크롤 + ScrollView { + LazyVStack(spacing: 12) { + ForEach(viewModel.transactions) { item in + DepositHistoryRow(item: item) + .onAppear { + Task { + await viewModel.loadMoreTransactionsIfNeeded(currentItem: item) + } + } + } + + if viewModel.isTransactionLoading { + ProgressView() + .padding() + } + } + .padding(.horizontal) + } + .background(Color.Light) + .navigationTitle("예치금 히스토리") + .navigationBarTitleDisplayMode(.inline) + .refreshable { + await viewModel.fetchPaymentTransactions() + } + } + .task { + await viewModel.fetchPaymentTransactions() + } + } +} + +// MARK: - 개별 거래 Row +struct DepositHistoryRow: View { + let item: PaymentTransactionItem + + var isCharge: Bool { item.transactionType == "CHARGE" } + + var body: some View { + HStack(alignment: .center, spacing: 13) { + // 아이콘 + Image(isCharge ? "exchange" : "bag") + .frame(width: 64, height: 64) + .foregroundColor(isCharge ? .Primary : .gray) + + VStack(alignment: .leading, spacing: 4) { + Text(isCharge ? "예치금 충전" : "실린더 어셈블리-브레이크 마스터 외 3개") + .font(.subheadline) + .fontWeight(.medium) + .foregroundColor(.black) + + Text(formatDate(item.transactionTime ?? "")) + .font(.caption) + .foregroundColor(.gray) + + Text(formatAmount(item.totalAmount, isCharge: isCharge)) + .font(.subheadline) + .foregroundColor(isCharge ? .Primary : .red) + } + Spacer() + + Image(systemName: "chevron.right") + .font(.system(size: 14, weight: .medium)) + .foregroundColor(.gray) + } + .padding() + .background(Color.white) + .cornerRadius(12) + .shadow(color: Color.black.opacity(0.05), radius: 2, x: 0, y: 1) + } + + // MARK: - Helper + private func formatAmount(_ amount: Int, isCharge: Bool) -> String { + let formatter = NumberFormatter() + formatter.numberStyle = .decimal + let formatted = formatter.string(from: NSNumber(value: amount)) ?? "\(amount)" + return (isCharge ? "+ " : "- ") + formatted + "원" + } + + private func formatDate(_ dateString: String) -> String { + // 서버에서 "2025.10.29 17:32:39" 형식이면 그대로 반환 + // 혹은 ISO8601이면 변환 필요 + if dateString.contains(".") { + return dateString + } else { + let formatter = ISO8601DateFormatter() + if let date = formatter.date(from: dateString) { + let displayFormatter = DateFormatter() + displayFormatter.dateFormat = "yyyy.MM.dd HH:mm:ss" + return displayFormatter.string(from: date) + } + return dateString + } + } +} + +// MARK: - Dummy Preview +#Preview { + VStack(spacing: 16) { + DepositHistoryRow( + item: PaymentTransactionItem( + transactionType: "CHARGE", + transactionTime: "2025-11-03T09:12:45", + totalAmount: 50000, + orderId: nil, + balance: 50000 + ) + ) + DepositHistoryRow( + item: PaymentTransactionItem( + transactionType: "PAY", + transactionTime: "2025-11-03T14:34:35.608713", + totalAmount: 49720, + orderId: 61, + balance: 4954617 + ) + ) + } + .padding() + .background(Color.Light) +} diff --git a/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift b/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift index 28c8eee..e01b262 100644 --- a/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift +++ b/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift @@ -10,12 +10,19 @@ import Alamofire @MainActor final class HistoryViewModel: ObservableObject { + // MARK: - 입출고 히스토리 관련 @Published var histories: [HistoryItem] = [] @Published var isLoading = false @Published var errorMessage: String? = nil // ✅ 추가 @Published var currentPage = 0 @Published var totalPages = 1 + // MARK: - 예치금 거래내역 관련 + @Published var transactions: [PaymentTransactionItem] = [] + @Published var transactionPage = 0 + @Published var transactionTotalPages = 1 + @Published var isTransactionLoading = false + private let repository: HistoryRepositoryProtocol init(repository: HistoryRepositoryProtocol = HistoryRepositoryImpl()) { @@ -59,4 +66,42 @@ final class HistoryViewModel: ObservableObject { } } } + + // MARK: - ✅ 예치금 거래내역 불러오기 + func fetchPaymentTransactions(page: Int = 0, size: Int = 20) async { + guard !isTransactionLoading else { return } + isTransactionLoading = true + defer { isTransactionLoading = false } + + let result = await repository.getPaymentTransaction(page: page, size: size) + switch result { + case .success(let response): + if let data = response.data { + if page == 0 { + transactions = data.content + } else { + transactions.append(contentsOf: data.content) + } + transactionPage = data.page + transactionTotalPages = data.totalPages + errorMessage = nil + } else { + errorMessage = "데이터가 없습니다." + } + case .failure(let error): + errorMessage = error.localizedDescription + print("❌ 예치금 거래내역 조회 실패:", error) + } + } + + // MARK: - ✅ 무한 스크롤 (예치금 내역) + func loadMoreTransactionsIfNeeded(currentItem item: PaymentTransactionItem?) async { + guard let item = item else { return } + let thresholdIndex = transactions.index(transactions.endIndex, offsetBy: -5) + if transactions.firstIndex(where: { $0.id == item.id }) == thresholdIndex { + if transactionPage + 1 < transactionTotalPages { + await fetchPaymentTransactions(page: transactionPage + 1) + } + } + } } diff --git a/StockMate/StockMate/app/feature/orders/data/OrderApi.swift b/StockMate/StockMate/app/feature/orders/data/OrderApi.swift index 211e3a5..40b4507 100644 --- a/StockMate/StockMate/app/feature/orders/data/OrderApi.swift +++ b/StockMate/StockMate/app/feature/orders/data/OrderApi.swift @@ -116,6 +116,13 @@ struct ReceiveOrderRequest: Encodable { } +struct OrderSummary { + let firstPartName: String + let itemCount: Int + let totalPrice: Int + let createdAt: String +} + // MARK: - API Call @@ -180,4 +187,31 @@ enum OrderApi { encoder: JSONParameterEncoder.default ) } + + // 주문 상세 정보 preview 데이터 가지고 오기 +// static func fetchOrderSummary(orderId: Int) async -> OrderSummary? { +// let result = await OrderRepositoryImpl().fetchOrderDetail(orderId: orderId) +// +// switch result { +// case .success(let order): // order == OrderResponseItem +// guard let first = order.orderItems.first else { return nil } +// return OrderSummary( +// firstPartName: first.partDetail.korName, +// itemCount: order.orderItems.count, +// totalPrice: order.totalPrice, +// createdAt: order.createdAt +// ) +// case .failure: +// return nil +// } +// } + static func fetchOrderSummary(orderId: Int) async -> OrderSummary? { + // 예시용 더미 데이터 + return OrderSummary( + firstPartName: "실린더 어셈블리-브레이크 마스터", + itemCount: 4, + totalPrice: 49720, + createdAt: "2025.11.03 14:34:35" + ) + } } diff --git a/StockMate/StockMate/app/feature/user/ui/ProfileView.swift b/StockMate/StockMate/app/feature/user/ui/ProfileView.swift index 92fdf6a..bf330f7 100644 --- a/StockMate/StockMate/app/feature/user/ui/ProfileView.swift +++ b/StockMate/StockMate/app/feature/user/ui/ProfileView.swift @@ -38,34 +38,14 @@ struct ProfileView: View { // MARK: - General Section VStack(alignment: .leading, spacing: 12) { - Text("General") - .font(.system(size: 16, weight: .semibold)) - .padding(.leading) - VStack(spacing: 10) { - SettingRow(icon: "person.crop.circle", title: "Edit Profile") - SettingRow(icon: "lock.circle", title: "Change Password") - SettingRow(icon: "bell", title: "Notifications") - SettingRow(icon: "location.circle", title: "배송 현황") - + SettingRow(icon: "user", title: "프로필 수정") + SettingRow(icon: "lock", title: "비밀번호 변경") + SettingRow(icon: "notification", title: "알림") + SettingNavigationRow(icon: "receipt", title: "예치금 히스토리", destination: DepositHistoryView()) +// SettingRow(icon: "credit", title: "예치금 히스토리") SettingNavigationRow(icon: "bag", title: "주문 내역", destination: OrderListView()) - } - .padding(3) - .background(Color.Light) - .cornerRadius(12) - .padding(.horizontal) - } - - // MARK: - Preferences Section - VStack(alignment: .leading, spacing: 12) { - Text("Preferences") - .font(.system(size: 16, weight: .semibold)) - .padding(.leading) - - VStack(spacing: 10) { - SettingRow(icon: "shield", title: "Legal and Policies") - SettingRow(icon: "questionmark.circle", title: "Help & Support") - SettingRow(icon: "arrow.right.circle", title: "Logout", iconColor: .red, textColor: .red) + SettingRow(icon: "logout", title: "로그아웃") } .padding(3) .background(Color.Light) @@ -93,7 +73,7 @@ struct SettingRow: View { var body: some View { HStack { - Image(systemName: icon) + Image(icon) .font(.system(size: 18)) .foregroundColor(iconColor) .frame(width: 24) @@ -124,7 +104,7 @@ struct SettingNavigationRow: View { var body: some View { NavigationLink(destination: destination) { HStack { - Image(systemName: icon) + Image(icon) .font(.system(size: 18)) .foregroundColor(iconColor) .frame(width: 24) diff --git a/StockMate/StockMate/resources/Assets.xcassets/bag.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/bag.imageset/Contents.json new file mode 100644 index 0000000..66aacdc --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/bag.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "bag.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/bag.imageset/bag.svg b/StockMate/StockMate/resources/Assets.xcassets/bag.imageset/bag.svg new file mode 100644 index 0000000..efc5703 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/bag.imageset/bag.svg @@ -0,0 +1,3 @@ + + + diff --git a/StockMate/StockMate/resources/Assets.xcassets/exchange.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/exchange.imageset/Contents.json new file mode 100644 index 0000000..b7cfc19 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/exchange.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "exchange.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/exchange.imageset/exchange.svg b/StockMate/StockMate/resources/Assets.xcassets/exchange.imageset/exchange.svg new file mode 100644 index 0000000..84b5196 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/exchange.imageset/exchange.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/StockMate/StockMate/resources/Assets.xcassets/lock.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/lock.imageset/Contents.json new file mode 100644 index 0000000..72cfcb5 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/lock.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "lock.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/lock.imageset/lock.svg b/StockMate/StockMate/resources/Assets.xcassets/lock.imageset/lock.svg new file mode 100644 index 0000000..fe83ff3 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/lock.imageset/lock.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/StockMate/StockMate/resources/Assets.xcassets/logout.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/logout.imageset/Contents.json new file mode 100644 index 0000000..bc4d524 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/logout.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "logout.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/logout.imageset/logout.svg b/StockMate/StockMate/resources/Assets.xcassets/logout.imageset/logout.svg new file mode 100644 index 0000000..039492d --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/logout.imageset/logout.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/StockMate/StockMate/resources/Assets.xcassets/notification 1.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/notification 1.imageset/Contents.json new file mode 100644 index 0000000..5dabea1 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/notification 1.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "notification.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/notification 1.imageset/notification.svg b/StockMate/StockMate/resources/Assets.xcassets/notification 1.imageset/notification.svg new file mode 100644 index 0000000..6ba310d --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/notification 1.imageset/notification.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/StockMate/StockMate/resources/Assets.xcassets/receipt.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/receipt.imageset/Contents.json new file mode 100644 index 0000000..59421d0 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/receipt.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "receipt.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/receipt.imageset/receipt.svg b/StockMate/StockMate/resources/Assets.xcassets/receipt.imageset/receipt.svg new file mode 100644 index 0000000..6c77a8c --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/receipt.imageset/receipt.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/StockMate/StockMate/resources/Assets.xcassets/user.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/user.imageset/Contents.json new file mode 100644 index 0000000..8ac482c --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/user.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "user.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/user.imageset/user.svg b/StockMate/StockMate/resources/Assets.xcassets/user.imageset/user.svg new file mode 100644 index 0000000..3f51a37 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/user.imageset/user.svg @@ -0,0 +1,4 @@ + + + + From f2246cdb73182a00a639d98dc143c187848fcd1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Mon, 3 Nov 2025 17:34:54 +0900 Subject: [PATCH 49/85] =?UTF-8?q?[FEAT]=20=EB=B6=80=ED=92=88=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EC=A1=B0=ED=9A=8C=20api=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../inventory/ui/OutgoingScanView.swift | 20 ------- .../app/feature/parts/data/PartApi.swift | 33 ++++++++++++ .../parts/data/PartRepositoryImpl.swift | 6 +++ .../parts/domain/PartRepositoryProtocol.swift | 1 + .../parts/viewmodel/PartViewModel.swift | 52 ++++++++++++++++++- 5 files changed, 91 insertions(+), 21 deletions(-) diff --git a/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift b/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift index 12a7502..62b5526 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift @@ -92,26 +92,6 @@ struct OutgoingScanView: View { } // ✅ 스캔된 코드로 출고 API 호출 -// private func handleScannedCode(_ code: String) async { -// await MainActor.run { -// partViewModel.isLoading = true -// } -// -// let request = [ReleaseItemRequest(partCode: code, quantity: 1)] // 기본 1개로 설정 -// let result = await partViewModel.releaseParts(items: request) -// -// await MainActor.run { -// partViewModel.isLoading = false -// switch result { -// case .success(let message): -// alertMessage = message -// case .failure(let error): -// alertMessage = error.message -// } -// showAlert = true -// } -// } - // ✅ 스캔된 코드로 출고 API 호출 private func handleScannedCode(_ code: String) async { await MainActor.run { partViewModel.isLoading = true diff --git a/StockMate/StockMate/app/feature/parts/data/PartApi.swift b/StockMate/StockMate/app/feature/parts/data/PartApi.swift index 67e835e..ede241d 100644 --- a/StockMate/StockMate/app/feature/parts/data/PartApi.swift +++ b/StockMate/StockMate/app/feature/parts/data/PartApi.swift @@ -15,6 +15,25 @@ struct ReleaseItemRequest: Encodable { let quantity: Int } +// ✅ 부품 상세 정보 모델 +struct PartDetailResponse: Decodable { + let id: Int + let name: String + let price: Int + let image: String + let trim: String + let model: String + let category: Int + let korName: String + let engName: String + let categoryName: String + let amount: Int + let code: String + let location: String + let cost: Int +} + + // ✅ API 정의 enum PartApi { static func releaseParts(items: [ReleaseItemRequest]) -> DataRequest { @@ -31,4 +50,18 @@ enum PartApi { encoding: JSONEncoding.default ) } + + // ✅ 부품 상세 조회 API + static func fetchPartDetail(partId: Int) -> DataRequest { + let url = ApiClient.baseURL + "api/v1/parts/detail" + let body: [String: Any] = ["partId": partId] + + return ApiClient.shared.request( + url, + method: .post, + parameters: body, + encoding: JSONEncoding.default + ) + } + } diff --git a/StockMate/StockMate/app/feature/parts/data/PartRepositoryImpl.swift b/StockMate/StockMate/app/feature/parts/data/PartRepositoryImpl.swift index 7ac1c08..8a063cc 100644 --- a/StockMate/StockMate/app/feature/parts/data/PartRepositoryImpl.swift +++ b/StockMate/StockMate/app/feature/parts/data/PartRepositoryImpl.swift @@ -13,4 +13,10 @@ final class PartRepositoryImpl: PartRepositoryProtocol { let dataReq = PartApi.releaseParts(items: items) return await safeApi(dataReq, decodeTo: ApiResponse.self) } + + // ✅ 부품 상세 조회 API + func fetchPartDetail(partId: Int) async -> AppResult> { + let dataReq = PartApi.fetchPartDetail(partId: partId) + return await safeApi(dataReq, decodeTo: ApiResponse<[PartDetailResponse]>.self) + } } diff --git a/StockMate/StockMate/app/feature/parts/domain/PartRepositoryProtocol.swift b/StockMate/StockMate/app/feature/parts/domain/PartRepositoryProtocol.swift index f3a939e..3e028d3 100644 --- a/StockMate/StockMate/app/feature/parts/domain/PartRepositoryProtocol.swift +++ b/StockMate/StockMate/app/feature/parts/domain/PartRepositoryProtocol.swift @@ -9,4 +9,5 @@ import Foundation protocol PartRepositoryProtocol { func releaseParts(items: [ReleaseItemRequest]) async -> AppResult> + func fetchPartDetail(partId: Int) async -> AppResult> } diff --git a/StockMate/StockMate/app/feature/parts/viewmodel/PartViewModel.swift b/StockMate/StockMate/app/feature/parts/viewmodel/PartViewModel.swift index fc3fecd..ead6256 100644 --- a/StockMate/StockMate/app/feature/parts/viewmodel/PartViewModel.swift +++ b/StockMate/StockMate/app/feature/parts/viewmodel/PartViewModel.swift @@ -14,12 +14,60 @@ final class PartViewModel: ObservableObject { @Published var message: String = "" @Published var shouldGoToLogin = false + + // ✅ 임시 저장 리스트 + @Published var selectedParts: [PartDetailResponse] = [] + @Published var quantities: [Int: Int] = [:] // partId별 수량 관리 + + private let repo: PartRepositoryProtocol init(repo: PartRepositoryProtocol = PartRepositoryImpl()) { self.repo = repo } + // ✅ 부품 상세 조회 (QR 스캔 후) + func fetchPartDetail(partId: Int) async { + isLoading = true + defer { isLoading = false } + + let result = await repo.fetchPartDetail(partId: partId) + switch result { + case .success(let apiResp): + if let detail = apiResp.data?.first { + if !selectedParts.contains(where: { $0.id == detail.id }) { + selectedParts.append(detail) + quantities[detail.id] = 1 + } else { + quantities[detail.id, default: 1] += 1 + } + } + case .failure(let error): + message = error.message + } + } + + func increaseQuantity(for partId: Int) { + quantities[partId, default: 1] += 1 + } + + func decreaseQuantity(for partId: Int) { + quantities[partId] = max(1, (quantities[partId] ?? 1) - 1) + } + + func removePart(partId: Int) { + selectedParts.removeAll { $0.id == partId } + quantities.removeValue(forKey: partId) + } + + func makeReleasePayload() -> [ReleaseItemRequest] { + selectedParts.map { part in + ReleaseItemRequest(partId: part.id, quantity: quantities[part.id] ?? 1) + } + } + + + func releaseParts(items: [ReleaseItemRequest]) async -> AppResult { isLoading = true defer { isLoading = false } @@ -41,4 +89,6 @@ final class PartViewModel: ObservableObject { return .failure(err) } } -} \ No newline at end of file + + +} From c64204b14325a29b5f7531e845b4f4d1a3c3f8c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Mon, 3 Nov 2025 21:42:01 +0900 Subject: [PATCH 50/85] =?UTF-8?q?[FEAT]=20=EB=B6=80=ED=92=88=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=B2=98=EB=A6=AC=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EC=9E=84=EC=8B=9C=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/core/components/CartCard.swift | 7 - .../feature/inventory/ui/InventoryView.swift | 2 +- .../inventory/ui/OutgoingScanView.swift | 226 ++++-- .../inventory/ui/PartBottomSheetView.swift | 672 ++++++++++++++++++ .../app/feature/parts/data/PartApi.swift | 40 +- .../parts/data/PartRepositoryImpl.swift | 6 +- .../app/feature/parts/data/PartStore.swift | 40 ++ .../parts/domain/PartRepositoryProtocol.swift | 2 +- .../app/feature/parts/ui/QRScannerView.swift | 10 +- .../parts/ui/QRScannerViewController.swift | 25 +- .../parts/viewmodel/PartViewModel.swift | 67 +- .../StockMate/app/navigation/AppNavHost.swift | 3 + .../app/navigation/MainTabView.swift | 7 +- 13 files changed, 988 insertions(+), 119 deletions(-) create mode 100644 StockMate/StockMate/app/feature/inventory/ui/PartBottomSheetView.swift create mode 100644 StockMate/StockMate/app/feature/parts/data/PartStore.swift diff --git a/StockMate/StockMate/app/core/components/CartCard.swift b/StockMate/StockMate/app/core/components/CartCard.swift index 1d959d0..772629f 100644 --- a/StockMate/StockMate/app/core/components/CartCard.swift +++ b/StockMate/StockMate/app/core/components/CartCard.swift @@ -47,13 +47,6 @@ struct CartCard: View { .font(.system(size: 13, weight: .semibold)) .foregroundColor(.black) -// Text("\(item.trim) / \(item.model)") -// .font(.system(size: 13)) -// .foregroundColor(.gray) -// -// Text("\(item.price)원") -// .font(.system(size: 13, weight: .semibold)) -// .foregroundColor(.black) } Spacer() diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift index 702a971..70063c4 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift @@ -126,7 +126,7 @@ struct GridMenuView: View { ("재고 조회", true, "InvStock", AnyView(InventorySearchView())), ("입출고 히스토리", false, "InvTrans", AnyView(InOutHistoryView())), ("입고 처리", false, "InvIncoming", AnyView(IncomingScanView())), - ("사용 처리", true, "InvUse", AnyView(OutgoingScanView())), + ("사용 처리", true, "InvUse", AnyView(OutgoingScanView().environmentObject(PartStore()))), ] var body: some View { diff --git a/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift b/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift index 62b5526..a6b11cb 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift @@ -13,13 +13,23 @@ struct OutgoingScanView: View { @State private var scannedCode: String? = nil @State private var showAlert = false @State private var alertMessage = "" + + // 사용 처리 임시 + @EnvironmentObject var partStore: PartStore + @State private var showBottomSheet = false + + @State private var scannerRestartTrigger = false +// @State private var isPresentingBottomSheet = false + @State private var partDetail: PartDetail? = nil + @StateObject private var partViewModel = PartViewModel() // ✅ ViewModel 추가 var body: some View { ZStack { // ✅ 카메라 미리보기 (QR 스캐너) QRScannerView(scannedCode: $scannedCode) + .id(scannerRestartTrigger) // 👈 다시 렌더링되어 카메라 리셋됨 .ignoresSafeArea() // ✅ 스캔 가이드 및 UI 오버레이 @@ -65,39 +75,85 @@ struct OutgoingScanView: View { // ✅ 로딩 인디케이터 if partViewModel.isLoading { - Color.black.opacity(0.3).ignoresSafeArea() - ProgressView("부품 사용 처리 중...") - .padding() - .background(.ultraThinMaterial) - .cornerRadius(10) - } - } - // ✅ 알림창 - .alert("부품 사용 결과", isPresented: $showAlert) { - Button("확인") { - dismiss() - } - } message: { - Text(alertMessage) - } - // ✅ QR 스캔 이벤트 발생 시 - .onChange(of: scannedCode) { newValue in - guard let code = newValue, !code.isEmpty else { return } - Task { - await handleScannedCode(code) - } + Color.black.opacity(0.3).ignoresSafeArea() + ProgressView("부품 조회 중...") + .padding() + .background(.ultraThinMaterial) + .cornerRadius(10) + } +// if partViewModel.isLoading { +// Color.black.opacity(0.3).ignoresSafeArea() +// ProgressView("부품 사용 처리 중...") +// .padding() +// .background(.ultraThinMaterial) +// .cornerRadius(10) +// } } - .navigationTitle("부품 사용 처리") - .navigationBarTitleDisplayMode(.inline) - } + .alert("알림", isPresented: $showAlert) { + Button("확인") {} + } message: { + Text(alertMessage) + } + .onChange(of: scannedCode) { newValue in + guard let code = newValue, !code.isEmpty else { return } + Task { await handleScannedCode(code) } + } + .sheet(isPresented: $showBottomSheet) { + if let part = partDetail { + PartBottomSheetView( + part: part, + onAddPart: { + // ✅ 부품 추가 버튼 액션 + partStore.addPart(part) - // ✅ 스캔된 코드로 출고 API 호출 + // ✅ 상태 초기화 (QR 다시 가능하도록) + resetScanState() + }, + onUseParts: { + // ✅ 사용 처리 버튼 액션 (예: 서버 전송) + let payload = partStore.makeRequestPayload() + print("🚀 사용 처리 API 호출 payload:", payload) + } + ) + .environmentObject(partStore) + } + } + .navigationTitle("부품 사용 처리") + .navigationBarTitleDisplayMode(.inline) +// .sheet(isPresented: $showBottomSheet) { +// PartBottomSheetView() +// .environmentObject(partStore) +// } +// .navigationTitle("부품 사용 처리") +// .navigationBarTitleDisplayMode(.inline) + + //-----// +// // ✅ 알림창 +// .alert("부품 사용 결과", isPresented: $showAlert) { +// Button("확인") { +// dismiss() +// } +// } message: { +// Text(alertMessage) +// } +// // ✅ QR 스캔 이벤트 발생 시 +// .onChange(of: scannedCode) { newValue in +// guard let code = newValue, !code.isEmpty else { return } +// Task { +// await handleScannedCode(code) +// } +// } +// .navigationTitle("부품 사용 처리") +// .navigationBarTitleDisplayMode(.inline) + } + + // ✅ 스캔된 코드로 부품 상세 조회만 수행 (출고 X) private func handleScannedCode(_ code: String) async { await MainActor.run { partViewModel.isLoading = true } - // ✅ 문자열 → Int 변환 (QR 코드가 숫자 아닐 경우 예외 처리) + // 문자열 → Int 변환 (QR 코드가 숫자 아닐 경우 예외 처리) guard let partId = Int(code) else { await MainActor.run { partViewModel.isLoading = false @@ -107,26 +163,114 @@ struct OutgoingScanView: View { return } - // ✅ 요청 생성 및 API 호출 - let request = [ReleaseItemRequest(partId: partId, quantity: 1)] // 기본 1개 사용 - let result = await partViewModel.releaseParts(items: request) + // ✅ 부품 상세 조회 API 호출 + await partViewModel.fetchPartDetail(partId: partId) + // ✅ 결과 출력 await MainActor.run { partViewModel.isLoading = false - switch result { - case .success(let message): - alertMessage = message - case .failure(let error): - alertMessage = error.message - } - showAlert = true + + guard let response = partViewModel.partDetails.first else { + alertMessage = "부품 정보를 불러오지 못했습니다." + showAlert = true + return + } + + partDetail = PartDetail( + id: response.id, + price: response.price, + image: response.image, + trim: response.trim, + model: response.model, + korName: response.korName, + categoryName: response.categoryName, + quantity: 1 + ) +// // ✅ API 응답을 PartDetail로 변환 +// let newPart = PartDetail( +// id: response.id, +// price: response.price, +// image: response.image, +// trim: response.trim, +// model: response.model, +// korName: response.korName, +// categoryName: response.categoryName, +// quantity: 1 +// ) +// +// // ✅ 리스트에 추가 +// partStore.addPart(newPart) + + // ✅ 바텀 시트 띄우기 + showBottomSheet = true + + //-----/// + +// if let part = partViewModel.partDetails.first { +// print("✅ 부품 상세 조회 성공") +// print("ID:", part.id) +// print("이름:", part.name) +// print("가격:", part.price) +// print("모델:", part.model) +// print("트림:", part.trim) +// print("코드:", part.code) +// alertMessage = "부품 조회 성공: \(part.name)" +// } else { +// print("⚠️ 부품 정보를 불러오지 못함") +// alertMessage = "부품 정보를 불러오지 못했습니다." +// } +// +// showAlert = true } } -} - -#Preview { - NavigationStack { - OutgoingScanView() +// // ✅ 스캔된 코드로 출고 API 호출 +// private func handleScannedCode(_ code: String) async { +// await MainActor.run { +// partViewModel.isLoading = true +// } +// +// // ✅ 문자열 → Int 변환 (QR 코드가 숫자 아닐 경우 예외 처리) +// guard let partId = Int(code) else { +// await MainActor.run { +// partViewModel.isLoading = false +// alertMessage = "잘못된 QR 코드입니다. (숫자형 ID가 아닙니다)" +// showAlert = true +// } +// return +// } +// +// // ✅ 요청 생성 및 API 호출 +// let request = [ReleaseItemRequest(partId: partId, quantity: 1)] // 기본 1개 사용 +// let result = await partViewModel.releaseParts(items: request) +// +// await MainActor.run { +// partViewModel.isLoading = false +// switch result { +// case .success(let message): +// alertMessage = message +// case .failure(let error): +// alertMessage = error.message +// } +// showAlert = true +// } +// } + + // ✅ 상태 초기화 (QR 다시 활성화) + private func resetScanState() { + scannedCode = nil + partDetail = nil + showBottomSheet = false + partViewModel.partDetails.removeAll() + + // ✅ 카메라 세션 재시작 + scannerRestartTrigger.toggle() } + } + +//#Preview { +// NavigationStack { +// OutgoingScanView() +// } +//} diff --git a/StockMate/StockMate/app/feature/inventory/ui/PartBottomSheetView.swift b/StockMate/StockMate/app/feature/inventory/ui/PartBottomSheetView.swift new file mode 100644 index 0000000..827e919 --- /dev/null +++ b/StockMate/StockMate/app/feature/inventory/ui/PartBottomSheetView.swift @@ -0,0 +1,672 @@ +// +// PartBottomSheetView.swift +// StockMate +// +// Created by Admin on 11/3/25. +// + +import SwiftUI + +struct PartBottomSheetView: View { + @EnvironmentObject var partStore: PartStore + @Environment(\.dismiss) private var dismiss + @StateObject private var partViewModel = PartViewModel() + + let part: PartDetail + let onAddPart: () -> Void + let onUseParts: () -> Void + + @State private var quantity: Int = 1 + @State private var isProcessing = false + @State private var showAlert = false + @State private var alertMessage = "" + + var body: some View { + VStack(spacing: 16) { + + // MARK: - 방금 스캔한 부품 미리보기 (추가 전) + HStack(spacing: 12) { + AsyncImage(url: URL(string: part.image)) { phase in + switch phase { + case .success(let img): + img.resizable().scaledToFill() + default: + Color.gray.opacity(0.3) + } + } + .frame(width: 48, height: 48) + .clipped() + .cornerRadius(6) + + VStack(alignment: .leading, spacing: 4) { + Text(part.korName) + .font(.subheadline) + Text("\(part.model) / \(part.trim)") + .font(.caption) + .foregroundColor(.gray) + Text("\(part.price) 원") + .font(.subheadline) + } + + Spacer() + + // ✅ 수정된 수량 조절 구역 + HStack(spacing: 8) { + Button(action: { + if quantity > 1 { quantity -= 1 } + }) { + Image(systemName: "minus.circle.fill") + .font(.title2) + .foregroundColor(.blue) + } + + Text("\(quantity)") + .font(.body) + .frame(width: 44, alignment: .center) + + Button(action: { + quantity += 1 + }) { + Image(systemName: "plus.circle.fill") + .font(.title2) + .foregroundColor(.blue) + } + + Spacer() + } + .padding(.horizontal) + } + .padding(.horizontal) + .padding() + + + + List { + ForEach(partStore.parts.indices, id: \.self) { idx in + let p = partStore.parts[idx] + HStack { + AsyncImage(url: URL(string: p.image)) { phase in + switch phase { + case .success(let img): + img.resizable().scaledToFill() + default: + Color.gray.opacity(0.3) + } + } + .frame(width: 48, height: 48) + .clipped() + .cornerRadius(6) + + VStack(alignment: .leading, spacing: 4) { + Text(p.korName) + .font(.subheadline) + Text("\(p.model) / \(p.trim)") + .font(.caption) + .foregroundColor(.gray) + Text("\(p.price) 원") + .font(.subheadline) + } + + Spacer() + + // ✅ 명확한 수량 표시 + +/- 버튼 (Stepper 대신 커스텀 컨트롤) + HStack(spacing: 8) { + Button { + let newQty = max(1, p.quantity - 1) + partStore.updateQuantity(for: p.id, to: newQty) + } label: { + Image(systemName: "minus.circle") + .font(.title2) + } + + Text("\(p.quantity)") + .font(.body) + .frame(width: 44, alignment: .center) + + Button { + let newQty = p.quantity + 1 + partStore.updateQuantity(for: p.id, to: newQty) + } label: { + Image(systemName: "plus.circle") + .font(.title2) + } + } + .buttonStyle(.plain) + .foregroundColor(.blue) + } + .padding() + } + .onDelete { idxs in + idxs.forEach { i in + let id = partStore.parts[i].id + partStore.removePart(id) + } + } + } + .frame(maxHeight: 240) + .listStyle(.plain) +// List { +// ForEach(partStore.parts) { p in +// HStack { +// AsyncImage(url: URL(string: p.image)) { phase in +// switch phase { +// case .success(let img): +// img.resizable().scaledToFill() +// default: +// Color.gray.opacity(0.3) +// } +// } +// .frame(width: 48, height: 48) +// .clipped() +// .cornerRadius(6) +// +// VStack(alignment: .leading, spacing: 4) { +// Text(p.korName) +// .font(.subheadline) +// Text("\(p.model) / \(p.trim)") +// .font(.caption) +// .foregroundColor(.gray) +// } +// +// Spacer() +// +// Stepper("", value: Binding( +// get: { p.quantity }, +// set: { newVal in partStore.updateQuantity(for: p.id, to: newVal) } +// ), in: 1...999) +// .labelsHidden() +// } +// .padding(.vertical, 6) +// } +// .onDelete { idxs in +// idxs.forEach { i in +// let id = partStore.parts[i].id +// partStore.removePart(id) +// } +// } +// } +// .frame(maxHeight: 240) +// .listStyle(.plain) + + Spacer() + + // — 하단 버튼 — + HStack(spacing: 12) { + Button { + var newPart = part + newPart.quantity = quantity + partStore.addPart(newPart) + onAddPart() + dismiss() + } label: { + Text("부품 추가") + .frame(maxWidth: .infinity) + .padding(.vertical, 12) + } + .buttonStyle(.borderedProminent) + + Button { + Task { await handleUseNow() } + } label: { + if isProcessing { + ProgressView() + .frame(maxWidth: .infinity) + .padding(.vertical, 12) + } else { + Text("사용 처리") + .frame(maxWidth: .infinity) + .padding(.vertical, 12) + } + } + .buttonStyle(.bordered) + .disabled(isProcessing) + } + .padding(.horizontal) + .padding(.bottom, 8) + } + .presentationDetents([.medium, .large]) + .onAppear { + quantity = part.quantity + } + .alert("알림", isPresented: $showAlert) { + Button("확인") {} + } message: { + Text(alertMessage) + } + } + + private func handleUseNow() async { + await MainActor.run { isProcessing = true } + + if !partStore.parts.contains(where: { $0.id == part.id }) { + var newPart = part + newPart.quantity = quantity + partStore.addPart(newPart) + } else { + partStore.updateQuantity(for: part.id, to: quantity) + } + + let items = partStore.parts.map { ReleaseItemRequest(partId: $0.id, quantity: $0.quantity) } + let result = await partViewModel.releaseParts(items: items) + + await MainActor.run { + isProcessing = false + switch result { + case .success(let msg): + alertMessage = msg + showAlert = true + partStore.clear() + onUseParts() + dismiss() + case .failure(let err): + alertMessage = err.message + showAlert = true + } + } + } +} +//struct PartBottomSheetView: View { +// @EnvironmentObject var partStore: PartStore +// @Environment(\.dismiss) private var dismiss +// @StateObject private var partViewModel = PartViewModel() +// +// // 네가 원했던 시그니처: 반드시 이대로 호출 가능함 +// let part: PartDetail // 방금 스캔한 파트 (OutgoingScanView에서 전달) +// let onAddPart: () -> Void // "부품 추가" 눌렀을 때 호출 (OutgoingScanView에서 resetScanState 등 처리) +// let onUseParts: () -> Void // "사용 처리" 눌렀을 때 호출(옵션적 추가 동작) +// +// @State private var quantity: Int = 1 +// @State private var isProcessing = false +// @State private var showAlert = false +// @State private var alertMessage = "" +// +// var body: some View { +// VStack(spacing: 12) { +// // — 단일(방금 스캔한) 파트 미리보기 — +// HStack(spacing: 12) { +// AsyncImage(url: URL(string: part.image)) { phase in +// switch phase { +// case .success(let img): +// img.resizable().scaledToFill() +// default: +// Color.gray.opacity(0.3) +// } +// } +// .frame(width: 84, height: 84) +// .clipped() +// .cornerRadius(8) +// +// VStack(alignment: .leading, spacing: 6) { +// Text(part.korName) +// .font(.headline) +// Text("\(part.model) / \(part.trim)") +// .font(.subheadline) +// .foregroundColor(.gray) +// Text("₩\(part.price)") +// .font(.subheadline) +// } +// +// Spacer() +// } +// .padding(.horizontal) +// +// // 수량 조절 (바로 이 시트에서 조정한 수량이 추가/전송에 반영됨) +// Stepper("수량: \(quantity)", value: $quantity, in: 1...999) +// .padding(.horizontal) +// +// Divider() +// .padding(.vertical, 6) +// +// // — 누적된 파트 리스트 (PartStore) — +// Text("추가된 부품 목록") +// .font(.subheadline) +// .bold() +// .padding(.horizontal) +// +// List { +// ForEach(partStore.parts) { p in +// HStack { +// AsyncImage(url: URL(string: p.image)) { phase in +// switch phase { +// case .success(let img): +// img.resizable().scaledToFill() +// default: +// Color.gray.opacity(0.3) +// } +// } +// .frame(width: 48, height: 48) +// .clipped() +// .cornerRadius(6) +// +// VStack(alignment: .leading, spacing: 4) { +// Text(p.korName) +// .font(.subheadline) +// Text("\(p.model) / \(p.trim)") +// .font(.caption) +// .foregroundColor(.gray) +// } +// +// Spacer() +// +// // stepper 바인딩은 store 업데이트로 연결 +// Stepper("", value: Binding( +// get: { p.quantity }, +// set: { newVal in partStore.updateQuantity(for: p.id, to: newVal) } +// ), in: 1...999) +// .labelsHidden() +// } +// .padding(.vertical, 6) +// } +// .onDelete { idxs in +// idxs.forEach { i in +// let id = partStore.parts[i].id +// partStore.removePart(id) +// } +// } +// } +// .frame(maxHeight: 240) +// .listStyle(.plain) +// +// Spacer() +// +// // 하단 버튼 +// HStack(spacing: 12) { +// // 1) 부품 추가: 방금 스캔한 파트를 (현재 quantity로) partStore에 추가하고 +// // 호출자에게 알려줌 (예: resetScanState) +// Button { +// var newPart = part +// newPart.quantity = quantity +// partStore.addPart(newPart) +// +// // 호출자 (OutgoingScanView)에게 알림 — 여기서 스캔 상태 초기화 등 처리 +// onAddPart() +// // 시트 닫기 +// dismiss() +// } label: { +// Text("부품 추가") +// .frame(maxWidth: .infinity) +// .padding(.vertical, 12) +// } +// .buttonStyle(.borderedProminent) +// +// // 2) 사용 처리: PartStore 전체를 서버로 보내는 흐름 (뷰모델 사용) +// Button { +// Task { +// await handleUseNow() +// } +// } label: { +// if isProcessing { +// ProgressView() +// .frame(maxWidth: .infinity) +// .padding(.vertical, 12) +// } else { +// Text("사용 처리") +// .frame(maxWidth: .infinity) +// .padding(.vertical, 12) +// } +// } +// .buttonStyle(.bordered) +// .disabled(isProcessing) +// } +// .padding(.horizontal) +// .padding(.bottom, 8) +// } +// .presentationDetents([.medium, .large]) +// .onAppear { +// quantity = part.quantity +// } +// .alert("알림", isPresented: $showAlert) { +// Button("확인") {} +// } message: { +// Text(alertMessage) +// } +// } +// +// // 사용 처리 로직 (뷰모델에 구현된 releaseParts 사용) +// private func handleUseNow() async { +// await MainActor.run { isProcessing = true } +// +// // 현재 시트에서 조정한 수량이 partStore에 반영되어야 함: +// if !partStore.parts.contains(where: { $0.id == part.id }) { +// var newPart = part +// newPart.quantity = quantity +// partStore.addPart(newPart) +// } else { +// partStore.updateQuantity(for: part.id, to: quantity) +// } +// +// let items = partStore.parts.map { ReleaseItemRequest(partId: $0.id, quantity: $0.quantity) } +// let result = await partViewModel.releaseParts(items: items) +// +// await MainActor.run { +// isProcessing = false +// switch result { +// case .success(let msg): +// alertMessage = msg +// showAlert = true +// // 성공 시 리스트 비우기 +// partStore.clear() +// // 호출자도 알림을 원하면 onUseParts 호출 +// onUseParts() +// dismiss() +// case .failure(let err): +// alertMessage = err.message +// showAlert = true +// } +// } +// } +//} + +//import SwiftUI +// +//struct PartBottomSheetView: View { +// @EnvironmentObject var partStore: PartStore +// @Environment(\.dismiss) private var dismiss +// @StateObject private var partViewModel = PartViewModel() +// +// var body: some View { +// VStack(spacing: 16) { +// Text("추가된 부품 목록") +// .font(.headline) +// .padding(.top, 8) +// +// List { +// ForEach(partStore.parts) { part in +// VStack(alignment: .leading, spacing: 4) { +// HStack { +// AsyncImage(url: URL(string: part.image)) { image in +// image.resizable().scaledToFit() +// } placeholder: { +// Color.gray +// } +// .frame(width: 60, height: 60) +// .cornerRadius(8) +// +// VStack(alignment: .leading) { +// Text(part.korName).font(.headline) +// Text("\(part.model) / \(part.trim)") +// .font(.subheadline) +// .foregroundColor(.gray) +// } +// +// Spacer() +// +// Stepper("", value: Binding( +// get: { part.quantity }, +// set: { newValue in +// partStore.updateQuantity(for: part.id, to: newValue) +// } +// ), in: 1...100) +// .labelsHidden() +// } +// +// Text("₩\(part.price)") +// .font(.subheadline) +// .foregroundColor(.secondary) +// } +// .padding(.vertical, 4) +// } +// .onDelete { indexSet in +// indexSet.forEach { idx in +// partStore.removePart(partStore.parts[idx].id) +// } +// } +// } +// +// HStack { +// Button("부품 추가") { +// dismiss() // 다시 QR 스캔 화면으로 복귀 +// } +// .buttonStyle(.borderedProminent) +// +// Button("사용 처리") { +// Task { +// let payload = partStore.parts.map { +// ReleaseItemRequest(partId: $0.id, quantity: $0.quantity) +// } +// let result = await partViewModel.releaseParts(items: payload) +// switch result { +// case .success(let msg): +// print("✅ 사용처리 성공:", msg) +// partStore.clear() +// dismiss() +// case .failure(let err): +// print("❌ 실패:", err.message) +// } +// } +// } +// .buttonStyle(.bordered) +// } +// .padding() +// } +// .presentationDetents([.medium, .large]) +// } +//} + +//import SwiftUI +// +//struct PartBottomSheetView: View { +// @EnvironmentObject var partStore: PartStore +// @Environment(\.dismiss) private var dismiss +// +// // part: 지금 방금 API로 받아온 파트 (아직 PartStore에 추가되지 않을 수 있음) +// let part: PartDetail +// +// @State private var quantity = 1 +// @State private var isProcessingUse = false +// @State private var showErrorAlert = false +// @State private var errorMessage = "" +// +// var body: some View { +// VStack(spacing: 12) { +// AsyncImage(url: URL(string: part.image)) { phase in +// switch phase { +// case .success(let image): +// image.resizable().scaledToFit() +// default: +// Color.gray +// } +// } +// .frame(height: 150) +// +// Text(part.korName) +// .font(.headline) +// Text("\(part.model) / \(part.trim)") +// .font(.subheadline) +// Text("₩\(part.price)") +// .font(.title3) +// .bold() +// +// Stepper("수량: \(quantity)", value: $quantity, in: 1...100) +// .padding(.vertical) +// +// HStack(spacing: 12) { +// // 1) 부품 추가: 현재 파트를 PartStore에 추가한 뒤 닫아서 QR 스캔 화면으로 돌아감 +// Button(action: { +// addCurrentPartToStoreAndReturnToScanner() +// }) { +// Text("부품 추가") +// .frame(maxWidth: .infinity) +// } +// .buttonStyle(.bordered) +// +// // 2) 사용 처리: PartStore의 항목들(및 현재 파트가 미추가 상태면 그것도 포함)을 서버로 전송 +// Button(action: { +// Task { +// await handleUseNow() +// } +// }) { +// if isProcessingUse { +// ProgressView() +// .frame(maxWidth: .infinity) +// } else { +// Text("사용 처리") +// .frame(maxWidth: .infinity) +// } +// } +// .buttonStyle(.borderedProminent) +// .disabled(isProcessingUse) +// } +// } +// .padding() +// .alert("오류", isPresented: $showErrorAlert) { +// Button("확인", role: .cancel) { } +// } message: { +// Text(errorMessage) +// } +// .onAppear { +// // 기본 수량을 part의 quantity가 이미 설정되어 있으면 그걸로. (일반적으로 1) +// quantity = part.quantity +// } +// } +// +// // MARK: - Helpers +// +// // 부품 추가 누르면 현재 파트를 PartStore에 넣고 바텀시트 닫음 (QR 스캔 화면으로 돌아감) +// private func addCurrentPartToStoreAndReturnToScanner() { +// var newPart = part +// newPart.quantity = quantity +// partStore.addPart(newPart) +// dismiss() // QR 화면으로 돌아감 (QR 화면이 스택에 남아있다면 바로 보일 것) +// } +// +// // 사용 처리: 현재 파트를 포함해 전부 전송 +// private func handleUseNow() async { +// isProcessingUse = true +// +// // 현재 part가 이미 store에 있는지 확인 +// if !partStore.parts.contains(where: { $0.id == part.id }) { +// // 자동으로 포함시켜서 누락 방지 +// var newPart = part +// newPart.quantity = quantity +// partStore.addPart(newPart) +// } else { +// // 이미 있으면 사용자가 바텀시트에서 조정한 수량을 반영 +// partStore.updateQuantity(for: part.id, to: quantity) +// } +// +// // payload 생성 +// let payload = partStore.makeRequestPayload() +// // ReleaseItemRequest로 변환 (예시) +// let items = partStore.parts.map { ReleaseItemRequest(partId: $0.id, quantity: $0.quantity) } +// +// // 실제로는 네가 사용 중인 ViewModel/Repository를 호출하는 게 좋음 (여기서는 간단 시도) +// let repository = PartRepositoryImpl() +// let result = await repository.releaseParts(items: items) +// +// await MainActor.run { +// isProcessingUse = false +// switch result { +// case .success(let apiResponse): +// // 성공 메시지 처리 — 필요하면 화면 닫고 partStore.clear() 호출 +// // 예: 사용 처리가 성공하면 임시 저장 리스트 비우기 +// partStore.parts.removeAll() +// dismiss() +// case .failure(let appError): +// // 실패 시 메시지 보여주기 +// errorMessage = appError.message +// showErrorAlert = true +// } +// } +// } +//} diff --git a/StockMate/StockMate/app/feature/parts/data/PartApi.swift b/StockMate/StockMate/app/feature/parts/data/PartApi.swift index ede241d..be73d35 100644 --- a/StockMate/StockMate/app/feature/parts/data/PartApi.swift +++ b/StockMate/StockMate/app/feature/parts/data/PartApi.swift @@ -15,8 +15,7 @@ struct ReleaseItemRequest: Encodable { let quantity: Int } -// ✅ 부품 상세 정보 모델 -struct PartDetailResponse: Decodable { +struct PartDetailResponse: Decodable, Identifiable { let id: Int let name: String let price: Int @@ -33,6 +32,18 @@ struct PartDetailResponse: Decodable { let cost: Int } +// 사용처리 임시 값 +struct PartDetail: Identifiable, Equatable { + let id: Int + let price: Int + let image: String + let trim: String + let model: String + let korName: String + let categoryName: String + var quantity: Int = 1 +} + // ✅ API 정의 enum PartApi { @@ -50,18 +61,17 @@ enum PartApi { encoding: JSONEncoding.default ) } - + // ✅ 부품 상세 조회 API - static func fetchPartDetail(partId: Int) -> DataRequest { - let url = ApiClient.baseURL + "api/v1/parts/detail" - let body: [String: Any] = ["partId": partId] - - return ApiClient.shared.request( - url, - method: .post, - parameters: body, - encoding: JSONEncoding.default - ) - } - + static func fetchPartDetail(partIds: [Int]) -> DataRequest { + let url = ApiClient.baseURL + "api/v1/parts/detail" + + // ✅ 요청 본문은 단순 배열 형태이므로 parameters 사용 X, 직접 body에 encode + return ApiClient.shared.request( + url, + method: .post, + parameters: partIds, + encoder: JSONParameterEncoder.default + ) + } } diff --git a/StockMate/StockMate/app/feature/parts/data/PartRepositoryImpl.swift b/StockMate/StockMate/app/feature/parts/data/PartRepositoryImpl.swift index 8a063cc..cc577f5 100644 --- a/StockMate/StockMate/app/feature/parts/data/PartRepositoryImpl.swift +++ b/StockMate/StockMate/app/feature/parts/data/PartRepositoryImpl.swift @@ -14,9 +14,9 @@ final class PartRepositoryImpl: PartRepositoryProtocol { return await safeApi(dataReq, decodeTo: ApiResponse.self) } - // ✅ 부품 상세 조회 API - func fetchPartDetail(partId: Int) async -> AppResult> { - let dataReq = PartApi.fetchPartDetail(partId: partId) + func fetchPartDetail(partIds: [Int]) async -> AppResult> { + let dataReq = PartApi.fetchPartDetail(partIds: partIds) return await safeApi(dataReq, decodeTo: ApiResponse<[PartDetailResponse]>.self) } + } diff --git a/StockMate/StockMate/app/feature/parts/data/PartStore.swift b/StockMate/StockMate/app/feature/parts/data/PartStore.swift new file mode 100644 index 0000000..4217933 --- /dev/null +++ b/StockMate/StockMate/app/feature/parts/data/PartStore.swift @@ -0,0 +1,40 @@ +// +// PartStore.swift +// StockMate +// +// Created by Admin on 11/3/25. +// + +import Foundation + +@MainActor +final class PartStore: ObservableObject { + @Published var parts: [PartDetail] = [] + + func addPart(_ part: PartDetail) { + if let index = parts.firstIndex(where: { $0.id == part.id }) { + // 이미 있으면 수량만 1 증가 + parts[index].quantity += 1 + } else { + parts.append(part) + } + } + + func updateQuantity(for partId: Int, to quantity: Int) { + if let index = parts.firstIndex(where: { $0.id == partId }) { + parts[index].quantity = quantity + } + } + + func removePart(_ partId: Int) { + parts.removeAll { $0.id == partId } + } + + func makeRequestPayload() -> [[String: Any]] { + parts.map { ["partId": $0.id, "quantity": $0.quantity] } + } + + func clear() { + parts.removeAll() + } +} diff --git a/StockMate/StockMate/app/feature/parts/domain/PartRepositoryProtocol.swift b/StockMate/StockMate/app/feature/parts/domain/PartRepositoryProtocol.swift index 3e028d3..14f73c1 100644 --- a/StockMate/StockMate/app/feature/parts/domain/PartRepositoryProtocol.swift +++ b/StockMate/StockMate/app/feature/parts/domain/PartRepositoryProtocol.swift @@ -9,5 +9,5 @@ import Foundation protocol PartRepositoryProtocol { func releaseParts(items: [ReleaseItemRequest]) async -> AppResult> - func fetchPartDetail(partId: Int) async -> AppResult> + func fetchPartDetail(partIds: [Int]) async -> AppResult> } diff --git a/StockMate/StockMate/app/feature/parts/ui/QRScannerView.swift b/StockMate/StockMate/app/feature/parts/ui/QRScannerView.swift index 761468f..c8a6a30 100644 --- a/StockMate/StockMate/app/feature/parts/ui/QRScannerView.swift +++ b/StockMate/StockMate/app/feature/parts/ui/QRScannerView.swift @@ -16,8 +16,14 @@ struct QRScannerView: UIViewControllerRepresentable { return controller } - func updateUIViewController(_ uiViewController: QRScannerViewController, context: Context) {} - +// func updateUIViewController(_ uiViewController: QRScannerViewController, context: Context) {} + func updateUIViewController(_ uiViewController: QRScannerViewController, context: Context) { + // ✅ scannedCode가 nil이면 다시 스캔 시작 + if scannedCode == nil { + uiViewController.startScanning() + } + } + func makeCoordinator() -> Coordinator { Coordinator(self) } diff --git a/StockMate/StockMate/app/feature/parts/ui/QRScannerViewController.swift b/StockMate/StockMate/app/feature/parts/ui/QRScannerViewController.swift index f338a55..e36d7b4 100644 --- a/StockMate/StockMate/app/feature/parts/ui/QRScannerViewController.swift +++ b/StockMate/StockMate/app/feature/parts/ui/QRScannerViewController.swift @@ -51,13 +51,32 @@ final class QRScannerViewController: UIViewController, AVCaptureMetadataOutputOb previewLayer.videoGravity = .resizeAspectFill view.layer.addSublayer(previewLayer) + + startScanning() // ⚠️ 백그라운드에서 실행 - DispatchQueue.global(qos: .userInitiated).async { - self.captureSession.startRunning() +// DispatchQueue.global(qos: .userInitiated).async { +// self.captureSession.startRunning() +// } + } + + // ✅ 스캔 재시작/중단 함수 추가 + func startScanning() { + guard captureSession != nil else { return } + if !captureSession.isRunning { + DispatchQueue.global(qos: .userInitiated).async { + self.captureSession.startRunning() + } } } - + func stopScanning() { + guard captureSession != nil else { return } + if captureSession.isRunning { + captureSession.stopRunning() + } + } + + // ✅ QR 감지 시 호출 func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) { if let metadataObject = metadataObjects.first as? AVMetadataMachineReadableCodeObject, let stringValue = metadataObject.stringValue { diff --git a/StockMate/StockMate/app/feature/parts/viewmodel/PartViewModel.swift b/StockMate/StockMate/app/feature/parts/viewmodel/PartViewModel.swift index ead6256..03543f8 100644 --- a/StockMate/StockMate/app/feature/parts/viewmodel/PartViewModel.swift +++ b/StockMate/StockMate/app/feature/parts/viewmodel/PartViewModel.swift @@ -15,59 +15,16 @@ final class PartViewModel: ObservableObject { @Published var shouldGoToLogin = false - // ✅ 임시 저장 리스트 - @Published var selectedParts: [PartDetailResponse] = [] - @Published var quantities: [Int: Int] = [:] // partId별 수량 관리 + @Published var partDetails: [PartDetailResponse] = [] + @Published var quantities: [Int: Int] = [:] // partId별 수량 관리 private let repo: PartRepositoryProtocol init(repo: PartRepositoryProtocol = PartRepositoryImpl()) { self.repo = repo } - - // ✅ 부품 상세 조회 (QR 스캔 후) - func fetchPartDetail(partId: Int) async { - isLoading = true - defer { isLoading = false } - - let result = await repo.fetchPartDetail(partId: partId) - switch result { - case .success(let apiResp): - if let detail = apiResp.data?.first { - if !selectedParts.contains(where: { $0.id == detail.id }) { - selectedParts.append(detail) - quantities[detail.id] = 1 - } else { - quantities[detail.id, default: 1] += 1 - } - } - case .failure(let error): - message = error.message - } - } - - func increaseQuantity(for partId: Int) { - quantities[partId, default: 1] += 1 - } - - func decreaseQuantity(for partId: Int) { - quantities[partId] = max(1, (quantities[partId] ?? 1) - 1) - } - - func removePart(partId: Int) { - selectedParts.removeAll { $0.id == partId } - quantities.removeValue(forKey: partId) - } - func makeReleasePayload() -> [ReleaseItemRequest] { - selectedParts.map { part in - ReleaseItemRequest(partId: part.id, quantity: quantities[part.id] ?? 1) - } - } - - - func releaseParts(items: [ReleaseItemRequest]) async -> AppResult { isLoading = true defer { isLoading = false } @@ -90,5 +47,25 @@ final class PartViewModel: ObservableObject { } } + func fetchPartDetail(partId: Int) async { + isLoading = true + defer { isLoading = false } + + let result = await repo.fetchPartDetail(partIds: [partId]) + switch result { + case .success(let apiResp): + if apiResp.success, let data = apiResp.data { + partDetails = data + print("✅ 부품 상세 조회 성공:", data) + } else { + message = apiResp.message + print("⚠️ 서버 응답 실패:", apiResp.message) + } + case .failure(let err): + message = err.message + print("❌ 네트워크 오류:", err) + } + } + } diff --git a/StockMate/StockMate/app/navigation/AppNavHost.swift b/StockMate/StockMate/app/navigation/AppNavHost.swift index f2c5bca..c55ea0a 100644 --- a/StockMate/StockMate/app/navigation/AppNavHost.swift +++ b/StockMate/StockMate/app/navigation/AppNavHost.swift @@ -9,6 +9,8 @@ import SwiftUI struct AppNavHost: View { @EnvironmentObject var authViewModel: AuthViewModel + @StateObject private var partStore = PartStore() + var body: some View { NavigationStack { @@ -22,6 +24,7 @@ struct AppNavHost: View { RegisterView() case .authenticated: MainTabView() + .environmentObject(partStore) } } } diff --git a/StockMate/StockMate/app/navigation/MainTabView.swift b/StockMate/StockMate/app/navigation/MainTabView.swift index b5bf61c..acc21ef 100644 --- a/StockMate/StockMate/app/navigation/MainTabView.swift +++ b/StockMate/StockMate/app/navigation/MainTabView.swift @@ -22,7 +22,12 @@ struct MainTabView: View { case 1: NavigationStack{ OrderView(cartViewModel: cartVM) } //, inventoryViewModel: inventoryVM) } // case 1: NavigationStack{ OrderView() } case 2: - NavigationStack { InventoryView(selectedTab: $selectedTab, tabTappedTrigger: $tabTappedTrigger) } + InventoryView( + selectedTab: $selectedTab, + tabTappedTrigger: $tabTappedTrigger + ) + +// NavigationStack { InventoryView(selectedTab: $selectedTab, tabTappedTrigger: $tabTappedTrigger) } // case 3: NavigationStack{ ContentView() } // case 3: NavigationStack{ ReceiptView() } case 3: NavigationStack{ ProfileView() } From 5edc05fd9220e86c9fe0ae47c526507f8dcc80a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Tue, 4 Nov 2025 00:10:06 +0900 Subject: [PATCH 51/85] =?UTF-8?q?[FEAT]=20=EB=B6=80=ED=92=88=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=B2=98=EB=A6=AC=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EC=9E=84=EC=8B=9C=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../inventory/ui/OutgoingScanView.swift | 9 +- .../inventory/ui/PartBottomSheetView.swift | 414 ++++++++++++------ .../app/feature/parts/data/PartStore.swift | 8 +- 3 files changed, 305 insertions(+), 126 deletions(-) diff --git a/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift b/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift index a6b11cb..280a9ee 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift @@ -105,9 +105,14 @@ struct OutgoingScanView: View { onAddPart: { // ✅ 부품 추가 버튼 액션 partStore.addPart(part) + + // ✅ UI가 업데이트될 시간을 준 후 스캐너 리셋 + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { + resetScanState() + } // ✅ 상태 초기화 (QR 다시 가능하도록) - resetScanState() +// resetScanState() }, onUseParts: { // ✅ 사용 처리 버튼 액션 (예: 서버 전송) @@ -261,7 +266,7 @@ struct OutgoingScanView: View { scannedCode = nil partDetail = nil showBottomSheet = false - partViewModel.partDetails.removeAll() +// partViewModel.partDetails.removeAll() // ✅ 카메라 세션 재시작 scannerRestartTrigger.toggle() diff --git a/StockMate/StockMate/app/feature/inventory/ui/PartBottomSheetView.swift b/StockMate/StockMate/app/feature/inventory/ui/PartBottomSheetView.swift index 827e919..8075a0a 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/PartBottomSheetView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/PartBottomSheetView.swift @@ -24,179 +24,91 @@ struct PartBottomSheetView: View { var body: some View { VStack(spacing: 16) { - // MARK: - 방금 스캔한 부품 미리보기 (추가 전) + // ✅ 방금 스캔한 부품 미리보기 HStack(spacing: 12) { AsyncImage(url: URL(string: part.image)) { phase in switch phase { - case .success(let img): - img.resizable().scaledToFill() - default: - Color.gray.opacity(0.3) + case .success(let img): img.resizable().scaledToFill() + default: Color.gray.opacity(0.3) } } .frame(width: 48, height: 48) - .clipped() .cornerRadius(6) VStack(alignment: .leading, spacing: 4) { - Text(part.korName) - .font(.subheadline) + Text(part.korName).font(.subheadline) Text("\(part.model) / \(part.trim)") - .font(.caption) - .foregroundColor(.gray) - Text("\(part.price) 원") - .font(.subheadline) + .font(.caption).foregroundColor(.gray) + Text("\(part.price) 원").font(.subheadline) } Spacer() - - // ✅ 수정된 수량 조절 구역 + HStack(spacing: 8) { - Button(action: { - if quantity > 1 { quantity -= 1 } - }) { + Button(action: { if quantity > 1 { quantity -= 1 } }) { Image(systemName: "minus.circle.fill") - .font(.title2) - .foregroundColor(.blue) + .font(.title2).foregroundColor(.blue) } - - Text("\(quantity)") - .font(.body) - .frame(width: 44, alignment: .center) - - Button(action: { - quantity += 1 - }) { + Text("\(quantity)").frame(width: 44) + Button(action: { quantity += 1 }) { Image(systemName: "plus.circle.fill") - .font(.title2) - .foregroundColor(.blue) + .font(.title2).foregroundColor(.blue) } - - Spacer() } - .padding(.horizontal) } .padding(.horizontal) - .padding() - - + .padding(.vertical, 8) + // ✅ 현재까지 누적된 부품 리스트 List { - ForEach(partStore.parts.indices, id: \.self) { idx in - let p = partStore.parts[idx] + ForEach(partStore.parts) { p in HStack { AsyncImage(url: URL(string: p.image)) { phase in switch phase { - case .success(let img): - img.resizable().scaledToFill() - default: - Color.gray.opacity(0.3) + case .success(let img): img.resizable().scaledToFill() + default: Color.gray.opacity(0.3) } } .frame(width: 48, height: 48) - .clipped() .cornerRadius(6) VStack(alignment: .leading, spacing: 4) { - Text(p.korName) - .font(.subheadline) + Text(p.korName).font(.subheadline) Text("\(p.model) / \(p.trim)") - .font(.caption) - .foregroundColor(.gray) - Text("\(p.price) 원") - .font(.subheadline) + .font(.caption).foregroundColor(.gray) } Spacer() - // ✅ 명확한 수량 표시 + +/- 버튼 (Stepper 대신 커스텀 컨트롤) HStack(spacing: 8) { - Button { - let newQty = max(1, p.quantity - 1) - partStore.updateQuantity(for: p.id, to: newQty) - } label: { - Image(systemName: "minus.circle") - .font(.title2) + Button { partStore.updateQuantity(for: p.id, to: max(1, p.quantity - 1)) } label: { + Image(systemName: "minus.circle").font(.title2) } - - Text("\(p.quantity)") - .font(.body) - .frame(width: 44, alignment: .center) - - Button { - let newQty = p.quantity + 1 - partStore.updateQuantity(for: p.id, to: newQty) - } label: { - Image(systemName: "plus.circle") - .font(.title2) + Text("\(p.quantity)").frame(width: 44) + Button { partStore.updateQuantity(for: p.id, to: p.quantity + 1) } label: { + Image(systemName: "plus.circle").font(.title2) } } - .buttonStyle(.plain) .foregroundColor(.blue) } - .padding() + .padding(.vertical, 4) } .onDelete { idxs in - idxs.forEach { i in - let id = partStore.parts[i].id - partStore.removePart(id) - } + idxs.forEach { partStore.removePart(partStore.parts[$0].id) } } } .frame(maxHeight: 240) .listStyle(.plain) -// List { -// ForEach(partStore.parts) { p in -// HStack { -// AsyncImage(url: URL(string: p.image)) { phase in -// switch phase { -// case .success(let img): -// img.resizable().scaledToFill() -// default: -// Color.gray.opacity(0.3) -// } -// } -// .frame(width: 48, height: 48) -// .clipped() -// .cornerRadius(6) -// -// VStack(alignment: .leading, spacing: 4) { -// Text(p.korName) -// .font(.subheadline) -// Text("\(p.model) / \(p.trim)") -// .font(.caption) -// .foregroundColor(.gray) -// } -// -// Spacer() -// -// Stepper("", value: Binding( -// get: { p.quantity }, -// set: { newVal in partStore.updateQuantity(for: p.id, to: newVal) } -// ), in: 1...999) -// .labelsHidden() -// } -// .padding(.vertical, 6) -// } -// .onDelete { idxs in -// idxs.forEach { i in -// let id = partStore.parts[i].id -// partStore.removePart(id) -// } -// } -// } -// .frame(maxHeight: 240) -// .listStyle(.plain) Spacer() - // — 하단 버튼 — + // ✅ 하단 버튼 HStack(spacing: 12) { Button { var newPart = part newPart.quantity = quantity partStore.addPart(newPart) - onAddPart() +// onAddPart() // QR 다시 준비 dismiss() } label: { Text("부품 추가") @@ -225,19 +137,16 @@ struct PartBottomSheetView: View { .padding(.bottom, 8) } .presentationDetents([.medium, .large]) - .onAppear { - quantity = part.quantity - } + .onAppear { quantity = part.quantity } .alert("알림", isPresented: $showAlert) { Button("확인") {} - } message: { - Text(alertMessage) - } + } message: { Text(alertMessage) } } private func handleUseNow() async { await MainActor.run { isProcessing = true } + // ✅ 현재 부품이 아직 store에 없으면 추가 if !partStore.parts.contains(where: { $0.id == part.id }) { var newPart = part newPart.quantity = quantity @@ -265,6 +174,267 @@ struct PartBottomSheetView: View { } } } + +//import SwiftUI +// +//struct PartBottomSheetView: View { +// @EnvironmentObject var partStore: PartStore +// @Environment(\.dismiss) private var dismiss +// @StateObject private var partViewModel = PartViewModel() +// +// let part: PartDetail +// let onAddPart: () -> Void +// let onUseParts: () -> Void +// +// @State private var quantity: Int = 1 +// @State private var isProcessing = false +// @State private var showAlert = false +// @State private var alertMessage = "" +// +// var body: some View { +// VStack(spacing: 16) { +// +// // MARK: - 방금 스캔한 부품 미리보기 (추가 전) +// HStack(spacing: 12) { +// AsyncImage(url: URL(string: part.image)) { phase in +// switch phase { +// case .success(let img): +// img.resizable().scaledToFill() +// default: +// Color.gray.opacity(0.3) +// } +// } +// .frame(width: 48, height: 48) +// .clipped() +// .cornerRadius(6) +// +// VStack(alignment: .leading, spacing: 4) { +// Text(part.korName) +// .font(.subheadline) +// Text("\(part.model) / \(part.trim)") +// .font(.caption) +// .foregroundColor(.gray) +// Text("\(part.price) 원") +// .font(.subheadline) +// } +// +// Spacer() +// +// // ✅ 수정된 수량 조절 구역 +// HStack(spacing: 8) { +// Button(action: { +// if quantity > 1 { quantity -= 1 } +// }) { +// Image(systemName: "minus.circle.fill") +// .font(.title2) +// .foregroundColor(.blue) +// } +// +// Text("\(quantity)") +// .font(.body) +// .frame(width: 44, alignment: .center) +// +// Button(action: { +// quantity += 1 +// }) { +// Image(systemName: "plus.circle.fill") +// .font(.title2) +// .foregroundColor(.blue) +// } +// +// Spacer() +// } +// .padding(.horizontal) +// } +// .padding(.horizontal) +// .padding() +// +// +// +// List { +// ForEach(partStore.parts.indices, id: \.self) { idx in +// let p = partStore.parts[idx] +// HStack { +// AsyncImage(url: URL(string: p.image)) { phase in +// switch phase { +// case .success(let img): +// img.resizable().scaledToFill() +// default: +// Color.gray.opacity(0.3) +// } +// } +// .frame(width: 48, height: 48) +// .clipped() +// .cornerRadius(6) +// +// VStack(alignment: .leading, spacing: 4) { +// Text(p.korName) +// .font(.subheadline) +// Text("\(p.model) / \(p.trim)") +// .font(.caption) +// .foregroundColor(.gray) +// Text("\(p.price) 원") +// .font(.subheadline) +// } +// +// Spacer() +// +// // ✅ 명확한 수량 표시 + +/- 버튼 (Stepper 대신 커스텀 컨트롤) +// HStack(spacing: 8) { +// Button { +// let newQty = max(1, p.quantity - 1) +// partStore.updateQuantity(for: p.id, to: newQty) +// } label: { +// Image(systemName: "minus.circle") +// .font(.title2) +// } +// +// Text("\(p.quantity)") +// .font(.body) +// .frame(width: 44, alignment: .center) +// +// Button { +// let newQty = p.quantity + 1 +// partStore.updateQuantity(for: p.id, to: newQty) +// } label: { +// Image(systemName: "plus.circle") +// .font(.title2) +// } +// } +// .buttonStyle(.plain) +// .foregroundColor(.blue) +// } +// .padding() +// } +// .onDelete { idxs in +// idxs.forEach { i in +// let id = partStore.parts[i].id +// partStore.removePart(id) +// } +// } +// } +// .frame(maxHeight: 240) +// .listStyle(.plain) +//// List { +//// ForEach(partStore.parts) { p in +//// HStack { +//// AsyncImage(url: URL(string: p.image)) { phase in +//// switch phase { +//// case .success(let img): +//// img.resizable().scaledToFill() +//// default: +//// Color.gray.opacity(0.3) +//// } +//// } +//// .frame(width: 48, height: 48) +//// .clipped() +//// .cornerRadius(6) +//// +//// VStack(alignment: .leading, spacing: 4) { +//// Text(p.korName) +//// .font(.subheadline) +//// Text("\(p.model) / \(p.trim)") +//// .font(.caption) +//// .foregroundColor(.gray) +//// } +//// +//// Spacer() +//// +//// Stepper("", value: Binding( +//// get: { p.quantity }, +//// set: { newVal in partStore.updateQuantity(for: p.id, to: newVal) } +//// ), in: 1...999) +//// .labelsHidden() +//// } +//// .padding(.vertical, 6) +//// } +//// .onDelete { idxs in +//// idxs.forEach { i in +//// let id = partStore.parts[i].id +//// partStore.removePart(id) +//// } +//// } +//// } +//// .frame(maxHeight: 240) +//// .listStyle(.plain) +// +// Spacer() +// +// // — 하단 버튼 — +// HStack(spacing: 12) { +// Button { +// var newPart = part +// newPart.quantity = quantity +// partStore.addPart(newPart) +// onAddPart() +// dismiss() +// } label: { +// Text("부품 추가") +// .frame(maxWidth: .infinity) +// .padding(.vertical, 12) +// } +// .buttonStyle(.borderedProminent) +// +// Button { +// Task { await handleUseNow() } +// } label: { +// if isProcessing { +// ProgressView() +// .frame(maxWidth: .infinity) +// .padding(.vertical, 12) +// } else { +// Text("사용 처리") +// .frame(maxWidth: .infinity) +// .padding(.vertical, 12) +// } +// } +// .buttonStyle(.bordered) +// .disabled(isProcessing) +// } +// .padding(.horizontal) +// .padding(.bottom, 8) +// } +// .presentationDetents([.medium, .large]) +// .onAppear { +// quantity = part.quantity +// } +// .alert("알림", isPresented: $showAlert) { +// Button("확인") {} +// } message: { +// Text(alertMessage) +// } +// } +// +// private func handleUseNow() async { +// await MainActor.run { isProcessing = true } +// +// if !partStore.parts.contains(where: { $0.id == part.id }) { +// var newPart = part +// newPart.quantity = quantity +// partStore.addPart(newPart) +// } else { +// partStore.updateQuantity(for: part.id, to: quantity) +// } +// +// let items = partStore.parts.map { ReleaseItemRequest(partId: $0.id, quantity: $0.quantity) } +// let result = await partViewModel.releaseParts(items: items) +// +// await MainActor.run { +// isProcessing = false +// switch result { +// case .success(let msg): +// alertMessage = msg +// showAlert = true +// partStore.clear() +// onUseParts() +// dismiss() +// case .failure(let err): +// alertMessage = err.message +// showAlert = true +// } +// } +// } +//} //struct PartBottomSheetView: View { // @EnvironmentObject var partStore: PartStore // @Environment(\.dismiss) private var dismiss diff --git a/StockMate/StockMate/app/feature/parts/data/PartStore.swift b/StockMate/StockMate/app/feature/parts/data/PartStore.swift index 4217933..5d31d76 100644 --- a/StockMate/StockMate/app/feature/parts/data/PartStore.swift +++ b/StockMate/StockMate/app/feature/parts/data/PartStore.swift @@ -14,9 +14,13 @@ final class PartStore: ObservableObject { func addPart(_ part: PartDetail) { if let index = parts.firstIndex(where: { $0.id == part.id }) { // 이미 있으면 수량만 1 증가 - parts[index].quantity += 1 +// parts[index].quantity += 1 } else { - parts.append(part) + // ✅ 반드시 새로운 복사본 append + var newPart = part + newPart.quantity = 1 + parts.append(newPart) +// parts.append(part) } } From e4ee73428f74ebf8478f4c480e8d288f61b7a1ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Tue, 4 Nov 2025 01:27:48 +0900 Subject: [PATCH 52/85] =?UTF-8?q?[FEAT]=20=EB=B6=80=ED=92=88=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=B2=98=EB=A6=AC=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../inventory/ui/OutgoingScanView.swift | 261 ++---- .../inventory/ui/PartBottomSheetView.swift | 842 ------------------ .../inventory/ui/UsedPartListSheetView.swift | 122 +++ .../app/feature/parts/data/PartApi.swift | 2 +- .../app/feature/parts/data/PartStore.swift | 35 +- .../app/feature/parts/ui/QRScannerView.swift | 17 +- .../parts/ui/QRScannerViewController.swift | 13 + 7 files changed, 254 insertions(+), 1038 deletions(-) delete mode 100644 StockMate/StockMate/app/feature/inventory/ui/PartBottomSheetView.swift create mode 100644 StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift diff --git a/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift b/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift index 280a9ee..a4d1105 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift @@ -14,24 +14,26 @@ struct OutgoingScanView: View { @State private var showAlert = false @State private var alertMessage = "" - // 사용 처리 임시 + // 전역 부품 저장소 @EnvironmentObject var partStore: PartStore + @StateObject private var partViewModel = PartViewModel() // ✅ ViewModel 추가 + @State private var showBottomSheet = false @State private var scannerRestartTrigger = false -// @State private var isPresentingBottomSheet = false @State private var partDetail: PartDetail? = nil - @StateObject private var partViewModel = PartViewModel() // ✅ ViewModel 추가 var body: some View { ZStack { // ✅ 카메라 미리보기 (QR 스캐너) - QRScannerView(scannedCode: $scannedCode) - .id(scannerRestartTrigger) // 👈 다시 렌더링되어 카메라 리셋됨 +// QRScannerView(scannedCode: $scannedCode) +// .ignoresSafeArea() + QRScannerView(scannedCode: $scannedCode, isActive: !showBottomSheet) .ignoresSafeArea() + // ✅ 스캔 가이드 및 UI 오버레이 VStack { Text("사용할 부품의 QR을 스캔해주세요") @@ -39,23 +41,23 @@ struct OutgoingScanView: View { .padding(.top, 60) .foregroundColor(.white) .shadow(radius: 2) - + Spacer() - + // 📷 스캔 박스 ZStack { RoundedRectangle(cornerRadius: 12) .fill(Color.clear) .frame(width: 250, height: 250) - + RoundedRectangle(cornerRadius: 8) .stroke(Color.green, lineWidth: 3) .frame(width: 220, height: 220) } .padding(.bottom, 180) - + Spacer() - + // 📦 직접 입력 버튼 Button(action: { dismiss() @@ -72,91 +74,72 @@ struct OutgoingScanView: View { .padding(.horizontal, 40) .padding(.bottom, 40) } - + // ✅ 로딩 인디케이터 if partViewModel.isLoading { - Color.black.opacity(0.3).ignoresSafeArea() - ProgressView("부품 조회 중...") - .padding() - .background(.ultraThinMaterial) - .cornerRadius(10) - } -// if partViewModel.isLoading { -// Color.black.opacity(0.3).ignoresSafeArea() -// ProgressView("부품 사용 처리 중...") -// .padding() -// .background(.ultraThinMaterial) -// .cornerRadius(10) -// } + Color.black.opacity(0.3).ignoresSafeArea() + ProgressView("부품 조회 중...") //ProgressView("부품 사용 처리 중...") + .padding() + .background(.ultraThinMaterial) + .cornerRadius(10) + } } .alert("알림", isPresented: $showAlert) { - Button("확인") {} - } message: { - Text(alertMessage) - } - .onChange(of: scannedCode) { newValue in - guard let code = newValue, !code.isEmpty else { return } - Task { await handleScannedCode(code) } - } - .sheet(isPresented: $showBottomSheet) { - if let part = partDetail { - PartBottomSheetView( - part: part, - onAddPart: { - // ✅ 부품 추가 버튼 액션 - partStore.addPart(part) - - // ✅ UI가 업데이트될 시간을 준 후 스캐너 리셋 - DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { - resetScanState() - } - - // ✅ 상태 초기화 (QR 다시 가능하도록) -// resetScanState() - }, - onUseParts: { - // ✅ 사용 처리 버튼 액션 (예: 서버 전송) - let payload = partStore.makeRequestPayload() - print("🚀 사용 처리 API 호출 payload:", payload) - } - ) - .environmentObject(partStore) - } - } + Button("확인") {} + } message: { + Text(alertMessage) + } + .onChange(of: scannedCode) { newValue in + guard let code = newValue, !code.isEmpty else { return } + Task { await handleScannedCode(code) } + } + .sheet(isPresented: $showBottomSheet) { + UsedPartListSheetView( + onUseParts: { + Task { + // ✅ payload 생성 + let payload = partStore.parts.map { + ReleaseItemRequest(partId: $0.id, quantity: $0.quantity) + } + + // ✅ API 호출 + let result = await partViewModel.releaseParts(items: payload) + + await MainActor.run { + switch result { + case .success(let message): + alertMessage = message + showAlert = true + + // ✅ 성공 시: 전역 부품 초기화 + 바텀시트 닫기 + 화면 복귀 + partStore.clear() + showBottomSheet = false + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { + dismiss() + } + + case .failure(let error): + alertMessage = error.message + showAlert = true + } + } + } + }, + onRescan: { + // ✅ 다시 스캔 버튼 눌렀을 때 + showBottomSheet = false + resetScanState() // QR 다시 활성화 + } + ) + .environmentObject(partStore) + } .navigationTitle("부품 사용 처리") .navigationBarTitleDisplayMode(.inline) -// .sheet(isPresented: $showBottomSheet) { -// PartBottomSheetView() -// .environmentObject(partStore) -// } -// .navigationTitle("부품 사용 처리") -// .navigationBarTitleDisplayMode(.inline) - - //-----// -// // ✅ 알림창 -// .alert("부품 사용 결과", isPresented: $showAlert) { -// Button("확인") { -// dismiss() -// } -// } message: { -// Text(alertMessage) -// } -// // ✅ QR 스캔 이벤트 발생 시 -// .onChange(of: scannedCode) { newValue in -// guard let code = newValue, !code.isEmpty else { return } -// Task { -// await handleScannedCode(code) -// } -// } -// .navigationTitle("부품 사용 처리") -// .navigationBarTitleDisplayMode(.inline) } // ✅ 스캔된 코드로 부품 상세 조회만 수행 (출고 X) private func handleScannedCode(_ code: String) async { - await MainActor.run { - partViewModel.isLoading = true - } + await MainActor.run { partViewModel.isLoading = true } // 문자열 → Int 변환 (QR 코드가 숫자 아닐 경우 예외 처리) guard let partId = Int(code) else { @@ -180,102 +163,34 @@ struct OutgoingScanView: View { showAlert = true return } - - partDetail = PartDetail( - id: response.id, - price: response.price, - image: response.image, - trim: response.trim, - model: response.model, - korName: response.korName, - categoryName: response.categoryName, - quantity: 1 - ) -// // ✅ API 응답을 PartDetail로 변환 -// let newPart = PartDetail( -// id: response.id, -// price: response.price, -// image: response.image, -// trim: response.trim, -// model: response.model, -// korName: response.korName, -// categoryName: response.categoryName, -// quantity: 1 -// ) -// -// // ✅ 리스트에 추가 -// partStore.addPart(newPart) - - // ✅ 바텀 시트 띄우기 - showBottomSheet = true - //-----/// + // ✅ 응답을 PartDetail로 변환 후 저장 + let newPart = PartDetail( + id: response.id, + price: response.price, + image: response.image, + trim: response.trim, + model: response.model, + korName: response.korName, + categoryName: response.categoryName, + quantity: 1 + ) -// if let part = partViewModel.partDetails.first { -// print("✅ 부품 상세 조회 성공") -// print("ID:", part.id) -// print("이름:", part.name) -// print("가격:", part.price) -// print("모델:", part.model) -// print("트림:", part.trim) -// print("코드:", part.code) -// alertMessage = "부품 조회 성공: \(part.name)" -// } else { -// print("⚠️ 부품 정보를 불러오지 못함") -// alertMessage = "부품 정보를 불러오지 못했습니다." -// } -// -// showAlert = true + partStore.addPart(newPart) + + // ✅ 자동으로 바텀시트 열기 + showBottomSheet = true + + // ✅ 스캔 상태 초기화 (다시 스캔 가능하도록) + resetScanState() + + print("✅ \(newPart.korName) 부품이 전역 Store에 추가됨") } } - -// // ✅ 스캔된 코드로 출고 API 호출 -// private func handleScannedCode(_ code: String) async { -// await MainActor.run { -// partViewModel.isLoading = true -// } -// -// // ✅ 문자열 → Int 변환 (QR 코드가 숫자 아닐 경우 예외 처리) -// guard let partId = Int(code) else { -// await MainActor.run { -// partViewModel.isLoading = false -// alertMessage = "잘못된 QR 코드입니다. (숫자형 ID가 아닙니다)" -// showAlert = true -// } -// return -// } -// -// // ✅ 요청 생성 및 API 호출 -// let request = [ReleaseItemRequest(partId: partId, quantity: 1)] // 기본 1개 사용 -// let result = await partViewModel.releaseParts(items: request) -// -// await MainActor.run { -// partViewModel.isLoading = false -// switch result { -// case .success(let message): -// alertMessage = message -// case .failure(let error): -// alertMessage = error.message -// } -// showAlert = true -// } -// } // ✅ 상태 초기화 (QR 다시 활성화) private func resetScanState() { scannedCode = nil - partDetail = nil - showBottomSheet = false -// partViewModel.partDetails.removeAll() - - // ✅ 카메라 세션 재시작 scannerRestartTrigger.toggle() } - } - -//#Preview { -// NavigationStack { -// OutgoingScanView() -// } -//} diff --git a/StockMate/StockMate/app/feature/inventory/ui/PartBottomSheetView.swift b/StockMate/StockMate/app/feature/inventory/ui/PartBottomSheetView.swift deleted file mode 100644 index 8075a0a..0000000 --- a/StockMate/StockMate/app/feature/inventory/ui/PartBottomSheetView.swift +++ /dev/null @@ -1,842 +0,0 @@ -// -// PartBottomSheetView.swift -// StockMate -// -// Created by Admin on 11/3/25. -// - -import SwiftUI - -struct PartBottomSheetView: View { - @EnvironmentObject var partStore: PartStore - @Environment(\.dismiss) private var dismiss - @StateObject private var partViewModel = PartViewModel() - - let part: PartDetail - let onAddPart: () -> Void - let onUseParts: () -> Void - - @State private var quantity: Int = 1 - @State private var isProcessing = false - @State private var showAlert = false - @State private var alertMessage = "" - - var body: some View { - VStack(spacing: 16) { - - // ✅ 방금 스캔한 부품 미리보기 - HStack(spacing: 12) { - AsyncImage(url: URL(string: part.image)) { phase in - switch phase { - case .success(let img): img.resizable().scaledToFill() - default: Color.gray.opacity(0.3) - } - } - .frame(width: 48, height: 48) - .cornerRadius(6) - - VStack(alignment: .leading, spacing: 4) { - Text(part.korName).font(.subheadline) - Text("\(part.model) / \(part.trim)") - .font(.caption).foregroundColor(.gray) - Text("\(part.price) 원").font(.subheadline) - } - - Spacer() - - HStack(spacing: 8) { - Button(action: { if quantity > 1 { quantity -= 1 } }) { - Image(systemName: "minus.circle.fill") - .font(.title2).foregroundColor(.blue) - } - Text("\(quantity)").frame(width: 44) - Button(action: { quantity += 1 }) { - Image(systemName: "plus.circle.fill") - .font(.title2).foregroundColor(.blue) - } - } - } - .padding(.horizontal) - .padding(.vertical, 8) - - // ✅ 현재까지 누적된 부품 리스트 - List { - ForEach(partStore.parts) { p in - HStack { - AsyncImage(url: URL(string: p.image)) { phase in - switch phase { - case .success(let img): img.resizable().scaledToFill() - default: Color.gray.opacity(0.3) - } - } - .frame(width: 48, height: 48) - .cornerRadius(6) - - VStack(alignment: .leading, spacing: 4) { - Text(p.korName).font(.subheadline) - Text("\(p.model) / \(p.trim)") - .font(.caption).foregroundColor(.gray) - } - - Spacer() - - HStack(spacing: 8) { - Button { partStore.updateQuantity(for: p.id, to: max(1, p.quantity - 1)) } label: { - Image(systemName: "minus.circle").font(.title2) - } - Text("\(p.quantity)").frame(width: 44) - Button { partStore.updateQuantity(for: p.id, to: p.quantity + 1) } label: { - Image(systemName: "plus.circle").font(.title2) - } - } - .foregroundColor(.blue) - } - .padding(.vertical, 4) - } - .onDelete { idxs in - idxs.forEach { partStore.removePart(partStore.parts[$0].id) } - } - } - .frame(maxHeight: 240) - .listStyle(.plain) - - Spacer() - - // ✅ 하단 버튼 - HStack(spacing: 12) { - Button { - var newPart = part - newPart.quantity = quantity - partStore.addPart(newPart) -// onAddPart() // QR 다시 준비 - dismiss() - } label: { - Text("부품 추가") - .frame(maxWidth: .infinity) - .padding(.vertical, 12) - } - .buttonStyle(.borderedProminent) - - Button { - Task { await handleUseNow() } - } label: { - if isProcessing { - ProgressView() - .frame(maxWidth: .infinity) - .padding(.vertical, 12) - } else { - Text("사용 처리") - .frame(maxWidth: .infinity) - .padding(.vertical, 12) - } - } - .buttonStyle(.bordered) - .disabled(isProcessing) - } - .padding(.horizontal) - .padding(.bottom, 8) - } - .presentationDetents([.medium, .large]) - .onAppear { quantity = part.quantity } - .alert("알림", isPresented: $showAlert) { - Button("확인") {} - } message: { Text(alertMessage) } - } - - private func handleUseNow() async { - await MainActor.run { isProcessing = true } - - // ✅ 현재 부품이 아직 store에 없으면 추가 - if !partStore.parts.contains(where: { $0.id == part.id }) { - var newPart = part - newPart.quantity = quantity - partStore.addPart(newPart) - } else { - partStore.updateQuantity(for: part.id, to: quantity) - } - - let items = partStore.parts.map { ReleaseItemRequest(partId: $0.id, quantity: $0.quantity) } - let result = await partViewModel.releaseParts(items: items) - - await MainActor.run { - isProcessing = false - switch result { - case .success(let msg): - alertMessage = msg - showAlert = true - partStore.clear() - onUseParts() - dismiss() - case .failure(let err): - alertMessage = err.message - showAlert = true - } - } - } -} - -//import SwiftUI -// -//struct PartBottomSheetView: View { -// @EnvironmentObject var partStore: PartStore -// @Environment(\.dismiss) private var dismiss -// @StateObject private var partViewModel = PartViewModel() -// -// let part: PartDetail -// let onAddPart: () -> Void -// let onUseParts: () -> Void -// -// @State private var quantity: Int = 1 -// @State private var isProcessing = false -// @State private var showAlert = false -// @State private var alertMessage = "" -// -// var body: some View { -// VStack(spacing: 16) { -// -// // MARK: - 방금 스캔한 부품 미리보기 (추가 전) -// HStack(spacing: 12) { -// AsyncImage(url: URL(string: part.image)) { phase in -// switch phase { -// case .success(let img): -// img.resizable().scaledToFill() -// default: -// Color.gray.opacity(0.3) -// } -// } -// .frame(width: 48, height: 48) -// .clipped() -// .cornerRadius(6) -// -// VStack(alignment: .leading, spacing: 4) { -// Text(part.korName) -// .font(.subheadline) -// Text("\(part.model) / \(part.trim)") -// .font(.caption) -// .foregroundColor(.gray) -// Text("\(part.price) 원") -// .font(.subheadline) -// } -// -// Spacer() -// -// // ✅ 수정된 수량 조절 구역 -// HStack(spacing: 8) { -// Button(action: { -// if quantity > 1 { quantity -= 1 } -// }) { -// Image(systemName: "minus.circle.fill") -// .font(.title2) -// .foregroundColor(.blue) -// } -// -// Text("\(quantity)") -// .font(.body) -// .frame(width: 44, alignment: .center) -// -// Button(action: { -// quantity += 1 -// }) { -// Image(systemName: "plus.circle.fill") -// .font(.title2) -// .foregroundColor(.blue) -// } -// -// Spacer() -// } -// .padding(.horizontal) -// } -// .padding(.horizontal) -// .padding() -// -// -// -// List { -// ForEach(partStore.parts.indices, id: \.self) { idx in -// let p = partStore.parts[idx] -// HStack { -// AsyncImage(url: URL(string: p.image)) { phase in -// switch phase { -// case .success(let img): -// img.resizable().scaledToFill() -// default: -// Color.gray.opacity(0.3) -// } -// } -// .frame(width: 48, height: 48) -// .clipped() -// .cornerRadius(6) -// -// VStack(alignment: .leading, spacing: 4) { -// Text(p.korName) -// .font(.subheadline) -// Text("\(p.model) / \(p.trim)") -// .font(.caption) -// .foregroundColor(.gray) -// Text("\(p.price) 원") -// .font(.subheadline) -// } -// -// Spacer() -// -// // ✅ 명확한 수량 표시 + +/- 버튼 (Stepper 대신 커스텀 컨트롤) -// HStack(spacing: 8) { -// Button { -// let newQty = max(1, p.quantity - 1) -// partStore.updateQuantity(for: p.id, to: newQty) -// } label: { -// Image(systemName: "minus.circle") -// .font(.title2) -// } -// -// Text("\(p.quantity)") -// .font(.body) -// .frame(width: 44, alignment: .center) -// -// Button { -// let newQty = p.quantity + 1 -// partStore.updateQuantity(for: p.id, to: newQty) -// } label: { -// Image(systemName: "plus.circle") -// .font(.title2) -// } -// } -// .buttonStyle(.plain) -// .foregroundColor(.blue) -// } -// .padding() -// } -// .onDelete { idxs in -// idxs.forEach { i in -// let id = partStore.parts[i].id -// partStore.removePart(id) -// } -// } -// } -// .frame(maxHeight: 240) -// .listStyle(.plain) -//// List { -//// ForEach(partStore.parts) { p in -//// HStack { -//// AsyncImage(url: URL(string: p.image)) { phase in -//// switch phase { -//// case .success(let img): -//// img.resizable().scaledToFill() -//// default: -//// Color.gray.opacity(0.3) -//// } -//// } -//// .frame(width: 48, height: 48) -//// .clipped() -//// .cornerRadius(6) -//// -//// VStack(alignment: .leading, spacing: 4) { -//// Text(p.korName) -//// .font(.subheadline) -//// Text("\(p.model) / \(p.trim)") -//// .font(.caption) -//// .foregroundColor(.gray) -//// } -//// -//// Spacer() -//// -//// Stepper("", value: Binding( -//// get: { p.quantity }, -//// set: { newVal in partStore.updateQuantity(for: p.id, to: newVal) } -//// ), in: 1...999) -//// .labelsHidden() -//// } -//// .padding(.vertical, 6) -//// } -//// .onDelete { idxs in -//// idxs.forEach { i in -//// let id = partStore.parts[i].id -//// partStore.removePart(id) -//// } -//// } -//// } -//// .frame(maxHeight: 240) -//// .listStyle(.plain) -// -// Spacer() -// -// // — 하단 버튼 — -// HStack(spacing: 12) { -// Button { -// var newPart = part -// newPart.quantity = quantity -// partStore.addPart(newPart) -// onAddPart() -// dismiss() -// } label: { -// Text("부품 추가") -// .frame(maxWidth: .infinity) -// .padding(.vertical, 12) -// } -// .buttonStyle(.borderedProminent) -// -// Button { -// Task { await handleUseNow() } -// } label: { -// if isProcessing { -// ProgressView() -// .frame(maxWidth: .infinity) -// .padding(.vertical, 12) -// } else { -// Text("사용 처리") -// .frame(maxWidth: .infinity) -// .padding(.vertical, 12) -// } -// } -// .buttonStyle(.bordered) -// .disabled(isProcessing) -// } -// .padding(.horizontal) -// .padding(.bottom, 8) -// } -// .presentationDetents([.medium, .large]) -// .onAppear { -// quantity = part.quantity -// } -// .alert("알림", isPresented: $showAlert) { -// Button("확인") {} -// } message: { -// Text(alertMessage) -// } -// } -// -// private func handleUseNow() async { -// await MainActor.run { isProcessing = true } -// -// if !partStore.parts.contains(where: { $0.id == part.id }) { -// var newPart = part -// newPart.quantity = quantity -// partStore.addPart(newPart) -// } else { -// partStore.updateQuantity(for: part.id, to: quantity) -// } -// -// let items = partStore.parts.map { ReleaseItemRequest(partId: $0.id, quantity: $0.quantity) } -// let result = await partViewModel.releaseParts(items: items) -// -// await MainActor.run { -// isProcessing = false -// switch result { -// case .success(let msg): -// alertMessage = msg -// showAlert = true -// partStore.clear() -// onUseParts() -// dismiss() -// case .failure(let err): -// alertMessage = err.message -// showAlert = true -// } -// } -// } -//} -//struct PartBottomSheetView: View { -// @EnvironmentObject var partStore: PartStore -// @Environment(\.dismiss) private var dismiss -// @StateObject private var partViewModel = PartViewModel() -// -// // 네가 원했던 시그니처: 반드시 이대로 호출 가능함 -// let part: PartDetail // 방금 스캔한 파트 (OutgoingScanView에서 전달) -// let onAddPart: () -> Void // "부품 추가" 눌렀을 때 호출 (OutgoingScanView에서 resetScanState 등 처리) -// let onUseParts: () -> Void // "사용 처리" 눌렀을 때 호출(옵션적 추가 동작) -// -// @State private var quantity: Int = 1 -// @State private var isProcessing = false -// @State private var showAlert = false -// @State private var alertMessage = "" -// -// var body: some View { -// VStack(spacing: 12) { -// // — 단일(방금 스캔한) 파트 미리보기 — -// HStack(spacing: 12) { -// AsyncImage(url: URL(string: part.image)) { phase in -// switch phase { -// case .success(let img): -// img.resizable().scaledToFill() -// default: -// Color.gray.opacity(0.3) -// } -// } -// .frame(width: 84, height: 84) -// .clipped() -// .cornerRadius(8) -// -// VStack(alignment: .leading, spacing: 6) { -// Text(part.korName) -// .font(.headline) -// Text("\(part.model) / \(part.trim)") -// .font(.subheadline) -// .foregroundColor(.gray) -// Text("₩\(part.price)") -// .font(.subheadline) -// } -// -// Spacer() -// } -// .padding(.horizontal) -// -// // 수량 조절 (바로 이 시트에서 조정한 수량이 추가/전송에 반영됨) -// Stepper("수량: \(quantity)", value: $quantity, in: 1...999) -// .padding(.horizontal) -// -// Divider() -// .padding(.vertical, 6) -// -// // — 누적된 파트 리스트 (PartStore) — -// Text("추가된 부품 목록") -// .font(.subheadline) -// .bold() -// .padding(.horizontal) -// -// List { -// ForEach(partStore.parts) { p in -// HStack { -// AsyncImage(url: URL(string: p.image)) { phase in -// switch phase { -// case .success(let img): -// img.resizable().scaledToFill() -// default: -// Color.gray.opacity(0.3) -// } -// } -// .frame(width: 48, height: 48) -// .clipped() -// .cornerRadius(6) -// -// VStack(alignment: .leading, spacing: 4) { -// Text(p.korName) -// .font(.subheadline) -// Text("\(p.model) / \(p.trim)") -// .font(.caption) -// .foregroundColor(.gray) -// } -// -// Spacer() -// -// // stepper 바인딩은 store 업데이트로 연결 -// Stepper("", value: Binding( -// get: { p.quantity }, -// set: { newVal in partStore.updateQuantity(for: p.id, to: newVal) } -// ), in: 1...999) -// .labelsHidden() -// } -// .padding(.vertical, 6) -// } -// .onDelete { idxs in -// idxs.forEach { i in -// let id = partStore.parts[i].id -// partStore.removePart(id) -// } -// } -// } -// .frame(maxHeight: 240) -// .listStyle(.plain) -// -// Spacer() -// -// // 하단 버튼 -// HStack(spacing: 12) { -// // 1) 부품 추가: 방금 스캔한 파트를 (현재 quantity로) partStore에 추가하고 -// // 호출자에게 알려줌 (예: resetScanState) -// Button { -// var newPart = part -// newPart.quantity = quantity -// partStore.addPart(newPart) -// -// // 호출자 (OutgoingScanView)에게 알림 — 여기서 스캔 상태 초기화 등 처리 -// onAddPart() -// // 시트 닫기 -// dismiss() -// } label: { -// Text("부품 추가") -// .frame(maxWidth: .infinity) -// .padding(.vertical, 12) -// } -// .buttonStyle(.borderedProminent) -// -// // 2) 사용 처리: PartStore 전체를 서버로 보내는 흐름 (뷰모델 사용) -// Button { -// Task { -// await handleUseNow() -// } -// } label: { -// if isProcessing { -// ProgressView() -// .frame(maxWidth: .infinity) -// .padding(.vertical, 12) -// } else { -// Text("사용 처리") -// .frame(maxWidth: .infinity) -// .padding(.vertical, 12) -// } -// } -// .buttonStyle(.bordered) -// .disabled(isProcessing) -// } -// .padding(.horizontal) -// .padding(.bottom, 8) -// } -// .presentationDetents([.medium, .large]) -// .onAppear { -// quantity = part.quantity -// } -// .alert("알림", isPresented: $showAlert) { -// Button("확인") {} -// } message: { -// Text(alertMessage) -// } -// } -// -// // 사용 처리 로직 (뷰모델에 구현된 releaseParts 사용) -// private func handleUseNow() async { -// await MainActor.run { isProcessing = true } -// -// // 현재 시트에서 조정한 수량이 partStore에 반영되어야 함: -// if !partStore.parts.contains(where: { $0.id == part.id }) { -// var newPart = part -// newPart.quantity = quantity -// partStore.addPart(newPart) -// } else { -// partStore.updateQuantity(for: part.id, to: quantity) -// } -// -// let items = partStore.parts.map { ReleaseItemRequest(partId: $0.id, quantity: $0.quantity) } -// let result = await partViewModel.releaseParts(items: items) -// -// await MainActor.run { -// isProcessing = false -// switch result { -// case .success(let msg): -// alertMessage = msg -// showAlert = true -// // 성공 시 리스트 비우기 -// partStore.clear() -// // 호출자도 알림을 원하면 onUseParts 호출 -// onUseParts() -// dismiss() -// case .failure(let err): -// alertMessage = err.message -// showAlert = true -// } -// } -// } -//} - -//import SwiftUI -// -//struct PartBottomSheetView: View { -// @EnvironmentObject var partStore: PartStore -// @Environment(\.dismiss) private var dismiss -// @StateObject private var partViewModel = PartViewModel() -// -// var body: some View { -// VStack(spacing: 16) { -// Text("추가된 부품 목록") -// .font(.headline) -// .padding(.top, 8) -// -// List { -// ForEach(partStore.parts) { part in -// VStack(alignment: .leading, spacing: 4) { -// HStack { -// AsyncImage(url: URL(string: part.image)) { image in -// image.resizable().scaledToFit() -// } placeholder: { -// Color.gray -// } -// .frame(width: 60, height: 60) -// .cornerRadius(8) -// -// VStack(alignment: .leading) { -// Text(part.korName).font(.headline) -// Text("\(part.model) / \(part.trim)") -// .font(.subheadline) -// .foregroundColor(.gray) -// } -// -// Spacer() -// -// Stepper("", value: Binding( -// get: { part.quantity }, -// set: { newValue in -// partStore.updateQuantity(for: part.id, to: newValue) -// } -// ), in: 1...100) -// .labelsHidden() -// } -// -// Text("₩\(part.price)") -// .font(.subheadline) -// .foregroundColor(.secondary) -// } -// .padding(.vertical, 4) -// } -// .onDelete { indexSet in -// indexSet.forEach { idx in -// partStore.removePart(partStore.parts[idx].id) -// } -// } -// } -// -// HStack { -// Button("부품 추가") { -// dismiss() // 다시 QR 스캔 화면으로 복귀 -// } -// .buttonStyle(.borderedProminent) -// -// Button("사용 처리") { -// Task { -// let payload = partStore.parts.map { -// ReleaseItemRequest(partId: $0.id, quantity: $0.quantity) -// } -// let result = await partViewModel.releaseParts(items: payload) -// switch result { -// case .success(let msg): -// print("✅ 사용처리 성공:", msg) -// partStore.clear() -// dismiss() -// case .failure(let err): -// print("❌ 실패:", err.message) -// } -// } -// } -// .buttonStyle(.bordered) -// } -// .padding() -// } -// .presentationDetents([.medium, .large]) -// } -//} - -//import SwiftUI -// -//struct PartBottomSheetView: View { -// @EnvironmentObject var partStore: PartStore -// @Environment(\.dismiss) private var dismiss -// -// // part: 지금 방금 API로 받아온 파트 (아직 PartStore에 추가되지 않을 수 있음) -// let part: PartDetail -// -// @State private var quantity = 1 -// @State private var isProcessingUse = false -// @State private var showErrorAlert = false -// @State private var errorMessage = "" -// -// var body: some View { -// VStack(spacing: 12) { -// AsyncImage(url: URL(string: part.image)) { phase in -// switch phase { -// case .success(let image): -// image.resizable().scaledToFit() -// default: -// Color.gray -// } -// } -// .frame(height: 150) -// -// Text(part.korName) -// .font(.headline) -// Text("\(part.model) / \(part.trim)") -// .font(.subheadline) -// Text("₩\(part.price)") -// .font(.title3) -// .bold() -// -// Stepper("수량: \(quantity)", value: $quantity, in: 1...100) -// .padding(.vertical) -// -// HStack(spacing: 12) { -// // 1) 부품 추가: 현재 파트를 PartStore에 추가한 뒤 닫아서 QR 스캔 화면으로 돌아감 -// Button(action: { -// addCurrentPartToStoreAndReturnToScanner() -// }) { -// Text("부품 추가") -// .frame(maxWidth: .infinity) -// } -// .buttonStyle(.bordered) -// -// // 2) 사용 처리: PartStore의 항목들(및 현재 파트가 미추가 상태면 그것도 포함)을 서버로 전송 -// Button(action: { -// Task { -// await handleUseNow() -// } -// }) { -// if isProcessingUse { -// ProgressView() -// .frame(maxWidth: .infinity) -// } else { -// Text("사용 처리") -// .frame(maxWidth: .infinity) -// } -// } -// .buttonStyle(.borderedProminent) -// .disabled(isProcessingUse) -// } -// } -// .padding() -// .alert("오류", isPresented: $showErrorAlert) { -// Button("확인", role: .cancel) { } -// } message: { -// Text(errorMessage) -// } -// .onAppear { -// // 기본 수량을 part의 quantity가 이미 설정되어 있으면 그걸로. (일반적으로 1) -// quantity = part.quantity -// } -// } -// -// // MARK: - Helpers -// -// // 부품 추가 누르면 현재 파트를 PartStore에 넣고 바텀시트 닫음 (QR 스캔 화면으로 돌아감) -// private func addCurrentPartToStoreAndReturnToScanner() { -// var newPart = part -// newPart.quantity = quantity -// partStore.addPart(newPart) -// dismiss() // QR 화면으로 돌아감 (QR 화면이 스택에 남아있다면 바로 보일 것) -// } -// -// // 사용 처리: 현재 파트를 포함해 전부 전송 -// private func handleUseNow() async { -// isProcessingUse = true -// -// // 현재 part가 이미 store에 있는지 확인 -// if !partStore.parts.contains(where: { $0.id == part.id }) { -// // 자동으로 포함시켜서 누락 방지 -// var newPart = part -// newPart.quantity = quantity -// partStore.addPart(newPart) -// } else { -// // 이미 있으면 사용자가 바텀시트에서 조정한 수량을 반영 -// partStore.updateQuantity(for: part.id, to: quantity) -// } -// -// // payload 생성 -// let payload = partStore.makeRequestPayload() -// // ReleaseItemRequest로 변환 (예시) -// let items = partStore.parts.map { ReleaseItemRequest(partId: $0.id, quantity: $0.quantity) } -// -// // 실제로는 네가 사용 중인 ViewModel/Repository를 호출하는 게 좋음 (여기서는 간단 시도) -// let repository = PartRepositoryImpl() -// let result = await repository.releaseParts(items: items) -// -// await MainActor.run { -// isProcessingUse = false -// switch result { -// case .success(let apiResponse): -// // 성공 메시지 처리 — 필요하면 화면 닫고 partStore.clear() 호출 -// // 예: 사용 처리가 성공하면 임시 저장 리스트 비우기 -// partStore.parts.removeAll() -// dismiss() -// case .failure(let appError): -// // 실패 시 메시지 보여주기 -// errorMessage = appError.message -// showErrorAlert = true -// } -// } -// } -//} diff --git a/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift b/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift new file mode 100644 index 0000000..f81f320 --- /dev/null +++ b/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift @@ -0,0 +1,122 @@ +// +// UsedPartListSheetView.swift +// StockMate +// +// Created by Admin on 11/4/25. +// + + +// +// UsedPartListSheetView.swift +// StockMate +// +// Created by Admin on 11/4/25. +// + +import SwiftUI + +struct UsedPartListSheetView: View { + @EnvironmentObject var partStore: PartStore + var onUseParts: (() -> Void)? // ‘사용 처리’ 버튼 액션 콜백 + var onRescan: (() -> Void)? // ✅ 다시 스캔 콜백 추가 + + var body: some View { + NavigationStack { + VStack { + if partStore.parts.isEmpty { + VStack(spacing: 12) { + Image(systemName: "cube.box") + .resizable() + .scaledToFit() + .frame(width: 60, height: 60) + .foregroundColor(.gray.opacity(0.6)) + Text("추가된 부품이 없습니다.") + .foregroundColor(.gray) + } + .padding(.top, 100) + } else { + ScrollView { + LazyVStack { + ForEach($partStore.parts) { $part in // ✅ 바인딩으로 변경 ($ 붙임) + HStack(spacing: 12) { + AsyncImage(url: URL(string: part.image)) { image in + image.resizable().scaledToFill() + } placeholder: { + Color.gray.opacity(0.2) + } + .frame(width: 60, height: 60) + .clipShape(RoundedRectangle(cornerRadius: 8)) + + VStack(alignment: .leading, spacing: 4) { + Text(part.korName) + .font(.headline) + Text(part.model) + .font(.subheadline) + .foregroundColor(.secondary) + Text(part.categoryName) + .font(.caption) + .foregroundColor(.gray) + } + + Spacer() + + // ✅ 수량 조절 버튼 + HStack { + Button("-") { if part.quantity > 1 { part.quantity -= 1 } } + Text("\(part.quantity)") + Button("+") { part.quantity += 1 } + } + .padding(.trailing, 4) + } + .padding(.vertical, 4) + } + .onDelete { indexSet in + partStore.parts.remove(atOffsets: indexSet) + } + } + .listStyle(.plain) + } + } + + // 🔹 다시 스캔 버튼 + Button { + onRescan?() + } label: { + Text("다시 스캔") + .font(.headline) + .foregroundColor(.blue) + .frame(maxWidth: .infinity) + .padding() + .background(Color.blue.opacity(0.1)) + .cornerRadius(10) + } + .padding(.bottom, 16) + + // ✅ 사용 처리 버튼 + Button { + onUseParts?() + } label: { + Text("사용 처리") + .font(.headline) + .foregroundColor(.white) + .frame(maxWidth: .infinity) + .padding() + .background(partStore.parts.isEmpty ? Color.gray : Color.blue) + .cornerRadius(10) + .padding(.horizontal) + } + .disabled(partStore.parts.isEmpty) + .padding(.bottom, 16) + } + .navigationTitle("사용할 부품 목록") + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + Button("전체삭제") { + partStore.clear() + } + .foregroundColor(.red) + } + } + } + } +} diff --git a/StockMate/StockMate/app/feature/parts/data/PartApi.swift b/StockMate/StockMate/app/feature/parts/data/PartApi.swift index be73d35..e1d4050 100644 --- a/StockMate/StockMate/app/feature/parts/data/PartApi.swift +++ b/StockMate/StockMate/app/feature/parts/data/PartApi.swift @@ -33,7 +33,7 @@ struct PartDetailResponse: Decodable, Identifiable { } // 사용처리 임시 값 -struct PartDetail: Identifiable, Equatable { +struct PartDetail: Identifiable { let id: Int let price: Int let image: String diff --git a/StockMate/StockMate/app/feature/parts/data/PartStore.swift b/StockMate/StockMate/app/feature/parts/data/PartStore.swift index 5d31d76..b769ce7 100644 --- a/StockMate/StockMate/app/feature/parts/data/PartStore.swift +++ b/StockMate/StockMate/app/feature/parts/data/PartStore.swift @@ -10,35 +10,34 @@ import Foundation @MainActor final class PartStore: ObservableObject { @Published var parts: [PartDetail] = [] - + func addPart(_ part: PartDetail) { if let index = parts.firstIndex(where: { $0.id == part.id }) { - // 이미 있으면 수량만 1 증가 -// parts[index].quantity += 1 + parts[index].quantity += 1 // 이미 존재하면 수량만 +1 } else { - // ✅ 반드시 새로운 복사본 append var newPart = part newPart.quantity = 1 parts.append(newPart) -// parts.append(part) - } - } - - func updateQuantity(for partId: Int, to quantity: Int) { - if let index = parts.firstIndex(where: { $0.id == partId }) { - parts[index].quantity = quantity } } - func removePart(_ partId: Int) { - parts.removeAll { $0.id == partId } + func clear() { + parts.removeAll() } - - func makeRequestPayload() -> [[String: Any]] { - parts.map { ["partId": $0.id, "quantity": $0.quantity] } + + // ✅ 수량 변경용 메서드 추가 + func increaseQuantity(for part: PartDetail) { + if let index = parts.firstIndex(where: { $0.id == part.id }) { + parts[index].quantity += 1 + objectWillChange.send() // 수동 갱신 트리거 + } } - func clear() { - parts.removeAll() + func decreaseQuantity(for part: PartDetail) { + if let index = parts.firstIndex(where: { $0.id == part.id }), + parts[index].quantity > 1 { + parts[index].quantity -= 1 + objectWillChange.send() + } } } diff --git a/StockMate/StockMate/app/feature/parts/ui/QRScannerView.swift b/StockMate/StockMate/app/feature/parts/ui/QRScannerView.swift index c8a6a30..c4a109a 100644 --- a/StockMate/StockMate/app/feature/parts/ui/QRScannerView.swift +++ b/StockMate/StockMate/app/feature/parts/ui/QRScannerView.swift @@ -9,6 +9,7 @@ import SwiftUI struct QRScannerView: UIViewControllerRepresentable { @Binding var scannedCode: String? + var isActive: Bool = true // ✅ 추가: 카메라 활성화 상태 func makeUIViewController(context: Context) -> QRScannerViewController { let controller = QRScannerViewController() @@ -17,12 +18,20 @@ struct QRScannerView: UIViewControllerRepresentable { } // func updateUIViewController(_ uiViewController: QRScannerViewController, context: Context) {} +// func updateUIViewController(_ uiViewController: QRScannerViewController, context: Context) { +// // ✅ scannedCode가 nil이면 다시 스캔 시작 +// if scannedCode == nil { +// uiViewController.startScanning() +// } +// } + func updateUIViewController(_ uiViewController: QRScannerViewController, context: Context) { - // ✅ scannedCode가 nil이면 다시 스캔 시작 - if scannedCode == nil { - uiViewController.startScanning() + if isActive { + uiViewController.startSession() // ✅ 바텀시트 닫혔을 때 다시 스캔 시작 + } else { + uiViewController.stopSession() // ✅ 바텀시트 열렸을 때 스캔 일시정지 + } } - } func makeCoordinator() -> Coordinator { Coordinator(self) diff --git a/StockMate/StockMate/app/feature/parts/ui/QRScannerViewController.swift b/StockMate/StockMate/app/feature/parts/ui/QRScannerViewController.swift index e36d7b4..b3d6321 100644 --- a/StockMate/StockMate/app/feature/parts/ui/QRScannerViewController.swift +++ b/StockMate/StockMate/app/feature/parts/ui/QRScannerViewController.swift @@ -76,6 +76,19 @@ final class QRScannerViewController: UIViewController, AVCaptureMetadataOutputOb } } + func startSession() { + if !captureSession.isRunning { + captureSession.startRunning() + } + } + + func stopSession() { + if captureSession.isRunning { + captureSession.stopRunning() + } + } + + // ✅ QR 감지 시 호출 func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) { if let metadataObject = metadataObjects.first as? AVMetadataMachineReadableCodeObject, From 2d5a3daaef7fd4f1f07e76616a80c6c8879c9db7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Tue, 4 Nov 2025 16:15:48 +0900 Subject: [PATCH 53/85] =?UTF-8?q?[FEAT]=20=EB=B6=80=ED=92=88=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=B2=98=EB=A6=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../inventory/ui/OutgoingScanView.swift | 2 + .../inventory/ui/UsedPartListSheetView.swift | 334 +++++++++++++----- .../app/feature/orders/ui/OrderInfoView.swift | 2 +- 3 files changed, 257 insertions(+), 81 deletions(-) diff --git a/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift b/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift index a4d1105..25d8f04 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift @@ -131,6 +131,8 @@ struct OutgoingScanView: View { resetScanState() // QR 다시 활성화 } ) + .presentationDetents([.fraction(0.80)]) // 시트 높이 80% + .presentationCornerRadius(28) // ✅ 모서리 곡률 .environmentObject(partStore) } .navigationTitle("부품 사용 처리") diff --git a/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift b/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift index f81f320..ca55bd2 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift @@ -21,102 +21,276 @@ struct UsedPartListSheetView: View { var onRescan: (() -> Void)? // ✅ 다시 스캔 콜백 추가 var body: some View { - NavigationStack { - VStack { - if partStore.parts.isEmpty { - VStack(spacing: 12) { - Image(systemName: "cube.box") - .resizable() - .scaledToFit() - .frame(width: 60, height: 60) - .foregroundColor(.gray.opacity(0.6)) - Text("추가된 부품이 없습니다.") - .foregroundColor(.gray) +// NavigationStack { + VStack (alignment: .center){ + // ✅ 상단 헤더 + ZStack { + Text("사용할 부품") + .font(.system(size: 18, weight: .bold)) + .foregroundColor(.black) + + HStack { + Spacer() + Button("전체 삭제") { + partStore.clear() + } + .font(.system(size: 14, weight: .medium)) + .foregroundColor(.red) + .padding(.trailing, 20) } - .padding(.top, 100) - } else { - ScrollView { + } + .padding(.vertical, 26) + .background(Color.white) + + + // ✅ 내용 영역 + ScrollView { + if partStore.parts.isEmpty { + // 부품 목록 전체 삭제의 경우 + VStack(spacing: 12) { + Image(systemName: "cube.box") + .resizable() + .scaledToFit() + .frame(width: 60, height: 60) + .foregroundColor(.gray.opacity(0.6)) + Text("추가된 부품이 없습니다.") + .foregroundColor(.gray) + } + .frame(maxWidth: .infinity, minHeight: 200) + } else { LazyVStack { - ForEach($partStore.parts) { $part in // ✅ 바인딩으로 변경 ($ 붙임) - HStack(spacing: 12) { - AsyncImage(url: URL(string: part.image)) { image in - image.resizable().scaledToFill() - } placeholder: { - Color.gray.opacity(0.2) + ForEach( + $partStore.parts + ) { $part in // ✅ 바인딩으로 변경 ($ 붙임) + VStack(alignment: .leading, spacing: 6) { + Text(part.categoryName) + .font( + .system(size: 12, weight: .semibold) + ) + .foregroundColor(.black) + + Divider() + .frame(height: 0.2) + .background(Color.textGray2) + + HStack(alignment: .center, spacing: 12) { + AsyncImage( + url: URL(string: part.image) + ) { image in + image.resizable().scaledToFill() + } placeholder: { + Color.gray.opacity(0.2) + } + .frame(width: 64, height: 64) + .clipShape( + RoundedRectangle(cornerRadius: 10) + ) + + VStack( + alignment: .leading, + spacing: 6 + ) { + Text(part.korName) + .font( + .system( + size: 13, + weight: .bold + ) + ) + .foregroundColor(.black) + .lineLimit(2) + Text( + "\((part.trim)) / \((part.model))" + ) + .font(.system(size: 12)) + .foregroundColor(.black) + .lineLimit(1) + Text("\(part.price)원") + .font( + .system( + size: 15, + weight: .semibold + ) + ) + .foregroundColor(.black) + } + + Spacer() + + // ✅ 수량 조절 버튼 (디자인 개선) + HStack(spacing: 10) { + Button { + if part.quantity > 1 { + part.quantity -= 1 + } + } label: { + Image(systemName: "minus") + .font( + .system( + size: 14, + weight: .regular + ) + ) + .frame( + width: 13, + height: 13 + ) + .foregroundColor(.black) + } + + Text("\(part.quantity)") + .font( + .system( + size: 14, + weight: .medium + ) + ) + .frame(width: 19) + + Button { + part.quantity += 1 + } label: { + Image(systemName: "plus") + .font( + .system( + size: 14, + weight: .regular + ) + ) + .frame( + width: 13, + height: 13 + ) + .foregroundColor(.black) + } + } + .padding(.vertical, 6) + .padding( + .horizontal, + 8 + ) // 🔹 살짝 늘려서 버튼 안이 넓어 보이게 + .background(Color.white) + .cornerRadius(10) + .overlay( + // ✅ 테두리 추가 + RoundedRectangle(cornerRadius: 10) + .stroke( + Color.LightBlue03, + lineWidth: 1 + ) + ) + .shadow( + color: .black.opacity(0.15), + radius: 4, + x: 0, + y: 4 + ) } - .frame(width: 60, height: 60) - .clipShape(RoundedRectangle(cornerRadius: 8)) - - VStack(alignment: .leading, spacing: 4) { - Text(part.korName) - .font(.headline) - Text(part.model) - .font(.subheadline) - .foregroundColor(.secondary) - Text(part.categoryName) - .font(.caption) - .foregroundColor(.gray) - } - - Spacer() - - // ✅ 수량 조절 버튼 - HStack { - Button("-") { if part.quantity > 1 { part.quantity -= 1 } } - Text("\(part.quantity)") - Button("+") { part.quantity += 1 } - } - .padding(.trailing, 4) } - .padding(.vertical, 4) + .padding() + .background(Color.white) + .cornerRadius(14) + .shadow( + color: .black.opacity(0.05), + radius: 4, + x: 0, + y: 4 + ) + } .onDelete { indexSet in partStore.parts.remove(atOffsets: indexSet) } } .listStyle(.plain) + } } - - // 🔹 다시 스캔 버튼 - Button { - onRescan?() - } label: { - Text("다시 스캔") - .font(.headline) - .foregroundColor(.blue) - .frame(maxWidth: .infinity) - .padding() - .background(Color.blue.opacity(0.1)) - .cornerRadius(10) - } - .padding(.bottom, 16) + .padding(7) + .frame(maxHeight: .infinity) + + HStack{ + // 🔹 다시 스캔 버튼 + Button { + onRescan?() + } label: { + Text("부품 추가") + .font(.headline) + .foregroundColor(.Primary) + .frame(maxWidth: .infinity) + .padding() + .background(Color.white) + .cornerRadius(9999) + .background( + RoundedRectangle(cornerRadius: 9999) + .stroke(Color.Primary, lineWidth: 2) + ) + } + .padding(.bottom, 16) - // ✅ 사용 처리 버튼 - Button { - onUseParts?() - } label: { - Text("사용 처리") - .font(.headline) - .foregroundColor(.white) - .frame(maxWidth: .infinity) - .padding() - .background(partStore.parts.isEmpty ? Color.gray : Color.blue) - .cornerRadius(10) - .padding(.horizontal) - } - .disabled(partStore.parts.isEmpty) - .padding(.bottom, 16) - } - .navigationTitle("사용할 부품 목록") - .toolbar { - ToolbarItem(placement: .navigationBarTrailing) { - Button("전체삭제") { - partStore.clear() + // ✅ 사용 처리 버튼 + Button { + onUseParts?() + } label: { + Text("사용 처리") + .font(.headline) + .foregroundColor(.white) + .frame(maxWidth: .infinity) + .padding() + .background( + partStore.parts.isEmpty ? Color.gray : Color.Primary + ) + .cornerRadius(9999) } - .foregroundColor(.red) + .disabled(partStore.parts.isEmpty) + .padding(.bottom, 16) } + .padding(.horizontal) + } + } +} + +// MARK: - 프리뷰 (안전 버전) + +@MainActor +struct UsedPartListSheetView_Previews: PreviewProvider { + static var previewStore: PartStore = { + let store = PartStore() + store.parts = [ + PartDetail( + id: 1, + price: 10000, + image: "https://via.placeholder.com/150", + trim: "준준형/소형", + model: "아반떼MD", + korName: "액츄에이터 - 템퍼러처 도어", + categoryName: "전기/램프", + quantity: 2 + ), + PartDetail( + id: 2, + price: 25000, + image: "https://via.placeholder.com/150", + trim: "준준형/소형", + model: "아반떼 MD", + korName: "스위치 어셈블리 - 도어", + categoryName: "전기/램프", + quantity: 1 + ) + ] + return store + }() + + static var previews: some View { + NavigationStack { + UsedPartListSheetView( + onUseParts: { print("✅ 사용 처리 버튼 눌림") }, + onRescan: { print("🔄 다시 스캔 버튼 눌림") } + ) + .environmentObject(previewStore) } } } + + diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift index f32ac74..430e3b3 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift @@ -91,7 +91,7 @@ struct OrderInfoView: View { // ✅ 충전 bottom sheet 연결 .sheet(isPresented: $depositViewModel.showChargeSheet) { DepositChargeView(viewModel: depositViewModel) - .presentationDetents([.fraction(0.80)]) // 시트 높이 85% + .presentationDetents([.fraction(0.80)]) // 시트 높이 80% // .presentationDragIndicator(.visible) } From 9e1805dbb621b091b1aecc31fadb7d680d0cfc03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Tue, 4 Nov 2025 16:50:44 +0900 Subject: [PATCH 54/85] =?UTF-8?q?[REFAC]=20=EC=9E=85=EC=B6=9C=EA=B3=A0=20?= =?UTF-8?q?=ED=9E=88=EC=8A=A4=ED=86=A0=EB=A6=AC=20=EC=8B=9C=EA=B0=84?= =?UTF-8?q?=EB=8C=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/dashboard/data/HistoryApi.swift | 9 -- .../dashboard/ui/ReleaseDetailView.swift | 27 ++---- .../inventory/ui/UsedPartListSheetView.swift | 93 ++++--------------- 3 files changed, 22 insertions(+), 107 deletions(-) diff --git a/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift b/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift index 8882e6b..9c63204 100644 --- a/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift +++ b/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift @@ -78,15 +78,6 @@ struct PaymentTransactionPageData: Decodable { let first: Bool } -//struct PaymentTransactionItem: Decodable, Identifiable { -// var id: UUID { UUID() } // 서버에서 id 제공 안하므로 로컬에서 생성 -// let transactionType: String // "CHARGE" or "PAY" -// let transactionTime: String -// let totalAmount: Int -// let orderId: Int -// let balance: Int -//} - struct PaymentTransactionItem: Decodable, Identifiable { var id: UUID { UUID() } // 서버에서 id 제공 안하므로 로컬 생성 let transactionType: String // "CHARGE" or "PAY" diff --git a/StockMate/StockMate/app/feature/dashboard/ui/ReleaseDetailView.swift b/StockMate/StockMate/app/feature/dashboard/ui/ReleaseDetailView.swift index af44590..d1cd2ed 100644 --- a/StockMate/StockMate/app/feature/dashboard/ui/ReleaseDetailView.swift +++ b/StockMate/StockMate/app/feature/dashboard/ui/ReleaseDetailView.swift @@ -36,9 +36,6 @@ struct ReleaseDetailView: View { .navigationTitle("출고 상세") .navigationBarTitleDisplayMode(.inline) } -// func formatDate(_ iso: String) -> String { -// String(iso.prefix(10)).replacingOccurrences(of: "-", with: ".") -// } } struct ReleasePartCard: View { @@ -109,24 +106,21 @@ struct ReleasePartCard: View { )) } + func formattedDate3(_ timestamp: String) -> String { - // 가능한 입력 포맷들 (서버별 변형 대응) let inputFormats = [ "yyyy-MM-dd'T'HH:mm:ss.SSSSSS", "yyyy-MM-dd'T'HH:mm:ss.SSSSSSS", "yyyy-MM-dd'T'HH:mm:ss.SSS", - "yyyy-MM-dd'T'HH:mm:ss", - "yyyy-MM-dd'T'HH:mm:ssZ", - "yyyy-MM-dd'T'HH:mm:ss.SSSZ" + "yyyy-MM-dd'T'HH:mm:ss" ] let trimmed = timestamp.trimmingCharacters(in: .whitespacesAndNewlines) let parser = DateFormatter() parser.locale = Locale(identifier: "en_US_POSIX") - parser.timeZone = TimeZone(abbreviation: "UTC") + parser.timeZone = TimeZone(identifier: "Asia/Seoul") // ✅ 서버 시간 기준으로 맞춤 var date: Date? = nil - for format in inputFormats { parser.dateFormat = format if let parsed = parser.date(from: trimmed) { @@ -135,22 +129,13 @@ func formattedDate3(_ timestamp: String) -> String { } } - // ISO8601 fallback - if date == nil { - let iso = ISO8601DateFormatter() - iso.formatOptions = [.withInternetDateTime, .withFractionalSeconds] - date = iso.date(from: trimmed) ?? ISO8601DateFormatter().date(from: trimmed) - } - - guard let finalDate = date else { - return timestamp // 파싱 실패 시 원본 반환 - } + guard let finalDate = date else { return timestamp } + // 출력도 한국시간으로 let output = DateFormatter() output.locale = Locale(identifier: "ko_KR") - output.timeZone = TimeZone.current + output.timeZone = TimeZone(identifier: "Asia/Seoul") output.dateFormat = "yyyy.MM.dd HH:mm:ss" return output.string(from: finalDate) } - diff --git a/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift b/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift index ca55bd2..6b14cc2 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift @@ -21,7 +21,6 @@ struct UsedPartListSheetView: View { var onRescan: (() -> Void)? // ✅ 다시 스캔 콜백 추가 var body: some View { -// NavigationStack { VStack (alignment: .center){ // ✅ 상단 헤더 ZStack { @@ -31,9 +30,7 @@ struct UsedPartListSheetView: View { HStack { Spacer() - Button("전체 삭제") { - partStore.clear() - } + Button("전체 삭제") {partStore.clear()} .font(.system(size: 14, weight: .medium)) .foregroundColor(.red) .padding(.trailing, 20) @@ -64,9 +61,7 @@ struct UsedPartListSheetView: View { ) { $part in // ✅ 바인딩으로 변경 ($ 붙임) VStack(alignment: .leading, spacing: 6) { Text(part.categoryName) - .font( - .system(size: 12, weight: .semibold) - ) + .font(.system(size: 12, weight: .semibold)) .foregroundColor(.black) Divider() @@ -86,32 +81,17 @@ struct UsedPartListSheetView: View { RoundedRectangle(cornerRadius: 10) ) - VStack( - alignment: .leading, - spacing: 6 - ) { + VStack(alignment: .leading,spacing: 6) { Text(part.korName) - .font( - .system( - size: 13, - weight: .bold - ) - ) + .font( .system(size: 13, weight: .bold)) .foregroundColor(.black) .lineLimit(2) - Text( - "\((part.trim)) / \((part.model))" - ) + Text("\((part.trim)) / \((part.model))") .font(.system(size: 12)) .foregroundColor(.black) .lineLimit(1) Text("\(part.price)원") - .font( - .system( - size: 15, - weight: .semibold - ) - ) + .font(.system(size: 15, weight: .semibold)) .foregroundColor(.black) } @@ -125,77 +105,37 @@ struct UsedPartListSheetView: View { } } label: { Image(systemName: "minus") - .font( - .system( - size: 14, - weight: .regular - ) - ) - .frame( - width: 13, - height: 13 - ) + .font(.system(size: 14, weight: .regular)) + .frame(width: 13,height: 13) .foregroundColor(.black) } - Text("\(part.quantity)") - .font( - .system( - size: 14, - weight: .medium - ) - ) + .font(.system(size: 14,weight: .medium)) .frame(width: 19) - Button { part.quantity += 1 } label: { Image(systemName: "plus") - .font( - .system( - size: 14, - weight: .regular - ) - ) - .frame( - width: 13, - height: 13 - ) + .font(.system(size: 14, weight: .regular)) + .frame(width: 13,height: 13) .foregroundColor(.black) } } .padding(.vertical, 6) - .padding( - .horizontal, - 8 - ) // 🔹 살짝 늘려서 버튼 안이 넓어 보이게 + .padding(.horizontal, 8) .background(Color.white) .cornerRadius(10) - .overlay( - // ✅ 테두리 추가 + .overlay( // ✅ 테두리 추가 RoundedRectangle(cornerRadius: 10) - .stroke( - Color.LightBlue03, - lineWidth: 1 - ) - ) - .shadow( - color: .black.opacity(0.15), - radius: 4, - x: 0, - y: 4 + .stroke( Color.LightBlue03, lineWidth: 1) ) + .shadow(color: .black.opacity(0.15), radius: 4, x: 0, y: 4 ) } } .padding() .background(Color.white) .cornerRadius(14) - .shadow( - color: .black.opacity(0.05), - radius: 4, - x: 0, - y: 4 - ) + .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) } .onDelete { indexSet in @@ -252,7 +192,6 @@ struct UsedPartListSheetView: View { } // MARK: - 프리뷰 (안전 버전) - @MainActor struct UsedPartListSheetView_Previews: PreviewProvider { static var previewStore: PartStore = { From 1b8843cba40d15e12c1fdeb039b8143bd295a622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Tue, 4 Nov 2025 23:24:54 +0900 Subject: [PATCH 55/85] =?UTF-8?q?[REFAC]=20=EB=B2=84=ED=8A=BC=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=84=B0=EB=A7=81=20=EB=B0=8F=20=EC=8A=A4=EC=BC=88?= =?UTF-8?q?=EB=A0=88=ED=86=A4=20UI=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/core/components/AlertModal.swift | 140 ++++++++++++++++++ .../app/core/components/CartCard.swift | 58 +++++--- .../core/components/CustomButtonStyle.swift | 50 +++++++ .../components/OrderRequestCardView.swift | 108 +++++++++++--- .../app/feature/auth/ui/HomeView.swift | 113 ++++++++------ .../inventory/ui/UsedPartListSheetView.swift | 4 +- .../app/feature/orders/ui/OrderInfoView.swift | 98 ++++++++++-- .../app/feature/orders/ui/OrderView.swift | 14 +- .../SuccessIllust.imageset/Contents.json | 12 ++ .../SuccessIllust.imageset/SuccessIllust.svg | 14 ++ .../add_shopping_cart.imageset/Contents.json | 23 +++ .../add_shopping_cart@1x.png | Bin 0 -> 458 bytes .../add_shopping_cart@2x.png | Bin 0 -> 752 bytes .../add_shopping_cart@3x.png | Bin 0 -> 1036 bytes .../shoppingcart.imageset/Contents.json | 12 ++ .../shoppingcart.imageset/shoppingcart.svg | 8 + StockMate/StockMate/resources/Color.swift | 3 + 17 files changed, 555 insertions(+), 102 deletions(-) create mode 100644 StockMate/StockMate/app/core/components/AlertModal.swift create mode 100644 StockMate/StockMate/app/core/components/CustomButtonStyle.swift create mode 100644 StockMate/StockMate/resources/Assets.xcassets/SuccessIllust.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/SuccessIllust.imageset/SuccessIllust.svg create mode 100644 StockMate/StockMate/resources/Assets.xcassets/add_shopping_cart.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/add_shopping_cart.imageset/add_shopping_cart@1x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/add_shopping_cart.imageset/add_shopping_cart@2x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/add_shopping_cart.imageset/add_shopping_cart@3x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/shoppingcart.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/shoppingcart.imageset/shoppingcart.svg diff --git a/StockMate/StockMate/app/core/components/AlertModal.swift b/StockMate/StockMate/app/core/components/AlertModal.swift new file mode 100644 index 0000000..72d1e1b --- /dev/null +++ b/StockMate/StockMate/app/core/components/AlertModal.swift @@ -0,0 +1,140 @@ +// +// AlertModal.swift +// StockMate +// +// Created by Admin on 11/4/25. +// + + +import SwiftUI + +struct AlertModal: View { + var icon: Image? = nil // ✅ 아이콘 없을 수도 있음 + var title: String + var message: String? = nil + + var primaryButtonTitle: String + var primaryAction: () -> Void + + var secondaryButtonTitle: String? = nil + var secondaryAction: (() -> Void)? = nil + + var buttonLayout: ButtonLayout = .vertical // ✅ horizontal / vertical + + enum ButtonLayout { + case vertical + case horizontal + } + + var body: some View { + VStack(spacing: 15) { + if let icon = icon { + icon + .resizable() + .scaledToFit() + .frame(width: 120, height: 120) + .padding(.top, 6) + } + + Text(title) + .font(.system(size: 18, weight: .bold)) + .multilineTextAlignment(.center) + .padding(.top, 10) + + if let message = message { + Text(message) + .font(.system(size: 14, weight: .regular)) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) + .padding(.bottom, 6) + } + + if buttonLayout == .vertical { + VStack(spacing: 10) { + if let secondary = secondaryButtonTitle, let secondaryAction = secondaryAction { + Button(secondary, action: secondaryAction) + .buttonStyle(CustomButtonStyle(type: .outlined(.Primary))) + } + Button(primaryButtonTitle, action: primaryAction) + .buttonStyle(CustomButtonStyle(type: .filled(Color.Primary))) + } + } else { + HStack(spacing: 10) { + if let secondary = secondaryButtonTitle, let secondaryAction = secondaryAction { + Button(secondary, action: secondaryAction) + .buttonStyle(CustomButtonStyle(type: .outlined(.Primary))) + } + Button(primaryButtonTitle, action: primaryAction) + .buttonStyle(CustomButtonStyle(type: .filled(Color.Primary))) + } + } + } + .padding(20) + .frame(maxWidth: 300) + .background(Color.white) + .cornerRadius(32) + .shadow(radius: 8) + } +} + +import SwiftUI + +#Preview { + ScrollView{ + VStack(spacing: 40) { + // ✅ 1. 체크 아이콘 + 버튼 1개 + AlertModal( + icon: Image("SuccessIllust"), + title: "등록 완료!", + message: "입고 부품 등록이 완료되었습니다.", + primaryButtonTitle: "확인", + primaryAction: {} + ) + AlertModal( + icon: Image("SuccessIllust"), + title: "출고 완료!", + message: "사용 처리가 완료되었습니다.", + primaryButtonTitle: "확인", + primaryAction: {} + ) + + + + // ✅ 2. 아이콘 없이 버튼 2개 (가로) + AlertModal( + title: "주문 취소", + message: "주문을 취소하시겠습니까?", + primaryButtonTitle: "예", + primaryAction: {}, + secondaryButtonTitle: "아니오", + secondaryAction: {}, + buttonLayout: .horizontal + ) + AlertModal( + title: "로그아웃", + message: "로그아웃 하시겠습니까?", + primaryButtonTitle: "로그아웃", + primaryAction: {}, + secondaryButtonTitle: "취소", + secondaryAction: {}, + buttonLayout: .horizontal + ) + + + // ✅ 3. 주문완료 + AlertModal( + icon: Image("SuccessIllust"), + title: "주문완료!", + message: "해당 부품 주문이 완료되었습니다.", + primaryButtonTitle: "주문상세", + primaryAction: {}, + secondaryButtonTitle: "홈으로", + secondaryAction: {}, + buttonLayout: .vertical + ) + } + .padding() + .background(Color.gray.opacity(0.1)) + } + +} diff --git a/StockMate/StockMate/app/core/components/CartCard.swift b/StockMate/StockMate/app/core/components/CartCard.swift index 772629f..556483f 100644 --- a/StockMate/StockMate/app/core/components/CartCard.swift +++ b/StockMate/StockMate/app/core/components/CartCard.swift @@ -55,61 +55,83 @@ struct CartCard: View { if quantity == 0 { if let onAddToCart = onAddToCart { Button(action: onAddToCart) { - Image(systemName: "cart.badge.plus") - .font(.system(size: 18)) - .foregroundColor(.Primary) +// Image(systemName: "cart.badge.plus") +// .font(.system(size: 18)) +// .foregroundColor(.Primary) +// .padding(10) +// .background(Color.Primary.opacity(0.1)) +// .clipShape(Circle()) + Image("add_shopping_cart") + .resizable() + .scaledToFit() + .frame(width: 18, height: 18) .padding(10) - .background(Color.Primary.opacity(0.1)) + .background(Color.white) .clipShape(Circle()) + .shadow(color: .black.opacity(0.2), radius: 4, x: 0, y: 4) } } } else if quantity == 1 { HStack(spacing: 10) { Button(action: onRemoveFromCart) { Image(systemName: "trash") - .font(.system(size: 14, weight: .bold)) - .foregroundColor(.red) + .font(.system(size: 14, weight: .regular)) + .frame(width: 13,height: 13) + .foregroundColor(.black) } Text("1") - .font(.system(size: 15, weight: .semibold)) - .frame(width: 24) + .font(.system(size: 15, weight: .medium)) + .frame(width: 20) Button(action: onIncrease) { Image(systemName: "plus") - .font(.system(size: 14, weight: .bold)) - .foregroundColor(.Primary) + .font(.system(size: 14, weight: .regular)) + .frame(width: 13,height: 13) + .foregroundColor(.black) } } .padding(.vertical, 6) .padding(.horizontal, 10) .background(Color.white) .cornerRadius(10) - .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) + .overlay( // ✅ 테두리 추가 + RoundedRectangle(cornerRadius: 10) + .stroke( Color.LightBlue03, lineWidth: 2) + ) + .clipShape(RoundedRectangle(cornerRadius: 10)) + .shadow(color: Color.black.opacity(0.25), radius: 4, x: 0, y: 4) } else { HStack(spacing: 10) { Button(action: onDecrease) { Image(systemName: "minus") - .font(.system(size: 14, weight: .bold)) - .foregroundColor(.gray) + .font(.system(size: 14, weight: .regular)) + .frame(width: 13,height: 13) + .foregroundColor(.black) } Text("\(quantity)") - .font(.system(size: 15, weight: .semibold)) - .frame(width: 24) + .font(.system(size: 15, weight: .medium)) + .frame(width: 20) Button(action: onIncrease) { Image(systemName: "plus") - .font(.system(size: 14, weight: .bold)) - .foregroundColor(.Primary) + .font(.system(size: 14, weight: .regular)) + .frame(width: 13,height: 13) + .foregroundColor(.black) } } .padding(.vertical, 6) .padding(.horizontal, 10) .background(Color.white) .cornerRadius(10) - .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) + .overlay( // ✅ 테두리 추가 + RoundedRectangle(cornerRadius: 10) + .stroke( Color.LightBlue03, lineWidth: 2) + ) + .clipShape(RoundedRectangle(cornerRadius: 10)) + .shadow(color: Color.black.opacity(0.25), radius: 4, x: 0, y: 4) } } } diff --git a/StockMate/StockMate/app/core/components/CustomButtonStyle.swift b/StockMate/StockMate/app/core/components/CustomButtonStyle.swift new file mode 100644 index 0000000..fe42295 --- /dev/null +++ b/StockMate/StockMate/app/core/components/CustomButtonStyle.swift @@ -0,0 +1,50 @@ +// +// CustomButtonStyle.swift +// StockMate +// +// Created by Admin on 11/4/25. +// + + +import SwiftUI + +struct CustomButtonStyle: ButtonStyle { + enum StyleType { + case filled(Color) + case outlined(Color) + } + + var type: StyleType + var height: CGFloat = 52 + var cornerRadius: CGFloat = 9999 + var fontSize: CGFloat = 16 + var fontWeight: Font.Weight = .semibold + + func makeBody(configuration: Configuration) -> some View { + switch type { + case .filled(let color): + configuration.label + .frame(maxWidth: .infinity, minHeight: height) + .background(color.opacity(configuration.isPressed ? 0.8 : 1)) + .foregroundColor(.white) + .cornerRadius(cornerRadius) + .font(.system(size: fontSize, weight: fontWeight)) +// .scaleEffect(configuration.isPressed ? 0.98 : 1.0) + .animation(.easeOut(duration: 0.15), value: configuration.isPressed) + + case .outlined(let color): + configuration.label + .frame(maxWidth: .infinity, minHeight: height) + .overlay( + RoundedRectangle(cornerRadius: cornerRadius) + .stroke(color, lineWidth: 1.5) + ) + .foregroundColor(color) + .font(.system(size: fontSize, weight: fontWeight)) + .background(Color.white) + .cornerRadius(cornerRadius) +// .scaleEffect(configuration.isPressed ? 0.98 : 1.0) + .animation(.easeOut(duration: 0.15), value: configuration.isPressed) + } + } +} diff --git a/StockMate/StockMate/app/core/components/OrderRequestCardView.swift b/StockMate/StockMate/app/core/components/OrderRequestCardView.swift index aa29bef..a328a43 100644 --- a/StockMate/StockMate/app/core/components/OrderRequestCardView.swift +++ b/StockMate/StockMate/app/core/components/OrderRequestCardView.swift @@ -52,61 +52,80 @@ struct OrderRequestCardView: View { // 🪄 수량에 따른 3단계 분기 if quantity == 0 { Button(action: onAddToCart) { - Image(systemName: "cart.badge.plus") - .font(.system(size: 18)) - .foregroundColor(.Primary) + Image("add_shopping_cart") + .resizable() + .scaledToFit() + .frame(width: 18, height: 18) .padding(10) - .background(Color.Primary.opacity(0.1)) + .background(Color.white) .clipShape(Circle()) + .shadow(color: Color.black.opacity(0.25), radius: 4, x: 0, y: 4) + } } else if quantity == 1 { HStack(spacing: 10) { Button(action: onRemoveFromCart) { Image(systemName: "trash") - .font(.system(size: 14, weight: .bold)) - .foregroundColor(.red) + .font(.system(size: 14, weight: .regular)) + .frame(width: 13,height: 13) + .foregroundColor(.black) } Text("1") - .font(.system(size: 15, weight: .semibold)) - .frame(width: 24) + .font(.system(size: 15, weight: .medium)) + .frame(width: 20) Button(action: onIncrease) { Image(systemName: "plus") - .font(.system(size: 14, weight: .bold)) - .foregroundColor(.Primary) + .font(.system(size: 14, weight: .regular)) + .frame(width: 13,height: 13) + .foregroundColor(.black) } } .padding(.vertical, 6) .padding(.horizontal, 10) .background(Color.white) .cornerRadius(10) - .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) + .overlay( // ✅ 테두리 추가 + RoundedRectangle(cornerRadius: 10) + .stroke( Color.LightBlue03, lineWidth: 2) + ) + .clipShape(RoundedRectangle(cornerRadius: 10)) + .shadow(color: Color.black.opacity(0.25), radius: 4, x: 0, y: 4) + } else { HStack(spacing: 10) { Button(action: onDecrease) { Image(systemName: "minus") - .font(.system(size: 14, weight: .bold)) - .foregroundColor(.gray) + .font(.system(size: 14, weight: .regular)) + .frame(width: 13,height: 13) + .foregroundColor(.black) } Text("\(quantity)") - .font(.system(size: 15, weight: .semibold)) - .frame(width: 24) + .font(.system(size: 15, weight: .medium)) + .frame(width: 20) Button(action: onIncrease) { Image(systemName: "plus") - .font(.system(size: 14, weight: .bold)) - .foregroundColor(.Primary) + .font(.system(size: 14, weight: .regular)) + .frame(width: 13,height: 13) + .foregroundColor(.black) } } .padding(.vertical, 6) .padding(.horizontal, 10) .background(Color.white) .cornerRadius(10) - .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) + .overlay( // ✅ 테두리 추가 + RoundedRectangle(cornerRadius: 10) + .stroke( Color.LightBlue03, lineWidth: 2) + ) + .clipShape(RoundedRectangle(cornerRadius: 10)) + .shadow(color: Color.black.opacity(0.25), radius: 4, x: 0, y: 4) + } } } @@ -116,3 +135,56 @@ struct OrderRequestCardView: View { .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) } } + +#Preview { + let sampleItem = InventoryItem( + id: 1, + name: "Engine Oil Filter", + price: 18000, + image: "https://picsum.photos/200", + trim: "1.6 Turbo", + model: "SM-230", + category: 3, + korName: "엔진 오일 필터", + engName: "Engine Oil Filter", + categoryName: "엔진 부품", + stock: 42, + amount: 3, + limitAmount: 5, + isLack: true + ) + + VStack(spacing: 20) { + // 수량 0 (아직 카트에 안 담김) + OrderRequestCardView( + item: sampleItem, + quantity: 0, + onIncrease: {}, + onDecrease: {}, + onAddToCart: {}, + onRemoveFromCart: {} + ) + + // 수량 1 (카트에 하나 있음) + OrderRequestCardView( + item: sampleItem, + quantity: 1, + onIncrease: {}, + onDecrease: {}, + onAddToCart: {}, + onRemoveFromCart: {} + ) + + // 수량 3 (여러 개 담긴 상태) + OrderRequestCardView( + item: sampleItem, + quantity: 3, + onIncrease: {}, + onDecrease: {}, + onAddToCart: {}, + onRemoveFromCart: {} + ) + } + .padding() + .background(Color(uiColor: .systemGray6)) +} diff --git a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift index ddd982c..615d4f4 100644 --- a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift @@ -62,7 +62,7 @@ struct HomeView: View { .cornerRadius(9999) .overlay( RoundedRectangle(cornerRadius: 9999) - .stroke(Color.gray.opacity(0.4), lineWidth: 1) + .stroke(Color.GrayMordern400, lineWidth: 1) ) .padding(.horizontal) } @@ -73,17 +73,19 @@ struct HomeView: View { // 도넛 차트 섹션 VStack(alignment: .leading, spacing: 18) { Text("지난달 카테고리 별 지출") - .font(.headline) + .font(.system(size: 15, weight: .semibold)) .padding(4) + .frame(maxWidth: .infinity, alignment: .leading) // ✅ 항상 왼쪽 정렬 HStack { if dashboardViewModel.isLoading { ProgressView("불러오는 중...") - .frame(height: 130) + .frame(height: 155) } else if dashboardViewModel.categorySpendings.isEmpty { Text("지난달 지출 내역이 없습니다.") .foregroundColor(.gray) - .frame(height: 150) + .frame(maxWidth: .infinity) + .frame(height: 155, alignment: .center) } else { DonutChartView(data: dashboardViewModel.categorySpendings) .frame(height: 155) @@ -103,25 +105,32 @@ struct HomeView: View { // 막대그래프 섹션 VStack(alignment: .leading, spacing: 8) { Text("월간 지출 현황") - .font(.headline) + .font(.system(size: 15, weight: .semibold)) .padding(4) + .frame(maxWidth: .infinity, alignment: .leading) // ✅ 항상 왼쪽 정렬 - if dashboardViewModel.isLoading { - ProgressView("데이터 불러오는 중...") - .frame(height: 150) - } else if dashboardViewModel.monthlySpendings.isEmpty { - Text("최근 지출 내역이 없습니다.") - .foregroundColor(.gray) - .frame(height: 150) - } else { - BarChartView( - values: dashboardViewModel.spendingRatios, - labels: dashboardViewModel.monthLabels, - amounts: dashboardViewModel.monthlySpendings.map { $0.totalAmount }, selectedMonth: $selectedMonth - ) - .frame(height: 220) - .background(Color.white) - .cornerRadius(16) + ZStack { // ✅ 크기 고정용 컨테이너 + RoundedRectangle(cornerRadius: 16) + .fill(Color.white) + .frame(height: 220) // ✅ 일정 높이 고정 + if dashboardViewModel.isLoading { + ProgressView("데이터 불러오는 중...") + .frame(height: 220) + } else if dashboardViewModel.monthlySpendings.isEmpty { + Text("최근 지출 내역이 없습니다.") + .foregroundColor(.gray) + .frame(height: 220) + } else { + BarChartView( + values: dashboardViewModel.spendingRatios, + labels: dashboardViewModel.monthLabels, + amounts: dashboardViewModel.monthlySpendings.map { $0.totalAmount }, selectedMonth: $selectedMonth + ) + .padding() + // .frame(height: 220) + // .background(Color.white) + // .cornerRadius(16) + } } } .padding() @@ -154,19 +163,33 @@ struct HomeView: View { private var lackStockSection: some View { VStack(alignment: .leading, spacing: 8) { Text("재고 부족 조회") - .font(.headline) + .font(.system(size: 15, weight: .semibold)) + .frame(maxWidth: .infinity, alignment: .leading) // ✅ 항상 왼쪽 정렬 유지 HStack(spacing: 13) { - ForEach(inventoryViewModel.lackCounts, id: \.id) { item in - NavigationLink { - LackListView(selectedCategory: item.categoryName) - } label: { + if inventoryViewModel.lackCounts.isEmpty { + // ✅ 데이터가 없을 때도 공간 확보 + ForEach(0..<5) { _ in StatusItem( - title: item.categoryName, - count: item.count, - color: colorForCategory(item.categoryName), - icon: iconForCategory(item.categoryName) + title: "-", + count: 0, + color: .gray.opacity(0.1), + icon: "questionmark" ) } + .redacted(reason: .placeholder) // 로딩 중 효과 (선택사항) + } else { + ForEach(inventoryViewModel.lackCounts, id: \.id) { item in + NavigationLink { + LackListView(selectedCategory: item.categoryName) + } label: { + StatusItem( + title: item.categoryName, + count: item.count, + color: colorForCategory(item.categoryName), + icon: iconForCategory(item.categoryName) + ) + } + } } } .padding(.vertical, 4) @@ -223,7 +246,7 @@ struct StatusItem: View { .clipShape(RoundedRectangle(cornerRadius: 100)) Text(title) - .font(.system(size: 13, weight: .semibold)) + .font(.system(size: 12, weight: .semibold)) .foregroundColor(.black) .padding(.top, 5) .lineLimit(1) @@ -242,17 +265,17 @@ struct StatusItem: View { // HomeView() //} -//#Preview { -// let dashboardVM = DashboardViewModel() -// dashboardVM.categorySpendings = [ -// CategorySpending(categoryName: "전기/램프", totalAmount: 450000), -// CategorySpending(categoryName: "엔진/미션", totalAmount: 300000), -// CategorySpending(categoryName: "하체/바디", totalAmount: 150000), -// CategorySpending(categoryName: "내장/외장", totalAmount: 100000), -// CategorySpending(categoryName: "기타소모품", totalAmount: 50000) -// ] -// -// return HomeView() -// .environmentObject(AuthViewModel()) -// .environmentObject(dashboardVM) // ✅ 이제 진짜 연결됨! -//} +#Preview { + let dashboardVM = DashboardViewModel() + dashboardVM.categorySpendings = [ + CategorySpending(categoryName: "전기/램프", totalAmount: 450000), + CategorySpending(categoryName: "엔진/미션", totalAmount: 300000), + CategorySpending(categoryName: "하체/바디", totalAmount: 150000), + CategorySpending(categoryName: "내장/외장", totalAmount: 100000), + CategorySpending(categoryName: "기타소모품", totalAmount: 50000) + ] + + return HomeView() + .environmentObject(AuthViewModel()) + .environmentObject(dashboardVM) // ✅ 이제 진짜 연결됨! +} diff --git a/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift b/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift index 6b14cc2..5b4f73c 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift @@ -110,8 +110,8 @@ struct UsedPartListSheetView: View { .foregroundColor(.black) } Text("\(part.quantity)") - .font(.system(size: 14,weight: .medium)) - .frame(width: 19) + .font(.system(size: 15, weight: .medium)) + .frame(width: 20) Button { part.quantity += 1 } label: { diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift index 430e3b3..9c6d8f4 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift @@ -29,15 +29,31 @@ struct OrderInfoView: View { @State private var navigateToSuccessPage = false + // ✅ 모달 관련 상태 + @State private var showOrderSuccessModal = false + @State private var navigateToOrderDetail = false + @State private var navigateToHome = false + private var destinationView: some View { - Group { - if let id = orderViewModel.createdOrderId { - OrderDetailView(orderId: id, orderViewModel: orderViewModel) - } else { - EmptyView() - } - } + Group { + if navigateToOrderDetail, let id = orderViewModel.createdOrderId { + OrderDetailView(orderId: id, orderViewModel: orderViewModel) + } else if navigateToHome { + HomeView() + } else { + EmptyView() + } + } } +// private var destinationView: some View { +// Group { +// if let id = orderViewModel.createdOrderId { +// OrderDetailView(orderId: id, orderViewModel: orderViewModel) +// } else { +// EmptyView() +// } +// } +// } func formattedShippingDate() -> String { let formatter = DateFormatter() @@ -83,8 +99,12 @@ struct OrderInfoView: View { .onChange(of: orderViewModel.isOrderSuccess) { success in if success { Task { + // 1) 서버에 반영된 장바구니를 먼저 비운다 (await) await cartViewModel.clearCart() - navigateToSuccessPage = true +// navigateToSuccessPage = true + // 2) cart가 비워진 후에 모달을 띄운다 + // (모달을 띄우기 전에 createdOrderId는 orderViewModel에 이미 세팅되어 있어야 함) + showOrderSuccessModal = true } } } @@ -92,8 +112,67 @@ struct OrderInfoView: View { .sheet(isPresented: $depositViewModel.showChargeSheet) { DepositChargeView(viewModel: depositViewModel) .presentationDetents([.fraction(0.80)]) // 시트 높이 80% -// .presentationDragIndicator(.visible) } + // 모달 오버레이 (body 안) + .overlay { + if showOrderSuccessModal { + ZStack { + Color.black.opacity(0.4).ignoresSafeArea() + .onTapGesture { + // 배경 탭으로도 모달 닫을 수 있게 하려면 uncomment + // showOrderSuccessModal = false + } + + AlertModal( + icon: Image("SuccessIllust"), + title: "주문완료!", + message: "해당 부품 주문이 완료되었습니다.", + primaryButtonTitle: "주문상세", + primaryAction: { + // 1) 모달 닫기 + showOrderSuccessModal = false + + // 2) 네비게이션 트리거 -> OrderDetail 로 이동 + // orderViewModel.createdOrderId 가 있어야 함 + navigateToOrderDetail = true + }, + secondaryButtonTitle: "홈으로", + secondaryAction: { + // 모달 닫고 홈으로 + showOrderSuccessModal = false + navigateToHome = true + }, + buttonLayout: .vertical + ) + .transition(.scale) + .padding(.horizontal, 20) + } + .animation(.easeInOut, value: showOrderSuccessModal) + } + } + + // 네비게이션 실행을 위한 숨은 링크 (body 밖 어디든) + .background( + Group { + // OrderDetail 우선 (OrderDetail은 createdOrderId 를 필요로 함) + NavigationLink(destination: + Group { + if let id = orderViewModel.createdOrderId { + OrderDetailView(orderId: id, orderViewModel: orderViewModel) + } else { + EmptyView() + } + }, + isActive: $navigateToOrderDetail) { + EmptyView() + } + + NavigationLink(destination: HomeView(), isActive: $navigateToHome) { + EmptyView() + } + } + ) + } @@ -238,7 +317,6 @@ extension OrderInfoView { .frame(maxWidth: .infinity) } - private var shippingDateSection: some View { VStack(alignment: .leading) { Text("배송 요청일") diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderView.swift index c238a30..3fa5db5 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderView.swift @@ -31,10 +31,10 @@ struct OrderView: View { // 🔍 검색창 NavigationLink(destination: - OrderRequestSearchView( - cartViewModel: cartViewModel - //inventoryViewModel: inventoryViewModel - ) + OrderRequestSearchView( + cartViewModel: cartViewModel + //inventoryViewModel: inventoryViewModel + ) ) { HStack { Image(systemName: "magnifyingglass") @@ -49,7 +49,7 @@ struct OrderView: View { .cornerRadius(9999) .overlay( RoundedRectangle(cornerRadius: 9999) - .stroke(Color.gray.opacity(0.4), lineWidth: 1) + .stroke(Color.GrayMordern400, lineWidth: 1) ) .padding(.horizontal) } @@ -126,7 +126,3 @@ struct OrderView: View { } } -// -//#Preview { -// OrderView() -//} diff --git a/StockMate/StockMate/resources/Assets.xcassets/SuccessIllust.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/SuccessIllust.imageset/Contents.json new file mode 100644 index 0000000..4890788 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/SuccessIllust.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "SuccessIllust.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/SuccessIllust.imageset/SuccessIllust.svg b/StockMate/StockMate/resources/Assets.xcassets/SuccessIllust.imageset/SuccessIllust.svg new file mode 100644 index 0000000..60b221d --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/SuccessIllust.imageset/SuccessIllust.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/StockMate/StockMate/resources/Assets.xcassets/add_shopping_cart.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/add_shopping_cart.imageset/Contents.json new file mode 100644 index 0000000..566c0a1 --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/add_shopping_cart.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "add_shopping_cart@1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "add_shopping_cart@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "add_shopping_cart@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/add_shopping_cart.imageset/add_shopping_cart@1x.png b/StockMate/StockMate/resources/Assets.xcassets/add_shopping_cart.imageset/add_shopping_cart@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..a9d1e53d74ce3a4868a7ee959395842a705f0f8b GIT binary patch literal 458 zcmV;*0X6=KP)=F9Wf)?3vcMSwk zlKwaeWZ~`G_ukF`{8t?W3%BRbFWPWJz4}4&NCdlxc=j^rh6R`tS&YzXBV)1X2dP6q z!`H#a(Y`-FMaB=2$R+>_5MvVgB)ZvpP*}(0VR+S@vV#1hi24xVofrwmAZQ|=H?0-I;T{zrEMspC{@Xx7W-M1tTqv{W5*+%Q>h7qp32RL=O&i| z&CF%?(ZVCdZPUcN>i=byJ3#10!%Y{ew_Ppiat9HtEQet&65}9MTiXVEUTXLJlakUX~$h{Ra6OuK1<`m)wOS_cC@*Jq)JvtP=}_L8T!AdFFT-HDqR=X6hI zb}2tKR%;b!O!f(ioy=P#@*Jz*O)@(+D89U-eB;z{@FAP{vQ{K#Q! z;cg1!t@snfm+SR*oa+QW4nct_tz|}s??8=!JYEW(I~w2+u@*>Yzga&wCSfA?u`#N| zgttWWz31h8rDCzlQX>kua<}+(TIhYY`{rWOpEBzlG*)bxMwsMdU^Q+!$F^B}Q#OTi zBXapHACTf^*E_tB^(ozq2O@pEes?5m7v?b-4fr_se6^MJHkPJ%XGYunM;` zWaiC8?AV54EqB^}7VV4HGwQWau2|ekGd8O7V30TYeX2Wi9XX|q=f;>vELI>Ck3kZL z>jX5IvFI0f7)K?ibSEM#{4IdHhpn9rImvylz9bNXwNj{kNVer94?nd#E#TZls`C4I iXvGI@&?KwetMD7XpC{*sXhY@z0000CQ|4P=nZ(RNUD+(G&g{pAbG9Y1QQhXvB?PvCunklP$fZqYicb?ON1DG=;Fgkw@bI~&sOE$fF(lsF(Sl0dlr_2 zvG714YKpAJ@R_gxh(*55bVa+w*ArL*#+-I%c)|F6*2UBZc0a?}#Mk9;dZy%URqjp* zg(F>v+wMQMdGv70mn?+Cygq#O#{K2H>iv#IPB=CX*K6+IXU24oCrw9^vsG=gJ=tvpsru!?^J(^eml({Z zd3msQ=4Inc@^l;{(v7~4Q~vTtcO~fO2LV)UBf zk3?Ku2aMBm>)#sDUH4oFZS@wZOS~D(xqByVo^>ZWZ zc+;Q783cA(Gb(>QeAtIMAW}mmN=m5sX%p&^g*i<9$Q*!;6n* z=gsokj-Pg+&WMljG9zIa7Fk{;Ln#T3J|vO`_#{+#NI@468Pwt>d$;*%y3CCdc%(=7 z2YThY{+!;0|4bmK^-_yZi;C$taiS(rcnAqP)3f?sC*Pt<3xS;G4gWyi%&YR60Sh8v zOrxV12(5l>$AUmCfspWV%?#6Lz+#Pi zVWCzcB}3P&!}b}dSYs*N2IpvHBElI-+MZ{p7e}8fqU0Z;te76}K;&=$0000 + + + + + + + diff --git a/StockMate/StockMate/resources/Color.swift b/StockMate/StockMate/resources/Color.swift index 44ecf7b..12b1d5e 100644 --- a/StockMate/StockMate/resources/Color.swift +++ b/StockMate/StockMate/resources/Color.swift @@ -33,6 +33,9 @@ extension Color { static let Dark = Color(hex: "#04150C") static let Light = Color(hex: "#F7F7F7") + static let GrayMordern400 = Color(hex: "#9AA4B2") + + // Text static let textBlack = Color(hex: "#152C07") From 43d3d9329902ee940e0a84e2b8199b72a20014fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Wed, 5 Nov 2025 10:01:52 +0900 Subject: [PATCH 56/85] =?UTF-8?q?[FEAT]=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/user/ui/ProfileCircleView.swift | 16 +- .../app/feature/user/ui/ProfileView.swift | 6 +- .../app/feature/user/ui/UserProfileView.swift | 146 ++++++++++++++++++ StockMate/StockMate/resources/Color.swift | 1 + 4 files changed, 159 insertions(+), 10 deletions(-) create mode 100644 StockMate/StockMate/app/feature/user/ui/UserProfileView.swift diff --git a/StockMate/StockMate/app/feature/user/ui/ProfileCircleView.swift b/StockMate/StockMate/app/feature/user/ui/ProfileCircleView.swift index 16fbe17..2e1a97d 100644 --- a/StockMate/StockMate/app/feature/user/ui/ProfileCircleView.swift +++ b/StockMate/StockMate/app/feature/user/ui/ProfileCircleView.swift @@ -18,12 +18,16 @@ struct ProfileCircleView: View { } var body: some View { - Text(initials) - .font(.headline) - .foregroundColor(Color(hex: "#374EAF")) - .frame(width: size, height: size) - .background(Color(hex: "#DCE0F1")) // 고정 색상 - .clipShape(Circle()) + GeometryReader { geometry in + let minSide = min(geometry.size.width, geometry.size.height) + Text(initials) + .font(.system(size: minSide * 0.35, weight: .regular)) // ✅ 내부 크기 비례 + .foregroundColor(Color(hex: "#374EAF")) + .frame(width: geometry.size.width, height: geometry.size.height) + .background(Color(hex: "#DCE0F1")) + .clipShape(Circle()) + } + .frame(width: size, height: size) } } diff --git a/StockMate/StockMate/app/feature/user/ui/ProfileView.swift b/StockMate/StockMate/app/feature/user/ui/ProfileView.swift index bf330f7..016e820 100644 --- a/StockMate/StockMate/app/feature/user/ui/ProfileView.swift +++ b/StockMate/StockMate/app/feature/user/ui/ProfileView.swift @@ -11,7 +11,6 @@ struct ProfileView: View { @StateObject private var userViewModel = UserViewModel() var body: some View { -// NavigationStack { VStack(alignment: .leading, spacing: 24) { // MARK: - Profile Header @@ -39,11 +38,11 @@ struct ProfileView: View { // MARK: - General Section VStack(alignment: .leading, spacing: 12) { VStack(spacing: 10) { - SettingRow(icon: "user", title: "프로필 수정") + SettingNavigationRow(icon: "user", title: "프로필 확인", destination: UserProfileView()) + SettingRow(icon: "lock", title: "비밀번호 변경") SettingRow(icon: "notification", title: "알림") SettingNavigationRow(icon: "receipt", title: "예치금 히스토리", destination: DepositHistoryView()) -// SettingRow(icon: "credit", title: "예치금 히스토리") SettingNavigationRow(icon: "bag", title: "주문 내역", destination: OrderListView()) SettingRow(icon: "logout", title: "로그아웃") } @@ -60,7 +59,6 @@ struct ProfileView: View { .onAppear { Task { await userViewModel.loadUserInfo() } } -// } } } diff --git a/StockMate/StockMate/app/feature/user/ui/UserProfileView.swift b/StockMate/StockMate/app/feature/user/ui/UserProfileView.swift new file mode 100644 index 0000000..fb70701 --- /dev/null +++ b/StockMate/StockMate/app/feature/user/ui/UserProfileView.swift @@ -0,0 +1,146 @@ +// +// UserProfileView.swift +// StockMate +// +// Created by Admin on 11/5/25. +// + +import SwiftUI + +struct UserProfileView: View { + @StateObject private var userViewModel = UserViewModel() + + var body: some View { + VStack(alignment: .leading, spacing: 12) { + HStack(spacing: 16) { + Spacer() + ProfileCircleView(name: userViewModel.userInfo?.owner ?? "사용자", size: 103) + Spacer() + } + .padding(.vertical, 32) + + VStack(spacing: 9) { + Text("대표자") + .font(.system(size: 14, weight: .medium)) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 10) + .foregroundColor(Color.black) + + HStack { + Text(userViewModel.userInfo?.owner ?? "이름 없음") + .font(.system(size: 16, weight: .regular)) + .foregroundColor(.black) + Spacer() + } + .padding() + .cornerRadius(12) + .overlay( + RoundedRectangle(cornerRadius: 12) + .stroke(Color.GrayMordern300, lineWidth: 1) + ) + .padding(.horizontal, 10) + .padding(.bottom, 10) + + Text("이에일") + .font(.system(size: 14, weight: .medium)) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 10) + .foregroundColor(Color.black) + + HStack { + Text(userViewModel.userInfo?.email ?? "이메일 없음") + .font(.system(size: 16, weight: .regular)) + .foregroundColor(.black) + Spacer() + } + .padding() + .cornerRadius(12) + .overlay( + RoundedRectangle(cornerRadius: 12) + .stroke(Color.GrayMordern300, lineWidth: 1) + ) + .padding(.horizontal, 10) + .padding(.bottom, 10) + + Text("지점") + .font(.system(size: 14, weight: .medium)) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 10) + .foregroundColor(Color.black) + + HStack { + Text(userViewModel.userInfo?.storeName ?? "지점명 없음") + .font(.system(size: 16, weight: .regular)) + .foregroundColor(.black) + Spacer() + } + .padding() + .cornerRadius(12) + .overlay( + RoundedRectangle(cornerRadius: 12) + .stroke(Color.GrayMordern300, lineWidth: 1) + ) + .padding(.horizontal, 10) + .padding(.bottom, 10) + + Text("주소") + .font(.system(size: 14, weight: .medium)) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 10) + .foregroundColor(Color.black) + + HStack { + Text(userViewModel.userInfo?.address ?? "주소 없음") + .font(.system(size: 16, weight: .regular)) + .foregroundColor(.black) + Spacer() + } + .padding() + .cornerRadius(12) + .overlay( + RoundedRectangle(cornerRadius: 12) + .stroke(Color.GrayMordern300, lineWidth: 1) + ) + .padding(.horizontal, 10) + .padding(.bottom, 10) + + Text("사업자등록번호") + .font(.system(size: 14, weight: .medium)) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 10) + .foregroundColor(Color.black) + + HStack { + Text(userViewModel.userInfo?.businessNumber ?? "사업자등록번호 없음") + .font(.system(size: 16, weight: .regular)) + .foregroundColor(.black) + Spacer() + } + .padding() + .cornerRadius(12) + .overlay( + RoundedRectangle(cornerRadius: 12) + .stroke(Color.GrayMordern300, lineWidth: 1) + ) + .padding(.horizontal, 10) + .padding(.bottom, 10) + + Spacer() + + } + .padding(3) + .cornerRadius(12) + .padding(.horizontal) + } + .navigationTitle("프로필 확인") + .background(Color.Light) + .navigationBarTitleDisplayMode(.inline) + .onAppear { + Task { await userViewModel.loadUserInfo() } + } + } +} + +#Preview { + UserProfileView() +} diff --git a/StockMate/StockMate/resources/Color.swift b/StockMate/StockMate/resources/Color.swift index 12b1d5e..a5baa30 100644 --- a/StockMate/StockMate/resources/Color.swift +++ b/StockMate/StockMate/resources/Color.swift @@ -33,6 +33,7 @@ extension Color { static let Dark = Color(hex: "#04150C") static let Light = Color(hex: "#F7F7F7") + static let GrayMordern300 = Color(hex: "#CDD5DF") static let GrayMordern400 = Color(hex: "#9AA4B2") From 58b8fc0d34ec8c6950db57bf8ae4d5437e5a8107 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Wed, 5 Nov 2025 17:26:49 +0900 Subject: [PATCH 57/85] =?UTF-8?q?[FEAT]=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=8B=9C=20=EC=B9=B4=EC=B9=B4=EC=98=A4=20=EC=9A=B0?= =?UTF-8?q?=ED=8E=B8=EB=B2=88=ED=98=B8=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- StockMate/StockMate/ContentView.swift | 71 ++++----------- .../core/KakaoPostcode/KakaoZipCodeVC.swift | 80 +++++++++++++++++ .../core/KakaoPostcode/KakaoZipCodeView.swift | 24 ++++++ .../core/KakaoPostcode/ViewController.swift | 53 ++++++++++++ .../app/core/components/CustomTextField.swift | 17 +++- .../app/feature/auth/ui/RegisterView.swift | 86 +++++++++++++++++-- .../app/feature/user/ui/ProfileView.swift | 38 +++++++- .../StockMate/app/navigation/AppNavHost.swift | 1 + 8 files changed, 306 insertions(+), 64 deletions(-) create mode 100644 StockMate/StockMate/app/core/KakaoPostcode/KakaoZipCodeVC.swift create mode 100644 StockMate/StockMate/app/core/KakaoPostcode/KakaoZipCodeView.swift create mode 100644 StockMate/StockMate/app/core/KakaoPostcode/ViewController.swift diff --git a/StockMate/StockMate/ContentView.swift b/StockMate/StockMate/ContentView.swift index 4d36f82..5787aab 100644 --- a/StockMate/StockMate/ContentView.swift +++ b/StockMate/StockMate/ContentView.swift @@ -7,67 +7,30 @@ import SwiftUI -//struct ContentView: View { -// @State private var showingScanner = false -// @State private var scannedCode: String? = nil -// -// var body: some View { -// NavigationView { -// VStack(spacing: 20) { -// if let code = scannedCode { -// Text("스캔 결과:") -// .font(.headline) -// Text(code) -// .font(.body) -// .multilineTextAlignment(.center) -// .padding() -// .background(Color(.systemGray6)) -// .cornerRadius(8) -// } else { -// Text("아직 스캔된 코드가 없습니다.") -// .foregroundColor(.secondary) -// } -// -// Button("QR 스캔 시작") { -// // 카메라 권한 체크는 시스템이 자동으로 권한 알림을 띄우므로 -// // 필요하면 권한 상태 확인 로직 추가 가능 -// showingScanner = true -// } -// .buttonStyle(.borderedProminent) -// .padding(.top) -// -// Spacer() -// } -// .padding() -// .navigationTitle("QR 스캐너 예제") -// .sheet(isPresented: $showingScanner) { -// QRScannerView(isPresented: $showingScanner, scannedCode: $scannedCode) -// .edgesIgnoringSafeArea(.all) -// } -// } -// } -//} struct ContentView: View { + @State private var address: String = "주소를 선택하세요" + @State private var showWebView = false + var body: some View { - VStack { - Image(systemName: "globe") - .imageScale(.large) - .foregroundStyle(.tint) - Text("임시 화면") - } - .padding() - HStack(spacing: 20) { - Image(systemName: "gearshape") - .font(.system(size: 40)) - .foregroundColor(.blue) + VStack(spacing: 20) { + Text(address) + .font(.title3) + .multilineTextAlignment(.center) + .padding() - Image(systemName: "lightbulb") - .font(.system(size: 40)) - .foregroundColor(.cyan) + Button("주소 검색") { + showWebView.toggle() + } + .font(.headline) + .buttonStyle(.borderedProminent) + } + .sheet(isPresented: $showWebView) { + KakaoZipCodeView(address: $address) } } } + #Preview { ContentView() } diff --git a/StockMate/StockMate/app/core/KakaoPostcode/KakaoZipCodeVC.swift b/StockMate/StockMate/app/core/KakaoPostcode/KakaoZipCodeVC.swift new file mode 100644 index 0000000..a4fecb1 --- /dev/null +++ b/StockMate/StockMate/app/core/KakaoPostcode/KakaoZipCodeVC.swift @@ -0,0 +1,80 @@ +// +// KakaoZipCodeVC.swift +// StockMate +// +// Created by Admin on 11/5/25. +// + + +import UIKit +import WebKit + +class KakaoZipCodeVC: UIViewController { + + // MARK: - Properties + var webView: WKWebView? + let indicator = UIActivityIndicatorView(style: .medium) + var onAddressSelected: ((String) -> Void)? // ✅ SwiftUI로 결과 전달용 콜백 + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = .white + setupWebView() + setupLayout() + } + + private func setupWebView() { + let contentController = WKUserContentController() + contentController.add(self, name: "callBackHandler") + + let config = WKWebViewConfiguration() + config.userContentController = contentController + + webView = WKWebView(frame: .zero, configuration: config) + webView?.navigationDelegate = self + + guard let webView = webView, + let url = URL(string: "https://yoo-hyuna.github.io/Kakao-Postcode/") else { return } + + webView.load(URLRequest(url: url)) + } + + private func setupLayout() { + guard let webView = webView else { return } + view.addSubview(webView) + webView.translatesAutoresizingMaskIntoConstraints = false + + webView.addSubview(indicator) + indicator.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + webView.topAnchor.constraint(equalTo: view.topAnchor), + webView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + webView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + webView.bottomAnchor.constraint(equalTo: view.bottomAnchor), + + indicator.centerXAnchor.constraint(equalTo: webView.centerXAnchor), + indicator.centerYAnchor.constraint(equalTo: webView.centerYAnchor) + ]) + } +} + +extension KakaoZipCodeVC: WKScriptMessageHandler { + func userContentController(_ userContentController: WKUserContentController, + didReceive message: WKScriptMessage) { + guard let data = message.body as? [String: Any] else { return } + let address = data["roadAddress"] as? String ?? "" + onAddressSelected?(address) // ✅ SwiftUI로 전달 + dismiss(animated: true) + } +} + +extension KakaoZipCodeVC: WKNavigationDelegate { + func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { + indicator.startAnimating() + } + + func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { + indicator.stopAnimating() + } +} diff --git a/StockMate/StockMate/app/core/KakaoPostcode/KakaoZipCodeView.swift b/StockMate/StockMate/app/core/KakaoPostcode/KakaoZipCodeView.swift new file mode 100644 index 0000000..fa69a87 --- /dev/null +++ b/StockMate/StockMate/app/core/KakaoPostcode/KakaoZipCodeView.swift @@ -0,0 +1,24 @@ +// +// KakaoZipCodeView.swift +// StockMate +// +// Created by Admin on 11/5/25. +// + + +import SwiftUI +import WebKit + +struct KakaoZipCodeView: UIViewControllerRepresentable { + @Binding var address: String + + func makeUIViewController(context: Context) -> KakaoZipCodeVC { + let vc = KakaoZipCodeVC() + vc.onAddressSelected = { selectedAddress in + address = selectedAddress + } + return vc + } + + func updateUIViewController(_ uiViewController: KakaoZipCodeVC, context: Context) {} +} diff --git a/StockMate/StockMate/app/core/KakaoPostcode/ViewController.swift b/StockMate/StockMate/app/core/KakaoPostcode/ViewController.swift new file mode 100644 index 0000000..2c793d0 --- /dev/null +++ b/StockMate/StockMate/app/core/KakaoPostcode/ViewController.swift @@ -0,0 +1,53 @@ +// +// ViewController.swift +// StockMate +// +// Created by Admin on 11/5/25. +// + + +import UIKit + +class ViewController: UIViewController { + + // MARK: - UI Components + let button = UIButton(type: .system) + let label = UILabel() + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = .white + configureUI() + } + + private func configureUI() { + [label, button].forEach { + view.addSubview($0) + $0.translatesAutoresizingMaskIntoConstraints = false + } + + label.text = "주소를 선택하세요" + label.font = UIFont.systemFont(ofSize: 18) + label.textAlignment = .center + + button.setTitle("주소 검색", for: .normal) + button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 18) + button.addTarget(self, action: #selector(handleButton(_:)), for: .touchUpInside) + + NSLayoutConstraint.activate([ + label.centerXAnchor.constraint(equalTo: view.centerXAnchor), + label.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -50), + label.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.8), + + button.centerXAnchor.constraint(equalTo: view.centerXAnchor), + button.topAnchor.constraint(equalTo: label.bottomAnchor, constant: 30) + ]) + } + + @objc + private func handleButton(_ sender: UIButton) { + let vc = KakaoZipCodeVC() + vc.modalPresentationStyle = .fullScreen + present(vc, animated: true) + } +} diff --git a/StockMate/StockMate/app/core/components/CustomTextField.swift b/StockMate/StockMate/app/core/components/CustomTextField.swift index 62eedc3..074d2c2 100644 --- a/StockMate/StockMate/app/core/components/CustomTextField.swift +++ b/StockMate/StockMate/app/core/components/CustomTextField.swift @@ -13,6 +13,7 @@ struct CustomTextField: View { @Binding var text: String var isEmail: Bool = false var errorMessage: String? = nil + var isReadOnly: Bool = false // ✅ 추가 @FocusState private var isFocused: Bool @@ -44,13 +45,27 @@ struct CustomTextField: View { y: 2 ) + if isReadOnly { + // ✅ 가로 스크롤 가능한 읽기 전용 텍스트 + ScrollView(.horizontal, showsIndicators: false) { + Text(text.isEmpty ? placeholder : text) + .font(.system(size: 15)) + .foregroundColor(text.isEmpty ? .gray : .black) + .padding(.horizontal, 14) + .padding(.vertical, 12) + .lineLimit(1) + } + .frame(height: 46) + .contentShape(Rectangle()) + + } else { TextField(placeholder, text: $text) .focused($isFocused) .padding(.horizontal, 14) .padding(.vertical, 12) .textInputAutocapitalization(.never) .autocorrectionDisabled() - + } } .frame(height: 46) // 높이 일정하게 고정 diff --git a/StockMate/StockMate/app/feature/auth/ui/RegisterView.swift b/StockMate/StockMate/app/feature/auth/ui/RegisterView.swift index 7656f2c..2e29490 100644 --- a/StockMate/StockMate/app/feature/auth/ui/RegisterView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/RegisterView.swift @@ -30,6 +30,9 @@ struct RegisterView: View { @State private var isLoading = false @State private var showToast = false + @State private var showAddressSearch = false + + var body: some View { ScrollView { VStack(spacing: 16) { @@ -83,19 +86,48 @@ struct RegisterView: View { text: $storeName, errorMessage: storeNameError ) - CustomTextField( - title: "주소", - placeholder: "서울특별시 강남구 ...", - text: $address, - errorMessage: addressError - ) + // ✅ 주소 입력 필드 + 버튼 추가 부분 + VStack(alignment: .leading, spacing: 4) { + HStack { + CustomTextField( + title: "주소", + placeholder: "서울특별시 강남구 ...", + text: $address, + errorMessage: addressError, + isReadOnly: true // ✅ 추가 + ) + .disabled(true) // 사용자가 직접 입력 못하게 + .onTapGesture { + // 탭해도 검색창 열 수 있게 (선택사항) + showAddressSearch.toggle() + } + + Button(action: { + showAddressSearch.toggle() + }) { + Text("주소 검색") + .font(.system(size: 14, weight: .semibold)) + .frame(height: 43) + .padding(.horizontal, 12) + .background(Color.Primary) + .foregroundColor(.white) + .cornerRadius(8) + } + .sheet(isPresented: $showAddressSearch) { + KakaoZipCodeView(address: $address) + } + } + } CustomTextField( title: "사업자등록번호", placeholder: "123-45-67890", text: $bizNo, errorMessage: bizNoError ) - .keyboardType(.numbersAndPunctuation) + .keyboardType(.numberPad) + .onChange(of: bizNo) { newValue in + formatBizNoInput(newValue) + } } .padding(.horizontal, 24) @@ -138,10 +170,15 @@ struct RegisterView: View { } } .padding(.bottom, 40) + + // ✅ 키보드 가림 방지용 여백 + Spacer().frame(height: 300) } } .background(Color.Light) .ignoresSafeArea() + .scrollDismissesKeyboard(.interactively) // ✅ 손가락으로 스크롤하면 키보드 자동 내려감 + } // MARK: - 유효성 검사 함수 @@ -212,9 +249,42 @@ struct RegisterView: View { owner: owner, address: address, storeName: storeName, - bizNo: bizNo + bizNo: bizNo.filter { $0.isNumber } // ← 여기서 숫자만 추출해서 전송 ) isLoading = false } } + + private func formatBizNoInput(_ input: String) { + // 1️⃣ 숫자만 남기기 + let digitsOnly = input.filter { $0.isNumber } + + // 2️⃣ 하이픈 자동 삽입 + var formatted = "" + let length = digitsOnly.count + + if length <= 3 { + formatted = digitsOnly + } else if length <= 5 { + formatted = "\(digitsOnly.prefix(3))-\(digitsOnly.suffix(from: digitsOnly.index(digitsOnly.startIndex, offsetBy: 3)))" + } else { + let first = digitsOnly.prefix(3) + let middleStart = digitsOnly.index(digitsOnly.startIndex, offsetBy: 3) + let middleEnd = digitsOnly.index(middleStart, offsetBy: 2, limitedBy: digitsOnly.endIndex) ?? digitsOnly.endIndex + let middle = digitsOnly[middleStart.. 10 { + formatted = String(formatted.prefix(12)) // 하이픈 포함 + } + + // 4️⃣ 상태 업데이트 + if formatted != bizNo { + bizNo = formatted + } + } } + diff --git a/StockMate/StockMate/app/feature/user/ui/ProfileView.swift b/StockMate/StockMate/app/feature/user/ui/ProfileView.swift index 016e820..55d236c 100644 --- a/StockMate/StockMate/app/feature/user/ui/ProfileView.swift +++ b/StockMate/StockMate/app/feature/user/ui/ProfileView.swift @@ -9,8 +9,11 @@ import SwiftUI struct ProfileView: View { @StateObject private var userViewModel = UserViewModel() + @EnvironmentObject var authViewModel: AuthViewModel // 🔹 전역 Auth 상태 참조 + @State private var showLogoutModal = false // 🔹 로그아웃 모달 상태 var body: some View { + ZStack{ VStack(alignment: .leading, spacing: 24) { // MARK: - Profile Header @@ -44,7 +47,13 @@ struct ProfileView: View { SettingRow(icon: "notification", title: "알림") SettingNavigationRow(icon: "receipt", title: "예치금 히스토리", destination: DepositHistoryView()) SettingNavigationRow(icon: "bag", title: "주문 내역", destination: OrderListView()) - SettingRow(icon: "logout", title: "로그아웃") + // SettingRow(icon: "logout", title: "로그아웃") + // 🔹 로그아웃 버튼 + Button { + showLogoutModal = true + } label: { + SettingRow(icon: "logout", title: "로그아웃") + } } .padding(3) .background(Color.Light) @@ -59,6 +68,33 @@ struct ProfileView: View { .onAppear { Task { await userViewModel.loadUserInfo() } } + + // 🔹 AlertModal (ZStack 위에 오버레이로 표시) + if showLogoutModal { + Color.black.opacity(0.3) + .ignoresSafeArea() + .transition(.opacity) + + AlertModal( + title: "로그아웃", + message: "정말 로그아웃 하시겠습니까?", + primaryButtonTitle: "로그아웃", + primaryAction: { + authViewModel.logout() + showLogoutModal = false + }, + secondaryButtonTitle: "취소", + secondaryAction: { + showLogoutModal = false + }, + buttonLayout: .horizontal + ) + .transition(.scale) + .zIndex(1) + } + + } + .animation(.easeInOut, value: showLogoutModal) } } diff --git a/StockMate/StockMate/app/navigation/AppNavHost.swift b/StockMate/StockMate/app/navigation/AppNavHost.swift index c55ea0a..ffb9779 100644 --- a/StockMate/StockMate/app/navigation/AppNavHost.swift +++ b/StockMate/StockMate/app/navigation/AppNavHost.swift @@ -24,6 +24,7 @@ struct AppNavHost: View { RegisterView() case .authenticated: MainTabView() + .environmentObject(authViewModel) .environmentObject(partStore) } } From c8419f93c06fa364f9351869ff386b4ad537ccc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Thu, 6 Nov 2025 10:59:08 +0900 Subject: [PATCH 58/85] =?UTF-8?q?[FEAT]=20=EC=98=88=EC=B9=98=EA=B8=88=20?= =?UTF-8?q?=ED=9E=88=EC=8A=A4=ED=86=A0=EB=A6=AC=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=9E=84=EC=8B=9C=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/feature/auth/ui/HomeView.swift | 2 +- .../feature/dashboard/data/HistoryApi.swift | 8 ++ .../dashboard/ui/DepositHistoryView.swift | 135 ------------------ .../ui/TransactionTypeListView.swift | 108 ++++++++++++++ .../viewmodel/HistoryViewModel.swift | 31 ++-- .../app/feature/orders/ui/ReceiptView.swift | 20 ++- .../app/feature/user/ui/ProfileView.swift | 3 +- 7 files changed, 160 insertions(+), 147 deletions(-) delete mode 100644 StockMate/StockMate/app/feature/dashboard/ui/DepositHistoryView.swift create mode 100644 StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift diff --git a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift index 615d4f4..fa9e0b8 100644 --- a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift @@ -224,7 +224,7 @@ private func iconForCategory(_ name: String) -> String { case "하체/바디": return "spanner" case "내장/외장": return "chair" case "기타소모품": return "package" - default: return "questionmark" + default: return "uploadprogress" } } diff --git a/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift b/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift index 9c63204..9da72bc 100644 --- a/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift +++ b/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift @@ -84,9 +84,17 @@ struct PaymentTransactionItem: Decodable, Identifiable { let transactionTime: String? // ✅ null 허용 let totalAmount: Int let orderId: Int? // ✅ null 허용 + let orderItems: [OrderItemHistory]? let balance: Int } +struct OrderItemHistory: Decodable { + let id: Int + let name: String + let image: String + let korName: String + let categoryName: String +} // MARK: - API diff --git a/StockMate/StockMate/app/feature/dashboard/ui/DepositHistoryView.swift b/StockMate/StockMate/app/feature/dashboard/ui/DepositHistoryView.swift deleted file mode 100644 index 97f8c6c..0000000 --- a/StockMate/StockMate/app/feature/dashboard/ui/DepositHistoryView.swift +++ /dev/null @@ -1,135 +0,0 @@ -// -// DepositHistoryView.swift -// StockMate -// -// Created by Admin on 11/3/25. -// - -import SwiftUI - -struct DepositHistoryView: View { - @StateObject private var viewModel = HistoryViewModel() - - var body: some View { - VStack { - // 본문 스크롤 - ScrollView { - LazyVStack(spacing: 12) { - ForEach(viewModel.transactions) { item in - DepositHistoryRow(item: item) - .onAppear { - Task { - await viewModel.loadMoreTransactionsIfNeeded(currentItem: item) - } - } - } - - if viewModel.isTransactionLoading { - ProgressView() - .padding() - } - } - .padding(.horizontal) - } - .background(Color.Light) - .navigationTitle("예치금 히스토리") - .navigationBarTitleDisplayMode(.inline) - .refreshable { - await viewModel.fetchPaymentTransactions() - } - } - .task { - await viewModel.fetchPaymentTransactions() - } - } -} - -// MARK: - 개별 거래 Row -struct DepositHistoryRow: View { - let item: PaymentTransactionItem - - var isCharge: Bool { item.transactionType == "CHARGE" } - - var body: some View { - HStack(alignment: .center, spacing: 13) { - // 아이콘 - Image(isCharge ? "exchange" : "bag") - .frame(width: 64, height: 64) - .foregroundColor(isCharge ? .Primary : .gray) - - VStack(alignment: .leading, spacing: 4) { - Text(isCharge ? "예치금 충전" : "실린더 어셈블리-브레이크 마스터 외 3개") - .font(.subheadline) - .fontWeight(.medium) - .foregroundColor(.black) - - Text(formatDate(item.transactionTime ?? "")) - .font(.caption) - .foregroundColor(.gray) - - Text(formatAmount(item.totalAmount, isCharge: isCharge)) - .font(.subheadline) - .foregroundColor(isCharge ? .Primary : .red) - } - Spacer() - - Image(systemName: "chevron.right") - .font(.system(size: 14, weight: .medium)) - .foregroundColor(.gray) - } - .padding() - .background(Color.white) - .cornerRadius(12) - .shadow(color: Color.black.opacity(0.05), radius: 2, x: 0, y: 1) - } - - // MARK: - Helper - private func formatAmount(_ amount: Int, isCharge: Bool) -> String { - let formatter = NumberFormatter() - formatter.numberStyle = .decimal - let formatted = formatter.string(from: NSNumber(value: amount)) ?? "\(amount)" - return (isCharge ? "+ " : "- ") + formatted + "원" - } - - private func formatDate(_ dateString: String) -> String { - // 서버에서 "2025.10.29 17:32:39" 형식이면 그대로 반환 - // 혹은 ISO8601이면 변환 필요 - if dateString.contains(".") { - return dateString - } else { - let formatter = ISO8601DateFormatter() - if let date = formatter.date(from: dateString) { - let displayFormatter = DateFormatter() - displayFormatter.dateFormat = "yyyy.MM.dd HH:mm:ss" - return displayFormatter.string(from: date) - } - return dateString - } - } -} - -// MARK: - Dummy Preview -#Preview { - VStack(spacing: 16) { - DepositHistoryRow( - item: PaymentTransactionItem( - transactionType: "CHARGE", - transactionTime: "2025-11-03T09:12:45", - totalAmount: 50000, - orderId: nil, - balance: 50000 - ) - ) - DepositHistoryRow( - item: PaymentTransactionItem( - transactionType: "PAY", - transactionTime: "2025-11-03T14:34:35.608713", - totalAmount: 49720, - orderId: 61, - balance: 4954617 - ) - ) - } - .padding() - .background(Color.Light) -} diff --git a/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift b/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift new file mode 100644 index 0000000..4407d1a --- /dev/null +++ b/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift @@ -0,0 +1,108 @@ +// +// TransactionTypeListView.swift +// StockMate +// +// Created by Admin on 11/6/25. +// + +import SwiftUI + +struct TransactionTypeListView: View { + @StateObject private var viewModel = HistoryViewModel() + + var body: some View { + NavigationView { + List(viewModel.transactions, id: \.id) { item in + HStack { + // 대표 이미지 + AsyncImage(url: URL(string: item.orderItems?.first?.image ?? "")) { image in + image.resizable().scaledToFit() + } placeholder: { + Image("exchange") + .foregroundColor(Color.Primary) + } + .frame(width: 64, height: 64) + .cornerRadius(10) + + + VStack (alignment: .leading, spacing: 3){ + + // ✅ PAY/CHARGE 별 상세 표시 (부품 이름/ 예치금 충전) + if item.transactionType == "PAY", + let orderItems = item.orderItems, + !orderItems.isEmpty { + let firstName = orderItems.first?.korName ?? "-" + let extraCount = orderItems.count - 1 + let displayText = extraCount > 0 + ? "\(firstName) 외 \(extraCount)개" + : firstName + + Text(displayText) + .font(.subheadline) + .foregroundColor(.primary) + } else if item.transactionType == "CHARGE" { + Text("예치금 충전") + .font(.subheadline) + .foregroundColor(.blue) + } + + // ✅ 거래 시간 (포맷 적용) + if let time = item.transactionTime { + Text(formattedDate(time)) + .font(.subheadline) + .foregroundColor(.gray) + } else { + Text("-") + .font(.subheadline) + .foregroundColor(.gray) + } + + + // ✅ 거래 금액 표시 (CHARGE: +파란색 / PAY: -빨간색) + let sign = item.transactionType == "PAY" ? "-" : "+" + let color: Color = item.transactionType == "PAY" ? .Danger : .Primary + + Text("\(sign) \(formatPrice(item.totalAmount))") + .font(.headline) + .foregroundColor(color) + .frame(alignment: .trailing) + + } + Spacer() + + if item.transactionType == "PAY" { + NavigationLink(destination: ReceiptView(orderId: item.orderId ?? 1)) { + Image(systemName: "chevron.right") + .font(.caption) + .foregroundColor(.gray) + } + } + + } + .padding(.vertical, 4) + .task { + // ✅ 스크롤 시 끝 근처에서 다음 페이지 로드 + await viewModel.loadMoreTransactionsIfNeeded(currentItem: item) + } + + } + .background(Color.Light) + .navigationTitle("예치금 히스토리") + .navigationBarTitleDisplayMode(.inline) + .overlay { + if viewModel.isTransactionLoading && viewModel.transactions.isEmpty { + ProgressView("불러오는 중...") + } + } + .task { + if viewModel.transactions.isEmpty { + await viewModel.fetchPaymentTransactions() + } + } + } + } +} + +#Preview { + TransactionTypeListView() +} diff --git a/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift b/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift index e01b262..b1848de 100644 --- a/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift +++ b/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift @@ -95,13 +95,26 @@ final class HistoryViewModel: ObservableObject { } // MARK: - ✅ 무한 스크롤 (예치금 내역) - func loadMoreTransactionsIfNeeded(currentItem item: PaymentTransactionItem?) async { - guard let item = item else { return } - let thresholdIndex = transactions.index(transactions.endIndex, offsetBy: -5) - if transactions.firstIndex(where: { $0.id == item.id }) == thresholdIndex { - if transactionPage + 1 < transactionTotalPages { - await fetchPaymentTransactions(page: transactionPage + 1) - } - } - } + func loadMoreTransactionsIfNeeded(currentItem item: PaymentTransactionItem?) async { + guard let item = item else { return } + guard !isTransactionLoading else { return } // 이미 로딩 중이면 중복 호출 방지 + guard transactionPage + 1 < transactionTotalPages else { return } // 더 불러올 페이지 없으면 종료 + + // 안전하게 threshold 계산 + let thresholdIndex = max(transactions.count - 5, 0) + if let currentIndex = transactions.firstIndex(where: { $0.id == item.id }), + currentIndex >= thresholdIndex { + await fetchPaymentTransactions(page: transactionPage + 1) + } + } + +// func loadMoreTransactionsIfNeeded(currentItem item: PaymentTransactionItem?) async { +// guard let item = item else { return } +// let thresholdIndex = transactions.index(transactions.endIndex, offsetBy: -5) +// if transactions.firstIndex(where: { $0.id == item.id }) == thresholdIndex { +// if transactionPage + 1 < transactionTotalPages { +// await fetchPaymentTransactions(page: transactionPage + 1) +// } +// } +// } } diff --git a/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift b/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift index 9e690f1..91b100a 100644 --- a/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift @@ -209,10 +209,27 @@ struct ReceiptView: View { // } //} +//func formattedDate(_ timestamp: String) -> String { +// let inputFormatter = DateFormatter() +// inputFormatter.locale = Locale(identifier: "ko_KR") +// inputFormatter.timeZone = TimeZone(abbreviation: "UTC") +// inputFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS" +// +// guard let date = inputFormatter.date(from: timestamp) else { +// return timestamp +// } +// +// let outputFormatter = DateFormatter() +// outputFormatter.locale = Locale(identifier: "ko_KR") +// outputFormatter.timeZone = TimeZone.current +// outputFormatter.dateFormat = "yyyy/MM/dd HH:mm:ss" +// +// return outputFormatter.string(from: date) +//} func formattedDate(_ timestamp: String) -> String { let inputFormatter = DateFormatter() inputFormatter.locale = Locale(identifier: "ko_KR") - inputFormatter.timeZone = TimeZone(abbreviation: "UTC") + inputFormatter.timeZone = TimeZone.current // ✅ 실제 한국 시간 기준 inputFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS" guard let date = inputFormatter.date(from: timestamp) else { @@ -227,6 +244,7 @@ func formattedDate(_ timestamp: String) -> String { return outputFormatter.string(from: date) } + func formattedApprovalNumber(_ timestamp: String) -> String { let inputFormatter = DateFormatter() inputFormatter.locale = Locale(identifier: "ko_KR") diff --git a/StockMate/StockMate/app/feature/user/ui/ProfileView.swift b/StockMate/StockMate/app/feature/user/ui/ProfileView.swift index 55d236c..6892def 100644 --- a/StockMate/StockMate/app/feature/user/ui/ProfileView.swift +++ b/StockMate/StockMate/app/feature/user/ui/ProfileView.swift @@ -45,7 +45,8 @@ struct ProfileView: View { SettingRow(icon: "lock", title: "비밀번호 변경") SettingRow(icon: "notification", title: "알림") - SettingNavigationRow(icon: "receipt", title: "예치금 히스토리", destination: DepositHistoryView()) + SettingNavigationRow(icon: "receipt", title: "예치금 히스토리", destination: TransactionTypeListView()) +// SettingNavigationRow(icon: "receipt", title: "예치금 히스토리", destination: PaymentTransactionView()) SettingNavigationRow(icon: "bag", title: "주문 내역", destination: OrderListView()) // SettingRow(icon: "logout", title: "로그아웃") // 🔹 로그아웃 버튼 From 5bcbaf8a5ea8ae5013fd6666d72c27eb82edce0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Thu, 6 Nov 2025 15:50:49 +0900 Subject: [PATCH 59/85] =?UTF-8?q?[FEAT]=20=EC=98=88=EC=B9=98=EA=B8=88=20?= =?UTF-8?q?=ED=9E=88=EC=8A=A4=ED=86=A0=EB=A6=AC=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/dashboard/data/HistoryApi.swift | 9 +- .../ui/TransactionTypeListView.swift | 285 +++++++++++++----- .../viewmodel/HistoryViewModel.swift | 19 +- 3 files changed, 220 insertions(+), 93 deletions(-) diff --git a/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift b/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift index 9da72bc..97f41d9 100644 --- a/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift +++ b/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift @@ -78,16 +78,17 @@ struct PaymentTransactionPageData: Decodable { let first: Bool } -struct PaymentTransactionItem: Decodable, Identifiable { - var id: UUID { UUID() } // 서버에서 id 제공 안하므로 로컬 생성 +struct PaymentTransactionItem: Decodable { + let transactionId: Int let transactionType: String // "CHARGE" or "PAY" - let transactionTime: String? // ✅ null 허용 + let transactionTime: String? let totalAmount: Int - let orderId: Int? // ✅ null 허용 + let orderId: Int? let orderItems: [OrderItemHistory]? let balance: Int } + struct OrderItemHistory: Decodable { let id: Int let name: String diff --git a/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift b/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift index 4407d1a..d004a72 100644 --- a/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift +++ b/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift @@ -1,3 +1,118 @@ +//// +//// TransactionTypeListView.swift +//// StockMate +//// +//// Created by Admin on 11/6/25. +//// +// +//import SwiftUI +// +//struct TransactionTypeListView: View { +// @StateObject private var viewModel = HistoryViewModel() +// +// var body: some View { +// NavigationView { +// List(viewModel.transactions, id: \.id) { item in +// HStack { +// // 대표 이미지 +// AsyncImage(url: URL(string: item.orderItems?.first?.image ?? "")) { image in +// image.resizable().scaledToFit() +// } placeholder: { +// Image("exchange") +// .foregroundColor(Color.Primary) +// } +// .frame(width: 64, height: 64) +// .cornerRadius(10) +// +// +// VStack (alignment: .leading, spacing: 3){ +// +// // ✅ PAY/CHARGE 별 상세 표시 (부품 이름/ 예치금 충전) +// if item.transactionType == "PAY", +// let orderItems = item.orderItems, +// !orderItems.isEmpty { +// NavigationLink(destination: ReceiptView(orderId: item.orderId ?? 1)) { +// let firstName = orderItems.first?.korName ?? "-" +// let extraCount = orderItems.count - 1 +// let displayText = extraCount > 0 +// ? "\(firstName) 외 \(extraCount)개" +// : firstName +// +// VStack(alignment: .leading){ +// Text(displayText) +// .font(.subheadline) +// .foregroundColor(.primary) +// +// // ✅ 거래 시간 (포맷 적용) +// if let time = item.transactionTime { +// Text(formattedDate(time)) +// .font(.subheadline) +// .foregroundColor(.gray) +// } else { +// Text("-") +// .font(.subheadline) +// .foregroundColor(.gray) +// } +// +// Text("- \(formatPrice(item.totalAmount))") +// .font(.headline) +// .foregroundColor(.Danger) +// .frame(alignment: .trailing) +// } +// } +// +// } else if item.transactionType == "CHARGE" { +// Text("예치금 충전") +// .font(.subheadline) +// .foregroundColor(.blue) +// +// // ✅ 거래 시간 (포맷 적용) +// if let time = item.transactionTime { +// Text(formattedDate(time)) +// .font(.subheadline) +// .foregroundColor(.gray) +// } else { +// Text("-") +// .font(.subheadline) +// .foregroundColor(.gray) +// } +// +// Text("+ \(formatPrice(item.totalAmount))") +// .font(.headline) +// .foregroundColor(.Primary) +// .frame(alignment: .trailing) +// } +// +// } +// +// } +// .padding(.vertical, 4) +// .task { +// // ✅ 스크롤 시 끝 근처에서 다음 페이지 로드 +// await viewModel.loadMoreTransactionsIfNeeded(currentItem: item) +// } +// } +// .background(Color.Light) +// .navigationTitle("예치금 히스토리") +// .navigationBarTitleDisplayMode(.inline) +// .overlay { +// if viewModel.isTransactionLoading && viewModel.transactions.isEmpty { +// ProgressView("불러오는 중...") +// } +// } +// .task { +// if viewModel.transactions.isEmpty { +// await viewModel.fetchPaymentTransactions() +// } +// } +// } +// .background(Color.Light) +// } +//} +// +////#Preview { +//// TransactionTypeListView() +////} // // TransactionTypeListView.swift // StockMate @@ -11,82 +126,22 @@ struct TransactionTypeListView: View { @StateObject private var viewModel = HistoryViewModel() var body: some View { - NavigationView { - List(viewModel.transactions, id: \.id) { item in - HStack { - // 대표 이미지 - AsyncImage(url: URL(string: item.orderItems?.first?.image ?? "")) { image in - image.resizable().scaledToFit() - } placeholder: { - Image("exchange") - .foregroundColor(Color.Primary) - } - .frame(width: 64, height: 64) - .cornerRadius(10) - - - VStack (alignment: .leading, spacing: 3){ - - // ✅ PAY/CHARGE 별 상세 표시 (부품 이름/ 예치금 충전) - if item.transactionType == "PAY", - let orderItems = item.orderItems, - !orderItems.isEmpty { - let firstName = orderItems.first?.korName ?? "-" - let extraCount = orderItems.count - 1 - let displayText = extraCount > 0 - ? "\(firstName) 외 \(extraCount)개" - : firstName - - Text(displayText) - .font(.subheadline) - .foregroundColor(.primary) - } else if item.transactionType == "CHARGE" { - Text("예치금 충전") - .font(.subheadline) - .foregroundColor(.blue) - } - - // ✅ 거래 시간 (포맷 적용) - if let time = item.transactionTime { - Text(formattedDate(time)) - .font(.subheadline) - .foregroundColor(.gray) - } else { - Text("-") - .font(.subheadline) - .foregroundColor(.gray) - } - - - // ✅ 거래 금액 표시 (CHARGE: +파란색 / PAY: -빨간색) - let sign = item.transactionType == "PAY" ? "-" : "+" - let color: Color = item.transactionType == "PAY" ? .Danger : .Primary - - Text("\(sign) \(formatPrice(item.totalAmount))") - .font(.headline) - .foregroundColor(color) - .frame(alignment: .trailing) - - } - Spacer() - - if item.transactionType == "PAY" { - NavigationLink(destination: ReceiptView(orderId: item.orderId ?? 1)) { - Image(systemName: "chevron.right") - .font(.caption) - .foregroundColor(.gray) - } + NavigationStack { + ScrollView { + LazyVStack(spacing: 12) { + ForEach(viewModel.transactions, id: \.transactionId) { item in + TransactionCard(item: item) + .onAppear { + Task { + await viewModel.loadMoreTransactionsIfNeeded(currentItem: item) + } + } } - } - .padding(.vertical, 4) - .task { - // ✅ 스크롤 시 끝 근처에서 다음 페이지 로드 - await viewModel.loadMoreTransactionsIfNeeded(currentItem: item) - } - + .padding(.horizontal) + .padding(.top, 10) } - .background(Color.Light) + .background(Color.Light.ignoresSafeArea()) .navigationTitle("예치금 히스토리") .navigationBarTitleDisplayMode(.inline) .overlay { @@ -100,9 +155,89 @@ struct TransactionTypeListView: View { } } } + .background(Color.Light) } } -#Preview { - TransactionTypeListView() +struct TransactionCard: View { + let item: PaymentTransactionItem + + var body: some View { + VStack(alignment: .leading, spacing: 8) { + HStack(alignment: .top, spacing: 12) { + // 대표 이미지 + AsyncImage( + url: URL(string: item.orderItems?.first?.image ?? "") + ) { image in + image.resizable().scaledToFit() + } placeholder: { + Image("exchange") + .foregroundColor(Color.Primary) + } + .frame(width: 64, height: 64) + .cornerRadius(10) + + VStack(alignment: .leading, spacing: 4) { + if item.transactionType == "PAY", + let orderItems = item.orderItems, + !orderItems.isEmpty { + + NavigationLink( + destination: ReceiptView(orderId: item.orderId ?? 1) + ) { + let firstName = orderItems.first?.korName ?? "-" + let extraCount = orderItems.count - 1 + let displayText = extraCount > 0 + ? "\(firstName) 외 \(extraCount)개" + : firstName + + VStack(alignment: .leading, spacing: 4) { + Text(displayText) + .font(.subheadline) + .foregroundColor(.primary) + + Text( + item.transactionTime != nil ? formattedDate( + item.transactionTime! + ) : "-" + ) + .font(.caption) + .foregroundColor(.gray) + + Text("- \(formatPrice(item.totalAmount))") + .font(.headline) + .foregroundColor(.Danger) + } + } + .buttonStyle(.plain) + + } else if item.transactionType == "CHARGE" { + VStack(alignment: .leading, spacing: 4) { + Text("예치금 충전") + .font(.subheadline) + .foregroundColor(.blue) + + Text( + item.transactionTime != nil ? formattedDate( + item.transactionTime! + ) : "-" + ) + .font(.caption) + .foregroundColor(.gray) + + Text("+ \(formatPrice(item.totalAmount))") + .font(.headline) + .foregroundColor(.Primary) + } + } + } + Spacer() + } + } + .padding() + .background(Color.white) + .cornerRadius(16) + .shadow(color: .gray.opacity(0.2), radius: 4, x: 0, y: 2) + } } + diff --git a/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift b/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift index b1848de..5e1f69b 100644 --- a/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift +++ b/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift @@ -97,24 +97,15 @@ final class HistoryViewModel: ObservableObject { // MARK: - ✅ 무한 스크롤 (예치금 내역) func loadMoreTransactionsIfNeeded(currentItem item: PaymentTransactionItem?) async { guard let item = item else { return } - guard !isTransactionLoading else { return } // 이미 로딩 중이면 중복 호출 방지 - guard transactionPage + 1 < transactionTotalPages else { return } // 더 불러올 페이지 없으면 종료 + guard !isTransactionLoading else { return } // 중복 로드 방지 + guard transactionPage + 1 < transactionTotalPages else { return } // 마지막 페이지 방지 - // 안전하게 threshold 계산 + // ✅ 안전한 threshold 계산 let thresholdIndex = max(transactions.count - 5, 0) - if let currentIndex = transactions.firstIndex(where: { $0.id == item.id }), + if let currentIndex = transactions.firstIndex(where: { $0.transactionId == item.transactionId }), currentIndex >= thresholdIndex { await fetchPaymentTransactions(page: transactionPage + 1) } } - -// func loadMoreTransactionsIfNeeded(currentItem item: PaymentTransactionItem?) async { -// guard let item = item else { return } -// let thresholdIndex = transactions.index(transactions.endIndex, offsetBy: -5) -// if transactions.firstIndex(where: { $0.id == item.id }) == thresholdIndex { -// if transactionPage + 1 < transactionTotalPages { -// await fetchPaymentTransactions(page: transactionPage + 1) -// } -// } -// } + } From 9db5ff85c52595a0246ffb654e33b7cfb03b4d7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Thu, 6 Nov 2025 19:35:38 +0900 Subject: [PATCH 60/85] =?UTF-8?q?[FEAT]=20=EC=98=88=EC=B9=98=EA=B8=88=20?= =?UTF-8?q?=ED=9E=88=EC=8A=A4=ED=86=A0=EB=A6=AC=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/TransactionTypeListView.swift | 196 ++++-------------- 1 file changed, 37 insertions(+), 159 deletions(-) diff --git a/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift b/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift index d004a72..79ec4dc 100644 --- a/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift +++ b/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift @@ -1,118 +1,3 @@ -//// -//// TransactionTypeListView.swift -//// StockMate -//// -//// Created by Admin on 11/6/25. -//// -// -//import SwiftUI -// -//struct TransactionTypeListView: View { -// @StateObject private var viewModel = HistoryViewModel() -// -// var body: some View { -// NavigationView { -// List(viewModel.transactions, id: \.id) { item in -// HStack { -// // 대표 이미지 -// AsyncImage(url: URL(string: item.orderItems?.first?.image ?? "")) { image in -// image.resizable().scaledToFit() -// } placeholder: { -// Image("exchange") -// .foregroundColor(Color.Primary) -// } -// .frame(width: 64, height: 64) -// .cornerRadius(10) -// -// -// VStack (alignment: .leading, spacing: 3){ -// -// // ✅ PAY/CHARGE 별 상세 표시 (부품 이름/ 예치금 충전) -// if item.transactionType == "PAY", -// let orderItems = item.orderItems, -// !orderItems.isEmpty { -// NavigationLink(destination: ReceiptView(orderId: item.orderId ?? 1)) { -// let firstName = orderItems.first?.korName ?? "-" -// let extraCount = orderItems.count - 1 -// let displayText = extraCount > 0 -// ? "\(firstName) 외 \(extraCount)개" -// : firstName -// -// VStack(alignment: .leading){ -// Text(displayText) -// .font(.subheadline) -// .foregroundColor(.primary) -// -// // ✅ 거래 시간 (포맷 적용) -// if let time = item.transactionTime { -// Text(formattedDate(time)) -// .font(.subheadline) -// .foregroundColor(.gray) -// } else { -// Text("-") -// .font(.subheadline) -// .foregroundColor(.gray) -// } -// -// Text("- \(formatPrice(item.totalAmount))") -// .font(.headline) -// .foregroundColor(.Danger) -// .frame(alignment: .trailing) -// } -// } -// -// } else if item.transactionType == "CHARGE" { -// Text("예치금 충전") -// .font(.subheadline) -// .foregroundColor(.blue) -// -// // ✅ 거래 시간 (포맷 적용) -// if let time = item.transactionTime { -// Text(formattedDate(time)) -// .font(.subheadline) -// .foregroundColor(.gray) -// } else { -// Text("-") -// .font(.subheadline) -// .foregroundColor(.gray) -// } -// -// Text("+ \(formatPrice(item.totalAmount))") -// .font(.headline) -// .foregroundColor(.Primary) -// .frame(alignment: .trailing) -// } -// -// } -// -// } -// .padding(.vertical, 4) -// .task { -// // ✅ 스크롤 시 끝 근처에서 다음 페이지 로드 -// await viewModel.loadMoreTransactionsIfNeeded(currentItem: item) -// } -// } -// .background(Color.Light) -// .navigationTitle("예치금 히스토리") -// .navigationBarTitleDisplayMode(.inline) -// .overlay { -// if viewModel.isTransactionLoading && viewModel.transactions.isEmpty { -// ProgressView("불러오는 중...") -// } -// } -// .task { -// if viewModel.transactions.isEmpty { -// await viewModel.fetchPaymentTransactions() -// } -// } -// } -// .background(Color.Light) -// } -//} -// -////#Preview { -//// TransactionTypeListView() -////} // // TransactionTypeListView.swift // StockMate @@ -164,7 +49,7 @@ struct TransactionCard: View { var body: some View { VStack(alignment: .leading, spacing: 8) { - HStack(alignment: .top, spacing: 12) { + HStack(alignment: .center, spacing: 12) { // 대표 이미지 AsyncImage( url: URL(string: item.orderItems?.first?.image ?? "") @@ -177,67 +62,60 @@ struct TransactionCard: View { .frame(width: 64, height: 64) .cornerRadius(10) - VStack(alignment: .leading, spacing: 4) { + VStack(alignment: .leading, spacing: 5) { if item.transactionType == "PAY", let orderItems = item.orderItems, !orderItems.isEmpty { - - NavigationLink( - destination: ReceiptView(orderId: item.orderId ?? 1) - ) { let firstName = orderItems.first?.korName ?? "-" let extraCount = orderItems.count - 1 let displayText = extraCount > 0 ? "\(firstName) 외 \(extraCount)개" : firstName - - VStack(alignment: .leading, spacing: 4) { - Text(displayText) - .font(.subheadline) - .foregroundColor(.primary) - - Text( - item.transactionTime != nil ? formattedDate( - item.transactionTime! - ) : "-" - ) - .font(.caption) - .foregroundColor(.gray) - - Text("- \(formatPrice(item.totalAmount))") - .font(.headline) - .foregroundColor(.Danger) - } - } - .buttonStyle(.plain) + Text(displayText) + .font(.system(size: 14, weight: .bold)) + .foregroundColor(.black) } else if item.transactionType == "CHARGE" { - VStack(alignment: .leading, spacing: 4) { Text("예치금 충전") - .font(.subheadline) - .foregroundColor(.blue) - - Text( - item.transactionTime != nil ? formattedDate( - item.transactionTime! - ) : "-" - ) - .font(.caption) - .foregroundColor(.gray) - - Text("+ \(formatPrice(item.totalAmount))") - .font(.headline) - .foregroundColor(.Primary) - } + .font(.system(size: 14, weight: .bold)) + .foregroundColor(.black) } + // 날짜 + Text( + item.transactionTime != nil ? formattedDate( + item.transactionTime! + ) : "-" + ) + .font(.system(size: 13, weight: .regular)) + .foregroundColor(.textGray1) + + // ✅ 금액 표시 (PAY/CHARGE 구분) + let isPay = item.transactionType == "PAY" + let sign = isPay ? "-" : "+" + let color: Color = isPay ? .Danger : .Primary + + Text("\(sign) \(formatPrice(item.totalAmount))") + .font(.system(size: 13, weight: .bold)) + .foregroundColor(color) + } + .frame(height: 60, alignment: .top) Spacer() + // ✅ PAY일 때만 꺾새 표시 + if item.transactionType == "PAY" { + NavigationLink(destination: ReceiptView(orderId: item.orderId ?? 1)) { + Image(systemName: "chevron.right") + .font(.system(size: 15, weight: .medium)) + .foregroundColor(.gray) + } + .buttonStyle(.plain) + } } } .padding() .background(Color.white) - .cornerRadius(16) - .shadow(color: .gray.opacity(0.2), radius: 4, x: 0, y: 2) + .cornerRadius(10) + .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) } } From d81387162ecc9ee6adacb16b543d91b982afc102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Fri, 7 Nov 2025 10:35:40 +0900 Subject: [PATCH 61/85] =?UTF-8?q?[REFAC]=20stausText=20=EB=B0=8F=20?= =?UTF-8?q?=EB=A7=88=EC=9D=B4=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift | 2 +- StockMate/StockMate/app/feature/user/ui/ProfileView.swift | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift index 4ab38d1..8ff3d78 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift @@ -366,7 +366,7 @@ func statusText(_ status: String) -> String { // 본사에서 "결제 완료"에 대해서 주문을 반려 or 승인 case "REJECTED": return "결제 실패" // 주문 반려 - case "APPROVAL_ORDER": return "출고 대기" // 주문 승인 + case "APPROVAL_ORDER": return "주문 승인" // 주문 승인 // 창고관리자가 "주문 승인"에 대해서 송장(인보이스)를 뽑으면 출고 대기 case "PENDING_SHIPPING": return "출고 대기" // 출고 대기 diff --git a/StockMate/StockMate/app/feature/user/ui/ProfileView.swift b/StockMate/StockMate/app/feature/user/ui/ProfileView.swift index 6892def..43d8ff3 100644 --- a/StockMate/StockMate/app/feature/user/ui/ProfileView.swift +++ b/StockMate/StockMate/app/feature/user/ui/ProfileView.swift @@ -42,8 +42,6 @@ struct ProfileView: View { VStack(alignment: .leading, spacing: 12) { VStack(spacing: 10) { SettingNavigationRow(icon: "user", title: "프로필 확인", destination: UserProfileView()) - - SettingRow(icon: "lock", title: "비밀번호 변경") SettingRow(icon: "notification", title: "알림") SettingNavigationRow(icon: "receipt", title: "예치금 히스토리", destination: TransactionTypeListView()) // SettingNavigationRow(icon: "receipt", title: "예치금 히스토리", destination: PaymentTransactionView()) From 5ceb482084b82fcd33a9b554b0f361077825ea03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Fri, 7 Nov 2025 12:12:45 +0900 Subject: [PATCH 62/85] =?UTF-8?q?[REFAC]=20=EC=BD=94=EB=93=9C=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/core/components/BarChartView.swift | 2 +- .../app/core/components/CartCard.swift | 6 ---- .../core/components/CustomButtonStyle.swift | 12 +++---- .../components/InventoryListSection.swift | 18 ---------- .../components/OrderRequestCardView.swift | 3 ++ .../app/feature/auth/ui/LoginView.swift | 8 ++--- .../viewmodel/HistoryViewModel.swift | 11 +++--- .../inventory/ui/OutgoingScanView.swift | 2 +- .../inventory/ui/UsedPartListSheetView.swift | 10 ++---- .../app/feature/orders/data/OrderApi.swift | 35 ------------------- .../app/feature/orders/ui/OrderInfoView.swift | 22 ------------ .../app/feature/parts/data/PartStore.swift | 11 ++++++ .../parts/ui/QRScannerViewController.swift | 2 +- .../parts/viewmodel/PartViewModel.swift | 4 +-- .../app/feature/user/ui/UserProfileView.swift | 2 +- 15 files changed, 38 insertions(+), 110 deletions(-) delete mode 100644 StockMate/StockMate/app/core/components/InventoryListSection.swift diff --git a/StockMate/StockMate/app/core/components/BarChartView.swift b/StockMate/StockMate/app/core/components/BarChartView.swift index a06f145..5fa4e35 100644 --- a/StockMate/StockMate/app/core/components/BarChartView.swift +++ b/StockMate/StockMate/app/core/components/BarChartView.swift @@ -9,7 +9,7 @@ import SwiftUI struct BarChartView: View { let values: [CGFloat] // 각 월별 비율값 (0~1) - let labels: [String] // 예: ["06", "07", "08", "09", "10"] + let labels: [String] // 예: ["10", "09", "08", "07", "06"] let amounts: [Int] // 예: [230000, 250000, 310000, 280000, 400000] @Binding var selectedMonth: String? diff --git a/StockMate/StockMate/app/core/components/CartCard.swift b/StockMate/StockMate/app/core/components/CartCard.swift index 556483f..6211f93 100644 --- a/StockMate/StockMate/app/core/components/CartCard.swift +++ b/StockMate/StockMate/app/core/components/CartCard.swift @@ -55,12 +55,6 @@ struct CartCard: View { if quantity == 0 { if let onAddToCart = onAddToCart { Button(action: onAddToCart) { -// Image(systemName: "cart.badge.plus") -// .font(.system(size: 18)) -// .foregroundColor(.Primary) -// .padding(10) -// .background(Color.Primary.opacity(0.1)) -// .clipShape(Circle()) Image("add_shopping_cart") .resizable() .scaledToFit() diff --git a/StockMate/StockMate/app/core/components/CustomButtonStyle.swift b/StockMate/StockMate/app/core/components/CustomButtonStyle.swift index fe42295..c25f344 100644 --- a/StockMate/StockMate/app/core/components/CustomButtonStyle.swift +++ b/StockMate/StockMate/app/core/components/CustomButtonStyle.swift @@ -26,10 +26,9 @@ struct CustomButtonStyle: ButtonStyle { configuration.label .frame(maxWidth: .infinity, minHeight: height) .background(color.opacity(configuration.isPressed ? 0.8 : 1)) + .clipShape(RoundedRectangle(cornerRadius: cornerRadius)) .foregroundColor(.white) - .cornerRadius(cornerRadius) .font(.system(size: fontSize, weight: fontWeight)) -// .scaleEffect(configuration.isPressed ? 0.98 : 1.0) .animation(.easeOut(duration: 0.15), value: configuration.isPressed) case .outlined(let color): @@ -37,13 +36,14 @@ struct CustomButtonStyle: ButtonStyle { .frame(maxWidth: .infinity, minHeight: height) .overlay( RoundedRectangle(cornerRadius: cornerRadius) - .stroke(color, lineWidth: 1.5) + .stroke(color, lineWidth: 1) ) .foregroundColor(color) .font(.system(size: fontSize, weight: fontWeight)) - .background(Color.white) - .cornerRadius(cornerRadius) -// .scaleEffect(configuration.isPressed ? 0.98 : 1.0) + .background( + RoundedRectangle(cornerRadius: cornerRadius) + .fill(Color.white) + ) .animation(.easeOut(duration: 0.15), value: configuration.isPressed) } } diff --git a/StockMate/StockMate/app/core/components/InventoryListSection.swift b/StockMate/StockMate/app/core/components/InventoryListSection.swift deleted file mode 100644 index c8d6c19..0000000 --- a/StockMate/StockMate/app/core/components/InventoryListSection.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// InventoryListSection.swift -// StockMate -// -// Created by Admin on 10/16/25. -// - -import SwiftUI - -struct InventoryListSection: View { - var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) - } -} - -#Preview { - InventoryListSection() -} diff --git a/StockMate/StockMate/app/core/components/OrderRequestCardView.swift b/StockMate/StockMate/app/core/components/OrderRequestCardView.swift index a328a43..f2fa6ab 100644 --- a/StockMate/StockMate/app/core/components/OrderRequestCardView.swift +++ b/StockMate/StockMate/app/core/components/OrderRequestCardView.swift @@ -24,6 +24,7 @@ struct OrderRequestCardView: View { Divider().frame(height: 0.2).background(Color.textGray2) HStack(alignment: .center, spacing: 12) { + // 부품 이미지 AsyncImage(url: URL(string: item.image)) { image in image.resizable().scaledToFit() } placeholder: { @@ -32,6 +33,7 @@ struct OrderRequestCardView: View { .frame(width: 64, height: 64) .cornerRadius(10) + // 이름 및 정보 VStack(alignment: .leading, spacing: 6) { Text(item.korName) .font(.system(size: 14, weight: .bold)) @@ -49,6 +51,7 @@ struct OrderRequestCardView: View { Spacer() + // 수량 컨트롤러 // 🪄 수량에 따른 3단계 분기 if quantity == 0 { Button(action: onAddToCart) { diff --git a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift index 72b16ad..4f1da94 100644 --- a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift @@ -119,10 +119,10 @@ struct LoginView: View { // MARK: - 유효성 검사 함수 private func isValidForm() -> Bool { -// emailError = isValidEmail(authViewModel.email) ? nil : "이메일 형식을 확인해주세요" -// pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" -// return emailError == nil && pwError == nil - return true + emailError = isValidEmail(authViewModel.email) ? nil : "이메일 형식을 확인해주세요" + pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" + return emailError == nil && pwError == nil +// return true } } diff --git a/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift b/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift index 5e1f69b..88c9a68 100644 --- a/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift +++ b/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift @@ -59,11 +59,11 @@ final class HistoryViewModel: ObservableObject { /// ✅ 다음 페이지 로드 (무한 스크롤 등) func loadMoreIfNeeded(currentItem item: HistoryItem?) async { guard let item = item else { return } - let thresholdIndex = histories.index(histories.endIndex, offsetBy: -5) - if histories.firstIndex(where: { $0.id == item.id }) == thresholdIndex { - if currentPage + 1 < totalPages { - await fetchInOutHistory(page: currentPage + 1) - } + let threshold = max(histories.count - 5, 0) + if let currentIndex = histories.firstIndex(where: { $0.id == item.id }), + currentIndex >= threshold, + currentPage + 1 < totalPages { + await fetchInOutHistory(page: currentPage + 1) } } @@ -107,5 +107,4 @@ final class HistoryViewModel: ObservableObject { await fetchPaymentTransactions(page: transactionPage + 1) } } - } diff --git a/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift b/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift index 25d8f04..af8bb03 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift @@ -154,7 +154,7 @@ struct OutgoingScanView: View { } // ✅ 부품 상세 조회 API 호출 - await partViewModel.fetchPartDetail(partId: partId) + await partViewModel.fetchPartDetail(partIds: partId) // ✅ 결과 출력 await MainActor.run { diff --git a/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift b/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift index 5b4f73c..f644a0a 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift @@ -56,9 +56,7 @@ struct UsedPartListSheetView: View { .frame(maxWidth: .infinity, minHeight: 200) } else { LazyVStack { - ForEach( - $partStore.parts - ) { $part in // ✅ 바인딩으로 변경 ($ 붙임) + ForEach($partStore.parts) { $part in // ✅ 바인딩으로 변경 ($ 붙임) VStack(alignment: .leading, spacing: 6) { Text(part.categoryName) .font(.system(size: 12, weight: .semibold)) @@ -100,9 +98,7 @@ struct UsedPartListSheetView: View { // ✅ 수량 조절 버튼 (디자인 개선) HStack(spacing: 10) { Button { - if part.quantity > 1 { - part.quantity -= 1 - } + partStore.decreaseQuantityOrRemove(for: part) } label: { Image(systemName: "minus") .font(.system(size: 14, weight: .regular)) @@ -113,7 +109,7 @@ struct UsedPartListSheetView: View { .font(.system(size: 15, weight: .medium)) .frame(width: 20) Button { - part.quantity += 1 + partStore.increaseQuantity(for: part) } label: { Image(systemName: "plus") .font(.system(size: 14, weight: .regular)) diff --git a/StockMate/StockMate/app/feature/orders/data/OrderApi.swift b/StockMate/StockMate/app/feature/orders/data/OrderApi.swift index 40b4507..bfa026e 100644 --- a/StockMate/StockMate/app/feature/orders/data/OrderApi.swift +++ b/StockMate/StockMate/app/feature/orders/data/OrderApi.swift @@ -116,14 +116,6 @@ struct ReceiveOrderRequest: Encodable { } -struct OrderSummary { - let firstPartName: String - let itemCount: Int - let totalPrice: Int - let createdAt: String -} - - // MARK: - API Call enum OrderApi { @@ -187,31 +179,4 @@ enum OrderApi { encoder: JSONParameterEncoder.default ) } - - // 주문 상세 정보 preview 데이터 가지고 오기 -// static func fetchOrderSummary(orderId: Int) async -> OrderSummary? { -// let result = await OrderRepositoryImpl().fetchOrderDetail(orderId: orderId) -// -// switch result { -// case .success(let order): // order == OrderResponseItem -// guard let first = order.orderItems.first else { return nil } -// return OrderSummary( -// firstPartName: first.partDetail.korName, -// itemCount: order.orderItems.count, -// totalPrice: order.totalPrice, -// createdAt: order.createdAt -// ) -// case .failure: -// return nil -// } -// } - static func fetchOrderSummary(orderId: Int) async -> OrderSummary? { - // 예시용 더미 데이터 - return OrderSummary( - firstPartName: "실린더 어셈블리-브레이크 마스터", - itemCount: 4, - totalPrice: 49720, - createdAt: "2025.11.03 14:34:35" - ) - } } diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift index 9c6d8f4..3310f9a 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift @@ -27,8 +27,6 @@ struct OrderInfoView: View { @State private var specificDate = Date() @State private var requestMessage: String = "" - @State private var navigateToSuccessPage = false - // ✅ 모달 관련 상태 @State private var showOrderSuccessModal = false @State private var navigateToOrderDetail = false @@ -45,15 +43,6 @@ struct OrderInfoView: View { } } } -// private var destinationView: some View { -// Group { -// if let id = orderViewModel.createdOrderId { -// OrderDetailView(orderId: id, orderViewModel: orderViewModel) -// } else { -// EmptyView() -// } -// } -// } func formattedShippingDate() -> String { let formatter = DateFormatter() @@ -101,7 +90,6 @@ struct OrderInfoView: View { Task { // 1) 서버에 반영된 장바구니를 먼저 비운다 (await) await cartViewModel.clearCart() -// navigateToSuccessPage = true // 2) cart가 비워진 후에 모달을 띄운다 // (모달을 띄우기 전에 createdOrderId는 orderViewModel에 이미 세팅되어 있어야 함) showOrderSuccessModal = true @@ -172,8 +160,6 @@ struct OrderInfoView: View { } } ) - - } @@ -286,9 +272,6 @@ extension OrderInfoView { .font(.system(size: 26, weight: .bold)) .foregroundColor(Color.white) } - - // Text("₩\(cartViewModel.depositBalance?.formatted() ?? "0")") - // .font(.system(size: 22, weight: .bold)) } } @@ -406,10 +389,6 @@ extension OrderInfoView { .frame(height: 70) .background(Color.Primary) } - - NavigationLink(destination: destinationView, isActive: $navigateToSuccessPage) { - EmptyView() - } } } } @@ -424,7 +403,6 @@ struct RadioButtonRow: View { Image(systemName: selected ? "circle.inset.filled" : "circle") .foregroundColor(selected ? .Primary : .gray) Text(title) - // Spacer() } .onTapGesture { action() } } diff --git a/StockMate/StockMate/app/feature/parts/data/PartStore.swift b/StockMate/StockMate/app/feature/parts/data/PartStore.swift index b769ce7..48436dc 100644 --- a/StockMate/StockMate/app/feature/parts/data/PartStore.swift +++ b/StockMate/StockMate/app/feature/parts/data/PartStore.swift @@ -40,4 +40,15 @@ final class PartStore: ObservableObject { objectWillChange.send() } } + + func decreaseQuantityOrRemove(for part: PartDetail) { + if let index = parts.firstIndex(where: { $0.id == part.id }) { + if parts[index].quantity > 1 { + parts[index].quantity -= 1 + } else { + parts.remove(at: index) + } + objectWillChange.send() + } + } } diff --git a/StockMate/StockMate/app/feature/parts/ui/QRScannerViewController.swift b/StockMate/StockMate/app/feature/parts/ui/QRScannerViewController.swift index b3d6321..626dab2 100644 --- a/StockMate/StockMate/app/feature/parts/ui/QRScannerViewController.swift +++ b/StockMate/StockMate/app/feature/parts/ui/QRScannerViewController.swift @@ -87,7 +87,7 @@ final class QRScannerViewController: UIViewController, AVCaptureMetadataOutputOb captureSession.stopRunning() } } - + // ✅ QR 감지 시 호출 func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) { diff --git a/StockMate/StockMate/app/feature/parts/viewmodel/PartViewModel.swift b/StockMate/StockMate/app/feature/parts/viewmodel/PartViewModel.swift index 03543f8..394dc62 100644 --- a/StockMate/StockMate/app/feature/parts/viewmodel/PartViewModel.swift +++ b/StockMate/StockMate/app/feature/parts/viewmodel/PartViewModel.swift @@ -47,11 +47,11 @@ final class PartViewModel: ObservableObject { } } - func fetchPartDetail(partId: Int) async { + func fetchPartDetail(partIds: Int) async { isLoading = true defer { isLoading = false } - let result = await repo.fetchPartDetail(partIds: [partId]) + let result = await repo.fetchPartDetail(partIds: [partIds]) switch result { case .success(let apiResp): if apiResp.success, let data = apiResp.data { diff --git a/StockMate/StockMate/app/feature/user/ui/UserProfileView.swift b/StockMate/StockMate/app/feature/user/ui/UserProfileView.swift index fb70701..90f28a2 100644 --- a/StockMate/StockMate/app/feature/user/ui/UserProfileView.swift +++ b/StockMate/StockMate/app/feature/user/ui/UserProfileView.swift @@ -41,7 +41,7 @@ struct UserProfileView: View { .padding(.horizontal, 10) .padding(.bottom, 10) - Text("이에일") + Text("이메일") .font(.system(size: 14, weight: .medium)) .frame(maxWidth: .infinity, alignment: .leading) .padding(.horizontal, 10) From 26fe712098e07fbd36e9b3a30396ef03aceb075c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Fri, 7 Nov 2025 12:33:25 +0900 Subject: [PATCH 63/85] =?UTF-8?q?[REFAC]=20=EC=BD=94=EB=93=9C=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/components/ProfileFieldView.swift | 38 ++++++ .../ui/TransactionTypeListView.swift | 30 +++-- .../viewmodel/HistoryViewModel.swift | 2 +- .../app/feature/user/ui/UserProfileView.swift | 109 +----------------- 4 files changed, 63 insertions(+), 116 deletions(-) create mode 100644 StockMate/StockMate/app/core/components/ProfileFieldView.swift diff --git a/StockMate/StockMate/app/core/components/ProfileFieldView.swift b/StockMate/StockMate/app/core/components/ProfileFieldView.swift new file mode 100644 index 0000000..3f680d5 --- /dev/null +++ b/StockMate/StockMate/app/core/components/ProfileFieldView.swift @@ -0,0 +1,38 @@ +// +// ProfileFieldView.swift +// StockMate +// +// Created by Admin on 11/7/25. +// + +import SwiftUI + +struct ProfileFieldView: View { + let label: String + let value: String + + var body: some View { + VStack(spacing: 9) { + Text(label) + .font(.system(size: 14, weight: .medium)) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 10) + .foregroundColor(Color.black) + + HStack { + Text(value) + .font(.system(size: 16, weight: .regular)) + .foregroundColor(.black) + Spacer() + } + .padding() + .cornerRadius(12) + .overlay( + RoundedRectangle(cornerRadius: 12) + .stroke(Color.GrayMordern300, lineWidth: 1) + ) + .padding(.horizontal, 10) + .padding(.bottom, 10) + } + } +} diff --git a/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift b/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift index 79ec4dc..622585a 100644 --- a/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift +++ b/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift @@ -50,17 +50,25 @@ struct TransactionCard: View { var body: some View { VStack(alignment: .leading, spacing: 8) { HStack(alignment: .center, spacing: 12) { - // 대표 이미지 - AsyncImage( - url: URL(string: item.orderItems?.first?.image ?? "") - ) { image in - image.resizable().scaledToFit() - } placeholder: { - Image("exchange") - .foregroundColor(Color.Primary) - } - .frame(width: 64, height: 64) - .cornerRadius(10) + + // PAY일 때만 부품 이미지 표시 + if item.transactionType == "PAY" { + // 대표 이미지 + AsyncImage(url: URL(string: item.orderItems?.first?.image ?? "")) { image in + image.resizable().scaledToFit() + } placeholder: { + Color.gray.opacity(0.2) + } + .frame(width: 64, height: 64) + .cornerRadius(10) + } else { + Image("exchange") + .foregroundColor(Color.Primary) + .frame(width: 64, height: 64) + .cornerRadius(10) + } + + VStack(alignment: .leading, spacing: 5) { if item.transactionType == "PAY", diff --git a/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift b/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift index 88c9a68..0eb95d7 100644 --- a/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift +++ b/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift @@ -13,7 +13,7 @@ final class HistoryViewModel: ObservableObject { // MARK: - 입출고 히스토리 관련 @Published var histories: [HistoryItem] = [] @Published var isLoading = false - @Published var errorMessage: String? = nil // ✅ 추가 + @Published var errorMessage: String? @Published var currentPage = 0 @Published var totalPages = 1 diff --git a/StockMate/StockMate/app/feature/user/ui/UserProfileView.swift b/StockMate/StockMate/app/feature/user/ui/UserProfileView.swift index 90f28a2..9094fbe 100644 --- a/StockMate/StockMate/app/feature/user/ui/UserProfileView.swift +++ b/StockMate/StockMate/app/feature/user/ui/UserProfileView.swift @@ -20,110 +20,11 @@ struct UserProfileView: View { .padding(.vertical, 32) VStack(spacing: 9) { - Text("대표자") - .font(.system(size: 14, weight: .medium)) - .frame(maxWidth: .infinity, alignment: .leading) - .padding(.horizontal, 10) - .foregroundColor(Color.black) - - HStack { - Text(userViewModel.userInfo?.owner ?? "이름 없음") - .font(.system(size: 16, weight: .regular)) - .foregroundColor(.black) - Spacer() - } - .padding() - .cornerRadius(12) - .overlay( - RoundedRectangle(cornerRadius: 12) - .stroke(Color.GrayMordern300, lineWidth: 1) - ) - .padding(.horizontal, 10) - .padding(.bottom, 10) - - Text("이메일") - .font(.system(size: 14, weight: .medium)) - .frame(maxWidth: .infinity, alignment: .leading) - .padding(.horizontal, 10) - .foregroundColor(Color.black) - - HStack { - Text(userViewModel.userInfo?.email ?? "이메일 없음") - .font(.system(size: 16, weight: .regular)) - .foregroundColor(.black) - Spacer() - } - .padding() - .cornerRadius(12) - .overlay( - RoundedRectangle(cornerRadius: 12) - .stroke(Color.GrayMordern300, lineWidth: 1) - ) - .padding(.horizontal, 10) - .padding(.bottom, 10) - - Text("지점") - .font(.system(size: 14, weight: .medium)) - .frame(maxWidth: .infinity, alignment: .leading) - .padding(.horizontal, 10) - .foregroundColor(Color.black) - - HStack { - Text(userViewModel.userInfo?.storeName ?? "지점명 없음") - .font(.system(size: 16, weight: .regular)) - .foregroundColor(.black) - Spacer() - } - .padding() - .cornerRadius(12) - .overlay( - RoundedRectangle(cornerRadius: 12) - .stroke(Color.GrayMordern300, lineWidth: 1) - ) - .padding(.horizontal, 10) - .padding(.bottom, 10) - - Text("주소") - .font(.system(size: 14, weight: .medium)) - .frame(maxWidth: .infinity, alignment: .leading) - .padding(.horizontal, 10) - .foregroundColor(Color.black) - - HStack { - Text(userViewModel.userInfo?.address ?? "주소 없음") - .font(.system(size: 16, weight: .regular)) - .foregroundColor(.black) - Spacer() - } - .padding() - .cornerRadius(12) - .overlay( - RoundedRectangle(cornerRadius: 12) - .stroke(Color.GrayMordern300, lineWidth: 1) - ) - .padding(.horizontal, 10) - .padding(.bottom, 10) - - Text("사업자등록번호") - .font(.system(size: 14, weight: .medium)) - .frame(maxWidth: .infinity, alignment: .leading) - .padding(.horizontal, 10) - .foregroundColor(Color.black) - - HStack { - Text(userViewModel.userInfo?.businessNumber ?? "사업자등록번호 없음") - .font(.system(size: 16, weight: .regular)) - .foregroundColor(.black) - Spacer() - } - .padding() - .cornerRadius(12) - .overlay( - RoundedRectangle(cornerRadius: 12) - .stroke(Color.GrayMordern300, lineWidth: 1) - ) - .padding(.horizontal, 10) - .padding(.bottom, 10) + ProfileFieldView(label: "대표자", value: userViewModel.userInfo?.owner ?? "이름 없음") + ProfileFieldView(label: "이메일", value: userViewModel.userInfo?.email ?? "이메일 없음") + ProfileFieldView(label: "지점", value: userViewModel.userInfo?.storeName ?? "지점명 없음") + ProfileFieldView(label: "주소", value: userViewModel.userInfo?.address ?? "주소 없음") + ProfileFieldView(label: "사업자등록번호", value: userViewModel.userInfo?.businessNumber ?? "사업자등록번호 없음") Spacer() From 80784755e6767b796211eab82679d08366d8102a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sat, 8 Nov 2025 16:55:34 +0900 Subject: [PATCH 64/85] =?UTF-8?q?[FEAT]=20=EC=95=8C=EB=A6=BC=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/feature/auth/ui/HomeView.swift | 35 +++++-- .../notification/data/NotificationApi.swift | 51 +++++++++++ .../data/NotificationRepositoryImpl.swift | 37 ++++++++ .../NotificationRepositoryProtocol.swift | 27 ++++++ .../ui/NotificationListView.swift | 91 +++++++++++++++++++ .../viewmodel/NotificationViewModel.swift | 84 +++++++++++++++++ .../feature/orders/ui/OrderDetailView.swift | 1 + .../app/feature/user/ui/ProfileView.swift | 3 +- .../app/navigation/MainTabView.swift | 5 +- .../notiImage.imageset/Contents.json | 21 +++++ .../notiImage.imageset/notiImage.svg | 5 + 11 files changed, 349 insertions(+), 11 deletions(-) create mode 100644 StockMate/StockMate/app/feature/notification/data/NotificationApi.swift create mode 100644 StockMate/StockMate/app/feature/notification/data/NotificationRepositoryImpl.swift create mode 100644 StockMate/StockMate/app/feature/notification/domain/NotificationRepositoryProtocol.swift create mode 100644 StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift create mode 100644 StockMate/StockMate/app/feature/notification/viewmodel/NotificationViewModel.swift create mode 100644 StockMate/StockMate/resources/Assets.xcassets/notiImage.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/notiImage.imageset/notiImage.svg diff --git a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift index fa9e0b8..e82a5f3 100644 --- a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift @@ -14,6 +14,7 @@ struct HomeView: View { @StateObject private var inventoryViewModel = InventoryViewModel() // @EnvironmentObject var dashboardViewModel: DashboardViewModel //preview 용 @StateObject private var dashboardViewModel = DashboardViewModel() + @StateObject private var notificationViewModel = NotificationViewModel() // 🔴 추가 @State private var selectedMonth: String? = nil // ✅ 추가 @@ -40,10 +41,32 @@ struct HomeView: View { Spacer() - Image("notification") - .font(.system(size: 20)) - .foregroundColor(.gray) +// Image("notification") +// .font(.system(size: 20)) +// .foregroundColor(.gray) + NavigationLink(destination: NotificationListView() + .environmentObject(notificationViewModel)) { // 🔴 전달 + ZStack(alignment: .topTrailing) { + Image("notification") + .resizable() + .scaledToFit() + .frame(width: 30, height: 30) + .foregroundColor(.gray) + + if notificationViewModel.unreadCount > 0 { + Text("\(notificationViewModel.unreadCount)") + .font(.system(size: 10, weight: .bold)) + .foregroundColor(.white) + .padding(5) + .background(Color.red) + .clipShape(Circle()) + .offset(x: 5, y: -5) + } + } + } .padding(.trailing, 5) + + } .padding(.horizontal) @@ -146,6 +169,7 @@ struct HomeView: View { await inventoryViewModel.loadLackCountByCategory() await dashboardViewModel.fetchMonthlySpending() // ✅ 추가 await dashboardViewModel.fetchCategorySpending() // ✅ 추가 + await notificationViewModel.fetchUnreadCount() // 🔴 추가 } .onAppear { Task { await userViewModel.loadUserInfo() } @@ -260,11 +284,6 @@ struct StatusItem: View { } - -//#Preview { -// HomeView() -//} - #Preview { let dashboardVM = DashboardViewModel() dashboardVM.categorySpendings = [ diff --git a/StockMate/StockMate/app/feature/notification/data/NotificationApi.swift b/StockMate/StockMate/app/feature/notification/data/NotificationApi.swift new file mode 100644 index 0000000..b9c139a --- /dev/null +++ b/StockMate/StockMate/app/feature/notification/data/NotificationApi.swift @@ -0,0 +1,51 @@ +// +// NotificationApi.swift +// StockMate +// +// Created by Admin on 11/7/25. +// + +import Foundation +import Alamofire + +struct NotificationItem: Decodable, Identifiable { + let id: Int + let orderId: Int + let orderNumber: String + let message: String + let createdAt: String + let read: Bool +} + +enum NotificationApi { + + // 1️⃣ 알림 전체 조회 + static func getAllNotifications() -> DataRequest { + let url = ApiClient.baseURL + "api/v1/order/store/notifications/" + return ApiClient.shared.request(url, method: .get) + } + + // 2️⃣ 읽지 않은 알림 개수 조회 + static func getUnreadCount() -> DataRequest { + let url = ApiClient.baseURL + "api/v1/order/store/notifications/unread/count" + return ApiClient.shared.request(url, method: .get) + } + + // 3️⃣ 읽지 않은 알림 조회 + static func getUnreadNotifications() -> DataRequest { + let url = ApiClient.baseURL + "api/v1/order/store/notifications/unread" + return ApiClient.shared.request(url, method: .get) + } + + // 4️⃣ 전체 알림 읽음 처리 + static func markAllAsRead() -> DataRequest { + let url = ApiClient.baseURL + "api/v1/order/store/notifications/read-all" + return ApiClient.shared.request(url, method: .patch) + } + + // 5️⃣ 개별 알림 읽음 처리 + static func markAsRead(notificationId: Int) -> DataRequest { + let url = ApiClient.baseURL + "api/v1/order/store/notifications/read?notificationId=\(notificationId)" + return ApiClient.shared.request(url, method: .patch) + } +} diff --git a/StockMate/StockMate/app/feature/notification/data/NotificationRepositoryImpl.swift b/StockMate/StockMate/app/feature/notification/data/NotificationRepositoryImpl.swift new file mode 100644 index 0000000..b5ec459 --- /dev/null +++ b/StockMate/StockMate/app/feature/notification/data/NotificationRepositoryImpl.swift @@ -0,0 +1,37 @@ +// +// NotificationRepositoryImpl.swift +// StockMate +// +// Created by Admin on 11/7/25. +// + +import SwiftUI +import Alamofire + +final class NotificationRepositoryImpl: NotificationRepositoryProtocol { + + func getAllNotifications() async -> AppResult> { + let request = NotificationApi.getAllNotifications() + return await safeApi(request, decodeTo: ApiResponse<[NotificationItem]>.self) + } + + func getUnreadCount() async -> AppResult> { + let request = NotificationApi.getUnreadCount() + return await safeApi(request, decodeTo: ApiResponse.self) + } + + func getUnreadNotifications() async -> AppResult> { + let request = NotificationApi.getUnreadNotifications() + return await safeApi(request, decodeTo: ApiResponse<[NotificationItem]>.self) + } + + func markAllAsRead() async -> AppResult> { + let request = NotificationApi.markAllAsRead() + return await safeApi(request, decodeTo: ApiResponse.self) + } + + func markAsRead(notificationId: Int) async -> AppResult> { + let request = NotificationApi.markAsRead(notificationId: notificationId) + return await safeApi(request, decodeTo: ApiResponse.self) + } +} diff --git a/StockMate/StockMate/app/feature/notification/domain/NotificationRepositoryProtocol.swift b/StockMate/StockMate/app/feature/notification/domain/NotificationRepositoryProtocol.swift new file mode 100644 index 0000000..207f460 --- /dev/null +++ b/StockMate/StockMate/app/feature/notification/domain/NotificationRepositoryProtocol.swift @@ -0,0 +1,27 @@ +// +// NotificationRepositoryProtocol.swift +// StockMate +// +// Created by Admin on 11/7/25. +// + +import SwiftUI +import Alamofire + + +protocol NotificationRepositoryProtocol { + // 1️⃣ 전체 알림 조회 + func getAllNotifications() async -> AppResult> + + // 2️⃣ 읽지 않은 알림 개수 조회 + func getUnreadCount() async -> AppResult> + + // 3️⃣ 읽지 않은 알림 조회 + func getUnreadNotifications() async -> AppResult> + + // 4️⃣ 전체 알림 읽음 처리 + func markAllAsRead() async -> AppResult> + + // 5️⃣ 개별 알림 읽음 처리 + func markAsRead(notificationId: Int) async -> AppResult> +} diff --git a/StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift b/StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift new file mode 100644 index 0000000..aed62a7 --- /dev/null +++ b/StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift @@ -0,0 +1,91 @@ +// +// NotificationListView.swift +// StockMate +// +// Created by Admin on 11/7/25. +// +import SwiftUI + +struct NotificationListView: View { + @StateObject private var viewModel = NotificationViewModel() + @State private var selectedOrderId: Int? = nil + + var body: some View { + VStack { + ScrollView { + LazyVStack(spacing: 12) { + ForEach(viewModel.notifications) { notification in + NotificationCardView(item: notification) { + Task { + await viewModel.markAsRead(notification.id) + selectedOrderId = notification.orderId + } + } + } + } + .padding() + } + } + .background(Color.Light) + .navigationTitle("알림") + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + Button("전체 읽음") { + Task { await viewModel.markAllAsRead() } + } + .foregroundColor(.red) + .font(.subheadline) + } + } + .navigationDestination(item: $selectedOrderId) { orderId in + OrderDetailView(orderId: orderId, orderViewModel: OrderViewModel()) + } + .task { + await viewModel.fetchNotifications() + } + } +} + + +struct NotificationCardView: View { + let item: NotificationItem + let onTap: () -> Void + + var body: some View { + Button(action: onTap) { + HStack(alignment: .center, spacing: 14) { + Image("notiImage") + .resizable() + .scaledToFit() + .frame(width: 38, height: 38) + + VStack(alignment: .leading, spacing: 6) { + Text(item.message) + .font(.system(size: 15, weight: .semibold)) + .foregroundColor(.primary) + Text(item.orderNumber) + .font(.system(size: 13, weight: .regular)) + .foregroundColor(.gray) + Text(formattedDate(item.createdAt)) + .font(.caption) + .foregroundColor(.gray) + } + Spacer() + + // 🔴 안 읽은 알림 표시 점 + if !item.read { + Circle() + .fill(Color.red) + .frame(width: 7, height: 7) + .padding(.trailing) + } + + } + .padding() + .background(Color.white) + .cornerRadius(12) + .shadow(color: Color.black.opacity(0.05), radius: 3, x: 0, y: 1) + } + } +} diff --git a/StockMate/StockMate/app/feature/notification/viewmodel/NotificationViewModel.swift b/StockMate/StockMate/app/feature/notification/viewmodel/NotificationViewModel.swift new file mode 100644 index 0000000..a09e741 --- /dev/null +++ b/StockMate/StockMate/app/feature/notification/viewmodel/NotificationViewModel.swift @@ -0,0 +1,84 @@ +// +// NotificationViewModel.swift +// StockMate +// +// Created by Admin on 11/7/25. +// + +import Foundation + +@MainActor +final class NotificationViewModel: ObservableObject { + @Published var notifications: [NotificationItem] = [] + @Published var unreadCount: Int = 0 // 🔴 추가 + @Published var isLoading = false + + private let repository: NotificationRepositoryProtocol = NotificationRepositoryImpl() + + // 전체 알림 조회 + func fetchNotifications() async { + isLoading = true + defer { isLoading = false } + + let result = await repository.getAllNotifications() + switch result { + case .success(let response): + notifications = response.data!.sorted { $0.createdAt > $1.createdAt } + case .failure(let error): + print("❌ 알림 조회 실패:", error.localizedDescription) + } + } + + // 🔴 읽지 않은 개수 조회 + func fetchUnreadCount() async { + let result = await repository.getUnreadCount() + switch result { + case .success(let response): + unreadCount = response.data ?? 0 + case .failure(let error): + print("❌ 읽지 않은 개수 조회 실패:", error.localizedDescription) + } + } + + // 개별 알림 읽음 처리 + func markAsRead(_ id: Int) async { + let result = await repository.markAsRead(notificationId: id) + switch result { + case .success: + if let index = notifications.firstIndex(where: { $0.id == id }) { + notifications[index] = NotificationItem( + id: notifications[index].id, + orderId: notifications[index].orderId, + orderNumber: notifications[index].orderNumber, + message: notifications[index].message, + createdAt: notifications[index].createdAt, + read: true + ) + } + unreadCount = max(0, unreadCount - 1) // 🔴 카운트 즉시 반영 + case .failure(let error): + print("❌ 알림 읽음 처리 실패:", error.localizedDescription) + } + } + + // 전체 읽음 처리 + func markAllAsRead() async { + let result = await repository.markAllAsRead() + switch result { + case .success: + for i in 0.. + + + + From 2092887c0d334af40c47a207eb6c73745aa28eb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sat, 8 Nov 2025 17:26:00 +0900 Subject: [PATCH 65/85] =?UTF-8?q?[REFAC]=20=EC=BD=94=EB=93=9C=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- StockMate/StockMate/app/feature/auth/ui/HomeView.swift | 8 ++------ .../feature/notification/ui/NotificationListView.swift | 10 +++++----- .../notification/viewmodel/NotificationViewModel.swift | 3 ++- .../app/feature/orders/ui/OrderDetailView.swift | 1 - 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift index e82a5f3..825748f 100644 --- a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift @@ -40,12 +40,8 @@ struct HomeView: View { } Spacer() - -// Image("notification") -// .font(.system(size: 20)) -// .foregroundColor(.gray) - NavigationLink(destination: NotificationListView() - .environmentObject(notificationViewModel)) { // 🔴 전달 + + NavigationLink(destination: NotificationListView()) { // 🔴 전달 ZStack(alignment: .topTrailing) { Image("notification") .resizable() diff --git a/StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift b/StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift index aed62a7..c04134c 100644 --- a/StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift +++ b/StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift @@ -7,17 +7,17 @@ import SwiftUI struct NotificationListView: View { - @StateObject private var viewModel = NotificationViewModel() + @StateObject private var notificationViewModel = NotificationViewModel() @State private var selectedOrderId: Int? = nil var body: some View { VStack { ScrollView { LazyVStack(spacing: 12) { - ForEach(viewModel.notifications) { notification in + ForEach(notificationViewModel.notifications) { notification in NotificationCardView(item: notification) { Task { - await viewModel.markAsRead(notification.id) + await notificationViewModel.markAsRead(notification.id) selectedOrderId = notification.orderId } } @@ -32,7 +32,7 @@ struct NotificationListView: View { .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button("전체 읽음") { - Task { await viewModel.markAllAsRead() } + Task { await notificationViewModel.markAllAsRead() } } .foregroundColor(.red) .font(.subheadline) @@ -42,7 +42,7 @@ struct NotificationListView: View { OrderDetailView(orderId: orderId, orderViewModel: OrderViewModel()) } .task { - await viewModel.fetchNotifications() + await notificationViewModel.fetchNotifications() } } } diff --git a/StockMate/StockMate/app/feature/notification/viewmodel/NotificationViewModel.swift b/StockMate/StockMate/app/feature/notification/viewmodel/NotificationViewModel.swift index a09e741..e4d6b11 100644 --- a/StockMate/StockMate/app/feature/notification/viewmodel/NotificationViewModel.swift +++ b/StockMate/StockMate/app/feature/notification/viewmodel/NotificationViewModel.swift @@ -23,7 +23,8 @@ final class NotificationViewModel: ObservableObject { let result = await repository.getAllNotifications() switch result { case .success(let response): - notifications = response.data!.sorted { $0.createdAt > $1.createdAt } + notifications = (response.data ?? []).sorted { $0.createdAt > $1.createdAt } +// notifications = response.data!.sorted { $0.createdAt > $1.createdAt } case .failure(let error): print("❌ 알림 조회 실패:", error.localizedDescription) } diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift index 7c3455d..8ff3d78 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift @@ -11,7 +11,6 @@ struct OrderDetailView: View { let orderId: Int @ObservedObject var orderViewModel: OrderViewModel @StateObject private var viewModel = OrderDetailViewModel() - @State private var didFetch = false // ✅ 한 번만 fetch var body: some View { ScrollView { From 8274d9de6d91c037333891b3d447adce425d7545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sat, 8 Nov 2025 17:32:25 +0900 Subject: [PATCH 66/85] =?UTF-8?q?[REFAC]=20=EC=BD=94=EB=93=9C=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/notification/viewmodel/NotificationViewModel.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/StockMate/StockMate/app/feature/notification/viewmodel/NotificationViewModel.swift b/StockMate/StockMate/app/feature/notification/viewmodel/NotificationViewModel.swift index e4d6b11..a2fb0de 100644 --- a/StockMate/StockMate/app/feature/notification/viewmodel/NotificationViewModel.swift +++ b/StockMate/StockMate/app/feature/notification/viewmodel/NotificationViewModel.swift @@ -24,7 +24,6 @@ final class NotificationViewModel: ObservableObject { switch result { case .success(let response): notifications = (response.data ?? []).sorted { $0.createdAt > $1.createdAt } -// notifications = response.data!.sorted { $0.createdAt > $1.createdAt } case .failure(let error): print("❌ 알림 조회 실패:", error.localizedDescription) } From de6e3511176db358b1505d9d8ebdb181cefb650b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sat, 8 Nov 2025 21:21:02 +0900 Subject: [PATCH 67/85] =?UTF-8?q?[REFAC]=20=EB=A7=89=EB=8C=80=EA=B7=B8?= =?UTF-8?q?=EB=9E=98=ED=94=84=20=ED=81=AC=EA=B8=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/core/components/BarChartView.swift | 41 ++++++++++--------- .../app/feature/auth/ui/HomeView.swift | 19 ++------- 2 files changed, 26 insertions(+), 34 deletions(-) diff --git a/StockMate/StockMate/app/core/components/BarChartView.swift b/StockMate/StockMate/app/core/components/BarChartView.swift index 5fa4e35..726ccf2 100644 --- a/StockMate/StockMate/app/core/components/BarChartView.swift +++ b/StockMate/StockMate/app/core/components/BarChartView.swift @@ -14,12 +14,12 @@ struct BarChartView: View { @Binding var selectedMonth: String? var body: some View { - // ✅ 최신월이 오른쪽에 오도록 역순 정렬 + // 최신월이 오른쪽에 오도록 역순 정렬 let reversedValues = Array(values.reversed()) let reversedLabels = Array(labels.reversed()) let reversedAmounts = Array(amounts.reversed()) - // ✅ "07" → "7월" 형식 변환 + // "07" → "7월" 형식 변환 let displayLabels = reversedLabels.map { label in if let monthInt = Int(label) { return "\(monthInt)월" @@ -28,56 +28,59 @@ struct BarChartView: View { } } - // ✅ 기본 선택: 최신월 + // 기본 선택: 최신월 let defaultMonth = displayLabels.last ?? "" let activeMonth = selectedMonth ?? defaultMonth - VStack(alignment: .leading, spacing: 14) { - // ✅ 막대 그래프 + VStack(alignment: .leading, spacing: 12) { + // 막대 그래프 GeometryReader { geometry in - let chartHeight = geometry.size.height * 0.85 // 상하 여백 고려 + let chartHeight = geometry.size.height * 0.88 // 상하 여백 고려 let totalWidth = geometry.size.width let barCount = CGFloat(reversedValues.count) - let barWidth: CGFloat = 28 + let barWidth: CGFloat = 35 let spacing = max((totalWidth - (barWidth * barCount)) / (barCount + 1), 6) HStack(alignment: .bottom, spacing: spacing) { ForEach(reversedValues.indices, id: \.self) { i in VStack { - RoundedRectangle(cornerRadius: 8) + RoundedRectangle(cornerRadius: 10) .fill(activeMonth == displayLabels[i] ? Color.Primary : Color.LightBlue04) - // ✅ 막대 높이를 geometry 기준으로 조정 - .frame(width: barWidth, height: chartHeight * reversedValues[i]) + .frame(width: barWidth, height: max(chartHeight * reversedValues[i], 8)) // ✅ 최소 높이 보장 .onTapGesture { selectedMonth = (selectedMonth == displayLabels[i]) ? nil : displayLabels[i] } Text(displayLabels[i]) - .font(.caption2) - .foregroundColor(.black) - .padding(.top, 4) + .font(.system(size: 13, weight: activeMonth == displayLabels[i] ? .semibold : .light)) // ✅ 선택된 막대는 bold + .padding(.top, 3) } } } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom) } - .frame(height: 140) // ← 전체 그래프 영역 높이 확장 - .padding(.vertical, 8) + .frame(height: 163) // ← 전체 그래프 영역 높이 확장 + .padding(.bottom, 7) + +// Divider() + Rectangle() + .fill(Color.textGray2) + .frame(height: 0.8) // Divider보다 살짝 두껍게 + .padding(.horizontal, 4) - Divider() // ✅ 하단 "n월 지출금액 ooo원" 표시 if let index = displayLabels.firstIndex(of: activeMonth) { HStack { Text("\(displayLabels[index]) 지출 현황") - .font(.system(size: 17, weight: .medium)) + .font(.system(size: 15, weight: .regular)) Spacer() Text("\(reversedAmounts[index].formatted())원") - .font(.system(size: 18, weight: .bold)) + .font(.system(size: 17, weight: .bold)) .foregroundColor(Color.Primary) } - .padding(.top, 6) .padding(.horizontal,4) + .padding(.vertical, 2) } } .frame(maxWidth: .infinity) diff --git a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift index 825748f..4659058 100644 --- a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift @@ -14,9 +14,9 @@ struct HomeView: View { @StateObject private var inventoryViewModel = InventoryViewModel() // @EnvironmentObject var dashboardViewModel: DashboardViewModel //preview 용 @StateObject private var dashboardViewModel = DashboardViewModel() - @StateObject private var notificationViewModel = NotificationViewModel() // 🔴 추가 + @StateObject private var notificationViewModel = NotificationViewModel() - @State private var selectedMonth: String? = nil // ✅ 추가 + @State private var selectedMonth: String? = nil var body: some View { @@ -128,29 +128,20 @@ struct HomeView: View { .padding(4) .frame(maxWidth: .infinity, alignment: .leading) // ✅ 항상 왼쪽 정렬 - ZStack { // ✅ 크기 고정용 컨테이너 - RoundedRectangle(cornerRadius: 16) - .fill(Color.white) - .frame(height: 220) // ✅ 일정 높이 고정 if dashboardViewModel.isLoading { ProgressView("데이터 불러오는 중...") - .frame(height: 220) + .frame(height: 163) } else if dashboardViewModel.monthlySpendings.isEmpty { Text("최근 지출 내역이 없습니다.") .foregroundColor(.gray) - .frame(height: 220) + .frame(height: 163) } else { BarChartView( values: dashboardViewModel.spendingRatios, labels: dashboardViewModel.monthLabels, amounts: dashboardViewModel.monthlySpendings.map { $0.totalAmount }, selectedMonth: $selectedMonth ) - .padding() - // .frame(height: 220) - // .background(Color.white) - // .cornerRadius(16) } - } } .padding() .background(Color.white) @@ -220,8 +211,6 @@ struct HomeView: View { .cornerRadius(16) .padding(.horizontal) } - - } From 04b8a705d3fd5d93ac3d9aeee17fe0f1237bca18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sat, 8 Nov 2025 21:42:01 +0900 Subject: [PATCH 68/85] =?UTF-8?q?[REFAC]=20=EB=B6=80=EC=A1=B1=20=EC=9E=AC?= =?UTF-8?q?=EA=B3=A0=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=83=81=EB=8B=A8=20?= =?UTF-8?q?=ED=83=AD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/core/components/BarChartView.swift | 1 - .../app/feature/auth/ui/HomeView.swift | 2 +- .../feature/inventory/ui/LackListView.swift | 49 +++++++++++++------ 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/StockMate/StockMate/app/core/components/BarChartView.swift b/StockMate/StockMate/app/core/components/BarChartView.swift index 726ccf2..095e3a7 100644 --- a/StockMate/StockMate/app/core/components/BarChartView.swift +++ b/StockMate/StockMate/app/core/components/BarChartView.swift @@ -62,7 +62,6 @@ struct BarChartView: View { .frame(height: 163) // ← 전체 그래프 영역 높이 확장 .padding(.bottom, 7) -// Divider() Rectangle() .fill(Color.textGray2) .frame(height: 0.8) // Divider보다 살짝 두껍게 diff --git a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift index 4659058..a5e40d0 100644 --- a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift @@ -175,7 +175,7 @@ struct HomeView: View { VStack(alignment: .leading, spacing: 8) { Text("재고 부족 조회") .font(.system(size: 15, weight: .semibold)) - .frame(maxWidth: .infinity, alignment: .leading) // ✅ 항상 왼쪽 정렬 유지 + .frame(maxWidth: .infinity, alignment: .leading) HStack(spacing: 13) { if inventoryViewModel.lackCounts.isEmpty { // ✅ 데이터가 없을 때도 공간 확보 diff --git a/StockMate/StockMate/app/feature/inventory/ui/LackListView.swift b/StockMate/StockMate/app/feature/inventory/ui/LackListView.swift index b9ca69a..f6a6b38 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/LackListView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/LackListView.swift @@ -17,23 +17,42 @@ struct LackListView: View { var body: some View { VStack(spacing: 0) { - // 상단 카테고리 탭 - HStack(spacing: 8) { - ForEach(categories, id: \.self) { category in - CategoryButton( - title: category, - isSelected: selectedCategory == category - ) { - Task { - selectedCategory = category - inventoryViewModel.selectedCategories = [category] - await inventoryViewModel.loadUnderLimitList(reset: true) + // 상단 카테고리 탭 (가로 스크롤 가능) + ScrollViewReader { proxy in + ScrollView(.horizontal, showsIndicators: false) { + HStack(spacing: 8) { + ForEach(categories, id: \.self) { category in + CategoryButton( + title: category, + isSelected: selectedCategory == category + ) { + Task { + selectedCategory = category + inventoryViewModel.selectedCategories = [category] + await inventoryViewModel.loadUnderLimitList(reset: true) + + // 버튼 클릭 시 해당 카테고리로 스크롤 이동 + withAnimation { + proxy.scrollTo(category, anchor: .center) + } + } + } + .id(category) // ScrollViewReader용 id + } + } + .padding(.horizontal) + .padding(.vertical, 10) + } + .onAppear { + // 진입 시 선택된 카테고리 위치로 자동 스크롤 + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + withAnimation { + proxy.scrollTo(selectedCategory, anchor: .center) + } } } } - } - .padding(.horizontal) - .padding(.vertical, 10) + // 리스트 ScrollView { @@ -81,7 +100,7 @@ struct CategoryButton: View { var body: some View { Button(action: action) { Text(title) - .font(.system(size: 11, weight: .regular)) + .font(.system(size: 13, weight: .regular)) .foregroundColor(isSelected ? .Primary : .black) .padding(.vertical, 8) .padding(.horizontal, 12) From c5cfa15885ae29da4a54c0c2f4dbebebb62d83ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sat, 8 Nov 2025 23:27:43 +0900 Subject: [PATCH 69/85] =?UTF-8?q?[REFAC]=20=EC=83=81=EB=8B=A8=EB=B0=94=20?= =?UTF-8?q?=EB=92=A4=EB=A1=9C=EA=B0=80=EA=B8=B0=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/core/components/CategoryButton.swift | 32 +++++++++++++ .../dashboard/ui/InOutHistoryView.swift | 15 ++++++ .../dashboard/ui/ReleaseDetailView.swift | 15 ++++++ .../ui/TransactionTypeListView.swift | 15 ++++++ .../inventory/ui/IncomingScanView.swift | 44 ++++++++++++------ .../inventory/ui/InventorySearchView.swift | 15 ++++++ .../feature/inventory/ui/LackListView.swift | 38 ++++++--------- .../inventory/ui/OutgoingScanView.swift | 46 ++++++++++++------- .../ui/NotificationListView.swift | 13 ++++++ .../app/feature/orders/ui/OrderCartView.swift | 15 ++++++ .../feature/orders/ui/OrderDetailView.swift | 15 ++++++ .../app/feature/orders/ui/OrderListView.swift | 15 ++++++ .../orders/ui/OrderRequestSearchView.swift | 15 ++++++ .../app/feature/orders/ui/ReceiptView.swift | 38 ++++++--------- .../app/feature/user/ui/UserProfileView.swift | 15 ++++++ 15 files changed, 268 insertions(+), 78 deletions(-) create mode 100644 StockMate/StockMate/app/core/components/CategoryButton.swift diff --git a/StockMate/StockMate/app/core/components/CategoryButton.swift b/StockMate/StockMate/app/core/components/CategoryButton.swift new file mode 100644 index 0000000..26461f1 --- /dev/null +++ b/StockMate/StockMate/app/core/components/CategoryButton.swift @@ -0,0 +1,32 @@ +// +// CategoryButton.swift +// StockMate +// +// Created by Admin on 11/8/25. +// + +import SwiftUI + +struct CategoryButton: View { + let title: String + let isSelected: Bool + let action: () -> Void + + var body: some View { + Button(action: action) { + Text(title) + .font(.system(size: 13, weight: .regular)) + .foregroundColor(isSelected ? .Primary : .black) + .padding(.vertical, 8) + .padding(.horizontal, 12) + .background( + RoundedRectangle(cornerRadius: 16) + .fill(Color.Light) + ) + .overlay( + RoundedRectangle(cornerRadius: 16) + .stroke(isSelected ? Color.Primary : Color.GrayStroke, lineWidth: 1) + ) + } + } +} diff --git a/StockMate/StockMate/app/feature/dashboard/ui/InOutHistoryView.swift b/StockMate/StockMate/app/feature/dashboard/ui/InOutHistoryView.swift index 12998e9..d72f7e9 100644 --- a/StockMate/StockMate/app/feature/dashboard/ui/InOutHistoryView.swift +++ b/StockMate/StockMate/app/feature/dashboard/ui/InOutHistoryView.swift @@ -8,6 +8,7 @@ import SwiftUI struct InOutHistoryView: View { + @Environment(\.dismiss) private var dismiss @StateObject private var viewModel = HistoryViewModel() var body: some View { @@ -55,6 +56,20 @@ struct InOutHistoryView: View { } .background(Color.Light) .navigationTitle("입출고 내역") + .navigationBarBackButtonHidden(true) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button { + dismiss() + } label: { + HStack(spacing: 4) { + Image(systemName: "arrow.left") + .font(.system(size: 15, weight: .medium)) + } + .foregroundColor(.black) + } + } + } .task { await viewModel.fetchInOutHistory() } diff --git a/StockMate/StockMate/app/feature/dashboard/ui/ReleaseDetailView.swift b/StockMate/StockMate/app/feature/dashboard/ui/ReleaseDetailView.swift index d1cd2ed..0b4937d 100644 --- a/StockMate/StockMate/app/feature/dashboard/ui/ReleaseDetailView.swift +++ b/StockMate/StockMate/app/feature/dashboard/ui/ReleaseDetailView.swift @@ -8,6 +8,7 @@ import SwiftUI struct ReleaseDetailView: View { + @Environment(\.dismiss) private var dismiss let history: HistoryItem var body: some View { @@ -35,6 +36,20 @@ struct ReleaseDetailView: View { .background(Color.Light) .navigationTitle("출고 상세") .navigationBarTitleDisplayMode(.inline) + .navigationBarBackButtonHidden(true) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button { + dismiss() + } label: { + HStack(spacing: 4) { + Image(systemName: "arrow.left") + .font(.system(size: 15, weight: .medium)) + } + .foregroundColor(.black) + } + } + } } } diff --git a/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift b/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift index 622585a..74c2e6b 100644 --- a/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift +++ b/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift @@ -8,6 +8,7 @@ import SwiftUI struct TransactionTypeListView: View { + @Environment(\.dismiss) private var dismiss @StateObject private var viewModel = HistoryViewModel() var body: some View { @@ -29,6 +30,20 @@ struct TransactionTypeListView: View { .background(Color.Light.ignoresSafeArea()) .navigationTitle("예치금 히스토리") .navigationBarTitleDisplayMode(.inline) + .navigationBarBackButtonHidden(true) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button { + dismiss() + } label: { + HStack(spacing: 4) { + Image(systemName: "arrow.left") + .font(.system(size: 15, weight: .medium)) + } + .foregroundColor(.black) + } + } + } .overlay { if viewModel.isTransactionLoading && viewModel.transactions.isEmpty { ProgressView("불러오는 중...") diff --git a/StockMate/StockMate/app/feature/inventory/ui/IncomingScanView.swift b/StockMate/StockMate/app/feature/inventory/ui/IncomingScanView.swift index 18e375a..c487a4d 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/IncomingScanView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/IncomingScanView.swift @@ -44,21 +44,21 @@ struct IncomingScanView: View { Spacer() - // ✅ 직접 등록 버튼 - Button(action: { - dismiss() - }) { - Text("직접 등록 하기") - .fontWeight(.semibold) - .foregroundColor(.black) - .frame(maxWidth: .infinity) - .padding() - .background(Color.white) - .cornerRadius(10) - .shadow(color: .gray.opacity(0.3), radius: 2, x: 0, y: 2) - } - .padding(.horizontal, 40) - .padding(.bottom, 40) +// // ✅ 직접 등록 버튼 +// Button(action: { +// dismiss() +// }) { +// Text("직접 등록 하기") +// .fontWeight(.semibold) +// .foregroundColor(.black) +// .frame(maxWidth: .infinity) +// .padding() +// .background(Color.white) +// .cornerRadius(10) +// .shadow(color: .gray.opacity(0.3), radius: 2, x: 0, y: 2) +// } +// .padding(.horizontal, 40) +// .padding(.bottom, 40) } // ✅ 로딩 표시 @@ -85,6 +85,20 @@ struct IncomingScanView: View { } .navigationTitle("입고 부품 등록") .navigationBarTitleDisplayMode(.inline) + .navigationBarBackButtonHidden(true) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button { + dismiss() + } label: { + HStack(spacing: 4) { + Image(systemName: "arrow.left") + .font(.system(size: 15, weight: .medium)) + } + .foregroundColor(.black) + } + } + } } private func handleScannedCode(_ code: String) async { diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift index 000cc5c..6ec0a6c 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift @@ -9,6 +9,7 @@ import SwiftUI struct InventorySearchView: View { + @Environment(\.dismiss) private var dismiss @StateObject private var inventoryViewModel = InventoryViewModel() @State private var searchText = "" @@ -152,6 +153,20 @@ struct InventorySearchView: View { } .background(Color.Light) .navigationTitle("재고 조회") + .navigationBarBackButtonHidden(true) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button { + dismiss() + } label: { + HStack(spacing: 4) { + Image(systemName: "arrow.left") + .font(.system(size: 15, weight: .medium)) + } + .foregroundColor(.black) + } + } + } .task { await inventoryViewModel.loadInventoryList(reset: true) } diff --git a/StockMate/StockMate/app/feature/inventory/ui/LackListView.swift b/StockMate/StockMate/app/feature/inventory/ui/LackListView.swift index f6a6b38..def5f29 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/LackListView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/LackListView.swift @@ -8,6 +8,7 @@ import SwiftUI struct LackListView: View { + @Environment(\.dismiss) private var dismiss @StateObject private var inventoryViewModel = InventoryViewModel() @State private var isFirstAppear = true @@ -82,6 +83,20 @@ struct LackListView: View { .background(Color.Light) .navigationTitle("부족 재고") .navigationBarTitleDisplayMode(.inline) + .navigationBarBackButtonHidden(true) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button { + dismiss() + } label: { + HStack(spacing: 4) { + Image(systemName: "arrow.left") + .font(.system(size: 15, weight: .medium)) + } + .foregroundColor(.black) + } + } + } .task { if isFirstAppear { isFirstAppear = false @@ -92,26 +107,3 @@ struct LackListView: View { } } -struct CategoryButton: View { - let title: String - let isSelected: Bool - let action: () -> Void - - var body: some View { - Button(action: action) { - Text(title) - .font(.system(size: 13, weight: .regular)) - .foregroundColor(isSelected ? .Primary : .black) - .padding(.vertical, 8) - .padding(.horizontal, 12) - .background( - RoundedRectangle(cornerRadius: 16) - .fill(Color.Light) - ) - .overlay( - RoundedRectangle(cornerRadius: 16) - .stroke(isSelected ? Color.Primary : Color.GrayStroke, lineWidth: 1) - ) - } - } -} diff --git a/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift b/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift index af8bb03..ef8692d 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift @@ -28,8 +28,6 @@ struct OutgoingScanView: View { var body: some View { ZStack { // ✅ 카메라 미리보기 (QR 스캐너) -// QRScannerView(scannedCode: $scannedCode) -// .ignoresSafeArea() QRScannerView(scannedCode: $scannedCode, isActive: !showBottomSheet) .ignoresSafeArea() @@ -51,7 +49,7 @@ struct OutgoingScanView: View { .frame(width: 250, height: 250) RoundedRectangle(cornerRadius: 8) - .stroke(Color.green, lineWidth: 3) + .stroke(Color.Primary, lineWidth: 3) .frame(width: 220, height: 220) } .padding(.bottom, 180) @@ -59,20 +57,20 @@ struct OutgoingScanView: View { Spacer() // 📦 직접 입력 버튼 - Button(action: { - dismiss() - }) { - Text("직접 입력 하기") - .fontWeight(.semibold) - .foregroundColor(.black) - .frame(maxWidth: .infinity) - .padding() - .background(Color.white) - .cornerRadius(10) - .shadow(color: .gray.opacity(0.3), radius: 2, x: 0, y: 2) - } - .padding(.horizontal, 40) - .padding(.bottom, 40) +// Button(action: { +// dismiss() +// }) { +// Text("직접 입력 하기") +// .fontWeight(.semibold) +// .foregroundColor(.black) +// .frame(maxWidth: .infinity) +// .padding() +// .background(Color.white) +// .cornerRadius(10) +// .shadow(color: .gray.opacity(0.3), radius: 2, x: 0, y: 2) +// } +// .padding(.horizontal, 40) +// .padding(.bottom, 40) } // ✅ 로딩 인디케이터 @@ -137,6 +135,20 @@ struct OutgoingScanView: View { } .navigationTitle("부품 사용 처리") .navigationBarTitleDisplayMode(.inline) + .navigationBarBackButtonHidden(true) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button { + dismiss() + } label: { + HStack(spacing: 4) { + Image(systemName: "arrow.left") + .font(.system(size: 15, weight: .medium)) + } + .foregroundColor(.black) + } + } + } } // ✅ 스캔된 코드로 부품 상세 조회만 수행 (출고 X) diff --git a/StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift b/StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift index c04134c..062e9e3 100644 --- a/StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift +++ b/StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift @@ -7,6 +7,7 @@ import SwiftUI struct NotificationListView: View { + @Environment(\.dismiss) private var dismiss @StateObject private var notificationViewModel = NotificationViewModel() @State private var selectedOrderId: Int? = nil @@ -29,7 +30,19 @@ struct NotificationListView: View { .background(Color.Light) .navigationTitle("알림") .navigationBarTitleDisplayMode(.inline) + .navigationBarBackButtonHidden(true) .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button { + dismiss() + } label: { + HStack(spacing: 4) { + Image(systemName: "arrow.left") + .font(.system(size: 15, weight: .medium)) + } + .foregroundColor(.black) + } + } ToolbarItem(placement: .navigationBarTrailing) { Button("전체 읽음") { Task { await notificationViewModel.markAllAsRead() } diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift index b39a506..ea68e16 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift @@ -8,6 +8,7 @@ import SwiftUI struct OrderCartView: View { + @Environment(\.dismiss) private var dismiss @ObservedObject var cartViewModel: CartViewModel var body: some View { @@ -54,6 +55,20 @@ struct OrderCartView: View { .background(Color.Light) .navigationTitle("장바구니 확인") .navigationBarTitleDisplayMode(.inline) + .navigationBarBackButtonHidden(true) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button { + dismiss() + } label: { + HStack(spacing: 4) { + Image(systemName: "arrow.left") + .font(.system(size: 15, weight: .medium)) + } + .foregroundColor(.black) + } + } + } .task { await cartViewModel.fetchCart() } diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift index 8ff3d78..f1b63fd 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift @@ -9,6 +9,7 @@ import SwiftUI struct OrderDetailView: View { let orderId: Int + @Environment(\.dismiss) private var dismiss @ObservedObject var orderViewModel: OrderViewModel @StateObject private var viewModel = OrderDetailViewModel() @@ -264,6 +265,20 @@ struct OrderDetailView: View { .background(Color.Light) .navigationTitle("주문 내역 상세") .navigationBarTitleDisplayMode(.inline) + .navigationBarBackButtonHidden(true) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button { + dismiss() + } label: { + HStack(spacing: 4) { + Image(systemName: "arrow.left") + .font(.system(size: 15, weight: .medium)) + } + .foregroundColor(.black) + } + } + } .task { await viewModel.fetchOrderDetail(orderId: orderId) } diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift index 7cd04b1..764c7d4 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift @@ -8,6 +8,7 @@ import SwiftUI struct OrderListView: View { + @Environment(\.dismiss) private var dismiss @StateObject private var orderViewModel = OrderViewModel() var body: some View { @@ -54,6 +55,20 @@ struct OrderListView: View { } .background(Color.Light) .navigationTitle("주문 내역") + .navigationBarBackButtonHidden(true) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button { + dismiss() + } label: { + HStack(spacing: 4) { + Image(systemName: "arrow.left") + .font(.system(size: 15, weight: .medium)) + } + .foregroundColor(.black) + } + } + } .task { await orderViewModel.loadOrders() } diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift index d75fea6..ad81fdf 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift @@ -8,6 +8,7 @@ import SwiftUI struct OrderRequestSearchView: View { + @Environment(\.dismiss) private var dismiss @ObservedObject var cartViewModel: CartViewModel @StateObject var inventoryViewModel = InventoryViewModel() @@ -166,6 +167,20 @@ struct OrderRequestSearchView: View { } .background(Color.Light) .navigationTitle("직접 발주") + .navigationBarBackButtonHidden(true) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button { + dismiss() + } label: { + HStack(spacing: 4) { + Image(systemName: "arrow.left") + .font(.system(size: 15, weight: .medium)) + } + .foregroundColor(.black) + } + } + } .task { await inventoryViewModel.loadInventoryList(reset: true) await cartViewModel.fetchCart() diff --git a/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift b/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift index 91b100a..7592465 100644 --- a/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift @@ -15,6 +15,7 @@ enum PDFType { } struct ReceiptView: View { + @Environment(\.dismiss) private var dismiss let orderId: Int @StateObject private var detailViewModel = OrderDetailViewModel() @@ -56,6 +57,20 @@ struct ReceiptView: View { .background(Color.Light) .navigationTitle("영수증") .navigationBarTitleDisplayMode(.inline) + .navigationBarBackButtonHidden(true) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button { + dismiss() + } label: { + HStack(spacing: 4) { + Image(systemName: "arrow.left") + .font(.system(size: 15, weight: .medium)) + } + .foregroundColor(.black) + } + } + } .task { await detailViewModel.fetchOrderDetail(orderId: orderId) } @@ -203,29 +218,6 @@ struct ReceiptView: View { } } -//struct ReceiptView_Previews: PreviewProvider { -// static var previews: some View { -// ReceiptView() -// } -//} - -//func formattedDate(_ timestamp: String) -> String { -// let inputFormatter = DateFormatter() -// inputFormatter.locale = Locale(identifier: "ko_KR") -// inputFormatter.timeZone = TimeZone(abbreviation: "UTC") -// inputFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS" -// -// guard let date = inputFormatter.date(from: timestamp) else { -// return timestamp -// } -// -// let outputFormatter = DateFormatter() -// outputFormatter.locale = Locale(identifier: "ko_KR") -// outputFormatter.timeZone = TimeZone.current -// outputFormatter.dateFormat = "yyyy/MM/dd HH:mm:ss" -// -// return outputFormatter.string(from: date) -//} func formattedDate(_ timestamp: String) -> String { let inputFormatter = DateFormatter() inputFormatter.locale = Locale(identifier: "ko_KR") diff --git a/StockMate/StockMate/app/feature/user/ui/UserProfileView.swift b/StockMate/StockMate/app/feature/user/ui/UserProfileView.swift index 9094fbe..0ce5291 100644 --- a/StockMate/StockMate/app/feature/user/ui/UserProfileView.swift +++ b/StockMate/StockMate/app/feature/user/ui/UserProfileView.swift @@ -8,6 +8,7 @@ import SwiftUI struct UserProfileView: View { + @Environment(\.dismiss) private var dismiss @StateObject private var userViewModel = UserViewModel() var body: some View { @@ -36,6 +37,20 @@ struct UserProfileView: View { .navigationTitle("프로필 확인") .background(Color.Light) .navigationBarTitleDisplayMode(.inline) + .navigationBarBackButtonHidden(true) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button { + dismiss() + } label: { + HStack(spacing: 4) { + Image(systemName: "arrow.left") + .font(.system(size: 15, weight: .medium)) + } + .foregroundColor(.black) + } + } + } .onAppear { Task { await userViewModel.loadUserInfo() } } From 630ac484e13c927aa188d2b6dd1e9a13a671573e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sun, 9 Nov 2025 01:08:50 +0900 Subject: [PATCH 70/85] =?UTF-8?q?[REFAC]=20=EC=88=98=EB=9F=89=EC=A1=B0?= =?UTF-8?q?=EC=A0=88=20=EA=B3=B5=ED=86=B5=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=ED=99=94=20=EB=B0=8F=20=EC=9E=A5=EB=B0=94=EA=B5=AC?= =?UTF-8?q?=EB=8B=88=20ui=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/core/components/CartCard.swift | 83 ++----------------- .../components/OrderRequestCardView.swift | 77 ++--------------- .../core/components/QuantityControlView.swift | 60 ++++++++++++++ .../inventory/ui/UsedPartListSheetView.swift | 8 -- .../app/feature/orders/ui/OrderCartView.swift | 6 -- .../app/feature/orders/ui/OrderListView.swift | 2 - .../orders/ui/OrderRequestSearchView.swift | 5 +- .../app/feature/orders/ui/OrderView.swift | 28 +------ 8 files changed, 78 insertions(+), 191 deletions(-) create mode 100644 StockMate/StockMate/app/core/components/QuantityControlView.swift diff --git a/StockMate/StockMate/app/core/components/CartCard.swift b/StockMate/StockMate/app/core/components/CartCard.swift index 6211f93..b8157e9 100644 --- a/StockMate/StockMate/app/core/components/CartCard.swift +++ b/StockMate/StockMate/app/core/components/CartCard.swift @@ -12,8 +12,6 @@ struct CartCard: View { let quantity: Int let onIncrease: () -> Void let onDecrease: () -> Void - let onAddToCart: (() -> Void)? - let onRemoveFromCart: () -> Void var body: some View { VStack(alignment: .leading, spacing: 6) { @@ -51,82 +49,11 @@ struct CartCard: View { Spacer() - // 🪄 수량에 따른 3단계 분기 - if quantity == 0 { - if let onAddToCart = onAddToCart { - Button(action: onAddToCart) { - Image("add_shopping_cart") - .resizable() - .scaledToFit() - .frame(width: 18, height: 18) - .padding(10) - .background(Color.white) - .clipShape(Circle()) - .shadow(color: .black.opacity(0.2), radius: 4, x: 0, y: 4) - } - } - } else if quantity == 1 { - HStack(spacing: 10) { - Button(action: onRemoveFromCart) { - Image(systemName: "trash") - .font(.system(size: 14, weight: .regular)) - .frame(width: 13,height: 13) - .foregroundColor(.black) - } - - Text("1") - .font(.system(size: 15, weight: .medium)) - .frame(width: 20) - - Button(action: onIncrease) { - Image(systemName: "plus") - .font(.system(size: 14, weight: .regular)) - .frame(width: 13,height: 13) - .foregroundColor(.black) - } - } - .padding(.vertical, 6) - .padding(.horizontal, 10) - .background(Color.white) - .cornerRadius(10) - .overlay( // ✅ 테두리 추가 - RoundedRectangle(cornerRadius: 10) - .stroke( Color.LightBlue03, lineWidth: 2) - ) - .clipShape(RoundedRectangle(cornerRadius: 10)) - .shadow(color: Color.black.opacity(0.25), radius: 4, x: 0, y: 4) - - } else { - HStack(spacing: 10) { - Button(action: onDecrease) { - Image(systemName: "minus") - .font(.system(size: 14, weight: .regular)) - .frame(width: 13,height: 13) - .foregroundColor(.black) - } - - Text("\(quantity)") - .font(.system(size: 15, weight: .medium)) - .frame(width: 20) - - Button(action: onIncrease) { - Image(systemName: "plus") - .font(.system(size: 14, weight: .regular)) - .frame(width: 13,height: 13) - .foregroundColor(.black) - } - } - .padding(.vertical, 6) - .padding(.horizontal, 10) - .background(Color.white) - .cornerRadius(10) - .overlay( // ✅ 테두리 추가 - RoundedRectangle(cornerRadius: 10) - .stroke( Color.LightBlue03, lineWidth: 2) - ) - .clipShape(RoundedRectangle(cornerRadius: 10)) - .shadow(color: Color.black.opacity(0.25), radius: 4, x: 0, y: 4) - } + QuantityControlView( + quantity: quantity, + onIncrease: onIncrease, + onDecrease: onDecrease + ) } } .padding() diff --git a/StockMate/StockMate/app/core/components/OrderRequestCardView.swift b/StockMate/StockMate/app/core/components/OrderRequestCardView.swift index f2fa6ab..99ffa7e 100644 --- a/StockMate/StockMate/app/core/components/OrderRequestCardView.swift +++ b/StockMate/StockMate/app/core/components/OrderRequestCardView.swift @@ -13,7 +13,6 @@ struct OrderRequestCardView: View { let onIncrease: () -> Void let onDecrease: () -> Void let onAddToCart: () -> Void - let onRemoveFromCart: () -> Void var body: some View { VStack(alignment: .leading, spacing: 6) { @@ -52,7 +51,6 @@ struct OrderRequestCardView: View { Spacer() // 수량 컨트롤러 - // 🪄 수량에 따른 3단계 분기 if quantity == 0 { Button(action: onAddToCart) { Image("add_shopping_cart") @@ -65,70 +63,12 @@ struct OrderRequestCardView: View { .shadow(color: Color.black.opacity(0.25), radius: 4, x: 0, y: 4) } - - } else if quantity == 1 { - HStack(spacing: 10) { - Button(action: onRemoveFromCart) { - Image(systemName: "trash") - .font(.system(size: 14, weight: .regular)) - .frame(width: 13,height: 13) - .foregroundColor(.black) - } - - Text("1") - .font(.system(size: 15, weight: .medium)) - .frame(width: 20) - - Button(action: onIncrease) { - Image(systemName: "plus") - .font(.system(size: 14, weight: .regular)) - .frame(width: 13,height: 13) - .foregroundColor(.black) - } - } - .padding(.vertical, 6) - .padding(.horizontal, 10) - .background(Color.white) - .cornerRadius(10) - .overlay( // ✅ 테두리 추가 - RoundedRectangle(cornerRadius: 10) - .stroke( Color.LightBlue03, lineWidth: 2) - ) - .clipShape(RoundedRectangle(cornerRadius: 10)) - .shadow(color: Color.black.opacity(0.25), radius: 4, x: 0, y: 4) - - } else { - HStack(spacing: 10) { - Button(action: onDecrease) { - Image(systemName: "minus") - .font(.system(size: 14, weight: .regular)) - .frame(width: 13,height: 13) - .foregroundColor(.black) - } - - Text("\(quantity)") - .font(.system(size: 15, weight: .medium)) - .frame(width: 20) - - Button(action: onIncrease) { - Image(systemName: "plus") - .font(.system(size: 14, weight: .regular)) - .frame(width: 13,height: 13) - .foregroundColor(.black) - } - } - .padding(.vertical, 6) - .padding(.horizontal, 10) - .background(Color.white) - .cornerRadius(10) - .overlay( // ✅ 테두리 추가 - RoundedRectangle(cornerRadius: 10) - .stroke( Color.LightBlue03, lineWidth: 2) + QuantityControlView( + quantity: quantity, + onIncrease: onIncrease, + onDecrease: onDecrease ) - .clipShape(RoundedRectangle(cornerRadius: 10)) - .shadow(color: Color.black.opacity(0.25), radius: 4, x: 0, y: 4) - } } } @@ -164,8 +104,7 @@ struct OrderRequestCardView: View { quantity: 0, onIncrease: {}, onDecrease: {}, - onAddToCart: {}, - onRemoveFromCart: {} + onAddToCart: {} ) // 수량 1 (카트에 하나 있음) @@ -174,8 +113,7 @@ struct OrderRequestCardView: View { quantity: 1, onIncrease: {}, onDecrease: {}, - onAddToCart: {}, - onRemoveFromCart: {} + onAddToCart: {} ) // 수량 3 (여러 개 담긴 상태) @@ -184,8 +122,7 @@ struct OrderRequestCardView: View { quantity: 3, onIncrease: {}, onDecrease: {}, - onAddToCart: {}, - onRemoveFromCart: {} + onAddToCart: {} ) } .padding() diff --git a/StockMate/StockMate/app/core/components/QuantityControlView.swift b/StockMate/StockMate/app/core/components/QuantityControlView.swift new file mode 100644 index 0000000..7f455ab --- /dev/null +++ b/StockMate/StockMate/app/core/components/QuantityControlView.swift @@ -0,0 +1,60 @@ +// +// QuantityControlView.swift +// StockMate +// +// Created by Admin on 11/8/25. +// + +import SwiftUI + +struct QuantityControlView: View { + let quantity: Int + let onIncrease: () -> Void + let onDecrease: () -> Void + + var body: some View { + HStack(spacing: 10) { + // 감소 버튼 + Button(action: onDecrease) { + Image(systemName: "minus") + .font(.system(size: 14, weight: .regular)) + .frame(width: 13, height: 13) + .foregroundColor(.black) + } + + // 수량 표시 + Text("\(quantity)") + .font(.system(size: 15, weight: .medium)) + .frame(width: 20) + .animation(.easeInOut(duration: 0.2), value: quantity) + + // 증가 버튼 + Button(action: onIncrease) { + Image(systemName: "plus") + .font(.system(size: 14, weight: .regular)) + .frame(width: 13, height: 13) + .foregroundColor(.black) + } + } + .padding(.vertical, 6) + .padding(.horizontal, 10) + .background(Color.white) + .cornerRadius(10) + .overlay( + RoundedRectangle(cornerRadius: 10) + .stroke( Color.LightBlue03, lineWidth: 2) + ) + .clipShape(RoundedRectangle(cornerRadius: 10)) + .shadow(color: Color.black.opacity(0.25), radius: 4, x: 0, y: 4) + } +} + +#Preview { + QuantityControlView( + quantity: 2, + onIncrease: { print("➕ 수량 증가") }, + onDecrease: { print("➖ 수량 감소") } + ) + .padding() + .background(Color.Light) +} diff --git a/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift b/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift index f644a0a..bd2d532 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift @@ -5,14 +5,6 @@ // Created by Admin on 11/4/25. // - -// -// UsedPartListSheetView.swift -// StockMate -// -// Created by Admin on 11/4/25. -// - import SwiftUI struct UsedPartListSheetView: View { diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift index ea68e16..11338a4 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift @@ -26,12 +26,6 @@ struct OrderCartView: View { }, onDecrease: { Task { await cartViewModel.decreaseQuantity(for: cartItem.partId) } - }, - onAddToCart: nil, - onRemoveFromCart: { - Task { - await cartViewModel.decreaseQuantity(for: cartItem.partId) - } } ) .padding(.horizontal) diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift index 764c7d4..be77a2f 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift @@ -12,7 +12,6 @@ struct OrderListView: View { @StateObject private var orderViewModel = OrderViewModel() var body: some View { -// NavigationStack { VStack(alignment: .leading, spacing: 0) { if orderViewModel.isLoading { @@ -72,7 +71,6 @@ struct OrderListView: View { .task { await orderViewModel.loadOrders() } -// } } func formatDate(_ dateString: String) -> String { diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift index ad81fdf..6a1811f 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift @@ -9,8 +9,8 @@ import SwiftUI struct OrderRequestSearchView: View { @Environment(\.dismiss) private var dismiss - @ObservedObject var cartViewModel: CartViewModel + @ObservedObject var cartViewModel: CartViewModel @StateObject var inventoryViewModel = InventoryViewModel() @State private var searchText = "" @@ -135,8 +135,7 @@ struct OrderRequestSearchView: View { quantity: qty, onIncrease: { Task { await cartViewModel.increaseQuantity(for: item.id) } }, onDecrease: { Task { await cartViewModel.decreaseQuantity(for: item.id) }}, - onAddToCart: { Task { await cartViewModel.addToCart(partId: item.id, amount: 1) }}, - onRemoveFromCart: { Task { await cartViewModel.decreaseQuantity(for: item.id) }} + onAddToCart: { Task { await cartViewModel.addToCart(partId: item.id, amount: 1) }} ) .padding(.horizontal) .onAppear { diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderView.swift index 3fa5db5..cc815e7 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderView.swift @@ -31,10 +31,7 @@ struct OrderView: View { // 🔍 검색창 NavigationLink(destination: - OrderRequestSearchView( - cartViewModel: cartViewModel - //inventoryViewModel: inventoryViewModel - ) + OrderRequestSearchView(cartViewModel: cartViewModel) ) { HStack { Image(systemName: "magnifyingglass") @@ -71,26 +68,9 @@ struct OrderView: View { OrderRequestCardView( item: item, quantity: qty, - onIncrease: { - Task { - await cartViewModel.increaseQuantity(for: item.id) - } - }, - onDecrease: { - Task { - await cartViewModel.decreaseQuantity(for: item.id) - } - }, - onAddToCart: { - Task { - await cartViewModel.addToCart(partId: item.id, amount: 1) - } - }, - onRemoveFromCart: { - Task { - await cartViewModel.decreaseQuantity(for: item.id) - } - } + onIncrease: { Task { await cartViewModel.increaseQuantity(for: item.id) } }, + onDecrease: { Task { await cartViewModel.decreaseQuantity(for: item.id) } }, + onAddToCart: { Task { await cartViewModel.addToCart(partId: item.id, amount: 1) } } ) .onAppear { if item.id == inventoryViewModel.underLimitItems.last?.id { From bf0123e5a1961dd31abf88e00f47af91b5b1b550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sun, 9 Nov 2025 01:09:23 +0900 Subject: [PATCH 71/85] =?UTF-8?q?[REFAC]=20=EC=88=98=EB=9F=89=EC=A1=B0?= =?UTF-8?q?=EC=A0=88=20=EA=B3=B5=ED=86=B5=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=ED=99=94=20=EB=B0=8F=20=EC=9E=A5=EB=B0=94=EA=B5=AC?= =?UTF-8?q?=EB=8B=88=20ui=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/feature/orders/ui/OrderCartView.swift | 60 ++++++++++++------- .../app/feature/orders/ui/OrderInfoView.swift | 1 - 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift index 11338a4..07fdcd7 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift @@ -14,36 +14,56 @@ struct OrderCartView: View { var body: some View { VStack(spacing: 0) { - ScrollView { - LazyVStack(spacing: 16) { - ForEach(cartViewModel.items) { cartItem in - - CartCard( - item: cartItem, - quantity: cartItem.amount, - onIncrease: { - Task { await cartViewModel.increaseQuantity(for: cartItem.partId) } - }, - onDecrease: { - Task { await cartViewModel.decreaseQuantity(for: cartItem.partId) } - } - ) - .padding(.horizontal) + if cartViewModel.items.isEmpty { + // 🛒 장바구니 비어있을 때 + VStack(spacing: 8) { + Text("장바구니가 비어있어요.") + .font(.system(size: 15, weight: .regular)) + .foregroundColor(.black) + Text("부품을 담아보세요.") + .font(.system(size: 13)) + .foregroundColor(.gray) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background(Color.Light) + } else { + ScrollView { + LazyVStack(spacing: 16) { + ForEach(cartViewModel.items) { cartItem in + + CartCard( + item: cartItem, + quantity: cartItem.amount, + onIncrease: { + Task { await cartViewModel.increaseQuantity(for: cartItem.partId) } + }, + onDecrease: { + Task { await cartViewModel.decreaseQuantity(for: cartItem.partId) } + } + ) + .padding(.horizontal) + } } + .padding(.vertical) } - .padding(.vertical) + .background(Color.Light) } - .background(Color.Light) - NavigationLink(destination: OrderInfoView(cartViewModel: cartViewModel)) { Text("\(cartViewModel.cart?.totalPrice ?? 0)원 결제하기") .font(.system(size: 16, weight: .bold)) .foregroundColor(.white) .frame(maxWidth: .infinity) - .frame(height: 60) - .background(Color.Primary) + .frame(height: 50) + .background( + RoundedRectangle(cornerRadius: 16) + .fill(cartViewModel.items.isEmpty ? Color.gray.opacity(0.3) : Color.Primary) + ) + .padding(.horizontal, 16) + .padding(.bottom, 30) } + .disabled(cartViewModel.items.isEmpty) + } .background(Color.Light) diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift index 3310f9a..accf114 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift @@ -55,7 +55,6 @@ struct OrderInfoView: View { case .tomorrow: let tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: Date()) ?? Date() return formatter.string(from: tomorrow) -// return formatter.string(from: Calendar.current.date(byAdding: .day, value: 1, to: Date())!) case .specific(let date): return formatter.string(from: date) } From 4198d58235c40fe367a1c388bc843ec4340a6238 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sun, 9 Nov 2025 02:29:30 +0900 Subject: [PATCH 72/85] =?UTF-8?q?[REFAC]=20=EC=9E=AC=EA=B3=A0=20=ED=95=84?= =?UTF-8?q?=ED=84=B0=EB=A7=81=20UI=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/OrderRequestCardView.swift | 1 - .../inventory/ui/InventorySearchView.swift | 53 +++++++++---------- .../orders/ui/OrderRequestSearchView.swift | 22 ++++---- 3 files changed, 37 insertions(+), 39 deletions(-) diff --git a/StockMate/StockMate/app/core/components/OrderRequestCardView.swift b/StockMate/StockMate/app/core/components/OrderRequestCardView.swift index 99ffa7e..b4c31f8 100644 --- a/StockMate/StockMate/app/core/components/OrderRequestCardView.swift +++ b/StockMate/StockMate/app/core/components/OrderRequestCardView.swift @@ -61,7 +61,6 @@ struct OrderRequestCardView: View { .background(Color.white) .clipShape(Circle()) .shadow(color: Color.black.opacity(0.25), radius: 4, x: 0, y: 4) - } } else { QuantityControlView( diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift index 6ec0a6c..b0afa5a 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift @@ -48,7 +48,6 @@ struct InventorySearchView: View { let term = searchText.trimmingCharacters(in: .whitespacesAndNewlines) guard !term.isEmpty else { return } Task { -// await inventoryViewModel.searchByName(name: searchText, reset: true) await inventoryViewModel.searchByName(name: term, reset: true) } } @@ -98,17 +97,21 @@ struct InventorySearchView: View { onTap: { inventoryViewModel.toggleModel($0) } ) - // 🔄 초기화 버튼 + // 초기화 버튼 Button(action: { inventoryViewModel.resetFilters(with: searchText) }) { - HStack(spacing: 4) { - Image(systemName: "arrow.counterclockwise") - Text("초기화") - } - .font(.system(size: 13, weight: .medium)) - .foregroundColor(.blue) - .padding(.trailing, 8) + Image(systemName: "arrow.clockwise") + .font(.system(size: 16, weight: .semibold)) + .foregroundColor( + inventoryViewModel.selectedCategories.isEmpty && + inventoryViewModel.selectedTrims.isEmpty && + inventoryViewModel.selectedModels.isEmpty + ? .black + : .Primary + ) + .padding(.trailing, 8) + .rotationEffect(.degrees(35)) } .frame(maxWidth: .infinity, alignment: .trailing) @@ -184,8 +187,6 @@ struct FilterMenu: View { var displayTitle: String { if selectedItems.isEmpty { return title - } else if selectedItems.count == 1 { - return selectedItems.first ?? title } else { return "\(title) (\(selectedItems.count))" } @@ -193,15 +194,6 @@ struct FilterMenu: View { var isActive: Bool { !selectedItems.isEmpty } - var truncatedTitle: String { - // 글자 6자까지만 표시, 이후 "..." 처리 - if displayTitle.count > 8 { - let prefix = displayTitle.prefix(8) - return "\(prefix)…" - } - return displayTitle - } - var body: some View { Menu { ForEach(items, id: \.self) { item in @@ -213,7 +205,7 @@ struct FilterMenu: View { if selectedItems.contains(item) { Spacer() Image(systemName: "checkmark") - .foregroundColor(.blue) + .foregroundColor(.Primary) } } } @@ -222,19 +214,22 @@ struct FilterMenu: View { HStack(spacing: 6) { Image(systemName: "chevron.down") .font(.system(size: 11, weight: .semibold)) - .foregroundColor(isActive ? .blue : .gray) + .foregroundColor(isActive ? .Primary : .gray) - Text(truncatedTitle) + Text(displayTitle) .font(.system(size: 13)) - .foregroundColor(isActive ? .blue : .black) + .foregroundColor(isActive ? .Primary : .black) .lineLimit(1) - .truncationMode(.tail) // 안전하게 "..." 처리 - .multilineTextAlignment(.center) + .truncationMode(.tail) } .padding(.horizontal, 12) - .padding(.vertical, 10) // 높이 늘림 - .background( - isActive ? Color.blue.opacity(0.2) : Color(.systemGray6) + .padding(.vertical, 9) + // ✅ 텍스트 길이에 맞게 자동 확장 + .fixedSize(horizontal: true, vertical: false) + .background(isActive ? Color(hex: "DBEAFE") : Color.Light) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(Color.gray.opacity(0.3), lineWidth: 1) ) .cornerRadius(8) } diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift index 6a1811f..8462058 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift @@ -101,17 +101,21 @@ struct OrderRequestSearchView: View { onTap: { inventoryViewModel.toggleModel($0) } ) - // 🔄 초기화 버튼 + // 초기화 버튼 Button(action: { inventoryViewModel.resetFilters(with: searchText) }) { - HStack(spacing: 4) { - Image(systemName: "arrow.counterclockwise") - Text("초기화") - } - .font(.system(size: 13, weight: .medium)) - .foregroundColor(.blue) - .padding(.trailing, 8) + Image(systemName: "arrow.clockwise") + .font(.system(size: 16, weight: .semibold)) + .foregroundColor( + inventoryViewModel.selectedCategories.isEmpty && + inventoryViewModel.selectedTrims.isEmpty && + inventoryViewModel.selectedModels.isEmpty + ? .black + : .Primary + ) + .padding(.trailing, 8) + .rotationEffect(.degrees(35)) } .frame(maxWidth: .infinity, alignment: .trailing) @@ -119,7 +123,7 @@ struct OrderRequestSearchView: View { .padding(.horizontal) .padding(.bottom, 16) - // 📋 재고 리스트 + // 재고 리스트 ScrollView { LazyVStack(spacing: 10) { From f48456536ee9d9f96712dbc8908e992ba8bc4fc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sun, 9 Nov 2025 13:59:40 +0900 Subject: [PATCH 73/85] =?UTF-8?q?[REFAC]=20=EC=9E=AC=EA=B3=A0=ED=83=AD=20?= =?UTF-8?q?=ED=94=8C=EB=A1=9C=ED=8C=85=20=EB=B2=84=ED=8A=BC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/feature/inventory/ui/InventoryView.swift | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift index 70063c4..bd9ebd3 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift @@ -91,15 +91,15 @@ struct InventoryView: View { Circle() .fill(Color.Primary) // 배경색 .frame(width: 50, height: 50) - Image(systemName: "arrow.up") + Image(systemName: "chevron.up") .font( - .system(size: 24, weight: .bold) + .system(size: 14, weight: .semibold) ) .foregroundColor(.white) // 화살표 색 } } .padding(.trailing, 20) - .padding(.bottom, 20) + .padding(.bottom, 15) } } .transition(.opacity) @@ -171,8 +171,7 @@ struct GridMenuView: View { .background( RoundedRectangle(cornerRadius: 24) .fill(item.1 ? Color.Primary : Color.white) - // 카드 그림자 (Figma 스펙: y=4, blur=4, opacity=25%, black) - .shadow(color: .black.opacity(0.35), radius: 2, x: 0, y: 4) + .shadow(color: .black.opacity(0.35), radius: 2, x: 0, y: 4) // 카드 그림자 (Figma 스펙: y=4, blur=4, opacity=25%, black) ) } .buttonStyle(.plain) From 3a62b31e6d21b8dfc96901994e2449b39647a015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sun, 9 Nov 2025 15:12:17 +0900 Subject: [PATCH 74/85] =?UTF-8?q?[FEAT]=20=ED=99=94=EB=A9=B4=20=ED=84=B0?= =?UTF-8?q?=EC=B9=98=20=EC=8B=9C=20=ED=82=A4=EB=B3=B4=EB=93=9C=20=EC=9E=90?= =?UTF-8?q?=EB=8F=99=20=EC=88=A8=EA=B9=80=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/UIApplication+Extension.swift | 14 +++++++++ .../app/feature/auth/ui/LoginView.swift | 3 ++ .../app/feature/auth/ui/RegisterView.swift | 3 ++ .../inventory/ui/InventorySearchView.swift | 3 ++ .../app/feature/orders/ui/OrderInfoView.swift | 29 +++++++++++++++--- .../orders/ui/OrderRequestSearchView.swift | 3 ++ .../bag.imageset/Contents.json | 11 +------ .../Contents.json | 4 ++- .../Assets.xcassets/cal.imageset/cal@1x.png | Bin 0 -> 198 bytes .../Assets.xcassets/cal.imageset/cal@2x.png | Bin 0 -> 274 bytes .../Assets.xcassets/cal.imageset/cal@3x.png | Bin 0 -> 413 bytes .../profile_sample.png | Bin 504 -> 0 bytes .../stockmate_logo.imageset/Contents.json | 4 ++- .../stockmate_logo@1x.png | Bin 0 -> 10210 bytes .../stockmate_logo@2x.png | Bin 0 -> 27579 bytes ...p 148272 (2).png => stockmate_logo@3x.png} | Bin 16 files changed, 57 insertions(+), 17 deletions(-) create mode 100644 StockMate/StockMate/app/core/components/UIApplication+Extension.swift rename StockMate/StockMate/resources/Assets.xcassets/{profile_sample.imageset => cal.imageset}/Contents.json (73%) create mode 100644 StockMate/StockMate/resources/Assets.xcassets/cal.imageset/cal@1x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/cal.imageset/cal@2x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/cal.imageset/cal@3x.png delete mode 100644 StockMate/StockMate/resources/Assets.xcassets/profile_sample.imageset/profile_sample.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/stockmate_logo.imageset/stockmate_logo@1x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/stockmate_logo.imageset/stockmate_logo@2x.png rename StockMate/StockMate/resources/Assets.xcassets/stockmate_logo.imageset/{Group 148272 (2).png => stockmate_logo@3x.png} (100%) diff --git a/StockMate/StockMate/app/core/components/UIApplication+Extension.swift b/StockMate/StockMate/app/core/components/UIApplication+Extension.swift new file mode 100644 index 0000000..f239309 --- /dev/null +++ b/StockMate/StockMate/app/core/components/UIApplication+Extension.swift @@ -0,0 +1,14 @@ +// +// UIApplication+Extension.swift +// StockMate +// +// Created by Admin on 11/9/25. +// + +import SwiftUI + +extension UIApplication { + func hideKeyboard() { + sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) + } +} diff --git a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift index 4f1da94..318a598 100644 --- a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift @@ -114,6 +114,9 @@ struct LoginView: View { Spacer() } .background(Color.Light) + .onTapGesture { + UIApplication.shared.hideKeyboard() + } .ignoresSafeArea() } diff --git a/StockMate/StockMate/app/feature/auth/ui/RegisterView.swift b/StockMate/StockMate/app/feature/auth/ui/RegisterView.swift index 2e29490..54620b7 100644 --- a/StockMate/StockMate/app/feature/auth/ui/RegisterView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/RegisterView.swift @@ -177,6 +177,9 @@ struct RegisterView: View { } .background(Color.Light) .ignoresSafeArea() + .onTapGesture { + UIApplication.shared.hideKeyboard() + } .scrollDismissesKeyboard(.interactively) // ✅ 손가락으로 스크롤하면 키보드 자동 내려감 } diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift index b0afa5a..85fc573 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift @@ -170,6 +170,9 @@ struct InventorySearchView: View { } } } + .onTapGesture { + UIApplication.shared.hideKeyboard() + } .task { await inventoryViewModel.loadInventoryList(reset: true) } diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift index accf114..3a47ff4 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift @@ -18,9 +18,11 @@ enum ShippingDateOption { } struct OrderInfoView: View { + @Environment(\.dismiss) private var dismiss @ObservedObject var cartViewModel: CartViewModel @StateObject var orderViewModel = OrderViewModel() @StateObject private var depositViewModel = DepositViewModel() + @StateObject private var userViewModel = UserViewModel() @State private var paymentType: PaymentType = .deposit @State private var shippingDateOption: ShippingDateOption = .today @@ -71,6 +73,9 @@ struct OrderInfoView: View { ScrollView { contentView } + .onTapGesture { + UIApplication.shared.hideKeyboard() // ✅ 화면 아무데나 탭하면 키보드 내려감 + } .padding(.horizontal) .padding(.top) @@ -79,9 +84,25 @@ struct OrderInfoView: View { .background(Color.Light) .navigationTitle("주문/결제") .navigationBarTitleDisplayMode(.inline) + .navigationBarBackButtonHidden(true) .task { await cartViewModel.fetchCart() await depositViewModel.fetchDepositAmount() + await userViewModel.loadUserInfo() + + } + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button { + dismiss() + } label: { + HStack(spacing: 4) { + Image(systemName: "arrow.left") + .font(.system(size: 15, weight: .medium)) + } + .foregroundColor(.black) + } + } } .edgesIgnoringSafeArea(.bottom) .onChange(of: orderViewModel.isOrderSuccess) { success in @@ -183,11 +204,9 @@ extension OrderInfoView { .font(.headline) VStack(alignment: .leading, spacing: 8) { - Text("홍길동").font(.system(size: 15, weight: .medium)) - Text("서울특별시 강남구 테헤란로114길") - .font(.system(size: 14)) - .foregroundColor(.textGray1) - Text("010-1111-2222") + Text(userViewModel.userInfo?.owner ?? "이름 없음") + .font(.system(size: 15, weight: .medium)) + Text(userViewModel.userInfo?.address ?? "주소 없음") .font(.system(size: 14)) .foregroundColor(.textGray1) diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift index 8462058..26c2886 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift @@ -196,5 +196,8 @@ struct OrderRequestSearchView: View { .ignoresSafeArea(edges: .bottom) } + .onTapGesture { + UIApplication.shared.hideKeyboard() + } } } diff --git a/StockMate/StockMate/resources/Assets.xcassets/bag.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/bag.imageset/Contents.json index 66aacdc..0ce9f32 100644 --- a/StockMate/StockMate/resources/Assets.xcassets/bag.imageset/Contents.json +++ b/StockMate/StockMate/resources/Assets.xcassets/bag.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "filename" : "bag.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "idiom" : "universal" } ], "info" : { diff --git a/StockMate/StockMate/resources/Assets.xcassets/profile_sample.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/cal.imageset/Contents.json similarity index 73% rename from StockMate/StockMate/resources/Assets.xcassets/profile_sample.imageset/Contents.json rename to StockMate/StockMate/resources/Assets.xcassets/cal.imageset/Contents.json index 54602a7..77d19ae 100644 --- a/StockMate/StockMate/resources/Assets.xcassets/profile_sample.imageset/Contents.json +++ b/StockMate/StockMate/resources/Assets.xcassets/cal.imageset/Contents.json @@ -1,15 +1,17 @@ { "images" : [ { - "filename" : "profile_sample.png", + "filename" : "cal@1x.png", "idiom" : "universal", "scale" : "1x" }, { + "filename" : "cal@2x.png", "idiom" : "universal", "scale" : "2x" }, { + "filename" : "cal@3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/StockMate/StockMate/resources/Assets.xcassets/cal.imageset/cal@1x.png b/StockMate/StockMate/resources/Assets.xcassets/cal.imageset/cal@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..e1c01f1ae8f0e77bf926f87fb200b50de6d00e49 GIT binary patch literal 198 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&k&H|6fVg?3oVGw3ym^DWND9BhG z&J^c)vS-XYX6WwONj%F{iqJC(s%OPgg&ebxsLQ03?+^SpWb4 literal 0 HcmV?d00001 diff --git a/StockMate/StockMate/resources/Assets.xcassets/cal.imageset/cal@2x.png b/StockMate/StockMate/resources/Assets.xcassets/cal.imageset/cal@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0ea001a25016db3785837a199e6f535b098a3254 GIT binary patch literal 274 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjoCO|{#S9E$svykh8Km+7D9BhG z)w}UXHQGD%%!>ois$;byYzd_f2G+p!}`}`sUYuFp7BSld2H8+ zuk8MD{7JE*`mW-xCGDCE*DMf~|M=tE=Z(1>wld}cKN8;_Y5Hs_)+KxSPG{>`ukTzn z9{e{}Eclb~?2E3E{Dj3%n07koZP=@2;kUQ@U6GQHio6W>od?3l;|`rU!f=1Sq5)To RU|k1|%Oc%$NbBI14-?iy0UcEkKyjb(&!UP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBIU`RO$B+ufx6`co4jJ&YoptVbe&Q5chjhaX z2OWdLV7|s3EFLio0^ABJE(gPGLzZSH-8%5d|ChLV=l>&l(hREhTq~qj>CA9Wj~2 zy&!A_-zvVmJAu_#gtqpsx$C@kGS5sYlLxGQ6C>syk9Q2)#nmEh?~?RLZFzF;@uZYA z#h9K`_U-5EGK>3s+BWUk#TxE>ZZ2Qq+xceTB?|*{@oPEua2y)%0GVAy>h?{-nL}FVdQ&MBb@ E09RZvV`bwwOsOmCJ=cxTzoPc+nur6)SYtWJc4y5`>AWHNEOa{2z~*mrRgnr>LTw=D73U2-mL`fhO< z9{1fznS$^5%HDRRP84z!aoKfe?}OQ!1g8lFDi(gZ{Y=c&{NMM|3susKm~O6%@M39w nJ+*cUM_NB>h)D?E{lSoYwq@e)e&&0?h+^<`^>bP0l+XkKAq~=j diff --git a/StockMate/StockMate/resources/Assets.xcassets/stockmate_logo.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/stockmate_logo.imageset/Contents.json index cbb69f9..10b8a40 100644 --- a/StockMate/StockMate/resources/Assets.xcassets/stockmate_logo.imageset/Contents.json +++ b/StockMate/StockMate/resources/Assets.xcassets/stockmate_logo.imageset/Contents.json @@ -1,15 +1,17 @@ { "images" : [ { - "filename" : "Group 148272 (2).png", + "filename" : "stockmate_logo@1x.png", "idiom" : "universal", "scale" : "1x" }, { + "filename" : "stockmate_logo@2x.png", "idiom" : "universal", "scale" : "2x" }, { + "filename" : "stockmate_logo@3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/StockMate/StockMate/resources/Assets.xcassets/stockmate_logo.imageset/stockmate_logo@1x.png b/StockMate/StockMate/resources/Assets.xcassets/stockmate_logo.imageset/stockmate_logo@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..0b4cb42c922d15c8cbe67d1fb9dbbe6f10829003 GIT binary patch literal 10210 zcmV<8Cmq;{P)iopdx~bC_%&mSU`|22+|P{kkEvP#L!zv zLLfjwLP$v2&Gs_oyLWbGvsuFPh!4K_zTX~x!%SvoZ$0Op|M{Q03qVkU5|p3>B`A+W zF!0Y5j6ax>EqBE!3H1O9^GM{*=sinvTc7oR4uTSt$0+|q1LE5BRTFzuR9o2v3l2iv z3=1$80~pDGPztwRxasIU@4WmZd<_RdBMQo66de5X1beoAX;|2kvZ5l2HdHwP>vUtl z?g7B=z`-qwk~e83&3LO_Ne}og3xX1q$0+|a1L9!W(2ikg#TFX+9Rg=@fHAO@&;bDk zJ!MI}q>7=tGz&inK#Bn6f)bR+BL6f4V*52(9ODpyW(*)O3Kp z13)G>8`=#7y--jdqmbaQ3sPkk2nw8-LUs;Si%N}dF>aBwKo9R#*7B(e@5#^@0PF4F zompAi-TV^~JUlJ{S|B5#3Fui^Z-66MIm`ctj7b77Z6r|#1SKesQT~bnA;^Sunr<|7 zWy?1G$IS9bMcri#Ft8pVs>c8fhc1JFnvcOV#(K{<{2SfJ;F;u~gAP;Jfx}r(;f=CS zHmwhz%N340B0apt-+v83)C9jRYOIaLra<6MoO&H8KH_K00h7ES57M=v^q9hi)}04Y+2wYa$5W zg7R49VGM{1*qcn)qi^}#pfcwTinSV{9y25k|Hn9uMux$AiW*AKRu|wah<^u%cE`Ga z0OP#H;9d`d@h`J%^#_HrXx>W&B`A+m9>#zW7skXt5ANA=JQNE=BvfXo{R|_M;W#4! z6!bn;n5uD!7)YAY=$RL;;XI7PcvvUgBRpO#2(L}PT8L`GK%fUO-ttg+EPo$8r?@wR z5|qa$f7XDIqS6Dlx_|NHkr;!CNa!k95(Yh(R%FRMoAsRQ)`~y!QonV9zb;~UxCO=y z9he_p!vai&gK)%u1>uAe*}wSwf0exd;ipfXI&(c)A(xhWXqUK2)96c!=g(aU{(E`Y zWcGvJ_ggam_sE|%pxD)30Wj=y|`h+nk)Qgg#I!A zq1=D00$j-{YTJviCP^`34}Qx2O$G<6X0QmGajf2;NsPLYcJe# zj5_!G4EQ^W7 zVN`zpMKgHB2J~k<2NvCPl0nCUM2aC;7O`hC7B9|m41-G&PaQm3p>XekdP@5p$Y>ZK zP}tzF2r?f0;K`e&U-oB=DY%hK7$RbTR7?Pw1P3ae29S^*)Q0GR2)enlqI;T^D}H$W zdO_te;5YCqHf)H{#dIr`MLkg_kLXwypD?6s!h{w7O-}7^7mUIY9plwtIEiYF0X(c6 z*|~*^ZQKfI!gW9sp$B4R7_&DuD;0Rh=07cG+(JBj)YQ2b&Sw$2NCS(?A=QyUtyEQl zM=R{7@2B)OQguq?5l@tf!s74sf9~ac@Hgn{pD`e&?$Eij-FF%9`^cPixG_CNz7u!{ z?a{&(qS^ydShcYTXLL}4A%TY$ChrcLb^hcfrP?54SWM2Ct#-INtARnUb3}%Pn-wwz zC6cH>MOis)3&l#IN4isVeKhbR77K3Q234jCP*rXM<&_nnsJPe}6?ON|j>F$geAHe( zj$wiu$5SfR? zaQlO~k1_P4-w`7%zUwwwsz7=9J*=|461!7cS_S?FsjcBy;Qi2W-4u@pkDRIrHI&z@U*CKrbt~HW?ej2VPe>I>;2f=p21_X>Sm@cP zfa*lG^v{REn>KB#ii(N~Y1F8b+V36o{Lif9K1FTQri~&hstGKokx-71Zmuzhw1+`! zA7J2t&ID1b&WdZVk}Hc4Di;J6gDW)(`pl(*rLg}bc<_SE#Yi_xz~+*|B8dK2QfTM{ zzLiqL!iNnTCaza6P8%H^ZD`rDrPA-cz*`OmADht*$ocvq9qsX;odYB*+z<(ELl1251aY^><`Jr>9s!zUZH`z^_CZ6o&;@~s z6Y@nMkWfI|dHxJ&2$0CA27OPRKMwSvA-LN^fnLvaxw!Ag?}njb&rLkSuAGSSxd1VV z*OqojO&YU*Z0=s*)OFwmEbPiB%+*O6Y#^gG@TL=y*x-wHZfKci#}| z<%Si#;x6~MI$SiFE=O*<6=2F)K(P`Q?HoXjp+tac z;8tE?+dK(zwf7z-1o*V~jOK>ZbEet=F#aAEuv3AAH=P(L&prr3Q6mtuhCC2*?<@nV zlc#>UAi%f&VOhA787JCl!y;h89tay)ASb0l;Li5-^b4Dsrg%;ngs%LhJ zfmDjK3=PehqXcR8=FK~C3?Tw`iyPF5qMHVcD>eD;xr=9gwrq+WE+Q(u7WRYT#;P1E zB5&U%^=U`!gH<~9;L)#s_{;iLa|eR^jfhi{d0(uUwqo5+3yW{x0je;*9O#ZKaEvS{ zykl?}jY-J&FzD$MCvwJ4pFVx&f&~li1(eP8<61tp!)i6MQkfjD@p**he6z}l z=fh8y%v-zer zKX&X~W3@^F6iOKb1t*}I(;|laZpGGkf+T=gsse7ir!iljKrCTJmE?ta^H;@@DkYp> z5;<55XIM~LQXqGG=(v*^nLCnRo{{&$fDSE(4%c^pljZ1x#6|bS)EAb3 zz~-+Rw&pXgIVAp2@mGg-PiC1ayfy=g1NO-AGd*8<<^74ET2UW_;kgyn>TtmEasZ09 zB3Qtn6@-fjpb@#DHPf(T3}Xcz1{cPao7DGbFHK##?8hk*xr)|?XjmMgVXNHs17AGhuuFm~dc9KKD0k40zCOXFs&o|Up>q21-Awc$FJz{IT8=m5A} z323VWxIHdRq0_<@-c9e?cWlC&?=AWb@Wz9YY<1sQj@o4X+xXhlkC&}l_wyTJ4eNQt zA~?kqhWG8+A?47|t7ieP@p`=qTJ`wQmu484J%{OR)`i;~M%HB09^~Ki#|HHD_vd## zb8?Aj+pl_Xx#k_$qTkw{ypJ&-~p=3VEMTNLOJ|9xc~SO_s2i}F_GZx zn7=?>l$YGxtyPD@UgKz=1SXM-sWlsSEKeLTzWjyJZ+`poE6H7^Oqm@RllWQ}*S6`c z_%2WW(YbxQLk^dV0Q?Rnz)&FZsn&bCc4_-dmnYh%w`yGHBL5L;@{Es@KVP__Z)kJ` z^4Gv*v|>`R)b?zjE}uH?<|K7&8vahFxORn=pT>nm zYN7G4z;0tj{d=|lc-O|Ivq50M>Me&mV1FQUsWOclJ}7DOw7J>3h;T*~9?BfexPsuG zo*y%M@V~}R+V)4G6BlOF@{>TWb7td07 zgv56nS(10-MwnVFr!QX0e>y%sKFYVc684G)EQ8fp`dPojcMnt>M6HJ~1Z=>2O!fD9 z1S4X3V`KcLNPuV@+=Z zp+=|;dZ5r?pvvyTj+{6hdo2CJyB~kHw&3{}r|bg(Ro$(sQu|yg{Le#q@NL_+1yUN+ zn?Wg*>ol?a-!;8^MT^fa5|F9;l?7f7MC;BvXZ6P>!; z95!Oo^i`V`tuqetb~t#(WzBEa!O zM|BGq|2Pee8G<<_ChR+vQUvjnj17;C0ijCQSex122@qZkiE;5U8wj06TQ{xRvS-it zmfGsxym{T#h!904(s-l|a=8xV=jU|?ezBgp%3=VxcS?}6g|M-$F6i8K;G4ceA*ONrW2YLPg+I9U zi*F(u=yljyt_82+OqpfD!`hu*pThzL!GD}58o$&Cef5k+_ zj&QpejDZmLAsvvnn^1Yz|M2;s#Kb|(RH0AIyk{;OeDzAc+U9ZqiAW4Ykg<^vI40IA z(Ox_1r=O1vnELLb3txZnW;_rAdpqB5fhmFHV|I3Spc1fIc}4w68G&3aX0uzts29il zviPfG{-Y!aH>#~0*ZC$V_u3~K4Xm5-ux;BVbUTy2A9U#4E$QUtEkLc;Vph8yBzEqS zwtM#y69D&ZO+n@0AAB$aK}Z|~k_WuVq$Id}t#G)*abu)bC4ym$M`VXF)Uf9V?fG%} z90c1C9bD_aMhqD^8b%nGkc5a+t`LQ%w@j&l{IrJ&trJ5p`+nQP2@@tPzI3Z<;n_b< z;~Im8DJv}{-kLsc&NnI3JU#pV3n3jY3`2k?0K$0_O;ARUo|d4Ctvm31#>sk+HB_K) z_=^b=dVR`RP+A$uG7N|RG00HaZB{7&wYQklZld<(HvPio4l<>jdrz&AgUgvWUmQJd zMmMY5Ns=hnf#EC*X+s#-i0pQ$nM7Nt861;|NN~?YHL7Wq{>I}=xUiI1%#ySmcObe1 zHvy^J2}4X4BiUM@n_w`3o=3PR7>>`7{633>km(z^+~EA>3+J9Zcdmo~3W$_qF90c{ zpC34NX-t$lS%k3+czEIBinO%7SK)ayx{;KWRM+K_b!An`L3eMPyI;x930LWq3`5hT zAyi8*`f6p|2{`+D?E?r5!;wJ} zF#$;;nncP287uZs>cMB{&)?E;#*7zk0#N(DF)uOd?J!MyAAkMzygR*~eQ9I*>D*BY znV7v(bQ|PGDi2)C%T3UR=n+)L;ybl7L*q%|%3x_0#acLy2UfwM_9;`Qh%Vi*;p8|dQZ3DrY; zYBwNLWrZFwb7)Zzi6qpe>{|oRUb?}>h!`deBF5#RSx6LV^bqPnR)c`^aq-gG2BiPU z1j03V&|*1D=9}43neSkm&}*IEbBv47iPzXID7J?niv`u|M<8_Lr_ew4!Te(eWlpDiouegy{~s?iovRva(X#JNBi#mNd|k=3#Wv;IByy<=VM( zCyH}^ zWFYzi3T~qbX3NjJP;jjP56DFc+yiPOf%%4!pv;BiTefcfue@tlF{MVuo;rW62h2?| z1k9D0?N+>wLGt_sp$y>7OQB&yq#lF$Mv$Y-?p7)8o;-C@tJX;=mQ_Rjm*Vy6#@cbf zB^zP75eNAB)XLlqV(T%K2a$x($bmQJ<6<>#*Vycs*=o#bJwcOnKchR{`8;jxPm+4FW`^`2! z`9!@__3KT%*zL*LuAu`NZj^TJQ*Aw+8S@o*xSins9@jr+@izhDyD#URXx1WZ4(0Y@ zb3!b|?%uiC00ewM7+4Cv)}lp=`^;$Mm?6KLiVHbyMHFhCm?oWTX$KJc@O%2Xeu@wS zx5B%fRyHA_!%glXW-DMIti|C!xmE=Z9z8R-mOdcy-^oA#|FL0fejx-WaF5;OB>VPh zw+SUY;A5CW0oavmx8$9kei7}wK-zGhp)#~LjzZI4P2vJ3EiKJSSKUdr*0>8JCj&j2lC(|m)1gIW!!WBZOpPM3|l zuDnxBOnf=%)icL7$}J^lbhgrSI!ozUof%%Eb5&7>&RA5ev)(KF;CGs1sXs4( zw+<=hayto)icu;=^y!mlJ1t$h0TCFU!13Xr4Pd#XCWF-&GvdPO18X|AZ+DNTybBKw z0}lz|!C6PtVBnvuhATaSRcP@+Yr200F^w8MN6HBn=9qA)Oo5f&x;$pfmLkz1Mf1H5!AjQ^~pEH(2=9f!3a&skkEX?B)0f&>3y)ra07yRc9j5VZL zvyO)J^ea90AHVTy*WNGP>OE-m#4%&uibZw$)i0hndDfe|4rWYIs-z537`M#@BD9Ll z!aJ*1uXj07oQ-zHVCcIx*X6x>4Ihqrh4yXj-L@?jwd;pftFoHbkIb?;Y=|wwNfAyR zJ$>VsH{Shx3ap0<0ML2DYwrv=lVi!W+p1t}$>I(dBoI2ZDPH#NXTPC$Zr{!{Mu*4_ zS_ka)2B~w32xtOx1&rZA7V~ zzTeP}?=N0+_4S8h@bJK7Y^+v1x9Paz4#LxBWAnR2g@w67kfGP zM(pv7?BzcnId{uO)UE2=dwgl6cWzOT9V1{~iBU;Zc*l-0NO6Qf9)ISkCWBhF zZq7Iy&>s>Qf#R2?D>r_WRS2xz22LvK(r3JB-e>F1-!8hRfH@iVPH{Q?bLzom(4e_C zvm`EzCI=CV3U3PI=0N?bA0_G(9u?(66ewK&Ntss3Z2b9Rrcgh65e4heO>yxLR1JVr z_c?#;i@6_Vjv6^&ucfS%h>413`##lr7zo%O$1R2e{6n#rABJxf5xUW4gP|i_Diko$ zee%N9cSg^cx2j9GXL=+g_8*usZ{-iWe>!%Oz{M~=qWSGb00f{}e9xXO#gJpLbB4ji zpcu95(7D4shfXQL6*1){Ra`uapeHrBZ|B~$3l0!Wu&Y%H=IYIxy7fCwWOnU4v3$^r zGYg&?IHB~4NmB`v%~Og>3Oq|!>^aw~*WlLZ^EC{J4WOf8wYal_rkga4Jk$Ft_Ew)S z+2Tfx>ZZFYX$m=W0fw>uo+l8lftp^a)*O?I32m2HIWa!*?lh%Ak?eNjKE_UnQ?Z)m z;phkCG>Z5-;2|WYas6nTazo0~jgc^lRjCymz{$(IF28y$JM3z1cKDs*B9S)SfC4k3 z%47j*ksSMa&dbd}%|#cBP!pqU<|=Ob;p&ezbAk)!FT@rXm8#BUWW+)!#c`wa=g+rv ziwkesq(L3rY<0r!(3n2Vz!nx35|=NYkGXNfGhK&p*!t zesBYp=Xv?Lr=!ASLKst}873b92-Ry&8xK5iprdxFKAPz4+XK z=Jp;?9XG|U5!DaTKClm=I>#~v($)wp!()oT99XfQttTwA} zYJGc${mMb_t^W3-XH#ZQY&?0wt07~@j&*{7MGg)agYmeZ%La*jO~DWD-@NUqF75Lx zjdrw;8Ykp(YV-0f<8uq?Uo&r2?fqoIlF<%m<_eV*E=4rs-BG~;sr0^ffcTct$u^?{ zVnn!j!PN^=(BRVPv$5r-GSW(cI0)4c1|;WxG&TLDr0&m2#d6YSvcY_o2uPJOh%7P< zzoi|G&CbjUGh59-YtS<;o0BZezX^s@Er>=hXDT{=E-8 zJ+!TA$dDn00b_-4-MISXtao1XsXUy~+5wZU>T@zz_f z-iuM%WJ3q{*r4AiK-LoX<3!Uw=4zc=NUKSH_L*H{D_Jz!ZoC(?^7ZWedLR^2YRqOHf;8%$g2?7T3{DfDPDme{JWw-XKrfVFCw$p-fm#!+!tcZRer%CHaQ;5`D?R*s!cSvyXHb2QhkcApLgD5L_qwgW zD#n6`6W?zepty%2<;}ZwACOd9QKhUjmMf%?KWY)z%62;K*u{HyukXsp$l%}&sJS%w z^Z*|2e*OAM+H~oeTyodgKrEKBR#W*MlCJ!2!-fquP)!93>=>1pI7}H9VHn=1b=wwK zbFaywBEwxChq0*W#+9|d{PK(O-_!>{HR(VPOGy#u?mLw9{D_I|v$Hd$;bAdUNnviG zxgvjcYHF$(_-(mIpM#5l=6X>@O9??hvYHVyB!2H)Mp`bYsodP|gQuS!(%xb*sbB_? zVhGY{&>IX#4*YZ&!a=O0!qMJsv*I+3Gb(*FcI?oGQ{dqQ>(i%?uC&VD9D~iEShu)- zr0(jTJ$vp6Z577qg%`%wsoS{a_@cWNT7763bNlMWJ9)-}<>3b&p!LC3Ja+}B~oJuce`m26Q<8QbMnASI8H854KIDVF?#eQ z!};8-7oQ(Gx=zluTyev?4cw=H|Lw-Tk}K;kU%qVfpQD-v)bsU)$iw&>NT8?T_rsz6 z#<;(c2(gdw6$bu2pYgE%M#LM0zwuza0pUEWSHp<-Awi6S6i%_#Ods2{U(^HqhwUao z3CiCsKK2PF^+kaOai8G`K|JlkDX}CS8jJ7CxZGDBxV;(tcivdDnT8Ft;scPDr zGZ4W4?62UJ5Jf8djT+!WSvqJbG(nh+ca1i?^YgS*4Nrie@dV``AU*?PDAJdS3S`p) zi2-?t1f1ADb)f0uHzx2uF9M7S8c$FjyZ8cQiLzxXzc-Tc&+XKfKjC#b-b{JGr)J5tx5d4! zIF_z3031@`FyRPqLLP(U&muA8-(y60?zwAS!9(l-2ue`?uS%d1aI|Yg*DEbTV@jT9 zTnzdWJLM$;Sey}s+C2rw7v-ILL!e0?<pgaRtisEC2ui07*qoM6N<$g1{icbN~PV literal 0 HcmV?d00001 diff --git a/StockMate/StockMate/resources/Assets.xcassets/stockmate_logo.imageset/stockmate_logo@2x.png b/StockMate/StockMate/resources/Assets.xcassets/stockmate_logo.imageset/stockmate_logo@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f7ea6fe8aee487bd7673176430d6243569cfcf06 GIT binary patch literal 27579 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 Wr-5>^^1oPHVAA3WVl^U$!T$%!kS}xq literal 0 HcmV?d00001 diff --git a/StockMate/StockMate/resources/Assets.xcassets/stockmate_logo.imageset/Group 148272 (2).png b/StockMate/StockMate/resources/Assets.xcassets/stockmate_logo.imageset/stockmate_logo@3x.png similarity index 100% rename from StockMate/StockMate/resources/Assets.xcassets/stockmate_logo.imageset/Group 148272 (2).png rename to StockMate/StockMate/resources/Assets.xcassets/stockmate_logo.imageset/stockmate_logo@3x.png From f8c2c67dfcdca261191c7850134250c45f2c1ae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sun, 9 Nov 2025 16:57:24 +0900 Subject: [PATCH 75/85] =?UTF-8?q?[STYLE]=20=EA=B2=B0=EC=A0=9C=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=95=88=EB=93=9C=EB=A1=9C=EC=9D=B4?= =?UTF-8?q?=EB=93=9C=EC=99=80=20=EC=9D=BC=EA=B4=80=EC=84=B1=20=EC=9C=A0?= =?UTF-8?q?=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/feature/orders/ui/OrderInfoView.swift | 98 ++++++++++++------- 1 file changed, 61 insertions(+), 37 deletions(-) diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift index 3a47ff4..e97a518 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift @@ -14,7 +14,7 @@ enum PaymentType: String { enum ShippingDateOption { case today case tomorrow - case specific(Date) + case specific(Date?) } struct OrderInfoView: View { @@ -26,7 +26,7 @@ struct OrderInfoView: View { @State private var paymentType: PaymentType = .deposit @State private var shippingDateOption: ShippingDateOption = .today - @State private var specificDate = Date() + @State private var specificDate: Date? = nil @State private var requestMessage: String = "" // ✅ 모달 관련 상태 @@ -58,7 +58,7 @@ struct OrderInfoView: View { let tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: Date()) ?? Date() return formatter.string(from: tomorrow) case .specific(let date): - return formatter.string(from: date) + return formatter.string(from: date ?? Date()) // nil이면 오늘 날짜로 fallback } } @@ -195,6 +195,8 @@ extension OrderInfoView { paymentSection shippingDateSection totalPriceSection + + Spacer().frame(height: 10) } } @@ -224,18 +226,34 @@ extension OrderInfoView { } TextEditor(text: $requestMessage) - .font(.system(size: 14)) - .padding(.top, 4) - .padding(.leading, 6) - .scrollContentBackground(.hidden) - .background(Color.clear) + .font(.system(size: 14)) + .padding(.top, 4) + .padding(.horizontal, 6) + .onChange(of: requestMessage) { newValue in + if newValue.count > 50 { // 50자 제한 + requestMessage = String(newValue.prefix(50)) + } + } + .scrollContentBackground(.hidden) + .background(Color.clear) } .frame(height: 70) .background(Color.white) - .overlay(RoundedRectangle(cornerRadius: 10) - .stroke(Color(.systemGray4), lineWidth: 1)) + .overlay( + RoundedRectangle(cornerRadius: 10) + .stroke(requestMessage.isEmpty ? Color(.systemGray4) : Color.Primary, lineWidth: 1) // ✅ 입력 시 Primary로 변경 + ) + HStack { + Spacer() + Text("\(requestMessage.count)/50") + .font(.system(size: 12)) + .foregroundColor(.gray) + .padding(.trailing, 4) + .padding(.bottom, -15) // 박스보다 살짝 아래로 + } } .padding() + .padding(.bottom,7) .background(Color.white) .cornerRadius(16) } @@ -244,7 +262,7 @@ extension OrderInfoView { private var orderListSection: some View { VStack(alignment: .leading) { - Text("주문 목록") + Text("주문 목록 (\(cartViewModel.items.count))") .font(.headline) .padding(.leading, 5) @@ -266,7 +284,7 @@ extension OrderInfoView { Image("deposit_background") // ← 에셋에 넣은 이미지 이름 .resizable() .scaledToFill() - .frame(height: 185) + .frame(height: 190) .clipped() .cornerRadius(16.39) @@ -302,8 +320,8 @@ extension OrderInfoView { } label: { Text("충전") .foregroundColor(Color.Primary) - .font(.system(size: 14, weight: .bold)) - .padding(.vertical, 6) + .font(.system(size: 14, weight: .semibold)) + .padding(.vertical, 8) .padding(.horizontal, 14) .background(Color.white) .cornerRadius(20) @@ -351,17 +369,18 @@ extension OrderInfoView { if case .specific(_) = shippingDateOption { return true } return false }()) { - shippingDateOption = .specific(specificDate) + shippingDateOption = .specific(nil) } .padding(.trailing, 5) - if case .specific(_) = shippingDateOption { - CustomDatePickerField(date: Binding { - specificDate - } set: { newValue in - specificDate = newValue - shippingDateOption = .specific(newValue) - }) + if case .specific(let selectedDate) = shippingDateOption { + CustomDatePickerField(date: Binding( + get: { selectedDate ?? Date() }, + set: { newValue in + specificDate = newValue + shippingDateOption = .specific(newValue) + } + ), isDateSelected: selectedDate != nil) } } .frame(height: 35) @@ -400,12 +419,17 @@ extension OrderInfoView { await orderViewModel.createOrder(request: orderRequest) } } label: { - Text("결제하기") + Text("\(cartViewModel.cart?.totalPrice ?? 0)원 결제하기") .font(.system(size: 16, weight: .bold)) .foregroundColor(.white) .frame(maxWidth: .infinity) - .frame(height: 70) - .background(Color.Primary) + .frame(height: 50) + .background( + RoundedRectangle(cornerRadius: 16) + .fill(cartViewModel.items.isEmpty ? Color.gray.opacity(0.3) : Color.Primary) + ) + .padding(.horizontal, 16) + .padding(.bottom, 30) } } } @@ -426,31 +450,32 @@ struct RadioButtonRow: View { } } - struct CustomDatePickerField: View { @Binding var date: Date + var isDateSelected: Bool @State private var showPicker: Bool = false - - // 날짜 포맷 변환용 Formatter + private var dateFormatter: DateFormatter { let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd" return formatter } - + var body: some View { Button { showPicker.toggle() } label: { HStack { - Text(dateFormatter.string(from: date)) + Text(isDateSelected ? dateFormatter.string(from: date) : "선택하세요") .font(.system(size: 14)) - .foregroundColor(.black) - + .foregroundColor(isDateSelected ? .black : .gray) + Spacer() - - Image(systemName: "calendar") - .foregroundColor(.gray) + + Image("cal") + .resizable() + .frame(width: 15, height: 15) + .scaledToFit() } .padding(10) .frame(width: 140, height: 30) @@ -469,7 +494,7 @@ struct CustomDatePickerField: View { ) .datePickerStyle(.graphical) .padding() - + Button("완료") { showPicker = false } @@ -481,4 +506,3 @@ struct CustomDatePickerField: View { } } } - From 201721683acf11f4963f2aec46c0aa668ae1082d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sun, 9 Nov 2025 17:48:04 +0900 Subject: [PATCH 76/85] =?UTF-8?q?[STYLE]=20=EC=98=81=EC=88=98=EC=A6=9D=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=95=88=EB=93=9C=EB=A1=9C?= =?UTF-8?q?=EC=9D=B4=EB=93=9C=EC=99=80=20=EC=9D=BC=EA=B4=80=EC=84=B1=20?= =?UTF-8?q?=EC=9C=A0=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/feature/orders/ui/ReceiptView.swift | 87 +++++++++++-------- 1 file changed, 53 insertions(+), 34 deletions(-) diff --git a/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift b/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift index 7592465..edf44e7 100644 --- a/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift @@ -34,24 +34,27 @@ struct ReceiptView: View { } else if let order = detailViewModel.order { VStack(alignment: .leading) { receiptContent(order: order) - - Button { - generatePDF(type: .receipt80mm, order: order) - } label: { - Text("영수증 PDF 저장") - .frame(maxWidth: .infinity) - .padding() - .background(Color.blue) - .foregroundColor(.white) - .cornerRadius(8) - } - .padding(.horizontal) - .padding(.bottom) - } .background(Color.white) .cornerRadius(12) - .padding() + .padding(.horizontal) + .padding(.top) + .padding(.bottom,4) + + Button { + generatePDF(type: .receipt80mm, order: order) + } label: { + Text("PDF 저장") + .font(.system(size: 13, weight: .semibold)) // ✅ 글씨 약간 작게 + .frame(maxWidth: .infinity) + .padding(.vertical, 12) + .padding(.horizontal, 8) + .background(Color.Primary) + .foregroundColor(.white) + .cornerRadius(8) + } + .padding(.horizontal) + .padding(.bottom) } } .background(Color.Light) @@ -138,24 +141,8 @@ struct ReceiptView: View { Divider() - Text(""" - • 비현금성으로 지급되는 예치금 사용 금액의 경우 현금 영수증 발행 대상에서 - 제외될 수 있습니다. - • 발생 정보는 구매확정 또는 거래 완료 이후 전달되기 때문에 국세청 사이트에서 - 즉시 확인되지 않을 수 있습니다. - • 이 영수증은 조세특례제한법 제 126조 3항에 의거 연말정산 시 소득공제혜택 - 부여 목적으로 발행됩니다. (국세청 회원가입 필수) - • 현금 영수증은 구매확정 또는 거래 완료 후 48시간 내로 국세청에서 확인 작업 - 후 최종 확정됩니다. - • 국세청 확인: 홈택스 홈페이지(https://www.hometax.go.kr/) 또는 국세청 - 상담센터(현금영수증 문의 ☎️126-1-1) - """) - .font(.system(size: 10.5)) - .foregroundColor(.textGray1) - .multilineTextAlignment(.leading) - .frame(maxWidth: .infinity, alignment: .leading) - .lineSpacing(4) - .padding(.leading, 2) // 문장 들여쓰기 추가 + NoticeTextView() + .padding(.top, 4) } .padding() @@ -176,7 +163,7 @@ struct ReceiptView: View { Spacer() Text(value) .fontWeight(highlight ? .bold : .regular) - .foregroundColor(highlight ? .blue : .primary) + .foregroundColor(highlight ? .Primary : .primary) } } @@ -254,3 +241,35 @@ func formattedApprovalNumber(_ timestamp: String) -> String { return outputFormatter.string(from: date) } + +struct NoticeTextView: View { + let notices = [ + "본 영수증은 거래완료 후 국세청 반영까지 시간이 소요될 수 있습니다.", + "현금영수증/지출증빙 여부는 국세청 홈페이지 또는 상담센터(126)에서 확인하세요.", + "비현금성으로 지급되는 포인트로 결제한 금액은 현금영수증 발행 대상에서 제외될 수 있습니다.", + "발행 방법이 자진 발급인 경우 국세청 사이트에서 자진발급분을 사용자 등록 후 소득공제 등 혜택을 받으실 수 있습니다.", + "발행 정보는 구매확정 또는 거래 완료 후 전달되며, 국세청 사이트에 즉시 반영되지 않을 수 있습니다.", + "이 영수증은 조세특례제한법 제126조 3항에 의거, 연말정산 시 소득공제 혜택 부여 목적 등으로 발행됩니다. (국세청 회원가입 필요)", + "현금 영수증은 구매 확정 또는 거래 완료 후 48시간 내에 국세청에서 확인 작업 후 최종 확정됩니다.", + "국세청 확인: 홈택스 홈페이지(https://www.hometax.go.kr/) 또는 국세청 상담센터(현금영수증 문의 ☎️126-1-1)." + ] + + var body: some View { + VStack(alignment: .leading, spacing: 5) { + ForEach(notices, id: \.self) { text in + HStack(alignment: .top, spacing: 3) { + Text("•") + .font(.system(size: 11)) + .foregroundColor(.gray) + Text(text) + .font(.system(size: 11.5)) + .foregroundColor(.textGray1) + .lineSpacing(4) + .fixedSize(horizontal: false, vertical: true) + } + } + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 2) + } +} From 6abb58d6439a7bd0d35f57c7433ee7558ee33070 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sun, 9 Nov 2025 18:02:36 +0900 Subject: [PATCH 77/85] =?UTF-8?q?[REFAC]=20=EC=A3=BC=EB=AC=B8=20=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20=EC=83=81=EC=84=B8:=20=EC=9A=94=EC=B2=AD=EC=82=AC?= =?UTF-8?q?=ED=95=AD=EC=9D=B4=20=EC=9E=88=EB=8A=94=20=EA=B2=BD=EC=9A=B0?= =?UTF-8?q?=EB=A7=8C=20=EC=9A=94=EC=B2=AD=EC=82=AC=ED=95=AD=20=EC=98=81?= =?UTF-8?q?=EC=97=AD=EC=9D=84=20=EB=B3=B4=EC=97=AC=EC=A3=BC=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/orders/ui/OrderDetailView.swift | 44 +++++++++++++------ 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift index f1b63fd..883045f 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift @@ -101,21 +101,39 @@ struct OrderDetailView: View { .shadow(color: .black.opacity(0.05), radius: 3, y: 2) - // 요청사항 따로 빼기 - VStack(alignment: .leading, spacing: 6) { - Text("요청사항") - .font(.system(size: 15, weight: .semibold)) - .padding(.bottom, 4) + // 요청사항이 있는 경우에만 표시 + if let etc = order.etc, !etc.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { + VStack(alignment: .leading, spacing: 6) { + Text("요청사항") + .font(.system(size: 15, weight: .semibold)) + .padding(.bottom, 4) - Text(order.etc ?? "") - .font(.system(size: 14)) - + Text(etc) + .font(.system(size: 14)) + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.all, 20) + .background(Color.white) + .cornerRadius(16) + .shadow(color: .black.opacity(0.05), radius: 3, y: 2) } - .frame(maxWidth: .infinity, alignment: .leading) // ✅ 여기도 추가 - .padding(.all, 20) - .background(Color.white) - .cornerRadius(16) - .shadow(color: .black.opacity(0.05), radius: 3, y: 2) + + + // 요청사항 따로 빼기 +// VStack(alignment: .leading, spacing: 6) { +// Text("요청사항") +// .font(.system(size: 15, weight: .semibold)) +// .padding(.bottom, 4) +// +// Text(order.etc ?? "") +// .font(.system(size: 14)) +// +// } +// .frame(maxWidth: .infinity, alignment: .leading) // ✅ 여기도 추가 +// .padding(.all, 20) +// .background(Color.white) +// .cornerRadius(16) +// .shadow(color: .black.opacity(0.05), radius: 3, y: 2) // ✅ 주문 상품 From d8cf1f46d7299413999f54eb2473ac42adf7e227 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sun, 9 Nov 2025 18:40:45 +0900 Subject: [PATCH 78/85] =?UTF-8?q?[REFAC]=20=EC=A3=BC=EB=AC=B8=20=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20=EC=83=81=EC=84=B8:=20=EC=9E=85=EA=B3=A0=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EB=B2=84=ED=8A=BC=20=EA=B8=B0=EB=8A=A5=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0=20=EB=B0=8F=20=EC=9E=AC=EC=A3=BC=EB=AC=B8=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EB=B2=84=ED=8A=BC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../inventory/ui/IncomingScanView.swift | 16 -- .../feature/orders/ui/OrderDetailView.swift | 188 ++++++++++-------- 2 files changed, 110 insertions(+), 94 deletions(-) diff --git a/StockMate/StockMate/app/feature/inventory/ui/IncomingScanView.swift b/StockMate/StockMate/app/feature/inventory/ui/IncomingScanView.swift index c487a4d..4dfef89 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/IncomingScanView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/IncomingScanView.swift @@ -43,22 +43,6 @@ struct IncomingScanView: View { .padding(.bottom, 180) Spacer() - -// // ✅ 직접 등록 버튼 -// Button(action: { -// dismiss() -// }) { -// Text("직접 등록 하기") -// .fontWeight(.semibold) -// .foregroundColor(.black) -// .frame(maxWidth: .infinity) -// .padding() -// .background(Color.white) -// .cornerRadius(10) -// .shadow(color: .gray.opacity(0.3), radius: 2, x: 0, y: 2) -// } -// .padding(.horizontal, 40) -// .padding(.bottom, 40) } // ✅ 로딩 표시 diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift index 883045f..59bef54 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift @@ -12,7 +12,12 @@ struct OrderDetailView: View { @Environment(\.dismiss) private var dismiss @ObservedObject var orderViewModel: OrderViewModel @StateObject private var viewModel = OrderDetailViewModel() - + + // ✅ 입고처리 버튼 + 모달 + 리프레시 + @State private var showSuccessModal = false // 모달 표시용 상태 + @State private var refreshTrigger = UUID() // 화면 리프레시 트리거 + + var body: some View { ScrollView { if viewModel.isLoading { @@ -117,24 +122,6 @@ struct OrderDetailView: View { .cornerRadius(16) .shadow(color: .black.opacity(0.05), radius: 3, y: 2) } - - - // 요청사항 따로 빼기 -// VStack(alignment: .leading, spacing: 6) { -// Text("요청사항") -// .font(.system(size: 15, weight: .semibold)) -// .padding(.bottom, 4) -// -// Text(order.etc ?? "") -// .font(.system(size: 14)) -// -// } -// .frame(maxWidth: .infinity, alignment: .leading) // ✅ 여기도 추가 -// .padding(.all, 20) -// .background(Color.white) -// .cornerRadius(16) -// .shadow(color: .black.opacity(0.05), radius: 3, y: 2) - // ✅ 주문 상품 OrderSectionCard { @@ -202,73 +189,91 @@ struct OrderDetailView: View { } } - // ✅ 하단 버튼 - HStack(spacing: 12) { - // 왼쪽: 영수증 확인 - NavigationLink(destination: ReceiptView(orderId: order.id)) { - Text("영수증 확인") - .font(.system(size: 15, weight: .semibold)) - .foregroundColor(Color.Primary) - .frame(maxWidth: .infinity) - .frame(height: 48) - .background(Color.white) - .overlay( - RoundedRectangle(cornerRadius: 10) - .stroke(Color.Primary, lineWidth: 1.5) - ) - .cornerRadius(10) - } - - - // 오른쪽 버튼: 주문 상태에 따라 변경 - if order.orderStatus == "ORDER_COMPLETED" || - order.orderStatus == "PAY_COMPLETED" { - // "주문취소" → 주문완료/결제완료/승인대기 - Button(action: { - Task { - await orderViewModel.cancelOrder(orderId: orderId) - } - }) { - Text("주문 취소") - .font(.system(size: 15, weight: .semibold)) - .frame(maxWidth: .infinity) - .frame(height: 48) - .background(Color(hex: "#1D4ED8")) - .foregroundColor(.white) - .cornerRadius(10) - } - - } else if order.orderStatus == "SHIPPING" { - // "입고 하기" → 입고대기/배송완료 - Button(action: { - // TODO: 입고 처리 버튼 - }) { - Text("입고 처리") - .font(.system(size: 15, weight: .semibold)) - .frame(maxWidth: .infinity) - .frame(height: 48) - .background(Color(hex: "#1D4ED8")) - .foregroundColor(.white) - .cornerRadius(10) - } - - } else { - // 👉 나머지 상태 → "재주문하기" 버튼 - Button(action: { - // TODO: 평가 액션 처리 - }) { - Text("재주문하기") + // 오른쪽 버튼: 주문 상태에 따라 변경 + if order.orderStatus == "ORDER_COMPLETED" || + order.orderStatus == "PAY_COMPLETED" || + order.orderStatus == "SHIPPING"{ + + HStack(spacing: 12) { + // 왼쪽: 영수증 확인 + NavigationLink(destination: ReceiptView(orderId: order.id)) { + Text("영수증 확인") .font(.system(size: 15, weight: .semibold)) + .foregroundColor(Color.Primary) .frame(maxWidth: .infinity) .frame(height: 48) - .background(Color(hex: "#1D4ED8")) - .foregroundColor(.white) + .background(Color.white) + .overlay( + RoundedRectangle(cornerRadius: 10) + .stroke(Color.Primary, lineWidth: 1.5) + ) .cornerRadius(10) } + + // 오른쪽 버튼: 주문 상태에 따라 변경 + if order.orderStatus == "ORDER_COMPLETED" || + order.orderStatus == "PAY_COMPLETED" { + // "주문취소" → 주문완료/결제완료/승인대기 + Button(action: { + Task { + await orderViewModel.cancelOrder(orderId: orderId) + } + }) { + Text("주문 취소") + .font(.system(size: 15, weight: .semibold)) + .frame(maxWidth: .infinity) + .frame(height: 48) + .background(Color(hex: "#1D4ED8")) + .foregroundColor(.white) + .cornerRadius(10) + } + + } else { + // "입고 하기" → 배송중 + Button(action: { + Task { + let result = await orderViewModel.receiveOrder(orderNumber: order.orderNumber) + switch result { + case .success(let message): + showSuccessModal = true // ✅ 입고 처리 성공 시 모달 표시 + print("입고 처리 성공: \(message)") + case .failure(let error): + print("입고 처리 실패: \(error.message)") + // 실패 토스트 띄우기 등 + } + } + }) { + Text("입고 처리") + .font(.system(size: 15, weight: .semibold)) + .frame(maxWidth: .infinity) + .frame(height: 48) + .background(Color(hex: "#1D4ED8")) + .foregroundColor(.white) + .cornerRadius(10) + } + + + } } + .padding(.top, 5) + } else{ + // 👉 나머지 상태: 영수증 확인 버튼만 하나 + NavigationLink(destination: ReceiptView(orderId: order.id)) { + Text("영수증 확인") + .font(.system(size: 15, weight: .semibold)) + .foregroundColor(Color.Primary) + .frame(maxWidth: .infinity) + .frame(height: 48) + .background(Color.white) + .overlay( + RoundedRectangle(cornerRadius: 10) + .stroke(Color.Primary, lineWidth: 1.5) + ) + .cornerRadius(10) + } + .padding(.top, 5) } - .padding(.top, 5) } .padding(.horizontal, 20) // ✅ 전체 섹션 동일 여백 @@ -280,6 +285,31 @@ struct OrderDetailView: View { .frame(maxWidth: .infinity, maxHeight: .infinity) } } + .overlay( + Group { + if showSuccessModal { + Color.black.opacity(0.4) + .ignoresSafeArea() + .overlay( + AlertModal( + icon: Image("SuccessIllust"), + title: "입고 처리 완료!", + message: "입고 부품 등록이 완료되었습니다.", + primaryButtonTitle: "확인", + primaryAction: { + showSuccessModal = false + // ✅ 화면 리프레시 트리거 + refreshTrigger = UUID() + Task { + await viewModel.fetchOrderDetail(orderId: orderId) + } + } + ) + ) + } + } + ) + .animation(.easeInOut, value: showSuccessModal) .background(Color.Light) .navigationTitle("주문 내역 상세") .navigationBarTitleDisplayMode(.inline) @@ -300,6 +330,8 @@ struct OrderDetailView: View { .task { await viewModel.fetchOrderDetail(orderId: orderId) } + .id(refreshTrigger) + } // MARK: - Helper From 3fc46e3c33eb2ebe3b8b6e2a5a5369286d4280f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sun, 9 Nov 2025 19:14:30 +0900 Subject: [PATCH 79/85] =?UTF-8?q?[REFAC]=20=EC=98=81=EC=88=98=EC=A6=9D=20?= =?UTF-8?q?=ED=8F=AD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/feature/orders/ui/ReceiptView.swift | 54 +++++++++---------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift b/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift index edf44e7..7c19331 100644 --- a/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift @@ -168,40 +168,36 @@ struct ReceiptView: View { } private func generatePDF(type: PDFType, order: OrderResponseItem) { - let view = receiptContent(order: order) // ✅ 실제 View 생성 - let renderer = ImageRenderer(content: view) - - let width: CGFloat - switch type { - case .a4: width = 595.2 // A4 width in pt - case .receipt80mm: width = 226.77 // 80mm in pt - } + // ✅ 아이폰 화면 비율로 렌더링 (디바이스 폭 고정) + let screenWidth = UIScreen.main.bounds.width + let view = receiptContent(order: order) + .frame(width: screenWidth) // 폭 고정 (문장 길이에 따라 늘어나지 않음) + .background(Color.white) + let renderer = ImageRenderer(content: view) renderer.scale = UIScreen.main.scale - - // ✅ cgImage 기반 안전 처리 - if let cgImage = renderer.cgImage { - let uiImage = UIImage(cgImage: cgImage) - let pdfDoc = PDFDocument() - if let pdfPage = PDFPage(image: uiImage) { - pdfDoc.insert(pdfPage, at: 0) - } - // ✅ 주문번호 기반 파일명 - let fileName = "receipt_\(order.orderNumber).pdf" - let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(fileName) + if let cgImage = renderer.cgImage { + let uiImage = UIImage(cgImage: cgImage) + let pdfDoc = PDFDocument() + if let pdfPage = PDFPage(image: uiImage) { + pdfDoc.insert(pdfPage, at: 0) + } - if pdfDoc.write(to: tempURL) { - let av = UIActivityViewController(activityItems: [tempURL], applicationActivities: nil) + // ✅ 파일명: 주문번호 기반 + let fileName = "receipt_\(order.orderNumber).pdf" + let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(fileName) - if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, - let rootVC = windowScene.windows.first?.rootViewController { - av.popoverPresentationController?.sourceView = rootVC.view - rootVC.present(av, animated: true) - } - } - } - + if pdfDoc.write(to: tempURL) { + let av = UIActivityViewController(activityItems: [tempURL], applicationActivities: nil) + + if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, + let rootVC = windowScene.windows.first?.rootViewController { + av.popoverPresentationController?.sourceView = rootVC.view + rootVC.present(av, animated: true) + } + } + } } } From d47b069e0b691b6cd31a5c2e3d6af10234fc8321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sun, 9 Nov 2025 21:57:16 +0900 Subject: [PATCH 80/85] =?UTF-8?q?[FEAT]=20=ED=86=A0=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=A9=94=EC=84=B8=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/core/components/ToastModifier.swift | 95 +++++++++++++++++++ .../app/feature/orders/ui/OrderInfoView.swift | 61 +++++++++--- .../payment/ui/DepositChargeView.swift | 28 +++--- .../payment/viewmodel/DepositViewModel.swift | 4 + 4 files changed, 161 insertions(+), 27 deletions(-) create mode 100644 StockMate/StockMate/app/core/components/ToastModifier.swift diff --git a/StockMate/StockMate/app/core/components/ToastModifier.swift b/StockMate/StockMate/app/core/components/ToastModifier.swift new file mode 100644 index 0000000..40fcd6c --- /dev/null +++ b/StockMate/StockMate/app/core/components/ToastModifier.swift @@ -0,0 +1,95 @@ +// +// ToastModifier.swift +// StockMate +// +// Created by Admin on 11/9/25. +// + +import SwiftUI + +struct ToastView: View { + let message: String + let iconName: String? // 아이콘 이름 (예: "checkmark.circle.fill" or "xmark.circle.fill") + let iconColor: Color? // 아이콘 색상 (예: .green, .red 등) + + var body: some View { + ZStack { + // 메시지 중앙 정렬 + Text(message) + .font(.system(size: 14, weight: .medium)) + .foregroundColor(.white) + .multilineTextAlignment(.center) + .frame(maxWidth: .infinity) + .padding(.horizontal, 40) // 아이콘 영역 고려해서 여백 확보 + + // 아이콘 왼쪽 고정 + if let iconName, let iconColor { + HStack { + Image(systemName: iconName) + .foregroundColor(iconColor) + .font(.system(size: 18)) + Spacer() + } + .padding(.leading, 16) + } + } + .padding(.vertical, 14) + .frame(maxWidth: .infinity) + .background(Color.black.opacity(0.8)) + .cornerRadius(9999) + .padding(.horizontal, 24) + } +} + +struct ToastModifier: ViewModifier { + @Binding var isPresented: Bool + let message: String + let iconName: String? + let iconColor: Color? + let duration: Double + + func body(content: Content) -> some View { + ZStack { + content + + if isPresented { + VStack { + Spacer() + ToastView(message: message, iconName: iconName, iconColor: iconColor) + .transition(.opacity.combined(with: .scale)) + .padding(.bottom, 60) + } + .animation(.easeInOut(duration: 0.35), value: isPresented) + } + } + .onChange(of: isPresented) { shown in + if shown { + DispatchQueue.main.asyncAfter(deadline: .now() + duration) { + withAnimation(.easeOut(duration: 0.4)) { + isPresented = false + } + } + } + } + } +} + +extension View { + func toast( + isPresented: Binding, + message: String, + iconName: String? = nil, + iconColor: Color? = nil, + duration: Double = 2.0 + ) -> some View { + self.modifier( + ToastModifier( + isPresented: isPresented, + message: message, + iconName: iconName, + iconColor: iconColor, + duration: duration + ) + ) + } +} diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift index e97a518..700fc11 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift @@ -29,6 +29,11 @@ struct OrderInfoView: View { @State private var specificDate: Date? = nil @State private var requestMessage: String = "" + // 토스트 메세지 관련 + @State private var showDepositToast = false // 예치금 부족 + @State private var showChargeToast = false // 충전 완료 + + // ✅ 모달 관련 상태 @State private var showOrderSuccessModal = false @State private var navigateToOrderDetail = false @@ -74,13 +79,26 @@ struct OrderInfoView: View { contentView } .onTapGesture { - UIApplication.shared.hideKeyboard() // ✅ 화면 아무데나 탭하면 키보드 내려감 + UIApplication.shared.hideKeyboard() // 화면 아무데나 탭하면 키보드 내려감 } .padding(.horizontal) .padding(.top) bottomOrderButton } + .toast( + isPresented: $showChargeToast, + message: "충전이 완료되었습니다.", + iconName: "checkmark", + iconColor: .green + ) + // ✅ 예치금 부족 토스트 + .toast( + isPresented: $showDepositToast, + message: "예치금이 부족합니다. (부족: \(formatPrice((cartViewModel.cart?.totalPrice ?? 0) - depositViewModel.balance))원)", + iconName: "info.circle", + iconColor: .LightBlue04 + ) .background(Color.Light) .navigationTitle("주문/결제") .navigationBarTitleDisplayMode(.inline) @@ -116,11 +134,15 @@ struct OrderInfoView: View { } } } - // ✅ 충전 bottom sheet 연결 - .sheet(isPresented: $depositViewModel.showChargeSheet) { - DepositChargeView(viewModel: depositViewModel) - .presentationDetents([.fraction(0.80)]) // 시트 높이 80% - } + .sheet(isPresented: $depositViewModel.showChargeSheet) { + DepositChargeView(viewModel: depositViewModel) { + // ✅ 충전 성공 시 토스트 표시 + withAnimation { + showChargeToast = true + } + } + .presentationDetents([.fraction(0.80)]) // 시트 높이 80% + } // 모달 오버레이 (body 안) .overlay { if showOrderSuccessModal { @@ -409,15 +431,26 @@ extension OrderInfoView { private var bottomOrderButton: some View { VStack { Button { - Task { - let orderRequest = OrderRequest( - orderItems: makeOrderItems(), - requestedShippingDate: formattedShippingDate(), - paymentType: paymentType.rawValue, - etc: requestMessage - ) - await orderViewModel.createOrder(request: orderRequest) + let totalPrice = cartViewModel.cart?.totalPrice ?? 0 + let deposit = depositViewModel.balance + + if totalPrice > deposit { + // ✅ 예치금 부족 + withAnimation { + showDepositToast = true + } + } else { + Task { + let orderRequest = OrderRequest( + orderItems: makeOrderItems(), + requestedShippingDate: formattedShippingDate(), + paymentType: paymentType.rawValue, + etc: requestMessage + ) + await orderViewModel.createOrder(request: orderRequest) + } } + } label: { Text("\(cartViewModel.cart?.totalPrice ?? 0)원 결제하기") .font(.system(size: 16, weight: .bold)) diff --git a/StockMate/StockMate/app/feature/payment/ui/DepositChargeView.swift b/StockMate/StockMate/app/feature/payment/ui/DepositChargeView.swift index 538412f..8446720 100644 --- a/StockMate/StockMate/app/feature/payment/ui/DepositChargeView.swift +++ b/StockMate/StockMate/app/feature/payment/ui/DepositChargeView.swift @@ -6,6 +6,10 @@ struct DepositChargeView: View { @State private var amountText: String = "" @State private var isCharging: Bool = false +// @State private var showToast = false + var onChargeSuccess: (() -> Void)? + + let keypad: [[String]] = [ ["1","2","3"], ["4","5","6"], @@ -45,15 +49,6 @@ struct DepositChargeView: View { .font(.system(size: 38, weight: .bold)) .padding(.top, 35) - // 아래 작은 라벨 -// Text("\(formattedNumberString)원") -// .font(.system(size: 15)) -// .foregroundColor(.textGray1) -// .padding(.horizontal, 10) -// .padding(.vertical, 4) -// .background(Color.white.opacity(0.9)) -// .cornerRadius(12) - Spacer().frame(height: 80) // 키패드 @@ -67,7 +62,6 @@ struct DepositChargeView: View { .font(.system(size: 20)) .frame(width: 50, height: 45) .foregroundColor(Color.black) -// .background(Color.red.opacity(0.5)) .padding(.horizontal, 4) .background(Color.white) } @@ -88,6 +82,9 @@ struct DepositChargeView: View { if success { viewModel.showChargeSheet = false dismiss() + onChargeSuccess?() + } else { + } } } label: { @@ -104,14 +101,19 @@ struct DepositChargeView: View { .frame(maxWidth: .infinity) } } - .background(Color.Primary) + .background( + (Int(amountText) ?? 0) > 0 && !isCharging + ? Color.Primary // ✅ 활성 상태 + : Color.gray.opacity(0.4) // ✅ 비활성(회색) + ) .cornerRadius(28) .padding(.top, 28) .padding(.horizontal, 10) - .disabled(isCharging) + .disabled(isCharging || (Int(amountText) ?? 0) == 0) +// .disabled(isCharging) } .padding(.horizontal, 20) -// .padding(.top, 20) .background(Color.white) + } } diff --git a/StockMate/StockMate/app/feature/payment/viewmodel/DepositViewModel.swift b/StockMate/StockMate/app/feature/payment/viewmodel/DepositViewModel.swift index 27d6009..82ee801 100644 --- a/StockMate/StockMate/app/feature/payment/viewmodel/DepositViewModel.swift +++ b/StockMate/StockMate/app/feature/payment/viewmodel/DepositViewModel.swift @@ -15,6 +15,9 @@ final class DepositViewModel: ObservableObject { @Published var isLoading: Bool = false @Published var showChargeSheet: Bool = false + @Published var depositAmount: Int = 0 + @Published var isChargeSuccess: Bool = false + /// ✅ 예치금 조회 func fetchDepositAmount() async { isLoading = true @@ -37,6 +40,7 @@ final class DepositViewModel: ObservableObject { switch result { case .success(_): await fetchDepositAmount() + isChargeSuccess = true return true case .failure(let error): print("❌ 충전 실패:", error.message) From 44a664bb9cd678540cf4dba813c1d6f83143135a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Sun, 9 Nov 2025 22:44:19 +0900 Subject: [PATCH 81/85] =?UTF-8?q?[REFAC]=20=EC=98=88=EC=B9=98=EA=B8=88=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=95=88=EB=93=9C=EB=A1=9C?= =?UTF-8?q?=EC=9D=B4=EB=93=9C=EC=99=80=20=EC=9D=BC=EA=B4=80=EC=84=B1=20?= =?UTF-8?q?=EC=9C=A0=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/feature/orders/ui/OrderInfoView.swift | 3 +- .../payment/ui/DepositChargeView.swift | 49 ++++++++++-------- .../backspace.imageset/Contents.json | 23 ++++++++ .../backspace.imageset/backspace@1x.png | Bin 0 -> 2474 bytes .../backspace.imageset/backspace@2x.png | Bin 0 -> 8260 bytes .../backspace.imageset/backspace@3x.png | Bin 0 -> 15826 bytes 6 files changed, 51 insertions(+), 24 deletions(-) create mode 100644 StockMate/StockMate/resources/Assets.xcassets/backspace.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/backspace.imageset/backspace@1x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/backspace.imageset/backspace@2x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/backspace.imageset/backspace@3x.png diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift index 700fc11..738c49e 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift @@ -141,7 +141,8 @@ struct OrderInfoView: View { showChargeToast = true } } - .presentationDetents([.fraction(0.80)]) // 시트 높이 80% + .presentationDetents([.fraction(0.58)]) // 시트 높이 80% + .presentationCornerRadius(20) } // 모달 오버레이 (body 안) .overlay { diff --git a/StockMate/StockMate/app/feature/payment/ui/DepositChargeView.swift b/StockMate/StockMate/app/feature/payment/ui/DepositChargeView.swift index 8446720..108ce84 100644 --- a/StockMate/StockMate/app/feature/payment/ui/DepositChargeView.swift +++ b/StockMate/StockMate/app/feature/payment/ui/DepositChargeView.swift @@ -6,9 +6,8 @@ struct DepositChargeView: View { @State private var amountText: String = "" @State private var isCharging: Bool = false -// @State private var showToast = false var onChargeSuccess: (() -> Void)? - + let keypad: [[String]] = [ ["1","2","3"], @@ -41,34 +40,38 @@ struct DepositChargeView: View { // Title Text("예치금 충전") - .font(.system(size: 20, weight: .semibold)) - .padding(.top, 35) + .font(.system(size: 18, weight: .semibold)) + .padding(.top, 42) // 금액 Text(formattedAmount) - .font(.system(size: 38, weight: .bold)) - .padding(.top, 35) - - Spacer().frame(height: 80) + .font(.system(size: 32, weight: .bold)) // 키패드 - LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 0), count: 3), - spacing: 1) { + LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 11), count: 3), + spacing: 16) { ForEach(keypad.flatMap { $0 }, id: \.self) { key in Button { buttonAction(key) } label: { - Text(key) - .font(.system(size: 20)) - .frame(width: 50, height: 45) - .foregroundColor(Color.black) - .padding(.horizontal, 4) - .background(Color.white) + if key == "⌫" { + Image("backspace") + .resizable() + .scaledToFit() + .frame(width: 32, height: 32) + .frame(width: 60, height: 45) + .background(Color.white) + } else { + Text(key) + .font(.system(size: 19)) + .foregroundColor(Color.black) + .frame(width: 60, height: 45) + .background(Color.white) + } } } } .padding(.horizontal,16) - .padding(.top, 28) // 충전 버튼 @@ -95,25 +98,25 @@ struct DepositChargeView: View { .frame(maxWidth: .infinity) } else { Text("충전") - .font(.system(size: 18, weight: .bold)) + .font(.system(size: 15, weight: .semibold)) .foregroundColor(.white) - .frame(height: 59) + .frame(height: 49) .frame(maxWidth: .infinity) } } .background( (Int(amountText) ?? 0) > 0 && !isCharging ? Color.Primary // ✅ 활성 상태 - : Color.gray.opacity(0.4) // ✅ 비활성(회색) + : Color.gray.opacity(0.3) // ✅ 비활성(회색) ) - .cornerRadius(28) - .padding(.top, 28) + .cornerRadius(18) + .padding(.bottom, 25) .padding(.horizontal, 10) .disabled(isCharging || (Int(amountText) ?? 0) == 0) -// .disabled(isCharging) } .padding(.horizontal, 20) .background(Color.white) + } } diff --git a/StockMate/StockMate/resources/Assets.xcassets/backspace.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/backspace.imageset/Contents.json new file mode 100644 index 0000000..b693e6f --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/backspace.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "backspace@1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "backspace@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "backspace@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/backspace.imageset/backspace@1x.png b/StockMate/StockMate/resources/Assets.xcassets/backspace.imageset/backspace@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..1aff5a9839de719d87c1baf64e2d0545d50a24e4 GIT binary patch literal 2474 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 Pe-s57W$7wOQIeI?_C-D^ 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 literal 0 HcmV?d00001 From c972bcc08e68f065ff4181f73792361ef3b239ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Mon, 10 Nov 2025 01:02:13 +0900 Subject: [PATCH 82/85] =?UTF-8?q?[REFAC]=20=EC=A3=BC=EB=AC=B8=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EC=95=88=EB=93=9C=EB=A1=9C=EC=9D=B4=EB=93=9C?= =?UTF-8?q?=EC=99=80=20=EC=9D=BC=EA=B4=80=EC=84=B1=20=EC=9C=A0=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/feature/auth/ui/HomeView.swift | 2 +- .../feature/cart/ui/DeliveryStatusView.swift | 89 ++++++++++++++----- .../dashboard/ui/InOutHistoryView.swift | 16 ++-- .../feature/inventory/ui/InventoryView.swift | 21 +++-- .../feature/orders/ui/OrderDetailView.swift | 4 +- .../dline.imageset/Contents.json | 12 +++ .../Assets.xcassets/dline.imageset/dline.svg | 8 ++ 7 files changed, 108 insertions(+), 44 deletions(-) create mode 100644 StockMate/StockMate/resources/Assets.xcassets/dline.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/dline.imageset/dline.svg diff --git a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift index a5e40d0..4160b73 100644 --- a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift @@ -56,7 +56,7 @@ struct HomeView: View { .padding(5) .background(Color.red) .clipShape(Circle()) - .offset(x: 5, y: -5) + .offset(x: 2, y: -9) } } } diff --git a/StockMate/StockMate/app/feature/cart/ui/DeliveryStatusView.swift b/StockMate/StockMate/app/feature/cart/ui/DeliveryStatusView.swift index 6a9f189..830476e 100644 --- a/StockMate/StockMate/app/feature/cart/ui/DeliveryStatusView.swift +++ b/StockMate/StockMate/app/feature/cart/ui/DeliveryStatusView.swift @@ -26,7 +26,7 @@ struct DeliveryStatusView: View { var body: some View { GeometryReader { geo in - HStack(alignment: .center, spacing: 0) { + HStack(alignment: .center, spacing: 4) { ForEach(0.. 전체 회색 +//// 4 -> 전체 파란색 +//switch status { +//case "ORDER_COMPLETED": return 0 // 주문 완료 +// +//// 결제 후 결과에 따라 결제 실패 or 완료 +//case "FAILED": return 6 // 결제 실패 +//case "PAY_COMPLETED": return 0 // 결제 완료 +// +//// 결제 완료 상태에서 지점이 주문 취소 +//case "CANCELLED": return 6 // 주문 취소 +// +//// 본사에서 "결제 완료"에 대해서 주문을 반려 or 승인 +//case "REJECTED": return 6 // 주문 반려 +//case "APPROVAL_ORDER": return 1 // 주문 승인 +// +//// 창고관리자가 "주문 승인"에 대해서 송장(인보이스)를 뽑으면 출고 대기 +//case "PENDING_SHIPPING": return 2 // 출고 대기 +// +//// 창고관리자가 QR을 스캔하여 출고처리 하면 배송중 +//case "SHIPPING": return 3 // 배송중 +// +//// 지점에서 QR을 스캔하여 입고 완료 처리 +//case "RECEIVED": return 4 // 입고 완료 diff --git a/StockMate/StockMate/app/feature/dashboard/ui/InOutHistoryView.swift b/StockMate/StockMate/app/feature/dashboard/ui/InOutHistoryView.swift index d72f7e9..8c8c0c8 100644 --- a/StockMate/StockMate/app/feature/dashboard/ui/InOutHistoryView.swift +++ b/StockMate/StockMate/app/feature/dashboard/ui/InOutHistoryView.swift @@ -40,7 +40,7 @@ struct InOutHistoryView: View { Text(formatDate(String(date))) .font(.headline) .padding(.leading, 25) - .padding(.top) + .padding(.top, 8) // ✅ 해당 날짜의 히스토리 카드들 ForEach(histories) { history in @@ -96,7 +96,7 @@ struct InOutHistoryCard: View { } placeholder: { Color.gray.opacity(0.2) } - .frame(width: 64, height: 64) + .frame(width: 60, height: 60) .cornerRadius(10) VStack(alignment: .leading, spacing: 4) { @@ -104,7 +104,7 @@ struct InOutHistoryCard: View { .font(.system(size: 15)) .lineLimit(1) if history.items.count > 1 { - Text("외 \(history.items.count - 1)개 품목") + Text("외 \(history.items.count - 1)개") .font(.caption) .foregroundColor(.gray) } @@ -121,7 +121,7 @@ struct InOutHistoryCard: View { .padding(.vertical, 4) .background(statusBgColor(history.status)) .foregroundColor(statusTextColor(history.status)) - .cornerRadius(8) + .cornerRadius(12) if history.status == "RECEIVED", let orderId = history.orderId { NavigationLink( @@ -143,9 +143,9 @@ struct InOutHistoryCard: View { .buttonStyle(.plain) } } - .padding() + .padding(9) .background(Color.white) - .cornerRadius(12) + .cornerRadius(16) .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 2) .padding(.horizontal) } @@ -156,8 +156,8 @@ struct InOutHistoryCard: View { func statusText(_ status: String) -> String { switch status { - case "RECEIVED": return "입고 완료" - case "RELEASED": return "출고 완료" + case "RECEIVED": return "입고" + case "RELEASED": return "출고" default: return status } } diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift index bd9ebd3..d797713 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift @@ -47,7 +47,7 @@ struct InventoryView: View { GridMenuView() Text("얼마 남지 않았어요!") - .font(.system(size: 22, weight: .semibold)) + .font(.system(size: 20, weight: .semibold)) .foregroundColor(.black) .padding(.horizontal, 25) .padding(.top) @@ -145,27 +145,30 @@ struct GridMenuView: View { Spacer() ZStack { // 타원 배경 - Rectangle() - .fill(item.1 ? Color.white.opacity(0.2) : Color.Primary) - .frame(width: 35, height: 26) - .cornerRadius(80) + RoundedRectangle(cornerRadius: 12) + .fill(item.1 ? Color.white.opacity(0.28) : Color.Primary.opacity(0.15)) + .frame(width: 32, height: 32) // 아이콘 Image(item.2) .renderingMode(.template) .resizable() .scaledToFit() - .frame(width: 14, height: 14) - .foregroundColor(.white) + .frame(width: 19, height: 19) + .foregroundColor(item.1 ? .white : .Primary) } } + .padding(.top, 34) + Text(item.0) .font(.system(size: 15, weight: .semibold)) .foregroundColor(item.1 ? .white : Color.Primary) + .padding(.leading, 5) + .padding(.top, 5) + .padding(.bottom, 40) } - .padding(.horizontal, 20) - .padding(.vertical, 20) + .padding(12) .frame(height: 99) .frame(maxWidth: .infinity) .background( diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift index 59bef54..cff9be8 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift @@ -24,7 +24,7 @@ struct OrderDetailView: View { ProgressView() .frame(maxWidth: .infinity, maxHeight: .infinity) } else if let order = viewModel.order { - VStack(spacing: 16) { + VStack(spacing: 10) { VStack { DeliveryStatusView(currentStep: deliveryStep(for: order.orderStatus)) .frame(maxWidth: .infinity, alignment: .center) @@ -93,7 +93,7 @@ struct OrderDetailView: View { let trackingNo = order.trackingNumber, !carrier.isEmpty, !trackingNo.isEmpty { - return "\(carrier): \(trackingNo)" + return "(\(carrier): \(trackingNo))" } return "-" }() diff --git a/StockMate/StockMate/resources/Assets.xcassets/dline.imageset/Contents.json b/StockMate/StockMate/resources/Assets.xcassets/dline.imageset/Contents.json new file mode 100644 index 0000000..30d1dee --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/dline.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "dline.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/dline.imageset/dline.svg b/StockMate/StockMate/resources/Assets.xcassets/dline.imageset/dline.svg new file mode 100644 index 0000000..b75984c --- /dev/null +++ b/StockMate/StockMate/resources/Assets.xcassets/dline.imageset/dline.svg @@ -0,0 +1,8 @@ + + + + + + + + From 44ee0d208e9936b28bcc72fcb6b748496c1639bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Mon, 10 Nov 2025 20:58:08 +0900 Subject: [PATCH 83/85] =?UTF-8?q?[REFAC]=20=EC=95=88=EB=93=9C=EB=A1=9C?= =?UTF-8?q?=EC=9D=B4=EB=93=9C=EC=99=80=20=EB=94=94=EC=9E=90=EC=9D=B8=20?= =?UTF-8?q?=EC=9D=BC=EA=B4=80=EC=84=B1=20=EC=9C=A0=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- StockMate/.DS_Store | Bin 6148 -> 6148 bytes .../app/core/components/CategoryButton.swift | 4 +- .../core/components/InventoryCardView.swift | 4 +- .../app/core/components/ToastModifier.swift | 2 +- .../app/core/components/TopToast.swift | 55 ++-- .../app/feature/auth/ui/LoginView.swift | 239 +++++++++++------- .../app/feature/auth/ui/RegisterView.swift | 51 ++-- .../auth/viewmodel/AuthViewModel.swift | 6 +- .../dashboard/ui/InOutHistoryView.swift | 2 +- .../ui/TransactionTypeListView.swift | 4 +- .../app/feature/orders/ui/OrderInfoView.swift | 52 ++-- .../app/feature/orders/ui/OrderListView.swift | 6 +- .../app/feature/orders/ui/OrderView.swift | 2 +- .../app/feature/user/ui/ProfileView.swift | 11 +- 14 files changed, 248 insertions(+), 190 deletions(-) diff --git a/StockMate/.DS_Store b/StockMate/.DS_Store index 96d21b81f6d7b95cdc550b0bdfe354e75ac4fec1..850a2690ff2224608a8c76f39333b4c9a242130f 100644 GIT binary patch delta 70 zcmZoMXffCj%gW3apfEXs)r@1Fh;HJX*yD~sj?Uy$tX#+fTKokWhQZ1CxdjX$z;<9` M;U~t;>>Pjj0S2%ZDF6Tf delta 70 zcmZoMXffCj%gW5M==|gaRx^%=OHwraqK-QPIXaV1v2q~`Xz>?h7zQWj=N2%40IR^p N!cUBw**X650|0A37k2;v diff --git a/StockMate/StockMate/app/core/components/CategoryButton.swift b/StockMate/StockMate/app/core/components/CategoryButton.swift index 26461f1..1e0b599 100644 --- a/StockMate/StockMate/app/core/components/CategoryButton.swift +++ b/StockMate/StockMate/app/core/components/CategoryButton.swift @@ -15,13 +15,13 @@ struct CategoryButton: View { var body: some View { Button(action: action) { Text(title) - .font(.system(size: 13, weight: .regular)) + .font(.system(size: 13, weight: .semibold)) .foregroundColor(isSelected ? .Primary : .black) .padding(.vertical, 8) .padding(.horizontal, 12) .background( RoundedRectangle(cornerRadius: 16) - .fill(Color.Light) + .fill(isSelected ? Color(hex: "#E1E7F7") : Color.Light) ) .overlay( RoundedRectangle(cornerRadius: 16) diff --git a/StockMate/StockMate/app/core/components/InventoryCardView.swift b/StockMate/StockMate/app/core/components/InventoryCardView.swift index 7113417..017f669 100644 --- a/StockMate/StockMate/app/core/components/InventoryCardView.swift +++ b/StockMate/StockMate/app/core/components/InventoryCardView.swift @@ -55,7 +55,7 @@ struct InventoryCardView: View { VStack(alignment: .center, spacing: 6) { if item.isLack { Text("수량 부족") - .font(.system(size: 13, weight: .regular)) + .font(.system(size: 13, weight: .semibold)) .padding(.horizontal, 10) .padding(.vertical, 5) .background(Color.DangerBg) @@ -63,7 +63,7 @@ struct InventoryCardView: View { .cornerRadius(12) } else { Text("수량 여유") - .font(.system(size: 13, weight: .regular)) + .font(.system(size: 13, weight: .semibold)) .padding(.horizontal, 10) .padding(.vertical, 5) .background(Color.StatusGreenBg) diff --git a/StockMate/StockMate/app/core/components/ToastModifier.swift b/StockMate/StockMate/app/core/components/ToastModifier.swift index 40fcd6c..ae13461 100644 --- a/StockMate/StockMate/app/core/components/ToastModifier.swift +++ b/StockMate/StockMate/app/core/components/ToastModifier.swift @@ -35,7 +35,7 @@ struct ToastView: View { } .padding(.vertical, 14) .frame(maxWidth: .infinity) - .background(Color.black.opacity(0.8)) + .background(Color.black.opacity(0.75)) .cornerRadius(9999) .padding(.horizontal, 24) } diff --git a/StockMate/StockMate/app/core/components/TopToast.swift b/StockMate/StockMate/app/core/components/TopToast.swift index 1e61918..abf4a60 100644 --- a/StockMate/StockMate/app/core/components/TopToast.swift +++ b/StockMate/StockMate/app/core/components/TopToast.swift @@ -10,36 +10,51 @@ import SwiftUI struct TopToast: View { let message: String @Binding var isVisible: Bool + var iconName: String = "exclamationmark.triangle.fill" // 기본 아이콘 + var iconColor: Color = .Danger var duration: Double = 2.2 var body: some View { - if isVisible { - VStack { - HStack(spacing: 8) { - Image(systemName: "exclamationmark.triangle.fill") - .foregroundColor(.white) + VStack(spacing: 0) { + if isVisible { + ZStack { + // 메시지 중앙 정렬 Text(message) - .foregroundColor(.white) .font(.system(size: 14, weight: .medium)) + .foregroundColor(Color(hex: "AB3029")) + .multilineTextAlignment(.center) + .frame(maxWidth: .infinity) + .padding(.horizontal, 40) // 아이콘 여백 확보 + + // 왼쪽 아이콘 + HStack { + Image(systemName: iconName) + .foregroundColor(iconColor) + .font(.system(size: 18)) + Spacer() + } + .padding(.leading, 16) } - .padding() - .background(Color.red.opacity(0.9)) + .padding(.vertical, 14) + .frame(maxWidth: .infinity) + .background(Color.DangerBg.opacity(0.85)) .cornerRadius(12) - .shadow(radius: 5) - .padding(.horizontal, 16) - .padding(.top, 8) - - Spacer() - } - .transition(.move(edge: .top).combined(with: .opacity)) - .animation(.easeInOut(duration: 0.25), value: isVisible) - .onAppear { - DispatchQueue.main.asyncAfter(deadline: .now() + duration) { - withAnimation { - isVisible = false + .padding(.horizontal, 24) + .padding(.top, 14) + .transition(.move(edge: .top).combined(with: .opacity)) + .onAppear { + DispatchQueue.main.asyncAfter(deadline: .now() + duration) { + withAnimation { + isVisible = false + } } } } + + Spacer() } + .frame(maxWidth: .infinity) + .animation(.easeInOut(duration: 0.3), value: isVisible) + .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) } } diff --git a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift index 318a598..144e971 100644 --- a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift @@ -18,114 +18,177 @@ struct LoginView: View { @State private var emailError: String? = nil @State private var pwError: String? = nil + // ✅ 토스트 관련 상태 추가 + @State private var showTopToast = false + @State private var topToastMessage = "" + var onLogin: (String, String) -> Void = { _, _ in } var onClickRegister: () -> Void = {} var body: some View { - VStack { - Spacer().frame(height: 140) - - // MARK: - Logo - Image("stockmate_logo") - .resizable() - .scaledToFit() - .frame(width: 216, height: 44) + ZStack { - Spacer().frame(height: 67) - // MARK: - Title - Text("로그인") - .font(.system(size: 28, weight: .bold)) - .frame(maxWidth: .infinity, alignment: .leading) + VStack { + Spacer().frame(height: 140) + + // MARK: - Logo + Image("stockmate_logo") + .resizable() + .scaledToFit() + .frame(width: 216, height: 44) + + Spacer().frame(height: 67) + + // MARK: - Title + Text("로그인") + .font(.system(size: 28, weight: .bold)) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 24) + .foregroundColor(Color.DarkBlue01) + + Spacer().frame(height: 17) + + // MARK: - Text Fields + CustomTextField( + title: "이메일", + placeholder: "stockmate@gmail.com", + text: $authViewModel.email, + isEmail: true, + errorMessage: emailError + ) + .keyboardType(.emailAddress) .padding(.horizontal, 24) - .foregroundColor(Color.DarkBlue01) - - Spacer().frame(height: 17) - - // MARK: - Text Fields - CustomTextField( - title: "이메일", - placeholder: "stockmate@gmail.com", - text: $authViewModel.email, - isEmail: true, - errorMessage: emailError - ) - .keyboardType(.emailAddress) - .padding(.horizontal, 24) - - Spacer().frame(height: 18) - - // 비밀번호 - CustomSecureField( - title: "비밀번호", - placeholder: "비밀번호를 입력하세요", - text: $authViewModel.password, - errorMessage: pwError - ) - .padding(.horizontal, 24) - - Spacer().frame(height: 56) - - // MARK: - Login Button - Button(action: { - if isValidForm() { - Task { - await authViewModel.login() - } + .onChange(of: authViewModel.email) { newValue in + // 입력 중 실시간 validation (뷰 업데이트 중이 아니므로 안전) + emailError = isValidEmail(newValue) ? nil : "이메일 형식을 확인해주세요" } - }) { - Text("로그인") - .font(.system(size: 16, weight: .semibold)) - .foregroundColor(.white) - .frame(maxWidth: .infinity) - .frame(height: 42) - .background(Color.Primary) - .cornerRadius(4) - } - .padding(.horizontal, 24) - - Spacer().frame(height: 24) - - // 회원가입 링크 - HStack { - Text("계정이 없으신가요?") - .foregroundColor(Color.Secondary) - .font(.system(size: 13)) - Button(action: { onClickRegister() }) { - Text("회원가입") - .font(.system(size: 13, weight: .bold)) - .foregroundColor(Color.Secondary) + + Spacer().frame(height: 18) + + // 비밀번호 + CustomSecureField( + title: "비밀번호", + placeholder: "비밀번호를 입력하세요", + text: $authViewModel.password, + errorMessage: pwError + ) + .padding(.horizontal, 24) + .onChange(of: authViewModel.password) { newValue in + pwError = newValue.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" } - } - - // 승인 아이디 받기 전 - // 홈화면으로 이동 - HStack { + + Spacer().frame(height: 26) + + // MARK: - Login Button + // 3) 버튼 — 시각적 상태 반영 + disabled 처리 Button(action: { - authViewModel.authState = .authenticated + // 버튼이 눌렸을 때는 한번 더 확정적으로 에러 상태를 설정 + // (뷰 업데이트 중이 아니므로 상태 변경해도 안전) + validateAndSetErrors() + guard isFormValid else { return } + + Task { + let success = await authViewModel.login() + if !success { + showToast("아이디 또는 비밀번호가 잘못되었습니다.") + } + // await authViewModel.login() + } }) { - Text("홈화면으로 이동") - .font(.system(size: 13, weight: .bold)) - .foregroundColor(Color.Secondary) + Text("로그인") + .font(.system(size: 16, weight: .semibold)) + .foregroundColor(.white) + .frame(maxWidth: .infinity) + .frame(height: 42) + .background( + RoundedRectangle(cornerRadius: 4) + .fill(isFormValid ? Color.Primary : Color.gray.opacity(0.45)) + ) + .cornerRadius(4) } + .padding(.horizontal, 24) + .disabled(!isFormValid) + + + Spacer().frame(height: 24) + + // 회원가입 링크 + HStack { + Text("계정이 없으신가요?") + .foregroundColor(Color.gray) + .font(.system(size: 13)) + Button(action: { onClickRegister() }) { + Text("회원가입") + .font(.system(size: 13, weight: .bold)) + .foregroundColor(Color.Secondary) + } + } + + // 승인 아이디 받기 전 + // 홈화면으로 이동 + HStack { + Button(action: { + authViewModel.authState = .authenticated + }) { + Text("홈화면으로 이동") + .font(.system(size: 13, weight: .bold)) + .foregroundColor(Color.Secondary) + } + } + .padding(.top, 5) + + Spacer() + + + // ✅ 상단 토스트 표시 + //TopToast(message: topToastMessage, isVisible: $showTopToast) + } + .background(Color.Light) + .onTapGesture { + UIApplication.shared.hideKeyboard() } - .padding(.top, 5) + .ignoresSafeArea() - Spacer() + // ✅ 오버레이로 위에 띄움 (맨 위에 고정) +// TopToast(message: topToastMessage, isVisible: $showTopToast) +// .zIndex(1) // 다른 뷰 위로 + TopToast(message: topToastMessage, + isVisible: $showTopToast, + iconName: "exclamationmark.circle", + iconColor: .black) + .zIndex(1) // 다른 뷰 위로 } - .background(Color.Light) - .onTapGesture { - UIApplication.shared.hideKeyboard() - } - .ignoresSafeArea() } // MARK: - 유효성 검사 함수 - private func isValidForm() -> Bool { +// private func isValidForm() -> Bool { +// emailError = isValidEmail(authViewModel.email) ? nil : "이메일 형식을 확인해주세요" +// pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" +// return emailError == nil && pwError == nil +//// return true +// } + // 1) 뷰 내부(바디 바깥) — 부작용 없는 computed property + private var isFormValid: Bool { + return isValidEmail(authViewModel.email) && authViewModel.password.count >= 8 + } + + // 4) body 바깥에 유틸 함수 추가 — 뷰 업데이트 중 호출하지 말고 이벤트에서만 호출 + private func validateAndSetErrors() { + // 이 함수는 호출되는 시점이 사용자 액션(버튼)일 때만 사용 emailError = isValidEmail(authViewModel.email) ? nil : "이메일 형식을 확인해주세요" pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" - return emailError == nil && pwError == nil -// return true } + + // 기존의 isValidForm 함수는 이제 뷰에 호출되는 순수 검사로 대체하거나 삭제 + // private func isValidForm() -> Bool { ... } 대신 위 isFormValid 사용 + + // ✅ 상단 토스트 표시 함수 + private func showToast(_ message: String) { + topToastMessage = message + withAnimation { + showTopToast = true + } + } } diff --git a/StockMate/StockMate/app/feature/auth/ui/RegisterView.swift b/StockMate/StockMate/app/feature/auth/ui/RegisterView.swift index 54620b7..0c79b5b 100644 --- a/StockMate/StockMate/app/feature/auth/ui/RegisterView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/RegisterView.swift @@ -82,45 +82,32 @@ struct RegisterView: View { ) CustomTextField( title: "지점 이름", - placeholder: "서울 1호점", + placeholder: "강남점", text: $storeName, errorMessage: storeNameError ) // ✅ 주소 입력 필드 + 버튼 추가 부분 VStack(alignment: .leading, spacing: 4) { - HStack { - CustomTextField( - title: "주소", - placeholder: "서울특별시 강남구 ...", - text: $address, - errorMessage: addressError, - isReadOnly: true // ✅ 추가 - ) - .disabled(true) // 사용자가 직접 입력 못하게 - .onTapGesture { - // 탭해도 검색창 열 수 있게 (선택사항) - showAddressSearch.toggle() - } - - Button(action: { - showAddressSearch.toggle() - }) { - Text("주소 검색") - .font(.system(size: 14, weight: .semibold)) - .frame(height: 43) - .padding(.horizontal, 12) - .background(Color.Primary) - .foregroundColor(.white) - .cornerRadius(8) - } - .sheet(isPresented: $showAddressSearch) { - KakaoZipCodeView(address: $address) - } + CustomTextField( + title: "주소", + placeholder: "도로명 주소를 검색하세요", + text: $address, + errorMessage: addressError, + isReadOnly: true // ✅ 추가 + ) + .disabled(true) // 사용자가 직접 입력 못하게 + .onTapGesture { + // 탭해도 검색창 열 수 있게 (선택사항) + showAddressSearch.toggle() + } + .sheet(isPresented: $showAddressSearch) { + KakaoZipCodeView(address: $address) } } + CustomTextField( title: "사업자등록번호", - placeholder: "123-45-67890", + placeholder: "000-00-00000", text: $bizNo, errorMessage: bizNoError ) @@ -157,7 +144,7 @@ struct RegisterView: View { // MARK: - Login Link HStack(spacing: 4) { Text("이미 계정이 있으신가요?") - .foregroundColor(Color.Secondary) + .foregroundColor(Color.gray) .font(.system(size: 13)) Button(action: { viewModel.goToLogin() @@ -166,7 +153,7 @@ struct RegisterView: View { Text("로그인") .fontWeight(.semibold) .font(.system(size: 13, weight: .bold)) - .foregroundColor(Color.Secondary) + .foregroundColor(Color.Primary) } } .padding(.bottom, 40) diff --git a/StockMate/StockMate/app/feature/auth/viewmodel/AuthViewModel.swift b/StockMate/StockMate/app/feature/auth/viewmodel/AuthViewModel.swift index 4ff557b..0ef1ef7 100644 --- a/StockMate/StockMate/app/feature/auth/viewmodel/AuthViewModel.swift +++ b/StockMate/StockMate/app/feature/auth/viewmodel/AuthViewModel.swift @@ -27,7 +27,7 @@ final class AuthViewModel: ObservableObject { } // MARK: - 로그인 - func login() async { + func login() async -> Bool{ print("로그인 시도 - email: \(email), password: \(password)") let req = LoginRequest(email: email, password: password) let result = await repo.login(req) @@ -38,7 +38,7 @@ final class AuthViewModel: ObservableObject { guard let data = apiResp.data else { print("데이터 없음: \(apiResp.message)") message = apiResp.message - return + return false } TokenStore.shared.save( access: data.accessToken, @@ -48,10 +48,12 @@ final class AuthViewModel: ObservableObject { message = "로그인 성공" authState = .authenticated print("authState 변경됨 → authenticated") + return true case .failure(let err): print("로그인 실패: \(err.message)") message = err.message + return false } } diff --git a/StockMate/StockMate/app/feature/dashboard/ui/InOutHistoryView.swift b/StockMate/StockMate/app/feature/dashboard/ui/InOutHistoryView.swift index 8c8c0c8..425cdf2 100644 --- a/StockMate/StockMate/app/feature/dashboard/ui/InOutHistoryView.swift +++ b/StockMate/StockMate/app/feature/dashboard/ui/InOutHistoryView.swift @@ -55,7 +55,7 @@ struct InOutHistoryView: View { } } .background(Color.Light) - .navigationTitle("입출고 내역") + .navigationTitle("입출고 히스토리") .navigationBarBackButtonHidden(true) .toolbar { ToolbarItem(placement: .navigationBarLeading) { diff --git a/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift b/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift index 74c2e6b..76250a9 100644 --- a/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift +++ b/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift @@ -117,7 +117,7 @@ struct TransactionCard: View { let sign = isPay ? "-" : "+" let color: Color = isPay ? .Danger : .Primary - Text("\(sign) \(formatPrice(item.totalAmount))") + Text("\(sign) \(formatPrice(item.totalAmount))원") .font(.system(size: 13, weight: .bold)) .foregroundColor(color) @@ -137,7 +137,7 @@ struct TransactionCard: View { } .padding() .background(Color.white) - .cornerRadius(10) + .cornerRadius(13) .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) } } diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift index 738c49e..4c2fadc 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift @@ -228,52 +228,44 @@ extension OrderInfoView { Text("배송 정보") .font(.headline) - VStack(alignment: .leading, spacing: 8) { + VStack(alignment: .leading, spacing: 4) { Text(userViewModel.userInfo?.owner ?? "이름 없음") .font(.system(size: 15, weight: .medium)) + .padding(.bottom, 4) Text(userViewModel.userInfo?.address ?? "주소 없음") .font(.system(size: 14)) + .padding(.bottom, 4) .foregroundColor(.textGray1) Text("요청사항") .font(.system(size: 14, weight: .medium)) .padding(.top, 5) - - ZStack(alignment: .topLeading) { - if requestMessage.isEmpty { - Text("요청사항을 입력하세요") - .foregroundColor(.gray) - .font(.system(size: 14)) - .padding(.top, 12) - .padding(.leading, 10) + + TextEditor(text: $requestMessage) + .font(.system(size: 14)) + .padding(10) + .onChange(of: requestMessage) { newValue in + if newValue.count > 50 { // 50자 제한 + requestMessage = String(newValue.prefix(50)) + } } - - TextEditor(text: $requestMessage) - .font(.system(size: 14)) - .padding(.top, 4) - .padding(.horizontal, 6) - .onChange(of: requestMessage) { newValue in - if newValue.count > 50 { // 50자 제한 - requestMessage = String(newValue.prefix(50)) - } - } - .scrollContentBackground(.hidden) - .background(Color.clear) - } - .frame(height: 70) - .background(Color.white) - .overlay( - RoundedRectangle(cornerRadius: 10) - .stroke(requestMessage.isEmpty ? Color(.systemGray4) : Color.Primary, lineWidth: 1) // ✅ 입력 시 Primary로 변경 - ) + .scrollContentBackground(.hidden) + .background(Color.white) + .frame(height: 93) // ✅ 높이 기존 70 → 110 으로 확대 + .overlay( + RoundedRectangle(cornerRadius: 10) + .stroke(requestMessage.isEmpty ? Color(.systemGray4) : Color.Primary, lineWidth: 1) + ) + + HStack { Spacer() Text("\(requestMessage.count)/50") .font(.system(size: 12)) .foregroundColor(.gray) - .padding(.trailing, 4) - .padding(.bottom, -15) // 박스보다 살짝 아래로 + .padding(.trailing, 2) } + .padding(.bottom, -8) } .padding() .padding(.bottom,7) diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift index be77a2f..76baad3 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift @@ -116,9 +116,11 @@ struct OrderListCardView: View { VStack(alignment: .leading, spacing: 4) { if let first = order.orderItems.first { Text(first.partDetail.korName) - .font(.system(size: 16, weight: .semibold)) + .font(.system(size: 13, weight: .semibold)) .foregroundColor(.black) - .lineLimit(1) + .lineLimit(nil) // ✅ 줄 제한 해제 + .fixedSize(horizontal: false, vertical: true) +// .lineLimit(2) if order.orderItems.count > 1 { Text("외 \(order.orderItems.count - 1)개") diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderView.swift index cc815e7..90e3452 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderView.swift @@ -15,7 +15,7 @@ struct OrderView: View { ZStack{ ScrollView { // 타이틀 - Text("재고 관리") + Text("발주 요청") .font(.title2) .bold() .padding(.top, 13) diff --git a/StockMate/StockMate/app/feature/user/ui/ProfileView.swift b/StockMate/StockMate/app/feature/user/ui/ProfileView.swift index 00d3191..8a986fa 100644 --- a/StockMate/StockMate/app/feature/user/ui/ProfileView.swift +++ b/StockMate/StockMate/app/feature/user/ui/ProfileView.swift @@ -21,16 +21,13 @@ struct ProfileView: View { ProfileCircleView(name: userViewModel.userInfo?.owner ?? "사용자", size: 50) VStack(alignment: .leading, spacing: 4) { - HStack { - Image("location") - .foregroundColor(.gray) - Text(userViewModel.userInfo?.storeName ?? "가게명 없음") - .foregroundColor(.gray) - .font(.subheadline) - } Text(userViewModel.userInfo?.owner ?? "이름 없음") .font(.title3.bold()) .foregroundColor(Color(hex: "#2B3A1A")) + + Text(userViewModel.userInfo?.email ?? "이메일 없음") + .foregroundColor(.gray) + .font(.subheadline) } Spacer() From c20c01f35bd6d0d0a75ce4ae8290c807aa2bc2a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Mon, 10 Nov 2025 23:54:05 +0900 Subject: [PATCH 84/85] =?UTF-8?q?[REFAC]=20=EC=95=88=EB=93=9C=EB=A1=9C?= =?UTF-8?q?=EC=9D=B4=EB=93=9C=EC=99=80=20=EB=94=94=EC=9E=90=EC=9D=B8=20?= =?UTF-8?q?=ED=86=B5=EC=9D=BC,=20=EC=95=B1=EB=A1=9C=EA=B3=A0=20=EB=B0=8F?= =?UTF-8?q?=20=EC=8A=A4=ED=94=8C=EB=9E=98=EC=8B=9C=20=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- StockMate/StockMate/app/StockMateApp.swift | 18 +- .../app/core/common/Validators.swift | 12 +- .../app/core/components/BottomToast.swift | 103 +++++ .../core/components/CustomSecureField.swift | 18 +- .../app/core/components/CustomTextField.swift | 26 +- .../app/feature/auth/ui/LoginView.swift | 10 +- .../app/feature/auth/ui/RegisterView.swift | 394 +++++++++--------- .../auth/viewmodel/AuthViewModel.swift | 4 +- .../StockMate/app/navigation/IntroView.swift | 21 + .../AppIcon.appiconset/AppIcon_1024.png | Bin 0 -> 163115 bytes .../AppIcon.appiconset/Contents.json | 1 + .../intrologo.imageset/Contents.json | 23 + .../intrologo.imageset/intrologo@1x.png | Bin 0 -> 36942 bytes .../intrologo.imageset/intrologo@2x.png | Bin 0 -> 100985 bytes .../intrologo.imageset/intrologo@3x.png | Bin 0 -> 180569 bytes .../toastlogo.imageset/Contents.json | 23 + .../toastlogo.imageset/toastlogo@1x.png | Bin 0 -> 54210 bytes .../toastlogo.imageset/toastlogo@2x.png | Bin 0 -> 150620 bytes .../toastlogo.imageset/toastlogo@3x.png | Bin 0 -> 272016 bytes 19 files changed, 436 insertions(+), 217 deletions(-) create mode 100644 StockMate/StockMate/app/core/components/BottomToast.swift create mode 100644 StockMate/StockMate/app/navigation/IntroView.swift create mode 100644 StockMate/StockMate/resources/Assets.xcassets/AppIcon.appiconset/AppIcon_1024.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/intrologo.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/intrologo.imageset/intrologo@1x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/intrologo.imageset/intrologo@2x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/intrologo.imageset/intrologo@3x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/toastlogo.imageset/Contents.json create mode 100644 StockMate/StockMate/resources/Assets.xcassets/toastlogo.imageset/toastlogo@1x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/toastlogo.imageset/toastlogo@2x.png create mode 100644 StockMate/StockMate/resources/Assets.xcassets/toastlogo.imageset/toastlogo@3x.png diff --git a/StockMate/StockMate/app/StockMateApp.swift b/StockMate/StockMate/app/StockMateApp.swift index d9d9848..57f9eb0 100644 --- a/StockMate/StockMate/app/StockMateApp.swift +++ b/StockMate/StockMate/app/StockMateApp.swift @@ -10,11 +10,25 @@ import SwiftUI @main struct StockMateApp: App { @StateObject private var authViewModel = AuthViewModel() + @State private var isLoading = true // 인트로 상태 var body: some Scene { WindowGroup { - AppNavHost() - .environmentObject(authViewModel) + + Group { + if isLoading { + IntroView() // ✅ 로고만 보여주는 화면 + } else { + AppNavHost() + .environmentObject(authViewModel) + } + } + .onAppear { + Task { + try await Task.sleep(nanoseconds: 1_500_000_000) // 1.5초 + isLoading = false + } + } } } } diff --git a/StockMate/StockMate/app/core/common/Validators.swift b/StockMate/StockMate/app/core/common/Validators.swift index 1b4b4d3..ccaaeb8 100644 --- a/StockMate/StockMate/app/core/common/Validators.swift +++ b/StockMate/StockMate/app/core/common/Validators.swift @@ -17,8 +17,12 @@ func isValidPassword(_ pw: String) -> Bool { return pw.range(of: "[A-Za-z]", options: .regularExpression) != nil && pw.range(of: "[0-9]", options: .regularExpression) != nil } - +// +//func isValidBizNo(_ no: String) -> Bool { +// let pattern = #"^\d{3}-\d{2}-\d{5}$"# +// return no.range(of: pattern, options: .regularExpression) != nil +//} func isValidBizNo(_ no: String) -> Bool { - let pattern = #"^\d{3}-\d{2}-\d{5}$"# - return no.range(of: pattern, options: .regularExpression) != nil -} + let regex = "^\\d{3}-\\d{2}-\\d{5}$" + return NSPredicate(format: "SELF MATCHES %@", regex).evaluate(with: no) + } diff --git a/StockMate/StockMate/app/core/components/BottomToast.swift b/StockMate/StockMate/app/core/components/BottomToast.swift new file mode 100644 index 0000000..3de1bbc --- /dev/null +++ b/StockMate/StockMate/app/core/components/BottomToast.swift @@ -0,0 +1,103 @@ +// +// BottomToast.swift +// StockMate +// +// Created by Admin on 11/10/25. +// + +//import SwiftUI +// +//struct BottomToast: View { +// let message: String +// @Binding var isVisible: Bool +// var iconName: String = "toastlogo" +// var backgroundColor: Color = Color(hex: "4CAF50") // 초록색 계열 +// var duration: Double = 2.3 +// +// var body: some View { +// VStack { +// Spacer() +// +// if isVisible { +// HStack(spacing: 10) { +// Image(iconName) +// .resizable() +// .scaledToFit() +// .foregroundColor(.white) +// .font(.system(size: 18, weight: .semibold)) +// +// Text(message) +// .font(.system(size: 14, weight: .semibold)) +// .foregroundColor(.white) +// .multilineTextAlignment(.center) +// .lineLimit(2) +// } +// .padding(.horizontal, 18) +// .padding(.vertical, 12) +// .background( +// RoundedRectangle(cornerRadius: 14) +// .fill(backgroundColor.opacity(0.95)) +// ) +// .shadow(color: .black.opacity(0.15), radius: 8, x: 0, y: 4) +// .padding(.bottom, 60) +// .transition(.move(edge: .bottom).combined(with: .opacity)) +// .onAppear { +// DispatchQueue.main.asyncAfter(deadline: .now() + duration) { +// withAnimation(.easeInOut(duration: 0.3)) { +// isVisible = false +// } +// } +// } +// } +// } +// .animation(.easeInOut(duration: 0.3), value: isVisible) +// } +//} +import SwiftUI + +struct BottomToast: View { + let message: String + @Binding var isVisible: Bool + var iconName: String = "toastlogo" + var iconColor: Color = .white + var backgroundColor: Color = Color(hex: "4CAF50") + var duration: Double = 2.2 + + var body: some View { + VStack { + Spacer() + + if isVisible { + HStack(spacing: 10) { + Image(iconName) + .resizable() + .scaledToFit() + .frame(width: 24, height: 24) + + Text(message) + .font(.system(size: 14, weight: .light)) + .foregroundColor(.black) + .multilineTextAlignment(.center) + .lineLimit(2) + } + .padding(.horizontal, 22) + .padding(.vertical, 12) + .background( + RoundedRectangle(cornerRadius: 9999) + .fill(backgroundColor) + ) + .shadow(color: .black.opacity(0.1), radius: 5, x: 0, y: 4) + .padding(.bottom, 60) + .padding(.horizontal, 50) + .onAppear { + DispatchQueue.main.asyncAfter(deadline: .now() + duration) { + withAnimation(.easeInOut(duration: 0.25)) { + isVisible = false + } + } + } + } + } + .animation(.easeInOut(duration: 0.25), value: isVisible) + } +} diff --git a/StockMate/StockMate/app/core/components/CustomSecureField.swift b/StockMate/StockMate/app/core/components/CustomSecureField.swift index 71da03e..a9a9f54 100644 --- a/StockMate/StockMate/app/core/components/CustomSecureField.swift +++ b/StockMate/StockMate/app/core/components/CustomSecureField.swift @@ -12,9 +12,21 @@ struct CustomSecureField: View { var placeholder: String @Binding var text: String var errorMessage: String? = nil + @FocusState private var isFocused: Bool @State private var showPassword = false + // ✅ 테두리 색상 계산 로직 (CustomTextField와 동일) + private var borderColor: Color { + if let error = errorMessage, !error.isEmpty { + return .red + } else if isFocused { + return .Primary + } else { + return .LightBlue04 + } + } + var body: some View { VStack(alignment: .leading, spacing: 6) { // 필드 제목 @@ -25,11 +37,7 @@ struct CustomSecureField: View { // 텍스트 입력 박스 ZStack { RoundedRectangle(cornerRadius: 8) - .strokeBorder( - isFocused ? Color.Primary : - (errorMessage == nil ? Color.LightBlue04 : .red), - lineWidth: 1 - ) + .strokeBorder(borderColor, lineWidth: 1) .background(RoundedRectangle(cornerRadius: 8).fill(Color.white)) // 포커스일 때만 그림자 표시 .shadow( diff --git a/StockMate/StockMate/app/core/components/CustomTextField.swift b/StockMate/StockMate/app/core/components/CustomTextField.swift index 074d2c2..cd08d8d 100644 --- a/StockMate/StockMate/app/core/components/CustomTextField.swift +++ b/StockMate/StockMate/app/core/components/CustomTextField.swift @@ -17,6 +17,18 @@ struct CustomTextField: View { @FocusState private var isFocused: Bool + // ✅ 테두리 색상 계산 로직 + private var borderColor: Color { + if let error = errorMessage, !error.isEmpty { + return .red + } else if isFocused { + return .Primary + } else { + return .LightBlue04 + } + } + + var body: some View { VStack(alignment: .leading, spacing: 6) { Text(title) @@ -25,12 +37,14 @@ struct CustomTextField: View { ZStack { RoundedRectangle(cornerRadius: 8) - .strokeBorder( - isFocused - ? Color.Primary - : (errorMessage == nil ? Color.LightBlue04 : .red), - lineWidth: 1 - ) +// .strokeBorder( +// isFocused +// ? Color.Primary +// : (errorMessage == nil ? Color.LightBlue04 : .red), +// lineWidth: 1 +// ) + + .strokeBorder(borderColor, lineWidth: 1) .background( RoundedRectangle(cornerRadius: 8) .fill(Color.white) diff --git a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift index 144e971..389a680 100644 --- a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift @@ -121,7 +121,7 @@ struct LoginView: View { Button(action: { onClickRegister() }) { Text("회원가입") .font(.system(size: 13, weight: .bold)) - .foregroundColor(Color.Secondary) + .foregroundColor(Color.Primary) } } @@ -151,13 +151,15 @@ struct LoginView: View { .ignoresSafeArea() // ✅ 오버레이로 위에 띄움 (맨 위에 고정) -// TopToast(message: topToastMessage, isVisible: $showTopToast) -// .zIndex(1) // 다른 뷰 위로 +// TopToast(message: topToastMessage, isVisible: $showTopToast) +// .zIndex(1) // 다른 뷰 위로 TopToast(message: topToastMessage, isVisible: $showTopToast, iconName: "exclamationmark.circle", iconColor: .black) - .zIndex(1) // 다른 뷰 위로 + .zIndex(1) // 다른 뷰 위로 + + } } diff --git a/StockMate/StockMate/app/feature/auth/ui/RegisterView.swift b/StockMate/StockMate/app/feature/auth/ui/RegisterView.swift index 0c79b5b..0da1967 100644 --- a/StockMate/StockMate/app/feature/auth/ui/RegisterView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/RegisterView.swift @@ -31,218 +31,222 @@ struct RegisterView: View { @State private var showToast = false @State private var showAddressSearch = false - + @State private var showSuccessToast = false var body: some View { - ScrollView { - VStack(spacing: 16) { - Spacer().frame(height: 70) - - // MARK: - Logo - Image("stockmate_logo") - .resizable() - .scaledToFit() - .frame(width: 216, height: 44) - - Spacer().frame(height: 4) - - // MARK: - Title - Text("회원가입") - .font(.system(size: 28, weight: .bold)) - .frame(maxWidth: .infinity, alignment: .leading) - .padding(.horizontal, 24) - .foregroundColor(Color.DarkBlue01) - - // MARK: - Text Fields - VStack { - CustomTextField( - title: "이메일", - placeholder: "stockmate@gmail.com", - text: $email, - errorMessage: emailError - ) - .keyboardType(.emailAddress) - CustomSecureField( - title: "비밀번호", - placeholder: "비밀번호를 입력하세요", - text: $password, - errorMessage: pwError - ) - CustomSecureField( - title: "비밀번호 확인", - placeholder: "비밀번호를 다시 입력하세요", - text: $confirmPassword, - errorMessage: confirmPasswordError - ) - CustomTextField( - title: "대표자 이름", - placeholder: "홍길동", - text: $owner, - errorMessage: ownerError - ) - CustomTextField( - title: "지점 이름", - placeholder: "강남점", - text: $storeName, - errorMessage: storeNameError - ) - // ✅ 주소 입력 필드 + 버튼 추가 부분 - VStack(alignment: .leading, spacing: 4) { + ZStack { + + + ScrollView { + VStack(spacing: 16) { + Spacer().frame(height: 70) + + // MARK: - Logo + Image("stockmate_logo") + .resizable() + .scaledToFit() + .frame(width: 216, height: 44) + + Spacer().frame(height: 4) + + // MARK: - Title + Text("회원가입") + .font(.system(size: 28, weight: .bold)) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 24) + .foregroundColor(Color.DarkBlue01) + + // MARK: - Text Fields + VStack { CustomTextField( - title: "주소", - placeholder: "도로명 주소를 검색하세요", - text: $address, - errorMessage: addressError, - isReadOnly: true // ✅ 추가 + title: "이메일", + placeholder: "stockmate@gmail.com", + text: $email, + errorMessage: emailError ) - .disabled(true) // 사용자가 직접 입력 못하게 - .onTapGesture { - // 탭해도 검색창 열 수 있게 (선택사항) - showAddressSearch.toggle() + .keyboardType(.emailAddress) + .onChange(of: email) { newValue in + emailError = isValidEmail(newValue) ? nil : "이메일 형식을 확인해주세요" } - .sheet(isPresented: $showAddressSearch) { - KakaoZipCodeView(address: $address) + CustomSecureField( + title: "비밀번호", + placeholder: "비밀번호를 입력하세요", + text: $password, + errorMessage: pwError + ) + .onChange(of: password) { newValue in + pwError = isValidPassword(newValue) ? nil : "8자 이상, 영문+숫자 조합입니다." + // confirm도 재검증 + confirmPasswordError = (confirmPassword.isEmpty || confirmPassword == newValue) ? nil : "비밀번호가 일치하지 않습니다" + } + + CustomSecureField( + title: "비밀번호 확인", + placeholder: "비밀번호를 다시 입력하세요", + text: $confirmPassword, + errorMessage: confirmPasswordError + ) + .onChange(of: confirmPassword) { newValue in + confirmPasswordError = (password == newValue) ? nil : "비밀번호가 일치하지 않습니다" + } + CustomTextField( + title: "대표자 이름", + placeholder: "홍길동", + text: $owner, + errorMessage: nil + ) + CustomTextField( + title: "지점 이름", + placeholder: "강남점", + text: $storeName, + errorMessage: nil + ) + // ✅ 주소 입력 필드 + 버튼 추가 부분 + VStack(alignment: .leading, spacing: 4) { + CustomTextField( + title: "주소", + placeholder: "도로명 주소를 검색하세요", + text: $address, + errorMessage: addressError, + isReadOnly: true // ✅ 추가 + ) + .disabled(true) // 사용자가 직접 입력 못하게 + .onTapGesture { + // 탭해도 검색창 열 수 있게 (선택사항) + showAddressSearch.toggle() + } + .sheet(isPresented: $showAddressSearch) { + KakaoZipCodeView(address: $address) + } + } + + CustomTextField( + title: "사업자등록번호", + placeholder: "000-00-00000", + text: $bizNo, + errorMessage: bizNoError + ) + .keyboardType(.numberPad) + .onChange(of: bizNo) { newValue in + formatBizNoInput(newValue) + bizNoError = isValidBizNo(newValue) ? nil : "형식: 000-00-00000" } } + .padding(.horizontal, 24) - CustomTextField( - title: "사업자등록번호", - placeholder: "000-00-00000", - text: $bizNo, - errorMessage: bizNoError - ) - .keyboardType(.numberPad) - .onChange(of: bizNo) { newValue in - formatBizNoInput(newValue) - } - } - .padding(.horizontal, 24) - - - // MARK: - Register Button - if isLoading { - ProgressView("회원가입 중...") - .progressViewStyle(CircularProgressViewStyle()) - } else { - Button(action: { - handleRegister() - }) { - Text("회원가입") - .font(.system(size: 16, weight: .semibold)) - .foregroundColor(.white) - .frame(maxWidth: .infinity) - .frame(height: 48) - .background(Color(hex: "#1D4ED8")) - .cornerRadius(8) + + // MARK: - Register Button + if isLoading { + ProgressView("회원가입 중...") + .progressViewStyle(CircularProgressViewStyle()) + } else { + Button(action: { + validateAndSetErrors() + guard isFormValid else { return } + Task { + isLoading = true + let success = await viewModel.register( + email: email, + password: password, + owner: owner, + address: address, + storeName: storeName, + bizNo: bizNo.filter { $0.isNumber } + ) + isLoading = false + + // ✅ 회원가입 성공 시 토스트 표시 +// if success { +// withAnimation { +// showSuccessToast = true +// } +// } + } + }) { + Text("회원가입") + .font(.system(size: 16, weight: .semibold)) + .foregroundColor(.white) + .frame(maxWidth: .infinity) + .frame(height: 48) + .background( + RoundedRectangle(cornerRadius: 8) + .fill(isFormValid ? Color.Primary : Color.gray.opacity(0.45)) + ) + .cornerRadius(8) + } + .disabled(!isFormValid) + .padding(.horizontal, 24) } - .padding(.horizontal, 24) - } - - TopToast(message: viewModel.message, isVisible: $showToast) - - Spacer().frame(height: 5) - // MARK: - Login Link - HStack(spacing: 4) { - Text("이미 계정이 있으신가요?") - .foregroundColor(Color.gray) - .font(.system(size: 13)) - Button(action: { - viewModel.goToLogin() - print("로그인으로 이동") - }) { - Text("로그인") - .fontWeight(.semibold) - .font(.system(size: 13, weight: .bold)) - .foregroundColor(Color.Primary) + + TopToast(message: viewModel.message, isVisible: $showToast) + + Spacer().frame(height: 5) + // MARK: - Login Link + HStack(spacing: 4) { + Text("이미 계정이 있으신가요?") + .foregroundColor(Color.gray) + .font(.system(size: 13)) + Button(action: { + viewModel.goToLogin() + print("로그인으로 이동") + }) { + Text("로그인") + .fontWeight(.semibold) + .font(.system(size: 13, weight: .bold)) + .foregroundColor(Color.Primary) + } } + .padding(.bottom, 40) + + // ✅ 키보드 가림 방지용 여백 + Spacer().frame(height: 300) } - .padding(.bottom, 40) - - // ✅ 키보드 가림 방지용 여백 - Spacer().frame(height: 300) } + .background(Color.Light) + .ignoresSafeArea() + .onTapGesture { + UIApplication.shared.hideKeyboard() + } + .scrollDismissesKeyboard(.interactively) // ✅ 손가락으로 스크롤하면 키보드 자동 내려감 + // showToast 자동으로 트리거: viewModel.message 변경 시 토스트 보여주기 + .onChange(of: viewModel.message) { newMsg in + guard !newMsg.isEmpty else { return } + showToast = true + } + + // ✅ 회원가입 성공 토스트 + BottomToast( + message: "회원가입 성공", + isVisible: $showSuccessToast, + iconName: "toastlogo", + backgroundColor: Color(hex: "EEEDF5") // 초록색 + ) + .zIndex(1) // 다른 뷰 위로 } - .background(Color.Light) - .ignoresSafeArea() - .onTapGesture { - UIApplication.shared.hideKeyboard() - } - .scrollDismissesKeyboard(.interactively) // ✅ 손가락으로 스크롤하면 키보드 자동 내려감 - + } // MARK: - 유효성 검사 함수 - private func isValidForm() -> Bool { + + + // MARK: - computed form valid (부작용 없음) + private var isFormValid: Bool { + return isValidEmail(email) + && isValidPassword(password) + && password == confirmPassword + && !owner.trimmingCharacters(in: .whitespaces).isEmpty + && !storeName.trimmingCharacters(in: .whitespaces).isEmpty + && !address.trimmingCharacters(in: .whitespaces).isEmpty + && isValidBizNo(bizNo) + } + + // MARK: - validate & helpers + private func validateAndSetErrors() { emailError = isValidEmail(email) ? nil : "이메일 형식을 확인해주세요" - pwError = isValidPassword(password) ? nil : "영문과 숫자를 포함한 8자 이상 비밀번호를 입력해주세요" - confirmPasswordError = (password == confirmPassword) ? nil : "비밀번호가 일치하지 않습니다." - bizNoError = isValidBizNo(bizNo) ? nil : "사업자등록번호 형식이 올바르지 않습니다. (예: 123-45-67890)" - - return emailError == nil && pwError == nil - && confirmPasswordError == nil && bizNoError == nil - } - - // MARK: - Register Handler - private func handleRegister() { - // 초기화 - emailError = nil - pwError = nil - confirmPasswordError = nil - ownerError = nil - storeNameError = nil - addressError = nil - bizNoError = nil - - var hasEmptyField = false - - // 필수 필드 체크 - if email.isEmpty { - emailError = "이메일을 입력해주세요." - hasEmptyField = true - } - if password.isEmpty { - pwError = "비밀번호를 입력해주세요." - hasEmptyField = true - } - if confirmPassword.isEmpty { - confirmPasswordError = "비밀번호를 다시 입력해주세요." - hasEmptyField = true - } - if owner.isEmpty { - ownerError = "대표자 이름을 입력해주세요." - showToast = true - } - if storeName.isEmpty { - storeNameError = "지점 이름을 입력해주세요." - showToast = true - } - if address.isEmpty { - addressError = "주소를 입력해주세요." - showToast = true - } - if bizNo.isEmpty { - bizNoError = "사업자등록번호를 입력해주세요." - hasEmptyField = true - } - - // 빈 칸이 하나라도 있으면 종료 - guard !hasEmptyField else { return } - // 유효성 검사 함수 실행 - guard isValidForm() else { return } - - // 통과 → 회원가입 진행 - Task { - isLoading = true - await viewModel.register( - email: email, - password: password, - owner: owner, - address: address, - storeName: storeName, - bizNo: bizNo.filter { $0.isNumber } // ← 여기서 숫자만 추출해서 전송 - ) - isLoading = false - } + pwError = isValidPassword(password) ? nil : "8자 이상, 영문+숫자 조합입니다." + confirmPasswordError = (password == confirmPassword) ? nil : "비밀번호가 일치하지 않습니다" + addressError = address.isEmpty ? "주소를 입력해주세요." : nil + bizNoError = isValidBizNo(bizNo) ? nil : "형식: 000-00-00000" } private func formatBizNoInput(_ input: String) { diff --git a/StockMate/StockMate/app/feature/auth/viewmodel/AuthViewModel.swift b/StockMate/StockMate/app/feature/auth/viewmodel/AuthViewModel.swift index 0ef1ef7..045de49 100644 --- a/StockMate/StockMate/app/feature/auth/viewmodel/AuthViewModel.swift +++ b/StockMate/StockMate/app/feature/auth/viewmodel/AuthViewModel.swift @@ -78,7 +78,7 @@ final class AuthViewModel: ObservableObject { address: String, storeName: String, bizNo: String - ) async { + ) async -> Bool { print( """ @@ -107,8 +107,10 @@ final class AuthViewModel: ObservableObject { case .success(let apiResp): message = apiResp.message authState = .unauthenticated + return true case .failure(let err): message = err.message + return false } } diff --git a/StockMate/StockMate/app/navigation/IntroView.swift b/StockMate/StockMate/app/navigation/IntroView.swift new file mode 100644 index 0000000..742d03a --- /dev/null +++ b/StockMate/StockMate/app/navigation/IntroView.swift @@ -0,0 +1,21 @@ +// +// IntroView.swift +// StockMate +// +// Created by Admin on 11/10/25. +// + +import SwiftUI + +struct IntroView: View { + var body: some View { + ZStack { + Color.white.ignoresSafeArea() // 배경 흰색 + + Image("intrologo") // Assets에 있는 이미지 이름 + .resizable() + .scaledToFit() + .frame(width: 180, height: 180) // 필요시 크기 조정 + } + } +} diff --git a/StockMate/StockMate/resources/Assets.xcassets/AppIcon.appiconset/AppIcon_1024.png b/StockMate/StockMate/resources/Assets.xcassets/AppIcon.appiconset/AppIcon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..de54277330363353930722174a1d4350766c156f GIT binary patch literal 163115 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�KeITxc1S9oj1VGesEHY- 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%0A*A+ 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

cx9b81~q|IF4=tj`idM&`5`>i z{AdC$-)ZxkNnL&yR{n@VMn#AgMv}Z*6*RX(78a;}v-mebZJ1gU zm74cDo_iGk(S{iQ_tAcGJ}FgslYjW3jQbcZbUrf(5h>4LH@rj}emREj9d(UxNc;&2 zk5UYBQ`OofWuNw$r7frPlt&L`v%Y&M6Pl78k%7Gw5mcS1_mv{m`}QlkuN7XN*t)!N zuLD68jX{V9Y4el?P9E)?-MX+b`RvT#oc}i2i^DoP}Hf4=Yk5^S##pOa(KxY_R#e7qx+e2`0^qK`>6R~woVqj(=;k08m6!9{^G<1bNc!;qe-TOY4w{sW zgY+Kp4Fz42wR?1;;WX~g*X}i=v4RSkyT(HEc^@}K|9L_G`|GOYem8{w{pSa7a4fRa WrD?>Pdh=Jn*HdLJr80$QA^!)os71*D literal 0 HcmV?d00001 From 36862d54ae017cc333a1e47d5024ca83ef30070b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=98=84=EC=95=84?= Date: Wed, 12 Nov 2025 17:37:31 +0900 Subject: [PATCH 85/85] =?UTF-8?q?[CHORE]=20=EC=A3=BC=EC=84=9D=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20=EC=BD=94=EB=93=9C=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- StockMate/.DS_Store | Bin 6148 -> 6148 bytes StockMate/StockMate.xcodeproj/project.pbxproj | 4 ++ StockMate/StockMate/app/StockMateApp.swift | 2 +- .../core/KakaoPostcode/KakaoZipCodeVC.swift | 4 +- .../core/KakaoPostcode/KakaoZipCodeView.swift | 9 ++- .../core/KakaoPostcode/ViewController.swift | 3 +- .../app/core/common/Validators.swift | 14 ++-- .../app/core/components/AlertModal.swift | 45 ++++++++----- .../app/core/components/BarChartView.swift | 10 +-- .../app/core/components/BottomToast.swift | 54 ++-------------- .../app/core/components/CartInfoCard.swift | 1 - .../core/components/CustomSecureField.swift | 2 +- .../app/core/components/CustomTextField.swift | 18 +----- .../app/core/components/DonutChartView.swift | 9 ++- .../app/core/components/PrimaryButton.swift | 18 ------ .../app/core/components/RoundedCorner.swift | 8 ++- .../app/core/components/ToastModifier.swift | 2 + .../app/core/network/ApiClient.swift | 10 ++- .../app/core/network/AuthInterceptor.swift | 3 +- .../app/feature/auth/data/AuthApi.swift | 13 ++++ .../auth/data/AuthRepositoryImpl.swift | 2 + .../app/feature/auth/data/TokenStore.swift | 6 +- .../auth/domain/AuthRepositoryProtocol.swift | 3 + .../app/feature/auth/ui/HomeView.swift | 47 +++++--------- .../app/feature/auth/ui/LoginView.swift | 34 +--------- .../app/feature/auth/ui/RegisterView.swift | 61 +++++++++--------- .../auth/viewmodel/AuthViewModel.swift | 20 ++---- .../app/feature/cart/data/CartApi.swift | 1 - .../cart/data/CartRepositoryImpl.swift | 6 +- .../feature/cart/ui/DeliveryStatusView.swift | 41 ------------ .../cart/viewmodel/CartViewModel.swift | 4 +- .../feature/dashboard/data/HistoryApi.swift | 19 ++++-- .../data/HistoryRepositoryImpl.swift | 5 +- .../domain/HistoryRepositoryProtocol.swift | 6 +- .../dashboard/ui/InOutHistoryView.swift | 6 +- .../dashboard/ui/ReleaseDetailView.swift | 6 +- .../ui/TransactionTypeListView.swift | 36 ++++++++--- .../viewmodel/HistoryViewModel.swift | 23 ++++--- .../feature/inventory/data/InventoryApi.swift | 13 ++-- .../data/InventoryRepositoryImpl.swift | 11 ++-- .../domain/InventoryRepositoryProtocol.swift | 6 +- .../inventory/ui/IncomingScanView.swift | 8 +-- .../inventory/ui/InventorySearchView.swift | 5 +- .../feature/inventory/ui/InventoryView.swift | 2 - .../inventory/ui/OutgoingScanView.swift | 51 ++++++--------- .../inventory/ui/UsedPartListSheetView.swift | 25 ++++--- .../viewmodel/InventoryViewModel.swift | 14 +--- .../notification/data/NotificationApi.swift | 12 ++-- .../data/NotificationRepositoryImpl.swift | 8 ++- .../NotificationRepositoryProtocol.swift | 10 +-- .../ui/NotificationListView.swift | 2 +- .../viewmodel/NotificationViewModel.swift | 10 +-- .../app/feature/orders/data/OrderApi.swift | 35 ++++++---- .../orders/data/OrderRepositoryImpl.swift | 25 +++---- .../domain/OrderRepositoryProtocol.swift | 2 +- .../app/feature/orders/ui/OrderCartView.swift | 2 +- .../feature/orders/ui/OrderDetailView.swift | 32 ++++----- .../app/feature/orders/ui/OrderInfoView.swift | 10 +-- .../app/feature/orders/ui/OrderListView.swift | 5 +- .../orders/ui/OrderRequestSearchView.swift | 2 +- .../feature/orders/ui/OrderResultView.swift | 6 +- .../app/feature/orders/ui/OrderView.swift | 2 +- .../app/feature/orders/ui/ReceiptView.swift | 12 ++-- .../orders/viewmodel/OrderViewModel.swift | 27 ++------ .../app/feature/parts/data/PartApi.swift | 10 +-- .../parts/data/PartRepositoryImpl.swift | 2 + .../app/feature/parts/data/PartStore.swift | 7 +- .../parts/domain/PartRepositoryProtocol.swift | 3 + .../app/feature/parts/ui/QRScannerView.swift | 14 +--- .../parts/ui/QRScannerViewController.swift | 8 +-- .../parts/viewmodel/PartViewModel.swift | 6 +- .../app/feature/payment/data/PaymentApi.swift | 6 +- .../payment/data/PaymentRepositoryImpl.swift | 7 +- .../domain/PaymentRepositoryProtocol.swift | 2 +- .../payment/ui/DepositChargeView.swift | 4 +- .../viewmodel/DashboardViewModel.swift | 30 ++++----- .../payment/viewmodel/DepositViewModel.swift | 4 +- .../app/feature/user/data/UserApi.swift | 3 + .../user/data/UserRepositoryImpl.swift | 2 + .../user/domain/UserRepositoryProtocol.swift | 1 + .../feature/user/ui/ProfileCircleView.swift | 2 +- .../app/feature/user/ui/ProfileView.swift | 11 ++-- .../user/viewmodel/UserViewModel.swift | 4 +- .../StockMate/app/navigation/AppNavHost.swift | 9 ++- .../app/navigation/MainTabView.swift | 17 ++--- 85 files changed, 477 insertions(+), 567 deletions(-) delete mode 100644 StockMate/StockMate/app/core/components/PrimaryButton.swift diff --git a/StockMate/.DS_Store b/StockMate/.DS_Store index 850a2690ff2224608a8c76f39333b4c9a242130f..7939a512b7c9e7cb48ba891300e71f06c6e19ef7 100644 GIT binary patch delta 43 ycmZoMXffCj$I3K!+T;XQGmbZZI&y_wh!Y%Ki5xS5^fFFydvk_-?4 diff --git a/StockMate/StockMate.xcodeproj/project.pbxproj b/StockMate/StockMate.xcodeproj/project.pbxproj index 788b909..97cbaa9 100644 --- a/StockMate/StockMate.xcodeproj/project.pbxproj +++ b/StockMate/StockMate.xcodeproj/project.pbxproj @@ -271,6 +271,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 35TSG7VB2B; @@ -290,6 +291,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.hyuna.StockMate; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -301,6 +303,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 35TSG7VB2B; @@ -320,6 +323,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.hyuna.StockMate; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/StockMate/StockMate/app/StockMateApp.swift b/StockMate/StockMate/app/StockMateApp.swift index 57f9eb0..da169f7 100644 --- a/StockMate/StockMate/app/StockMateApp.swift +++ b/StockMate/StockMate/app/StockMateApp.swift @@ -17,7 +17,7 @@ struct StockMateApp: App { Group { if isLoading { - IntroView() // ✅ 로고만 보여주는 화면 + IntroView() // 로고만 보여주는 화면 } else { AppNavHost() .environmentObject(authViewModel) diff --git a/StockMate/StockMate/app/core/KakaoPostcode/KakaoZipCodeVC.swift b/StockMate/StockMate/app/core/KakaoPostcode/KakaoZipCodeVC.swift index a4fecb1..04e3245 100644 --- a/StockMate/StockMate/app/core/KakaoPostcode/KakaoZipCodeVC.swift +++ b/StockMate/StockMate/app/core/KakaoPostcode/KakaoZipCodeVC.swift @@ -14,7 +14,7 @@ class KakaoZipCodeVC: UIViewController { // MARK: - Properties var webView: WKWebView? let indicator = UIActivityIndicatorView(style: .medium) - var onAddressSelected: ((String) -> Void)? // ✅ SwiftUI로 결과 전달용 콜백 + var onAddressSelected: ((String) -> Void)? // SwiftUI로 결과 전달용 콜백 override func viewDidLoad() { super.viewDidLoad() @@ -64,7 +64,7 @@ extension KakaoZipCodeVC: WKScriptMessageHandler { didReceive message: WKScriptMessage) { guard let data = message.body as? [String: Any] else { return } let address = data["roadAddress"] as? String ?? "" - onAddressSelected?(address) // ✅ SwiftUI로 전달 + onAddressSelected?(address) // SwiftUI로 전달 dismiss(animated: true) } } diff --git a/StockMate/StockMate/app/core/KakaoPostcode/KakaoZipCodeView.swift b/StockMate/StockMate/app/core/KakaoPostcode/KakaoZipCodeView.swift index fa69a87..2efd44f 100644 --- a/StockMate/StockMate/app/core/KakaoPostcode/KakaoZipCodeView.swift +++ b/StockMate/StockMate/app/core/KakaoPostcode/KakaoZipCodeView.swift @@ -9,16 +9,23 @@ import SwiftUI import WebKit +// SwiftUI에서 UIKit 기반의 Kakao 우편번호 검색 화면을 사용하기 위한 래퍼 뷰 +// UIViewControllerRepresentable을 통해 KakaoZipCodeVC를 SwiftUI에 통합 struct KakaoZipCodeView: UIViewControllerRepresentable { + // 선택된 주소 값을 SwiftUI와 바인딩 @Binding var address: String + // KakaoZipCodeVC 생성 및 초기 설정 func makeUIViewController(context: Context) -> KakaoZipCodeVC { let vc = KakaoZipCodeVC() + + // 주소가 선택되었을 때 SwiftUI 바인딩 변수로 전달 vc.onAddressSelected = { selectedAddress in address = selectedAddress } return vc } - + + // UIViewController 상태 갱신 (현재는 별도 갱신 로직 없음) func updateUIViewController(_ uiViewController: KakaoZipCodeVC, context: Context) {} } diff --git a/StockMate/StockMate/app/core/KakaoPostcode/ViewController.swift b/StockMate/StockMate/app/core/KakaoPostcode/ViewController.swift index 2c793d0..b6d74f8 100644 --- a/StockMate/StockMate/app/core/KakaoPostcode/ViewController.swift +++ b/StockMate/StockMate/app/core/KakaoPostcode/ViewController.swift @@ -5,9 +5,10 @@ // Created by Admin on 11/5/25. // - import UIKit +// 기본 테스트용 ViewController +// 버튼을 눌러 Kakao 우편번호 검색 화면(KakaoZipCodeVC)을 표시함 class ViewController: UIViewController { // MARK: - UI Components diff --git a/StockMate/StockMate/app/core/common/Validators.swift b/StockMate/StockMate/app/core/common/Validators.swift index ccaaeb8..61c264c 100644 --- a/StockMate/StockMate/app/core/common/Validators.swift +++ b/StockMate/StockMate/app/core/common/Validators.swift @@ -15,14 +15,10 @@ func isValidEmail(_ email: String) -> Bool { func isValidPassword(_ pw: String) -> Bool { guard pw.count >= 8 else { return false } return pw.range(of: "[A-Za-z]", options: .regularExpression) != nil && - pw.range(of: "[0-9]", options: .regularExpression) != nil + pw.range(of: "[0-9]", options: .regularExpression) != nil } -// -//func isValidBizNo(_ no: String) -> Bool { -// let pattern = #"^\d{3}-\d{2}-\d{5}$"# -// return no.range(of: pattern, options: .regularExpression) != nil -//} + func isValidBizNo(_ no: String) -> Bool { - let regex = "^\\d{3}-\\d{2}-\\d{5}$" - return NSPredicate(format: "SELF MATCHES %@", regex).evaluate(with: no) - } + let regex = "^\\d{3}-\\d{2}-\\d{5}$" + return NSPredicate(format: "SELF MATCHES %@", regex).evaluate(with: no) +} diff --git a/StockMate/StockMate/app/core/components/AlertModal.swift b/StockMate/StockMate/app/core/components/AlertModal.swift index 72d1e1b..88db275 100644 --- a/StockMate/StockMate/app/core/components/AlertModal.swift +++ b/StockMate/StockMate/app/core/components/AlertModal.swift @@ -5,22 +5,28 @@ // Created by Admin on 11/4/25. // - import SwiftUI + +// 공용 알림 모달 뷰 +// 아이콘, 제목, 메시지, 버튼 구성에 따라 다양한 형태로 표시 가능 struct AlertModal: View { - var icon: Image? = nil // ✅ 아이콘 없을 수도 있음 + var icon: Image? = nil var title: String var message: String? = nil + // 주요 버튼 텍스트 및 동작 var primaryButtonTitle: String var primaryAction: () -> Void + // 보조 버튼 텍스트 및 동작 (선택) var secondaryButtonTitle: String? = nil var secondaryAction: (() -> Void)? = nil - var buttonLayout: ButtonLayout = .vertical // ✅ horizontal / vertical + // 버튼 배치 방향 설정 (세로 / 가로) + var buttonLayout: ButtonLayout = .vertical + // 버튼 레이아웃 타입 정의 enum ButtonLayout { case vertical case horizontal @@ -28,6 +34,7 @@ struct AlertModal: View { var body: some View { VStack(spacing: 15) { + // 아이콘이 있을 경우 표시 if let icon = icon { icon .resizable() @@ -35,12 +42,13 @@ struct AlertModal: View { .frame(width: 120, height: 120) .padding(.top, 6) } - + // 제목 텍스트 Text(title) .font(.system(size: 18, weight: .bold)) .multilineTextAlignment(.center) .padding(.top, 10) + // 메시지 텍스트가 있을 경우 표시 if let message = message { Text(message) .font(.system(size: 14, weight: .regular)) @@ -49,23 +57,34 @@ struct AlertModal: View { .padding(.bottom, 6) } + // 버튼 레이아웃에 따라 분기 if buttonLayout == .vertical { + // 세로 방향 버튼 배치 VStack(spacing: 10) { if let secondary = secondaryButtonTitle, let secondaryAction = secondaryAction { Button(secondary, action: secondaryAction) - .buttonStyle(CustomButtonStyle(type: .outlined(.Primary))) + .buttonStyle( + CustomButtonStyle(type: .outlined(.Primary)) + ) } Button(primaryButtonTitle, action: primaryAction) - .buttonStyle(CustomButtonStyle(type: .filled(Color.Primary))) + .buttonStyle( + CustomButtonStyle(type: .filled(Color.Primary)) + ) } } else { + // 가로 방향 버튼 배치 HStack(spacing: 10) { if let secondary = secondaryButtonTitle, let secondaryAction = secondaryAction { Button(secondary, action: secondaryAction) - .buttonStyle(CustomButtonStyle(type: .outlined(.Primary))) + .buttonStyle( + CustomButtonStyle(type: .outlined(.Primary)) + ) } Button(primaryButtonTitle, action: primaryAction) - .buttonStyle(CustomButtonStyle(type: .filled(Color.Primary))) + .buttonStyle( + CustomButtonStyle(type: .filled(Color.Primary)) + ) } } } @@ -82,7 +101,7 @@ import SwiftUI #Preview { ScrollView{ VStack(spacing: 40) { - // ✅ 1. 체크 아이콘 + 버튼 1개 + // 체크 아이콘 + 버튼 1개 AlertModal( icon: Image("SuccessIllust"), title: "등록 완료!", @@ -98,9 +117,7 @@ import SwiftUI primaryAction: {} ) - - - // ✅ 2. 아이콘 없이 버튼 2개 (가로) + // 아이콘 없이 버튼 2개 (가로 배치) AlertModal( title: "주문 취소", message: "주문을 취소하시겠습니까?", @@ -120,8 +137,7 @@ import SwiftUI buttonLayout: .horizontal ) - - // ✅ 3. 주문완료 + // 주문 완료 알림 (세로 버튼 배치) AlertModal( icon: Image("SuccessIllust"), title: "주문완료!", @@ -136,5 +152,4 @@ import SwiftUI .padding() .background(Color.gray.opacity(0.1)) } - } diff --git a/StockMate/StockMate/app/core/components/BarChartView.swift b/StockMate/StockMate/app/core/components/BarChartView.swift index 095e3a7..852180d 100644 --- a/StockMate/StockMate/app/core/components/BarChartView.swift +++ b/StockMate/StockMate/app/core/components/BarChartView.swift @@ -46,20 +46,20 @@ struct BarChartView: View { VStack { RoundedRectangle(cornerRadius: 10) .fill(activeMonth == displayLabels[i] ? Color.Primary : Color.LightBlue04) - .frame(width: barWidth, height: max(chartHeight * reversedValues[i], 8)) // ✅ 최소 높이 보장 + .frame(width: barWidth, height: max(chartHeight * reversedValues[i], 8)) // 최소 높이 보장 .onTapGesture { selectedMonth = (selectedMonth == displayLabels[i]) ? nil : displayLabels[i] } Text(displayLabels[i]) - .font(.system(size: 13, weight: activeMonth == displayLabels[i] ? .semibold : .light)) // ✅ 선택된 막대는 bold + .font(.system(size: 13, weight: activeMonth == displayLabels[i] ? .semibold : .light)) // 선택된 막대는 글씨 bold .padding(.top, 3) } } } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom) } - .frame(height: 163) // ← 전체 그래프 영역 높이 확장 + .frame(height: 163) // 전체 그래프 영역 높이 확장 .padding(.bottom, 7) Rectangle() @@ -68,7 +68,7 @@ struct BarChartView: View { .padding(.horizontal, 4) - // ✅ 하단 "n월 지출금액 ooo원" 표시 + // 하단 "n월 지출금액 ooo원" 표시 if let index = displayLabels.firstIndex(of: activeMonth) { HStack { Text("\(displayLabels[index]) 지출 현황") @@ -83,7 +83,7 @@ struct BarChartView: View { } } .frame(maxWidth: .infinity) - // ✅ 초기 로드 시 최신월 자동 선택 + // 초기 로드 시 최신월 자동 선택 .onAppear { if selectedMonth == nil { selectedMonth = defaultMonth diff --git a/StockMate/StockMate/app/core/components/BottomToast.swift b/StockMate/StockMate/app/core/components/BottomToast.swift index 3de1bbc..dd9f586 100644 --- a/StockMate/StockMate/app/core/components/BottomToast.swift +++ b/StockMate/StockMate/app/core/components/BottomToast.swift @@ -5,56 +5,9 @@ // Created by Admin on 11/10/25. // -//import SwiftUI -// -//struct BottomToast: View { -// let message: String -// @Binding var isVisible: Bool -// var iconName: String = "toastlogo" -// var backgroundColor: Color = Color(hex: "4CAF50") // 초록색 계열 -// var duration: Double = 2.3 -// -// var body: some View { -// VStack { -// Spacer() -// -// if isVisible { -// HStack(spacing: 10) { -// Image(iconName) -// .resizable() -// .scaledToFit() -// .foregroundColor(.white) -// .font(.system(size: 18, weight: .semibold)) -// -// Text(message) -// .font(.system(size: 14, weight: .semibold)) -// .foregroundColor(.white) -// .multilineTextAlignment(.center) -// .lineLimit(2) -// } -// .padding(.horizontal, 18) -// .padding(.vertical, 12) -// .background( -// RoundedRectangle(cornerRadius: 14) -// .fill(backgroundColor.opacity(0.95)) -// ) -// .shadow(color: .black.opacity(0.15), radius: 8, x: 0, y: 4) -// .padding(.bottom, 60) -// .transition(.move(edge: .bottom).combined(with: .opacity)) -// .onAppear { -// DispatchQueue.main.asyncAfter(deadline: .now() + duration) { -// withAnimation(.easeInOut(duration: 0.3)) { -// isVisible = false -// } -// } -// } -// } -// } -// .animation(.easeInOut(duration: 0.3), value: isVisible) -// } -//} import SwiftUI +// 화면 하단에 토스트 알림 뷰 struct BottomToast: View { let message: String @Binding var isVisible: Bool @@ -66,7 +19,8 @@ struct BottomToast: View { var body: some View { VStack { Spacer() - + + // 토스트가 표시될 때만 렌더링 if isVisible { HStack(spacing: 10) { Image(iconName) @@ -89,7 +43,7 @@ struct BottomToast: View { .shadow(color: .black.opacity(0.1), radius: 5, x: 0, y: 4) .padding(.bottom, 60) .padding(.horizontal, 50) - .onAppear { + .onAppear { // 토스트가 나타날 때 타이머 시작 DispatchQueue.main.asyncAfter(deadline: .now() + duration) { withAnimation(.easeInOut(duration: 0.25)) { isVisible = false diff --git a/StockMate/StockMate/app/core/components/CartInfoCard.swift b/StockMate/StockMate/app/core/components/CartInfoCard.swift index d38e0f1..6c3c0c6 100644 --- a/StockMate/StockMate/app/core/components/CartInfoCard.swift +++ b/StockMate/StockMate/app/core/components/CartInfoCard.swift @@ -59,7 +59,6 @@ struct CartInfoCard: View { .padding() .background(Color.white) .cornerRadius(14) -// .shadow(color: .black.opacity(0.05), radius: 4, x: 0, y: 4) } } diff --git a/StockMate/StockMate/app/core/components/CustomSecureField.swift b/StockMate/StockMate/app/core/components/CustomSecureField.swift index a9a9f54..c85f895 100644 --- a/StockMate/StockMate/app/core/components/CustomSecureField.swift +++ b/StockMate/StockMate/app/core/components/CustomSecureField.swift @@ -16,7 +16,7 @@ struct CustomSecureField: View { @FocusState private var isFocused: Bool @State private var showPassword = false - // ✅ 테두리 색상 계산 로직 (CustomTextField와 동일) + // 테두리 색상 계산 로직 (CustomTextField와 동일) private var borderColor: Color { if let error = errorMessage, !error.isEmpty { return .red diff --git a/StockMate/StockMate/app/core/components/CustomTextField.swift b/StockMate/StockMate/app/core/components/CustomTextField.swift index cd08d8d..a2fa910 100644 --- a/StockMate/StockMate/app/core/components/CustomTextField.swift +++ b/StockMate/StockMate/app/core/components/CustomTextField.swift @@ -13,11 +13,11 @@ struct CustomTextField: View { @Binding var text: String var isEmail: Bool = false var errorMessage: String? = nil - var isReadOnly: Bool = false // ✅ 추가 + var isReadOnly: Bool = false @FocusState private var isFocused: Bool - // ✅ 테두리 색상 계산 로직 + // 테두리 색상 계산 로직 private var borderColor: Color { if let error = errorMessage, !error.isEmpty { return .red @@ -37,13 +37,6 @@ struct CustomTextField: View { ZStack { RoundedRectangle(cornerRadius: 8) -// .strokeBorder( -// isFocused -// ? Color.Primary -// : (errorMessage == nil ? Color.LightBlue04 : .red), -// lineWidth: 1 -// ) - .strokeBorder(borderColor, lineWidth: 1) .background( RoundedRectangle(cornerRadius: 8) @@ -60,7 +53,7 @@ struct CustomTextField: View { ) if isReadOnly { - // ✅ 가로 스크롤 가능한 읽기 전용 텍스트 + // 가로 스크롤 가능한 읽기 전용 텍스트 ScrollView(.horizontal, showsIndicators: false) { Text(text.isEmpty ? placeholder : text) .font(.system(size: 15)) @@ -88,11 +81,6 @@ struct CustomTextField: View { .foregroundColor(.red) .frame(height: 14) // 고정 높이 확보 .opacity(errorMessage == nil ? 0 : 1) // 없을 땐 투명 -// if let errorMessage = errorMessage { -// Text(errorMessage) -// .font(.caption) -// .foregroundColor(.red) -// } } } } diff --git a/StockMate/StockMate/app/core/components/DonutChartView.swift b/StockMate/StockMate/app/core/components/DonutChartView.swift index 2dd37c6..5163845 100644 --- a/StockMate/StockMate/app/core/components/DonutChartView.swift +++ b/StockMate/StockMate/app/core/components/DonutChartView.swift @@ -15,7 +15,7 @@ struct DonutChartView: View { Double(data.map { $0.totalAmount }.reduce(0, +)) } - // ✅ 각 항목별 비율 계산 + // 각 항목별 비율 계산 var percentages: [Double] { data.map { total == 0 ? 0 : (Double($0.totalAmount) / total * 100) } } @@ -39,7 +39,7 @@ struct DonutChartView: View { var body: some View { HStack(alignment: .center, spacing: 24) { - // ✅ 도넛 차트 + // 도넛 차트 if total == 0 { Text("데이터 없음") .foregroundColor(.gray) @@ -61,9 +61,8 @@ struct DonutChartView: View { endPoint: .bottomTrailing ) ) -// .foregroundStyle(colors[index % colors.count]) .cornerRadius(8.0) - // ✅ 도넛 안쪽에 비율 표시 + // 도넛 안쪽에 비율 표시 .annotation(position: .overlay) { let percentage = percentages[index] Text("\(percentage, specifier: "%.1f")%") @@ -77,7 +76,7 @@ struct DonutChartView: View { .chartLegend(.hidden) // 기본 범례 숨김 } - // ✅ 오른쪽 커스텀 범례 + // 오른쪽 커스텀 범례 VStack(alignment: .leading, spacing: 18) { ForEach(Array(data.enumerated()), id: \.offset) { index, item in let percentage = percentages[index] diff --git a/StockMate/StockMate/app/core/components/PrimaryButton.swift b/StockMate/StockMate/app/core/components/PrimaryButton.swift deleted file mode 100644 index b8f165f..0000000 --- a/StockMate/StockMate/app/core/components/PrimaryButton.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// PrimaryButton.swift -// StockMate -// -// Created by Admin on 10/10/25. -// - -import SwiftUI - -struct PrimaryButton: View { - var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) - } -} - -#Preview { - PrimaryButton() -} diff --git a/StockMate/StockMate/app/core/components/RoundedCorner.swift b/StockMate/StockMate/app/core/components/RoundedCorner.swift index b93b448..43057fe 100644 --- a/StockMate/StockMate/app/core/components/RoundedCorner.swift +++ b/StockMate/StockMate/app/core/components/RoundedCorner.swift @@ -7,10 +7,12 @@ import SwiftUI +// 특정 모서리만 둥글게 처리할 수 있는 커스텀 Shape 구조체 struct RoundedCorner: Shape { - var radius: CGFloat = .infinity - var corners: UIRectCorner = .allCorners - + var radius: CGFloat = .infinity // 둥근 모서리의 반경 (기본값은 무한대) + var corners: UIRectCorner = .allCorners // 둥글게 적용할 모서리 (기본값: 전체 모서리) + + // 지정된 모서리에만 둥근 경로를 적용하여 Path 생성 func path(in rect: CGRect) -> Path { let path = UIBezierPath( roundedRect: rect, diff --git a/StockMate/StockMate/app/core/components/ToastModifier.swift b/StockMate/StockMate/app/core/components/ToastModifier.swift index ae13461..4fc4ab1 100644 --- a/StockMate/StockMate/app/core/components/ToastModifier.swift +++ b/StockMate/StockMate/app/core/components/ToastModifier.swift @@ -41,6 +41,7 @@ struct ToastView: View { } } +// ViewModifier를 사용해 기존 View 위에 토스트를 표시 struct ToastModifier: ViewModifier { @Binding var isPresented: Bool let message: String @@ -74,6 +75,7 @@ struct ToastModifier: ViewModifier { } } +// View 확장을 통해 modifier를 간편하게 사용할 수 있도록 함 extension View { func toast( isPresented: Binding, diff --git a/StockMate/StockMate/app/core/network/ApiClient.swift b/StockMate/StockMate/app/core/network/ApiClient.swift index 94307e0..d28bd7a 100644 --- a/StockMate/StockMate/app/core/network/ApiClient.swift +++ b/StockMate/StockMate/app/core/network/ApiClient.swift @@ -9,12 +9,18 @@ import Foundation import Alamofire struct ApiClient { - static let baseURL = "https://api.stockmate.site/" - static let shared: Session = { + static let baseURL = "https://api.stockmate.site/" // 기본 API 서버 주소 + static let shared: Session = { // Alamofire의 Session을 싱글톤 형태로 공유 + + // 요청 시 토큰 등을 자동으로 처리하기 위한 인터셉터 설정 let interceptor = AuthInterceptor() + + // 네트워크 요청 관련 기본 설정 구성 let config = URLSessionConfiguration.default config.timeoutIntervalForRequest = 20 config.timeoutIntervalForResource = 20 + + // 커스텀 설정과 인터셉터를 적용한 세션 생성 return Session(configuration: config, interceptor: interceptor, eventMonitors: []) }() } diff --git a/StockMate/StockMate/app/core/network/AuthInterceptor.swift b/StockMate/StockMate/app/core/network/AuthInterceptor.swift index 4a94468..76709f9 100644 --- a/StockMate/StockMate/app/core/network/AuthInterceptor.swift +++ b/StockMate/StockMate/app/core/network/AuthInterceptor.swift @@ -8,6 +8,7 @@ import Foundation import Alamofire +// API 요청 시 Access Token을 자동으로 헤더에 추가하는 인터셉터 final class AuthInterceptor: RequestInterceptor, @unchecked Sendable { private let tokenStore = TokenStore.shared @@ -19,5 +20,5 @@ final class AuthInterceptor: RequestInterceptor, @unchecked Sendable { completion(.success(req)) } - // 필요 시 retry(_:for:dueTo:completion:) 구현해서 401 -> refresh token 흐름 처리 가능 + // TODO: 401 Unauthorized 응답 시 Refresh Token을 사용해 토큰 재발급 로직 추가 } diff --git a/StockMate/StockMate/app/feature/auth/data/AuthApi.swift b/StockMate/StockMate/app/feature/auth/data/AuthApi.swift index d8e4840..2528705 100644 --- a/StockMate/StockMate/app/feature/auth/data/AuthApi.swift +++ b/StockMate/StockMate/app/feature/auth/data/AuthApi.swift @@ -8,6 +8,8 @@ import Foundation import Alamofire +// === Request === +// 회원가입 요청 바디 struct RegisterRequest: Encodable { let email: String let password: String @@ -16,22 +18,29 @@ struct RegisterRequest: Encodable { let storeName: String let businessNumber: String } +// 로그인 요청 바디 struct LoginRequest: Encodable { let email: String let password: String } + +// === Response === +// 회원가입 응답 struct RegisterResponse: Decodable { let status: Int let success: Bool let message: String } +// 로그인 응답 데이터 struct LoginData: Decodable { let accessToken: String let refreshToken: String let role: String } + +// 로그인 응답 전체 구조 struct LoginResponse: Decodable { let status: Int let success: Bool @@ -39,12 +48,16 @@ struct LoginResponse: Decodable { let data: LoginData? } +// === API === +// 인증 관련 API 모음 enum AuthApi { + // POST - 회원가입 요청 static func register(_ req: RegisterRequest) -> DataRequest { let url = ApiClient.baseURL + "api/v1/auth/register" return ApiClient.shared.request(url, method: .post, parameters: req, encoder: JSONParameterEncoder.default) } + // POST - 로그인 요청 static func login(_ req: LoginRequest) -> DataRequest { let url = ApiClient.baseURL + "api/v1/auth/login" return ApiClient.shared.request(url, method: .post, parameters: req, encoder: JSONParameterEncoder.default) diff --git a/StockMate/StockMate/app/feature/auth/data/AuthRepositoryImpl.swift b/StockMate/StockMate/app/feature/auth/data/AuthRepositoryImpl.swift index cd07cd2..1e83f57 100644 --- a/StockMate/StockMate/app/feature/auth/data/AuthRepositoryImpl.swift +++ b/StockMate/StockMate/app/feature/auth/data/AuthRepositoryImpl.swift @@ -9,11 +9,13 @@ import Foundation import Alamofire final class AuthRepositoryImpl: AuthRepositoryProtocol { + // 회원가입 요청 func register(_ req: RegisterRequest) async -> AppResult> { let dataReq = AuthApi.register(req) return await safeApi(dataReq, decodeTo: ApiResponse.self) } + // 로그인 요청 func login(_ req: LoginRequest) async -> AppResult> { let dataReq = AuthApi.login(req) return await safeApi(dataReq, decodeTo: ApiResponse.self) diff --git a/StockMate/StockMate/app/feature/auth/data/TokenStore.swift b/StockMate/StockMate/app/feature/auth/data/TokenStore.swift index 6a7e353..45d7e42 100644 --- a/StockMate/StockMate/app/feature/auth/data/TokenStore.swift +++ b/StockMate/StockMate/app/feature/auth/data/TokenStore.swift @@ -14,27 +14,31 @@ final class TokenStore: @unchecked Sendable { private let defaults = UserDefaults.standard + // 토큰 및 역할 저장 func save(access: String, refresh: String, role: String) { defaults.set(role, forKey: "role") - // access/refresh는 Keychain에 저장하는 걸 권장 KeychainHelper.standard.save(access, service: "com.stockmate", account: "accessToken") KeychainHelper.standard.save(refresh, service: "com.stockmate", account: "refreshToken") } + // 저장된 토큰 및 역할 제거 func clear() { defaults.removeObject(forKey: "role") KeychainHelper.standard.delete(service: "com.stockmate", account: "accessToken") KeychainHelper.standard.delete(service: "com.stockmate", account: "refreshToken") } + // 액세스 토큰 조회 func getAccessToken() -> String? { return KeychainHelper.standard.read(service: "com.stockmate", account: "accessToken") } + // 리프레시 토큰 조회 func getRefreshToken() -> String? { return KeychainHelper.standard.read(service: "com.stockmate", account: "refreshToken") } + // 사용자 역할(Role) 조회 func getRole() -> String? { return defaults.string(forKey: "role") } diff --git a/StockMate/StockMate/app/feature/auth/domain/AuthRepositoryProtocol.swift b/StockMate/StockMate/app/feature/auth/domain/AuthRepositoryProtocol.swift index 6691965..eac76f4 100644 --- a/StockMate/StockMate/app/feature/auth/domain/AuthRepositoryProtocol.swift +++ b/StockMate/StockMate/app/feature/auth/domain/AuthRepositoryProtocol.swift @@ -9,6 +9,9 @@ import Foundation import Alamofire protocol AuthRepositoryProtocol { + // 회원가입 요청 func register(_ req: RegisterRequest) async -> AppResult> + + // 로그인 요청 func login(_ req: LoginRequest) async -> AppResult> } diff --git a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift index 4160b73..3d6de8d 100644 --- a/StockMate/StockMate/app/feature/auth/ui/HomeView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/HomeView.swift @@ -12,7 +12,6 @@ struct HomeView: View { @EnvironmentObject var authViewModel: AuthViewModel @StateObject private var userViewModel = UserViewModel() @StateObject private var inventoryViewModel = InventoryViewModel() -// @EnvironmentObject var dashboardViewModel: DashboardViewModel //preview 용 @StateObject private var dashboardViewModel = DashboardViewModel() @StateObject private var notificationViewModel = NotificationViewModel() @@ -41,7 +40,7 @@ struct HomeView: View { Spacer() - NavigationLink(destination: NotificationListView()) { // 🔴 전달 + NavigationLink(destination: NotificationListView()) { ZStack(alignment: .topTrailing) { Image("notification") .resizable() @@ -66,7 +65,7 @@ struct HomeView: View { } .padding(.horizontal) - // 🔍 검색창 + // 검색창 NavigationLink(destination: InventorySearchView()) { HStack { Image(systemName: "magnifyingglass") @@ -94,7 +93,7 @@ struct HomeView: View { Text("지난달 카테고리 별 지출") .font(.system(size: 15, weight: .semibold)) .padding(4) - .frame(maxWidth: .infinity, alignment: .leading) // ✅ 항상 왼쪽 정렬 + .frame(maxWidth: .infinity, alignment: .leading) HStack { if dashboardViewModel.isLoading { @@ -126,7 +125,7 @@ struct HomeView: View { Text("월간 지출 현황") .font(.system(size: 15, weight: .semibold)) .padding(4) - .frame(maxWidth: .infinity, alignment: .leading) // ✅ 항상 왼쪽 정렬 + .frame(maxWidth: .infinity, alignment: .leading) if dashboardViewModel.isLoading { ProgressView("데이터 불러오는 중...") @@ -154,21 +153,21 @@ struct HomeView: View { .task { // 카테고리 데이터 로드 await inventoryViewModel.loadLackCountByCategory() - await dashboardViewModel.fetchMonthlySpending() // ✅ 추가 - await dashboardViewModel.fetchCategorySpending() // ✅ 추가 - await notificationViewModel.fetchUnreadCount() // 🔴 추가 + await dashboardViewModel.fetchMonthlySpending() + await dashboardViewModel.fetchCategorySpending() + await notificationViewModel.fetchUnreadCount() } .onAppear { Task { await userViewModel.loadUserInfo() } } // 화면 디자인 시 잠시 주석처리 - // ✅ 세션 만료 시 자동으로 로그인 뷰로 이동 -// .onChange(of: userViewModel.shouldGoToLogin) { shouldGo in -// if shouldGo { -// print("세션 만료됨 → 로그인 화면으로 이동") -// authViewModel.logout() -// } -// } + // 세션 만료 시 자동으로 로그인 뷰로 이동 + .onChange(of: userViewModel.shouldGoToLogin) { shouldGo in + if shouldGo { + print("세션 만료됨 → 로그인 화면으로 이동") + authViewModel.logout() + } + } } private var lackStockSection: some View { @@ -178,7 +177,7 @@ struct HomeView: View { .frame(maxWidth: .infinity, alignment: .leading) HStack(spacing: 13) { if inventoryViewModel.lackCounts.isEmpty { - // ✅ 데이터가 없을 때도 공간 확보 + // 데이터가 없을 때도 공간 확보 ForEach(0..<5) { _ in StatusItem( title: "-", @@ -267,19 +266,3 @@ struct StatusItem: View { }.frame(maxWidth: .infinity, minHeight: 70) } } - - -#Preview { - let dashboardVM = DashboardViewModel() - dashboardVM.categorySpendings = [ - CategorySpending(categoryName: "전기/램프", totalAmount: 450000), - CategorySpending(categoryName: "엔진/미션", totalAmount: 300000), - CategorySpending(categoryName: "하체/바디", totalAmount: 150000), - CategorySpending(categoryName: "내장/외장", totalAmount: 100000), - CategorySpending(categoryName: "기타소모품", totalAmount: 50000) - ] - - return HomeView() - .environmentObject(AuthViewModel()) - .environmentObject(dashboardVM) // ✅ 이제 진짜 연결됨! -} diff --git a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift index 389a680..a83eb77 100644 --- a/StockMate/StockMate/app/feature/auth/ui/LoginView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/LoginView.swift @@ -18,7 +18,7 @@ struct LoginView: View { @State private var emailError: String? = nil @State private var pwError: String? = nil - // ✅ 토스트 관련 상태 추가 + // 토스트 관련 상태 추가 @State private var showTopToast = false @State private var topToastMessage = "" @@ -60,7 +60,7 @@ struct LoginView: View { .keyboardType(.emailAddress) .padding(.horizontal, 24) .onChange(of: authViewModel.email) { newValue in - // 입력 중 실시간 validation (뷰 업데이트 중이 아니므로 안전) + // 입력 중 실시간 validation emailError = isValidEmail(newValue) ? nil : "이메일 형식을 확인해주세요" } @@ -125,24 +125,8 @@ struct LoginView: View { } } - // 승인 아이디 받기 전 - // 홈화면으로 이동 - HStack { - Button(action: { - authViewModel.authState = .authenticated - }) { - Text("홈화면으로 이동") - .font(.system(size: 13, weight: .bold)) - .foregroundColor(Color.Secondary) - } - } - .padding(.top, 5) - Spacer() - - // ✅ 상단 토스트 표시 - //TopToast(message: topToastMessage, isVisible: $showTopToast) } .background(Color.Light) .onTapGesture { @@ -150,9 +134,6 @@ struct LoginView: View { } .ignoresSafeArea() - // ✅ 오버레이로 위에 띄움 (맨 위에 고정) -// TopToast(message: topToastMessage, isVisible: $showTopToast) -// .zIndex(1) // 다른 뷰 위로 TopToast(message: topToastMessage, isVisible: $showTopToast, iconName: "exclamationmark.circle", @@ -164,12 +145,6 @@ struct LoginView: View { } // MARK: - 유효성 검사 함수 -// private func isValidForm() -> Bool { -// emailError = isValidEmail(authViewModel.email) ? nil : "이메일 형식을 확인해주세요" -// pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" -// return emailError == nil && pwError == nil -//// return true -// } // 1) 뷰 내부(바디 바깥) — 부작용 없는 computed property private var isFormValid: Bool { return isValidEmail(authViewModel.email) && authViewModel.password.count >= 8 @@ -182,10 +157,7 @@ struct LoginView: View { pwError = authViewModel.password.count >= 8 ? nil : "8자 이상 비밀번호를 입력해주세요" } - // 기존의 isValidForm 함수는 이제 뷰에 호출되는 순수 검사로 대체하거나 삭제 - // private func isValidForm() -> Bool { ... } 대신 위 isFormValid 사용 - - // ✅ 상단 토스트 표시 함수 + // 상단 토스트 표시 함수 private func showToast(_ message: String) { topToastMessage = message withAnimation { diff --git a/StockMate/StockMate/app/feature/auth/ui/RegisterView.swift b/StockMate/StockMate/app/feature/auth/ui/RegisterView.swift index 0da1967..bd9a1b4 100644 --- a/StockMate/StockMate/app/feature/auth/ui/RegisterView.swift +++ b/StockMate/StockMate/app/feature/auth/ui/RegisterView.swift @@ -9,7 +9,7 @@ import SwiftUI struct RegisterView: View { @EnvironmentObject private var viewModel: AuthViewModel - + // MARK: - 사용자 입력값 @State private var email = "" @State private var password = "" @State private var confirmPassword = "" @@ -18,7 +18,7 @@ struct RegisterView: View { @State private var address = "" @State private var bizNo = "" - // 에러 메시지 상태 + // MARK: - 에러 메시지 상태값 @State private var emailError: String? = nil @State private var pwError: String? = nil @State private var confirmPasswordError: String? = nil @@ -27,21 +27,20 @@ struct RegisterView: View { @State private var addressError: String? = nil @State private var bizNoError: String? = nil + // MARK: - UI 상태 관리 @State private var isLoading = false @State private var showToast = false - @State private var showAddressSearch = false @State private var showSuccessToast = false var body: some View { ZStack { - - + // MARK: - 메인 스크롤 영역 ScrollView { VStack(spacing: 16) { Spacer().frame(height: 70) - // MARK: - Logo + // MARK: - 로고 Image("stockmate_logo") .resizable() .scaledToFit() @@ -49,15 +48,16 @@ struct RegisterView: View { Spacer().frame(height: 4) - // MARK: - Title + // MARK: - 화면 제목 Text("회원가입") .font(.system(size: 28, weight: .bold)) .frame(maxWidth: .infinity, alignment: .leading) .padding(.horizontal, 24) .foregroundColor(Color.DarkBlue01) - // MARK: - Text Fields + // MARK: - 입력 폼 VStack { + // 이메일 CustomTextField( title: "이메일", placeholder: "stockmate@gmail.com", @@ -68,6 +68,8 @@ struct RegisterView: View { .onChange(of: email) { newValue in emailError = isValidEmail(newValue) ? nil : "이메일 형식을 확인해주세요" } + + // 비밀번호 CustomSecureField( title: "비밀번호", placeholder: "비밀번호를 입력하세요", @@ -80,6 +82,7 @@ struct RegisterView: View { confirmPasswordError = (confirmPassword.isEmpty || confirmPassword == newValue) ? nil : "비밀번호가 일치하지 않습니다" } + // 비밀번호 확인 CustomSecureField( title: "비밀번호 확인", placeholder: "비밀번호를 다시 입력하세요", @@ -89,26 +92,28 @@ struct RegisterView: View { .onChange(of: confirmPassword) { newValue in confirmPasswordError = (password == newValue) ? nil : "비밀번호가 일치하지 않습니다" } + // 대표자 이름 CustomTextField( title: "대표자 이름", placeholder: "홍길동", text: $owner, errorMessage: nil ) + // 지점 이름 CustomTextField( title: "지점 이름", placeholder: "강남점", text: $storeName, errorMessage: nil ) - // ✅ 주소 입력 필드 + 버튼 추가 부분 + // 주소 입력 필드 VStack(alignment: .leading, spacing: 4) { CustomTextField( title: "주소", placeholder: "도로명 주소를 검색하세요", text: $address, errorMessage: addressError, - isReadOnly: true // ✅ 추가 + isReadOnly: true ) .disabled(true) // 사용자가 직접 입력 못하게 .onTapGesture { @@ -135,7 +140,7 @@ struct RegisterView: View { .padding(.horizontal, 24) - // MARK: - Register Button + // MARK: - 회원가입 버튼 if isLoading { ProgressView("회원가입 중...") .progressViewStyle(CircularProgressViewStyle()) @@ -154,13 +159,6 @@ struct RegisterView: View { bizNo: bizNo.filter { $0.isNumber } ) isLoading = false - - // ✅ 회원가입 성공 시 토스트 표시 -// if success { -// withAnimation { -// showSuccessToast = true -// } -// } } }) { Text("회원가입") @@ -178,10 +176,12 @@ struct RegisterView: View { .padding(.horizontal, 24) } + // 상단 토스트 (오류/알림용) TopToast(message: viewModel.message, isVisible: $showToast) Spacer().frame(height: 5) - // MARK: - Login Link + + // MARK: - 로그인 페이지로 이동 링크 HStack(spacing: 4) { Text("이미 계정이 있으신가요?") .foregroundColor(Color.gray) @@ -198,7 +198,7 @@ struct RegisterView: View { } .padding(.bottom, 40) - // ✅ 키보드 가림 방지용 여백 + // 키보드 가림 방지용 여백 Spacer().frame(height: 300) } } @@ -207,14 +207,13 @@ struct RegisterView: View { .onTapGesture { UIApplication.shared.hideKeyboard() } - .scrollDismissesKeyboard(.interactively) // ✅ 손가락으로 스크롤하면 키보드 자동 내려감 - // showToast 자동으로 트리거: viewModel.message 변경 시 토스트 보여주기 + .scrollDismissesKeyboard(.interactively) // 손가락으로 스크롤하면 키보드 자동 내려감 .onChange(of: viewModel.message) { newMsg in guard !newMsg.isEmpty else { return } showToast = true } - // ✅ 회원가입 성공 토스트 + // 회원가입 성공 토스트 BottomToast( message: "회원가입 성공", isVisible: $showSuccessToast, @@ -225,11 +224,8 @@ struct RegisterView: View { } } - - // MARK: - 유효성 검사 함수 - - // MARK: - computed form valid (부작용 없음) + // MARK: - 전체 폼 유효성 검사 private var isFormValid: Bool { return isValidEmail(email) && isValidPassword(password) @@ -240,7 +236,7 @@ struct RegisterView: View { && isValidBizNo(bizNo) } - // MARK: - validate & helpers + // MARK: - 입력값 검증 및 에러 설정 private func validateAndSetErrors() { emailError = isValidEmail(email) ? nil : "이메일 형식을 확인해주세요" pwError = isValidPassword(password) ? nil : "8자 이상, 영문+숫자 조합입니다." @@ -249,11 +245,12 @@ struct RegisterView: View { bizNoError = isValidBizNo(bizNo) ? nil : "형식: 000-00-00000" } + // MARK: - 사업자등록번호 자동 포맷팅 private func formatBizNoInput(_ input: String) { - // 1️⃣ 숫자만 남기기 + // 숫자만 남기기 let digitsOnly = input.filter { $0.isNumber } - // 2️⃣ 하이픈 자동 삽입 + // 하이픈 자동 삽입 var formatted = "" let length = digitsOnly.count @@ -270,12 +267,12 @@ struct RegisterView: View { formatted = "\(first)-\(middle)-\(last)" } - // 3️⃣ 10자리 이상은 자르기 + // 10자리 이상은 자르기 if digitsOnly.count > 10 { formatted = String(formatted.prefix(12)) // 하이픈 포함 } - // 4️⃣ 상태 업데이트 + // 상태 업데이트 if formatted != bizNo { bizNo = formatted } diff --git a/StockMate/StockMate/app/feature/auth/viewmodel/AuthViewModel.swift b/StockMate/StockMate/app/feature/auth/viewmodel/AuthViewModel.swift index 045de49..72eea38 100644 --- a/StockMate/StockMate/app/feature/auth/viewmodel/AuthViewModel.swift +++ b/StockMate/StockMate/app/feature/auth/viewmodel/AuthViewModel.swift @@ -7,12 +7,14 @@ import SwiftUI +// 인증 상태를 나타내는 enum enum AuthState { case unauthenticated case registering case authenticated } +// 인증 관련 로직(ViewModel) @MainActor final class AuthViewModel: ObservableObject { @Published var email = "" @@ -20,8 +22,10 @@ final class AuthViewModel: ObservableObject { @Published var message: String = "" @Published var authState: AuthState = .unauthenticated + // MARK: - Dependencies private let repo: AuthRepositoryProtocol + // MARK: - Init init(repo: AuthRepositoryProtocol = AuthRepositoryImpl()) { self.repo = repo } @@ -57,11 +61,13 @@ final class AuthViewModel: ObservableObject { } } + // MARK: - 로그아웃 func logout() { TokenStore.shared.clear() authState = .unauthenticated } + // MARK: - 화면 상태 전환 func goToLogin() { authState = .unauthenticated } @@ -79,19 +85,7 @@ final class AuthViewModel: ObservableObject { storeName: String, bizNo: String ) async -> Bool { - - print( - """ - [회원가입 시도] - email: \(email) - password: \(password) - owner: \(owner) - address: \(address) - storeName: \(storeName) - businessNumber: \(bizNo) - """ - ) - + let req = RegisterRequest( email: email, password: password, diff --git a/StockMate/StockMate/app/feature/cart/data/CartApi.swift b/StockMate/StockMate/app/feature/cart/data/CartApi.swift index 9a717b4..23281f4 100644 --- a/StockMate/StockMate/app/feature/cart/data/CartApi.swift +++ b/StockMate/StockMate/app/feature/cart/data/CartApi.swift @@ -34,7 +34,6 @@ struct CartItem: Decodable, Identifiable { let stock: Int? let image: String? -// var id: Int { cartItemId } var id: Int { partId } } diff --git a/StockMate/StockMate/app/feature/cart/data/CartRepositoryImpl.swift b/StockMate/StockMate/app/feature/cart/data/CartRepositoryImpl.swift index 479f1d2..e72b3b3 100644 --- a/StockMate/StockMate/app/feature/cart/data/CartRepositoryImpl.swift +++ b/StockMate/StockMate/app/feature/cart/data/CartRepositoryImpl.swift @@ -9,13 +9,16 @@ import Foundation import Alamofire +// 장바구니 관련 API 통신을 수행하는 Repository 구현체 final class CartRepositoryImpl: CartRepositoryProtocol { + // MARK: - 장바구니 조회 func fetchCart() async -> AppResult> { let req = CartApi.fetchCart() return await safeApi(req, decodeTo: ApiResponse.self) } + // MARK: - 장바구니 추가 func addToCart( request: CartUpdateRequest ) async -> AppResult> { @@ -23,6 +26,7 @@ final class CartRepositoryImpl: CartRepositoryProtocol { return await safeApi(req, decodeTo: ApiResponse.self) } + // MARK: - 장바구니 수정 func updateCart( request: CartUpdateRequest ) async -> AppResult> { @@ -30,9 +34,9 @@ final class CartRepositoryImpl: CartRepositoryProtocol { return await safeApi(req, decodeTo: ApiResponse.self) } + // MARK: - 장바구니 비우기 func clearCart() async -> AppResult> { let req = CartApi.clearCart() return await safeApi(req, decodeTo: ApiResponse.self) } - } diff --git a/StockMate/StockMate/app/feature/cart/ui/DeliveryStatusView.swift b/StockMate/StockMate/app/feature/cart/ui/DeliveryStatusView.swift index 830476e..3f21bfd 100644 --- a/StockMate/StockMate/app/feature/cart/ui/DeliveryStatusView.swift +++ b/StockMate/StockMate/app/feature/cart/ui/DeliveryStatusView.swift @@ -38,11 +38,6 @@ struct DeliveryStatusView: View { ZStack { RoundedRectangle(cornerRadius: 8) .fill(isCompleted ? Color.Primary : Color.gray.opacity(0.2)) -// .strokeBorder(isCompleted ? .clear : Color.gray.opacity(0.4), lineWidth: 1.5) -// .background( -// RoundedRectangle(cornerRadius: 8) -// .fill(isCompleted ? Color.Primary : Color.white) -// ) .frame(width: 40, height: 40) Image(step.iconName) @@ -70,8 +65,6 @@ struct DeliveryStatusView: View { .foregroundColor(.gray.opacity(0.2)) .frame(width: 32, height: 3) .offset(x: 18) -// .frame(width: 42, height: 4) -// .offset(x: 21) } else if index < currentStep { Rectangle() @@ -86,8 +79,6 @@ struct DeliveryStatusView: View { .foregroundColor(Color.Primary) .frame(width: 32, height: 3) .offset(x: 18) -// .frame(width: 38, height: 3) -// .offset(x: 21) } else { Image("dline") .renderingMode(.template) @@ -96,8 +87,6 @@ struct DeliveryStatusView: View { .foregroundColor(.gray.opacity(0.2)) .frame(width: 32, height: 3) .offset(x: 18) -// .frame(width: 42, height: 4) -// .offset(x: 21) } } .offset(y: -10) // 선이 정확히 중앙에 오도록 조정 @@ -108,11 +97,6 @@ struct DeliveryStatusView: View { } } .frame(height: 80) -// .padding(.horizontal, 20) // ✅ 전체 섹션 동일 여백 -// .padding(.vertical, 16) -// .background(Color.white) -// .clipShape(RoundedRectangle(cornerRadius: 16)) // 박스 모서리 잘림 방지 -// .shadow(color: .black.opacity(0.05), radius: 3, y: 2) } } @@ -120,28 +104,3 @@ struct DeliveryStatusView: View { #Preview { DeliveryStatusView(currentStep: deliveryStep(for: "APPROVAL_ORDER")) } - -// // 6 -> 전체 회색 -//// 4 -> 전체 파란색 -//switch status { -//case "ORDER_COMPLETED": return 0 // 주문 완료 -// -//// 결제 후 결과에 따라 결제 실패 or 완료 -//case "FAILED": return 6 // 결제 실패 -//case "PAY_COMPLETED": return 0 // 결제 완료 -// -//// 결제 완료 상태에서 지점이 주문 취소 -//case "CANCELLED": return 6 // 주문 취소 -// -//// 본사에서 "결제 완료"에 대해서 주문을 반려 or 승인 -//case "REJECTED": return 6 // 주문 반려 -//case "APPROVAL_ORDER": return 1 // 주문 승인 -// -//// 창고관리자가 "주문 승인"에 대해서 송장(인보이스)를 뽑으면 출고 대기 -//case "PENDING_SHIPPING": return 2 // 출고 대기 -// -//// 창고관리자가 QR을 스캔하여 출고처리 하면 배송중 -//case "SHIPPING": return 3 // 배송중 -// -//// 지점에서 QR을 스캔하여 입고 완료 처리 -//case "RECEIVED": return 4 // 입고 완료 diff --git a/StockMate/StockMate/app/feature/cart/viewmodel/CartViewModel.swift b/StockMate/StockMate/app/feature/cart/viewmodel/CartViewModel.swift index 28bf1fd..06e27ed 100644 --- a/StockMate/StockMate/app/feature/cart/viewmodel/CartViewModel.swift +++ b/StockMate/StockMate/app/feature/cart/viewmodel/CartViewModel.swift @@ -50,7 +50,7 @@ final class CartViewModel: ObservableObject { // MARK: - Update Quantity (전체 덮어쓰기) func updateCart() async { - // ✅ items가 비면 clearCart 호출하고 return + // items가 비면 clearCart 호출하고 return if items.isEmpty { await clearCart() return @@ -158,7 +158,7 @@ final class CartViewModel: ObservableObject { private func syncCart() async { if items.isEmpty { - // ✅ 장바구니가 빈 경우는 clearCart 호출 + // 장바구니가 빈 경우는 clearCart 호출 await clearCart() return } diff --git a/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift b/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift index 97f41d9..e00051b 100644 --- a/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift +++ b/StockMate/StockMate/app/feature/dashboard/data/HistoryApi.swift @@ -8,7 +8,8 @@ import Foundation import Alamofire -// MARK: - 입출고 히스토리 데이터 구조 +// === Response === +// 입출고 히스토리 페이지 데이터 struct HistoryPageData: Decodable { let totalElements: Int let totalPages: Int @@ -18,6 +19,7 @@ struct HistoryPageData: Decodable { let last: Bool } +// 입출고 히스토리 단일 항목 struct HistoryItem: Decodable, Identifiable { let id: Int let memberId: Int @@ -32,6 +34,7 @@ struct HistoryItem: Decodable, Identifiable { let items: [HistoryPart] } +// 입출고 히스토리에 포함된 사용자 정보 struct HistoryUserInfo: Decodable { let id: Int let memberId: Int @@ -46,6 +49,7 @@ struct HistoryUserInfo: Decodable { let longitude: Double } +// 입출고 히스토리에 포함된 부품 정보 struct HistoryPart: Decodable, Identifiable { let id: Int let name: String @@ -65,7 +69,8 @@ struct HistoryPart: Decodable, Identifiable { } -// MARK: - 예치금 거래내역 데이터 구조 +// === 예치금 거래내역 === +// 예치금 거래내역 페이지 데이터 struct PaymentTransactionPageData: Decodable { let content: [PaymentTransactionItem] let page: Int @@ -78,6 +83,7 @@ struct PaymentTransactionPageData: Decodable { let first: Bool } +// 예치금 거래내역 단일 항목 struct PaymentTransactionItem: Decodable { let transactionId: Int let transactionType: String // "CHARGE" or "PAY" @@ -88,7 +94,7 @@ struct PaymentTransactionItem: Decodable { let balance: Int } - +// 거래내역에 포함된 주문 항목 struct OrderItemHistory: Decodable { let id: Int let name: String @@ -98,15 +104,16 @@ struct OrderItemHistory: Decodable { } -// MARK: - API +// === API === +// 입출고 및 거래내역 관련 API 모음 enum HistoryApi { - // ✅ 가맹점별 입출고 히스토리 조회 + // GET - 가맹점별 입출고 히스토리 조회 static func getInOutHistory(page: Int = 0, size: Int = 20) -> DataRequest { let url = ApiClient.baseURL + "api/v1/information/order-history/my?page=\(page)&size=\(size)" return ApiClient.shared.request(url, method: .get) } - // ✅ 예치금 거래내역 조회 + // GET - 예치금 거래내역 조회 static func getPaymentTransaction(page: Int = 0, size: Int = 20) -> DataRequest { let url = ApiClient.baseURL + "api/v1/payment/transaction?page=\(page)&size=\(size)" return ApiClient.shared.request(url, method: .get) diff --git a/StockMate/StockMate/app/feature/dashboard/data/HistoryRepositoryImpl.swift b/StockMate/StockMate/app/feature/dashboard/data/HistoryRepositoryImpl.swift index 1c8dfe4..ad9e84a 100644 --- a/StockMate/StockMate/app/feature/dashboard/data/HistoryRepositoryImpl.swift +++ b/StockMate/StockMate/app/feature/dashboard/data/HistoryRepositoryImpl.swift @@ -8,13 +8,16 @@ import Foundation import Alamofire +// === Repository Implementation === +// 입출고 및 예치금 거래내역 데이터 요청을 처리하는 구현체 final class HistoryRepositoryImpl: HistoryRepositoryProtocol { + // GET - 입출고 히스토리 조회 func getInOutHistory(page: Int, size: Int) async -> AppResult> { let request = HistoryApi.getInOutHistory(page: page, size: size) return await safeApi(request, decodeTo: ApiResponse.self) } - // ✅ 예치금 거래내역 조회 + // GET - 예치금 거래내역 조회 func getPaymentTransaction(page: Int, size: Int) async -> AppResult> { let request = HistoryApi.getPaymentTransaction(page: page, size: size) return await safeApi(request, decodeTo: ApiResponse.self) diff --git a/StockMate/StockMate/app/feature/dashboard/domain/HistoryRepositoryProtocol.swift b/StockMate/StockMate/app/feature/dashboard/domain/HistoryRepositoryProtocol.swift index fddd5cc..5c534fb 100644 --- a/StockMate/StockMate/app/feature/dashboard/domain/HistoryRepositoryProtocol.swift +++ b/StockMate/StockMate/app/feature/dashboard/domain/HistoryRepositoryProtocol.swift @@ -8,9 +8,13 @@ import Foundation import Alamofire +// === Repository Protocol === +// 입출고 및 예치금 거래내역 관련 데이터 요청 정의 protocol HistoryRepositoryProtocol { + + // GET - 입출고 히스토리 조회 func getInOutHistory(page: Int, size: Int) async -> AppResult> - // ✅ 예치금 거래내역 조회 + // GET - 예치금 거래내역 조회 func getPaymentTransaction(page: Int, size: Int) async -> AppResult> } diff --git a/StockMate/StockMate/app/feature/dashboard/ui/InOutHistoryView.swift b/StockMate/StockMate/app/feature/dashboard/ui/InOutHistoryView.swift index 425cdf2..2f6dc2a 100644 --- a/StockMate/StockMate/app/feature/dashboard/ui/InOutHistoryView.swift +++ b/StockMate/StockMate/app/feature/dashboard/ui/InOutHistoryView.swift @@ -26,7 +26,7 @@ struct InOutHistoryView: View { .foregroundColor(.gray) .frame(maxWidth: .infinity, maxHeight: .infinity) } else { - // ✅ 날짜별로 그룹화 (최신순) + // 날짜별로 그룹화 (최신순) let groupedHistories = Dictionary(grouping: viewModel.histories) { history in history.createdAt.split(separator: "T").first.map(String.init) ?? "" } @@ -36,13 +36,13 @@ struct InOutHistoryView: View { LazyVStack(alignment: .leading, spacing: 20) { ForEach(groupedHistories, id: \.key) { date, histories in VStack(alignment: .leading, spacing: 12) { - // ✅ 날짜 헤더 + // 날짜 헤더 Text(formatDate(String(date))) .font(.headline) .padding(.leading, 25) .padding(.top, 8) - // ✅ 해당 날짜의 히스토리 카드들 + // 해당 날짜의 히스토리 카드들 ForEach(histories) { history in InOutHistoryCard(history: history) } diff --git a/StockMate/StockMate/app/feature/dashboard/ui/ReleaseDetailView.swift b/StockMate/StockMate/app/feature/dashboard/ui/ReleaseDetailView.swift index 0b4937d..edd2ede 100644 --- a/StockMate/StockMate/app/feature/dashboard/ui/ReleaseDetailView.swift +++ b/StockMate/StockMate/app/feature/dashboard/ui/ReleaseDetailView.swift @@ -14,13 +14,13 @@ struct ReleaseDetailView: View { var body: some View { ScrollView { VStack(alignment: .leading, spacing: 20) { - // ✅ 부품 리스트 + // 부품 리스트 VStack(alignment: .leading, spacing: 7) { Text("출고 품목 (\(history.items.count)개)") .font(.system(size: 17, weight: .semibold)) .padding(.leading) - // ✅ 처리일자 포맷팅 + // 처리일자 포맷팅 Text("처리일자: \(formattedDate3(history.createdAt))") .font(.system(size: 15)) .foregroundColor(Color.textGray1) @@ -133,7 +133,7 @@ func formattedDate3(_ timestamp: String) -> String { let trimmed = timestamp.trimmingCharacters(in: .whitespacesAndNewlines) let parser = DateFormatter() parser.locale = Locale(identifier: "en_US_POSIX") - parser.timeZone = TimeZone(identifier: "Asia/Seoul") // ✅ 서버 시간 기준으로 맞춤 + parser.timeZone = TimeZone(identifier: "Asia/Seoul") // 서버 시간 기준으로 맞춤 var date: Date? = nil for format in inputFormats { diff --git a/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift b/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift index 76250a9..dac5ebd 100644 --- a/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift +++ b/StockMate/StockMate/app/feature/dashboard/ui/TransactionTypeListView.swift @@ -67,8 +67,13 @@ struct TransactionCard: View { HStack(alignment: .center, spacing: 12) { // PAY일 때만 부품 이미지 표시 - if item.transactionType == "PAY" { - // 대표 이미지 + if item.transactionType == "CHARGE" { + Image("exchange") + .foregroundColor(Color.Primary) + .frame(width: 64, height: 64) + .cornerRadius(10) + }else { + // 부품 대표 이미지 AsyncImage(url: URL(string: item.orderItems?.first?.image ?? "")) { image in image.resizable().scaledToFit() } placeholder: { @@ -76,13 +81,7 @@ struct TransactionCard: View { } .frame(width: 64, height: 64) .cornerRadius(10) - } else { - Image("exchange") - .foregroundColor(Color.Primary) - .frame(width: 64, height: 64) - .cornerRadius(10) } - VStack(alignment: .leading, spacing: 5) { @@ -102,7 +101,24 @@ struct TransactionCard: View { Text("예치금 충전") .font(.system(size: 14, weight: .bold)) .foregroundColor(.black) + } else { + HStack { + Text("주문 취소:") + .font(.system(size: 14, weight: .bold)) + .foregroundColor(.black) + if let orderItems = item.orderItems, !orderItems.isEmpty { + let firstName = orderItems.first?.korName ?? "-" + let extraCount = orderItems.count - 1 + let displayText = extraCount > 0 + ? "\(firstName) 외 \(extraCount)개" + : firstName + Text(displayText) + .font(.system(size: 14, weight: .bold)) + .foregroundColor(.black) + } + } } + // 날짜 Text( item.transactionTime != nil ? formattedDate( @@ -112,7 +128,7 @@ struct TransactionCard: View { .font(.system(size: 13, weight: .regular)) .foregroundColor(.textGray1) - // ✅ 금액 표시 (PAY/CHARGE 구분) + // 금액 표시 (PAY/CHARGE 구분) let isPay = item.transactionType == "PAY" let sign = isPay ? "-" : "+" let color: Color = isPay ? .Danger : .Primary @@ -124,7 +140,7 @@ struct TransactionCard: View { } .frame(height: 60, alignment: .top) Spacer() - // ✅ PAY일 때만 꺾새 표시 + // PAY일 때만 상세 페이지 연결 if item.transactionType == "PAY" { NavigationLink(destination: ReceiptView(orderId: item.orderId ?? 1)) { Image(systemName: "chevron.right") diff --git a/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift b/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift index 0eb95d7..82fa036 100644 --- a/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift +++ b/StockMate/StockMate/app/feature/dashboard/viewmodel/HistoryViewModel.swift @@ -8,34 +8,40 @@ import Foundation import Alamofire + +// === ViewModel === +// 입출고 및 예치금 거래내역 화면에서 사용할 데이터 상태 관리 @MainActor final class HistoryViewModel: ObservableObject { - // MARK: - 입출고 히스토리 관련 + + // MARK: - 입출고 히스토리 관련 상태 @Published var histories: [HistoryItem] = [] @Published var isLoading = false @Published var errorMessage: String? @Published var currentPage = 0 @Published var totalPages = 1 - // MARK: - 예치금 거래내역 관련 + // MARK: - 예치금 거래내역 관련 상태 @Published var transactions: [PaymentTransactionItem] = [] @Published var transactionPage = 0 @Published var transactionTotalPages = 1 @Published var isTransactionLoading = false + // Repository 의존성 주입 private let repository: HistoryRepositoryProtocol init(repository: HistoryRepositoryProtocol = HistoryRepositoryImpl()) { self.repository = repository } - /// ✅ 입출고 히스토리 불러오기 + // === 입출고 히스토리 === + // 입출고 히스토리 조회 func fetchInOutHistory(page: Int = 0, size: Int = 20) async { guard !isLoading else { return } isLoading = true defer { isLoading = false } - let result = await repository.getInOutHistory(page: page, size: size) // ✅ 오타도 수정됨 (getInout → getInOut) + let result = await repository.getInOutHistory(page: page, size: size) switch result { case .success(let response): if let data = response.data { @@ -56,7 +62,7 @@ final class HistoryViewModel: ObservableObject { } } - /// ✅ 다음 페이지 로드 (무한 스크롤 등) + // 무한 스크롤 시 다음 페이지 로드 func loadMoreIfNeeded(currentItem item: HistoryItem?) async { guard let item = item else { return } let threshold = max(histories.count - 5, 0) @@ -67,7 +73,8 @@ final class HistoryViewModel: ObservableObject { } } - // MARK: - ✅ 예치금 거래내역 불러오기 + // === 예치금 거래내역 === + // 예치금 거래내역 조회 func fetchPaymentTransactions(page: Int = 0, size: Int = 20) async { guard !isTransactionLoading else { return } isTransactionLoading = true @@ -94,13 +101,13 @@ final class HistoryViewModel: ObservableObject { } } - // MARK: - ✅ 무한 스크롤 (예치금 내역) + // 무한 스크롤 시 다음 페이지 로드 (예치금 내역) func loadMoreTransactionsIfNeeded(currentItem item: PaymentTransactionItem?) async { guard let item = item else { return } guard !isTransactionLoading else { return } // 중복 로드 방지 guard transactionPage + 1 < transactionTotalPages else { return } // 마지막 페이지 방지 - // ✅ 안전한 threshold 계산 + // 안전한 threshold 계산 let thresholdIndex = max(transactions.count - 5, 0) if let currentIndex = transactions.firstIndex(where: { $0.transactionId == item.transactionId }), currentIndex >= thresholdIndex { diff --git a/StockMate/StockMate/app/feature/inventory/data/InventoryApi.swift b/StockMate/StockMate/app/feature/inventory/data/InventoryApi.swift index a4ef6e9..058b8f1 100644 --- a/StockMate/StockMate/app/feature/inventory/data/InventoryApi.swift +++ b/StockMate/StockMate/app/feature/inventory/data/InventoryApi.swift @@ -5,10 +5,11 @@ // Created by Admin on 10/15/25. // - import Foundation import Alamofire + +// MARK: - 재고 조회 응답 구조 struct InventoryResponse: Decodable { let status: Int let success: Bool @@ -16,6 +17,7 @@ struct InventoryResponse: Decodable { let data: InventoryPageData? } +// MARK: - 페이징 데이터 struct InventoryPageData: Decodable { let content: [InventoryItem] let page: Int @@ -24,6 +26,7 @@ struct InventoryPageData: Decodable { let totalPages: Int } +// MARK: - 개별 재고 아이템 struct InventoryItem: Decodable, Identifiable { let id: Int let name: String @@ -41,6 +44,7 @@ struct InventoryItem: Decodable, Identifiable { let isLack: Bool } +// MARK: - 카테고리별 부족 재고 개수 struct LackCountItem: Decodable, Identifiable { var id: String { categoryName } // SwiftUI ForEach에서 식별자 사용 let categoryName: String @@ -48,6 +52,7 @@ struct LackCountItem: Decodable, Identifiable { } +// MARK: 재고 목록 조회 enum InventoryApi { static func getInventoryList( page: Int, @@ -71,7 +76,7 @@ enum InventoryApi { return ApiClient.shared.request(url, method: .get) } - // ✅ 부족 재고 조회 API 추가 + // MARK: 부족 재고 목록 조회 static func getUnderLimitList(categoryName: String? = nil, page: Int = 0, size: Int = 10) -> DataRequest { var url = ApiClient.baseURL + "api/v1/store/under-limit?page=\(page)&size=\(size)" if let categoryName = categoryName, !categoryName.isEmpty { @@ -80,14 +85,14 @@ enum InventoryApi { return ApiClient.shared.request(url, method: .get) } - // ✅ 부품 이름으로 검색 + // MARK: 부품명 검색 static func findByName(name: String, page: Int, size: Int) -> DataRequest { let encodedName = name.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "" let url = ApiClient.baseURL + "api/v1/store/find-name?name=\(encodedName)&page=\(page)&size=\(size)" return ApiClient.shared.request(url, method: .get) } - // ✅ 카테고리별 부족 재고 개수 조회 + // MARK: 카테고리별 부족 재고 개수 조회 static func getLackCountByCategory() -> DataRequest { let url = ApiClient.baseURL + "api/v1/store/lack-count" return ApiClient.shared.request(url, method: .get) diff --git a/StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift b/StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift index 93a8885..8bc5dac 100644 --- a/StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift +++ b/StockMate/StockMate/app/feature/inventory/data/InventoryRepositoryImpl.swift @@ -8,7 +8,10 @@ import Foundation import Alamofire +// MARK: - 재고 관련 Repository 구현체 final class InventoryRepositoryImpl: InventoryRepositoryProtocol { + + // MARK: 재고 리스트 조회 func getInventoryList( page: Int, size: Int, @@ -25,7 +28,8 @@ final class InventoryRepositoryImpl: InventoryRepositoryProtocol { ) return await safeApi(dataReq, decodeTo: ApiResponse.self) } - // 부족 재고 리스트 호출 + + // MARK: 부족 재고 목록 조회 func getUnderLimitList( categoryName: String?, page: Int, @@ -35,7 +39,7 @@ final class InventoryRepositoryImpl: InventoryRepositoryProtocol { return await safeApi(dataReq, decodeTo: ApiResponse.self) } - // ✅ 이름 검색 + // MARK: 부품명 검색 func findByName( name: String, page: Int, @@ -45,10 +49,9 @@ final class InventoryRepositoryImpl: InventoryRepositoryProtocol { return await safeApi(dataReq, decodeTo: ApiResponse.self) } - // 카테고리별 부족 재고 개수 조회 + // MARK: 카테고리별 부족 재고 개수 조회 func getLackCountByCategory() async -> AppResult> { let dataReq = InventoryApi.getLackCountByCategory() return await safeApi(dataReq, decodeTo: ApiResponse<[LackCountItem]>.self) } - } diff --git a/StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift b/StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift index fe2a090..19873d5 100644 --- a/StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift +++ b/StockMate/StockMate/app/feature/inventory/domain/InventoryRepositoryProtocol.swift @@ -8,7 +8,9 @@ import Foundation import Alamofire +// MARK: - 재고 관련 Repository 프로토콜 protocol InventoryRepositoryProtocol { + // MARK: 재고 리스트 조회 func getInventoryList( page: Int, size: Int, @@ -17,19 +19,21 @@ protocol InventoryRepositoryProtocol { models: [String] ) async -> AppResult> + // MARK: 부족 재고 목록 조회 func getUnderLimitList( categoryName: String?, page: Int, size: Int ) async -> AppResult> - // 이름 검색 + // MARK: 부품명 검색 func findByName( name: String, page: Int, size: Int ) async -> AppResult> + // MARK: 카테고리별 부족 재고 개수 조회 func getLackCountByCategory() async -> AppResult> } diff --git a/StockMate/StockMate/app/feature/inventory/ui/IncomingScanView.swift b/StockMate/StockMate/app/feature/inventory/ui/IncomingScanView.swift index 4dfef89..7f23226 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/IncomingScanView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/IncomingScanView.swift @@ -13,15 +13,15 @@ struct IncomingScanView: View { @State private var showAlert = false @State private var alertMessage = "" - @StateObject private var orderViewModel = OrderViewModel() // ✅ 뷰모델 추가 + @StateObject private var orderViewModel = OrderViewModel() // 뷰모델 추가 var body: some View { ZStack { - // ✅ 1. 카메라 화면 (QR 스캐너) + // 1. 카메라 화면 (QR 스캐너) QRScannerView(scannedCode: $scannedCode) .ignoresSafeArea() - // ✅ 2. 스캔 영역 가이드 박스 + // 2. 스캔 영역 가이드 박스 VStack { Text("입고 부품의 QR을 스캔해주세요") .font(.headline) @@ -45,7 +45,7 @@ struct IncomingScanView: View { Spacer() } - // ✅ 로딩 표시 + // 로딩 표시 if orderViewModel.isLoading { Color.black.opacity(0.3).ignoresSafeArea() ProgressView("입고 처리 중...") diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift index 85fc573..1fd2b69 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/InventorySearchView.swift @@ -37,7 +37,7 @@ struct InventorySearchView: View { var body: some View { NavigationStack { VStack(spacing: 0) { - // 🔍 검색창 + // 검색창 HStack { Image(systemName: "magnifyingglass") .foregroundColor(.gray) @@ -119,7 +119,7 @@ struct InventorySearchView: View { .padding(.horizontal) .padding(.bottom, 16) - // 📋 재고 리스트 + // 재고 리스트 ScrollView { LazyVStack(spacing: 10) { @@ -227,7 +227,6 @@ struct FilterMenu: View { } .padding(.horizontal, 12) .padding(.vertical, 9) - // ✅ 텍스트 길이에 맞게 자동 확장 .fixedSize(horizontal: true, vertical: false) .background(isActive ? Color(hex: "DBEAFE") : Color.Light) .overlay( diff --git a/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift b/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift index d797713..c6a598c 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/InventoryView.swift @@ -26,8 +26,6 @@ struct InventoryView: View { GeometryReader { geo in Color.clear .onChange(of: geo.frame(in: .global).minY) { newValue in - // 👇 스크롤 시 값이 변함 - //print("📏 Scroll offsetY:", newValue) // 테스트용, 화면 안정화 후 제거 withAnimation(.easeInOut(duration: 0.25)) { showScrollToTopButton = newValue < -150 } diff --git a/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift b/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift index ef8692d..38a894e 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/OutgoingScanView.swift @@ -16,7 +16,7 @@ struct OutgoingScanView: View { // 전역 부품 저장소 @EnvironmentObject var partStore: PartStore - @StateObject private var partViewModel = PartViewModel() // ✅ ViewModel 추가 + @StateObject private var partViewModel = PartViewModel() @State private var showBottomSheet = false @@ -27,12 +27,12 @@ struct OutgoingScanView: View { var body: some View { ZStack { - // ✅ 카메라 미리보기 (QR 스캐너) + // 카메라 미리보기 (QR 스캐너) QRScannerView(scannedCode: $scannedCode, isActive: !showBottomSheet) .ignoresSafeArea() - // ✅ 스캔 가이드 및 UI 오버레이 + // 스캔 가이드 및 UI 오버레이 VStack { Text("사용할 부품의 QR을 스캔해주세요") .font(.headline) @@ -42,7 +42,7 @@ struct OutgoingScanView: View { Spacer() - // 📷 스캔 박스 + // 스캔 박스 ZStack { RoundedRectangle(cornerRadius: 12) .fill(Color.clear) @@ -56,24 +56,9 @@ struct OutgoingScanView: View { Spacer() - // 📦 직접 입력 버튼 -// Button(action: { -// dismiss() -// }) { -// Text("직접 입력 하기") -// .fontWeight(.semibold) -// .foregroundColor(.black) -// .frame(maxWidth: .infinity) -// .padding() -// .background(Color.white) -// .cornerRadius(10) -// .shadow(color: .gray.opacity(0.3), radius: 2, x: 0, y: 2) -// } -// .padding(.horizontal, 40) -// .padding(.bottom, 40) } - // ✅ 로딩 인디케이터 + // 로딩 인디케이터 if partViewModel.isLoading { Color.black.opacity(0.3).ignoresSafeArea() ProgressView("부품 조회 중...") //ProgressView("부품 사용 처리 중...") @@ -95,12 +80,12 @@ struct OutgoingScanView: View { UsedPartListSheetView( onUseParts: { Task { - // ✅ payload 생성 + // payload 생성 let payload = partStore.parts.map { ReleaseItemRequest(partId: $0.id, quantity: $0.quantity) } - // ✅ API 호출 + // API 호출 let result = await partViewModel.releaseParts(items: payload) await MainActor.run { @@ -109,7 +94,7 @@ struct OutgoingScanView: View { alertMessage = message showAlert = true - // ✅ 성공 시: 전역 부품 초기화 + 바텀시트 닫기 + 화면 복귀 + // 성공 시: 전역 부품 초기화 + 바텀시트 닫기 + 화면 복귀 partStore.clear() showBottomSheet = false DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { @@ -124,13 +109,13 @@ struct OutgoingScanView: View { } }, onRescan: { - // ✅ 다시 스캔 버튼 눌렀을 때 + // 다시 스캔 버튼 눌렀을 때 showBottomSheet = false resetScanState() // QR 다시 활성화 } ) .presentationDetents([.fraction(0.80)]) // 시트 높이 80% - .presentationCornerRadius(28) // ✅ 모서리 곡률 + .presentationCornerRadius(28) .environmentObject(partStore) } .navigationTitle("부품 사용 처리") @@ -151,7 +136,7 @@ struct OutgoingScanView: View { } } - // ✅ 스캔된 코드로 부품 상세 조회만 수행 (출고 X) + // 스캔된 코드로 부품 상세 조회만 수행 (출고 X) private func handleScannedCode(_ code: String) async { await MainActor.run { partViewModel.isLoading = true } @@ -165,10 +150,10 @@ struct OutgoingScanView: View { return } - // ✅ 부품 상세 조회 API 호출 + // 부품 상세 조회 API 호출 await partViewModel.fetchPartDetail(partIds: partId) - // ✅ 결과 출력 + // 결과 출력 await MainActor.run { partViewModel.isLoading = false @@ -178,7 +163,7 @@ struct OutgoingScanView: View { return } - // ✅ 응답을 PartDetail로 변환 후 저장 + // 응답을 PartDetail로 변환 후 저장 let newPart = PartDetail( id: response.id, price: response.price, @@ -192,17 +177,17 @@ struct OutgoingScanView: View { partStore.addPart(newPart) - // ✅ 자동으로 바텀시트 열기 + // 자동으로 바텀시트 열기 showBottomSheet = true - // ✅ 스캔 상태 초기화 (다시 스캔 가능하도록) + // 스캔 상태 초기화 (다시 스캔 가능하도록) resetScanState() - print("✅ \(newPart.korName) 부품이 전역 Store에 추가됨") + print("\(newPart.korName) 부품이 전역 Store에 추가됨") } } - // ✅ 상태 초기화 (QR 다시 활성화) + // 상태 초기화 (QR 다시 활성화) private func resetScanState() { scannedCode = nil scannerRestartTrigger.toggle() diff --git a/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift b/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift index bd2d532..8e5b5cc 100644 --- a/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift +++ b/StockMate/StockMate/app/feature/inventory/ui/UsedPartListSheetView.swift @@ -9,12 +9,12 @@ import SwiftUI struct UsedPartListSheetView: View { @EnvironmentObject var partStore: PartStore - var onUseParts: (() -> Void)? // ‘사용 처리’ 버튼 액션 콜백 - var onRescan: (() -> Void)? // ✅ 다시 스캔 콜백 추가 + var onUseParts: (() -> Void)? // ‘사용 처리’ 버튼 액션 콜백 + var onRescan: (() -> Void)? // 다시 스캔 콜백 추가 var body: some View { VStack (alignment: .center){ - // ✅ 상단 헤더 + // 상단 헤더 ZStack { Text("사용할 부품") .font(.system(size: 18, weight: .bold)) @@ -32,7 +32,7 @@ struct UsedPartListSheetView: View { .background(Color.white) - // ✅ 내용 영역 + // 내용 영역 ScrollView { if partStore.parts.isEmpty { // 부품 목록 전체 삭제의 경우 @@ -48,7 +48,7 @@ struct UsedPartListSheetView: View { .frame(maxWidth: .infinity, minHeight: 200) } else { LazyVStack { - ForEach($partStore.parts) { $part in // ✅ 바인딩으로 변경 ($ 붙임) + ForEach($partStore.parts) { $part in // 바인딩으로 변경 ($ 붙임) VStack(alignment: .leading, spacing: 6) { Text(part.categoryName) .font(.system(size: 12, weight: .semibold)) @@ -87,7 +87,7 @@ struct UsedPartListSheetView: View { Spacer() - // ✅ 수량 조절 버튼 (디자인 개선) + // 수량 조절 버튼 (디자인 개선) HStack(spacing: 10) { Button { partStore.decreaseQuantityOrRemove(for: part) @@ -113,7 +113,7 @@ struct UsedPartListSheetView: View { .padding(.horizontal, 8) .background(Color.white) .cornerRadius(10) - .overlay( // ✅ 테두리 추가 + .overlay( RoundedRectangle(cornerRadius: 10) .stroke( Color.LightBlue03, lineWidth: 1) ) @@ -138,7 +138,7 @@ struct UsedPartListSheetView: View { .frame(maxHeight: .infinity) HStack{ - // 🔹 다시 스캔 버튼 + // 다시 스캔 버튼 Button { onRescan?() } label: { @@ -156,7 +156,7 @@ struct UsedPartListSheetView: View { } .padding(.bottom, 16) - // ✅ 사용 처리 버튼 + // 사용 처리 버튼 Button { onUseParts?() } label: { @@ -174,12 +174,11 @@ struct UsedPartListSheetView: View { .padding(.bottom, 16) } .padding(.horizontal) - } } } -// MARK: - 프리뷰 (안전 버전) +// MARK: - 프리뷰 @MainActor struct UsedPartListSheetView_Previews: PreviewProvider { static var previewStore: PartStore = { @@ -212,8 +211,8 @@ struct UsedPartListSheetView_Previews: PreviewProvider { static var previews: some View { NavigationStack { UsedPartListSheetView( - onUseParts: { print("✅ 사용 처리 버튼 눌림") }, - onRescan: { print("🔄 다시 스캔 버튼 눌림") } + onUseParts: { print("사용 처리 버튼 눌림") }, + onRescan: { print("다시 스캔 버튼 눌림") } ) .environmentObject(previewStore) } diff --git a/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift b/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift index a9e1329..1b466b5 100644 --- a/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift +++ b/StockMate/StockMate/app/feature/inventory/viewmodel/InventoryViewModel.swift @@ -97,7 +97,6 @@ final class InventoryViewModel: ObservableObject { // MARK: - 부족 재고 로드 func loadUnderLimitList(reset: Bool = false, size: Int = 10) async { -// guard !isLoading, underLimitHasMore else { return } guard !isLoading, (underLimitHasMore || reset) else { return } isLoading = true @@ -195,15 +194,12 @@ final class InventoryViewModel: ObservableObject { } else { selectedCategories.append(name) } - // ✅ 검색 중일 때는 로컬 필터링만 다시 계산 + // 검색 중일 때는 로컬 필터링만 다시 계산 if isSearching { objectWillChange.send() } else { Task { await resetAndLoad() } } - -// isSearching = false -// Task { await resetAndLoad() } } func toggleTrim(_ trim: String) { @@ -217,8 +213,6 @@ final class InventoryViewModel: ObservableObject { } else { Task { await resetAndLoad() } } -// isSearching = false -// Task { await resetAndLoad() } } func toggleModel(_ model: String) { @@ -232,8 +226,6 @@ final class InventoryViewModel: ObservableObject { } else { Task { await resetAndLoad() } } -// isSearching = false -// Task { await resetAndLoad() } } // MARK: - 필터 초기화 @@ -253,9 +245,7 @@ final class InventoryViewModel: ObservableObject { // 3. 검색어가 있는 경우 → 해당 검색어로 전체 결과 다시 검색 else { isSearching = true - //searchPage = 0 searchResults.removeAll() -// Task { await searchByName(name: searchText, reset: true) } Task { await searchByName( name: searchText.trimmingCharacters(in: .whitespacesAndNewlines), @@ -265,7 +255,7 @@ final class InventoryViewModel: ObservableObject { } } - // MARK: - ✅ 무한 스크롤 로드 + // MARK: - 무한 스크롤 로드 func loadMore(searchText: String) async { if isSearching { guard !searchText.trimmingCharacters(in: .whitespaces).isEmpty else { return } diff --git a/StockMate/StockMate/app/feature/notification/data/NotificationApi.swift b/StockMate/StockMate/app/feature/notification/data/NotificationApi.swift index b9c139a..a2513c7 100644 --- a/StockMate/StockMate/app/feature/notification/data/NotificationApi.swift +++ b/StockMate/StockMate/app/feature/notification/data/NotificationApi.swift @@ -8,6 +8,7 @@ import Foundation import Alamofire +// MARK: - 알림 데이터 구조 struct NotificationItem: Decodable, Identifiable { let id: Int let orderId: Int @@ -17,33 +18,34 @@ struct NotificationItem: Decodable, Identifiable { let read: Bool } +// MARK: - 알림 관련 API enum NotificationApi { - // 1️⃣ 알림 전체 조회 + // MARK: 알림 전체 조회 static func getAllNotifications() -> DataRequest { let url = ApiClient.baseURL + "api/v1/order/store/notifications/" return ApiClient.shared.request(url, method: .get) } - // 2️⃣ 읽지 않은 알림 개수 조회 + // MARK: 읽지 않은 알림 개수 조회 static func getUnreadCount() -> DataRequest { let url = ApiClient.baseURL + "api/v1/order/store/notifications/unread/count" return ApiClient.shared.request(url, method: .get) } - // 3️⃣ 읽지 않은 알림 조회 + // MARK: 읽지 않은 알림 목록 조회 static func getUnreadNotifications() -> DataRequest { let url = ApiClient.baseURL + "api/v1/order/store/notifications/unread" return ApiClient.shared.request(url, method: .get) } - // 4️⃣ 전체 알림 읽음 처리 + // MARK: 전체 알림 읽음 처리 static func markAllAsRead() -> DataRequest { let url = ApiClient.baseURL + "api/v1/order/store/notifications/read-all" return ApiClient.shared.request(url, method: .patch) } - // 5️⃣ 개별 알림 읽음 처리 + // MARK: 개별 알림 읽음 처리 static func markAsRead(notificationId: Int) -> DataRequest { let url = ApiClient.baseURL + "api/v1/order/store/notifications/read?notificationId=\(notificationId)" return ApiClient.shared.request(url, method: .patch) diff --git a/StockMate/StockMate/app/feature/notification/data/NotificationRepositoryImpl.swift b/StockMate/StockMate/app/feature/notification/data/NotificationRepositoryImpl.swift index b5ec459..47d6593 100644 --- a/StockMate/StockMate/app/feature/notification/data/NotificationRepositoryImpl.swift +++ b/StockMate/StockMate/app/feature/notification/data/NotificationRepositoryImpl.swift @@ -8,28 +8,32 @@ import SwiftUI import Alamofire +// 알림 관련 API 호출을 담당하는 Repository 구현체 final class NotificationRepositoryImpl: NotificationRepositoryProtocol { - + // 전체 알림 목록 조회 func getAllNotifications() async -> AppResult> { let request = NotificationApi.getAllNotifications() return await safeApi(request, decodeTo: ApiResponse<[NotificationItem]>.self) } - + // 읽지 않은 알림 개수 조회 func getUnreadCount() async -> AppResult> { let request = NotificationApi.getUnreadCount() return await safeApi(request, decodeTo: ApiResponse.self) } + // 읽지 않은 알림 목록 조회 func getUnreadNotifications() async -> AppResult> { let request = NotificationApi.getUnreadNotifications() return await safeApi(request, decodeTo: ApiResponse<[NotificationItem]>.self) } + // 모든 알림을 읽음 처리 func markAllAsRead() async -> AppResult> { let request = NotificationApi.markAllAsRead() return await safeApi(request, decodeTo: ApiResponse.self) } + // 특정 알림을 읽음 처리 func markAsRead(notificationId: Int) async -> AppResult> { let request = NotificationApi.markAsRead(notificationId: notificationId) return await safeApi(request, decodeTo: ApiResponse.self) diff --git a/StockMate/StockMate/app/feature/notification/domain/NotificationRepositoryProtocol.swift b/StockMate/StockMate/app/feature/notification/domain/NotificationRepositoryProtocol.swift index 207f460..07fe713 100644 --- a/StockMate/StockMate/app/feature/notification/domain/NotificationRepositoryProtocol.swift +++ b/StockMate/StockMate/app/feature/notification/domain/NotificationRepositoryProtocol.swift @@ -10,18 +10,18 @@ import Alamofire protocol NotificationRepositoryProtocol { - // 1️⃣ 전체 알림 조회 + // 전체 알림 조회 func getAllNotifications() async -> AppResult> - // 2️⃣ 읽지 않은 알림 개수 조회 + // 읽지 않은 알림 개수 조회 func getUnreadCount() async -> AppResult> - // 3️⃣ 읽지 않은 알림 조회 + // 읽지 않은 알림 조회 func getUnreadNotifications() async -> AppResult> - // 4️⃣ 전체 알림 읽음 처리 + // 전체 알림 읽음 처리 func markAllAsRead() async -> AppResult> - // 5️⃣ 개별 알림 읽음 처리 + // 개별 알림 읽음 처리 func markAsRead(notificationId: Int) async -> AppResult> } diff --git a/StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift b/StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift index 062e9e3..7df3f6a 100644 --- a/StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift +++ b/StockMate/StockMate/app/feature/notification/ui/NotificationListView.swift @@ -86,7 +86,7 @@ struct NotificationCardView: View { } Spacer() - // 🔴 안 읽은 알림 표시 점 + // 안 읽은 알림 표시 삘간 점 if !item.read { Circle() .fill(Color.red) diff --git a/StockMate/StockMate/app/feature/notification/viewmodel/NotificationViewModel.swift b/StockMate/StockMate/app/feature/notification/viewmodel/NotificationViewModel.swift index a2fb0de..8c5d479 100644 --- a/StockMate/StockMate/app/feature/notification/viewmodel/NotificationViewModel.swift +++ b/StockMate/StockMate/app/feature/notification/viewmodel/NotificationViewModel.swift @@ -7,12 +7,14 @@ import Foundation +// 알림 데이터를 관리하고, API 요청을 통해 상태를 갱신하는 ViewModel @MainActor final class NotificationViewModel: ObservableObject { @Published var notifications: [NotificationItem] = [] - @Published var unreadCount: Int = 0 // 🔴 추가 + @Published var unreadCount: Int = 0 @Published var isLoading = false + // 알림 데이터 요청용 Repository private let repository: NotificationRepositoryProtocol = NotificationRepositoryImpl() // 전체 알림 조회 @@ -29,7 +31,7 @@ final class NotificationViewModel: ObservableObject { } } - // 🔴 읽지 않은 개수 조회 + // 읽지 않은 개수 조회 func fetchUnreadCount() async { let result = await repository.getUnreadCount() switch result { @@ -55,7 +57,7 @@ final class NotificationViewModel: ObservableObject { read: true ) } - unreadCount = max(0, unreadCount - 1) // 🔴 카운트 즉시 반영 + unreadCount = max(0, unreadCount - 1) // 카운트 즉시 반영 case .failure(let error): print("❌ 알림 읽음 처리 실패:", error.localizedDescription) } @@ -76,7 +78,7 @@ final class NotificationViewModel: ObservableObject { read: true ) } - unreadCount = 0 // 🔴 전체 읽음 시 0으로 초기화 + unreadCount = 0 // 전체 읽음 시 0으로 초기화 case .failure(let error): print("❌ 전체 읽음 실패:", error.localizedDescription) } diff --git a/StockMate/StockMate/app/feature/orders/data/OrderApi.swift b/StockMate/StockMate/app/feature/orders/data/OrderApi.swift index bfa026e..13159e1 100644 --- a/StockMate/StockMate/app/feature/orders/data/OrderApi.swift +++ b/StockMate/StockMate/app/feature/orders/data/OrderApi.swift @@ -10,6 +10,7 @@ import Alamofire // MARK: - Response Models +// 주문 목록 응답 모델 struct OrderListResponse: Decodable { let status: Int let success: Bool @@ -17,6 +18,7 @@ struct OrderListResponse: Decodable { let data: OrderPageData? } +// 주문 상세 응답 모델 struct OrderDetailResponse: Decodable { let status: Int let success: Bool @@ -24,7 +26,7 @@ struct OrderDetailResponse: Decodable { let data: OrderResponseItem? } - +// 주문 페이지 데이터 struct OrderPageData: Decodable { let totalElements: Int let totalPages: Int @@ -34,6 +36,7 @@ struct OrderPageData: Decodable { let last: Bool } +// 주문 항목 정보 struct OrderResponseItem: Decodable, Identifiable { var id: Int { orderId } @@ -55,6 +58,7 @@ struct OrderResponseItem: Decodable, Identifiable { let updatedAt: String } +// 주문자 정보 struct OrderUserInfo: Decodable { let id: Int let memberId: Int @@ -69,12 +73,14 @@ struct OrderUserInfo: Decodable { let longitude: Double } +// 주문된 부품 항목 struct OrderItem: Decodable { let partId: Int let amount: Int let partDetail: OrderPartDetail } +// 주문 부품 상세 정보 struct OrderPartDetail: Decodable { let id: Int let name: String @@ -90,7 +96,9 @@ struct OrderPartDetail: Decodable { } -// MARK: - 주문 생성 Request +// MARK: - Request Models + +// 주문 생성 요청 모델 struct OrderRequest: Encodable { let orderItems: [OrderItems] let requestedShippingDate: String @@ -98,28 +106,29 @@ struct OrderRequest: Encodable { let etc: String } +// 개별 주문 품목 요청 모델 struct OrderItems: Encodable { let partId: Int let amount: Int } +// 주문 생성 응답 데이터 struct OrderCreateResponseData: Decodable { let orderId: Int let orderNumber: String let totalPrice: Int - let paymentType: String // ✅ 서버 필드와 맞춤 + let paymentType: String } -// ✅ 입고 처리 요청 API +// 입고 처리 요청 모델 struct ReceiveOrderRequest: Encodable { let orderNumber: String } -// MARK: - API Call - +// MARK: - API enum OrderApi { - // ✅ 내 주문 리스트 조회 API + // 내 주문 리스트 조회 API static func getMyOrderList( status: String? = nil, startDate: String? = nil, @@ -143,14 +152,14 @@ enum OrderApi { } - // ✅ 주문 상세 조회 API + // 주문 상세 조회 API static func getOrderDetail(orderId: Int) -> DataRequest { let url = ApiClient.baseURL + "api/v1/order/detail?orderId=\(orderId)" return ApiClient.shared.request(url, method: .get) } - // ✅ 주문 생성 API + // 주문 생성 API static func createOrder(_ requestBody: OrderRequest) -> DataRequest { let url = ApiClient.baseURL + "api/v1/order" return ApiClient.shared.request( @@ -161,15 +170,15 @@ enum OrderApi { ) } - // ✅ 주문 취소 API + // 주문 취소 API static func cancelOrder(orderId: Int) -> DataRequest { let url = ApiClient.baseURL + "api/v1/order/\(orderId)/cancel" - print("🚀 CancelOrder URL:", url) + print("CancelOrder URL:", url) return ApiClient.shared.request(url, method: .put) - .validate() // ✅ 서버 상태코드 확인 + .validate() } - // ✅ 입고 처리 API + // 입고 처리 API static func receiveOrder(_ requestBody: ReceiveOrderRequest) -> DataRequest { let url = ApiClient.baseURL + "api/v1/order/receive" return ApiClient.shared.request( diff --git a/StockMate/StockMate/app/feature/orders/data/OrderRepositoryImpl.swift b/StockMate/StockMate/app/feature/orders/data/OrderRepositoryImpl.swift index c670736..6de6a5b 100644 --- a/StockMate/StockMate/app/feature/orders/data/OrderRepositoryImpl.swift +++ b/StockMate/StockMate/app/feature/orders/data/OrderRepositoryImpl.swift @@ -9,6 +9,8 @@ import Foundation import Alamofire final class OrderRepositoryImpl: OrderRepositoryProtocol { + + // 내 주문 목록 조회 func fetchMyOrders( status: String?, startDate: String?, @@ -24,16 +26,15 @@ final class OrderRepositoryImpl: OrderRepositoryProtocol { size: size ) - // safeApi 으로 전체 ApiResponse 를 디코딩 + // 서버 응답을 OrderListResponse 형태로 디코딩 let result = await safeApi(request, decodeTo: OrderListResponse.self) switch result { case .success(let response): - // response.data 는 OrderPageData? 이므로 안전하게 꺼내서 반환 - if let pageData = response.data { + if let pageData = response.data { // response.data 는 OrderPageData? 이므로 안전하게 꺼내서 반환 return .success(pageData) } else { - // 서버가 data를 비워서 보냈다면 메시지로 실패 처리 + // data가 없을 경우 서버 메시지로 실패 처리 return .failure(AppError(code: response.status, message: response.message, underlying: nil)) } @@ -42,23 +43,24 @@ final class OrderRepositoryImpl: OrderRepositoryProtocol { } } - + // 주문 상세 조회 func fetchOrderDetail(orderId: Int) async -> AppResult { let request = OrderApi.getOrderDetail(orderId: orderId) - let result = await safeApi(request, decodeTo: OrderDetailResponse.self) // ✅ 올바른 타입으로 변경 + let result = await safeApi(request, decodeTo: OrderDetailResponse.self) switch result { case .success(let response): if let data = response.data { return .success(data) } else { - return .failure(.init(code: -1, message: "주문 상세 데이터를 불러오지 못했습니다.", underlying: nil)) // ✅ 누락된 인자 채움 + return .failure(.init(code: -1, message: "주문 상세 데이터를 불러오지 못했습니다.", underlying: nil)) } case .failure(let error): return .failure(error) } } + // 주문 생성 func createOrder(request: OrderRequest) async -> AppResult { let request = OrderApi.createOrder(request) @@ -76,30 +78,31 @@ final class OrderRepositoryImpl: OrderRepositoryProtocol { } } - + // 주문 취소 func cancelOrder(orderId: Int) async -> AppResult { let request = OrderApi.cancelOrder(orderId: orderId) let result = await safeApi(request, decodeTo: ApiResponse.self) switch result { case .success(let response): - print("✅ 취소 성공:", response) + print(" 취소 성공:", response) return .success(response.data ?? "success") case .failure(let error): return .failure(error) } } + // 입고 처리 func receiveOrder(orderNumber: String) async -> AppResult { let request = OrderApi.receiveOrder(.init(orderNumber: orderNumber)) let result = await safeApi(request, decodeTo: ApiResponse.self) switch result { case .success(let response): - print("✅ 입고 처리 성공:", response) + print("입고 처리 성공:", response) return .success(response.data ?? "입고 처리가 완료되었습니다.") case .failure(let error): - print("❌ 입고 처리 실패:", error.message) + print("입고 처리 실패:", error.message) return .failure(error) } } diff --git a/StockMate/StockMate/app/feature/orders/domain/OrderRepositoryProtocol.swift b/StockMate/StockMate/app/feature/orders/domain/OrderRepositoryProtocol.swift index 81be290..f2837be 100644 --- a/StockMate/StockMate/app/feature/orders/domain/OrderRepositoryProtocol.swift +++ b/StockMate/StockMate/app/feature/orders/domain/OrderRepositoryProtocol.swift @@ -16,7 +16,7 @@ protocol OrderRepositoryProtocol { size: Int ) async -> AppResult - /// 주문 상세 조회 + // 주문 상세 조회 func fetchOrderDetail( orderId: Int ) async -> AppResult diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift index 07fdcd7..ba66504 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderCartView.swift @@ -15,7 +15,7 @@ struct OrderCartView: View { VStack(spacing: 0) { if cartViewModel.items.isEmpty { - // 🛒 장바구니 비어있을 때 + // 장바구니 비어있을 때 VStack(spacing: 8) { Text("장바구니가 비어있어요.") .font(.system(size: 15, weight: .regular)) diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift index cff9be8..bdc8067 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderDetailView.swift @@ -13,7 +13,7 @@ struct OrderDetailView: View { @ObservedObject var orderViewModel: OrderViewModel @StateObject private var viewModel = OrderDetailViewModel() - // ✅ 입고처리 버튼 + 모달 + 리프레시 + // 입고처리 버튼 + 모달 + 리프레시 @State private var showSuccessModal = false // 모달 표시용 상태 @State private var refreshTrigger = UUID() // 화면 리프레시 트리거 @@ -32,7 +32,7 @@ struct OrderDetailView: View { .frame(maxWidth: .infinity, alignment: .center) - // ✅ 주문 정보 + // 주문 정보 VStack(alignment: .leading, spacing: 6) { Text(formatDate(order.createdAt)) .font(.system(size: 15, weight: .semibold)) @@ -56,7 +56,7 @@ struct OrderDetailView: View { } } - .frame(maxWidth: .infinity, alignment: .leading) // ✅ 이거 추가 + .frame(maxWidth: .infinity, alignment: .leading) .padding(.all, 20) .background(Color.white) .cornerRadius(16) @@ -72,14 +72,14 @@ struct OrderDetailView: View { .font(.system(size: 14)) } - .frame(maxWidth: .infinity, alignment: .leading) // ✅ 여기도 추가 + .frame(maxWidth: .infinity, alignment: .leading) .padding(.all, 20) .background(Color.white) .cornerRadius(16) .shadow(color: .black.opacity(0.05), radius: 3, y: 2) } - // ✅ 배송 정보 + // 배송 정보 VStack(alignment: .leading, spacing: 6) { Text("배송정보") .font(.system(size: 15, weight: .semibold)) @@ -87,7 +87,7 @@ struct OrderDetailView: View { infoRow("주문자명", order.userInfo?.owner ?? "-") infoRow("주소", order.userInfo?.address ?? "-") - // ✅ 운송장정보 안전 처리 + // 운송장정보 안전 처리 let trackingText: String = { if let carrier = order.carrier, let trackingNo = order.trackingNumber, @@ -99,7 +99,7 @@ struct OrderDetailView: View { }() infoRow("운송장번호", trackingText) } - .frame(maxWidth: .infinity, alignment: .leading) // ✅ 여기도 추가 + .frame(maxWidth: .infinity, alignment: .leading) .padding(.all, 20) .background(Color.white) .cornerRadius(16) @@ -123,7 +123,7 @@ struct OrderDetailView: View { .shadow(color: .black.opacity(0.05), radius: 3, y: 2) } - // ✅ 주문 상품 + // 주문 상품 OrderSectionCard { VStack(alignment: .leading, spacing: 6) { Text("주문 상품 \(order.orderItems.count)개") @@ -154,16 +154,16 @@ struct OrderDetailView: View { .font(.system(size: 14, weight: .bold)) } } - .frame(maxWidth: .infinity, alignment: .leading) // ✅ 왼쪽 정렬 강제 + .frame(maxWidth: .infinity, alignment: .leading) } } .padding(.top, 8) // 위 여백만 살짝 } - .frame(maxWidth: .infinity, alignment: .leading) // ✅ 섹션 전체도 왼쪽으로 정렬 + .frame(maxWidth: .infinity, alignment: .leading) } - // ✅ 결제 정보 + // 결제 정보 OrderSectionCard { VStack(alignment: .leading, spacing: 10) { Text("결제 정보") @@ -236,7 +236,7 @@ struct OrderDetailView: View { let result = await orderViewModel.receiveOrder(orderNumber: order.orderNumber) switch result { case .success(let message): - showSuccessModal = true // ✅ 입고 처리 성공 시 모달 표시 + showSuccessModal = true // 입고 처리 성공 시 모달 표시 print("입고 처리 성공: \(message)") case .failure(let error): print("입고 처리 실패: \(error.message)") @@ -258,7 +258,7 @@ struct OrderDetailView: View { } .padding(.top, 5) } else{ - // 👉 나머지 상태: 영수증 확인 버튼만 하나 + // 나머지 상태: 영수증 확인 버튼만 하나 NavigationLink(destination: ReceiptView(orderId: order.id)) { Text("영수증 확인") .font(.system(size: 15, weight: .semibold)) @@ -276,7 +276,7 @@ struct OrderDetailView: View { } } - .padding(.horizontal, 20) // ✅ 전체 섹션 동일 여백 + .padding(.horizontal, 20) // 전체 섹션 동일 여백 .padding(.vertical, 16) } else if let error = viewModel.errorMessage { @@ -298,7 +298,7 @@ struct OrderDetailView: View { primaryButtonTitle: "확인", primaryAction: { showSuccessModal = false - // ✅ 화면 리프레시 트리거 + // 화면 리프레시 트리거 refreshTrigger = UUID() Task { await viewModel.fetchOrderDetail(orderId: orderId) @@ -347,7 +347,7 @@ struct OrderDetailView: View { } -// ✅ 카드 레이아웃 통일용 +// 카드 레이아웃 통일용 struct OrderSectionCard: View { let content: Content init(@ViewBuilder content: () -> Content) { diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift index 4c2fadc..3fa994e 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderInfoView.swift @@ -34,7 +34,7 @@ struct OrderInfoView: View { @State private var showChargeToast = false // 충전 완료 - // ✅ 모달 관련 상태 + // 모달 관련 상태 @State private var showOrderSuccessModal = false @State private var navigateToOrderDetail = false @State private var navigateToHome = false @@ -92,7 +92,7 @@ struct OrderInfoView: View { iconName: "checkmark", iconColor: .green ) - // ✅ 예치금 부족 토스트 + // 예치금 부족 토스트 .toast( isPresented: $showDepositToast, message: "예치금이 부족합니다. (부족: \(formatPrice((cartViewModel.cart?.totalPrice ?? 0) - depositViewModel.balance))원)", @@ -136,7 +136,7 @@ struct OrderInfoView: View { } .sheet(isPresented: $depositViewModel.showChargeSheet) { DepositChargeView(viewModel: depositViewModel) { - // ✅ 충전 성공 시 토스트 표시 + // 충전 성공 시 토스트 표시 withAnimation { showChargeToast = true } @@ -251,7 +251,7 @@ extension OrderInfoView { } .scrollContentBackground(.hidden) .background(Color.white) - .frame(height: 93) // ✅ 높이 기존 70 → 110 으로 확대 + .frame(height: 93) .overlay( RoundedRectangle(cornerRadius: 10) .stroke(requestMessage.isEmpty ? Color(.systemGray4) : Color.Primary, lineWidth: 1) @@ -428,7 +428,7 @@ extension OrderInfoView { let deposit = depositViewModel.balance if totalPrice > deposit { - // ✅ 예치금 부족 + // 예치금 부족 withAnimation { showDepositToast = true } diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift index 76baad3..160593b 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderListView.swift @@ -118,9 +118,8 @@ struct OrderListCardView: View { Text(first.partDetail.korName) .font(.system(size: 13, weight: .semibold)) .foregroundColor(.black) - .lineLimit(nil) // ✅ 줄 제한 해제 + .lineLimit(nil) .fixedSize(horizontal: false, vertical: true) -// .lineLimit(2) if order.orderItems.count > 1 { Text("외 \(order.orderItems.count - 1)개") @@ -143,7 +142,7 @@ struct OrderListCardView: View { } // 주문취소 버튼 (필요 시) - if order.orderStatus == "ORDER_COMPLETED" { + if order.orderStatus == "PAY_COMPLETED" { Button(action: { // 주문취소 처리 Task { diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift index 26c2886..fd44969 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderRequestSearchView.swift @@ -38,7 +38,7 @@ struct OrderRequestSearchView: View { var body: some View { ZStack { VStack(spacing: 0) { - // 🔍 검색창 + // 검색창 HStack { Image(systemName: "magnifyingglass") .foregroundColor(.gray) diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderResultView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderResultView.swift index 39fc6bf..0b89be5 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderResultView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderResultView.swift @@ -15,14 +15,14 @@ struct OrderResultView: View { Spacer() - // ✅ 결제 완료 아이콘 + // 결제 완료 아이콘 Image(systemName: "checkmark.circle.fill") .resizable() .frame(width: 80, height: 80) .foregroundColor(.blue) .padding(.bottom, 8) - // ✅ 완료 문구 + // 완료 문구 Text("결제가 완료되었습니다") .font(.title3) .bold() @@ -32,7 +32,7 @@ struct OrderResultView: View { Spacer() - // ✅ 하단 버튼 + // 하단 버튼 VStack(spacing: 12) { Button { dismiss() // 이전 화면으로 돌아가기 diff --git a/StockMate/StockMate/app/feature/orders/ui/OrderView.swift b/StockMate/StockMate/app/feature/orders/ui/OrderView.swift index 90e3452..8334011 100644 --- a/StockMate/StockMate/app/feature/orders/ui/OrderView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/OrderView.swift @@ -29,7 +29,7 @@ struct OrderView: View { .padding(.leading, 25) .frame(maxWidth: .infinity, alignment: .leading) - // 🔍 검색창 + // 검색창 NavigationLink(destination: OrderRequestSearchView(cartViewModel: cartViewModel) ) { diff --git a/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift b/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift index 7c19331..e0b953b 100644 --- a/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift +++ b/StockMate/StockMate/app/feature/orders/ui/ReceiptView.swift @@ -21,7 +21,7 @@ struct ReceiptView: View { @StateObject private var detailViewModel = OrderDetailViewModel() @State var sellerName = "홍길동" - @State var businessNumber = "215-87-12345" // 형식만 맞춘 랜덤번호 + @State var businessNumber = "215-87-12345" @State var phone = "02-567-8901" @State var address = "서울특별시 금천구 가산동 459-9" @@ -45,7 +45,7 @@ struct ReceiptView: View { generatePDF(type: .receipt80mm, order: order) } label: { Text("PDF 저장") - .font(.system(size: 13, weight: .semibold)) // ✅ 글씨 약간 작게 + .font(.system(size: 13, weight: .semibold)) .frame(maxWidth: .infinity) .padding(.vertical, 12) .padding(.horizontal, 8) @@ -168,7 +168,7 @@ struct ReceiptView: View { } private func generatePDF(type: PDFType, order: OrderResponseItem) { - // ✅ 아이폰 화면 비율로 렌더링 (디바이스 폭 고정) + // 아이폰 화면 비율로 렌더링 (디바이스 폭 고정) let screenWidth = UIScreen.main.bounds.width let view = receiptContent(order: order) .frame(width: screenWidth) // 폭 고정 (문장 길이에 따라 늘어나지 않음) @@ -184,7 +184,7 @@ struct ReceiptView: View { pdfDoc.insert(pdfPage, at: 0) } - // ✅ 파일명: 주문번호 기반 + // 파일명: 주문번호 기반 let fileName = "receipt_\(order.orderNumber).pdf" let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(fileName) @@ -204,7 +204,7 @@ struct ReceiptView: View { func formattedDate(_ timestamp: String) -> String { let inputFormatter = DateFormatter() inputFormatter.locale = Locale(identifier: "ko_KR") - inputFormatter.timeZone = TimeZone.current // ✅ 실제 한국 시간 기준 + inputFormatter.timeZone = TimeZone.current inputFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS" guard let date = inputFormatter.date(from: timestamp) else { @@ -233,7 +233,7 @@ func formattedApprovalNumber(_ timestamp: String) -> String { let outputFormatter = DateFormatter() outputFormatter.locale = Locale(identifier: "ko_KR") outputFormatter.timeZone = TimeZone.current - outputFormatter.dateFormat = "yyyyMMddHHmm" // ✅ 승인번호 포맷 + outputFormatter.dateFormat = "yyyyMMddHHmm" return outputFormatter.string(from: date) } diff --git a/StockMate/StockMate/app/feature/orders/viewmodel/OrderViewModel.swift b/StockMate/StockMate/app/feature/orders/viewmodel/OrderViewModel.swift index 447274b..ddb87c9 100644 --- a/StockMate/StockMate/app/feature/orders/viewmodel/OrderViewModel.swift +++ b/StockMate/StockMate/app/feature/orders/viewmodel/OrderViewModel.swift @@ -23,7 +23,7 @@ final class OrderViewModel: ObservableObject { init(repository: OrderRepositoryProtocol = OrderRepositoryImpl()) { self.repository = repository } - + // MARK: - 주문 목록 조회 func loadOrders( status: String? = nil, startDate: String? = nil, @@ -50,7 +50,7 @@ final class OrderViewModel: ObservableObject { } } - // 주문 생성 + // MARK: - 주문 생성 func createOrder(request: OrderRequest) async { let result = await repository.createOrder(request: request) @@ -60,25 +60,27 @@ final class OrderViewModel: ObservableObject { self.isOrderSuccess = true case .failure(let error): - print("❌ 주문 실패:", error.message) + print("주문 실패:", error.message) self.errorMessage = error.message } } + // MARK: - 주문 취소 func cancelOrder(orderId: Int) async { isLoading = true let result = await repository.cancelOrder(orderId: orderId) switch result { case .success: - await loadOrders() // ✅ 취소 후 즉시 UI 새로고침 + await loadOrders() // 취소 후 즉시 UI 새로고침 case .failure(let error): errorMessage = error.message } isLoading = false } + // MARK: - 입고 처리 (주문 수령) func receiveOrder(orderNumber: String) async -> AppResult { isLoading = true defer { isLoading = false } @@ -86,7 +88,7 @@ final class OrderViewModel: ObservableObject { let result = await repository.receiveOrder(orderNumber: orderNumber) switch result { case .success(let message): - print("✅ 입고 처리 성공:", message) + print("입고 처리 성공:", message) await loadOrders() return .success(message) case .failure(let error): @@ -94,19 +96,4 @@ final class OrderViewModel: ObservableObject { return .failure(error) } } - - -// func receiveOrder(orderNumber: String) async { -// isLoading = true -// defer { isLoading = false } -// -// let result = await repository.receiveOrder(orderNumber: orderNumber) -// switch result { -// case .success(let message): -// print("✅ 입고 처리 성공:", message) -// await loadOrders() // 리스트 갱신 -// case .failure(let error): -// errorMessage = error.message -// } -// } } diff --git a/StockMate/StockMate/app/feature/parts/data/PartApi.swift b/StockMate/StockMate/app/feature/parts/data/PartApi.swift index e1d4050..9df2b88 100644 --- a/StockMate/StockMate/app/feature/parts/data/PartApi.swift +++ b/StockMate/StockMate/app/feature/parts/data/PartApi.swift @@ -9,12 +9,13 @@ import Foundation import Alamofire -// ✅ 요청 모델 (partCode → partId 로 변경) +// 출고(Release) 요청 시 사용되는 부품 항목 데이터 모델 struct ReleaseItemRequest: Encodable { let partId: Int let quantity: Int } +// 부품 상세 정보 응답 struct PartDetailResponse: Decodable, Identifiable { let id: Int let name: String @@ -45,12 +46,11 @@ struct PartDetail: Identifiable { } -// ✅ API 정의 +// API enum PartApi { static func releaseParts(items: [ReleaseItemRequest]) -> DataRequest { let url = ApiClient.baseURL + "api/v1/store/release" let body: [String: Any] = [ -// "items": items.map { ["partId": $0.partId, "quantity": $0.quantity] } "items": items.map { ["partId": $0.partId, "quantity": $0.quantity] } ] @@ -62,11 +62,11 @@ enum PartApi { ) } - // ✅ 부품 상세 조회 API + // 부품 상세 조회 API static func fetchPartDetail(partIds: [Int]) -> DataRequest { let url = ApiClient.baseURL + "api/v1/parts/detail" - // ✅ 요청 본문은 단순 배열 형태이므로 parameters 사용 X, 직접 body에 encode + // 요청 본문은 단순 배열 형태이므로 parameters 사용 X, 직접 body에 encode return ApiClient.shared.request( url, method: .post, diff --git a/StockMate/StockMate/app/feature/parts/data/PartRepositoryImpl.swift b/StockMate/StockMate/app/feature/parts/data/PartRepositoryImpl.swift index cc577f5..87da8ce 100644 --- a/StockMate/StockMate/app/feature/parts/data/PartRepositoryImpl.swift +++ b/StockMate/StockMate/app/feature/parts/data/PartRepositoryImpl.swift @@ -9,11 +9,13 @@ import Foundation import Alamofire final class PartRepositoryImpl: PartRepositoryProtocol { + // 부품 출고 요청 func releaseParts(items: [ReleaseItemRequest]) async -> AppResult> { let dataReq = PartApi.releaseParts(items: items) return await safeApi(dataReq, decodeTo: ApiResponse.self) } + // 부품 상세 정보 조회 func fetchPartDetail(partIds: [Int]) async -> AppResult> { let dataReq = PartApi.fetchPartDetail(partIds: partIds) return await safeApi(dataReq, decodeTo: ApiResponse<[PartDetailResponse]>.self) diff --git a/StockMate/StockMate/app/feature/parts/data/PartStore.swift b/StockMate/StockMate/app/feature/parts/data/PartStore.swift index 48436dc..11f7fb2 100644 --- a/StockMate/StockMate/app/feature/parts/data/PartStore.swift +++ b/StockMate/StockMate/app/feature/parts/data/PartStore.swift @@ -7,10 +7,12 @@ import Foundation +// 부품 출고 화면에서 선택된 부품들을 관리하는 상태 저장 클래스 @MainActor final class PartStore: ObservableObject { @Published var parts: [PartDetail] = [] + // 부품 추가 func addPart(_ part: PartDetail) { if let index = parts.firstIndex(where: { $0.id == part.id }) { parts[index].quantity += 1 // 이미 존재하면 수량만 +1 @@ -21,11 +23,12 @@ final class PartStore: ObservableObject { } } + // 선택된 부품 전체 초기화 func clear() { parts.removeAll() } - // ✅ 수량 변경용 메서드 추가 + // 수량 변경용 메서드 추가 func increaseQuantity(for part: PartDetail) { if let index = parts.firstIndex(where: { $0.id == part.id }) { parts[index].quantity += 1 @@ -33,6 +36,7 @@ final class PartStore: ObservableObject { } } + // 부품 수량 감소 (최소 1개까지) func decreaseQuantity(for part: PartDetail) { if let index = parts.firstIndex(where: { $0.id == part.id }), parts[index].quantity > 1 { @@ -41,6 +45,7 @@ final class PartStore: ObservableObject { } } + // 수량 감소 후 1개 이하일 경우 목록에서 제거 func decreaseQuantityOrRemove(for part: PartDetail) { if let index = parts.firstIndex(where: { $0.id == part.id }) { if parts[index].quantity > 1 { diff --git a/StockMate/StockMate/app/feature/parts/domain/PartRepositoryProtocol.swift b/StockMate/StockMate/app/feature/parts/domain/PartRepositoryProtocol.swift index 14f73c1..d75befa 100644 --- a/StockMate/StockMate/app/feature/parts/domain/PartRepositoryProtocol.swift +++ b/StockMate/StockMate/app/feature/parts/domain/PartRepositoryProtocol.swift @@ -8,6 +8,9 @@ import Foundation protocol PartRepositoryProtocol { + // 부품 출고 요청 func releaseParts(items: [ReleaseItemRequest]) async -> AppResult> + + // 부품 상세 정보 조회 func fetchPartDetail(partIds: [Int]) async -> AppResult> } diff --git a/StockMate/StockMate/app/feature/parts/ui/QRScannerView.swift b/StockMate/StockMate/app/feature/parts/ui/QRScannerView.swift index c4a109a..4773356 100644 --- a/StockMate/StockMate/app/feature/parts/ui/QRScannerView.swift +++ b/StockMate/StockMate/app/feature/parts/ui/QRScannerView.swift @@ -9,27 +9,19 @@ import SwiftUI struct QRScannerView: UIViewControllerRepresentable { @Binding var scannedCode: String? - var isActive: Bool = true // ✅ 추가: 카메라 활성화 상태 + var isActive: Bool = true func makeUIViewController(context: Context) -> QRScannerViewController { let controller = QRScannerViewController() controller.delegate = context.coordinator return controller } - -// func updateUIViewController(_ uiViewController: QRScannerViewController, context: Context) {} -// func updateUIViewController(_ uiViewController: QRScannerViewController, context: Context) { -// // ✅ scannedCode가 nil이면 다시 스캔 시작 -// if scannedCode == nil { -// uiViewController.startScanning() -// } -// } func updateUIViewController(_ uiViewController: QRScannerViewController, context: Context) { if isActive { - uiViewController.startSession() // ✅ 바텀시트 닫혔을 때 다시 스캔 시작 + uiViewController.startSession() // 바텀시트 닫혔을 때 다시 스캔 시작 } else { - uiViewController.stopSession() // ✅ 바텀시트 열렸을 때 스캔 일시정지 + uiViewController.stopSession() // 바텀시트 열렸을 때 스캔 일시정지 } } diff --git a/StockMate/StockMate/app/feature/parts/ui/QRScannerViewController.swift b/StockMate/StockMate/app/feature/parts/ui/QRScannerViewController.swift index 626dab2..0ec635f 100644 --- a/StockMate/StockMate/app/feature/parts/ui/QRScannerViewController.swift +++ b/StockMate/StockMate/app/feature/parts/ui/QRScannerViewController.swift @@ -53,13 +53,9 @@ final class QRScannerViewController: UIViewController, AVCaptureMetadataOutputOb startScanning() - // ⚠️ 백그라운드에서 실행 -// DispatchQueue.global(qos: .userInitiated).async { -// self.captureSession.startRunning() -// } } - // ✅ 스캔 재시작/중단 함수 추가 + // 스캔 재시작/중단 함수 추가 func startScanning() { guard captureSession != nil else { return } if !captureSession.isRunning { @@ -89,7 +85,7 @@ final class QRScannerViewController: UIViewController, AVCaptureMetadataOutputOb } - // ✅ QR 감지 시 호출 + // QR 감지 시 호출 func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) { if let metadataObject = metadataObjects.first as? AVMetadataMachineReadableCodeObject, let stringValue = metadataObject.stringValue { diff --git a/StockMate/StockMate/app/feature/parts/viewmodel/PartViewModel.swift b/StockMate/StockMate/app/feature/parts/viewmodel/PartViewModel.swift index 394dc62..d4ce883 100644 --- a/StockMate/StockMate/app/feature/parts/viewmodel/PartViewModel.swift +++ b/StockMate/StockMate/app/feature/parts/viewmodel/PartViewModel.swift @@ -56,14 +56,14 @@ final class PartViewModel: ObservableObject { case .success(let apiResp): if apiResp.success, let data = apiResp.data { partDetails = data - print("✅ 부품 상세 조회 성공:", data) + print("부품 상세 조회 성공:", data) } else { message = apiResp.message - print("⚠️ 서버 응답 실패:", apiResp.message) + print("서버 응답 실패:", apiResp.message) } case .failure(let err): message = err.message - print("❌ 네트워크 오류:", err) + print("네트워크 오류:", err) } } diff --git a/StockMate/StockMate/app/feature/payment/data/PaymentApi.swift b/StockMate/StockMate/app/feature/payment/data/PaymentApi.swift index c2db3ef..fcca9b3 100644 --- a/StockMate/StockMate/app/feature/payment/data/PaymentApi.swift +++ b/StockMate/StockMate/app/feature/payment/data/PaymentApi.swift @@ -16,7 +16,7 @@ struct PaymentAmount: Decodable { let userId: Int } -// ✅ 월별 소비 내역 구조체 +// 월별 소비 내역 구조체 struct MonthlySpending: Decodable, Identifiable { var id: String { month } // 리스트에서 사용하기 편하게 let month: String @@ -51,14 +51,14 @@ enum PaymentApi { ) } - // ✅ 최근 5개월 소비 내역 조회 + // 최근 5개월 소비 내역 조회 static func getMonthlySpending() -> DataRequest { let url = ApiClient.baseURL + "api/v1/payment/monthly-spending" return ApiClient.shared.request(url, method: .get) } - // ✅ 지난달 카테고리별 지출금액 조회 + // 지난달 카테고리별 지출금액 조회 static func getCategorySpending() -> DataRequest { let url = ApiClient.baseURL + "api/v1/order/category-spend" return ApiClient.shared.request(url, method: .get) diff --git a/StockMate/StockMate/app/feature/payment/data/PaymentRepositoryImpl.swift b/StockMate/StockMate/app/feature/payment/data/PaymentRepositoryImpl.swift index 6024082..eee8a37 100644 --- a/StockMate/StockMate/app/feature/payment/data/PaymentRepositoryImpl.swift +++ b/StockMate/StockMate/app/feature/payment/data/PaymentRepositoryImpl.swift @@ -10,8 +10,10 @@ import Foundation import Alamofire +// 결제 및 예치금 관련 Repository 구현체 final class PaymentRepositoryImpl: PaymentRepositoryProtocol { + // 예치금 잔액 조회 func fetchDepositAmount() async -> AppResult { let request = PaymentApi.getPaymentAmount() let result = await safeApi(request, decodeTo: ApiResponse.self) @@ -33,6 +35,7 @@ final class PaymentRepositoryImpl: PaymentRepositoryProtocol { } } + // 예치금 충전 func chargeDeposit(amount: Int) async -> AppResult { let request = PaymentApi.chargeDeposit(amount: amount) let result = await safeApi(request, decodeTo: ApiResponse.self) @@ -46,7 +49,7 @@ final class PaymentRepositoryImpl: PaymentRepositoryProtocol { } } - // ✅ 최근 5개월 소비 내역 조회 + // 최근 5개월 소비 내역 조회 func fetchMonthlySpending() async -> AppResult<[MonthlySpending]> { let request = PaymentApi.getMonthlySpending() let result = await safeApi(request, decodeTo: ApiResponse<[MonthlySpending]>.self) @@ -62,7 +65,7 @@ final class PaymentRepositoryImpl: PaymentRepositoryProtocol { } } - // ✅ 지난달 카테고리별 지출 + // 지난달 카테고리별 지출 func fetchCategorySpending() async -> AppResult<[CategorySpending]> { let request = PaymentApi.getCategorySpending() let result = await safeApi(request, decodeTo: ApiResponse<[CategorySpending]>.self) diff --git a/StockMate/StockMate/app/feature/payment/domain/PaymentRepositoryProtocol.swift b/StockMate/StockMate/app/feature/payment/domain/PaymentRepositoryProtocol.swift index fe22926..bc5ffbc 100644 --- a/StockMate/StockMate/app/feature/payment/domain/PaymentRepositoryProtocol.swift +++ b/StockMate/StockMate/app/feature/payment/domain/PaymentRepositoryProtocol.swift @@ -12,7 +12,7 @@ protocol PaymentRepositoryProtocol { func fetchDepositAmount() async -> AppResult func chargeDeposit(amount: Int) async -> AppResult - // ✅ 최근 5개월 소비 내역 조회 + // 최근 5개월 소비 내역 조회 func fetchMonthlySpending() async -> AppResult<[MonthlySpending]> // 지난달 카테고리별 지출 diff --git a/StockMate/StockMate/app/feature/payment/ui/DepositChargeView.swift b/StockMate/StockMate/app/feature/payment/ui/DepositChargeView.swift index 108ce84..5070451 100644 --- a/StockMate/StockMate/app/feature/payment/ui/DepositChargeView.swift +++ b/StockMate/StockMate/app/feature/payment/ui/DepositChargeView.swift @@ -106,8 +106,8 @@ struct DepositChargeView: View { } .background( (Int(amountText) ?? 0) > 0 && !isCharging - ? Color.Primary // ✅ 활성 상태 - : Color.gray.opacity(0.3) // ✅ 비활성(회색) + ? Color.Primary + : Color.gray.opacity(0.3) ) .cornerRadius(18) .padding(.bottom, 25) diff --git a/StockMate/StockMate/app/feature/payment/viewmodel/DashboardViewModel.swift b/StockMate/StockMate/app/feature/payment/viewmodel/DashboardViewModel.swift index 8ef7136..07b2b94 100644 --- a/StockMate/StockMate/app/feature/payment/viewmodel/DashboardViewModel.swift +++ b/StockMate/StockMate/app/feature/payment/viewmodel/DashboardViewModel.swift @@ -16,7 +16,7 @@ final class DashboardViewModel: ObservableObject { @Published var isLoading = false - /// ✅ 최근 5개월 소비 내역 조회 + // 최근 5개월 소비 내역 조회 func fetchMonthlySpending() async { isLoading = true let result = await repo.fetchMonthlySpending() @@ -30,27 +30,27 @@ final class DashboardViewModel: ObservableObject { } } - // ✅ 지난달 카테고리별 지출 금액 조회 - func fetchCategorySpending() async { - isLoading = true - let result = await repo.fetchCategorySpending() - isLoading = false + // 지난달 카테고리별 지출 금액 조회 + func fetchCategorySpending() async { + isLoading = true + let result = await repo.fetchCategorySpending() + isLoading = false - switch result { - case .success(let data): - categorySpendings = data - case .failure(let err): - print("❌ 카테고리별 지출 금액 조회 실패:", err.message) - } - } + switch result { + case .success(let data): + categorySpendings = data + case .failure(let err): + print("❌ 카테고리별 지출 금액 조회 실패:", err.message) + } + } - /// ✅ 막대그래프 비율 계산 + // 막대그래프 비율 계산 var spendingRatios: [CGFloat] { guard let max = monthlySpendings.map({ $0.totalAmount }).max(), max > 0 else { return [] } return monthlySpendings.map { CGFloat($0.totalAmount) / CGFloat(max) } } - /// ✅ 월 라벨 (예: "10월", "11월") + // 월 라벨 (예: "10월", "11월") var monthLabels: [String] { monthlySpendings.map { month in // "2025-10" → "10월" diff --git a/StockMate/StockMate/app/feature/payment/viewmodel/DepositViewModel.swift b/StockMate/StockMate/app/feature/payment/viewmodel/DepositViewModel.swift index 82ee801..3eaad6e 100644 --- a/StockMate/StockMate/app/feature/payment/viewmodel/DepositViewModel.swift +++ b/StockMate/StockMate/app/feature/payment/viewmodel/DepositViewModel.swift @@ -18,7 +18,7 @@ final class DepositViewModel: ObservableObject { @Published var depositAmount: Int = 0 @Published var isChargeSuccess: Bool = false - /// ✅ 예치금 조회 + // 예치금 조회 func fetchDepositAmount() async { isLoading = true @@ -34,7 +34,7 @@ final class DepositViewModel: ObservableObject { } } - /// ✅ 예치금 충전 + // 예치금 충전 func chargeDeposit(amount: Int) async -> Bool { let result = await repository.chargeDeposit(amount: amount) switch result { diff --git a/StockMate/StockMate/app/feature/user/data/UserApi.swift b/StockMate/StockMate/app/feature/user/data/UserApi.swift index 368f6ff..5a6970a 100644 --- a/StockMate/StockMate/app/feature/user/data/UserApi.swift +++ b/StockMate/StockMate/app/feature/user/data/UserApi.swift @@ -8,6 +8,7 @@ import Foundation import Alamofire +// MARK: - 사용자 정보 응답 모델 struct UserInfoResponse: Decodable { let status: Int let success: Bool @@ -15,6 +16,7 @@ struct UserInfoResponse: Decodable { let data: UserInfo? } +// MARK: - 사용자 정보 모델 struct UserInfo: Decodable { let createdAt: String let updatedAt: String @@ -31,6 +33,7 @@ struct UserInfo: Decodable { let verified: String } +// MARK: - 사용자 API 요청 정의 enum UserApi { static func getUserInfo() -> DataRequest { let url = ApiClient.baseURL + "api/v1/user/my" diff --git a/StockMate/StockMate/app/feature/user/data/UserRepositoryImpl.swift b/StockMate/StockMate/app/feature/user/data/UserRepositoryImpl.swift index 7bd1289..69caa17 100644 --- a/StockMate/StockMate/app/feature/user/data/UserRepositoryImpl.swift +++ b/StockMate/StockMate/app/feature/user/data/UserRepositoryImpl.swift @@ -8,7 +8,9 @@ import Foundation import Alamofire +// MARK: - 사용자 관련 Repository 구현체 final class UserRepositoryImpl: UserRepositoryProtocol { + // 사용자 정보 조회 func getUserInfo() async -> AppResult> { let dataReq = UserApi.getUserInfo() return await safeApi(dataReq, decodeTo: ApiResponse.self) diff --git a/StockMate/StockMate/app/feature/user/domain/UserRepositoryProtocol.swift b/StockMate/StockMate/app/feature/user/domain/UserRepositoryProtocol.swift index f5b031b..460f943 100644 --- a/StockMate/StockMate/app/feature/user/domain/UserRepositoryProtocol.swift +++ b/StockMate/StockMate/app/feature/user/domain/UserRepositoryProtocol.swift @@ -8,5 +8,6 @@ import Foundation protocol UserRepositoryProtocol { + // 사용자 정보 조회 func getUserInfo() async -> AppResult> } diff --git a/StockMate/StockMate/app/feature/user/ui/ProfileCircleView.swift b/StockMate/StockMate/app/feature/user/ui/ProfileCircleView.swift index 2e1a97d..3f26da6 100644 --- a/StockMate/StockMate/app/feature/user/ui/ProfileCircleView.swift +++ b/StockMate/StockMate/app/feature/user/ui/ProfileCircleView.swift @@ -21,7 +21,7 @@ struct ProfileCircleView: View { GeometryReader { geometry in let minSide = min(geometry.size.width, geometry.size.height) Text(initials) - .font(.system(size: minSide * 0.35, weight: .regular)) // ✅ 내부 크기 비례 + .font(.system(size: minSide * 0.35, weight: .regular)) .foregroundColor(Color(hex: "#374EAF")) .frame(width: geometry.size.width, height: geometry.size.height) .background(Color(hex: "#DCE0F1")) diff --git a/StockMate/StockMate/app/feature/user/ui/ProfileView.swift b/StockMate/StockMate/app/feature/user/ui/ProfileView.swift index 8a986fa..fc1edc6 100644 --- a/StockMate/StockMate/app/feature/user/ui/ProfileView.swift +++ b/StockMate/StockMate/app/feature/user/ui/ProfileView.swift @@ -9,8 +9,8 @@ import SwiftUI struct ProfileView: View { @StateObject private var userViewModel = UserViewModel() - @EnvironmentObject var authViewModel: AuthViewModel // 🔹 전역 Auth 상태 참조 - @State private var showLogoutModal = false // 🔹 로그아웃 모달 상태 + @EnvironmentObject var authViewModel: AuthViewModel // 전역 Auth 상태 참조 + @State private var showLogoutModal = false // 로그아웃 모달 상태 var body: some View { ZStack{ @@ -40,12 +40,9 @@ struct ProfileView: View { VStack(spacing: 10) { SettingNavigationRow(icon: "user", title: "프로필 확인", destination: UserProfileView()) SettingNavigationRow(icon: "notification", title: "알림", destination: NotificationListView()) -// SettingRow(icon: "notification", title: "알림") SettingNavigationRow(icon: "receipt", title: "예치금 히스토리", destination: TransactionTypeListView()) -// SettingNavigationRow(icon: "receipt", title: "예치금 히스토리", destination: PaymentTransactionView()) SettingNavigationRow(icon: "bag", title: "주문 내역", destination: OrderListView()) - // SettingRow(icon: "logout", title: "로그아웃") - // 🔹 로그아웃 버튼 + // 로그아웃 버튼 Button { showLogoutModal = true } label: { @@ -66,7 +63,7 @@ struct ProfileView: View { Task { await userViewModel.loadUserInfo() } } - // 🔹 AlertModal (ZStack 위에 오버레이로 표시) + // AlertModal (ZStack 위에 오버레이로 표시) if showLogoutModal { Color.black.opacity(0.3) .ignoresSafeArea() diff --git a/StockMate/StockMate/app/feature/user/viewmodel/UserViewModel.swift b/StockMate/StockMate/app/feature/user/viewmodel/UserViewModel.swift index e47d22e..c683087 100644 --- a/StockMate/StockMate/app/feature/user/viewmodel/UserViewModel.swift +++ b/StockMate/StockMate/app/feature/user/viewmodel/UserViewModel.swift @@ -33,8 +33,8 @@ final class UserViewModel: ObservableObject { case .failure(let err): message = err.message print("유저 정보 불러오기 실패:", err.message) - // ✅ 세션 만료나 인증 문제면 로그인화면으로 유도 - if err.code == 401 || err.code == 403 { + + if err.code == 401 || err.code == 403 { // 세션 만료나 인증 문제면 로그인 화면으로 유도 shouldGoToLogin = true } diff --git a/StockMate/StockMate/app/navigation/AppNavHost.swift b/StockMate/StockMate/app/navigation/AppNavHost.swift index ffb9779..f95ea21 100644 --- a/StockMate/StockMate/app/navigation/AppNavHost.swift +++ b/StockMate/StockMate/app/navigation/AppNavHost.swift @@ -7,22 +7,25 @@ import SwiftUI +// 앱의 전반적인 네비게이션 흐름을 관리하는 뷰 struct AppNavHost: View { - @EnvironmentObject var authViewModel: AuthViewModel - @StateObject private var partStore = PartStore() + @EnvironmentObject var authViewModel: AuthViewModel // 인증 상태 관리 뷰모델 + @StateObject private var partStore = PartStore() // 부품 관련 상태 저장소 var body: some View { NavigationStack { switch authViewModel.authState { - case .unauthenticated: + // 로그인되지 않은 경우 → 로그인 화면 표시 LoginView(onClickRegister: { authViewModel.goToRegister() }) case .registering: + // 회원가입 중인 경우 → 회원가입 화면 표시 RegisterView() case .authenticated: + // 로그인 완료된 경우 → 메인 탭 화면 표시 MainTabView() .environmentObject(authViewModel) .environmentObject(partStore) diff --git a/StockMate/StockMate/app/navigation/MainTabView.swift b/StockMate/StockMate/app/navigation/MainTabView.swift index 3645577..ec1ad2a 100644 --- a/StockMate/StockMate/app/navigation/MainTabView.swift +++ b/StockMate/StockMate/app/navigation/MainTabView.swift @@ -19,18 +19,12 @@ struct MainTabView: View { ZStack { switch selectedTab { case 0: HomeView() - case 1: NavigationStack{ OrderView(cartViewModel: cartVM) } //, inventoryViewModel: inventoryVM) } -// case 1: NavigationStack{ OrderView() } + case 1: NavigationStack{ OrderView(cartViewModel: cartVM) } case 2: InventoryView( selectedTab: $selectedTab, tabTappedTrigger: $tabTappedTrigger ) - -// NavigationStack { InventoryView(selectedTab: $selectedTab, tabTappedTrigger: $tabTappedTrigger) } -// case 3: NavigationStack{ ContentView() } -// case 3: NavigationStack{ ReceiptView() } -// case 3: NavigationStack{ NotificationListView() } case 3: ProfileView() default: NavigationStack{ ContentView() } } @@ -44,9 +38,9 @@ struct MainTabView: View { tabButton(index: 2, icon: "tabInventory", text: "재고관리") tabButton(index: 3, icon: "tabProfile", text: "사용자") } - .padding(.vertical, 24) // 탭 높이 조절 + .padding(.vertical, 24) .padding(.horizontal, 20) - .background(Color.White) // 탭바 배경색 + .background(Color.White) } .edgesIgnoringSafeArea(.bottom) } @@ -56,16 +50,13 @@ struct MainTabView: View { let isSelected = selectedTab == index return Button { if selectedTab == index { - // ✅ 같은 탭 다시 누르면 트리거 토글 + // 같은 탭 다시 누르면 트리거 토글 tabTappedTrigger.toggle() } else { withAnimation(.easeInOut) { selectedTab = index } } -// withAnimation(.easeInOut) { // 탭 전환 애니메이션. 없애도 됨 -// selectedTab = index -// } } label: { VStack(spacing: 6) { Image(icon)

S76&l5*MKq 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%0Du$W!~{NckIc*G>HavRQ8b~Uj_5E@+B1D;dq{M_ zFbx$;FB|ME+05-?(33o+*}BEGNmASr^i1C`9#!nB7l>%8rc$FciA7VdT<=(qFQH&jvZ%bDgB(RhX{M z)4o0rU8*Z7w+w$gOaieW?PvkOzpl(16s*>{iz zka_H;ZCOj~Ph^808Bf;y|E^tnf8&U*@BR}X5&!_e3FgoV^rZgE2p{uekI$gsrOWvg0uc!5z^t{CxV5LAM zi=GNi>oejP4iQ_!tMbDvh^ki2))|R1Rd1b}@2c?qBnwE!KZ-ej9ybha9w+cL`Mw;E zjr(!1Nk0#_=w~()_nu7X&c18^Mj!sICIA3{6YS)ax{5xa=;;05-`V=J2P6OdMO##% z%aKnH!$^_R{Cbb%HCu6cbs((zPEx{Bl1zJ7*-WCeu$5;k>f?Kan?npA(wl-~QY=QuGrW2mOpB{P@&% z@1xtVkxy4oX7&&O0HEmPcUD(Lo&6OpAyLkjt_iF$9?zWNM1pJ)aHO1YD#jYqnOl|Q z&&pY!!hxlGPg6VgK85q|L=}f1XWv1*Uq}?iK~KCIbG(0A^hArf<+;6|PPX{EH=sMw z0e#{>zVVRWeh2$-0RX@Y&>A^h?m(WU4VUE*!}>gE=`wL$dKCIzqCjL$`8 zw`T*MGd-cx$=GM6P5VK6lnjXGQYI%kTb6EV9QBi}izCuh8)}*#KWo)hu4 z@YXmBtl`FsgQoMKr`SGynnr+H(?mk?O(c;YI*&r;1Ec6@%=CW1^huy?dCQ~!oqUz< zU>gbm0CN_m` zcp8}FN>&2P#sg(_C@$2Rra=Q2OIg|2pthBfV{N&QIXRKSjvQ1S6vSMqe^sKLQVFd# zC9V8RC(*`=Pv7^uSd+Lg-pQZo93CoW z$8n?=$3O9z6qnX*o&^~#moC3XEw{ONH7adi40?(OHe{KkTy~p9KvKJ3&JtQii`an2 zCU}&Iw2Z@u4vmpXsO5ebId^S^YoEk?Vnd;816MzAPHleHdrSu(yiJDwhBPtCS^xm> zVtYXYpBCSg-la`CYsR!44C!?~LzkSFX?x>5UFkb?wx?}?Wazx>($IeJzRhZElS9L# z92SHd6p+K7!zv=1B(edKBXL(h8~aT8B>Ch~S}SUq{@(QRoFdt*U17XYd=*ol@m@QJ zqIl=@IE|A&Hv@a@z`i`Nx6-pg3&m5k94`vhQDn#qB6{qH^vHxX3W)9p1G*P%(Si8% z*}&VmdFJ$!$JhS;O`ke$lji2K0000^mKV0v)#yFj$zP;#;8WRulS8N7c(|5^~RYN>L%QE zkLq1EU2yMO&HQxo;bkvp)EreYf z=G!Mdmig`3W1(iSNO|^M%Z@q!oTXW1S-a%eeE7FqP$_y5SkNZ-^0bMe`4i9MalTVCOD5-$r!ubMH5wSAYBbx8cg^{kwJ>?#Ni3Sl+bIddX#(Oz9gI z%zaOoK7ZKaEWLc^`#GB(o#t0RSs}-pJMU;`xQmZ?iJ94ofSV-+$(tQMCjS;rk9c&~ z=JD~LR|+Qoo$NEW-+Xfa|Fx@KMLpxxd!0p7pQlcJGeLB_ncG%2DXn?)rt05a;j`;i zLWVs1sWuz451mue^NmcL{GEjFh;#Crv|Hbsck^xFi_1s57qiP%e%ZG7)m4@AEen7( zuHAII_@(Pu_k28FZ!WRB`rMBRzh*C9Kj+)t`}ZFGn{HkmzcBcjZqZX|;Ntwbzsw2( zN~ZXH^?EF}GS#BX`+tDY<0qSXrCX-|sViN$*ZF1Am6VDN+2J3D@-&E3O2C(rlEuCYqjeth#YkM*spsR}D^ zl+D~avHj-zANtj&|Gl}*|4~qKtK+fTi%!VO{do65{UTrO{bwzg8t=Aj5(Qc}3djxt ahd2Kj#FHOiz8>-(Y=)<+pUXO@geCyY7CKr0 literal 0 HcmV?d00001 diff --git a/StockMate/StockMate/resources/Assets.xcassets/intrologo.imageset/intrologo@3x.png b/StockMate/StockMate/resources/Assets.xcassets/intrologo.imageset/intrologo@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..e3e80d020d493b1c9d6628dc26d4a248c661b0e5 GIT binary patch literal 180569 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 z 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 literal 0 HcmV?d00001 diff --git a/StockMate/StockMate/resources/Assets.xcassets/toastlogo.imageset/toastlogo@2x.png b/StockMate/StockMate/resources/Assets.xcassets/toastlogo.imageset/toastlogo@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..1193e9d9a499615f73e8dd0a6ddebb424268c300 GIT binary patch literal 150620 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 ec`v{3e~VC|MEC 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 JM9jed{{fQgdoKV0 literal 0 HcmV?d00001 diff --git a/StockMate/StockMate/resources/Assets.xcassets/toastlogo.imageset/toastlogo@3x.png b/StockMate/StockMate/resources/Assets.xcassets/toastlogo.imageset/toastlogo@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..3d0fa5dc4cc88902094dfd8548809e7ed31af4ad GIT binary patch literal 272016 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⋙LQcl`I@`B>>#Z9 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 z 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