import { useState } from 'react' import { zodResolver } from '@hookform/resolvers/zod' import { useForm, useWatch } from 'react-hook-form' import { toast } from 'sonner' import { FormError, FormField } from '@/components/form-field' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { ApiError } from '@/lib/api' import { PageError, PageLoading } from '@/components/page' import { formatDate } from '@/lib/format' import { daysInclusive, isIsoDate, lastToEnd, suggestRanges } from '@/lib/period-dates' import { useCreatePeriod, usePeriods, useUpdatePeriod } from '@/lib/periods' import { t } from '@/lib/strings' import type { Period } from '@/types/Period' import { datesSchema, type DatesForm } from './schemas' import { StepActions, StepIntro } from './wizard-ui' const s = t.wizard.dates const monthName = new Intl.DateTimeFormat('tr-TR', { month: 'long', year: 'numeric' }) /** Hızlı seçim etiketi: takvim ayıysa "Aralık 2026", değilse "15.11.2026 – 14.12.2026" */ function rangeLabel(r: { start: string; last: string }) { const [y, m, d] = r.start.split('-').map(Number) const monthEnd = new Date(Date.UTC(y, m, 0)).toISOString().slice(0, 10) return d === 1 && r.last === monthEnd ? monthName.format(new Date(y, m - 1, 1)) : `${formatDate(r.start)} – ${formatDate(r.last)}` } /** * 1. adım: dönem yoksa açar, varsa (geri gelindiyse) günceller. * Öneriler son dönemin bitişinden başlar (erken kapatılan ay tekrar önerilmez). */ export function StepDates({ period, onDone }: { period: Period | null; onDone: () => void }) { const periods = usePeriods() if (periods.isPending) return if (periods.isError) return // Liste en yeni başta; sihirbazda açık dönem varsa (geri gelindi) o hariç tutulur const previous = periods.data.find((p) => p.id !== period?.id) return } function DatesStepForm({ period, prevEnd, onDone, }: { period: Period | null prevEnd?: string onDone: () => void }) { const create = useCreatePeriod() const update = useUpdatePeriod() const [today] = useState(() => new Date()) // render başına değil, bir kez const presets = suggestRanges(today, prevEnd).map((range) => ({ label: rangeLabel(range), range, })) const { register, control, handleSubmit, setValue, setError, formState: { errors, isSubmitting }, } = useForm({ resolver: zodResolver(datesSchema), mode: 'onTouched', defaultValues: period ? { start: period.start_date, last: period.last_date, name: period.name } : { ...presets[0].range, name: '' }, }) const [start, last] = useWatch({ control, name: ['start', 'last'] }) const days = isIsoDate(start) && isIsoDate(last) && last >= start ? daysInclusive(start, last) : null const onSubmit = handleSubmit(async (v) => { const end = lastToEnd(v.last) try { if (!period) { await create.mutateAsync({ start_date: v.start, end_date: end, name: v.name || undefined }) toast.success(s.created) } else if ( v.start !== period.start_date || end !== period.end_date || v.name !== period.name ) { await update.mutateAsync({ id: period.id, version: period.version, start_date: v.start, end_date: end, name: v.name, // boş → backend tarihlere göre otomatik ad verir }) } onDone() } catch (e) { setError('root', { message: e instanceof ApiError ? e.message : String(e) }) } }) return (
{presets.map((p) => { const selected = start === p.range.start && last === p.range.last return ( ) })}
{(f) => } {(f) => }
{days !== null && (

{s.days(days)}

)} {(f) => }
) }