feat(forms): migrate remaining native date inputs to <DatePicker> / <DateTimePicker>

Sweeps the last ~17 native `<Input type="date"|"datetime-local">`
call sites onto the shared `<DatePicker>` / `<DateTimePicker>`
primitives so date entry is uniform across the app (calendar popover
on desktop, native OS picker on mobile via the primitive's
viewport-aware fallback).

Three patterns handled:

  1. Controlled value/onChange — direct swap to <DatePicker
     value/onChange>:
       audit-log-list.tsx (audit-from / audit-to filters)
       reports/generate-report-form.tsx (date range)
       scan/scan-shell.tsx (expense date)
       reservations/reservation-detail.tsx (end-reservation dialog)
       shared/filter-bar.tsx ('date' filter variant)

  2. RHF `register('field')` pattern — wrapped in <Controller> with
     field.value/field.onChange bridge. The picker's '' → undefined
     normalisation kicks in via `field.onChange(v || undefined)`:
       berths/berth-form.tsx (tenureStartDate + tenureEndDate)
       reservations/berth-reserve-dialog.tsx (startDate)
       companies/add-membership-dialog.tsx (startDate)
       yachts/yacht-transfer-dialog.tsx (effectiveDate)
       invoices/invoice-detail.tsx (paymentDate)

  3. RHF + Date-typed schema — same Controller wrap, plus a
     Date<->YYYY-MM-DD bridge in the render() since the zod schema
     coerces these to Date:
       expenses/expense-form-dialog.tsx (expenseDate)
       companies/company-form.tsx (incorporationDate)

  4. Datetime variants — swapped onto <DateTimePicker>:
       interests/interest-contact-log-tab.tsx (occurredAt + followUpAt)

