Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions SampoomManagement/App/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ struct ContentView: View {
// MARK: - Properties
let dependencies: AppDependencies
@StateObject private var partViewModel: PartViewModel
@StateObject private var dashboardViewModel: DashboardViewModel
@State private var selectedTab: Tabs = .dashboard
@State private var ordersNavigationPath = NavigationPath()
@State private var partsNavigationPath = NavigationPath()
Expand All @@ -23,6 +24,7 @@ struct ContentView: View {
init(dependencies: AppDependencies) {
self.dependencies = dependencies
_partViewModel = StateObject(wrappedValue: dependencies.makePartViewModel())
_dashboardViewModel = StateObject(wrappedValue: DashboardViewModel(getOrderUseCase: dependencies.getOrderUseCase))
}

// MARK: - Body
Expand All @@ -33,9 +35,27 @@ struct ContentView: View {
.ignoresSafeArea(.all)

TabView(selection: $selectedTab) {
// Dashboard 탭 (임시)
// Dashboard 탭 (DashboardView directly)
Tab(value: .dashboard) {
DashboardScreen(dependencies: dependencies)
NavigationStack {
DashboardView(
viewModel: dashboardViewModel,
onLogoutClick: {
Task { await dependencies.authViewModel.signOut() }
},
onNavigateOrderDetail: { order in
selectedTab = .orders
DispatchQueue.main.async {
ordersNavigationPath.append(order.orderId)
}
},
onNavigateOrderList: {
selectedTab = .orders
},
userName: ((try? dependencies.authPreferences.getStoredUser())?.name) ?? "",
branch: ((try? dependencies.authPreferences.getStoredUser())?.branch) ?? ""
)
}
} label: {
Label {
Text(StringResources.Tabs.dashboard)
Expand Down
43 changes: 0 additions & 43 deletions SampoomManagement/App/Screens/DashboardScreen.swift

This file was deleted.

5 changes: 4 additions & 1 deletion SampoomManagement/Core/Network/TokenRefreshService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ class TokenRefreshService {
role: existingUser.role,
accessToken: dto.accessToken,
refreshToken: dto.refreshToken,
expiresIn: dto.expiresIn
expiresIn: dto.expiresIn,
position: existingUser.position,
workspace: existingUser.workspace,
branch: existingUser.branch
)

try authPreferences.saveUser(updatedUser)
Expand Down
13 changes: 13 additions & 0 deletions SampoomManagement/Core/Resources/StringResources.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@ struct StringResources {
static let title = "SampoomManagement"
}

// MARK: - Dashboard
struct Dashboard {
static let greetingPrefix = "안녕하세요, "
static let greetingSuffix = " 님"
static let intro = "오늘도 효율적인 재고 관리를 시작해보세요."
static let employee = "직원 관리"
static let partsOnHand = "보유 부품"
static let partsInProgress = "진행중 주문"
static let shortageOfParts = "부족 부품"
static let orderAmount = "주문 금액"
static let recentOrdersTitle = "최근 주문"
}

// MARK: - Tabs
struct Tabs {
static let dashboard = "대시보드"
Expand Down
12 changes: 11 additions & 1 deletion SampoomManagement/Core/UI/Components/CommonTextField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ struct CommonTextField: View {
let isError: Bool
let errorMessage: String?
let onTextChange: (String) -> Void
let submitLabel: SubmitLabel
let onSubmit: () -> Void

init(
value: Binding<String>,
Expand All @@ -67,7 +69,9 @@ struct CommonTextField: View {
size: TextFieldSize = .medium,
isError: Bool = false,
errorMessage: String? = nil,
onTextChange: @escaping (String) -> Void = { _ in }
onTextChange: @escaping (String) -> Void = { _ in },
submitLabel: SubmitLabel = .next,
onSubmit: @escaping () -> Void = {}
) {
self._value = value
self.placeholder = placeholder
Expand All @@ -76,6 +80,8 @@ struct CommonTextField: View {
self.isError = isError
self.errorMessage = errorMessage
self.onTextChange = onTextChange
self.submitLabel = submitLabel
self.onSubmit = onSubmit
}

var body: some View {
Expand All @@ -85,13 +91,17 @@ struct CommonTextField: View {
Group {
if type == .password && !isPasswordVisible {
SecureField(placeholder, text: $value)
.submitLabel(submitLabel)
.onSubmit { onSubmit() }
.textFieldStyle(PlainTextFieldStyle())
.focused($isFocused)
} else {
TextField(placeholder, text: $value)
.keyboardType(keyboardType)
.textInputAutocapitalization(autocapitalization)
.disableAutocorrection(disableAutocorrection)
.submitLabel(submitLabel)
.onSubmit { onSubmit() }
.textFieldStyle(PlainTextFieldStyle())
.focused($isFocused)
}
Expand Down
41 changes: 41 additions & 0 deletions SampoomManagement/Core/UI/Components/OrderItem.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// OrderItem.swift
// SampoomManagement
//
// Created by Generated.
//

import SwiftUI

struct OrderItem: View {
let order: Order
let onClick: () -> Void

var body: some View {
Button(action: onClick) {
HStack(alignment: .center, spacing: 12) {
VStack(alignment: .leading, spacing: 4) {
Text(OrderFormatter.buildOrderTitle(order))
.font(.gmarketBody)
.lineLimit(1)
.foregroundColor(.text)
Text(order.agencyName ?? "-")
.font(.gmarketCaption)
.foregroundColor(.textSecondary)
}
Spacer(minLength: 12)
VStack(alignment: .trailing, spacing: 6) {
Text(order.createdAt ?? "-")
.font(.gmarketCaption)
.foregroundColor(.textSecondary)
StatusChip(status: order.status.rawValue)
}
}
.padding(16)
.background(Color.backgroundCard)
.clipShape(RoundedRectangle(cornerRadius: 12))
}
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ class AuthPreferences {
static let userName = "auth.userName"
static let userRole = "auth.userRole"
static let expiresIn = "auth.expiresIn"
static let position = "auth.position"
static let workspace = "auth.workspace"
static let branch = "auth.branch"
}

func saveUser(_ user: User) throws {
Expand All @@ -27,6 +30,9 @@ class AuthPreferences {
try keychain.save(user.name, for: Keys.userName)
try keychain.save(user.role, for: Keys.userRole)
try keychain.save(String(user.expiresIn), for: Keys.expiresIn)
try keychain.save(user.position, for: Keys.position)
try keychain.save(user.workspace, for: Keys.workspace)
try keychain.save(user.branch, for: Keys.branch)
} catch {
// 부분 저장 실패 시 롤백
try? keychain.delete(Keys.accessToken)
Expand All @@ -35,6 +41,9 @@ class AuthPreferences {
try? keychain.delete(Keys.userName)
try? keychain.delete(Keys.userRole)
try? keychain.delete(Keys.expiresIn)
try? keychain.delete(Keys.position)
try? keychain.delete(Keys.workspace)
try? keychain.delete(Keys.branch)
throw error
}
}
Expand Down Expand Up @@ -63,14 +72,21 @@ class AuthPreferences {
let expiresIn = Int(expiresInString) else {
return nil
}
// Tolerate missing profile keys by defaulting to empty strings
let position = (try? keychain.get(Keys.position)) ?? ""
let workspace = (try? keychain.get(Keys.workspace)) ?? ""
let branch = (try? keychain.get(Keys.branch)) ?? ""

return User(
id: userId,
name: userName,
role: userRole,
accessToken: accessToken,
refreshToken: refreshToken,
expiresIn: expiresIn
expiresIn: expiresIn,
position: position,
workspace: workspace,
branch: branch
)
} catch {
print("AuthPreferences - 사용자 정보 조회 실패: \(error)")
Expand Down Expand Up @@ -113,6 +129,9 @@ class AuthPreferences {
try keychain.delete(Keys.userName)
try keychain.delete(Keys.userRole)
try keychain.delete(Keys.expiresIn)
try keychain.delete(Keys.position)
try keychain.delete(Keys.workspace)
try keychain.delete(Keys.branch)
} catch {
// 로그아웃 시에는 실패해도 에러를 던지지 않음 (이미 로그아웃 상태로 간주)
print("AuthPreferences - 키체인 삭제 실패: \(error)")
Expand Down
39 changes: 37 additions & 2 deletions SampoomManagement/Features/Auth/Data/Mappers/AuthMappers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,46 @@ extension LoginResponseDTO {
func toModel() -> User {
return User(
id: self.userId,
name: self.userName,
name: self.userName ?? "",
role: self.role,
accessToken: self.accessToken,
refreshToken: self.refreshToken,
expiresIn: self.expiresIn
expiresIn: self.expiresIn,
position: "",
workspace: "",
branch: ""
)
}
}

extension GetProfileResponseDTO {
func toModel() -> User {
return User(
id: self.userId,
name: self.userName ?? "",
role: "",
accessToken: "",
refreshToken: "",
expiresIn: 0,
position: self.position ?? "",
workspace: self.workspace ?? "",
branch: self.branch ?? ""
)
}
}

extension User {
func mergeWith(profile: User) -> User {
return User(
id: self.id,
name: profile.name,
role: self.role,
accessToken: self.accessToken,
refreshToken: self.refreshToken,
expiresIn: self.expiresIn,
position: profile.position,
workspace: profile.workspace,
branch: profile.branch
)
}
}
10 changes: 10 additions & 0 deletions SampoomManagement/Features/Auth/Data/Remote/API/AuthAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,15 @@ class AuthAPI {
responseType: RefreshResponseDTO.self
)
}

// 프로필 조회
func getProfile() async throws -> APIResponse<GetProfileResponseDTO> {
return try await networkManager.request(
endpoint: "user/profile",
method: .get,
parameters: nil,
responseType: GetProfileResponseDTO.self
)
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// GetProfileResponseDTO.swift
// SampoomManagement
//
// Created by Generated.
//

import Foundation

struct GetProfileResponseDTO: Codable {
let userId: Int
let userName: String?
let workspace: String?
let branch: String?
let position: String?
}


Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Foundation

struct LoginResponseDTO: Codable {
let userId: Int
let userName: String
let userName: String?
let role: String
let accessToken: String
let refreshToken: String
Expand Down
Loading