@open-mercato/ui 0.6.2-develop.3467.1.2a1818709d → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai/conversation-store.js +2 -0
- package/dist/ai/conversation-store.js.map +2 -2
- package/dist/ai/parts/MutationPreviewCard.js +4 -3
- package/dist/ai/parts/MutationPreviewCard.js.map +2 -2
- package/dist/ai/parts/MutationResultCard.js +9 -16
- package/dist/ai/parts/MutationResultCard.js.map +2 -2
- package/dist/backend/CrudForm.js +2 -1
- package/dist/backend/CrudForm.js.map +2 -2
- package/dist/backend/FilterOverlay.js +1 -1
- package/dist/backend/FilterOverlay.js.map +1 -1
- package/dist/backend/dashboard/widgetRegistry.js +2 -1
- package/dist/backend/dashboard/widgetRegistry.js.map +2 -2
- package/dist/backend/date-range/dateRanges.js +16 -18
- package/dist/backend/date-range/dateRanges.js.map +2 -2
- package/dist/backend/detail/AttachmentMetadataDialog.js +3 -1
- package/dist/backend/detail/AttachmentMetadataDialog.js.map +2 -2
- package/dist/backend/detail/InlineEditors.js +1 -1
- package/dist/backend/detail/InlineEditors.js.map +1 -1
- package/dist/backend/injection/useAppEvent.js +2 -7
- package/dist/backend/injection/useAppEvent.js.map +2 -2
- package/dist/backend/injection/widgetRegistry.js +2 -1
- package/dist/backend/injection/widgetRegistry.js.map +2 -2
- package/dist/backend/messages/useMessageCompose.js +62 -5
- package/dist/backend/messages/useMessageCompose.js.map +2 -2
- package/dist/backend/messages/useMessageComposeOperations.js +64 -5
- package/dist/backend/messages/useMessageComposeOperations.js.map +2 -2
- package/dist/backend/schedule/ScheduleCalendar.js +11 -1
- package/dist/backend/schedule/ScheduleCalendar.js.map +2 -2
- package/dist/backend/schedule/ScheduleToolbar.js +11 -1
- package/dist/backend/schedule/ScheduleToolbar.js.map +2 -2
- package/dist/primitives/calendar.js +1 -1
- package/dist/primitives/calendar.js.map +1 -1
- package/dist/primitives/date-format.js +1 -1
- package/dist/primitives/date-format.js.map +1 -1
- package/dist/primitives/date-picker-helpers.js +7 -9
- package/dist/primitives/date-picker-helpers.js.map +2 -2
- package/dist/primitives/date-picker.js +1 -1
- package/dist/primitives/date-picker.js.map +2 -2
- package/dist/primitives/date-range-picker.js +1 -1
- package/dist/primitives/date-range-picker.js.map +2 -2
- package/jest.config.cjs +4 -1
- package/package.json +6 -7
- package/src/ai/__tests__/AiAssistantLauncher.test.tsx +1 -1
- package/src/ai/__tests__/AiChat.test.tsx +17 -2
- package/src/ai/__tests__/conversation-store.test.ts +29 -0
- package/src/ai/conversation-store.ts +2 -0
- package/src/ai/parts/MutationPreviewCard.tsx +25 -20
- package/src/ai/parts/MutationResultCard.tsx +15 -27
- package/src/ai/parts/__tests__/MutationPreviewCard.test.tsx +26 -1
- package/src/ai/parts/__tests__/MutationResultCard.test.tsx +16 -1
- package/src/backend/CrudForm.tsx +3 -2
- package/src/backend/FilterOverlay.tsx +1 -1
- package/src/backend/__tests__/AppShell.test.tsx +1 -1
- package/src/backend/__tests__/SidebarCustomizationEditor.test.tsx +8 -1
- package/src/backend/dashboard/widgetRegistry.ts +2 -1
- package/src/backend/date-range/dateRanges.ts +16 -18
- package/src/backend/detail/AttachmentMetadataDialog.tsx +1 -1
- package/src/backend/detail/InlineEditors.tsx +1 -1
- package/src/backend/injection/__tests__/useAppEvent.test.ts +16 -0
- package/src/backend/injection/useAppEvent.ts +2 -9
- package/src/backend/injection/widgetRegistry.ts +2 -1
- package/src/backend/messages/__tests__/MessageComposer.test.tsx +77 -0
- package/src/backend/messages/useMessageCompose.ts +64 -5
- package/src/backend/messages/useMessageComposeOperations.ts +70 -4
- package/src/backend/schedule/ScheduleCalendar.tsx +11 -1
- package/src/backend/schedule/ScheduleToolbar.tsx +11 -1
- package/src/primitives/calendar.tsx +2 -2
- package/src/primitives/date-format.ts +2 -2
- package/src/primitives/date-picker-helpers.ts +7 -9
- package/src/primitives/date-picker.tsx +2 -2
- package/src/primitives/date-range-picker.tsx +2 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/primitives/calendar.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { DayPicker, useDayPicker } from 'react-day-picker'\nimport type { DayPickerProps, CalendarMonth } from 'react-day-picker'\nimport { ChevronLeft, ChevronRight } from 'lucide-react'\nimport { format } from 'date-fns'\nimport type { Locale } from 'date-fns'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\nexport type CalendarProps = DayPickerProps\n\nfunction MonthNavButton({\n direction,\n locale,\n}: {\n direction: 'prev' | 'next'\n locale?: Locale\n}) {\n const dayPicker = useDayPicker() as unknown as {\n previousMonth?: Date\n nextMonth?: Date\n goToMonth?: (month: Date) => void\n }\n const target = direction === 'prev' ? dayPicker.previousMonth : dayPicker.nextMonth\n const Icon = direction === 'prev' ? ChevronLeft : ChevronRight\n const targetLabel = format(target ?? new Date(), 'MMMM yyyy', locale ? { locale } : undefined)\n const ariaLabel = `Go to ${direction === 'prev' ? 'previous' : 'next'} month: ${targetLabel}`\n return (\n <button\n type=\"button\"\n disabled={!target}\n aria-label={ariaLabel}\n onClick={() => {\n if (target && dayPicker.goToMonth) dayPicker.goToMonth(target)\n }}\n className={cn(\n 'h-9 w-9 inline-flex items-center justify-center rounded-md shrink-0',\n 'border border-border bg-background text-muted-foreground transition-colors',\n 'hover:bg-accent hover:text-accent-foreground hover:border-input',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n 'disabled:opacity-30 disabled:pointer-events-none disabled:hover:bg-background',\n )}\n >\n <Icon className=\"h-4 w-4\" aria-hidden=\"true\" />\n </button>\n )\n}\n\nfunction buildMonthCaption(locale: Locale | undefined, totalMonths: number) {\n return function MonthCaption({\n calendarMonth,\n displayIndex,\n }: {\n calendarMonth: CalendarMonth\n displayIndex?: number\n }) {\n const label = format(calendarMonth.date, 'MMMM yyyy', locale ? { locale } : undefined)\n const index = typeof displayIndex === 'number' ? displayIndex : 0\n // For multi-month layouts (e.g. range pickers) only the leftmost month\n // exposes the previous-month chevron and only the rightmost exposes the\n // next-month chevron. Navigation is always global across all visible\n // months, so showing both arrows on every month is confusing.\n const showPrev = index === 0\n const showNext = index === totalMonths - 1\n return (\n <div className=\"flex items-center justify-between gap-2 mb-3\">\n {showPrev ? (\n <MonthNavButton direction=\"prev\" locale={locale} />\n ) : (\n <div className=\"h-9 w-9 shrink-0\" aria-hidden=\"true\" />\n )}\n <div\n className=\"flex-1 flex items-center justify-center h-9 rounded-md bg-muted px-3 text-sm font-medium\"\n aria-live=\"polite\"\n >\n {label}\n </div>\n {showNext ? (\n <MonthNavButton direction=\"next\" locale={locale} />\n ) : (\n <div className=\"h-9 w-9 shrink-0\" aria-hidden=\"true\" />\n )}\n </div>\n )\n }\n}\n\nexport function Calendar({\n className,\n classNames,\n showOutsideDays = true,\n fixedWeeks = true,\n locale,\n components,\n numberOfMonths = 1,\n pagedNavigation = true,\n ...props\n}: CalendarProps) {\n const monthCaption = React.useMemo(\n () => buildMonthCaption(locale as Locale | undefined, numberOfMonths),\n [locale, numberOfMonths],\n )\n return (\n <DayPicker\n showOutsideDays={showOutsideDays}\n fixedWeeks={fixedWeeks}\n pagedNavigation={pagedNavigation}\n locale={locale}\n numberOfMonths={numberOfMonths}\n className={cn('p-3', className)}\n classNames={{\n months: 'flex flex-col sm:flex-row gap-4',\n month: 'space-y-2',\n month_caption: '',\n caption_label: 'sr-only',\n nav: 'sr-only',\n month_grid: 'w-full border-collapse',\n weekdays: 'flex',\n weekday: 'text-muted-foreground rounded-md w-9 font-normal text-xs',\n weeks: 'w-full border-collapse',\n week: 'flex w-full mt-1',\n day: 'h-9 w-9 text-center text-sm p-0 relative focus-within:relative focus-within:z-20',\n day_button: cn(\n 'h-9 w-9 p-0 font-normal aria-selected:opacity-100',\n 'inline-flex items-center justify-center rounded-md text-sm',\n 'transition-colors focus:outline-none focus-visible:outline-none disabled:pointer-events-none',\n // Focus indicator is a soft accent fill instead of a ring overlay \u2014 keyboard\n // users get a visible cue, but mouse-click focus does not leave a stuck ring\n // on top of the selected cell.\n 'hover:bg-accent hover:text-accent-foreground',\n 'focus-visible:bg-accent focus-visible:text-accent-foreground',\n ),\n // React-day-picker v9 applies `classNames.selected` / `range_*` to the day\n // CELL (`<td>`) wrapper, not to the inner `<button>`. To keep the parent\n // fill visible through interaction, we (a) paint the cell with the desired\n // bg/text, and (b) force the inner button to render transparent \u2014 so the\n // button's own hover/focus-visible bg overrides cannot cover the cell fill.\n selected: cn(\n '!bg-primary !text-primary-foreground rounded-md',\n '[&_button]:!bg-transparent [&_button]:!text-primary-foreground',\n '[&_button:hover]:!bg-transparent [&_button:hover]:!text-primary-foreground',\n '[&_button:focus-visible]:!bg-transparent [&_button:focus-visible]:!text-primary-foreground',\n ),\n range_start: cn(\n '!bg-primary !text-primary-foreground rounded-l-md !rounded-r-none',\n '[&_button]:!bg-transparent [&_button]:!text-primary-foreground',\n '[&_button:hover]:!bg-transparent [&_button:hover]:!text-primary-foreground',\n '[&_button:focus-visible]:!bg-transparent [&_button:focus-visible]:!text-primary-foreground',\n ),\n range_end: cn(\n '!bg-primary !text-primary-foreground rounded-r-md !rounded-l-none',\n '[&_button]:!bg-transparent [&_button]:!text-primary-foreground',\n '[&_button:hover]:!bg-transparent [&_button:hover]:!text-primary-foreground',\n '[&_button:focus-visible]:!bg-transparent [&_button:focus-visible]:!text-primary-foreground',\n ),\n range_middle: cn(\n '!bg-accent !text-accent-foreground !rounded-none',\n '[&_button]:!bg-transparent [&_button]:!text-accent-foreground',\n '[&_button:hover]:!bg-transparent [&_button:hover]:!text-accent-foreground',\n '[&_button:focus-visible]:!bg-transparent [&_button:focus-visible]:!text-accent-foreground',\n ),\n today: 'font-semibold text-primary rounded-md',\n outside:\n 'day-outside text-muted-foreground opacity-40 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30',\n disabled: 'text-muted-foreground opacity-50',\n hidden: 'invisible',\n ...classNames,\n }}\n components={{\n MonthCaption: monthCaption,\n ...components,\n }}\n {...props}\n />\n )\n}\n"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { DayPicker, useDayPicker } from 'react-day-picker'\nimport type { DayPickerProps, CalendarMonth } from 'react-day-picker'\nimport { ChevronLeft, ChevronRight } from 'lucide-react'\nimport { format } from 'date-fns/format'\nimport type { Locale } from 'date-fns/locale'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\nexport type CalendarProps = DayPickerProps\n\nfunction MonthNavButton({\n direction,\n locale,\n}: {\n direction: 'prev' | 'next'\n locale?: Locale\n}) {\n const dayPicker = useDayPicker() as unknown as {\n previousMonth?: Date\n nextMonth?: Date\n goToMonth?: (month: Date) => void\n }\n const target = direction === 'prev' ? dayPicker.previousMonth : dayPicker.nextMonth\n const Icon = direction === 'prev' ? ChevronLeft : ChevronRight\n const targetLabel = format(target ?? new Date(), 'MMMM yyyy', locale ? { locale } : undefined)\n const ariaLabel = `Go to ${direction === 'prev' ? 'previous' : 'next'} month: ${targetLabel}`\n return (\n <button\n type=\"button\"\n disabled={!target}\n aria-label={ariaLabel}\n onClick={() => {\n if (target && dayPicker.goToMonth) dayPicker.goToMonth(target)\n }}\n className={cn(\n 'h-9 w-9 inline-flex items-center justify-center rounded-md shrink-0',\n 'border border-border bg-background text-muted-foreground transition-colors',\n 'hover:bg-accent hover:text-accent-foreground hover:border-input',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n 'disabled:opacity-30 disabled:pointer-events-none disabled:hover:bg-background',\n )}\n >\n <Icon className=\"h-4 w-4\" aria-hidden=\"true\" />\n </button>\n )\n}\n\nfunction buildMonthCaption(locale: Locale | undefined, totalMonths: number) {\n return function MonthCaption({\n calendarMonth,\n displayIndex,\n }: {\n calendarMonth: CalendarMonth\n displayIndex?: number\n }) {\n const label = format(calendarMonth.date, 'MMMM yyyy', locale ? { locale } : undefined)\n const index = typeof displayIndex === 'number' ? displayIndex : 0\n // For multi-month layouts (e.g. range pickers) only the leftmost month\n // exposes the previous-month chevron and only the rightmost exposes the\n // next-month chevron. Navigation is always global across all visible\n // months, so showing both arrows on every month is confusing.\n const showPrev = index === 0\n const showNext = index === totalMonths - 1\n return (\n <div className=\"flex items-center justify-between gap-2 mb-3\">\n {showPrev ? (\n <MonthNavButton direction=\"prev\" locale={locale} />\n ) : (\n <div className=\"h-9 w-9 shrink-0\" aria-hidden=\"true\" />\n )}\n <div\n className=\"flex-1 flex items-center justify-center h-9 rounded-md bg-muted px-3 text-sm font-medium\"\n aria-live=\"polite\"\n >\n {label}\n </div>\n {showNext ? (\n <MonthNavButton direction=\"next\" locale={locale} />\n ) : (\n <div className=\"h-9 w-9 shrink-0\" aria-hidden=\"true\" />\n )}\n </div>\n )\n }\n}\n\nexport function Calendar({\n className,\n classNames,\n showOutsideDays = true,\n fixedWeeks = true,\n locale,\n components,\n numberOfMonths = 1,\n pagedNavigation = true,\n ...props\n}: CalendarProps) {\n const monthCaption = React.useMemo(\n () => buildMonthCaption(locale as Locale | undefined, numberOfMonths),\n [locale, numberOfMonths],\n )\n return (\n <DayPicker\n showOutsideDays={showOutsideDays}\n fixedWeeks={fixedWeeks}\n pagedNavigation={pagedNavigation}\n locale={locale}\n numberOfMonths={numberOfMonths}\n className={cn('p-3', className)}\n classNames={{\n months: 'flex flex-col sm:flex-row gap-4',\n month: 'space-y-2',\n month_caption: '',\n caption_label: 'sr-only',\n nav: 'sr-only',\n month_grid: 'w-full border-collapse',\n weekdays: 'flex',\n weekday: 'text-muted-foreground rounded-md w-9 font-normal text-xs',\n weeks: 'w-full border-collapse',\n week: 'flex w-full mt-1',\n day: 'h-9 w-9 text-center text-sm p-0 relative focus-within:relative focus-within:z-20',\n day_button: cn(\n 'h-9 w-9 p-0 font-normal aria-selected:opacity-100',\n 'inline-flex items-center justify-center rounded-md text-sm',\n 'transition-colors focus:outline-none focus-visible:outline-none disabled:pointer-events-none',\n // Focus indicator is a soft accent fill instead of a ring overlay \u2014 keyboard\n // users get a visible cue, but mouse-click focus does not leave a stuck ring\n // on top of the selected cell.\n 'hover:bg-accent hover:text-accent-foreground',\n 'focus-visible:bg-accent focus-visible:text-accent-foreground',\n ),\n // React-day-picker v9 applies `classNames.selected` / `range_*` to the day\n // CELL (`<td>`) wrapper, not to the inner `<button>`. To keep the parent\n // fill visible through interaction, we (a) paint the cell with the desired\n // bg/text, and (b) force the inner button to render transparent \u2014 so the\n // button's own hover/focus-visible bg overrides cannot cover the cell fill.\n selected: cn(\n '!bg-primary !text-primary-foreground rounded-md',\n '[&_button]:!bg-transparent [&_button]:!text-primary-foreground',\n '[&_button:hover]:!bg-transparent [&_button:hover]:!text-primary-foreground',\n '[&_button:focus-visible]:!bg-transparent [&_button:focus-visible]:!text-primary-foreground',\n ),\n range_start: cn(\n '!bg-primary !text-primary-foreground rounded-l-md !rounded-r-none',\n '[&_button]:!bg-transparent [&_button]:!text-primary-foreground',\n '[&_button:hover]:!bg-transparent [&_button:hover]:!text-primary-foreground',\n '[&_button:focus-visible]:!bg-transparent [&_button:focus-visible]:!text-primary-foreground',\n ),\n range_end: cn(\n '!bg-primary !text-primary-foreground rounded-r-md !rounded-l-none',\n '[&_button]:!bg-transparent [&_button]:!text-primary-foreground',\n '[&_button:hover]:!bg-transparent [&_button:hover]:!text-primary-foreground',\n '[&_button:focus-visible]:!bg-transparent [&_button:focus-visible]:!text-primary-foreground',\n ),\n range_middle: cn(\n '!bg-accent !text-accent-foreground !rounded-none',\n '[&_button]:!bg-transparent [&_button]:!text-accent-foreground',\n '[&_button:hover]:!bg-transparent [&_button:hover]:!text-accent-foreground',\n '[&_button:focus-visible]:!bg-transparent [&_button:focus-visible]:!text-accent-foreground',\n ),\n today: 'font-semibold text-primary rounded-md',\n outside:\n 'day-outside text-muted-foreground opacity-40 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30',\n disabled: 'text-muted-foreground opacity-50',\n hidden: 'invisible',\n ...classNames,\n }}\n components={{\n MonthCaption: monthCaption,\n ...components,\n }}\n {...props}\n />\n )\n}\n"],
|
|
5
5
|
"mappings": ";AA4CM,cAsBA,YAtBA;AA1CN,YAAY,WAAW;AACvB,SAAS,WAAW,oBAAoB;AAExC,SAAS,aAAa,oBAAoB;AAC1C,SAAS,cAAc;AAEvB,SAAS,UAAU;AAInB,SAAS,eAAe;AAAA,EACtB;AAAA,EACA;AACF,GAGG;AACD,QAAM,YAAY,aAAa;AAK/B,QAAM,SAAS,cAAc,SAAS,UAAU,gBAAgB,UAAU;AAC1E,QAAM,OAAO,cAAc,SAAS,cAAc;AAClD,QAAM,cAAc,OAAO,UAAU,oBAAI,KAAK,GAAG,aAAa,SAAS,EAAE,OAAO,IAAI,MAAS;AAC7F,QAAM,YAAY,SAAS,cAAc,SAAS,aAAa,MAAM,WAAW,WAAW;AAC3F,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,UAAU,CAAC;AAAA,MACX,cAAY;AAAA,MACZ,SAAS,MAAM;AACb,YAAI,UAAU,UAAU,UAAW,WAAU,UAAU,MAAM;AAAA,MAC/D;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MAEA,8BAAC,QAAK,WAAU,WAAU,eAAY,QAAO;AAAA;AAAA,EAC/C;AAEJ;AAEA,SAAS,kBAAkB,QAA4B,aAAqB;AAC1E,SAAO,SAAS,aAAa;AAAA,IAC3B;AAAA,IACA;AAAA,EACF,GAGG;AACD,UAAM,QAAQ,OAAO,cAAc,MAAM,aAAa,SAAS,EAAE,OAAO,IAAI,MAAS;AACrF,UAAM,QAAQ,OAAO,iBAAiB,WAAW,eAAe;AAKhE,UAAM,WAAW,UAAU;AAC3B,UAAM,WAAW,UAAU,cAAc;AACzC,WACE,qBAAC,SAAI,WAAU,gDACZ;AAAA,iBACC,oBAAC,kBAAe,WAAU,QAAO,QAAgB,IAEjD,oBAAC,SAAI,WAAU,oBAAmB,eAAY,QAAO;AAAA,MAEvD;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,aAAU;AAAA,UAET;AAAA;AAAA,MACH;AAAA,MACC,WACC,oBAAC,kBAAe,WAAU,QAAO,QAAgB,IAEjD,oBAAC,SAAI,WAAU,oBAAmB,eAAY,QAAO;AAAA,OAEzD;AAAA,EAEJ;AACF;AAEO,SAAS,SAAS;AAAA,EACvB;AAAA,EACA;AAAA,EACA,kBAAkB;AAAA,EAClB,aAAa;AAAA,EACb;AAAA,EACA;AAAA,EACA,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAClB,GAAG;AACL,GAAkB;AAChB,QAAM,eAAe,MAAM;AAAA,IACzB,MAAM,kBAAkB,QAA8B,cAAc;AAAA,IACpE,CAAC,QAAQ,cAAc;AAAA,EACzB;AACA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,GAAG,OAAO,SAAS;AAAA,MAC9B,YAAY;AAAA,QACV,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,eAAe;AAAA,QACf,eAAe;AAAA,QACf,KAAK;AAAA,QACL,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,SAAS;AAAA,QACT,OAAO;AAAA,QACP,MAAM;AAAA,QACN,KAAK;AAAA,QACL,YAAY;AAAA,UACV;AAAA,UACA;AAAA,UACA;AAAA;AAAA;AAAA;AAAA,UAIA;AAAA,UACA;AAAA,QACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAMA,UAAU;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,QACA,aAAa;AAAA,UACX;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,QACA,WAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,QACA,cAAc;AAAA,UACZ;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,QACA,OAAO;AAAA,QACP,SACE;AAAA,QACF,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,GAAG;AAAA,MACL;AAAA,MACA,YAAY;AAAA,QACV,cAAc;AAAA,QACd,GAAG;AAAA,MACL;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/primitives/date-format.ts"],
|
|
4
|
-
"sourcesContent": ["import { format as formatDateFns } from 'date-fns'\nimport type { Locale } from 'date-fns'\n\ntype LocaleLike = Locale | string | null | undefined\n\nconst DAY_FIRST_LOCALE_CODES = new Set([\n 'pl', 'de', 'fr', 'es', 'it', 'pt', 'nl', 'ru', 'cs', 'sk', 'hu', 'ro',\n])\n\nconst SYSTEM_FORMAT_VALUES = new Set(['auto', 'default', 'locale', 'system'])\n\nfunction getLocaleCode(locale?: LocaleLike): string {\n if (!locale) return ''\n if (typeof locale === 'string') return locale.split('-')[0]?.toLowerCase() ?? ''\n return locale.code?.split('-')[0]?.toLowerCase() ?? ''\n}\n\nexport function normalizeDateFormatPattern(value: string | null | undefined): string | null {\n const trimmed = value?.trim()\n if (!trimmed) return null\n if (SYSTEM_FORMAT_VALUES.has(trimmed.toLowerCase())) return null\n return trimmed\n .replace(/YYYY/g, 'yyyy')\n .replace(/YY/g, 'yy')\n .replace(/DD/g, 'dd')\n}\n\nexport function deriveDateDisplayFormat(locale?: LocaleLike): string {\n const code = getLocaleCode(locale)\n return code && DAY_FIRST_LOCALE_CODES.has(code) ? 'd MMM yyyy' : 'MMM d, yyyy'\n}\n\nexport function resolvePublicDateFormat(locale?: LocaleLike, explicitFormat?: string | null): string {\n return (\n normalizeDateFormatPattern(explicitFormat)\n ?? normalizeDateFormatPattern(process.env.NEXT_PUBLIC_OM_DATE_FORMAT)\n ?? normalizeDateFormatPattern(process.env.NEXT_PUBLIC_DATE_FORMAT)\n ?? deriveDateDisplayFormat(locale)\n )\n}\n\nexport function resolvePublicDateTimeFormat(locale?: LocaleLike, explicitFormat?: string | null): string {\n const dateFormat = deriveDateDisplayFormat(locale)\n return (\n normalizeDateFormatPattern(explicitFormat)\n ?? normalizeDateFormatPattern(process.env.NEXT_PUBLIC_OM_DATE_TIME_FORMAT)\n ?? normalizeDateFormatPattern(process.env.NEXT_PUBLIC_DATE_TIME_FORMAT)\n ?? normalizeDateFormatPattern(process.env.NEXT_PUBLIC_OM_DATE_FORMAT)\n ?? normalizeDateFormatPattern(process.env.NEXT_PUBLIC_DATE_FORMAT)\n ?? `${dateFormat} HH:mm`\n )\n}\n\nexport function formatWithPublicDateFormat(date: Date, format: string, locale?: Locale): string | null {\n try {\n return formatDateFns(date, format, locale ? { locale } : undefined)\n } catch {\n return null\n }\n}\n"],
|
|
4
|
+
"sourcesContent": ["import { format as formatDateFns } from 'date-fns/format'\nimport type { Locale } from 'date-fns/locale'\n\ntype LocaleLike = Locale | string | null | undefined\n\nconst DAY_FIRST_LOCALE_CODES = new Set([\n 'pl', 'de', 'fr', 'es', 'it', 'pt', 'nl', 'ru', 'cs', 'sk', 'hu', 'ro',\n])\n\nconst SYSTEM_FORMAT_VALUES = new Set(['auto', 'default', 'locale', 'system'])\n\nfunction getLocaleCode(locale?: LocaleLike): string {\n if (!locale) return ''\n if (typeof locale === 'string') return locale.split('-')[0]?.toLowerCase() ?? ''\n return locale.code?.split('-')[0]?.toLowerCase() ?? ''\n}\n\nexport function normalizeDateFormatPattern(value: string | null | undefined): string | null {\n const trimmed = value?.trim()\n if (!trimmed) return null\n if (SYSTEM_FORMAT_VALUES.has(trimmed.toLowerCase())) return null\n return trimmed\n .replace(/YYYY/g, 'yyyy')\n .replace(/YY/g, 'yy')\n .replace(/DD/g, 'dd')\n}\n\nexport function deriveDateDisplayFormat(locale?: LocaleLike): string {\n const code = getLocaleCode(locale)\n return code && DAY_FIRST_LOCALE_CODES.has(code) ? 'd MMM yyyy' : 'MMM d, yyyy'\n}\n\nexport function resolvePublicDateFormat(locale?: LocaleLike, explicitFormat?: string | null): string {\n return (\n normalizeDateFormatPattern(explicitFormat)\n ?? normalizeDateFormatPattern(process.env.NEXT_PUBLIC_OM_DATE_FORMAT)\n ?? normalizeDateFormatPattern(process.env.NEXT_PUBLIC_DATE_FORMAT)\n ?? deriveDateDisplayFormat(locale)\n )\n}\n\nexport function resolvePublicDateTimeFormat(locale?: LocaleLike, explicitFormat?: string | null): string {\n const dateFormat = deriveDateDisplayFormat(locale)\n return (\n normalizeDateFormatPattern(explicitFormat)\n ?? normalizeDateFormatPattern(process.env.NEXT_PUBLIC_OM_DATE_TIME_FORMAT)\n ?? normalizeDateFormatPattern(process.env.NEXT_PUBLIC_DATE_TIME_FORMAT)\n ?? normalizeDateFormatPattern(process.env.NEXT_PUBLIC_OM_DATE_FORMAT)\n ?? normalizeDateFormatPattern(process.env.NEXT_PUBLIC_DATE_FORMAT)\n ?? `${dateFormat} HH:mm`\n )\n}\n\nexport function formatWithPublicDateFormat(date: Date, format: string, locale?: Locale): string | null {\n try {\n return formatDateFns(date, format, locale ? { locale } : undefined)\n } catch {\n return null\n }\n}\n"],
|
|
5
5
|
"mappings": "AAAA,SAAS,UAAU,qBAAqB;AAKxC,MAAM,yBAAyB,oBAAI,IAAI;AAAA,EACrC;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AACpE,CAAC;AAED,MAAM,uBAAuB,oBAAI,IAAI,CAAC,QAAQ,WAAW,UAAU,QAAQ,CAAC;AAE5E,SAAS,cAAc,QAA6B;AAClD,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,OAAO,WAAW,SAAU,QAAO,OAAO,MAAM,GAAG,EAAE,CAAC,GAAG,YAAY,KAAK;AAC9E,SAAO,OAAO,MAAM,MAAM,GAAG,EAAE,CAAC,GAAG,YAAY,KAAK;AACtD;AAEO,SAAS,2BAA2B,OAAiD;AAC1F,QAAM,UAAU,OAAO,KAAK;AAC5B,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI,qBAAqB,IAAI,QAAQ,YAAY,CAAC,EAAG,QAAO;AAC5D,SAAO,QACJ,QAAQ,SAAS,MAAM,EACvB,QAAQ,OAAO,IAAI,EACnB,QAAQ,OAAO,IAAI;AACxB;AAEO,SAAS,wBAAwB,QAA6B;AACnE,QAAM,OAAO,cAAc,MAAM;AACjC,SAAO,QAAQ,uBAAuB,IAAI,IAAI,IAAI,eAAe;AACnE;AAEO,SAAS,wBAAwB,QAAqB,gBAAwC;AACnG,SACE,2BAA2B,cAAc,KACtC,2BAA2B,QAAQ,IAAI,0BAA0B,KACjE,2BAA2B,QAAQ,IAAI,uBAAuB,KAC9D,wBAAwB,MAAM;AAErC;AAEO,SAAS,4BAA4B,QAAqB,gBAAwC;AACvG,QAAM,aAAa,wBAAwB,MAAM;AACjD,SACE,2BAA2B,cAAc,KACtC,2BAA2B,QAAQ,IAAI,+BAA+B,KACtE,2BAA2B,QAAQ,IAAI,4BAA4B,KACnE,2BAA2B,QAAQ,IAAI,0BAA0B,KACjE,2BAA2B,QAAQ,IAAI,uBAAuB,KAC9D,GAAG,UAAU;AAEpB;AAEO,SAAS,2BAA2B,MAAY,QAAgB,QAAgC;AACrG,MAAI;AACF,WAAO,cAAc,MAAM,QAAQ,SAAS,EAAE,OAAO,IAAI,MAAS;AAAA,EACpE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
subYears
|
|
9
|
-
} from "date-fns";
|
|
1
|
+
import { endOfDay } from "date-fns/endOfDay";
|
|
2
|
+
import { startOfDay } from "date-fns/startOfDay";
|
|
3
|
+
import { startOfMonth } from "date-fns/startOfMonth";
|
|
4
|
+
import { startOfYear } from "date-fns/startOfYear";
|
|
5
|
+
import { subDays } from "date-fns/subDays";
|
|
6
|
+
import { subMonths } from "date-fns/subMonths";
|
|
7
|
+
import { subYears } from "date-fns/subYears";
|
|
10
8
|
function defaultDateRangePresets() {
|
|
11
9
|
return [
|
|
12
10
|
{
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/primitives/date-picker-helpers.ts"],
|
|
4
|
-
"sourcesContent": ["import {\
|
|
5
|
-
"mappings": "AAAA
|
|
4
|
+
"sourcesContent": ["import { endOfDay } from 'date-fns/endOfDay'\nimport { startOfDay } from 'date-fns/startOfDay'\nimport { startOfMonth } from 'date-fns/startOfMonth'\nimport { startOfYear } from 'date-fns/startOfYear'\nimport { subDays } from 'date-fns/subDays'\nimport { subMonths } from 'date-fns/subMonths'\nimport { subYears } from 'date-fns/subYears'\nimport type { DateRange } from '../backend/date-range/dateRanges'\n\nexport type DateRangePresetItem = {\n id: string\n labelKey: string\n range: (referenceDate?: Date) => DateRange\n}\n\n/**\n * Returns the canonical 8 date-range presets aligned with the Figma\n * Range Picker spec (`446:7412` Period Range track).\n *\n * The list is intentionally shorter than `DATE_RANGE_OPTIONS` from\n * `@open-mercato/ui/backend/date-range`: that constant powers\n * dashboard/analytics modules with 13 presets (today, yesterday,\n * this/last week/month/quarter/year, last 7/30/90 days), but the Figma\n * Range Picker UI exposes 8 options optimised for filter UX (today plus\n * sliding windows and month/year-to-date).\n *\n * Used by `<DateRangePicker>` as the default `presets` value.\n */\nexport function defaultDateRangePresets(): DateRangePresetItem[] {\n return [\n {\n id: 'today',\n labelKey: 'ui.dateRangePicker.presets.today',\n range: (ref = new Date()) => ({ start: startOfDay(ref), end: endOfDay(ref) }),\n },\n {\n id: 'last_7_days',\n labelKey: 'ui.dateRangePicker.presets.last7Days',\n range: (ref = new Date()) => ({\n start: startOfDay(subDays(ref, 6)),\n end: endOfDay(ref),\n }),\n },\n {\n id: 'last_30_days',\n labelKey: 'ui.dateRangePicker.presets.last30Days',\n range: (ref = new Date()) => ({\n start: startOfDay(subDays(ref, 29)),\n end: endOfDay(ref),\n }),\n },\n {\n id: 'last_3_months',\n labelKey: 'ui.dateRangePicker.presets.last3Months',\n range: (ref = new Date()) => ({\n start: startOfDay(subMonths(ref, 3)),\n end: endOfDay(ref),\n }),\n },\n {\n id: 'last_12_months',\n labelKey: 'ui.dateRangePicker.presets.last12Months',\n range: (ref = new Date()) => ({\n start: startOfDay(subYears(ref, 1)),\n end: endOfDay(ref),\n }),\n },\n {\n id: 'month_to_date',\n labelKey: 'ui.dateRangePicker.presets.monthToDate',\n range: (ref = new Date()) => ({\n start: startOfMonth(ref),\n end: endOfDay(ref),\n }),\n },\n {\n id: 'year_to_date',\n labelKey: 'ui.dateRangePicker.presets.yearToDate',\n range: (ref = new Date()) => ({\n start: startOfYear(ref),\n end: endOfDay(ref),\n }),\n },\n {\n id: 'all_time',\n labelKey: 'ui.dateRangePicker.presets.allTime',\n range: (ref = new Date()) => ({\n // Symbolic \"all time\": from epoch to end of reference day.\n start: new Date(0),\n end: endOfDay(ref),\n }),\n },\n ]\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,gBAAgB;AACzB,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB;AAC7B,SAAS,mBAAmB;AAC5B,SAAS,eAAe;AACxB,SAAS,iBAAiB;AAC1B,SAAS,gBAAgB;AAsBlB,SAAS,0BAAiD;AAC/D,SAAO;AAAA,IACL;AAAA,MACE,IAAI;AAAA,MACJ,UAAU;AAAA,MACV,OAAO,CAAC,MAAM,oBAAI,KAAK,OAAO,EAAE,OAAO,WAAW,GAAG,GAAG,KAAK,SAAS,GAAG,EAAE;AAAA,IAC7E;AAAA,IACA;AAAA,MACE,IAAI;AAAA,MACJ,UAAU;AAAA,MACV,OAAO,CAAC,MAAM,oBAAI,KAAK,OAAO;AAAA,QAC5B,OAAO,WAAW,QAAQ,KAAK,CAAC,CAAC;AAAA,QACjC,KAAK,SAAS,GAAG;AAAA,MACnB;AAAA,IACF;AAAA,IACA;AAAA,MACE,IAAI;AAAA,MACJ,UAAU;AAAA,MACV,OAAO,CAAC,MAAM,oBAAI,KAAK,OAAO;AAAA,QAC5B,OAAO,WAAW,QAAQ,KAAK,EAAE,CAAC;AAAA,QAClC,KAAK,SAAS,GAAG;AAAA,MACnB;AAAA,IACF;AAAA,IACA;AAAA,MACE,IAAI;AAAA,MACJ,UAAU;AAAA,MACV,OAAO,CAAC,MAAM,oBAAI,KAAK,OAAO;AAAA,QAC5B,OAAO,WAAW,UAAU,KAAK,CAAC,CAAC;AAAA,QACnC,KAAK,SAAS,GAAG;AAAA,MACnB;AAAA,IACF;AAAA,IACA;AAAA,MACE,IAAI;AAAA,MACJ,UAAU;AAAA,MACV,OAAO,CAAC,MAAM,oBAAI,KAAK,OAAO;AAAA,QAC5B,OAAO,WAAW,SAAS,KAAK,CAAC,CAAC;AAAA,QAClC,KAAK,SAAS,GAAG;AAAA,MACnB;AAAA,IACF;AAAA,IACA;AAAA,MACE,IAAI;AAAA,MACJ,UAAU;AAAA,MACV,OAAO,CAAC,MAAM,oBAAI,KAAK,OAAO;AAAA,QAC5B,OAAO,aAAa,GAAG;AAAA,QACvB,KAAK,SAAS,GAAG;AAAA,MACnB;AAAA,IACF;AAAA,IACA;AAAA,MACE,IAAI;AAAA,MACJ,UAAU;AAAA,MACV,OAAO,CAAC,MAAM,oBAAI,KAAK,OAAO;AAAA,QAC5B,OAAO,YAAY,GAAG;AAAA,QACtB,KAAK,SAAS,GAAG;AAAA,MACnB;AAAA,IACF;AAAA,IACA;AAAA,MACE,IAAI;AAAA,MACJ,UAAU;AAAA,MACV,OAAO,CAAC,MAAM,oBAAI,KAAK,OAAO;AAAA;AAAA,QAE5B,OAAO,oBAAI,KAAK,CAAC;AAAA,QACjB,KAAK,SAAS,GAAG;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -158,7 +158,7 @@ function DatePicker({
|
|
|
158
158
|
onSelect: handleDaySelect,
|
|
159
159
|
locale,
|
|
160
160
|
disabled: disabledMatcher,
|
|
161
|
-
|
|
161
|
+
autoFocus: true
|
|
162
162
|
}
|
|
163
163
|
),
|
|
164
164
|
withTime && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-t px-3 py-2", children: [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/primitives/date-picker.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport type { Locale } from 'date-fns'\nimport { CalendarIcon } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Popover, PopoverContent, PopoverTrigger } from './popover'\nimport { Calendar } from './calendar'\nimport { Button } from './button'\nimport { LinkButton } from './link-button'\nimport { TimeInput } from '../backend/inputs/TimeInput'\nimport { formatWithPublicDateFormat, resolvePublicDateFormat, resolvePublicDateTimeFormat } from './date-format'\n\nexport type DatePickerFooter = 'apply-cancel' | 'today-clear' | 'none'\n\nexport type DatePickerProps = {\n value?: Date | null\n onChange: (value: Date | null) => void\n placeholder?: string\n size?: 'sm' | 'default'\n disabled?: boolean\n readOnly?: boolean\n /**\n * Footer mode in the popover.\n * - `'apply-cancel'` (default, Figma-aligned): selecting a day stages a draft;\n * Apply commits and closes; Cancel reverts and closes.\n * - `'today-clear'`: legacy behavior \u2014 Today commits today, Clear sets null.\n * - `'none'`: no footer; selecting a day commits immediately and closes.\n */\n footer?: DatePickerFooter\n /**\n * Whether selecting a day commits the value and closes the popover.\n * - In `footer='apply-cancel'`: ignored (commit happens via Apply).\n * - In `footer='today-clear'`: default `true` for backward compatibility.\n * - In `footer='none'`: always `true` (popover always closes after selection).\n */\n closeOnSelect?: boolean\n /**\n * Whether to show the \"Today\" link in `footer='today-clear'` mode. Default `true`.\n * Only meaningful when `footer='today-clear'`.\n */\n showTodayButton?: boolean\n /**\n * Whether to show the \"Clear\" link in `footer='today-clear'` mode. Default `true`.\n * Only meaningful when `footer='today-clear'`.\n */\n showClearButton?: boolean\n withTime?: boolean\n minuteStep?: number\n align?: 'start' | 'center' | 'end'\n minDate?: Date\n maxDate?: Date\n locale?: Locale\n displayFormat?: string\n className?: string\n popoverClassName?: string\n id?: string\n name?: string\n required?: boolean\n 'aria-label'?: string\n 'aria-describedby'?: string\n}\n\nfunction extractTime(date: Date): string {\n return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`\n}\n\nfunction applyTimeToDate(base: Date, time: string): Date {\n const [hourStr = '0', minuteStr = '0'] = time.split(':')\n const hour = parseInt(hourStr, 10)\n const minute = parseInt(minuteStr, 10)\n const next = new Date(base)\n next.setHours(isNaN(hour) ? 0 : hour, isNaN(minute) ? 0 : minute, 0, 0)\n return next\n}\n\nfunction startOfDay(date: Date): Date {\n const next = new Date(date)\n next.setHours(0, 0, 0, 0)\n return next\n}\n\nexport function DatePicker({\n value,\n onChange,\n placeholder,\n size = 'default',\n disabled = false,\n readOnly = false,\n footer = 'apply-cancel',\n closeOnSelect,\n showTodayButton = true,\n showClearButton = true,\n withTime = false,\n minuteStep = 1,\n align = 'start',\n minDate,\n maxDate,\n locale,\n displayFormat,\n className,\n popoverClassName,\n id,\n name,\n required,\n 'aria-label': ariaLabel,\n 'aria-describedby': ariaDescribedBy,\n}: DatePickerProps) {\n const resolvedCloseOnSelect = closeOnSelect ?? footer === 'today-clear'\n const t = useT()\n const [open, setOpen] = React.useState(false)\n const [draft, setDraft] = React.useState<Date | null>(value ?? null)\n\n const isInteractive = !disabled && !readOnly\n const useDraft = footer === 'apply-cancel'\n\n React.useEffect(() => {\n if (open) setDraft(value ?? null)\n }, [open, value])\n\n const resolvedFormat = withTime\n ? resolvePublicDateTimeFormat(locale, displayFormat)\n : resolvePublicDateFormat(locale, displayFormat)\n const placeholderText = placeholder ?? (\n withTime\n ? t('ui.dateTimePicker.placeholder', 'Pick date and time')\n : t('ui.datePicker.placeholder', 'Pick a date')\n )\n\n const formattedValue = React.useMemo(() => {\n if (!value) return null\n return formatWithPublicDateFormat(value, resolvedFormat, locale)\n }, [value, resolvedFormat, locale])\n\n const handleDaySelect = React.useCallback(\n (day: Date | undefined) => {\n if (!day || !isInteractive) return\n const reference = withTime ? (useDraft ? draft : value) : null\n const base = withTime\n ? applyTimeToDate(day, reference ? extractTime(reference) : '00:00')\n : startOfDay(day)\n if (useDraft) {\n setDraft(base)\n } else {\n onChange(base)\n if (footer === 'none' || resolvedCloseOnSelect) setOpen(false)\n }\n },\n [isInteractive, withTime, useDraft, draft, value, footer, resolvedCloseOnSelect, onChange],\n )\n\n const handleTimeChange = React.useCallback(\n (time: string) => {\n const target = useDraft ? draft : value\n if (!target) return\n const next = applyTimeToDate(target, time)\n if (useDraft) setDraft(next)\n else onChange(next)\n },\n [useDraft, draft, value, onChange],\n )\n\n const handleApply = React.useCallback(() => {\n onChange(draft)\n setOpen(false)\n }, [draft, onChange])\n\n const handleCancel = React.useCallback(() => {\n setDraft(value ?? null)\n setOpen(false)\n }, [value])\n\n const handleToday = React.useCallback(() => {\n const today = withTime ? new Date() : startOfDay(new Date())\n onChange(today)\n setOpen(false)\n }, [withTime, onChange])\n\n const handleClear = React.useCallback(() => {\n onChange(null)\n setOpen(false)\n }, [onChange])\n\n const disabledMatcher = React.useMemo(() => {\n if (!minDate && !maxDate) return undefined\n const matchers: import('react-day-picker').Matcher[] = []\n if (minDate) matchers.push({ before: minDate })\n if (maxDate) matchers.push({ after: maxDate })\n return matchers\n }, [minDate, maxDate])\n\n const heightClass = size === 'sm' ? 'h-8 px-2.5 text-xs' : 'h-9 px-3 text-sm'\n const selectedDate = useDraft ? draft : value\n\n return (\n <Popover open={open} onOpenChange={isInteractive ? setOpen : undefined}>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n id={id}\n name={name}\n aria-label={ariaLabel}\n aria-describedby={ariaDescribedBy}\n aria-haspopup=\"dialog\"\n aria-required={required ? true : undefined}\n data-crud-focus-target=\"\"\n data-slot=\"date-picker-trigger\"\n disabled={disabled}\n className={cn(\n 'w-full inline-flex items-center gap-2 rounded-md border border-input bg-background shadow-xs transition-colors text-left',\n 'focus-visible:outline-none focus-visible:shadow-focus focus-visible:border-foreground',\n 'hover:bg-muted/40',\n 'disabled:bg-bg-disabled disabled:border-border-disabled disabled:shadow-none disabled:hover:bg-bg-disabled disabled:cursor-not-allowed',\n 'aria-invalid:border-destructive',\n heightClass,\n readOnly && 'cursor-default opacity-70',\n !formattedValue && 'text-muted-foreground',\n className,\n )}\n onClick={isInteractive ? undefined : (event) => event.preventDefault()}\n >\n <CalendarIcon className=\"h-4 w-4 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />\n <span className=\"flex-1 truncate\">\n {formattedValue ?? placeholderText}\n </span>\n </button>\n </PopoverTrigger>\n <PopoverContent align={align} className={cn('w-auto p-0', popoverClassName)}>\n <Calendar\n mode=\"single\"\n selected={selectedDate ?? undefined}\n onSelect={handleDaySelect}\n locale={locale}\n disabled={disabledMatcher}\n
|
|
5
|
-
"mappings": ";AAsMQ,SAwBE,KAxBF;AApMR,YAAY,WAAW;AAEvB,SAAS,oBAAoB;AAC7B,SAAS,UAAU;AACnB,SAAS,YAAY;AACrB,SAAS,SAAS,gBAAgB,sBAAsB;AACxD,SAAS,gBAAgB;AACzB,SAAS,cAAc;AACvB,SAAS,kBAAkB;AAC3B,SAAS,iBAAiB;AAC1B,SAAS,4BAA4B,yBAAyB,mCAAmC;AAoDjG,SAAS,YAAY,MAAoB;AACvC,SAAO,GAAG,OAAO,KAAK,SAAS,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,OAAO,KAAK,WAAW,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC;AAClG;AAEA,SAAS,gBAAgB,MAAY,MAAoB;AACvD,QAAM,CAAC,UAAU,KAAK,YAAY,GAAG,IAAI,KAAK,MAAM,GAAG;AACvD,QAAM,OAAO,SAAS,SAAS,EAAE;AACjC,QAAM,SAAS,SAAS,WAAW,EAAE;AACrC,QAAM,OAAO,IAAI,KAAK,IAAI;AAC1B,OAAK,SAAS,MAAM,IAAI,IAAI,IAAI,MAAM,MAAM,MAAM,IAAI,IAAI,QAAQ,GAAG,CAAC;AACtE,SAAO;AACT;AAEA,SAAS,WAAW,MAAkB;AACpC,QAAM,OAAO,IAAI,KAAK,IAAI;AAC1B,OAAK,SAAS,GAAG,GAAG,GAAG,CAAC;AACxB,SAAO;AACT;AAEO,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,WAAW;AAAA,EACX,WAAW;AAAA,EACX,SAAS;AAAA,EACT;AAAA,EACA,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,WAAW;AAAA,EACX,aAAa;AAAA,EACb,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,oBAAoB;AACtB,GAAoB;AAClB,QAAM,wBAAwB,iBAAiB,WAAW;AAC1D,QAAM,IAAI,KAAK;AACf,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,KAAK;AAC5C,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAsB,SAAS,IAAI;AAEnE,QAAM,gBAAgB,CAAC,YAAY,CAAC;AACpC,QAAM,WAAW,WAAW;AAE5B,QAAM,UAAU,MAAM;AACpB,QAAI,KAAM,UAAS,SAAS,IAAI;AAAA,EAClC,GAAG,CAAC,MAAM,KAAK,CAAC;AAEhB,QAAM,iBAAiB,WACnB,4BAA4B,QAAQ,aAAa,IACjD,wBAAwB,QAAQ,aAAa;AACjD,QAAM,kBAAkB,gBACtB,WACI,EAAE,iCAAiC,oBAAoB,IACvD,EAAE,6BAA6B,aAAa;AAGlD,QAAM,iBAAiB,MAAM,QAAQ,MAAM;AACzC,QAAI,CAAC,MAAO,QAAO;AACnB,WAAO,2BAA2B,OAAO,gBAAgB,MAAM;AAAA,EACjE,GAAG,CAAC,OAAO,gBAAgB,MAAM,CAAC;AAElC,QAAM,kBAAkB,MAAM;AAAA,IAC5B,CAAC,QAA0B;AACzB,UAAI,CAAC,OAAO,CAAC,cAAe;AAC5B,YAAM,YAAY,WAAY,WAAW,QAAQ,QAAS;AAC1D,YAAM,OAAO,WACT,gBAAgB,KAAK,YAAY,YAAY,SAAS,IAAI,OAAO,IACjE,WAAW,GAAG;AAClB,UAAI,UAAU;AACZ,iBAAS,IAAI;AAAA,MACf,OAAO;AACL,iBAAS,IAAI;AACb,YAAI,WAAW,UAAU,sBAAuB,SAAQ,KAAK;AAAA,MAC/D;AAAA,IACF;AAAA,IACA,CAAC,eAAe,UAAU,UAAU,OAAO,OAAO,QAAQ,uBAAuB,QAAQ;AAAA,EAC3F;AAEA,QAAM,mBAAmB,MAAM;AAAA,IAC7B,CAAC,SAAiB;AAChB,YAAM,SAAS,WAAW,QAAQ;AAClC,UAAI,CAAC,OAAQ;AACb,YAAM,OAAO,gBAAgB,QAAQ,IAAI;AACzC,UAAI,SAAU,UAAS,IAAI;AAAA,UACtB,UAAS,IAAI;AAAA,IACpB;AAAA,IACA,CAAC,UAAU,OAAO,OAAO,QAAQ;AAAA,EACnC;AAEA,QAAM,cAAc,MAAM,YAAY,MAAM;AAC1C,aAAS,KAAK;AACd,YAAQ,KAAK;AAAA,EACf,GAAG,CAAC,OAAO,QAAQ,CAAC;AAEpB,QAAM,eAAe,MAAM,YAAY,MAAM;AAC3C,aAAS,SAAS,IAAI;AACtB,YAAQ,KAAK;AAAA,EACf,GAAG,CAAC,KAAK,CAAC;AAEV,QAAM,cAAc,MAAM,YAAY,MAAM;AAC1C,UAAM,QAAQ,WAAW,oBAAI,KAAK,IAAI,WAAW,oBAAI,KAAK,CAAC;AAC3D,aAAS,KAAK;AACd,YAAQ,KAAK;AAAA,EACf,GAAG,CAAC,UAAU,QAAQ,CAAC;AAEvB,QAAM,cAAc,MAAM,YAAY,MAAM;AAC1C,aAAS,IAAI;AACb,YAAQ,KAAK;AAAA,EACf,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,kBAAkB,MAAM,QAAQ,MAAM;AAC1C,QAAI,CAAC,WAAW,CAAC,QAAS,QAAO;AACjC,UAAM,WAAiD,CAAC;AACxD,QAAI,QAAS,UAAS,KAAK,EAAE,QAAQ,QAAQ,CAAC;AAC9C,QAAI,QAAS,UAAS,KAAK,EAAE,OAAO,QAAQ,CAAC;AAC7C,WAAO;AAAA,EACT,GAAG,CAAC,SAAS,OAAO,CAAC;AAErB,QAAM,cAAc,SAAS,OAAO,uBAAuB;AAC3D,QAAM,eAAe,WAAW,QAAQ;AAExC,SACE,qBAAC,WAAQ,MAAY,cAAc,gBAAgB,UAAU,QAC3D;AAAA,wBAAC,kBAAe,SAAO,MACrB;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL;AAAA,QACA;AAAA,QACA,cAAY;AAAA,QACZ,oBAAkB;AAAA,QAClB,iBAAc;AAAA,QACd,iBAAe,WAAW,OAAO;AAAA,QACjC,0BAAuB;AAAA,QACvB,aAAU;AAAA,QACV;AAAA,QACA,WAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,YAAY;AAAA,UACZ,CAAC,kBAAkB;AAAA,UACnB;AAAA,QACF;AAAA,QACA,SAAS,gBAAgB,SAAY,CAAC,UAAU,MAAM,eAAe;AAAA,QAErE;AAAA,8BAAC,gBAAa,WAAU,0CAAyC,eAAY,QAAO;AAAA,UACpF,oBAAC,UAAK,WAAU,mBACb,4BAAkB,iBACrB;AAAA;AAAA;AAAA,IACF,GACF;AAAA,IACA,qBAAC,kBAAe,OAAc,WAAW,GAAG,cAAc,gBAAgB,GACxE;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,UAAU,gBAAgB;AAAA,UAC1B,UAAU;AAAA,UACV;AAAA,UACA,UAAU;AAAA,UACV,
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport type { Locale } from 'date-fns/locale'\nimport { CalendarIcon } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Popover, PopoverContent, PopoverTrigger } from './popover'\nimport { Calendar } from './calendar'\nimport { Button } from './button'\nimport { LinkButton } from './link-button'\nimport { TimeInput } from '../backend/inputs/TimeInput'\nimport { formatWithPublicDateFormat, resolvePublicDateFormat, resolvePublicDateTimeFormat } from './date-format'\n\nexport type DatePickerFooter = 'apply-cancel' | 'today-clear' | 'none'\n\nexport type DatePickerProps = {\n value?: Date | null\n onChange: (value: Date | null) => void\n placeholder?: string\n size?: 'sm' | 'default'\n disabled?: boolean\n readOnly?: boolean\n /**\n * Footer mode in the popover.\n * - `'apply-cancel'` (default, Figma-aligned): selecting a day stages a draft;\n * Apply commits and closes; Cancel reverts and closes.\n * - `'today-clear'`: legacy behavior \u2014 Today commits today, Clear sets null.\n * - `'none'`: no footer; selecting a day commits immediately and closes.\n */\n footer?: DatePickerFooter\n /**\n * Whether selecting a day commits the value and closes the popover.\n * - In `footer='apply-cancel'`: ignored (commit happens via Apply).\n * - In `footer='today-clear'`: default `true` for backward compatibility.\n * - In `footer='none'`: always `true` (popover always closes after selection).\n */\n closeOnSelect?: boolean\n /**\n * Whether to show the \"Today\" link in `footer='today-clear'` mode. Default `true`.\n * Only meaningful when `footer='today-clear'`.\n */\n showTodayButton?: boolean\n /**\n * Whether to show the \"Clear\" link in `footer='today-clear'` mode. Default `true`.\n * Only meaningful when `footer='today-clear'`.\n */\n showClearButton?: boolean\n withTime?: boolean\n minuteStep?: number\n align?: 'start' | 'center' | 'end'\n minDate?: Date\n maxDate?: Date\n locale?: Locale\n displayFormat?: string\n className?: string\n popoverClassName?: string\n id?: string\n name?: string\n required?: boolean\n 'aria-label'?: string\n 'aria-describedby'?: string\n}\n\nfunction extractTime(date: Date): string {\n return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`\n}\n\nfunction applyTimeToDate(base: Date, time: string): Date {\n const [hourStr = '0', minuteStr = '0'] = time.split(':')\n const hour = parseInt(hourStr, 10)\n const minute = parseInt(minuteStr, 10)\n const next = new Date(base)\n next.setHours(isNaN(hour) ? 0 : hour, isNaN(minute) ? 0 : minute, 0, 0)\n return next\n}\n\nfunction startOfDay(date: Date): Date {\n const next = new Date(date)\n next.setHours(0, 0, 0, 0)\n return next\n}\n\nexport function DatePicker({\n value,\n onChange,\n placeholder,\n size = 'default',\n disabled = false,\n readOnly = false,\n footer = 'apply-cancel',\n closeOnSelect,\n showTodayButton = true,\n showClearButton = true,\n withTime = false,\n minuteStep = 1,\n align = 'start',\n minDate,\n maxDate,\n locale,\n displayFormat,\n className,\n popoverClassName,\n id,\n name,\n required,\n 'aria-label': ariaLabel,\n 'aria-describedby': ariaDescribedBy,\n}: DatePickerProps) {\n const resolvedCloseOnSelect = closeOnSelect ?? footer === 'today-clear'\n const t = useT()\n const [open, setOpen] = React.useState(false)\n const [draft, setDraft] = React.useState<Date | null>(value ?? null)\n\n const isInteractive = !disabled && !readOnly\n const useDraft = footer === 'apply-cancel'\n\n React.useEffect(() => {\n if (open) setDraft(value ?? null)\n }, [open, value])\n\n const resolvedFormat = withTime\n ? resolvePublicDateTimeFormat(locale, displayFormat)\n : resolvePublicDateFormat(locale, displayFormat)\n const placeholderText = placeholder ?? (\n withTime\n ? t('ui.dateTimePicker.placeholder', 'Pick date and time')\n : t('ui.datePicker.placeholder', 'Pick a date')\n )\n\n const formattedValue = React.useMemo(() => {\n if (!value) return null\n return formatWithPublicDateFormat(value, resolvedFormat, locale)\n }, [value, resolvedFormat, locale])\n\n const handleDaySelect = React.useCallback(\n (day: Date | undefined) => {\n if (!day || !isInteractive) return\n const reference = withTime ? (useDraft ? draft : value) : null\n const base = withTime\n ? applyTimeToDate(day, reference ? extractTime(reference) : '00:00')\n : startOfDay(day)\n if (useDraft) {\n setDraft(base)\n } else {\n onChange(base)\n if (footer === 'none' || resolvedCloseOnSelect) setOpen(false)\n }\n },\n [isInteractive, withTime, useDraft, draft, value, footer, resolvedCloseOnSelect, onChange],\n )\n\n const handleTimeChange = React.useCallback(\n (time: string) => {\n const target = useDraft ? draft : value\n if (!target) return\n const next = applyTimeToDate(target, time)\n if (useDraft) setDraft(next)\n else onChange(next)\n },\n [useDraft, draft, value, onChange],\n )\n\n const handleApply = React.useCallback(() => {\n onChange(draft)\n setOpen(false)\n }, [draft, onChange])\n\n const handleCancel = React.useCallback(() => {\n setDraft(value ?? null)\n setOpen(false)\n }, [value])\n\n const handleToday = React.useCallback(() => {\n const today = withTime ? new Date() : startOfDay(new Date())\n onChange(today)\n setOpen(false)\n }, [withTime, onChange])\n\n const handleClear = React.useCallback(() => {\n onChange(null)\n setOpen(false)\n }, [onChange])\n\n const disabledMatcher = React.useMemo(() => {\n if (!minDate && !maxDate) return undefined\n const matchers: import('react-day-picker').Matcher[] = []\n if (minDate) matchers.push({ before: minDate })\n if (maxDate) matchers.push({ after: maxDate })\n return matchers\n }, [minDate, maxDate])\n\n const heightClass = size === 'sm' ? 'h-8 px-2.5 text-xs' : 'h-9 px-3 text-sm'\n const selectedDate = useDraft ? draft : value\n\n return (\n <Popover open={open} onOpenChange={isInteractive ? setOpen : undefined}>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n id={id}\n name={name}\n aria-label={ariaLabel}\n aria-describedby={ariaDescribedBy}\n aria-haspopup=\"dialog\"\n aria-required={required ? true : undefined}\n data-crud-focus-target=\"\"\n data-slot=\"date-picker-trigger\"\n disabled={disabled}\n className={cn(\n 'w-full inline-flex items-center gap-2 rounded-md border border-input bg-background shadow-xs transition-colors text-left',\n 'focus-visible:outline-none focus-visible:shadow-focus focus-visible:border-foreground',\n 'hover:bg-muted/40',\n 'disabled:bg-bg-disabled disabled:border-border-disabled disabled:shadow-none disabled:hover:bg-bg-disabled disabled:cursor-not-allowed',\n 'aria-invalid:border-destructive',\n heightClass,\n readOnly && 'cursor-default opacity-70',\n !formattedValue && 'text-muted-foreground',\n className,\n )}\n onClick={isInteractive ? undefined : (event) => event.preventDefault()}\n >\n <CalendarIcon className=\"h-4 w-4 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />\n <span className=\"flex-1 truncate\">\n {formattedValue ?? placeholderText}\n </span>\n </button>\n </PopoverTrigger>\n <PopoverContent align={align} className={cn('w-auto p-0', popoverClassName)}>\n <Calendar\n mode=\"single\"\n selected={selectedDate ?? undefined}\n onSelect={handleDaySelect}\n locale={locale}\n disabled={disabledMatcher}\n autoFocus\n />\n {withTime && (\n <div className=\"flex items-center gap-2 border-t px-3 py-2\">\n <span className=\"text-sm text-muted-foreground shrink-0\">\n {t('ui.datePicker.timeLabel', 'Time')}:\n </span>\n <TimeInput\n value={selectedDate ? extractTime(selectedDate) : undefined}\n onChange={handleTimeChange}\n minuteStep={minuteStep}\n disabled={!isInteractive || !selectedDate}\n />\n </div>\n )}\n {footer === 'apply-cancel' && (\n <div className=\"flex items-center justify-end gap-2 border-t px-3 py-2\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={handleCancel}>\n {t('ui.datePicker.cancelButton', 'Cancel')}\n </Button>\n <Button type=\"button\" size=\"sm\" onClick={handleApply}>\n {t('ui.datePicker.applyButton', 'Apply')}\n </Button>\n </div>\n )}\n {footer === 'today-clear' && (showTodayButton || showClearButton) && (\n <div className=\"flex items-center justify-between gap-2 border-t px-3 py-2\">\n {showTodayButton ? (\n <LinkButton type=\"button\" variant=\"primary\" onClick={handleToday}>\n {t('ui.datePicker.todayButton', 'Today')}\n </LinkButton>\n ) : null}\n {showClearButton ? (\n <LinkButton type=\"button\" variant=\"gray\" onClick={handleClear} className=\"ml-auto\">\n {t('ui.datePicker.clearButton', 'Clear')}\n </LinkButton>\n ) : null}\n </div>\n )}\n </PopoverContent>\n </Popover>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/primitives/date-range-picker.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport type { Locale } from 'date-fns'\nimport { CalendarIcon } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Popover, PopoverContent, PopoverTrigger } from './popover'\nimport { Calendar } from './calendar'\nimport { Button } from './button'\nimport type { DateRange as RDPRange } from 'react-day-picker'\nimport type { DateRange } from '../backend/date-range/dateRanges'\nimport {\n defaultDateRangePresets,\n type DateRangePresetItem,\n} from './date-picker-helpers'\nimport { formatWithPublicDateFormat, resolvePublicDateFormat } from './date-format'\n\nexport type { DateRangePresetItem } from './date-picker-helpers'\n\nexport type DateRangePickerProps = {\n value?: DateRange | null\n onChange: (value: DateRange | null) => void\n presets?: DateRangePresetItem[]\n showPresets?: boolean\n placeholder?: string\n size?: 'sm' | 'default'\n disabled?: boolean\n readOnly?: boolean\n withFooter?: boolean\n align?: 'start' | 'center' | 'end'\n minDate?: Date\n maxDate?: Date\n numberOfMonths?: 1 | 2\n locale?: Locale\n formatRange?: (value: DateRange, locale?: Locale) => string\n className?: string\n popoverClassName?: string\n id?: string\n name?: string\n required?: boolean\n 'aria-label'?: string\n 'aria-describedby'?: string\n}\n\nfunction defaultFormatRange(range: DateRange, locale?: Locale): string {\n const fmt = resolvePublicDateFormat(locale)\n return `${formatWithPublicDateFormat(range.start, fmt, locale) ?? ''} \u2013 ${formatWithPublicDateFormat(range.end, fmt, locale) ?? ''}`\n}\n\nfunction toRDPRange(range: DateRange | null | undefined): RDPRange | undefined {\n if (!range) return undefined\n return { from: range.start, to: range.end }\n}\n\nfunction fromRDPRange(rdp: RDPRange | undefined): DateRange | null {\n if (!rdp || !rdp.from) return null\n return { start: rdp.from, end: rdp.to ?? rdp.from }\n}\n\nfunction rangesEqual(a: DateRange | null, b: DateRange | null): boolean {\n if (!a && !b) return true\n if (!a || !b) return false\n return a.start.getTime() === b.start.getTime() && a.end.getTime() === b.end.getTime()\n}\n\nexport function DateRangePicker({\n value,\n onChange,\n presets,\n showPresets = true,\n placeholder,\n size = 'default',\n disabled = false,\n readOnly = false,\n withFooter = true,\n align = 'start',\n minDate,\n maxDate,\n numberOfMonths = 2,\n locale,\n formatRange,\n className,\n popoverClassName,\n id,\n name,\n required,\n 'aria-label': ariaLabel,\n 'aria-describedby': ariaDescribedBy,\n}: DateRangePickerProps) {\n const t = useT()\n const [open, setOpen] = React.useState(false)\n const [draft, setDraft] = React.useState<DateRange | null>(value ?? null)\n const [activePresetId, setActivePresetId] = React.useState<string | null>(null)\n\n const isInteractive = !disabled && !readOnly\n const useDraft = withFooter\n\n React.useEffect(() => {\n if (open) {\n setDraft(value ?? null)\n setActivePresetId(null)\n }\n }, [open, value])\n\n const resolvedPresets = React.useMemo<DateRangePresetItem[]>(\n () => presets ?? defaultDateRangePresets(),\n [presets],\n )\n\n const placeholderText = placeholder ?? t('ui.dateRangePicker.placeholder', 'Pick a date range')\n const formatter = formatRange ?? defaultFormatRange\n const formattedValue = React.useMemo(() => {\n if (!value) return null\n return formatter(value, locale)\n }, [value, formatter, locale])\n\n const selectedDate = useDraft ? draft : value\n const rdpSelected = toRDPRange(selectedDate)\n\n const handleRangeSelect = React.useCallback(\n (rdpRange: RDPRange | undefined) => {\n if (!isInteractive) return\n const next = fromRDPRange(rdpRange)\n setActivePresetId(null)\n if (useDraft) {\n setDraft(next)\n } else {\n onChange(next)\n if (next?.start && next?.end) setOpen(false)\n }\n },\n [isInteractive, useDraft, onChange],\n )\n\n const handlePresetClick = React.useCallback(\n (preset: DateRangePresetItem) => {\n if (!isInteractive) return\n const range = preset.range()\n setActivePresetId(preset.id)\n if (useDraft) {\n setDraft(range)\n } else {\n onChange(range)\n setOpen(false)\n }\n },\n [isInteractive, useDraft, onChange],\n )\n\n const handleApply = React.useCallback(() => {\n onChange(draft)\n setOpen(false)\n }, [draft, onChange])\n\n const handleCancel = React.useCallback(() => {\n setDraft(value ?? null)\n setActivePresetId(null)\n setOpen(false)\n }, [value])\n\n const disabledMatcher = React.useMemo(() => {\n if (!minDate && !maxDate) return undefined\n const matchers: import('react-day-picker').Matcher[] = []\n if (minDate) matchers.push({ before: minDate })\n if (maxDate) matchers.push({ after: maxDate })\n return matchers\n }, [minDate, maxDate])\n\n const heightClass = size === 'sm' ? 'h-8 px-2.5 text-xs' : 'h-9 px-3 text-sm'\n\n return (\n <Popover open={open} onOpenChange={isInteractive ? setOpen : undefined}>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n id={id}\n name={name}\n aria-label={ariaLabel}\n aria-describedby={ariaDescribedBy}\n aria-haspopup=\"dialog\"\n aria-required={required ? true : undefined}\n data-crud-focus-target=\"\"\n data-slot=\"date-range-picker-trigger\"\n disabled={disabled}\n className={cn(\n 'w-full inline-flex items-center gap-2 rounded-md border border-input bg-background shadow-xs transition-colors text-left',\n 'focus-visible:outline-none focus-visible:shadow-focus focus-visible:border-foreground',\n 'hover:bg-muted/40',\n 'disabled:bg-bg-disabled disabled:border-border-disabled disabled:shadow-none disabled:hover:bg-bg-disabled disabled:cursor-not-allowed',\n 'aria-invalid:border-destructive',\n heightClass,\n readOnly && 'cursor-default opacity-70',\n !formattedValue && 'text-muted-foreground',\n className,\n )}\n onClick={isInteractive ? undefined : (event) => event.preventDefault()}\n >\n <CalendarIcon className=\"h-4 w-4 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />\n <span className=\"flex-1 truncate\">\n {formattedValue ?? placeholderText}\n </span>\n </button>\n </PopoverTrigger>\n <PopoverContent\n align={align}\n className={cn(\n 'w-auto p-0 flex flex-col overflow-hidden',\n 'max-h-[var(--radix-popover-content-available-height)]',\n popoverClassName,\n )}\n >\n <div className=\"flex min-h-0 flex-1 overflow-auto\">\n {showPresets ? (\n <div\n data-slot=\"date-range-presets\"\n className=\"flex w-[200px] shrink-0 flex-col gap-0.5 overflow-y-auto border-r p-2\"\n >\n {resolvedPresets.map((preset) => {\n const isActive = activePresetId === preset.id\n return (\n <button\n key={preset.id}\n type=\"button\"\n onClick={() => handlePresetClick(preset)}\n className={cn(\n 'inline-flex h-8 items-center rounded-md px-3 text-left text-sm transition-colors shrink-0',\n 'hover:bg-accent hover:text-accent-foreground',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n isActive && 'bg-muted font-medium text-foreground',\n )}\n >\n {t(preset.labelKey, preset.id.replace(/_/g, ' '))}\n </button>\n )\n })}\n </div>\n ) : null}\n <div className=\"p-3\">\n <Calendar\n mode=\"range\"\n selected={rdpSelected}\n onSelect={handleRangeSelect}\n locale={locale}\n disabled={disabledMatcher}\n numberOfMonths={numberOfMonths}\n
|
|
5
|
-
"mappings": ";AA8KQ,SAwBE,KAxBF;AA5KR,YAAY,WAAW;AAEvB,SAAS,oBAAoB;AAC7B,SAAS,UAAU;AACnB,SAAS,YAAY;AACrB,SAAS,SAAS,gBAAgB,sBAAsB;AACxD,SAAS,gBAAgB;AACzB,SAAS,cAAc;AAGvB;AAAA,EACE;AAAA,OAEK;AACP,SAAS,4BAA4B,+BAA+B;AA6BpE,SAAS,mBAAmB,OAAkB,QAAyB;AACrE,QAAM,MAAM,wBAAwB,MAAM;AAC1C,SAAO,GAAG,2BAA2B,MAAM,OAAO,KAAK,MAAM,KAAK,EAAE,WAAM,2BAA2B,MAAM,KAAK,KAAK,MAAM,KAAK,EAAE;AACpI;AAEA,SAAS,WAAW,OAA2D;AAC7E,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,EAAE,MAAM,MAAM,OAAO,IAAI,MAAM,IAAI;AAC5C;AAEA,SAAS,aAAa,KAA6C;AACjE,MAAI,CAAC,OAAO,CAAC,IAAI,KAAM,QAAO;AAC9B,SAAO,EAAE,OAAO,IAAI,MAAM,KAAK,IAAI,MAAM,IAAI,KAAK;AACpD;AAEA,SAAS,YAAY,GAAqB,GAA8B;AACtE,MAAI,CAAC,KAAK,CAAC,EAAG,QAAO;AACrB,MAAI,CAAC,KAAK,CAAC,EAAG,QAAO;AACrB,SAAO,EAAE,MAAM,QAAQ,MAAM,EAAE,MAAM,QAAQ,KAAK,EAAE,IAAI,QAAQ,MAAM,EAAE,IAAI,QAAQ;AACtF;AAEO,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA,OAAO;AAAA,EACP,WAAW;AAAA,EACX,WAAW;AAAA,EACX,aAAa;AAAA,EACb,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA,iBAAiB;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,oBAAoB;AACtB,GAAyB;AACvB,QAAM,IAAI,KAAK;AACf,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,KAAK;AAC5C,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAA2B,SAAS,IAAI;AACxE,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,MAAM,SAAwB,IAAI;AAE9E,QAAM,gBAAgB,CAAC,YAAY,CAAC;AACpC,QAAM,WAAW;AAEjB,QAAM,UAAU,MAAM;AACpB,QAAI,MAAM;AACR,eAAS,SAAS,IAAI;AACtB,wBAAkB,IAAI;AAAA,IACxB;AAAA,EACF,GAAG,CAAC,MAAM,KAAK,CAAC;AAEhB,QAAM,kBAAkB,MAAM;AAAA,IAC5B,MAAM,WAAW,wBAAwB;AAAA,IACzC,CAAC,OAAO;AAAA,EACV;AAEA,QAAM,kBAAkB,eAAe,EAAE,kCAAkC,mBAAmB;AAC9F,QAAM,YAAY,eAAe;AACjC,QAAM,iBAAiB,MAAM,QAAQ,MAAM;AACzC,QAAI,CAAC,MAAO,QAAO;AACnB,WAAO,UAAU,OAAO,MAAM;AAAA,EAChC,GAAG,CAAC,OAAO,WAAW,MAAM,CAAC;AAE7B,QAAM,eAAe,WAAW,QAAQ;AACxC,QAAM,cAAc,WAAW,YAAY;AAE3C,QAAM,oBAAoB,MAAM;AAAA,IAC9B,CAAC,aAAmC;AAClC,UAAI,CAAC,cAAe;AACpB,YAAM,OAAO,aAAa,QAAQ;AAClC,wBAAkB,IAAI;AACtB,UAAI,UAAU;AACZ,iBAAS,IAAI;AAAA,MACf,OAAO;AACL,iBAAS,IAAI;AACb,YAAI,MAAM,SAAS,MAAM,IAAK,SAAQ,KAAK;AAAA,MAC7C;AAAA,IACF;AAAA,IACA,CAAC,eAAe,UAAU,QAAQ;AAAA,EACpC;AAEA,QAAM,oBAAoB,MAAM;AAAA,IAC9B,CAAC,WAAgC;AAC/B,UAAI,CAAC,cAAe;AACpB,YAAM,QAAQ,OAAO,MAAM;AAC3B,wBAAkB,OAAO,EAAE;AAC3B,UAAI,UAAU;AACZ,iBAAS,KAAK;AAAA,MAChB,OAAO;AACL,iBAAS,KAAK;AACd,gBAAQ,KAAK;AAAA,MACf;AAAA,IACF;AAAA,IACA,CAAC,eAAe,UAAU,QAAQ;AAAA,EACpC;AAEA,QAAM,cAAc,MAAM,YAAY,MAAM;AAC1C,aAAS,KAAK;AACd,YAAQ,KAAK;AAAA,EACf,GAAG,CAAC,OAAO,QAAQ,CAAC;AAEpB,QAAM,eAAe,MAAM,YAAY,MAAM;AAC3C,aAAS,SAAS,IAAI;AACtB,sBAAkB,IAAI;AACtB,YAAQ,KAAK;AAAA,EACf,GAAG,CAAC,KAAK,CAAC;AAEV,QAAM,kBAAkB,MAAM,QAAQ,MAAM;AAC1C,QAAI,CAAC,WAAW,CAAC,QAAS,QAAO;AACjC,UAAM,WAAiD,CAAC;AACxD,QAAI,QAAS,UAAS,KAAK,EAAE,QAAQ,QAAQ,CAAC;AAC9C,QAAI,QAAS,UAAS,KAAK,EAAE,OAAO,QAAQ,CAAC;AAC7C,WAAO;AAAA,EACT,GAAG,CAAC,SAAS,OAAO,CAAC;AAErB,QAAM,cAAc,SAAS,OAAO,uBAAuB;AAE3D,SACE,qBAAC,WAAQ,MAAY,cAAc,gBAAgB,UAAU,QAC3D;AAAA,wBAAC,kBAAe,SAAO,MACrB;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL;AAAA,QACA;AAAA,QACA,cAAY;AAAA,QACZ,oBAAkB;AAAA,QAClB,iBAAc;AAAA,QACd,iBAAe,WAAW,OAAO;AAAA,QACjC,0BAAuB;AAAA,QACvB,aAAU;AAAA,QACV;AAAA,QACA,WAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,YAAY;AAAA,UACZ,CAAC,kBAAkB;AAAA,UACnB;AAAA,QACF;AAAA,QACA,SAAS,gBAAgB,SAAY,CAAC,UAAU,MAAM,eAAe;AAAA,QAErE;AAAA,8BAAC,gBAAa,WAAU,0CAAyC,eAAY,QAAO;AAAA,UACpF,oBAAC,UAAK,WAAU,mBACb,4BAAkB,iBACrB;AAAA;AAAA;AAAA,IACF,GACF;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,WAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,QAEA;AAAA,+BAAC,SAAI,WAAU,qCACZ;AAAA,0BACC;AAAA,cAAC;AAAA;AAAA,gBACC,aAAU;AAAA,gBACV,WAAU;AAAA,gBAET,0BAAgB,IAAI,CAAC,WAAW;AAC/B,wBAAM,WAAW,mBAAmB,OAAO;AAC3C,yBACE;AAAA,oBAAC;AAAA;AAAA,sBAEC,MAAK;AAAA,sBACL,SAAS,MAAM,kBAAkB,MAAM;AAAA,sBACvC,WAAW;AAAA,wBACT;AAAA,wBACA;AAAA,wBACA;AAAA,wBACA,YAAY;AAAA,sBACd;AAAA,sBAEC,YAAE,OAAO,UAAU,OAAO,GAAG,QAAQ,MAAM,GAAG,CAAC;AAAA;AAAA,oBAV3C,OAAO;AAAA,kBAWd;AAAA,gBAEJ,CAAC;AAAA;AAAA,YACH,IACE;AAAA,YACJ,oBAAC,SAAI,WAAU,OACb;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,UAAU;AAAA,gBACV,UAAU;AAAA,gBACV;AAAA,gBACA,UAAU;AAAA,gBACV;AAAA,gBACA,
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport type { Locale } from 'date-fns/locale'\nimport { CalendarIcon } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Popover, PopoverContent, PopoverTrigger } from './popover'\nimport { Calendar } from './calendar'\nimport { Button } from './button'\nimport type { DateRange as RDPRange } from 'react-day-picker'\nimport type { DateRange } from '../backend/date-range/dateRanges'\nimport {\n defaultDateRangePresets,\n type DateRangePresetItem,\n} from './date-picker-helpers'\nimport { formatWithPublicDateFormat, resolvePublicDateFormat } from './date-format'\n\nexport type { DateRangePresetItem } from './date-picker-helpers'\n\nexport type DateRangePickerProps = {\n value?: DateRange | null\n onChange: (value: DateRange | null) => void\n presets?: DateRangePresetItem[]\n showPresets?: boolean\n placeholder?: string\n size?: 'sm' | 'default'\n disabled?: boolean\n readOnly?: boolean\n withFooter?: boolean\n align?: 'start' | 'center' | 'end'\n minDate?: Date\n maxDate?: Date\n numberOfMonths?: 1 | 2\n locale?: Locale\n formatRange?: (value: DateRange, locale?: Locale) => string\n className?: string\n popoverClassName?: string\n id?: string\n name?: string\n required?: boolean\n 'aria-label'?: string\n 'aria-describedby'?: string\n}\n\nfunction defaultFormatRange(range: DateRange, locale?: Locale): string {\n const fmt = resolvePublicDateFormat(locale)\n return `${formatWithPublicDateFormat(range.start, fmt, locale) ?? ''} \u2013 ${formatWithPublicDateFormat(range.end, fmt, locale) ?? ''}`\n}\n\nfunction toRDPRange(range: DateRange | null | undefined): RDPRange | undefined {\n if (!range) return undefined\n return { from: range.start, to: range.end }\n}\n\nfunction fromRDPRange(rdp: RDPRange | undefined): DateRange | null {\n if (!rdp || !rdp.from) return null\n return { start: rdp.from, end: rdp.to ?? rdp.from }\n}\n\nfunction rangesEqual(a: DateRange | null, b: DateRange | null): boolean {\n if (!a && !b) return true\n if (!a || !b) return false\n return a.start.getTime() === b.start.getTime() && a.end.getTime() === b.end.getTime()\n}\n\nexport function DateRangePicker({\n value,\n onChange,\n presets,\n showPresets = true,\n placeholder,\n size = 'default',\n disabled = false,\n readOnly = false,\n withFooter = true,\n align = 'start',\n minDate,\n maxDate,\n numberOfMonths = 2,\n locale,\n formatRange,\n className,\n popoverClassName,\n id,\n name,\n required,\n 'aria-label': ariaLabel,\n 'aria-describedby': ariaDescribedBy,\n}: DateRangePickerProps) {\n const t = useT()\n const [open, setOpen] = React.useState(false)\n const [draft, setDraft] = React.useState<DateRange | null>(value ?? null)\n const [activePresetId, setActivePresetId] = React.useState<string | null>(null)\n\n const isInteractive = !disabled && !readOnly\n const useDraft = withFooter\n\n React.useEffect(() => {\n if (open) {\n setDraft(value ?? null)\n setActivePresetId(null)\n }\n }, [open, value])\n\n const resolvedPresets = React.useMemo<DateRangePresetItem[]>(\n () => presets ?? defaultDateRangePresets(),\n [presets],\n )\n\n const placeholderText = placeholder ?? t('ui.dateRangePicker.placeholder', 'Pick a date range')\n const formatter = formatRange ?? defaultFormatRange\n const formattedValue = React.useMemo(() => {\n if (!value) return null\n return formatter(value, locale)\n }, [value, formatter, locale])\n\n const selectedDate = useDraft ? draft : value\n const rdpSelected = toRDPRange(selectedDate)\n\n const handleRangeSelect = React.useCallback(\n (rdpRange: RDPRange | undefined) => {\n if (!isInteractive) return\n const next = fromRDPRange(rdpRange)\n setActivePresetId(null)\n if (useDraft) {\n setDraft(next)\n } else {\n onChange(next)\n if (next?.start && next?.end) setOpen(false)\n }\n },\n [isInteractive, useDraft, onChange],\n )\n\n const handlePresetClick = React.useCallback(\n (preset: DateRangePresetItem) => {\n if (!isInteractive) return\n const range = preset.range()\n setActivePresetId(preset.id)\n if (useDraft) {\n setDraft(range)\n } else {\n onChange(range)\n setOpen(false)\n }\n },\n [isInteractive, useDraft, onChange],\n )\n\n const handleApply = React.useCallback(() => {\n onChange(draft)\n setOpen(false)\n }, [draft, onChange])\n\n const handleCancel = React.useCallback(() => {\n setDraft(value ?? null)\n setActivePresetId(null)\n setOpen(false)\n }, [value])\n\n const disabledMatcher = React.useMemo(() => {\n if (!minDate && !maxDate) return undefined\n const matchers: import('react-day-picker').Matcher[] = []\n if (minDate) matchers.push({ before: minDate })\n if (maxDate) matchers.push({ after: maxDate })\n return matchers\n }, [minDate, maxDate])\n\n const heightClass = size === 'sm' ? 'h-8 px-2.5 text-xs' : 'h-9 px-3 text-sm'\n\n return (\n <Popover open={open} onOpenChange={isInteractive ? setOpen : undefined}>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n id={id}\n name={name}\n aria-label={ariaLabel}\n aria-describedby={ariaDescribedBy}\n aria-haspopup=\"dialog\"\n aria-required={required ? true : undefined}\n data-crud-focus-target=\"\"\n data-slot=\"date-range-picker-trigger\"\n disabled={disabled}\n className={cn(\n 'w-full inline-flex items-center gap-2 rounded-md border border-input bg-background shadow-xs transition-colors text-left',\n 'focus-visible:outline-none focus-visible:shadow-focus focus-visible:border-foreground',\n 'hover:bg-muted/40',\n 'disabled:bg-bg-disabled disabled:border-border-disabled disabled:shadow-none disabled:hover:bg-bg-disabled disabled:cursor-not-allowed',\n 'aria-invalid:border-destructive',\n heightClass,\n readOnly && 'cursor-default opacity-70',\n !formattedValue && 'text-muted-foreground',\n className,\n )}\n onClick={isInteractive ? undefined : (event) => event.preventDefault()}\n >\n <CalendarIcon className=\"h-4 w-4 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />\n <span className=\"flex-1 truncate\">\n {formattedValue ?? placeholderText}\n </span>\n </button>\n </PopoverTrigger>\n <PopoverContent\n align={align}\n className={cn(\n 'w-auto p-0 flex flex-col overflow-hidden',\n 'max-h-[var(--radix-popover-content-available-height)]',\n popoverClassName,\n )}\n >\n <div className=\"flex min-h-0 flex-1 overflow-auto\">\n {showPresets ? (\n <div\n data-slot=\"date-range-presets\"\n className=\"flex w-[200px] shrink-0 flex-col gap-0.5 overflow-y-auto border-r p-2\"\n >\n {resolvedPresets.map((preset) => {\n const isActive = activePresetId === preset.id\n return (\n <button\n key={preset.id}\n type=\"button\"\n onClick={() => handlePresetClick(preset)}\n className={cn(\n 'inline-flex h-8 items-center rounded-md px-3 text-left text-sm transition-colors shrink-0',\n 'hover:bg-accent hover:text-accent-foreground',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n isActive && 'bg-muted font-medium text-foreground',\n )}\n >\n {t(preset.labelKey, preset.id.replace(/_/g, ' '))}\n </button>\n )\n })}\n </div>\n ) : null}\n <div className=\"p-3\">\n <Calendar\n mode=\"range\"\n selected={rdpSelected}\n onSelect={handleRangeSelect}\n locale={locale}\n disabled={disabledMatcher}\n numberOfMonths={numberOfMonths}\n autoFocus\n />\n </div>\n </div>\n {withFooter ? (\n <div className=\"flex shrink-0 items-center justify-between gap-3 border-t bg-popover px-3 py-2\">\n <span className=\"truncate text-xs text-muted-foreground\">\n {selectedDate\n ? `${t('ui.dateRangePicker.rangeLabel', 'Range')}: ${formatter(selectedDate, locale)}`\n : t('ui.dateRangePicker.rangeEmpty', 'No range selected')}\n </span>\n <div className=\"flex shrink-0 items-center gap-2\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={handleCancel}>\n {t('ui.dateRangePicker.cancelButton', 'Cancel')}\n </Button>\n <Button type=\"button\" size=\"sm\" onClick={handleApply}>\n {t('ui.dateRangePicker.applyButton', 'Apply')}\n </Button>\n </div>\n </div>\n ) : null}\n </PopoverContent>\n </Popover>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/jest.config.cjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** @type {import('jest').Config} */
|
|
2
2
|
module.exports = {
|
|
3
3
|
testEnvironment: 'jsdom',
|
|
4
|
+
testTimeout: 30000,
|
|
4
5
|
watchman: false,
|
|
5
6
|
rootDir: '.',
|
|
6
7
|
maxWorkers: 4,
|
|
@@ -18,13 +19,15 @@ module.exports = {
|
|
|
18
19
|
{
|
|
19
20
|
tsconfig: {
|
|
20
21
|
jsx: 'react-jsx',
|
|
22
|
+
rootDir: '.',
|
|
23
|
+
ignoreDeprecations: '6.0',
|
|
21
24
|
},
|
|
22
25
|
},
|
|
23
26
|
],
|
|
24
27
|
},
|
|
25
28
|
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
|
|
26
29
|
transformIgnorePatterns: [
|
|
27
|
-
'node_modules/(?!(@mikro-orm)/)',
|
|
30
|
+
'node_modules/(?!(@mikro-orm|kysely)/)',
|
|
28
31
|
],
|
|
29
32
|
testMatch: [
|
|
30
33
|
'<rootDir>/src/**/__tests__/**/*.test.(ts|tsx)',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/ui",
|
|
3
|
-
"version": "0.6.2
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -133,21 +133,21 @@
|
|
|
133
133
|
"@radix-ui/react-select": "^2.1.6",
|
|
134
134
|
"@radix-ui/react-tooltip": "^1.2.8",
|
|
135
135
|
"@tanstack/react-virtual": "^3.13.24",
|
|
136
|
-
"date-fns": "4.1
|
|
136
|
+
"date-fns": "4.2.1",
|
|
137
137
|
"react-big-calendar": "^1.19.4",
|
|
138
|
-
"react-day-picker": "^
|
|
138
|
+
"react-day-picker": "^10.0.1",
|
|
139
139
|
"react-markdown": "^10.1.0",
|
|
140
140
|
"recharts": "^3.8.1",
|
|
141
141
|
"remark-gfm": "^4.0.1"
|
|
142
142
|
},
|
|
143
143
|
"peerDependencies": {
|
|
144
|
-
"@open-mercato/shared": "0.6.2
|
|
144
|
+
"@open-mercato/shared": "0.6.2",
|
|
145
145
|
"react": ">=18.0.0",
|
|
146
146
|
"react-dom": ">=18.0.0",
|
|
147
147
|
"react-is": ">=18.0.0"
|
|
148
148
|
},
|
|
149
149
|
"devDependencies": {
|
|
150
|
-
"@open-mercato/shared": "0.6.2
|
|
150
|
+
"@open-mercato/shared": "0.6.2",
|
|
151
151
|
"@testing-library/dom": "^10.4.1",
|
|
152
152
|
"@testing-library/jest-dom": "^6.9.1",
|
|
153
153
|
"@testing-library/react": "^16.3.1",
|
|
@@ -168,6 +168,5 @@
|
|
|
168
168
|
"type": "git",
|
|
169
169
|
"url": "https://github.com/open-mercato/open-mercato",
|
|
170
170
|
"directory": "packages/ui"
|
|
171
|
-
}
|
|
172
|
-
"stableVersion": "0.6.1"
|
|
171
|
+
}
|
|
173
172
|
}
|
|
@@ -185,6 +185,19 @@ function createErrorResponse(status: number, payload: Record<string, unknown>):
|
|
|
185
185
|
describe('<AiChat>', () => {
|
|
186
186
|
beforeEach(() => {
|
|
187
187
|
jest.clearAllMocks()
|
|
188
|
+
const apiCallMock = apiCall as unknown as jest.Mock
|
|
189
|
+
apiCallMock.mockReset()
|
|
190
|
+
apiCallMock.mockResolvedValue({
|
|
191
|
+
ok: true,
|
|
192
|
+
status: 200,
|
|
193
|
+
result: {
|
|
194
|
+
agentId: 'customers.account_assistant',
|
|
195
|
+
allowRuntimeModelOverride: false,
|
|
196
|
+
defaultProviderId: 'openai',
|
|
197
|
+
defaultModelId: 'gpt-5-mini',
|
|
198
|
+
providers: [],
|
|
199
|
+
},
|
|
200
|
+
})
|
|
188
201
|
window.localStorage.clear()
|
|
189
202
|
lastResizeObserver = null
|
|
190
203
|
})
|
|
@@ -238,7 +251,9 @@ describe('<AiChat>', () => {
|
|
|
238
251
|
'Default: openai / gpt-5-mini',
|
|
239
252
|
)
|
|
240
253
|
})
|
|
241
|
-
|
|
254
|
+
await waitFor(() => {
|
|
255
|
+
expect(window.localStorage.getItem('om-ai-model-picker:customers.account_assistant')).toBeNull()
|
|
256
|
+
})
|
|
242
257
|
})
|
|
243
258
|
|
|
244
259
|
it('does not send provider or model overrides while the model picker is on Default', async () => {
|
|
@@ -293,7 +308,7 @@ describe('<AiChat>', () => {
|
|
|
293
308
|
expect(parsedUrl.searchParams.get('agent')).toBe('customers.deal_analyzer')
|
|
294
309
|
expect(parsedUrl.searchParams.has('provider')).toBe(false)
|
|
295
310
|
expect(parsedUrl.searchParams.has('model')).toBe(false)
|
|
296
|
-
})
|
|
311
|
+
}, 15_000)
|
|
297
312
|
|
|
298
313
|
it('renders a compact footer before a constrained host is measured', async () => {
|
|
299
314
|
const apiCallMock = apiCall as unknown as jest.Mock
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { listAiServerConversations } from '../conversation-store'
|
|
2
|
+
|
|
3
|
+
describe('AI conversation store', () => {
|
|
4
|
+
const originalFetch = global.fetch
|
|
5
|
+
|
|
6
|
+
afterEach(() => {
|
|
7
|
+
global.fetch = originalFetch
|
|
8
|
+
jest.restoreAllMocks()
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('suppresses auth redirects for optional conversation sync calls', async () => {
|
|
12
|
+
const fetchMock = jest.fn(async () => new Response(JSON.stringify({ error: 'Forbidden' }), { status: 403 }))
|
|
13
|
+
global.fetch = fetchMock as unknown as typeof fetch
|
|
14
|
+
|
|
15
|
+
await expect(listAiServerConversations({ limit: 100 })).resolves.toBeNull()
|
|
16
|
+
|
|
17
|
+
expect(fetchMock).toHaveBeenCalledWith(
|
|
18
|
+
'/api/ai_assistant/ai/conversations?limit=100',
|
|
19
|
+
expect.objectContaining({
|
|
20
|
+
credentials: 'include',
|
|
21
|
+
headers: expect.objectContaining({
|
|
22
|
+
Accept: 'application/json',
|
|
23
|
+
'x-om-forbidden-redirect': '0',
|
|
24
|
+
'x-om-unauthorized-redirect': '0',
|
|
25
|
+
}),
|
|
26
|
+
}),
|
|
27
|
+
)
|
|
28
|
+
})
|
|
29
|
+
})
|
|
@@ -77,6 +77,8 @@ async function requestJson<T>(
|
|
|
77
77
|
...init,
|
|
78
78
|
headers: {
|
|
79
79
|
Accept: 'application/json',
|
|
80
|
+
'x-om-forbidden-redirect': '0',
|
|
81
|
+
'x-om-unauthorized-redirect': '0',
|
|
80
82
|
...(init?.body ? { 'Content-Type': 'application/json' } : {}),
|
|
81
83
|
...(init?.headers ?? {}),
|
|
82
84
|
},
|
|
@@ -180,6 +180,7 @@ export function MutationPreviewCard(props: MutationPreviewCardProps) {
|
|
|
180
180
|
|
|
181
181
|
const batch = Array.isArray(action?.records) && action!.records!.length > 0 ? action!.records! : null
|
|
182
182
|
const summary = batch ? summarizeBatch(batch) : null
|
|
183
|
+
const hasReviewDetails = Boolean(batch)
|
|
183
184
|
|
|
184
185
|
return (
|
|
185
186
|
<section
|
|
@@ -242,7 +243,7 @@ export function MutationPreviewCard(props: MutationPreviewCardProps) {
|
|
|
242
243
|
)}
|
|
243
244
|
</div>
|
|
244
245
|
|
|
245
|
-
{expanded ? (
|
|
246
|
+
{expanded && hasReviewDetails ? (
|
|
246
247
|
<div className="mt-3 rounded-md border border-border bg-muted/20 p-3" data-ai-mutation-preview-details>
|
|
247
248
|
<FieldDiffCard
|
|
248
249
|
fieldDiff={action?.fieldDiff ?? null}
|
|
@@ -252,25 +253,29 @@ export function MutationPreviewCard(props: MutationPreviewCardProps) {
|
|
|
252
253
|
) : null}
|
|
253
254
|
|
|
254
255
|
<div className="mt-3 flex items-center justify-between gap-2">
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
256
|
+
{hasReviewDetails ? (
|
|
257
|
+
<Button
|
|
258
|
+
type="button"
|
|
259
|
+
variant="ghost"
|
|
260
|
+
size="sm"
|
|
261
|
+
onClick={() => setExpanded((value) => !value)}
|
|
262
|
+
data-ai-mutation-preview-review
|
|
263
|
+
>
|
|
264
|
+
<Eye className="size-4" aria-hidden />
|
|
265
|
+
<span>
|
|
266
|
+
{t(
|
|
267
|
+
'ai_assistant.chat.mutation_cards.preview.reviewDetails',
|
|
268
|
+
'Review details',
|
|
269
|
+
)}
|
|
270
|
+
</span>
|
|
271
|
+
<ChevronDown
|
|
272
|
+
className={`size-4 transition-transform ${expanded ? 'rotate-180' : ''}`}
|
|
273
|
+
aria-hidden
|
|
274
|
+
/>
|
|
275
|
+
</Button>
|
|
276
|
+
) : (
|
|
277
|
+
<span aria-hidden />
|
|
278
|
+
)}
|
|
274
279
|
<div className="flex items-center gap-2">
|
|
275
280
|
<Button
|
|
276
281
|
type="button"
|
|
@@ -122,35 +122,23 @@ export function MutationResultCard(props: MutationResultCardProps) {
|
|
|
122
122
|
</AlertTitle>
|
|
123
123
|
<div className="text-sm leading-relaxed">
|
|
124
124
|
<p>
|
|
125
|
-
{
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
) + `: ${result.commandName}`
|
|
130
|
-
: t(
|
|
131
|
-
'ai_assistant.chat.mutation_cards.result.successBody',
|
|
132
|
-
'The mutation completed successfully.',
|
|
133
|
-
)}
|
|
125
|
+
{t(
|
|
126
|
+
'ai_assistant.chat.mutation_cards.result.successBody',
|
|
127
|
+
'The requested change was saved successfully.',
|
|
128
|
+
)}
|
|
134
129
|
</p>
|
|
135
|
-
{recordId ? (
|
|
130
|
+
{recordId && href ? (
|
|
136
131
|
<p className="mt-1 text-xs">
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
: {recordId}
|
|
148
|
-
</a>
|
|
149
|
-
) : (
|
|
150
|
-
<span className="font-mono" data-ai-mutation-result-record-id>
|
|
151
|
-
{recordId}
|
|
152
|
-
</span>
|
|
153
|
-
)}
|
|
132
|
+
<a
|
|
133
|
+
className="text-primary underline"
|
|
134
|
+
href={href}
|
|
135
|
+
data-ai-mutation-result-link
|
|
136
|
+
>
|
|
137
|
+
{t(
|
|
138
|
+
'ai_assistant.chat.mutation_cards.result.viewRecord',
|
|
139
|
+
'View record',
|
|
140
|
+
)}
|
|
141
|
+
</a>
|
|
154
142
|
</p>
|
|
155
143
|
) : null}
|
|
156
144
|
</div>
|