import SwiftUI import SwiftData /// 매장 선택 후: 연도를 고르고 1~12월 그리드에서 달을 선택한다. struct YearMonthView: View { let store: Store @Query private var allRecords: [ProductRecord] @State private var year: Int init(store: Store) { self.store = store _year = State(initialValue: KoreanCalendar.calendar.component(.year, from: .now)) } private struct MonthRoute: Hashable { let year: Int let month: Int } private var storeRecords: [ProductRecord] { allRecords.filter { $0.store === store } } /// 이 연도의 월별 기록 개수 private var countsByMonth: [Int: Int] { var counts: [Int: Int] = [:] for record in storeRecords { let c = KoreanCalendar.calendar.dateComponents([.year, .month], from: record.expiryDate) if c.year == year, let month = c.month { counts[month, default: 0] += 1 } } return counts } var body: some View { ZStack { Theme.bg.ignoresSafeArea() ScrollView { VStack(spacing: 24) { yearPicker monthGrid yearSummary } .padding(20) } } .navigationTitle(store.name) .navigationBarTitleDisplayMode(.inline) .navigationDestination(for: MonthRoute.self) { route in MonthCalendarView(store: store, year: route.year, month: route.month) } } // MARK: - 연도 선택 private var yearPicker: some View { HStack { yearButton(systemName: "chevron.left") { year -= 1 } Spacer() Text(verbatim: "\(year)년") .font(.system(size: 26, weight: .bold, design: .rounded)) .foregroundStyle(Theme.textPrimary) .contentTransition(.numericText()) Spacer() yearButton(systemName: "chevron.right") { year += 1 } } .padding(.top, 8) } private func yearButton(systemName: String, action: @escaping () -> Void) -> some View { Button { withAnimation(.snappy) { action() } Haptics.tap() } label: { Image(systemName: systemName) .font(.body.weight(.semibold)) .foregroundStyle(Theme.textPrimary) .frame(width: 40, height: 40) .background(Circle().fill(Theme.surface)) .overlay(Circle().strokeBorder(Theme.stroke, lineWidth: 1)) } } // MARK: - 월 그리드 private var monthGrid: some View { let columns = Array(repeating: GridItem(.flexible(), spacing: 12), count: 3) let now = KoreanCalendar.calendar.dateComponents([.year, .month], from: .now) return LazyVGrid(columns: columns, spacing: 12) { ForEach(1...12, id: \.self) { month in NavigationLink(value: MonthRoute(year: year, month: month)) { monthCard(month: month, isCurrent: now.year == year && now.month == month) } .buttonStyle(.plain) } } } private func monthCard(month: Int, isCurrent: Bool) -> some View { let count = countsByMonth[month] ?? 0 return VStack(alignment: .leading, spacing: 0) { Text(verbatim: "\(month)월") .font(.system(.title3, design: .rounded).weight(.bold)) .foregroundStyle(count > 0 ? Theme.textPrimary : Theme.textSecondary) Spacer(minLength: 18) if count > 0 { Text(verbatim: "\(count)개") .font(.caption.weight(.bold)) .foregroundStyle(Theme.onAccent) .padding(.horizontal, 9) .padding(.vertical, 4) .background(Capsule().fill(Theme.accent)) } else { Text("기록 없음") .font(.caption2) .foregroundStyle(Theme.textSecondary.opacity(0.6)) } } .padding(14) .frame(maxWidth: .infinity, alignment: .leading) .frame(height: 96) .background( RoundedRectangle(cornerRadius: 18, style: .continuous) .fill(Theme.surface) ) .overlay( RoundedRectangle(cornerRadius: 18, style: .continuous) .strokeBorder(isCurrent ? Theme.accent.opacity(0.8) : Theme.stroke, lineWidth: isCurrent ? 1.5 : 1) ) } // MARK: - 연도 요약 private var yearSummary: some View { let total = countsByMonth.values.reduce(0, +) return Group { if total > 0 { Text(verbatim: "\(year)년에 기록한 제품 \(total)개") .font(.footnote) .foregroundStyle(Theme.textSecondary) } } } }