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
45 changes: 34 additions & 11 deletions Projects/Core/DoriDesignSystem/Sources/DoriNavigationBar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public struct DoriNavigationBarConfig {
public enum CenterItem {
case none
case title(String)
case searchField(text: Binding<String>, placeholder: String)
case searchField(text: Binding<String>, placeholder: String, onClear: (@MainActor () -> Void)? = nil)
}

public enum TrailingItem {
Expand Down Expand Up @@ -131,6 +131,17 @@ public struct DoriNavigationBar: View {
.background(.doriWhite)
}

// MARK: - Helpers

private var searchFieldLeadingPadding: CGFloat {
if case .none = config.leading { return 8 }
return 52 // 8pt outer leading + 44pt back button
}

private var searchFieldTrailingPadding: CGFloat {
16 + CGFloat(config.trailing.count) * 44 // 16pt outer trailing + 44pt per trailing item
}

// MARK: - Leading

@ViewBuilder
Expand Down Expand Up @@ -163,16 +174,27 @@ public struct DoriNavigationBar: View {
.foregroundStyle(.doriBlack)
.frame(maxWidth: .infinity)

case .searchField(let text, let placeholder):
TextField(placeholder, text: text)
.pretendard(.body(.r3))
.padding(.horizontal, 12)
.frame(height: 36)
.background(
RoundedRectangle(cornerRadius: 8)
.fill(.grey100)
)
.padding(.horizontal, 8)
case .searchField(let text, let placeholder, let onClear):
HStack {
TextField(placeholder, text: text)
.pretendard(.body(.sb3))
.foregroundStyle(.doriBlack)

if !text.wrappedValue.isEmpty, let onClear {
Button(action: onClear) {
Image(systemName: "xmark.circle.fill")
.foregroundStyle(.grey400)
}
}
}
.padding(.horizontal, 12)
.frame(height: 36)
.background(
RoundedRectangle(cornerRadius: 8)
.fill(.grey100)
)
.padding(.leading, searchFieldLeadingPadding)
.padding(.trailing, searchFieldTrailingPadding)
}
}

Expand All @@ -187,6 +209,7 @@ public struct DoriNavigationBar: View {
case .iconButton(let image, let action):
Button(action: action) {
image
.foregroundStyle(.doriBlack)
.frame(width: 44, height: 44)
.contentShape(Rectangle())
}
Expand Down
11 changes: 6 additions & 5 deletions Projects/Feature/AddDori/Sources/Views/Page1NameTypeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,12 @@ struct Page1NameTypeView: View {
Array(store.searchResults.enumerated()),
id: \.offset
) { _, partner in
PartnerSearchResultRow(searchQuery: $store.searchQuery.sending(\.searchQueryChanged), partner: partner)
.contentShape(Rectangle())
.onTapGesture {
store.send(.partnerSelected(partner))
}
PartnerSearchResultRow(searchQuery: $store.searchQuery.sending(\.searchQueryChanged), partner: partner
)
.contentShape(Rectangle())
.onTapGesture {
store.send(.partnerSelected(partner))
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,16 @@ import DoriDesignSystem
import DoriNetwork
import DoriCore

struct PartnerSearchResultRow: View {
@Binding var searchQuery: String
let partner: Dori
public struct PartnerSearchResultRow: View {
@Binding public var searchQuery: String
public let partner: Dori

var body: some View {
public init(searchQuery: Binding<String>, partner: Dori) {
self._searchQuery = searchQuery
self.partner = partner
}

public var body: some View {
HStack(spacing: 6) {
HStack(spacing: 6) {
Text(highlightedName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,13 @@ public struct DoriListFeature {
case refresh
case fabTapped
case partnerTapped(PartnerSummary)
case searchTapped
case delegate(Delegate)

public enum Delegate: Equatable, Sendable {
case partnerTapped(PartnerSummary)
case fabTapped
case searchTapped
}
}

Expand Down Expand Up @@ -101,6 +103,9 @@ public struct DoriListFeature {
case .fabTapped:
return .send(.delegate(.fabTapped))

case .searchTapped:
return .send(.delegate(.searchTapped))

case .delegate:
return .none
}
Expand Down
12 changes: 11 additions & 1 deletion Projects/Feature/History/Sources/DoriList/DoriListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,17 @@ public struct DoriListView: View {
.padding(20)
}
.background(.grey100)
.doriNavigationBar(.titleWithActions("내역"))
.doriNavigationBar(
.titleWithActions(
"내역",
trailing: [
.iconButton(
image: Image(systemName: "magnifyingglass"),
action: { store.send(.searchTapped) }
)
]
)
)
.refreshable {
store.send(.refresh)
}
Expand Down
33 changes: 31 additions & 2 deletions Projects/Feature/History/Sources/HistoryAPIClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ extension HistoryAPIClient: DependencyKey {
]
},
searchPartners: { query in
[
let allPartners: [Dori] = [
Dori(
doriId: 1,
userId: 1,
Expand All @@ -219,8 +219,37 @@ extension HistoryAPIClient: DependencyKey {
isVisited: true,
memo: "",
createdAt: "2026-02-17T09:00:00"
),
Dori(
doriId: 100,
userId: 1,
partnerId: 100,
direction: .judori,
partnerName: "조카 1",
relationship: "가족",
eventType: "생일",
amount: 50_000,
eventDate: "2025-05-01",
isVisited: true,
memo: "",
createdAt: "2026-02-17T09:00:00"
),
Dori(
doriId: 101,
userId: 1,
partnerId: 101,
direction: .baddori,
partnerName: "조카1",
relationship: "친구",
eventType: "결혼식",
amount: 100_000,
eventDate: "2025-08-20",
isVisited: true,
memo: "",
createdAt: "2026-02-17T09:00:00"
)
].filter { $0.partnerName.contains(query) }
]
return allPartners.filter { $0.partnerName.contains(query) }
},
fetchPartnerDoriList: { partnerId in
PartnerDoriList(
Expand Down
92 changes: 60 additions & 32 deletions Projects/Feature/History/Sources/HistoryFeature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,45 +16,49 @@ import FeatureAddDori
@Reducer
public struct HistoryFeature {
public init() {}

// MARK: - Path Reducer

@Reducer
public enum Path {
case partnerHistory(PartnerDoriHistoryFeature)
case partnerDoriDetail(PartnerDoriDetailFeature)
case addDori(AddDoriFeature)
case editDori(EditDoriFeature)
case search(SearchFeature)
}

// MARK: - State

@ObservableState
public struct State {
public var doriList: DoriListFeature.State = .init()
public var path = StackState<Path.State>()
public init() {}
}

// MARK: - Action

public enum Action {
case doriList(DoriListFeature.Action)
case path(StackActionOf<Path>)
}

// MARK: - Reducer

public var body: some ReducerOf<Self> {
Scope(state: \.doriList, action: \.doriList) {
DoriListFeature()
}


// Scope(state: \.doriSearch, action: \.doriSearch) {
// SearchFeature()
// }

Reduce { state, action in
switch action {

// MARK: DoriList delegate


// MARK: DoriList delegate
case .doriList(.delegate(.partnerTapped(let partner))):
state.path.append(
.partnerHistory(
Expand All @@ -66,51 +70,73 @@ public struct HistoryFeature {
)
)
return .none

case .doriList(.delegate(.fabTapped)):
state.path.append(.addDori(AddDoriFeature.State()))
return .none


case .doriList(.delegate(.searchTapped)):
state.path.append(.search(SearchFeature.State()))
return .none

case .doriList:
return .none

// MARK: Path delegate 처리

// PartnerDoriHistory에서 doriTapped → Detail push
// MARK: Path delegate 처리
// PartnerDoriHistory에서 doriTapped → Detail push
case .path(.element(id: _, action: .partnerHistory(.doriTapped(let dori)))):
state.path.append(.partnerDoriDetail(PartnerDoriDetailFeature.State(dori: dori)))
return .none

// PartnerDoriHistory에서 전체 삭제
// PartnerDoriHistory에서 전체 삭제
case .path(.element(id: _, action: .partnerHistory(.delegate(.allDoriDeleted)))):
state.path.removeAll()
return .send(.doriList(.refresh))

// PartnerDoriDetail에서 editTapped → Edit push
// PartnerDoriDetail에서 editTapped → Edit push
case .path(.element(id: _, action: .partnerDoriDetail(.delegate(.editTapped(let dori))))):
state.path.append(.editDori(EditDoriFeature.State(dori: dori)))
return .none

// PartnerDoriDetail에서 단건 삭제 → History로 복귀
// PartnerDoriDetail에서 단건 삭제 → History로 복귀
case .path(.element(id: _, action: .partnerDoriDetail(.delegate(.doriDeleted)))):
state.path.removeLast()
return .none

// AddDori 완료 → 리스트로 복귀
// AddDori 완료 → 리스트로 복귀
case .path(.element(id: _, action: .addDori(.delegate(.doriCreated(_))))):
state.path.removeAll()
return .send(.doriList(.refresh))

// AddDori dismiss
// AddDori dismiss
case .path(.element(id: _, action: .addDori(.delegate(.dismissed)))):
state.path.removeAll()
return .none

// EditDori 완료 → 리스트로 복귀
// EditDori 완료 → 리스트로 복귀
case .path(.element(id: _, action: .editDori(.delegate(.doriUpdated(_))))):
state.path.removeAll()
return .send(.doriList(.refresh))

// Search에서 partnerTapped → PartnerDoriHistory push
case .path(.element(id: _, action: .search(.delegate(.partnerTapped(let partner))))):
state.path.append(
.partnerHistory(
PartnerDoriHistoryFeature.State(
partnerId: partner.partnerId,
partnerName: partner.partnerName,
relationship: partner.relationship
)
)
)
return .none

// Search dismiss → 리스트로 복귀
case .path(.element(id: _, action: .search(.delegate(.dismissed)))):
state.path.removeLast()
return .none

case .path:
return .none
}
Expand All @@ -123,11 +149,11 @@ public struct HistoryFeature {

public struct HistoryView: View {
@Bindable var store: StoreOf<HistoryFeature>

public init(store: StoreOf<HistoryFeature>) {
self.store = store
}

public var body: some View {
NavigationStack(
path: $store.scope(state: \.path, action: \.path)
Expand All @@ -145,6 +171,8 @@ public struct HistoryView: View {
AddDoriView(store: addDoriStore)
case .editDori(let editDoriStore):
EditDoriView(store: editDoriStore)
case .search(let searchStore):
SearchView(store: searchStore)
}
}
}
Expand Down
Loading
Loading