// // MonthCalendarView.swift // Expiranner // // 월별 캘린더: 날짜 그리드 + 선택한 날의 아이템 목록 import SwiftUI import SwiftData struct MonthCalendarView: View { let year: Int let month: Int @Environment(\.modelContext) private var modelContext @Query private var items: [ProductItem] @State private var selectedDay: Int? @State private var showScanner = false @State private var editingItem: ProductItem? private let columns = Array(repeating: GridItem(.flexible()), count: 7) private let weekdaySymbols = ["일", "월", "화", "수", "목", "금", "토"] init(year: Int, month: Int) { self.year = year self.month = month let (start, end) = MonthCell.monthBounds(year: year, month: month) _items = Query( filter: #Predicate { $0.date >= start && $0.date < end }, sort: \ProductItem.date ) } private var daysInMonth: Int { let calendar = Calendar.current let start = calendar.date(from: DateComponents(year: year, month: month, day: 1)) ?? Date() return calendar.range(of: .day, in: .month, for: start)?.count ?? 30 } /// 1일의 요일 오프셋 (일요일 시작 기준) private var firstWeekdayOffset: Int { let calendar = Calendar.current let start = calendar.date(from: DateComponents(year: year, month: month, day: 1)) ?? Date() return calendar.component(.weekday, from: start) - 1 } private func day(of item: ProductItem) -> Int { Calendar.current.component(.day, from: item.date) } private var selectedDayItems: [ProductItem] { guard let selectedDay else { return [] } return items.filter { day(of: $0) == selectedDay } } var body: some View { ZStack(alignment: .bottomTrailing) { VStack { LazyVGrid(columns: columns, spacing: 8) { ForEach(weekdaySymbols, id: \.self) { symbol in Text(symbol) .font(.caption) .foregroundStyle(.secondary) } ForEach(0.. 0 ? .red : .clear) .frame(width: 6, height: 6) } .frame(maxWidth: .infinity) .padding(.vertical, 4) .background( RoundedRectangle(cornerRadius: 6) .fill(selectedDay == day ? Color(.systemGray4) : .clear) ) } .buttonStyle(.plain) } } .padding(.horizontal) if let selectedDay { List { Section("\(month)월 \(selectedDay)일 저장된 항목") { if selectedDayItems.isEmpty { Text("저장된 항목이 없습니다") .foregroundStyle(.secondary) } ForEach(selectedDayItems) { item in Button { editingItem = item } label: { HStack { if let data = item.imageData, let image = UIImage(data: data) { Image(uiImage: image) .resizable() .scaledToFill() .frame(width: 44, height: 44) .clipShape(RoundedRectangle(cornerRadius: 6)) } Text(item.name) Spacer() } } .buttonStyle(.plain) } .onDelete(perform: deleteItems) } } .listStyle(.insetGrouped) } else { Spacer() Text("날짜를 선택하면 저장된 항목이 표시됩니다") .foregroundStyle(.secondary) Spacer() } } FloatingScanButton { showScanner = true } } .navigationTitle("\(String(year))년 \(month)월") .navigationBarTitleDisplayMode(.inline) .fullScreenCover(isPresented: $showScanner) { ScannerView(year: year, month: month) } .sheet(item: $editingItem) { item in ItemEditView(item: item) } } private func deleteItems(at offsets: IndexSet) { let targets = offsets.map { selectedDayItems[$0] } for item in targets { print("항목 삭제: \(item.name)") modelContext.delete(item) } } } /// 저장된 항목의 날짜(연/월/일) 수정 뷰 struct ItemEditView: View { @Bindable var item: ProductItem @Environment(\.dismiss) private var dismiss var body: some View { NavigationStack { Form { Section("제품명") { TextField("제품명", text: $item.name) } Section("날짜 이동") { DatePicker("날짜", selection: $item.date, displayedComponents: .date) .datePickerStyle(.graphical) } } .navigationTitle("항목 수정") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .confirmationAction) { Button("완료") { print("항목 수정 완료: \(item.name) → \(item.date)") dismiss() } } } } } } #Preview { NavigationStack { MonthCalendarView(year: 2026, month: 7) } .modelContainer(for: ProductItem.self, inMemory: true) }