diff --git a/Cherrish-iOS/Cherrish-iOS/Data/DataDependencyAssembler.swift b/Cherrish-iOS/Cherrish-iOS/Data/DataDependencyAssembler.swift index ffadc551..77af5573 100644 --- a/Cherrish-iOS/Cherrish-iOS/Data/DataDependencyAssembler.swift +++ b/Cherrish-iOS/Cherrish-iOS/Data/DataDependencyAssembler.swift @@ -35,5 +35,7 @@ final class DataDependencyAssembler: DependencyAssembler { DIContainer.shared.register(type: TreatmentInterface.self) { return DefaultTreatmentRepository(networkService: self.networkService, userDefaultService: self.userDefaultService) } + + } } diff --git a/Cherrish-iOS/Cherrish-iOS/Data/Model/ProcedureResponseDTO.swift b/Cherrish-iOS/Cherrish-iOS/Data/Model/ProcedureResponseDTO.swift new file mode 100644 index 00000000..b281f19e --- /dev/null +++ b/Cherrish-iOS/Cherrish-iOS/Data/Model/ProcedureResponseDTO.swift @@ -0,0 +1,36 @@ +// +// ProcedureResponseDTO.swift +// Cherrish-iOS +// +// Created by 어재선 on 1/20/26. +// + +import Foundation + +struct ProcedureDTO: Decodable { + let id: Int + let name: String + let worries: [TreatmentCategoryResponseDTO] + let minDowntimeDays: Int + let maxDowntimeDays: Int +} + +struct ProceduresResponseDTO: Decodable { + let procedures: [ProcedureDTO] +} + + +extension ProcedureDTO { + func toEntity() -> TreatmentEntity { + return TreatmentEntity( + id: id, + name: name, + benefits: worries.map { + $0.toEntity().title + }, + downtimeMin: minDowntimeDays, + downtimeMax: maxDowntimeDays, + setDowntime: nil + ) + } +} diff --git a/Cherrish-iOS/Cherrish-iOS/Data/Network/EndPoint/OnboardingAPI.swift b/Cherrish-iOS/Cherrish-iOS/Data/Network/EndPoint/OnboardingAPI.swift index 79a3c077..0986c70c 100644 --- a/Cherrish-iOS/Cherrish-iOS/Data/Network/EndPoint/OnboardingAPI.swift +++ b/Cherrish-iOS/Cherrish-iOS/Data/Network/EndPoint/OnboardingAPI.swift @@ -41,7 +41,7 @@ enum OnboardingAPI: EndPoint { return JSONEncoding.default } - var queryParameters: [String: String]? { + var queryParameters: [String: Any]? { return nil } diff --git a/Cherrish-iOS/Cherrish-iOS/Data/Network/EndPoint/TreatmentAPI.swift b/Cherrish-iOS/Cherrish-iOS/Data/Network/EndPoint/TreatmentAPI.swift index 79d6b9c5..780acfad 100644 --- a/Cherrish-iOS/Cherrish-iOS/Data/Network/EndPoint/TreatmentAPI.swift +++ b/Cherrish-iOS/Cherrish-iOS/Data/Network/EndPoint/TreatmentAPI.swift @@ -6,16 +6,19 @@ // import Foundation - import Alamofire enum TreatmentAPI: EndPoint { case fetchCategories(userId: Int) + case fetchProcedures(userId: Int, id: Int? = nil, text: String? = nil) var basePath: String { switch self { case .fetchCategories: return "/api" + + case .fetchProcedures: + return "/api" } } @@ -23,6 +26,8 @@ enum TreatmentAPI: EndPoint { switch self { case .fetchCategories: return "/worries" + case .fetchProcedures: + return "/procedures" } } @@ -30,6 +35,8 @@ enum TreatmentAPI: EndPoint { switch self { case .fetchCategories: return .get + case .fetchProcedures: + return .get } } @@ -37,6 +44,8 @@ enum TreatmentAPI: EndPoint { switch self { case .fetchCategories(let userId): return .withAuth(userID: userId) + case .fetchProcedures(let userId, _, _): + return .withAuth(userID: userId) } } @@ -45,6 +54,8 @@ enum TreatmentAPI: EndPoint { switch self { case .fetchCategories: return URLEncoding.default + case .fetchProcedures: + return URLEncoding.default } } @@ -52,6 +63,15 @@ enum TreatmentAPI: EndPoint { switch self { case .fetchCategories: return nil + case .fetchProcedures(_, let id, let text): + var params: [String: Any] = [:] + if let id = id { + params["worryId"] = id + } + if let text = text { + params["keyword"] = "\(text)" + } + return params } } @@ -59,7 +79,8 @@ enum TreatmentAPI: EndPoint { switch self { case .fetchCategories: return .none + case .fetchProcedures: + return .none } - } + } } - diff --git a/Cherrish-iOS/Cherrish-iOS/Data/Repository/TreatmentRepository.swift b/Cherrish-iOS/Cherrish-iOS/Data/Repository/TreatmentRepository.swift index 250a76ae..b9f3c230 100644 --- a/Cherrish-iOS/Cherrish-iOS/Data/Repository/TreatmentRepository.swift +++ b/Cherrish-iOS/Cherrish-iOS/Data/Repository/TreatmentRepository.swift @@ -26,9 +26,19 @@ struct DefaultTreatmentRepository: TreatmentInterface { ) return response.map { $0.toEntity() } } + + func fetchTreatment(id: Int?, keyword: String?) async throws -> [TreatmentEntity] { + let userId: Int = userDefaultService.load(key: .userID) ?? 1 + let response = try await networkService.request(TreatmentAPI.fetchProcedures(userId: userId,id: id, text: keyword), decodingType: ProceduresResponseDTO.self) + return response.procedures.map { $0.toEntity() } + } } struct MockTreatmentRepository: TreatmentInterface { + func fetchTreatment(id: Int?, keyword: String?) async throws -> [TreatmentEntity] { + return [] + } + func fetchCategories() async throws -> [TreatmentCategoryEntity] { return [ TreatmentCategoryEntity(id: 1, title: "피부결 ∙ 각질"), diff --git a/Cherrish-iOS/Cherrish-iOS/Domain/DomainDependencyAssembler.swift b/Cherrish-iOS/Cherrish-iOS/Domain/DomainDependencyAssembler.swift index 67f0d25f..04b9503a 100644 --- a/Cherrish-iOS/Cherrish-iOS/Domain/DomainDependencyAssembler.swift +++ b/Cherrish-iOS/Cherrish-iOS/Domain/DomainDependencyAssembler.swift @@ -45,12 +45,16 @@ final class DomainDependencyAssembler: DependencyAssembler { return DefaultCreateProfileUseCase(repository: onboardingRepository) } - guard let treatmentCategoryRepository = DIContainer.shared.resolve(type: TreatmentInterface.self) else { + guard let treatmentRepository = DIContainer.shared.resolve(type: TreatmentInterface.self) else { return } DIContainer.shared.register(type: FetchTreatmentCategoriesUseCase.self) { - return DefaultFetchTreatmentCategoriesUseCase(repository: treatmentCategoryRepository) + return DefaultFetchTreatmentCategoriesUseCase(repository: treatmentRepository) + } + + DIContainer.shared.register(type: FetchTreatmentsUseCase.self) { + return DefaultFetchTreatmentsUseCase(repository: treatmentRepository) } } } diff --git a/Cherrish-iOS/Cherrish-iOS/Domain/Interface/TreatmentInterface.swift b/Cherrish-iOS/Cherrish-iOS/Domain/Interface/TreatmentInterface.swift index 46930e80..f715e020 100644 --- a/Cherrish-iOS/Cherrish-iOS/Domain/Interface/TreatmentInterface.swift +++ b/Cherrish-iOS/Cherrish-iOS/Domain/Interface/TreatmentInterface.swift @@ -9,4 +9,5 @@ import Foundation protocol TreatmentInterface { func fetchCategories() async throws -> [TreatmentCategoryEntity] + func fetchTreatment(id: Int?, keyword: String?) async throws -> [TreatmentEntity] } diff --git a/Cherrish-iOS/Cherrish-iOS/Domain/Model/TreatmentEntity.swift b/Cherrish-iOS/Cherrish-iOS/Domain/Model/TreatmentEntity.swift index 3f5431cf..3f76c349 100644 --- a/Cherrish-iOS/Cherrish-iOS/Domain/Model/TreatmentEntity.swift +++ b/Cherrish-iOS/Cherrish-iOS/Domain/Model/TreatmentEntity.swift @@ -8,103 +8,17 @@ import Foundation struct TreatmentEntity: Identifiable, Equatable, Hashable { - var id: Self { self } + let id: Int let name: String let benefits: [String] let downtimeMin: Int let downtimeMax: Int + var setDowntime: Int? + + mutating func updateDowntime(_ value: Int) { + setDowntime = value + } } -extension TreatmentEntity { - static let mockData: [TreatmentEntity] = [ - // 피부결 · 각질 - TreatmentEntity( - name: "아쿠아필", - benefits: ["각질 제거", "모공 청소", "피부결 개선"], - downtimeMin: 0, - downtimeMax: 1 - ), - TreatmentEntity( - name: "크리스탈 필링", - benefits: ["각질 제거", "피부 톤 개선", "잔주름 완화"], - downtimeMin: 1, - downtimeMax: 3 - ), - - // 색소 · 잡티 - TreatmentEntity( - name: "피코토닝", - benefits: ["색소 침착 개선", "잡티 제거", "피부 톤 균일화"], - downtimeMin: 0, - downtimeMax: 1 - ), - TreatmentEntity( - name: "레이저토닝", - benefits: ["기미 개선", "색소 침착 완화", "피부 톤 업"], - downtimeMin: 1, - downtimeMax: 2 - ), - - // 홍조 - TreatmentEntity( - name: "브이빔 레이저", - benefits: ["홍조 완화", "혈관 축소", "피부 진정"], - downtimeMin: 1, - downtimeMax: 3 - ), - TreatmentEntity( - name: "엑셀브이", - benefits: ["홍조 개선", "안면홍조 치료", "혈관 병변 제거"], - downtimeMin: 2, - downtimeMax: 5 - ), - - // 탄력 · 주름 - TreatmentEntity( - name: "울쎄라", - benefits: ["리프팅", "탄력 개선", "주름 완화"], - downtimeMin: 3, - downtimeMax: 7 - ), - TreatmentEntity( - name: "써마지", - benefits: ["피부 탄력", "콜라겐 생성", "처진 피부 개선"], - downtimeMin: 0, - downtimeMax: 2 - ), - TreatmentEntity( - name: "보톡스", - benefits: ["주름 개선", "이마 주름", "미간 주름"], - downtimeMin: 0, - downtimeMax: 1 - ), - - // 모공 - TreatmentEntity( - name: "프락셀", - benefits: ["모공 축소", "피부 재생", "흉터 개선"], - downtimeMin: 5, - downtimeMax: 7 - ), - TreatmentEntity( - name: "모공보톡스", - benefits: ["모공 축소", "피지 조절", "매끈한 피부"], - downtimeMin: 0, - downtimeMax: 1 - ), - - // 트러블 - TreatmentEntity( - name: "PDT", - benefits: ["여드름 치료", "피지 조절", "염증 완화"], - downtimeMin: 2, - downtimeMax: 5 - ), - TreatmentEntity( - name: "압출 관리", - benefits: ["여드름 제거", "모공 청소", "피부 진정"], - downtimeMin: 1, - downtimeMax: 3 - ) - ] -} + + diff --git a/Cherrish-iOS/Cherrish-iOS/Domain/UseCase/FetchTreatmentsUseCase.swift b/Cherrish-iOS/Cherrish-iOS/Domain/UseCase/FetchTreatmentsUseCase.swift new file mode 100644 index 00000000..75aead51 --- /dev/null +++ b/Cherrish-iOS/Cherrish-iOS/Domain/UseCase/FetchTreatmentsUseCase.swift @@ -0,0 +1,25 @@ +// +// FetchTreatmentsUseCase.swift +// Cherrish-iOS +// +// Created by 어재선 on 1/20/26. +// + +import Foundation + +protocol FetchTreatmentsUseCase { + func execute(id: Int?, keyword: String?) async throws -> [TreatmentEntity] +} + +struct DefaultFetchTreatmentsUseCase: FetchTreatmentsUseCase { + + private let repository: TreatmentInterface + + init(repository: TreatmentInterface) { + self.repository = repository + } + + func execute(id: Int?, keyword: String?) async throws -> [TreatmentEntity] { + return try await repository.fetchTreatment(id: id, keyword: keyword) + } +} diff --git a/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/DownTimeSettingView.swift b/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/DownTimeSettingView.swift index 53f15a87..da3ad424 100644 --- a/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/DownTimeSettingView.swift +++ b/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/DownTimeSettingView.swift @@ -9,13 +9,14 @@ import SwiftUI struct DownTimeSettingView: View { @State var selectedTreatment: TreatmentEntity? = nil - var treatments: [TreatmentEntity] + @Binding var treatments: [TreatmentEntity] let setday: (year: Int, month: Int, day: Int) let today: (year: Int, month: Int, day: Int) + var body: some View { VStack { Spacer() - .frame(height: 30) + .frame(height: 30.adjustedH) HStack { TypographyText( @@ -27,71 +28,79 @@ struct DownTimeSettingView: View { Spacer() } + .padding(.horizontal, 25.adjustedW) ScrollView(.vertical, showsIndicators: false) { - Spacer() - .frame(height: 24.adjustedH) - ForEach(treatments, id: \.self) { treatment in - TreatmentRowView( - displayMode: .completeBoxView, - treatmentEntity: treatment, - isSelected: .constant( - selectedTreatment == treatment - - ), - isCompleted: .constant( - false - ), - action: { - selectedTreatment = treatment - }) - } - - Spacer() - .frame(height: 14.adjustedH) - - HStack(alignment: .top, spacing: 0) { - TypographyText( - "◎", - style: .body3_r_12, - color: .gray600 - ) + VStack { + Spacer() + .frame(height: 24.adjustedH) + ForEach(treatments, id: \.self) { treatment in + TreatmentRowView( + displayMode: .completeBoxView, + treatmentEntity: treatment, + isSelected: .constant( + selectedTreatment == treatment + + ), + isCompleted: .constant( + treatment.setDowntime == nil ? false : true + ), + action: { + selectedTreatment = treatment + }) + } - VStack(alignment: .leading, spacing: 0) { - TypographyText( - "본 정보는 의료 상담이나 진단을 대체하지 않으며,", - style: .body3_r_12, - color: .gray600 - ) - + Spacer() + .frame(height: 14.adjustedH) + + HStack(alignment: .top, spacing: 0) { TypographyText( - "실제 다운타임 및 회복 과정은 개인에 따라 다를 수 있습니다.", + "◎", style: .body3_r_12, color: .gray600 ) - TypographyText( - "정확한 내용은 의료진 상담을 통해 확인하세요.", - style: .body3_r_12, - color: .gray600 - ) + VStack(alignment: .leading, spacing: 0) { + TypographyText( + "본 정보는 의료 상담이나 진단을 대체하지 않으며,", + style: .body3_r_12, + color: .gray600 + ) + + TypographyText( + "실제 다운타임 및 회복 과정은 개인에 따라 다를 수 있습니다.", + style: .body3_r_12, + color: .gray600 + ) + + TypographyText( + "정확한 내용은 의료진 상담을 통해 확인하세요.", + style: .body3_r_12, + color: .gray600 + ) + + } + Spacer() } - - Spacer() } + .padding(.horizontal, 25.adjustedW) } } - .padding(.horizontal, 25.adjustedW) .sheet(item: $selectedTreatment) { treatment in - DowntimeBottomSheetView( - treatment: treatment, - today: today, - setday: setday - ) - .presentationDetents([.extraLarge]) - .presentationBackground(.gray0) - .presentationDragIndicator(.visible) + if let index = treatments.firstIndex(where: { $0.id == treatment.id }) { + DowntimeBottomSheetView( + treatment: $treatments[index], + selectedTreatment: $selectedTreatment, + today: today, + setday: setday + ) + .presentationDetents([.extraLarge]) + .presentationBackground(.gray0) + .presentationDragIndicator(.visible) + } + + } } } diff --git a/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/DowntimeBottomSheetView.swift b/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/DowntimeBottomSheetView.swift index 1b446ee7..87b332c0 100644 --- a/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/DowntimeBottomSheetView.swift +++ b/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/DowntimeBottomSheetView.swift @@ -8,13 +8,15 @@ import SwiftUI struct DowntimeBottomSheetView: View { - let treatment: TreatmentEntity + @Binding var treatment: TreatmentEntity + @Binding var selectedTreatment: TreatmentEntity? let today: (year: Int, month: Int, day: Int) let setday: (year: Int, month: Int, day: Int) @State private var selectedDowntime: Int = 1 @State private var rate: Double = 0.0 @State private var betweenDays: Int = 0 + var body: some View { VStack(spacing: 0) { Spacer() @@ -61,7 +63,7 @@ struct DowntimeBottomSheetView: View { } extension DowntimeBottomSheetView { - + private var speechBubble: some View { ZStack { Image(.speechBubble) @@ -183,7 +185,11 @@ extension DowntimeBottomSheetView { ), leadingIcon: .none, trailingIcon: .none, - action: { }) + action: { + treatment.updateDowntime(0) + selectedTreatment = nil + + }) .frame(width: geo.size.width * 2/3 - 2) CherrishButton( @@ -194,7 +200,10 @@ extension DowntimeBottomSheetView { ), leadingIcon: .none, trailingIcon: .none, - action: { }) + action: { + treatment.updateDowntime(selectedDowntime) + selectedTreatment = nil + }) .frame( width: geo.size.width * 1/3 - 2 ) diff --git a/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/NoTreatment/NoTreatmentFilterView.swift b/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/NoTreatment/NoTreatmentFilterView.swift index 77111e4d..5b343f66 100644 --- a/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/NoTreatment/NoTreatmentFilterView.swift +++ b/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/NoTreatment/NoTreatmentFilterView.swift @@ -33,6 +33,9 @@ struct NoTreatmentFilterView: View { } } } + .task { + await viewModel.fetchNoTreatments() + } } } diff --git a/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/NoTreatment/NoTreatmentView.swift b/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/NoTreatment/NoTreatmentView.swift index 3cc85133..cf35b030 100644 --- a/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/NoTreatment/NoTreatmentView.swift +++ b/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/NoTreatment/NoTreatmentView.swift @@ -50,7 +50,7 @@ struct NoTreatmentView: View { } .ignoresSafeArea(.keyboard) .task { - await viewModel.loadCategories() + await viewModel.fetchCategories() } .onAppear { tabBarCoordinator.isTabbarHidden = true @@ -80,7 +80,7 @@ struct NoTreatmentView: View { case .downTimeSetting: DownTimeSettingView( - treatments: viewModel.selectedTreatments, + treatments: $viewModel.selectedTreatments, setday: ( viewModel.toInt( viewModel.year diff --git a/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/TargetDdaySettingView.swift b/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/TargetDdaySettingView.swift index f33536d1..17af9bc4 100644 --- a/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/TargetDdaySettingView.swift +++ b/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/TargetDdaySettingView.swift @@ -96,6 +96,7 @@ struct TargetDdaySettingView: View { Spacer() } } + .padding(.horizontal, 34.adjustedW) } .scrollDismissesKeyboard(.interactively) } diff --git a/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/Treatment/TreatmentFilterView.swift b/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/Treatment/TreatmentFilterView.swift index 06cb07f9..013fb197 100644 --- a/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/Treatment/TreatmentFilterView.swift +++ b/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/Treatment/TreatmentFilterView.swift @@ -11,7 +11,16 @@ struct TreatmentFilterView: View { @ObservedObject var viewModel: TreatmentViewModel var body: some View { VStack { - TreatmentSearchBarTextField(text: $viewModel.searchText, enter: { }, isDisabled: !viewModel.searchText.isEmpty) + TreatmentSearchBarTextField( + text: $viewModel.searchText, + onTap: { + Task { + try await viewModel.fetchTreatments() + } + }, + isDisabled: false + ) + .padding(.horizontal, 25.adjustedW) ScrollView(.vertical, showsIndicators: false) { HStack(alignment: .top,spacing: 4) { @@ -23,7 +32,7 @@ struct TreatmentFilterView: View { style: .body3_r_12, color: .gray600 ) - .lineLimit(2) + .lineLimit(2) } @@ -31,30 +40,23 @@ struct TreatmentFilterView: View { } .frame(height: 34.adjustedH) - - if viewModel.filteredTreatments.isEmpty { - ForEach(viewModel.treatments, id: \.id) { treatment in - TreatmentRowView( - displayMode: .checkBoxView, - treatmentEntity: treatment, - isSelected: .constant(viewModel.isSelected(treatment)), - action: { viewModel.addTreatment(treatment) } - ) - } - } else { - ForEach(viewModel.filteredTreatments, id: \.id) { - treatment in - TreatmentRowView( - displayMode: .checkBoxView, - treatmentEntity: treatment, - isSelected: .constant(viewModel.isSelected(treatment)), - action: { viewModel.addTreatment(treatment) } - ) - } - } + .padding(.horizontal, 25.adjustedW) + ForEach(viewModel.treatments, id: \.id) { treatment in + TreatmentRowView( + displayMode: .checkBoxView, + treatmentEntity: treatment, + isSelected: .constant(viewModel.isSelected(treatment)), + action: { viewModel.addTreatment(treatment) } + ) + } .padding(.horizontal, 24.adjustedW) } } - .padding(.horizontal, 24.5.adjustedW) + .task { + Task { + try await viewModel.fetchTreatments() + } + } + } } diff --git a/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/Treatment/TreatmentView.swift b/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/Treatment/TreatmentView.swift index cea9a4e2..5781e5c5 100644 --- a/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/Treatment/TreatmentView.swift +++ b/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/Treatment/TreatmentView.swift @@ -10,8 +10,8 @@ import SwiftUI struct TreatmentView: View { @EnvironmentObject private var calendarCoordinator: CalendarCoordinator @EnvironmentObject private var tabBarCoordinator: TabBarCoordinator - @ObservedObject var viewModel: TreatmentViewModel - + @StateObject var viewModel: TreatmentViewModel + var body: some View { VStack(spacing: 0) { CherrishNavigationBar( @@ -36,7 +36,7 @@ struct TreatmentView: View { currentStep: .constant(viewModel.step) ) .padding(.horizontal, 33.5.adjustedW) - + Spacer() .frame(height: 20.adjustedH) @@ -51,13 +51,10 @@ struct TreatmentView: View { } .id(viewModel.step) } - .ignoresSafeArea(.keyboard,edges: .bottom) + .ignoresSafeArea(.keyboard ,edges: .bottom) .onTapGesture { hideKeyboard() } - .onAppear { - tabBarCoordinator.isTabbarHidden = true - } } @ViewBuilder @@ -70,8 +67,8 @@ struct TreatmentView: View { month: $viewModel.month, day: $viewModel.day ) - .padding(.leading, 34.adjustedW) - .padding(.trailing, 33.adjustedW) + + .id(viewModel.state) case .treatmentFilter: @@ -79,7 +76,7 @@ struct TreatmentView: View { case .downTimeSetting: DownTimeSettingView( - treatments: viewModel.selectedTreatments, + treatments: $viewModel.selectedTreatments, setday: ( viewModel.toInt( viewModel.year diff --git a/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/TreatmentSearchBarTextField.swift b/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/TreatmentSearchBarTextField.swift index 2245b34e..359ab5ca 100644 --- a/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/TreatmentSearchBarTextField.swift +++ b/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/View/TreatmentSearchBarTextField.swift @@ -9,7 +9,7 @@ import SwiftUI struct TreatmentSearchBarTextField: View { @Binding var text: String - let enter: () -> Void + let onTap: () -> Void var isDisabled: Bool var body: some View { @@ -30,14 +30,14 @@ struct TreatmentSearchBarTextField: View { .multilineTextAlignment(.leading) .tint(.gray1000) .onSubmit { - enter() + onTap() } } .frame(height: 20.adjustedH) .padding(.vertical, 8.adjustedH) Button{ - enter() + onTap() } label: { ZStack { Image(.search) diff --git a/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/NoTreatment/NoTreatmentViewModel+Filter.swift b/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/NoTreatment/NoTreatmentViewModel+Filter.swift deleted file mode 100644 index bea06940..00000000 --- a/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/NoTreatment/NoTreatmentViewModel+Filter.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// NoTreatmentViewModel+Filter.swift -// Cherrish-iOS -// -// Created by 어재선 on 1/16/26. -// - -import Foundation - -extension NoTreatmentViewModel { - - func addTreatment(_ treatment: TreatmentEntity) { - guard !isSelected(treatment) else { return } - selectedTreatments.append(treatment) - } - - func removeTreatment(_ treatment: TreatmentEntity) { - selectedTreatments.removeAll { $0.id == treatment.id } - } - func isSelected(_ treatment: TreatmentEntity) -> Bool { - selectedTreatments.contains(where: { $0.id == treatment.id }) - } -} diff --git a/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/NoTreatment/NoTreatmentViewModel.swift b/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/NoTreatment/NoTreatmentViewModel.swift index 23199495..0413318e 100644 --- a/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/NoTreatment/NoTreatmentViewModel.swift +++ b/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/NoTreatment/NoTreatmentViewModel.swift @@ -8,37 +8,39 @@ import Foundation final class NoTreatmentViewModel: ObservableObject{ - @Published var state: NoTreatmentStep = .treatmentSelectedCategory @Published private(set) var categories: [TreatmentCategoryEntity] = [] @Published private(set) var selectedCategory: TreatmentCategoryEntity? + @Published private(set) var treatments: [TreatmentEntity] = [] + @Published var selectedTreatments: [TreatmentEntity] = [] + @Published var state: NoTreatmentStep = .treatmentSelectedCategory @Published var dDay: DdayState? @Published var year: String = "" @Published var month: String = "" @Published var day: String = "" - @Published var treatments: [TreatmentEntity] = TreatmentEntity.mockData - @Published var selectedTreatments: [TreatmentEntity] = [] private let fetchCategoriesUseCase: FetchTreatmentCategoriesUseCase + private let fetchTreatmentsUseCase: FetchTreatmentsUseCase + + init(fetchCategoriesUseCase: FetchTreatmentCategoriesUseCase, fetchTreatmentsUseCase: FetchTreatmentsUseCase) { + self.fetchCategoriesUseCase = fetchCategoriesUseCase + self.fetchTreatmentsUseCase = fetchTreatmentsUseCase + } var step: Int { state.rawValue } var canProceed: Bool { - switch state { - case .treatmentSelectedCategory: - return selectedCategory != nil - case .targetDdaySetting: - return isDateTextFieldNotEmpty() - case .treatmentFilter: - return !selectedTreatments.isEmpty - case .downTimeSetting: - return true - } - } - - init(fetchCategoriesUseCase: FetchTreatmentCategoriesUseCase) { - self.fetchCategoriesUseCase = fetchCategoriesUseCase + switch state { + case .treatmentSelectedCategory: + return selectedCategory != nil + case .targetDdaySetting: + return isDateTextFieldNotEmpty() + case .treatmentFilter: + return !selectedTreatments.isEmpty + case .downTimeSetting: + return selectedTreatments.allSatisfy { $0.setDowntime != nil } } - + } + var today: (year: Int, month: Int, day: Int) { let calendar = Calendar.current let now = Date() @@ -58,17 +60,26 @@ final class NoTreatmentViewModel: ObservableObject{ } @MainActor - func loadCategories() async { - do { - categories = try await fetchCategoriesUseCase.execute() - } catch { - - } + func fetchCategories() async { + do { + categories = try await fetchCategoriesUseCase.execute() + } catch { + + } + } + + @MainActor + func fetchNoTreatments() async { + do { + treatments = try await fetchTreatmentsUseCase.execute(id: selectedCategory?.id, keyword: "") + } catch { + treatments = [] } + } func selectCategory(_ category: TreatmentCategoryEntity) { - selectedCategory = category - } + selectedCategory = category + } func toInt(_ value: String) -> Int { Int(value) ?? 0 @@ -94,5 +105,21 @@ final class NoTreatmentViewModel: ObservableObject{ return true } + +} + +extension NoTreatmentViewModel { + + func addTreatment(_ treatment: TreatmentEntity) { + guard !isSelected(treatment) else { return } + selectedTreatments.append(treatment) + } + + func removeTreatment(_ treatment: TreatmentEntity) { + selectedTreatments.removeAll { $0.id == treatment.id } + } + func isSelected(_ treatment: TreatmentEntity) -> Bool { + selectedTreatments.contains(where: { $0.id == treatment.id }) + } } diff --git a/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/Treatment/TreatmentViewModel+Filter.swift b/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/Treatment/TreatmentViewModel+Filter.swift deleted file mode 100644 index b7ce7fd1..00000000 --- a/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/Treatment/TreatmentViewModel+Filter.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// TreatmentViewModel+Filter.swift -// Cherrish-iOS -// -// Created by 어재선 on 1/16/26. -// - -import Foundation - -extension TreatmentViewModel { - - func addTreatment(_ treatment: TreatmentEntity) { - guard !isSelected(treatment) else { return } - selectedTreatments.append(treatment) - } - - func removeTreatment(_ treatment: TreatmentEntity) { - selectedTreatments.removeAll { $0.id == treatment.id } - } - func isSelected(_ treatment: TreatmentEntity) -> Bool { - selectedTreatments.contains(where: { $0.id == treatment.id }) - } -} diff --git a/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/Treatment/TreatmentViewModel+Search.swift b/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/Treatment/TreatmentViewModel+Search.swift deleted file mode 100644 index bac3eaa7..00000000 --- a/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/Treatment/TreatmentViewModel+Search.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// TreatmentViewModel+Search.swift -// Cherrish-iOS -// -// Created by 어재선 on 1/17/26. -// - -import Combine -import Foundation - -extension TreatmentViewModel { - - func setupSearch() { - $searchText - .debounce(for: .milliseconds(300), scheduler: RunLoop.main) - .removeDuplicates() - .sink { [weak self] text in - self?.filterTreatments(by: text) - } - .store(in: &cancellables) - } - - func filterTreatments(by text: String) { - if text.isEmpty { - filteredTreatments = treatments - } else { - filteredTreatments = treatments.filter { - $0.name.localizedCaseInsensitiveContains(text) - } - } - } - - func clearSearch() { - searchText = "" - } -} diff --git a/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/Treatment/TreatmentViewModel.swift b/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/Treatment/TreatmentViewModel.swift index 20ea6ad9..05288c9f 100644 --- a/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/Treatment/TreatmentViewModel.swift +++ b/Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/ViewModel/Treatment/TreatmentViewModel.swift @@ -9,19 +9,22 @@ import SwiftUI import Combine final class TreatmentViewModel: ObservableObject{ + @Published private(set) var treatments: [TreatmentEntity] = [] + @Published var selectedTreatments: [TreatmentEntity] = [] @Published var state: TreatmentStep = .targetDdaySetting @Published var dDay: DdayState? @Published var year: String = "" @Published var month: String = "" @Published var day: String = "" - @Published var treatments: [TreatmentEntity] = TreatmentEntity.mockData - @Published var selectedTreatments: [TreatmentEntity] = [] @Published var searchText = "" - @Published var filteredTreatments: [TreatmentEntity] = [] - var step: Int { state.rawValue } + private let fetchTreatmentsUseCase: FetchTreatmentsUseCase - var cancellables = Set() + init(fetchTreatmentsUseCase: FetchTreatmentsUseCase) { + self.fetchTreatmentsUseCase = fetchTreatmentsUseCase + } + + var step: Int { state.rawValue } var today: (year: Int, month: Int, day: Int) { let calendar = Calendar.current @@ -33,10 +36,6 @@ final class TreatmentViewModel: ObservableObject{ ) } - init() { - filteredTreatments = treatments - setupSearch() - } var canProceed: Bool { switch state { case .targetDdaySetting: @@ -44,10 +43,20 @@ final class TreatmentViewModel: ObservableObject{ case .treatmentFilter: return !selectedTreatments.isEmpty case .downTimeSetting: - return true + return selectedTreatments.allSatisfy { $0.setDowntime != nil } + } } + @MainActor + func fetchTreatments() async throws { + do { + treatments = try await fetchTreatmentsUseCase.execute(id: nil, keyword: searchText) + } catch { + treatments = [] + } + } + func next() { state.next() } @@ -73,6 +82,20 @@ final class TreatmentViewModel: ObservableObject{ return true } +} + + +extension TreatmentViewModel { - + func addTreatment(_ treatment: TreatmentEntity) { + guard !isSelected(treatment) else { return } + selectedTreatments.append(treatment) + } + + func removeTreatment(_ treatment: TreatmentEntity) { + selectedTreatments.removeAll { $0.id == treatment.id } + } + func isSelected(_ treatment: TreatmentEntity) -> Bool { + selectedTreatments.contains(where: { $0.id == treatment.id }) + } } diff --git a/Cherrish-iOS/Cherrish-iOS/Presentation/PresentationDependencyAssembler.swift b/Cherrish-iOS/Cherrish-iOS/Presentation/PresentationDependencyAssembler.swift index 2a017023..a6cc612a 100644 --- a/Cherrish-iOS/Cherrish-iOS/Presentation/PresentationDependencyAssembler.swift +++ b/Cherrish-iOS/Cherrish-iOS/Presentation/PresentationDependencyAssembler.swift @@ -53,16 +53,21 @@ final class PresentationDependencyAssembler: DependencyAssembler { return } + guard let fetchTreatmentsUseCase = DIContainer.shared.resolve(type: FetchTreatmentsUseCase.self) else { + return + } + DIContainer.shared.register(type: SelectTreatmentViewModel.self) { return SelectTreatmentViewModel() } DIContainer.shared.register(type: NoTreatmentViewModel.self) { - return NoTreatmentViewModel(fetchCategoriesUseCase: fetchTreatmentCategoriesUseCase) + return NoTreatmentViewModel(fetchCategoriesUseCase: fetchTreatmentCategoriesUseCase, fetchTreatmentsUseCase: fetchTreatmentsUseCase) } + DIContainer.shared.register(type: TreatmentViewModel.self) { - return TreatmentViewModel() + return TreatmentViewModel(fetchTreatmentsUseCase: fetchTreatmentsUseCase) } } }