Skip to content

Commit 538f8a3

Browse files
committed
[Feat] TraineeHomeView UI μž‘μ„±
1 parent 94a4dd1 commit 538f8a3

File tree

1 file changed

+180
-1
lines changed

1 file changed

+180
-1
lines changed

β€ŽTnT/Projects/Presentation/Sources/Home/Trainee/TraineeHomeView.swiftβ€Ž

Lines changed: 180 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,183 @@
66
// Copyright Β© 2025 yapp25thTeamTnT. All rights reserved.
77
//
88

9-
import Foundation
9+
import SwiftUI
10+
import ComposableArchitecture
11+
12+
import Domain
13+
import DesignSystem
14+
15+
/// νŠΈλ ˆμ΄λ‹ˆμ˜ 메인 ν™ˆ λ·°μž…λ‹ˆλ‹€
16+
public struct TraineeHomeView: View {
17+
18+
// MARK: μž„μ‹œ State
19+
@State var ispresented: Bool = false
20+
@State var selectedDate: Date = Date()
21+
@State var currentPage: Date = Date()
22+
@State var events: [Date: Int] = [:]
23+
@State var todaysSessionInfo: WorkoutListItemEntity? = .init(currentCount: 8, startDate: .now, endDate: .now, trainerProfileImageUrl: nil, trainerName: "κΉ€λ―Όμˆ˜", hasRecord: true)
24+
@State var records: [RecordListItemEntity] = [
25+
.init(type: .meal(type: .lunch), date: .now, title: "μžκ³ μ‹Άλ‹€", hasFeedBack: true, imageUrl: nil),
26+
.init(type: .meal(type: .dinner), date: .now, title: "μžκ³ μ‹Άλ‹€", hasFeedBack: false, imageUrl: "https://images.genius.com/8e0b15e4847f8e59db7dfda22b4db4ec.1000x1000x1.png"),
27+
.init(type: .meal(type: .morning), date: .now, title: "μžκ³ μ‹Άλ‹€", hasFeedBack: true, imageUrl: nil)
28+
]
29+
@State var toggleMode: Bool = true
30+
31+
32+
public init() {}
33+
34+
public var body: some View {
35+
ScrollView {
36+
VStack(spacing: 0) {
37+
CalendarSection()
38+
39+
RecordListSection()
40+
.background(Color.neutral100)
41+
42+
Spacer()
43+
}
44+
}
45+
.overlay(alignment: .bottomTrailing) {
46+
Button(action: {
47+
// TODO: STORE
48+
ispresented = true
49+
}, label: {
50+
Image(.icnPlus)
51+
.renderingMode(.template)
52+
.resizable()
53+
.tint(Color.common0)
54+
.frame(width: 24, height: 24)
55+
.padding(16)
56+
.background(Color.neutral900)
57+
.clipShape(.rect(cornerRadius: 16))
58+
})
59+
.padding(.bottom, 20)
60+
.padding(.trailing, 12)
61+
}
62+
.navigationBarBackButtonHidden()
63+
.sheet(isPresented: $ispresented) {
64+
TraineeRecordStartView(itemContents: [
65+
("πŸ‹πŸ»β€β™€οΈ", "개인 μš΄λ™", {
66+
// TODO: Store μ—°κ²°
67+
print("pop")
68+
}),
69+
("πŸ₯—", "식단", {
70+
// TODO: Store μ—°κ²°
71+
print("pop")
72+
})
73+
])
74+
.autoSizingBottomSheet()
75+
}
76+
}
77+
78+
// MARK: - Sections
79+
@ViewBuilder
80+
private func CalendarSection() -> some View {
81+
VStack(spacing: 16) {
82+
TCalendarHeader(
83+
currentPage: $currentPage,
84+
formatter: { TDateFormatUtility.formatter(for: .yyyyλ…„_MMμ›”).string(from: $0) },
85+
rightView: {
86+
Button(action: {
87+
// TODO: Store μ—°κ²°
88+
print("pop")
89+
toggleMode.toggle()
90+
}, label: {
91+
Image(.icnAlarm)
92+
.resizable()
93+
.scaledToFit()
94+
.frame(width: 24, height: 24)
95+
})
96+
}
97+
)
98+
99+
// Calendar + 금일 μˆ˜μ—… μΉ΄λ“œ
100+
VStack(spacing: 12) {
101+
TCalendarView(
102+
selectedDate: $selectedDate,
103+
currentPage: $currentPage,
104+
events: events,
105+
isWeekMode: toggleMode
106+
)
107+
.padding(.horizontal, 20)
108+
109+
if let todaysSessionInfo {
110+
TWorkoutCard(
111+
chipUIInfo: RecordType.session(count: todaysSessionInfo.currentCount).chipInfo,
112+
timeText: "\(TDateFormatUtility.formatter(for: .a_HHmm).string(from: todaysSessionInfo.startDate)) ~ \(TDateFormatUtility.formatter(for: .a_HHmm).string(from: todaysSessionInfo.endDate))",
113+
title: "\(todaysSessionInfo.trainerName) νŠΈλ ˆμ΄λ„ˆ",
114+
imgURL: .init(string: todaysSessionInfo.trainerProfileImageUrl ?? ""),
115+
hasRecord: todaysSessionInfo.hasRecord,
116+
footerTapAction: {
117+
// TODO: STORe
118+
print("얍ㅂ삐")
119+
}
120+
)
121+
.padding(.horizontal, 20)
122+
.padding(.bottom, 16)
123+
} else {
124+
SessionEmptyView()
125+
.padding(.horizontal, 20)
126+
.padding(.vertical, 16)
127+
}
128+
}
129+
130+
}
131+
.padding(.vertical, 12)
132+
133+
}
134+
135+
@ViewBuilder
136+
private func RecordListSection() -> some View {
137+
VStack(alignment: .leading, spacing: 0) {
138+
Text(TDateFormatUtility.formatter(for: .MMμ›”_dd일_EEEE).string(from: selectedDate))
139+
.typographyStyle(.heading3, with: .neutral800)
140+
.padding(20)
141+
142+
VStack(spacing: 12) {
143+
ForEach(records.indices, id: \.self) { index in
144+
let item = records[index]
145+
TRecordCard(
146+
chipUIInfo: item.type.chipInfo,
147+
timeText: TDateFormatUtility.formatter(for: .a_HHmm).string(from: item.date),
148+
title: "자고 μ‹Άμ–΄μš” μ§„μ§œλ‘œ γ„΄γ…‡γ…γ„Ήγ…γ„΄γ…‡λž˜γ…£γ…‘γ…•γ…—γ…γ…ˆγ„·;γ…γ…“γ…‘γ…—γ…γ…ˆγ„·λž˜γ…‘;γ…—γ…“γ…γ„·γ„Ήγ…ˆ;γ…γ…—γ…•γ…‘γ„·γ„Ήγ…γ…ˆ",
149+
imgURL: URL(string: item.imageUrl ?? ""),
150+
hasFeedback: item.hasFeedBack,
151+
footerTapAction: {
152+
// TODO: STORE
153+
print("pop\(index)")
154+
}
155+
)
156+
}
157+
}
158+
.padding(.horizontal, 16)
159+
}
160+
}
161+
}
162+
163+
private extension TraineeHomeView {
164+
/// μ˜ˆμ •λœ μˆ˜μ—…μ΄ μ—†μ–΄μš”
165+
struct SessionEmptyView: View {
166+
var body: some View {
167+
Text("μ˜ˆμ •λœ μˆ˜μ—…μ΄ μ—†μ–΄μš”")
168+
.typographyStyle(.label1Medium, with: .neutral400)
169+
.frame(maxWidth: .infinity)
170+
.padding(.vertical, 12)
171+
}
172+
}
173+
174+
/// 아직 기둝이 μ—†μ–΄μš”
175+
struct RecordEmptyView: View {
176+
var body: some View {
177+
VStack(spacing: 4) {
178+
Text("μΆ”κ°€ λ²„νŠΌμ„ 눌러 식사와 μš΄λ™μ„ κΈ°λ‘ν•΄λ³΄μ„Έμš”")
179+
.typographyStyle(.body2Bold, with: .neutral600)
180+
.frame(maxWidth: .infinity)
181+
182+
Text("μΆ”κ°€ λ²„νŠΌμ„ 눌러 식사와 μš΄λ™μ„ κΈ°λ‘ν•΄λ³΄μ„Έμš”")
183+
.typographyStyle(.label1Medium, with: .neutral400)
184+
.frame(maxWidth: .infinity)
185+
}
186+
}
187+
}
188+
}

0 commit comments

Comments
Β (0)