Facturas de cliente

This commit is contained in:
David Arranz 2025-10-15 11:40:22 +02:00
parent 3da0d3858f
commit dc49094f00
13 changed files with 317 additions and 191 deletions

View File

@ -1 +1 @@
export * from "./customer-invoice-edit-form";
export * from "./invoice-edit-form";

View File

@ -1,5 +1,6 @@
import { FieldErrors, useFormContext } from "react-hook-form";
import { FormDebug } from '@erp/core/components';
import { cn } from '@repo/shadcn-ui/lib/utils';
import { InvoiceFormData } from "../../schemas";
import { InvoiceBasicInfoFields } from "./invoice-basic-info-fields";
@ -14,7 +15,7 @@ interface CustomerInvoiceFormProps {
className: string;
}
export const CustomerInvoiceEditForm = ({
export const InvoiceEditForm = ({
formId,
onSubmit,
onError,
@ -27,18 +28,18 @@ export const CustomerInvoiceEditForm = ({
<section className={cn("space-y-6", className)}>
<div className="w-full border p-6 bg-background">
<InvoiceBasicInfoFields className="flex flex-col" />
<InvoiceRecipient className='lg:col-span-1 border p-6 bg-background' />
</div>
<div className='w-full grid grid-cols-1 lg:grid-cols-4 gap-6'>
<InvoiceItems className="col-start-1 lg:col-span-3 border p-6 bg-background -p-6" />
<InvoiceRecipient className='lg:col-span-1 border p-6 bg-background' />
<div className='w-full gap-6'>
<InvoiceItems className="border p-6 bg-background -p-6" />
</div>
<div className="w-full border p-6 bg-background">
<InvoiceTotals />
</div>
<div className="w-full border p-6 bg-background">
<FormDebug />
</div>
</section>
</form>

View File

@ -1,7 +1,7 @@
import { Button, Checkbox, TableCell, TableRow, Tooltip, TooltipContent, TooltipTrigger } from "@repo/shadcn-ui/components";
import { cn } from '@repo/shadcn-ui/lib/utils';
import { ArrowDownIcon, ArrowUpIcon, CopyIcon, Trash2Icon } from "lucide-react";
import { Control, Controller } from "react-hook-form";
import { Control, Controller, FieldValues } from "react-hook-form";
import { useInvoiceContext } from '../../../context';
import { useTranslation } from '../../../i18n';
import { CustomerInvoiceTaxesMultiSelect } from '../../customer-invoice-taxes-multi-select';
@ -10,9 +10,9 @@ import { HoverCardTotalsSummary } from './hover-card-total-summary';
import { PercentageInputField } from './percentage-input-field';
import { QuantityInputField } from './quantity-input-field';
export type ItemRowProps = {
export type ItemRowProps<TFieldValues extends FieldValues = FieldValues> = {
control: Control,
control: Control<TFieldValues>,
rowIndex: number;
isSelected: boolean;
isFirst: boolean;
@ -26,8 +26,7 @@ export type ItemRowProps = {
}
export const ItemRow = ({
export const ItemRow = <TFieldValues extends FieldValues = FieldValues>({
control,
rowIndex,
isSelected,
@ -38,7 +37,7 @@ export const ItemRow = ({
onDuplicate,
onMoveUp,
onMoveDown,
onRemove, }: ItemRowProps) => {
onRemove, }: ItemRowProps<TFieldValues>) => {
const { t } = useTranslation();
const { currency_code, language_code } = useInvoiceContext();

View File

@ -5,7 +5,7 @@ import * as z from "zod";
import { CustomerModalSelector } from "@erp/customers/components";
import { DevTool } from "@hookform/devtools";
import { DatePickerInputField, TextAreaField, TextField } from "@repo/rdx-ui/components";
import { TextAreaField, TextField } from "@repo/rdx-ui/components";
import {
Button,
Calendar,

View File

@ -10,7 +10,7 @@ import { useMemo } from 'react';
import { FieldErrors, FormProvider } from "react-hook-form";
import { useNavigate } from "react-router-dom";
import {
CustomerInvoiceEditForm,
InvoiceEditForm,
PageHeader
} from "../../components";
import { useInvoiceContext } from '../../context';
@ -104,7 +104,7 @@ export const InvoiceUpdateComp = ({
<AppContent>
<FormProvider {...form}>
<CustomerInvoiceEditForm
<InvoiceEditForm
formId="invoice-update-form"
onSubmit={handleSubmit}
onError={handleError}

View File

@ -1,7 +1,6 @@
import { ModuleClientParams } from "@erp/core/client";
import { lazy } from "react";
import { Outlet, RouteObject } from "react-router-dom";
import { CustomerUpdatePage } from "./pages/update";
// Lazy load components
const CustomersLayout = lazy(() =>
@ -22,11 +21,11 @@ export const CustomerRoutes = (params: ModuleClientParams): RouteObject[] => {
</CustomersLayout>
),
children: [
{ path: "", index: true, element: <CustomersList /> }, // index
/*{ path: "", index: true, element: <CustomersList /> }, // index
{ path: "list", element: <CustomersList /> },
{ path: "create", element: <CustomerAdd /> },
{ path: ":id", element: <CustomerView /> },
{ path: ":id/edit", element: <CustomerUpdatePage /> },
{ path: ":id/edit", element: <CustomerUpdatePage /> },*/
//
/*{ path: "create", element: <CustomersList /> },

View File

@ -48,7 +48,7 @@ export function TextAreaField<TFormValues extends FieldValues>({
name={name}
render={({ field, fieldState }) => {
return (
<Field data-invalid={fieldState.invalid} orientation={orientation} className={className}>
<Field data-invalid={fieldState.invalid} orientation={orientation} className={cn("gap-1", className)}>
{label && <FieldLabel className='text-xs text-muted-foreground text-nowrap' htmlFor={name}>{label}</FieldLabel>}
<Textarea

View File

@ -56,7 +56,7 @@ export function TextField<TFormValues extends FieldValues>({
name={name}
render={({ field, fieldState }) => {
return (
<Field data-invalid={fieldState.invalid} orientation={orientation} className={className}>
<Field data-invalid={fieldState.invalid} orientation={orientation} className={cn("gap-1", className)}>
{label && <FieldLabel className='text-xs text-muted-foreground text-nowrap' htmlFor={name}>{label}</FieldLabel>}
<Input

View File

@ -0,0 +1,87 @@
import { Button, Input } from '@repo/shadcn-ui/components';
import { cn } from "@repo/shadcn-ui/lib/utils";
// DateInputField.tsx
import { CalendarIcon, LockIcon, XIcon } from "lucide-react";
export function DateInputField({
id,
value,
onChange,
onBlurConfirm,
onClear,
placeholder,
disabled,
readOnly,
required,
hasError,
describedBy,
onOpenRequest,
triggerButton, // ← PopoverTrigger se inyecta aquí
}: {
id: string;
value: string;
onChange: (val: string) => void;
onBlurConfirm: () => void; // valida+normaliza en blur/Enter
onClear: () => void;
placeholder?: string;
disabled: boolean;
readOnly: boolean;
required: boolean;
hasError: boolean;
describedBy?: string;
onOpenRequest?: () => void;
triggerButton?: React.ReactNode;
}) {
return (
<div className="relative">
<Input
id={id}
type="text"
pattern="\d{2}/\d{2}/\d{4}"
value={value}
onChange={(e) => onChange(e.target.value)}
onBlur={onBlurConfirm}
onKeyDown={(e) => {
if (e.key === "Enter") {
e.preventDefault();
onBlurConfirm();
}
if ((e.altKey || e.metaKey) && e.key === "ArrowDown") {
onOpenRequest?.();
}
}}
readOnly={readOnly}
disabled={disabled}
aria-invalid={hasError || undefined}
aria-describedby={describedBy}
className={cn(
"text-ellipsis pr-12",
disabled && "bg-muted text-muted-foreground cursor-not-allowed",
readOnly && "bg-muted text-foreground cursor-default",
!disabled && !readOnly && "bg-white text-foreground",
hasError && "border-destructive ring-destructive"
)}
placeholder={placeholder}
/>
<div className="absolute inset-y-0 right-2 flex items-center gap-2 pr-1">
{!readOnly && !required && value && (
<Button
variant={'link'}
type='button'
size={"icon-sm"}
onClick={onClear}
aria-label="Clear date"
className="text-muted-foreground hover:text-destructive transition cursor-pointer -mr-3"
>
<XIcon className="size-4" />
</Button>
)}
{readOnly ? (
<LockIcon className="size-4" />
) : (
triggerButton ?? <CalendarIcon className="size-4" aria-hidden="true" />
)}
</div>
</div>
);
}

View File

@ -1,30 +1,22 @@
import {
Button,
Calendar,
Card,
CardAction,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
Field,
FieldDescription,
FieldError,
FieldLabel,
FormControl,
Input,
Popover,
PopoverContent,
PopoverTrigger
} from "@repo/shadcn-ui/components";
import { CalendarIcon, LockIcon, XIcon } from "lucide-react";
import { CalendarIcon } from "lucide-react";
import { cn } from "@repo/shadcn-ui/lib/utils";
import { format, isValid, parse } from "date-fns";
import { useEffect, useState } from "react";
import { useCallback, useEffect, useMemo, useState } from "react";
import { ControllerFieldState, ControllerRenderProps, FieldValues, Path, UseFormStateReturn } from "react-hook-form";
import { useTranslation } from "../../../locales/i18n.ts";
import { DateInputField } from './date-input-field.tsx';
import { DatePopoverCalendar } from './date-popover-calendar.tsx';
export type SUICalendarProps = Omit<React.ComponentProps<
@ -35,8 +27,6 @@ type DatePickerInputCompProps<TFormValues extends FieldValues = FieldValues> = S
fieldState: ControllerFieldState;
formState: UseFormStateReturn<TFormValues>;
htmlFor: string,
displayDateFormat: string; // e.g. "dd/MM/yyyy"
parseDateFormat: string; // e.g. "yyyy/MM/dd"
@ -54,13 +44,11 @@ type DatePickerInputCompProps<TFormValues extends FieldValues = FieldValues> = S
className?: string;
};
export function DatePickerInputComp<TFormValues>({
export function DatePickerInputComp<TFormValues extends FieldValues = FieldValues>({
field,
fieldState,
formState,
htmlFor,
parseDateFormat,
displayDateFormat,
@ -78,172 +66,138 @@ export function DatePickerInputComp<TFormValues>({
...calendarProps
}: DatePickerInputCompProps<TFormValues>) {
const { t } = useTranslation();
const isDisabled = disabled;
const isReadOnly = readOnly && !disabled;
const [open, setOpen] = useState(false);
const [displayValue, setDisplayValue] = useState("");
const [localError, setLocalError] = useState<string | null>(null);
const [open, setOpen] = useState(false); // Popover
const [displayValue, setDisplayValue] = useState<string>("");
const parsedDate = useMemo(() => {
if (!field.value) return null;
const d = parse(field.value, parseDateFormat, new Date());
return isValid(d) ? d : null;
}, [field.value, parseDateFormat]);
// Sync cuando RHF actualiza el valor externamente
useEffect(() => {
if (field.value) {
// field.value ya viene en formato parseDateFormat
const parsed = parse(field.value, parseDateFormat, new Date());
if (isValid(parsed)) {
setDisplayValue(format(parsed, displayDateFormat));
}
} else {
setDisplayValue("");
}
}, [field.value, parseDateFormat, displayDateFormat]);
setDisplayValue(parsedDate ? format(parsedDate, displayDateFormat) : "");
}, [parsedDate, displayDateFormat]);
const [inputError, setInputError] = useState<string | null>(null);
const handleClear = useCallback(() => {
field.onChange("");
setDisplayValue("");
setLocalError(null);
}, [field]);
const handleDisplayValueChange = (value: string) => {
setDisplayValue(value);
setInputError(null);
};
const handleClearDate = () => {
handleDisplayValueChange("");
}
const validateAndSetDate = () => {
const validateAndSet = useCallback(() => {
const trimmed = displayValue.trim();
if (!trimmed) {
field.onChange(""); // guardar vacío en el form
setInputError(null);
handleClear();
return;
}
const parsed = parse(trimmed, displayDateFormat, new Date());
if (isValid(parsed)) {
// Guardar en form como string con parseDateFormat
const newDateStr = format(parsed, parseDateFormat);
field.onChange(newDateStr);
// Asegurar displayValue consistente
handleDisplayValueChange(newDateStr);
const d = parse(trimmed, displayDateFormat, new Date());
if (isValid(d)) {
field.onChange(format(d, parseDateFormat));
setDisplayValue(format(d, displayDateFormat));
setLocalError(null);
} else {
setInputError(t("components.date_picker_input_field.invalid_date"));
setLocalError(t("components.date_picker_input_field.invalid_date"));
}
};
}, [displayValue, displayDateFormat, parseDateFormat, field, t, handleClear]);
const hasError = Boolean(localError || fieldState.error);
const describedById = description ? `${field.name}-desc` : undefined;
const errorId = hasError ? `${field.name}-err` : undefined;
const popoverId = `${field.name}-popover`;
return (
<Field data-invalid={invalid} orientation={orientation} className={className}>
{label && <FieldLabel className='text-xs text-muted-foreground text-nowrap' htmlFor={htmlFor}>{label}</FieldLabel>}
<Field data-invalid={invalid} orientation={orientation} className={cn("gap-1", className)}>
<div className="flex justify-between gap-2 overflow-hidden">
<div className="flex items-center gap-2 flex-nowrap">
<FieldLabel htmlFor={field.name} className={cn("m-0 text-xs text-muted-foreground text-nowrap text-ellipsis", disabled && "text-muted-foreground")}>
{label}
</FieldLabel>
{required && <span className="text-xs text-destructive">{t("common.required")}</span>}
</div>
{fieldState.isDirty && <span className="text-[10px] text-primary text-ellipsis">{t("common.modified")}</span>}
</div>
<Popover modal={true} open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<FormControl>
<div className='relative'>
<Input
type='text'
value={displayValue}
onChange={(e) => handleDisplayValueChange(e.target.value)}
onBlur={() => { if (!open) validateAndSetDate(); }}
onKeyDown={(e) => {
if (e.key === "Enter") {
e.preventDefault();
validateAndSetDate();
}
}}
readOnly={isReadOnly}
disabled={isDisabled}
className={cn(
"w-full rounded-md border px-3 py-2 text-sm shadow-sm focus:outline-none focus:ring-2 focus:ring-ring placeholder:font-normal placeholder:italic",
isDisabled && "bg-muted text-muted-foreground cursor-not-allowed",
isReadOnly && "bg-muted text-foreground cursor-default",
!isDisabled && !isReadOnly && "bg-white text-foreground",
inputError && "border-destructive ring-destructive"
)}
placeholder={placeholder}
/>
<div className='absolute inset-y-0 right-2 flex items-center gap-2 pr-1'>
{!isReadOnly && !required && displayValue && (
<button
<DateInputField
id={field.name}
value={displayValue}
onChange={(v) => {
setDisplayValue(v);
setLocalError(null);
}}
onBlurConfirm={validateAndSet}
onClear={handleClear}
placeholder={placeholder}
disabled={disabled}
readOnly={readOnly}
required={required}
hasError={hasError}
describedBy={[describedById, errorId].filter(Boolean).join(" ") || undefined}
onOpenRequest={() => setOpen(true)}
triggerButton={
!readOnly && !disabled && (
<PopoverTrigger asChild>
<Button
variant={'link'}
type='button'
onClick={(e) => {
e.preventDefault();
field.onChange(""); // limpiar valor real en el form
setDisplayValue(""); // limpiar input visible
setInputError(null); // limpiar error
}} aria-label={t("common.clear_date")}
className='text-muted-foreground hover:text-foreground focus:outline-none'
size={"icon-sm"}
aria-label={t("components.date_picker_input_field.open_calendar")}
aria-haspopup="dialog"
aria-expanded={open}
aria-controls={popoverId}
onClick={() => setOpen((o) => !o)}
className="text-muted-foreground transition cursor-pointer -mr-3"
>
<XIcon className='size-4 hover:text-destructive' />
</button>
)}
{isReadOnly ? (
<LockIcon className='size-4 text-muted-foreground' />
) : (
<CalendarIcon className='size-4 text-muted-foreground hover:text-primary hover:cursor-pointer' />
)}
</div>
</div>
</FormControl>
<CalendarIcon className="size-4" />
</Button>
</PopoverTrigger>
)
}
/>
</PopoverTrigger>
{!isDisabled && !isReadOnly && (
<PopoverContent className='w-auto p-0'>
<Card className='border-none shadow-none py-6 gap-3'>
<CardHeader className="border-b px-3 [.border-b]:pb-3">
<CardTitle>{label}</CardTitle>
<CardDescription>{description || "\u00A0"}</CardDescription>
<CardAction>
<Button
size="sm"
variant="outline"
onClick={(e) => {
e.preventDefault();
const today = format(new Date(), parseDateFormat);
field.onChange(today);
handleDisplayValueChange(today);
setOpen(false);
}}
>
{t("components.date_picker_input_field.today")}
</Button>
</CardAction>
</CardHeader>
<CardContent className='px-0'>
<Calendar
defaultMonth={field.value ? new Date(field.value) : undefined}
{...calendarProps}
mode='single'
selected={field.value ? new Date(field.value) : undefined}
onSelect={(date) => {
const newDateStr = date ? format(date, parseDateFormat) : "";
field.onChange(newDateStr);
handleDisplayValueChange(newDateStr);
setOpen(false);
}}
initialFocus
/>
</CardContent>
<CardFooter className='mx-auto'>
<Button
size="sm"
variant="outline"
onClick={(e) => {
e.preventDefault();
setOpen(false);
}}
>
{t("components.date_picker_input_field.close")}
</Button>
</CardFooter>
</Card>
</PopoverContent>
{!disabled && !readOnly && (
<DatePopoverCalendar
contentId={popoverId}
label={label}
description={description}
parsedDate={parsedDate}
onSelect={(date) => {
if (!date) return;
field.onChange(format(date, parseDateFormat));
setDisplayValue(format(date, displayDateFormat));
setOpen(false);
}}
onToday={() => {
const today = new Date();
field.onChange(format(today, parseDateFormat));
setDisplayValue(format(today, displayDateFormat));
setOpen(false);
}}
onClose={() => setOpen(false)}
{...calendarProps}
/>
)}
</Popover>
{isReadOnly && (
<p className='text-xs text-muted-foreground italic mt-1 flex items-center gap-1'>
<LockIcon className='w-3 h-3' /> {t("common.read_only") || "Solo lectura"}
</p>
{false && (
<div className='mt-1 flex items-start justify-between'>
<FieldDescription
id={describedById}
className={cn("text-xs truncate", !description && "invisible")}
>
{description || "\u00A0"}
</FieldDescription>
</div>
)}
{false && <FieldDescription className='text-xs'>{description || "\u00A0"}</FieldDescription>}
<FieldError errors={[fieldState.error]} />
</Field>

View File

@ -14,7 +14,7 @@ type DatePickerInputFieldProps<TFormValues extends FieldValues> = SUICalendarPro
required?: boolean;
readOnly?: boolean;
displayDateFormat?: string; // e.g. "dd/MM/yyyy"
displayDateFormat?: string; // e.g. "dd-MM-yyyy"
parseDateFormat?: string; // e.g. "yyyy-MM-dd"
orientation?: "vertical" | "horizontal" | "responsive",
};
@ -22,7 +22,7 @@ type DatePickerInputFieldProps<TFormValues extends FieldValues> = SUICalendarPro
export function DatePickerInputField<TFormValues extends FieldValues>({
control,
name,
displayDateFormat = "dd-MM-y1qyyy",
displayDateFormat = "dd-MM-yyyy",
parseDateFormat = "yyyy-MM-dd",
...props
}: DatePickerInputFieldProps<TFormValues>) {
@ -35,8 +35,6 @@ export function DatePickerInputField<TFormValues extends FieldValues>({
field={field}
fieldState={fieldState}
formState={formState}
htmlFor={name}
displayDateFormat={displayDateFormat}
parseDateFormat={parseDateFormat}

View File

@ -0,0 +1,76 @@
// DatePopoverCalendar.tsx
import {
Button,
Calendar,
Card,
CardAction,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
PopoverContent,
} from "@repo/shadcn-ui/components";
import { useTranslation } from "../../../locales/i18n.ts";
export function DatePopoverCalendar({
contentId,
label,
description,
parsedDate,
onSelect,
onToday,
onClose,
...calendarProps
}: {
contentId: string;
label?: string;
description?: string;
parsedDate: Date | null;
onSelect: (date: Date | undefined) => void;
onToday: () => void;
onClose: () => void;
} & Omit<React.ComponentProps<typeof Calendar>, "mode" | "selected" | "onSelect">) {
const { t } = useTranslation();
return (
<PopoverContent id={contentId} className="w-auto p-0">
<Card className="border-none shadow-none">
<CardHeader className="border-b">
<CardTitle>{label}</CardTitle>
<CardDescription>{description || "\u00A0"}</CardDescription>
<CardAction>
<Button
size="sm"
variant="outline"
onClick={onToday}
>
{t("components.date_picker_input_field.today")}
</Button>
</CardAction>
</CardHeader>
<CardContent className='px-0'>
<Calendar
selected={parsedDate ?? undefined}
defaultMonth={parsedDate ?? undefined}
onSelect={onSelect}
mode='single'
numberOfMonths={2}
/>
</CardContent>
<CardFooter className='mx-auto'>
<Button
size="sm"
variant="outline"
onClick={onClose}>
{t("components.date_picker_input_field.close")}
</Button>
</CardFooter>
</Card>
</PopoverContent >
);
}

View File

@ -1069,8 +1069,8 @@ importers:
specifier: ^19.1.0
version: 19.2.0
react-day-picker:
specifier: 8.10.1
version: 8.10.1(date-fns@4.1.0)(react@19.2.0)
specifier: 9.11.1
version: 9.11.1(react@19.2.0)
react-dom:
specifier: ^19.1.0
version: 19.2.0(react@19.2.0)
@ -1302,6 +1302,9 @@ packages:
'@dabh/diagnostics@2.0.8':
resolution: {integrity: sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==}
'@date-fns/tz@1.4.1':
resolution: {integrity: sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==}
'@dnd-kit/accessibility@3.1.1':
resolution: {integrity: sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==}
peerDependencies:
@ -3607,6 +3610,9 @@ packages:
resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==}
engines: {node: '>= 14'}
date-fns-jalali@4.1.0-0:
resolution: {integrity: sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==}
date-fns@4.1.0:
resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==}
@ -5304,11 +5310,11 @@ packages:
react: '>= 17.0.0'
styled-components: '>= 5.0.0'
react-day-picker@8.10.1:
resolution: {integrity: sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA==}
react-day-picker@9.11.1:
resolution: {integrity: sha512-l3ub6o8NlchqIjPKrRFUCkTUEq6KwemQlfv3XZzzwpUeGwmDJ+0u0Upmt38hJyd7D/vn2dQoOoLV/qAp0o3uUw==}
engines: {node: '>=18'}
peerDependencies:
date-fns: ^2.28.0 || ^3.0.0
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react: '>=16.8.0'
react-dom@19.2.0:
resolution: {integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==}
@ -6567,6 +6573,8 @@ snapshots:
enabled: 2.0.0
kuler: 2.0.0
'@date-fns/tz@1.4.1': {}
'@dnd-kit/accessibility@3.1.1(react@19.2.0)':
dependencies:
react: 19.2.0
@ -8897,6 +8905,8 @@ snapshots:
data-uri-to-buffer@6.0.2: {}
date-fns-jalali@4.1.0-0: {}
date-fns@4.1.0: {}
debug@2.6.9:
@ -10687,9 +10697,11 @@ snapshots:
react: 19.2.0
styled-components: 6.1.19(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
react-day-picker@8.10.1(date-fns@4.1.0)(react@19.2.0):
react-day-picker@9.11.1(react@19.2.0):
dependencies:
'@date-fns/tz': 1.4.1
date-fns: 4.1.0
date-fns-jalali: 4.1.0-0
react: 19.2.0
react-dom@19.2.0(react@19.2.0):