116 lines
3.5 KiB
Swift
116 lines
3.5 KiB
Swift
//
|
|
// ContentView.swift
|
|
// Expiranner
|
|
//
|
|
// 메인 뷰: 연도 선택 + 월(1~12) 그리드
|
|
|
|
import SwiftUI
|
|
import SwiftData
|
|
|
|
struct ContentView: View {
|
|
@State private var selectedYear = Calendar.current.component(.year, from: Date())
|
|
@State private var showScanner = false
|
|
|
|
private let years = Array(2020...2040)
|
|
private let columns = Array(repeating: GridItem(.flexible()), count: 3)
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
ZStack(alignment: .bottomTrailing) {
|
|
VStack {
|
|
Picker("연도", selection: $selectedYear) {
|
|
ForEach(years, id: \.self) { year in
|
|
Text(String(format: "%d년", year)).tag(year)
|
|
}
|
|
}
|
|
.pickerStyle(.menu)
|
|
.font(.title2)
|
|
|
|
LazyVGrid(columns: columns, spacing: 16) {
|
|
ForEach(1...12, id: \.self) { month in
|
|
NavigationLink {
|
|
MonthCalendarView(year: selectedYear, month: month)
|
|
} label: {
|
|
MonthCell(year: selectedYear, month: month)
|
|
}
|
|
.buttonStyle(.plain)
|
|
}
|
|
}
|
|
.padding()
|
|
|
|
Spacer()
|
|
}
|
|
|
|
FloatingScanButton {
|
|
showScanner = true
|
|
}
|
|
}
|
|
.navigationTitle("Expiranner")
|
|
.fullScreenCover(isPresented: $showScanner) {
|
|
ScannerView(
|
|
year: Calendar.current.component(.year, from: Date()),
|
|
month: Calendar.current.component(.month, from: Date())
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 월 셀: 해당 월에 저장된 아이템 개수 표시
|
|
struct MonthCell: View {
|
|
let year: Int
|
|
let month: Int
|
|
|
|
@Query private var items: [ProductItem]
|
|
|
|
init(year: Int, month: Int) {
|
|
self.year = year
|
|
self.month = month
|
|
let (start, end) = Self.monthBounds(year: year, month: month)
|
|
_items = Query(filter: #Predicate<ProductItem> { $0.date >= start && $0.date < end })
|
|
}
|
|
|
|
static func monthBounds(year: Int, month: Int) -> (Date, Date) {
|
|
let calendar = Calendar.current
|
|
let start = calendar.date(from: DateComponents(year: year, month: month, day: 1)) ?? Date()
|
|
let end = calendar.date(byAdding: .month, value: 1, to: start) ?? start
|
|
return (start, end)
|
|
}
|
|
|
|
var body: some View {
|
|
VStack(spacing: 4) {
|
|
Text("\(month)월")
|
|
.font(.title3)
|
|
Text(items.isEmpty ? " " : "\(items.count)개")
|
|
.font(.caption)
|
|
.foregroundStyle(.red)
|
|
}
|
|
.frame(maxWidth: .infinity)
|
|
.padding(.vertical, 16)
|
|
.background(Color(.secondarySystemBackground))
|
|
.clipShape(RoundedRectangle(cornerRadius: 10))
|
|
}
|
|
}
|
|
|
|
/// 플로팅 액션 버튼 (카메라 스캐너 실행)
|
|
struct FloatingScanButton: View {
|
|
let action: () -> Void
|
|
|
|
var body: some View {
|
|
Button(action: action) {
|
|
Image(systemName: "camera.fill")
|
|
.font(.title2)
|
|
.foregroundStyle(.white)
|
|
.padding(18)
|
|
.background(Circle().fill(.blue))
|
|
.shadow(radius: 4)
|
|
}
|
|
.padding(24)
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
ContentView()
|
|
.modelContainer(for: ProductItem.self, inMemory: true)
|
|
}
|