Skipped because they ARE picker primitives or internal date variants:
  - ui/date-picker.tsx, ui/date-time-picker.tsx (the primitives)
  - shared/inline-editable-field.tsx (the InlineEditableField date variant)
  - dashboard/date-range-picker.tsx (its own popover with min/max gating
    that doesn't map cleanly onto the shared primitive)

Removed now-unused Input imports from four files.

Verified: tsc clean, vitest 1448/1448.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-21 20:14:33 +02:00
parent f0dbefcac2
commit 0c6e7b72af
13 changed files with 141 additions and 78 deletions

View File

@@ -12,6 +12,7 @@ import { PageHeader } from '@/components/shared/page-header';
import { EmptyState } from '@/components/shared/empty-state'; import { EmptyState } from '@/components/shared/empty-state';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { DatePicker } from '@/components/ui/date-picker';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { import {
@@ -530,12 +531,11 @@ export function AuditLogList() {
<Label htmlFor="audit-from" className="text-xs"> <Label htmlFor="audit-from" className="text-xs">
From From
</Label> </Label>
<Input <DatePicker
id="audit-from" id="audit-from"
type="date"
className="w-44 h-9" className="w-44 h-9"
value={dateFrom} value={dateFrom}
onChange={(e) => setDateFrom(e.target.value)} onChange={setDateFrom}
/> />
</div> </div>
@@ -543,13 +543,7 @@ export function AuditLogList() {
<Label htmlFor="audit-to" className="text-xs"> <Label htmlFor="audit-to" className="text-xs">
To To
</Label> </Label>
<Input <DatePicker id="audit-to" className="w-44 h-9" value={dateTo} onChange={setDateTo} />
id="audit-to"
type="date"
className="w-44 h-9"
value={dateTo}
onChange={(e) => setDateTo(e.target.value)}
/>
</div> </div>
{/* M-AU03: CSV export inherits the current filter set. The {/* M-AU03: CSV export inherits the current filter set. The

View File

@@ -1,7 +1,7 @@
'use client'; 'use client';
import { useState } from 'react'; import { useState } from 'react';
import { useForm } from 'react-hook-form'; import { useForm, Controller } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query';
import { toast } from 'sonner'; import { toast } from 'sonner';
@@ -9,6 +9,7 @@ import { toast } from 'sonner';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { DatePicker } from '@/components/ui/date-picker';
import { import {
Select, Select,
SelectContent, SelectContent,
@@ -120,6 +121,7 @@ export function BerthForm({ berth, open, onOpenChange }: BerthFormProps) {
handleSubmit, handleSubmit,
setValue, setValue,
watch, watch,
control,
formState: { isSubmitting }, formState: { isSubmitting },
} = useForm<z.input<typeof updateBerthSchema>, unknown, UpdateBerthInput>({ } = useForm<z.input<typeof updateBerthSchema>, unknown, UpdateBerthInput>({
resolver: zodResolver(updateBerthSchema), resolver: zodResolver(updateBerthSchema),
@@ -449,11 +451,29 @@ export function BerthForm({ berth, open, onOpenChange }: BerthFormProps) {
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Label>Start Date</Label> <Label>Start Date</Label>
<Input type="date" {...register('tenureStartDate')} /> <Controller
control={control}
name="tenureStartDate"
render={({ field }) => (
<DatePicker
value={field.value ?? ''}
onChange={(v) => field.onChange(v || undefined)}
/>
)}
/>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Label>End Date</Label> <Label>End Date</Label>
<Input type="date" {...register('tenureEndDate')} /> <Controller
control={control}
name="tenureEndDate"
render={({ field }) => (
<DatePicker
value={field.value ?? ''}
onChange={(v) => field.onChange(v || undefined)}
/>
)}
/>
</div> </div>
</div> </div>
)} )}

View File

@@ -1,7 +1,7 @@
'use client'; 'use client';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form'; import { useForm, Controller } from 'react-hook-form';
import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query';
import { Loader2 } from 'lucide-react'; import { Loader2 } from 'lucide-react';
@@ -17,6 +17,7 @@ import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox'; import { Checkbox } from '@/components/ui/checkbox';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { DatePicker } from '@/components/ui/date-picker';
import { import {
Select, Select,
SelectContent, SelectContent,
@@ -69,6 +70,7 @@ export function AddMembershipDialog({ open, onOpenChange, companyId }: AddMember
watch, watch,
setValue, setValue,
reset, reset,
control,
formState: { errors, isSubmitting }, formState: { errors, isSubmitting },
} = useForm<FormValues>({ } = useForm<FormValues>({
defaultValues: { defaultValues: {
@@ -180,7 +182,14 @@ export function AddMembershipDialog({ open, onOpenChange, companyId }: AddMember
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="startDate">Start date</Label> <Label htmlFor="startDate">Start date</Label>
<Input id="startDate" type="date" {...register('startDate', { required: true })} /> <Controller
control={control}
name="startDate"
rules={{ required: true }}
render={({ field }) => (
<DatePicker id="startDate" value={field.value ?? ''} onChange={field.onChange} />
)}
/>
{errors.startDate && <p className="text-xs text-destructive">Required</p>} {errors.startDate && <p className="text-xs text-destructive">Required</p>}
</div> </div>

View File

@@ -1,7 +1,7 @@
'use client'; 'use client';
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { useForm } from 'react-hook-form'; import { useForm, Controller } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
@@ -11,6 +11,7 @@ import { z } from 'zod';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { DatePicker } from '@/components/ui/date-picker';
import { Textarea } from '@/components/ui/textarea'; import { Textarea } from '@/components/ui/textarea';
import { import {
Select, Select,
@@ -117,6 +118,7 @@ export function CompanyForm({ open, onOpenChange, company, prefill }: CompanyFor
watch, watch,
setValue, setValue,
reset, reset,
control,
formState: { errors, isSubmitting }, formState: { errors, isSubmitting },
} = useForm<CompanyFormValues>({ } = useForm<CompanyFormValues>({
resolver: zodResolver(createCompanySchema), resolver: zodResolver(createCompanySchema),
@@ -327,7 +329,27 @@ export function CompanyForm({ open, onOpenChange, company, prefill }: CompanyFor
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Label>Incorporation Date</Label> <Label>Incorporation Date</Label>
<Input type="date" {...register('incorporationDate')} /> <Controller
control={control}
name="incorporationDate"
render={({ field }) => {
// Schema coerces incorporationDate to a Date; the picker
// speaks YYYY-MM-DD. Bridge both directions so validation
// + downstream API payload stay unchanged.
const isoValue =
field.value instanceof Date
? field.value.toISOString().split('T')[0]
: typeof field.value === 'string'
? (field.value as string).split('T')[0]
: '';
return (
<DatePicker
value={isoValue}
onChange={(v) => field.onChange(v ? new Date(v) : undefined)}
/>
);
}}
/>
{errors.incorporationDate && ( {errors.incorporationDate && (
<p className="text-xs text-destructive">{errors.incorporationDate.message}</p> <p className="text-xs text-destructive">{errors.incorporationDate.message}</p>
)} )}

View File

@@ -1,7 +1,7 @@
'use client'; 'use client';
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { useForm } from 'react-hook-form'; import { useForm, Controller } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { AlertTriangle, Loader2, Upload, X } from 'lucide-react'; import { AlertTriangle, Loader2, Upload, X } from 'lucide-react';
@@ -10,6 +10,7 @@ import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox'; import { Checkbox } from '@/components/ui/checkbox';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { DatePicker } from '@/components/ui/date-picker';
import { Textarea } from '@/components/ui/textarea'; import { Textarea } from '@/components/ui/textarea';
import { FormErrorSummary } from '@/components/forms/form-error-summary'; import { FormErrorSummary } from '@/components/forms/form-error-summary';
import { useFormScrollToError } from '@/hooks/use-form-scroll-to-error'; import { useFormScrollToError } from '@/hooks/use-form-scroll-to-error';
@@ -69,6 +70,7 @@ export function ExpenseFormDialog({ open, onOpenChange, expense }: ExpenseFormDi
setValue, setValue,
reset, reset,
watch, watch,
control,
formState: { errors, isSubmitting }, formState: { errors, isSubmitting },
} = useForm<z.input<typeof createExpenseSchema>, unknown, CreateExpenseInput>({ } = useForm<z.input<typeof createExpenseSchema>, unknown, CreateExpenseInput>({
resolver: zodResolver(createExpenseSchema), resolver: zodResolver(createExpenseSchema),
@@ -230,17 +232,33 @@ export function ExpenseFormDialog({ open, onOpenChange, expense }: ExpenseFormDi
/> />
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="expenseDate">Date *</Label> <Label htmlFor="expenseDate">Date *</Label>
<Input <Controller
id="expenseDate" control={control}
type="date" name="expenseDate"
{...register('expenseDate', {
setValueAs: (v) => (v ? new Date(v) : undefined),
})}
defaultValue={ defaultValue={
expense?.expenseDate expense?.expenseDate
? new Date(expense.expenseDate).toISOString().split('T')[0] ? (new Date(expense.expenseDate) as unknown as Date)
: new Date().toISOString().split('T')[0] : (new Date() as unknown as Date)
} }
render={({ field }) => {
// Schema stores expenseDate as a Date; the picker speaks
// YYYY-MM-DD strings. Bridge both directions on the fly so
// upstream validation + downstream API payload stay
// unchanged.
const isoValue =
field.value instanceof Date
? field.value.toISOString().split('T')[0]
: typeof field.value === 'string'
? field.value.split('T')[0]
: '';
return (
<DatePicker
id="expenseDate"
value={isoValue}
onChange={(v) => field.onChange(v ? new Date(v) : undefined)}
/>
);
}}
/> />
{errors.expenseDate && ( {errors.expenseDate && (
<p className="text-xs text-destructive">{errors.expenseDate.message}</p> <p className="text-xs text-destructive">{errors.expenseDate.message}</p>

View File

@@ -42,7 +42,7 @@ import {
DropdownMenuItem, DropdownMenuItem,
DropdownMenuTrigger, DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'; } from '@/components/ui/dropdown-menu';
import { Input } from '@/components/ui/input'; import { DateTimePicker } from '@/components/ui/date-time-picker';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { Checkbox } from '@/components/ui/checkbox'; import { Checkbox } from '@/components/ui/checkbox';
import { import {
@@ -497,12 +497,7 @@ function ComposeDialogBody({
<div className="space-y-1"> <div className="space-y-1">
<Label htmlFor="cl-occurred">When did the conversation happen?</Label> <Label htmlFor="cl-occurred">When did the conversation happen?</Label>
<Input <DateTimePicker id="cl-occurred" value={occurredAt} onChange={setOccurredAt} />
id="cl-occurred"
type="datetime-local"
value={occurredAt}
onChange={(e) => setOccurredAt(e.target.value)}
/>
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
@@ -588,11 +583,10 @@ function ComposeDialogBody({
<Label htmlFor="cl-followup" className="text-xs text-muted-foreground"> <Label htmlFor="cl-followup" className="text-xs text-muted-foreground">
Remind me on Remind me on
</Label> </Label>
<Input <DateTimePicker
id="cl-followup" id="cl-followup"
type="datetime-local"
value={followUpAt} value={followUpAt}
onChange={(e) => setFollowUpAt(e.target.value)} onChange={setFollowUpAt}
className="max-w-xs" className="max-w-xs"
/> />
<p className="text-[11px] text-muted-foreground"> <p className="text-[11px] text-muted-foreground">

View File

@@ -4,7 +4,7 @@ import { useState, useEffect } from 'react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { Loader2, Send, CreditCard } from 'lucide-react'; import { Loader2, Send, CreditCard } from 'lucide-react';
import { useForm } from 'react-hook-form'; import { useForm, Controller } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { format } from 'date-fns'; import { format } from 'date-fns';
@@ -15,6 +15,7 @@ import { PermissionGate } from '@/components/shared/permission-gate';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { DatePicker } from '@/components/ui/date-picker';
import { import {
Select, Select,
SelectContent, SelectContent,
@@ -397,10 +398,16 @@ export function InvoiceDetail({ invoiceId }: InvoiceDetailProps) {
> >
<div className="space-y-1"> <div className="space-y-1">
<Label htmlFor="paymentDate">Payment Date</Label> <Label htmlFor="paymentDate">Payment Date</Label>
<Input <Controller
id="paymentDate" control={paymentForm.control}
type="date" name="paymentDate"
{...paymentForm.register('paymentDate')} render={({ field }) => (
<DatePicker
id="paymentDate"
value={field.value ?? ''}
onChange={(v) => field.onChange(v || undefined)}
/>
)}
/> />
{paymentForm.formState.errors.paymentDate && ( {paymentForm.formState.errors.paymentDate && (
<p className="text-xs text-destructive"> <p className="text-xs text-destructive">

View File

@@ -6,6 +6,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { DatePicker } from '@/components/ui/date-picker';
import { import {
Select, Select,
SelectContent, SelectContent,
@@ -148,23 +149,16 @@ export function GenerateReportForm() {
<div className="flex flex-wrap gap-4"> <div className="flex flex-wrap gap-4">
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="dateFrom">Date From (optional)</Label> <Label htmlFor="dateFrom">Date From (optional)</Label>
<Input <DatePicker
id="dateFrom" id="dateFrom"
type="date"
value={dateFrom} value={dateFrom}
onChange={(e) => setDateFrom(e.target.value)} onChange={setDateFrom}
className="w-auto" className="w-auto"
/> />
</div> </div>
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="dateTo">Date To (optional)</Label> <Label htmlFor="dateTo">Date To (optional)</Label>
<Input <DatePicker id="dateTo" value={dateTo} onChange={setDateTo} className="w-auto" />
id="dateTo"
type="date"
value={dateTo}
onChange={(e) => setDateTo(e.target.value)}
className="w-auto"
/>
</div> </div>
</div> </div>

View File

@@ -1,7 +1,7 @@
'use client'; 'use client';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form'; import { useForm, Controller } from 'react-hook-form';
import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query';
import { Loader2 } from 'lucide-react'; import { Loader2 } from 'lucide-react';
import { toast } from 'sonner'; import { toast } from 'sonner';
@@ -16,7 +16,7 @@ import {
} from '@/components/ui/dialog'; } from '@/components/ui/dialog';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { Input } from '@/components/ui/input'; import { DatePicker } from '@/components/ui/date-picker';
import { Textarea } from '@/components/ui/textarea'; import { Textarea } from '@/components/ui/textarea';
import { import {
Select, Select,
@@ -55,6 +55,7 @@ export function BerthReserveDialog({ open, onOpenChange, berthId }: BerthReserve
watch, watch,
setValue, setValue,
reset, reset,
control,
formState: { errors, isSubmitting }, formState: { errors, isSubmitting },
} = useForm<FormValues>({ } = useForm<FormValues>({
defaultValues: { defaultValues: {
@@ -186,7 +187,14 @@ export function BerthReserveDialog({ open, onOpenChange, berthId }: BerthReserve
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="startDate">Start date</Label> <Label htmlFor="startDate">Start date</Label>
<Input id="startDate" type="date" {...register('startDate', { required: true })} /> <Controller
control={control}
name="startDate"
rules={{ required: true }}
render={({ field }) => (
<DatePicker id="startDate" value={field.value ?? ''} onChange={field.onChange} />
)}
/>
{errors.startDate && <p className="text-xs text-destructive">Required</p>} {errors.startDate && <p className="text-xs text-destructive">Required</p>}
</div> </div>

View File

@@ -15,8 +15,8 @@ import {
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
} from '@/components/ui/dialog'; } from '@/components/ui/dialog';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { DatePicker } from '@/components/ui/date-picker';
import { PageHeader } from '@/components/shared/page-header'; import { PageHeader } from '@/components/shared/page-header';
import { StatusPill, type StatusPillStatus } from '@/components/ui/status-pill'; import { StatusPill, type StatusPillStatus } from '@/components/ui/status-pill';
import { EmptyState } from '@/components/ui/empty-state'; import { EmptyState } from '@/components/ui/empty-state';
@@ -96,13 +96,7 @@ function EndReservationDialog({ reservationId, open, onOpenChange }: EndReservat
<form onSubmit={handleSubmit} className="space-y-4 pt-2"> <form onSubmit={handleSubmit} className="space-y-4 pt-2">
<div className="space-y-1.5"> <div className="space-y-1.5">
<Label htmlFor="end-date">End date</Label> <Label htmlFor="end-date">End date</Label>
<Input <DatePicker id="end-date" value={endDate} onChange={setEndDate} />
id="end-date"
type="date"
value={endDate}
onChange={(e) => setEndDate(e.target.value)}
required
/>
</div> </div>
<DialogFooter> <DialogFooter>
<Button type="button" variant="outline" onClick={() => onOpenChange(false)}> <Button type="button" variant="outline" onClick={() => onOpenChange(false)}>

View File

@@ -9,6 +9,7 @@ import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea'; import { Textarea } from '@/components/ui/textarea';
import { DatePicker } from '@/components/ui/date-picker';
import { import {
Select, Select,
SelectContent, SelectContent,
@@ -239,13 +240,7 @@ function VerifyForm({
</div> </div>
<div className="space-y-1.5"> <div className="space-y-1.5">
<Label htmlFor="expenseDate">Date</Label> <Label htmlFor="expenseDate">Date</Label>
<Input <DatePicker id="expenseDate" value={expenseDate} onChange={setExpenseDate} />
id="expenseDate"
type="date"
value={expenseDate}
onChange={(e) => setExpenseDate(e.target.value)}
required
/>
</div> </div>
<div className="space-y-1.5"> <div className="space-y-1.5">
<Label htmlFor="category">Category</Label> <Label htmlFor="category">Category</Label>

View File

@@ -4,6 +4,7 @@ import { X, Filter, ChevronDown } from 'lucide-react';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { DatePicker } from '@/components/ui/date-picker';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { import {
@@ -272,10 +273,9 @@ function FilterField({
return ( return (
<div className="space-y-1"> <div className="space-y-1">
<Label className="text-xs">{definition.label}</Label> <Label className="text-xs">{definition.label}</Label>
<Input <DatePicker
type="date"
value={(value as string) ?? ''} value={(value as string) ?? ''}
onChange={(e) => onChange(e.target.value || undefined)} onChange={(v) => onChange(v || undefined)}
className="h-8" className="h-8"
/> />
</div> </div>

View File

@@ -1,7 +1,7 @@
'use client'; 'use client';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form'; import { useForm, Controller } from 'react-hook-form';
import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query';
import { Loader2 } from 'lucide-react'; import { Loader2 } from 'lucide-react';
import { toast } from 'sonner'; import { toast } from 'sonner';
@@ -16,8 +16,8 @@ import {
} from '@/components/ui/dialog'; } from '@/components/ui/dialog';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea'; import { Textarea } from '@/components/ui/textarea';
import { DatePicker } from '@/components/ui/date-picker';
import { import {
Select, Select,
SelectContent, SelectContent,
@@ -62,6 +62,7 @@ export function YachtTransferDialog({
watch, watch,
setValue, setValue,
reset, reset,
control,
formState: { errors, isSubmitting }, formState: { errors, isSubmitting },
} = useForm<FormValues>({ } = useForm<FormValues>({
defaultValues: { defaultValues: {
@@ -150,10 +151,17 @@ export function YachtTransferDialog({
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="effectiveDate">Effective date</Label> <Label htmlFor="effectiveDate">Effective date</Label>
<Input <Controller
id="effectiveDate" control={control}
type="date" name="effectiveDate"
{...register('effectiveDate', { required: true })} rules={{ required: true }}
render={({ field }) => (
<DatePicker
id="effectiveDate"
value={field.value ?? ''}
onChange={field.onChange}
/>
)}
/> />
{errors.effectiveDate && <p className="text-xs text-destructive">Required</p>} {errors.effectiveDate && <p className="text-xs text-destructive">Required</p>}
</div> </div>