import SwiftUI import SwiftData /// 이미 저장된 기록의 유통기한(날짜·시간)을 수정하는 시트. /// 촬영 플로우와 같은 규칙: 기본은 날짜만, 필요할 때만 '시간 추가'. struct RecordEditView: View { let record: ProductRecord @Environment(\.dismiss) private var dismiss @Environment(\.modelContext) private var context @State private var selectedDate: Date @State private var includeTime: Bool @State private var timeSelection: Date init(record: ProductRecord) { self.record = record _selectedDate = State(initialValue: record.expiryDate) _includeTime = State(initialValue: record.hasTime) _timeSelection = State(initialValue: record.hasTime ? record.expiryDate : (KoreanCalendar.calendar.date(bySettingHour: 18, minute: 0, second: 0, of: .now) ?? .now)) } var body: some View { NavigationStack { ZStack { Theme.bg.ignoresSafeArea() ScrollView { VStack(spacing: 16) { currentHeader datePickerCard timeRow saveButton } .padding(20) } .scrollIndicators(.hidden) } .navigationTitle("유통기한 수정") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .cancellationAction) { Button("취소") { dismiss() } } } } .presentationDragIndicator(.visible) } // MARK: - 현재 값 요약 private var currentHeader: some View { HStack(spacing: 12) { Group { if let image = UIImage(data: record.thumbnailData) { Image(uiImage: image) .resizable() .scaledToFill() } else { Theme.surfaceHi } } .frame(width: 56, height: 56) .clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous)) .overlay( RoundedRectangle(cornerRadius: 12, style: .continuous) .strokeBorder(Theme.stroke, lineWidth: 1) ) VStack(alignment: .leading, spacing: 3) { Text("현재 유통기한") .font(.caption) .foregroundStyle(Theme.textSecondary) Text(currentText) .font(.system(.headline, design: .rounded)) .foregroundStyle(Theme.textPrimary) } Spacer() } .padding(14) .cardStyle() } private var currentText: String { let formatter = DateFormatter() formatter.locale = Locale(identifier: "ko_KR") formatter.dateFormat = record.hasTime ? "M월 d일 (E) a h:mm" : "M월 d일 (E)" return formatter.string(from: record.expiryDate) } // MARK: - 날짜 선택 private var datePickerCard: some View { DatePicker( "유통기한 날짜", selection: $selectedDate, displayedComponents: .date ) .datePickerStyle(.graphical) .tint(Theme.accent) .environment(\.locale, Locale(identifier: "ko_KR")) .environment(\.calendar, KoreanCalendar.calendar) .padding(10) .cardStyle() } // MARK: - 시간 추가 (선택) @ViewBuilder private var timeRow: some View { if includeTime { HStack(spacing: 10) { Image(systemName: "clock.fill") .font(.footnote) .foregroundStyle(Theme.accent) Text("유통 시각") .font(.system(.subheadline, design: .rounded).weight(.semibold)) .foregroundStyle(Theme.textPrimary) Spacer() DatePicker( "", selection: $timeSelection, displayedComponents: .hourAndMinute ) .labelsHidden() .tint(Theme.accent) Button { withAnimation(.snappy) { includeTime = false } Haptics.tap() } label: { Image(systemName: "xmark.circle.fill") .font(.title3) .foregroundStyle(Theme.textSecondary) } .buttonStyle(.plain) } .padding(.horizontal, 14) .padding(.vertical, 10) .cardStyle() } else { Button { withAnimation(.snappy) { includeTime = true } Haptics.tap() } label: { Label("시간 추가", systemImage: "clock") .font(.system(.footnote, design: .rounded).weight(.semibold)) .foregroundStyle(Theme.textSecondary) .padding(.horizontal, 14) .padding(.vertical, 8) .background(Capsule().fill(Theme.surfaceHi)) } .buttonStyle(.plain) } } // MARK: - 저장 private var saveButton: some View { Button { save() } label: { Label("저장하기", systemImage: "checkmark.circle.fill") .font(.system(.body, design: .rounded).weight(.bold)) .foregroundStyle(Theme.onAccent) .frame(maxWidth: .infinity) .frame(height: 54) .background(Capsule().fill(Theme.accentGradient)) } .padding(.top, 4) } private func save() { let calendar = KoreanCalendar.calendar let base = calendar.startOfDay(for: selectedDate) if includeTime { let t = calendar.dateComponents([.hour, .minute], from: timeSelection) record.expiryDate = calendar.date( bySettingHour: t.hour ?? 0, minute: t.minute ?? 0, second: 0, of: base ) ?? base } else { record.expiryDate = base } record.hasTime = includeTime try? context.save() Haptics.success() dismiss() } }