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
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@
import SwiftUI

struct SelectedTreatmentView: View {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

뒤에 sheet 붙이는 네이밍으로 바꾸는건 어떨까요

@ObservedObject var viewModel: NoTreatmentViewModel
let selectedTreatments: [TreatmentEntity]
let removeTreatment: (TreatmentEntity) -> Void

private let itemHeight: CGFloat = 34.adjustedH
private let spacing: CGFloat = 16.adjustedH
private let maxVisibleCount = 3

private var scrollViewHeight: CGFloat {
let count = min(viewModel.selectedTreatments.count, maxVisibleCount)
let count = min(selectedTreatments.count, maxVisibleCount)
let contentHeight = CGFloat(count) * itemHeight + CGFloat(max(count - 1, 0)) * spacing
return contentHeight + 24.adjustedH
}
Expand Down Expand Up @@ -47,20 +48,20 @@ struct SelectedTreatmentView: View {
)
ScrollView(.vertical, showsIndicators: false) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스크롤되는것도 gif 한번만 첨부해주세요!

VStack(spacing: spacing) {
ForEach(viewModel.selectedTreatments, id: \.id) { treatment in
ForEach(selectedTreatments, id: \.id) { treatment in
TreatmentRowView(
displayMode: .summary,
treatmentEntity: treatment,
isSelected: .constant(true),
action: { viewModel.removeTreatment(treatment) }
action: { removeTreatment(treatment) }
)
.frame(height: itemHeight)
}
}
.padding(.vertical, 14.adjustedH)
}
.frame(height: scrollViewHeight)
.scrollDisabled(viewModel.selectedTreatments.count <= maxVisibleCount)
.scrollDisabled(selectedTreatments.count <= maxVisibleCount)
.padding(.horizontal, 24.5.adjustedW)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//
// TreatmentSearchBarTextField.swift
// Cherrish-iOS
//
// Created by 어재선 on 1/16/26.
//

import SwiftUI

struct TreatmentSearchBarTextField: View {
@Binding var text: String
let enter: () -> Void
var isDisabled: Bool
var body: some View {

VStack {

Comment on lines +16 to +17
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 엔터좀여

HStack{
ZStack {
if text.isEmpty {
HStack{
TypographyText("원하시는 시술을 적어주세요.", style: .body1_r_14, color: .gray600)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

원하시는 -> 원하는

Spacer()
}
}
TextField("" ,text: $text)
.foregroundStyle(.gray1000)
.typography(.body1_m_14)
.multilineTextAlignment(.leading)
.tint(.gray1000)
.onSubmit {
enter()
}
Comment on lines +26 to +33
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

비활성 상태에서도 엔터 제출이 실행됩니다

isDisabled를 검색 비활성 의미로 쓰는 거라면, 버튼만 막는 것으론 onSubmit이 그대로 실행됩니다. 의도라면 TextField 비활성 + onSubmit 가드를 같이 두는 편이 안전합니다.

✅ 제안 수정
-                    TextField("" ,text: $text)
+                    TextField("" ,text: $text)
                         .foregroundStyle(.gray1000)
                         .typography(.body1_m_14)
                         .multilineTextAlignment(.leading)
                         .tint(.gray1000)
+                        .disabled(isDisabled)
                         .onSubmit {
-                            enter()
+                            guard !isDisabled else { return }
+                            enter()
                         }

Also applies to: 45-45

🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Components/TreatmentSearchBarTextField.swift`
around lines 26 - 33, The TextField's onSubmit currently fires even when
searches are meant to be disabled; update the component to both disable the
TextField (e.g., via .disabled(isDisabled) or equivalent) and add a guard inside
the onSubmit handler to no-op when isDisabled is true (guard !isDisabled else {
return }), referencing the existing TextField, its onSubmit closure, the enter()
call, and the isDisabled flag so both UI interaction and programmatic submit are
blocked.

}
.padding(.vertical, 8.adjustedH)
.padding(.leading, 16.5.adjustedW)
Button{
Comment on lines +35 to +37
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이부분 엔터해주세용

enter()
} label: {
ZStack {
Image(.search)
}
}
.padding(.leading, 8)
.disabled(isDisabled)
}
.padding(.trailing, 16.5.adjustedW)
}
.background{
RoundedRectangle(cornerRadius: 30)
.foregroundStyle(.gray200)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,16 @@ struct NoTreatmentView: View {
}
}
Spacer()

CherrishButton(title: "다음", type: .next, state: .constant(viewModel.canProceed ? .active : .normal)) {
viewModel.next()

Group {
if viewModel.state == .treatmentFilter {
if !viewModel.selectedTreatments.isEmpty {
SelectedTreatmentView(selectedTreatments: viewModel.selectedTreatments, removeTreatment: viewModel.removeTreatment(_:))
}
}
CherrishButton(title: "다음", type: .next, state: .constant(viewModel.canProceed ? .active : .normal)) {
viewModel.next()
}
.padding(.horizontal, 25.adjustedW)
}
.padding(.leading, 25.adjustedW)
.padding(.trailing, 24.adjustedW)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// Treatment.swift
// Cherrish-iOS
//
// Created by 어재선 on 1/16/26.
//

import Foundation

enum Treatment: Int, CaseIterable, Identifiable {
case targetDdaySetting = 1
case treatmentFilter
case downTimeSetting

var id: Self { self }

var title: String {
switch self {
case .targetDdaySetting:
return "목표 디데이 설정"
case .treatmentFilter:
return "시술 필터링"
case .downTimeSetting:
return "다운타임 설정"
}
}

var isFirst: Bool { self == Self.allCases.first }
var isLast: Bool { self == Self.allCases.last }
}

// MARK: - Navigation
extension Treatment {
mutating func next() {
let allCases = Self.allCases

guard let currentIndex = allCases.firstIndex(of: self),
currentIndex + 1 < allCases.count else { return }

self = allCases[allCases.index(after: currentIndex)]

}

mutating func previous() {
let allCases = Self.allCases

guard let currentIndex = allCases.firstIndex(of: self),
currentIndex > 0 else { return }

self = allCases[allCases.index(before: currentIndex)]

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//
// TreatmentFilterView.swift
// Cherrish-iOS
//
// Created by 어재선 on 1/16/26.
//

import SwiftUI

struct TreatmentFilterView: View {
@ObservedObject var viewModel: TreatmentViewModel
var body: some View {
VStack {
TreatmentSearchBarTextField(text: $viewModel.searchText, enter: { }, isDisabled: !viewModel.searchText.isEmpty)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

개행 + 엔터좀요

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

검색 버튼이 사실상 무동작/비활성입니다

enter가 빈 클로저이고 isDisabled!searchText.isEmpty라 입력 시 버튼이 비활성됩니다. 버튼을 유지할 거라면 실제 동작(즉시 검색/클리어 등)과 비활성 조건을 의도에 맞게 조정해 주세요.

✅ 최소 수정 예시
-        TreatmentSearchBarTextField(text: $viewModel.searchText, enter: { }, isDisabled: !viewModel.searchText.isEmpty)
+        TreatmentSearchBarTextField(
+            text: $viewModel.searchText,
+            enter: { viewModel.filterTreatments(by: viewModel.searchText) },
+            isDisabled: viewModel.searchText.isEmpty
+        )
🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Treatment/View/TreatmentFilterView.swift`
at line 14, The TreatmentSearchBarTextField currently passes an empty enter
closure and sets isDisabled to !viewModel.searchText.isEmpty which disables the
button when there is input; update the call so enter invokes the intended action
on the view model (e.g., viewModel.performSearch() or viewModel.clearSearch())
and fix the isDisabled logic to reflect the intended state (for example use
viewModel.searchText.isEmpty to disable when there is no input, or remove the
isDisabled prop if the button should always be enabled). Ensure you reference
TreatmentSearchBarTextField, the enter closure, and viewModel.searchText when
making the change.


ScrollView(.vertical, showsIndicators: false) {
HStack(alignment: .top,spacing: 4) {
TypographyText("◎", style: .body3_r_12, color: .gray600)

VStack(alignment: .leading, spacing: 0) {
TypographyText("본 정보는 인터넷 빅테이터 검색 및 분석을 통해 수집된 정보이며,공식적인 의료 정보가 아닙니다. ", style: .body3_r_12, color: .gray600)
.lineLimit(2)

}

Spacer()

}
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) }
)
}
}
Comment on lines +29 to +48
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

검색 결과 0건이면 전체 리스트로 되돌아가는 버그

현재는 filteredTreatments.isEmpty일 때 전체 목록을 보여서, 검색 결과가 없는 경우에도 필터가 적용되지 않은 것처럼 보입니다. searchText로 분기해서 “검색 중 / 기본 상태”를 구분해야 합니다.

🐛 제안 수정
-                if viewModel.filteredTreatments.isEmpty {
-                    ForEach(viewModel.treatments, id: \.id) { treatment in
+                if viewModel.searchText.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) }
                         )
                     }
                 }
🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Treatment/View/TreatmentFilterView.swift`
around lines 29 - 48, The list currently checks filteredTreatments.isEmpty which
makes an empty search show the full list; change the branching to check the
search state instead (use viewModel.searchText.isEmpty or the searchText
binding) so that when searchText is empty you iterate viewModel.treatments,
otherwise you always iterate viewModel.filteredTreatments (even if empty).
Update the conditional around the ForEach (the block that builds
TreatmentRowView using viewModel.isSelected and viewModel.addTreatment) to use
searchText.isEmpty as the condition and keep the same TreatmentRowView
parameters.

}

}
.padding(.horizontal, 24.5.adjustedW)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
//
// TreatmentView.swift
// Cherrish-iOS
//
// Created by 어재선 on 1/16/26.
//

import SwiftUI

struct TreatmentView: View {
@StateObject private var viewModel: TreatmentViewModel

init(viewModel: TreatmentViewModel = TreatmentViewModel()) {
_viewModel = StateObject(wrappedValue: viewModel)
}
var body: some View {
VStack {
CherrishNavigationBar(
title:viewModel.state.title,
leftButtonAction: {
viewModel.previous()
},
rightButtonAction: {

}
)

ProgressBar(
totalSteps: Treatment.allCases.count,
currentStep: .constant(viewModel.step)
)
.padding( .horizontal, 33.5.adjustedW)

VStack(spacing: 0){
Group {
switch viewModel.state {
case .targetDdaySetting:
TargetDdaySettingView(dDayState: $viewModel.dDay, year: $viewModel.year, month: $viewModel.month, day: $viewModel.day)
.padding( .leading, 34.adjustedW)
.padding( .trailing, 33.adjustedW)
.id(viewModel.state)
case .treatmentFilter:
TreatmentFilterView(viewModel: viewModel)
case .downTimeSetting:
DownTimeSettingView(treatments: viewModel.selectedTreatments)
}
}

Spacer()

Group {
if viewModel.state == .treatmentFilter {
if !viewModel.selectedTreatments.isEmpty {
SelectedTreatmentView(selectedTreatments: viewModel.selectedTreatments, removeTreatment: viewModel.removeTreatment(_:))
}
}

CherrishButton(title: "다음", type: .next, state: .constant(viewModel.canProceed ? .active : .normal)) {
viewModel.next()

}
.padding(.horizontal, 25.adjustedW)

}

Spacer()
.frame(height: 38.adjustedH)

}
.id(viewModel.step)
}
.ignoresSafeArea(.keyboard)
}
}

//MARK: - TreatmentSelectedCategoryView
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

MARK 주석 포맷을 SwiftLint 규칙에 맞게 수정하세요 (Line 76).

//MARK:// MARK: - ... 형태가 규칙입니다.

🧹 제안 수정
-//MARK: - TreatmentSelectedCategoryView
+// MARK: - TreatmentSelectedCategoryView
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
//MARK: - TreatmentSelectedCategoryView
// MARK: - TreatmentSelectedCategoryView
🧰 Tools
🪛 SwiftLint (0.57.0)

[Warning] 76-76: MARK comment should be in valid format. e.g. '// MARK: ...' or '// MARK: - ...'

(mark)

🤖 Prompt for AI Agents
In
`@Cherrish-iOS/Cherrish-iOS/Presentation/Feature/Calendar/Treatment/Treatment/View/TreatmentView.swift`
at line 76, Update the SwiftLint MARK comment formatting for the
TreatmentSelectedCategoryView section: replace the current comment token that
reads "//MARK: - TreatmentSelectedCategoryView" with the correct spaced format
"// MARK: - TreatmentSelectedCategoryView" so it conforms to the SwiftLint rule;
locate the comment near the TreatmentSelectedCategoryView section in
TreatmentView.swift and adjust the spacing around the MARK token.


private struct TreatmentSelectedCategory: View {
@ObservedObject var viewModel: NoTreatmentViewModel
let columns = [
GridItem(.flexible()),
GridItem(.flexible())
]
var body: some View {
VStack {
Spacer()
.frame(height: 70.adjustedH)
HStack(spacing: 0) {
VStack(alignment: .leading, spacing: 0) {
TypographyText(
"요즘 가장 신경 쓰이는 ",
style: .title1_sb_18,
color: .gray1000
)
TypographyText(
"피부 고민은 무엇인가요?",
style: .title1_sb_18,
color: .gray1000
)
TypographyText(
"선택한 고민을 기준으로 시술 정보를 정리해줘요.",
style: .body1_m_14,
color: .gray700
)
}
Spacer()
}
Spacer()
.frame(height: 40.adjustedH)
LazyVGrid(columns: columns, spacing: 12) {
ForEach(TreatmentCategory.allCases, id: \.id) { catagory in
SelectionChip(
title: catagory.title,
isSelected: Binding(
get: {
viewModel.treatmentCatagory == catagory
},
set: {
isSelected in
guard isSelected else {
return
}
viewModel.treatmentCatagory = catagory
}
)
)

}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// 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 })
}
}
Loading