@voyantjs/ui 0.28.1 → 0.29.0
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/components/async-combobox.d.ts +37 -0
- package/dist/components/async-combobox.d.ts.map +1 -0
- package/dist/components/async-combobox.js +66 -0
- package/dist/components/big-calendar/calendar-view.d.ts +13 -0
- package/dist/components/big-calendar/calendar-view.d.ts.map +1 -0
- package/dist/components/big-calendar/calendar-view.js +51 -0
- package/dist/components/big-calendar/change-badge-variant-input.d.ts +2 -0
- package/dist/components/big-calendar/change-badge-variant-input.d.ts.map +1 -0
- package/dist/components/big-calendar/change-badge-variant-input.js +8 -0
- package/dist/components/big-calendar/context.d.ts +47 -0
- package/dist/components/big-calendar/context.d.ts.map +1 -0
- package/dist/components/big-calendar/context.js +48 -0
- package/dist/components/big-calendar/header/calendar-header.d.ts +12 -0
- package/dist/components/big-calendar/header/calendar-header.d.ts.map +1 -0
- package/dist/components/big-calendar/header/calendar-header.js +34 -0
- package/dist/components/big-calendar/header/date-navigator.d.ts +9 -0
- package/dist/components/big-calendar/header/date-navigator.d.ts.map +1 -0
- package/dist/components/big-calendar/header/date-navigator.js +17 -0
- package/dist/components/big-calendar/header/today-button.d.ts +2 -0
- package/dist/components/big-calendar/header/today-button.d.ts.map +1 -0
- package/dist/components/big-calendar/header/today-button.js +9 -0
- package/dist/components/big-calendar/header/user-select.d.ts +2 -0
- package/dist/components/big-calendar/header/user-select.d.ts.map +1 -0
- package/dist/components/big-calendar/header/user-select.js +10 -0
- package/dist/components/big-calendar/helpers.d.ts +37 -0
- package/dist/components/big-calendar/helpers.d.ts.map +1 -0
- package/dist/components/big-calendar/helpers.js +217 -0
- package/dist/components/big-calendar/index.d.ts +20 -0
- package/dist/components/big-calendar/index.d.ts.map +1 -0
- package/dist/components/big-calendar/index.js +17 -0
- package/dist/components/big-calendar/interfaces.d.ts +21 -0
- package/dist/components/big-calendar/interfaces.d.ts.map +1 -0
- package/dist/components/big-calendar/interfaces.js +1 -0
- package/dist/components/big-calendar/month-view/calendar-month-view.d.ts +10 -0
- package/dist/components/big-calendar/month-view/calendar-month-view.d.ts.map +1 -0
- package/dist/components/big-calendar/month-view/calendar-month-view.js +13 -0
- package/dist/components/big-calendar/month-view/day-cell.d.ts +11 -0
- package/dist/components/big-calendar/month-view/day-cell.d.ts.map +1 -0
- package/dist/components/big-calendar/month-view/day-cell.js +24 -0
- package/dist/components/big-calendar/month-view/event-bullet.d.ts +6 -0
- package/dist/components/big-calendar/month-view/event-bullet.d.ts.map +1 -0
- package/dist/components/big-calendar/month-view/event-bullet.js +22 -0
- package/dist/components/big-calendar/month-view/month-event-badge.d.ts +12 -0
- package/dist/components/big-calendar/month-view/month-event-badge.d.ts.map +1 -0
- package/dist/components/big-calendar/month-view/month-event-badge.js +64 -0
- package/dist/components/big-calendar/types.d.ts +14 -0
- package/dist/components/big-calendar/types.d.ts.map +1 -0
- package/dist/components/big-calendar/types.js +1 -0
- package/dist/components/big-calendar/week-and-day-view/calendar-day-view.d.ts +8 -0
- package/dist/components/big-calendar/week-and-day-view/calendar-day-view.d.ts.map +1 -0
- package/dist/components/big-calendar/week-and-day-view/calendar-day-view.js +49 -0
- package/dist/components/big-calendar/week-and-day-view/calendar-time-line.d.ts +7 -0
- package/dist/components/big-calendar/week-and-day-view/calendar-time-line.d.ts.map +1 -0
- package/dist/components/big-calendar/week-and-day-view/calendar-time-line.js +21 -0
- package/dist/components/big-calendar/week-and-day-view/calendar-week-view.d.ts +8 -0
- package/dist/components/big-calendar/week-and-day-view/calendar-week-view.d.ts.map +1 -0
- package/dist/components/big-calendar/week-and-day-view/calendar-week-view.js +40 -0
- package/dist/components/big-calendar/week-and-day-view/day-view-multi-day-events-row.d.ts +8 -0
- package/dist/components/big-calendar/week-and-day-view/day-view-multi-day-events-row.d.ts.map +1 -0
- package/dist/components/big-calendar/week-and-day-view/day-view-multi-day-events-row.js +31 -0
- package/dist/components/big-calendar/week-and-day-view/event-block.d.ts +8 -0
- package/dist/components/big-calendar/week-and-day-view/event-block.d.ts.map +1 -0
- package/dist/components/big-calendar/week-and-day-view/event-block.js +38 -0
- package/dist/components/big-calendar/week-and-day-view/week-view-multi-day-events-row.d.ts +8 -0
- package/dist/components/big-calendar/week-and-day-view/week-view-multi-day-events-row.d.ts.map +1 -0
- package/dist/components/big-calendar/week-and-day-view/week-view-multi-day-events-row.js +80 -0
- package/dist/components/notification-reminder-rule-dialog.d.ts.map +1 -1
- package/dist/components/notification-reminder-rule-dialog.js +10 -17
- package/dist/components/notification-reminder-rules-page.d.ts +9 -1
- package/dist/components/notification-reminder-rules-page.d.ts.map +1 -1
- package/dist/components/notification-reminder-rules-page.js +7 -17
- package/dist/components/select.d.ts +2 -2
- package/dist/components/select.d.ts.map +1 -1
- package/dist/components/select.js +28 -1
- package/package.json +10 -5
- package/src/styles/globals.css +4 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { addDays, areIntervalsOverlapping, format, isSameDay, parseISO, startOfWeek, } from "date-fns";
|
|
3
|
+
import { cn } from "../../../lib/utils.js";
|
|
4
|
+
import { ScrollArea } from "../../scroll-area.js";
|
|
5
|
+
import { useCalendar } from "../context.js";
|
|
6
|
+
import { getEventBlockStyle, getVisibleHours, groupEvents, isWorkingHour } from "../helpers.js";
|
|
7
|
+
import { CalendarTimeline } from "./calendar-time-line.js";
|
|
8
|
+
import { EventBlock } from "./event-block.js";
|
|
9
|
+
import { WeekViewMultiDayEventsRow } from "./week-view-multi-day-events-row.js";
|
|
10
|
+
export function CalendarWeekView({ singleDayEvents, multiDayEvents }) {
|
|
11
|
+
const { selectedDate, workingHours, visibleHours, onAddEvent } = useCalendar();
|
|
12
|
+
const { hours, earliestEventHour, latestEventHour } = getVisibleHours(visibleHours, singleDayEvents);
|
|
13
|
+
const weekStart = startOfWeek(selectedDate);
|
|
14
|
+
const weekDays = Array.from({ length: 7 }, (_, i) => addDays(weekStart, i));
|
|
15
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex flex-col items-center justify-center border-b py-4 text-sm text-muted-foreground sm:hidden", children: [_jsx("p", { children: "Weekly view is not available on smaller devices." }), _jsx("p", { children: "Please switch to daily or monthly view." })] }), _jsxs("div", { className: "hidden flex-col sm:flex", children: [_jsxs("div", { children: [_jsx(WeekViewMultiDayEventsRow, { selectedDate: selectedDate, multiDayEvents: multiDayEvents }), _jsxs("div", { className: "relative z-20 flex border-b", children: [_jsx("div", { className: "w-18" }), _jsx("div", { className: "grid flex-1 grid-cols-7 divide-x border-l", children: weekDays.map((day) => (_jsxs("span", { className: "py-2 text-center text-xs font-medium text-muted-foreground", children: [format(day, "EE"), " ", _jsx("span", { className: "ml-1 font-semibold text-foreground", children: format(day, "d") })] }, day.toISOString()))) })] })] }), _jsx(ScrollArea, { className: "h-[736px]", children: _jsxs("div", { className: "flex overflow-hidden", children: [_jsx("div", { className: "relative w-18", children: hours.map((hour, index) => (_jsx("div", { className: "relative", style: { height: "96px" }, children: _jsx("div", { className: "absolute -top-3 right-2 flex h-6 items-center", children: index !== 0 ? (_jsx("span", { className: "text-xs text-muted-foreground", children: format(new Date().setHours(hour, 0, 0, 0), "hh a") })) : null }) }, hour))) }), _jsxs("div", { className: "relative flex-1 border-l", children: [_jsx("div", { className: "grid grid-cols-7 divide-x", children: weekDays.map((day) => {
|
|
16
|
+
const dayEvents = singleDayEvents.filter((event) => isSameDay(parseISO(event.startDate), day) ||
|
|
17
|
+
isSameDay(parseISO(event.endDate), day));
|
|
18
|
+
const groupedEvents = groupEvents(dayEvents);
|
|
19
|
+
return (_jsxs("div", { className: "relative", children: [hours.map((hour, index) => {
|
|
20
|
+
const isDisabled = !isWorkingHour(day, hour, workingHours);
|
|
21
|
+
return (_jsxs("div", { className: cn("relative", isDisabled && "bg-calendar-disabled-hour"), style: { height: "96px" }, children: [index !== 0 ? (_jsx("div", { className: "pointer-events-none absolute inset-x-0 top-0 border-b" })) : null, [0, 15, 30, 45].map((minute, slot) => (_jsx("button", { type: "button", onClick: () => onAddEvent?.({ date: day, hour, minute }), disabled: !onAddEvent, className: "absolute inset-x-0 h-[24px] cursor-pointer transition-colors hover:bg-accent disabled:cursor-default disabled:hover:bg-transparent", style: { top: slot * 24 }, "aria-label": `Add event at ${hour}:${String(minute).padStart(2, "0")}` }, minute))), _jsx("div", { className: "pointer-events-none absolute inset-x-0 top-1/2 border-b border-dashed" })] }, hour));
|
|
22
|
+
}), groupedEvents.map((group, groupIndex) => group.map((event) => {
|
|
23
|
+
let style = getEventBlockStyle(event, day, groupIndex, groupedEvents.length, {
|
|
24
|
+
from: earliestEventHour,
|
|
25
|
+
to: latestEventHour,
|
|
26
|
+
});
|
|
27
|
+
const hasOverlap = groupedEvents.some((otherGroup, otherIndex) => otherIndex !== groupIndex &&
|
|
28
|
+
otherGroup.some((otherEvent) => areIntervalsOverlapping({
|
|
29
|
+
start: parseISO(event.startDate),
|
|
30
|
+
end: parseISO(event.endDate),
|
|
31
|
+
}, {
|
|
32
|
+
start: parseISO(otherEvent.startDate),
|
|
33
|
+
end: parseISO(otherEvent.endDate),
|
|
34
|
+
})));
|
|
35
|
+
if (!hasOverlap)
|
|
36
|
+
style = { ...style, width: "100%", left: "0%" };
|
|
37
|
+
return (_jsx("div", { className: "absolute p-1", style: style, children: _jsx(EventBlock, { event: event }) }, event.id));
|
|
38
|
+
}))] }, day.toISOString()));
|
|
39
|
+
}) }), _jsx(CalendarTimeline, { firstVisibleHour: earliestEventHour, lastVisibleHour: latestEventHour })] })] }) })] })] }));
|
|
40
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { IEvent } from "../interfaces.js";
|
|
2
|
+
interface IProps {
|
|
3
|
+
selectedDate: Date;
|
|
4
|
+
multiDayEvents: IEvent[];
|
|
5
|
+
}
|
|
6
|
+
export declare function DayViewMultiDayEventsRow({ selectedDate, multiDayEvents }: IProps): import("react/jsx-runtime").JSX.Element | null;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=day-view-multi-day-events-row.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"day-view-multi-day-events-row.d.ts","sourceRoot":"","sources":["../../../../src/components/big-calendar/week-and-day-view/day-view-multi-day-events-row.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAG9C,UAAU,MAAM;IACd,YAAY,EAAE,IAAI,CAAA;IAClB,cAAc,EAAE,MAAM,EAAE,CAAA;CACzB;AAED,wBAAgB,wBAAwB,CAAC,EAAE,YAAY,EAAE,cAAc,EAAE,EAAE,MAAM,kDAiDhF"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { differenceInDays, endOfDay, isWithinInterval, parseISO, startOfDay } from "date-fns";
|
|
3
|
+
import { MonthEventBadge } from "../month-view/month-event-badge.js";
|
|
4
|
+
export function DayViewMultiDayEventsRow({ selectedDate, multiDayEvents }) {
|
|
5
|
+
const dayStart = startOfDay(selectedDate);
|
|
6
|
+
const dayEnd = endOfDay(selectedDate);
|
|
7
|
+
const multiDayEventsInDay = multiDayEvents
|
|
8
|
+
.filter((event) => {
|
|
9
|
+
const eventStart = parseISO(event.startDate);
|
|
10
|
+
const eventEnd = parseISO(event.endDate);
|
|
11
|
+
const isOverlapping = isWithinInterval(dayStart, { start: eventStart, end: eventEnd }) ||
|
|
12
|
+
isWithinInterval(dayEnd, { start: eventStart, end: eventEnd }) ||
|
|
13
|
+
(eventStart <= dayStart && eventEnd >= dayEnd);
|
|
14
|
+
return isOverlapping;
|
|
15
|
+
})
|
|
16
|
+
.sort((a, b) => {
|
|
17
|
+
const durationA = differenceInDays(parseISO(a.endDate), parseISO(a.startDate));
|
|
18
|
+
const durationB = differenceInDays(parseISO(b.endDate), parseISO(b.startDate));
|
|
19
|
+
return durationB - durationA;
|
|
20
|
+
});
|
|
21
|
+
if (multiDayEventsInDay.length === 0)
|
|
22
|
+
return null;
|
|
23
|
+
return (_jsxs("div", { className: "flex border-b", children: [_jsx("div", { className: "w-18" }), _jsx("div", { className: "flex flex-1 flex-col gap-1 border-l py-1", children: multiDayEventsInDay.map((event) => {
|
|
24
|
+
const eventStart = startOfDay(parseISO(event.startDate));
|
|
25
|
+
const eventEnd = startOfDay(parseISO(event.endDate));
|
|
26
|
+
const currentDate = startOfDay(selectedDate);
|
|
27
|
+
const eventTotalDays = differenceInDays(eventEnd, eventStart) + 1;
|
|
28
|
+
const eventCurrentDay = differenceInDays(currentDate, eventStart) + 1;
|
|
29
|
+
return (_jsx(MonthEventBadge, { event: event, cellDate: selectedDate, eventCurrentDay: eventCurrentDay, eventTotalDays: eventTotalDays }, event.id));
|
|
30
|
+
}) })] }));
|
|
31
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { IEvent } from "../interfaces.js";
|
|
2
|
+
interface IProps {
|
|
3
|
+
event: IEvent;
|
|
4
|
+
className?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function EventBlock({ event, className }: IProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=event-block.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-block.d.ts","sourceRoot":"","sources":["../../../../src/components/big-calendar/week-and-day-view/event-block.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAiC9C,UAAU,MAAM;IACd,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,MAAM,2CA+CtD"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cva } from "class-variance-authority";
|
|
3
|
+
import { differenceInMinutes, format, parseISO } from "date-fns";
|
|
4
|
+
import { cn } from "../../../lib/utils.js";
|
|
5
|
+
import { useCalendar } from "../context.js";
|
|
6
|
+
const calendarWeekEventCardVariants = cva("flex select-none flex-col gap-0.5 truncate whitespace-nowrap rounded-md border px-2 py-1.5 text-xs focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", {
|
|
7
|
+
variants: {
|
|
8
|
+
color: {
|
|
9
|
+
blue: "border-blue-200 bg-blue-50 text-blue-700 dark:border-blue-800 dark:bg-blue-950 dark:text-blue-300 [&_.event-dot]:fill-blue-600",
|
|
10
|
+
green: "border-green-200 bg-green-50 text-green-700 dark:border-green-800 dark:bg-green-950 dark:text-green-300 [&_.event-dot]:fill-green-600",
|
|
11
|
+
red: "border-red-200 bg-red-50 text-red-700 dark:border-red-800 dark:bg-red-950 dark:text-red-300 [&_.event-dot]:fill-red-600",
|
|
12
|
+
yellow: "border-yellow-200 bg-yellow-50 text-yellow-700 dark:border-yellow-800 dark:bg-yellow-950 dark:text-yellow-300 [&_.event-dot]:fill-yellow-600",
|
|
13
|
+
purple: "border-purple-200 bg-purple-50 text-purple-700 dark:border-purple-800 dark:bg-purple-950 dark:text-purple-300 [&_.event-dot]:fill-purple-600",
|
|
14
|
+
orange: "border-orange-200 bg-orange-50 text-orange-700 dark:border-orange-800 dark:bg-orange-950 dark:text-orange-300 [&_.event-dot]:fill-orange-600",
|
|
15
|
+
gray: "border-neutral-200 bg-neutral-50 text-neutral-700 dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-300 [&_.event-dot]:fill-neutral-600",
|
|
16
|
+
"blue-dot": "bg-neutral-50 dark:bg-neutral-900 [&_.event-dot]:fill-blue-600",
|
|
17
|
+
"green-dot": "bg-neutral-50 dark:bg-neutral-900 [&_.event-dot]:fill-green-600",
|
|
18
|
+
"red-dot": "bg-neutral-50 dark:bg-neutral-900 [&_.event-dot]:fill-red-600",
|
|
19
|
+
"orange-dot": "bg-neutral-50 dark:bg-neutral-900 [&_.event-dot]:fill-orange-600",
|
|
20
|
+
"purple-dot": "bg-neutral-50 dark:bg-neutral-900 [&_.event-dot]:fill-purple-600",
|
|
21
|
+
"yellow-dot": "bg-neutral-50 dark:bg-neutral-900 [&_.event-dot]:fill-yellow-600",
|
|
22
|
+
"gray-dot": "bg-neutral-50 dark:bg-neutral-900 [&_.event-dot]:fill-neutral-600",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
defaultVariants: {
|
|
26
|
+
color: "blue-dot",
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
export function EventBlock({ event, className }) {
|
|
30
|
+
const { badgeVariant, onEventClick } = useCalendar();
|
|
31
|
+
const start = parseISO(event.startDate);
|
|
32
|
+
const end = parseISO(event.endDate);
|
|
33
|
+
const durationInMinutes = differenceInMinutes(end, start);
|
|
34
|
+
const heightInPixels = (durationInMinutes / 60) * 96 - 8;
|
|
35
|
+
const color = (badgeVariant === "dot" ? `${event.color}-dot` : event.color);
|
|
36
|
+
const calendarWeekEventCardClasses = cn(calendarWeekEventCardVariants({ color, className }), durationInMinutes < 35 && "py-0 justify-center");
|
|
37
|
+
return (_jsxs("button", { type: "button", className: calendarWeekEventCardClasses, style: { height: `${heightInPixels}px` }, onClick: () => onEventClick?.(event), children: [_jsxs("div", { className: "flex items-center gap-1.5 truncate", children: [["mixed", "dot"].includes(badgeVariant) ? (_jsx("svg", { "aria-hidden": "true", width: "8", height: "8", viewBox: "0 0 8 8", className: "event-dot shrink-0", children: _jsx("circle", { cx: "4", cy: "4", r: "4" }) })) : null, _jsx("p", { className: "truncate font-semibold", children: event.title })] }), durationInMinutes > 25 ? (_jsxs("p", { children: [format(start, "h:mm a"), " - ", format(end, "h:mm a")] })) : null] }));
|
|
38
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { IEvent } from "../interfaces.js";
|
|
2
|
+
interface IProps {
|
|
3
|
+
selectedDate: Date;
|
|
4
|
+
multiDayEvents: IEvent[];
|
|
5
|
+
}
|
|
6
|
+
export declare function WeekViewMultiDayEventsRow({ selectedDate, multiDayEvents }: IProps): import("react/jsx-runtime").JSX.Element | null;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=week-view-multi-day-events-row.d.ts.map
|
package/dist/components/big-calendar/week-and-day-view/week-view-multi-day-events-row.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"week-view-multi-day-events-row.d.ts","sourceRoot":"","sources":["../../../../src/components/big-calendar/week-and-day-view/week-view-multi-day-events-row.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAG9C,UAAU,MAAM;IACd,YAAY,EAAE,IAAI,CAAA;IAClB,cAAc,EAAE,MAAM,EAAE,CAAA;CACzB;AAED,wBAAgB,yBAAyB,CAAC,EAAE,YAAY,EAAE,cAAc,EAAE,EAAE,MAAM,kDA2GjF"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { addDays, differenceInDays, endOfWeek, isAfter, isBefore, parseISO, startOfDay, startOfWeek, } from "date-fns";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { MonthEventBadge } from "../month-view/month-event-badge.js";
|
|
5
|
+
export function WeekViewMultiDayEventsRow({ selectedDate, multiDayEvents }) {
|
|
6
|
+
const weekStart = startOfWeek(selectedDate);
|
|
7
|
+
const weekEnd = endOfWeek(selectedDate);
|
|
8
|
+
const weekDays = Array.from({ length: 7 }, (_, i) => addDays(weekStart, i));
|
|
9
|
+
const processedEvents = React.useMemo(() => {
|
|
10
|
+
return multiDayEvents
|
|
11
|
+
.map((event) => {
|
|
12
|
+
const start = parseISO(event.startDate);
|
|
13
|
+
const end = parseISO(event.endDate);
|
|
14
|
+
const adjustedStart = isBefore(start, weekStart) ? weekStart : start;
|
|
15
|
+
const adjustedEnd = isAfter(end, weekEnd) ? weekEnd : end;
|
|
16
|
+
const startIndex = differenceInDays(adjustedStart, weekStart);
|
|
17
|
+
const endIndex = differenceInDays(adjustedEnd, weekStart);
|
|
18
|
+
return {
|
|
19
|
+
...event,
|
|
20
|
+
adjustedStart,
|
|
21
|
+
adjustedEnd,
|
|
22
|
+
startIndex,
|
|
23
|
+
endIndex,
|
|
24
|
+
};
|
|
25
|
+
})
|
|
26
|
+
.sort((a, b) => {
|
|
27
|
+
const startDiff = a.adjustedStart.getTime() - b.adjustedStart.getTime();
|
|
28
|
+
if (startDiff !== 0)
|
|
29
|
+
return startDiff;
|
|
30
|
+
return b.endIndex - b.startIndex - (a.endIndex - a.startIndex);
|
|
31
|
+
});
|
|
32
|
+
}, [multiDayEvents, weekStart, weekEnd]);
|
|
33
|
+
const eventRows = React.useMemo(() => {
|
|
34
|
+
const rows = [];
|
|
35
|
+
processedEvents.forEach((event) => {
|
|
36
|
+
let rowIndex = rows.findIndex((row) => row.every((e) => e.endIndex < event.startIndex || e.startIndex > event.endIndex));
|
|
37
|
+
if (rowIndex === -1) {
|
|
38
|
+
rowIndex = rows.length;
|
|
39
|
+
rows.push([]);
|
|
40
|
+
}
|
|
41
|
+
const target = rows[rowIndex];
|
|
42
|
+
if (target)
|
|
43
|
+
target.push(event);
|
|
44
|
+
});
|
|
45
|
+
return rows;
|
|
46
|
+
}, [processedEvents]);
|
|
47
|
+
const hasEventsInWeek = React.useMemo(() => {
|
|
48
|
+
return multiDayEvents.some((event) => {
|
|
49
|
+
const start = parseISO(event.startDate);
|
|
50
|
+
const end = parseISO(event.endDate);
|
|
51
|
+
return ((start >= weekStart && start <= weekEnd) ||
|
|
52
|
+
(end >= weekStart && end <= weekEnd) ||
|
|
53
|
+
(start <= weekStart && end >= weekEnd));
|
|
54
|
+
});
|
|
55
|
+
}, [multiDayEvents, weekStart, weekEnd]);
|
|
56
|
+
if (!hasEventsInWeek) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
return (_jsxs("div", { className: "hidden overflow-hidden sm:flex", children: [_jsx("div", { className: "w-18 border-b" }), _jsx("div", { className: "grid flex-1 grid-cols-7 divide-x border-b border-l", children: weekDays.map((day, dayIndex) => (_jsx("div", { className: "flex h-full flex-col gap-1 py-1", children: eventRows.map((row, rowIndex) => {
|
|
60
|
+
const event = row.find((e) => e.startIndex <= dayIndex && e.endIndex >= dayIndex);
|
|
61
|
+
if (!event) {
|
|
62
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: empty layout spacer slot has no semantic id; rowIndex is stable within the column
|
|
63
|
+
return _jsx("div", { className: "h-6.5" }, `empty-${rowIndex}`);
|
|
64
|
+
}
|
|
65
|
+
let position = "none";
|
|
66
|
+
if (dayIndex === event.startIndex && dayIndex === event.endIndex) {
|
|
67
|
+
position = "none";
|
|
68
|
+
}
|
|
69
|
+
else if (dayIndex === event.startIndex) {
|
|
70
|
+
position = "first";
|
|
71
|
+
}
|
|
72
|
+
else if (dayIndex === event.endIndex) {
|
|
73
|
+
position = "last";
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
position = "middle";
|
|
77
|
+
}
|
|
78
|
+
return (_jsx(MonthEventBadge, { event: event, cellDate: startOfDay(day), position: position }, `${event.id}-${day.toISOString()}`));
|
|
79
|
+
}) }, day.toISOString()))) })] }));
|
|
80
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notification-reminder-rule-dialog.d.ts","sourceRoot":"","sources":["../../src/components/notification-reminder-rule-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,8BAA8B,EAGpC,MAAM,+BAA+B,CAAA;
|
|
1
|
+
{"version":3,"file":"notification-reminder-rule-dialog.d.ts","sourceRoot":"","sources":["../../src/components/notification-reminder-rule-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,8BAA8B,EAGpC,MAAM,+BAA+B,CAAA;AAsDtC,KAAK,mCAAmC,GAAG;IACzC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,IAAI,CAAC,EAAE,8BAA8B,CAAA;IACrC,SAAS,EAAE,MAAM,IAAI,CAAA;CACtB,CAAA;AAED,wBAAgB,8BAA8B,CAAC,EAC7C,IAAI,EACJ,YAAY,EACZ,IAAI,EACJ,SAAS,GACV,EAAE,mCAAmC,2CAkMrC"}
|
|
@@ -21,8 +21,9 @@ const reminderRuleFormSchema = z.object({
|
|
|
21
21
|
"booking_cancelled_non_payment",
|
|
22
22
|
]),
|
|
23
23
|
channel: z.enum(["email", "sms"]),
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
// Optional default template — stages own per-channel templates and
|
|
25
|
+
// override this. Empty string is normalized to null in the payload.
|
|
26
|
+
templateId: z.string().optional(),
|
|
26
27
|
});
|
|
27
28
|
const reminderTargetOptions = [
|
|
28
29
|
{ value: "booking_confirmed", label: "Booking confirmed" },
|
|
@@ -30,7 +31,6 @@ const reminderTargetOptions = [
|
|
|
30
31
|
{ value: "booking_cancelled_non_payment", label: "Booking cancelled (non-payment)" },
|
|
31
32
|
{ value: "booking_payment_schedule", label: "Booking payment schedule" },
|
|
32
33
|
];
|
|
33
|
-
const dueDateTargetTypes = new Set(["booking_payment_schedule"]);
|
|
34
34
|
function slugifyReminderRule(value) {
|
|
35
35
|
const slug = value
|
|
36
36
|
.trim()
|
|
@@ -50,12 +50,9 @@ export function NotificationReminderRuleDialog({ open, onOpenChange, rule, onSuc
|
|
|
50
50
|
targetType: "booking_payment_schedule",
|
|
51
51
|
channel: "email",
|
|
52
52
|
templateId: "",
|
|
53
|
-
relativeDaysFromDueDate: 0,
|
|
54
53
|
},
|
|
55
54
|
});
|
|
56
55
|
const channel = form.watch("channel");
|
|
57
|
-
const targetType = form.watch("targetType");
|
|
58
|
-
const usesDueDateTiming = dueDateTargetTypes.has(targetType);
|
|
59
56
|
const { data: templates } = useNotificationTemplates({
|
|
60
57
|
channel,
|
|
61
58
|
status: "active",
|
|
@@ -75,7 +72,6 @@ export function NotificationReminderRuleDialog({ open, onOpenChange, rule, onSuc
|
|
|
75
72
|
targetType: rule.targetType === "invoice" ? "booking_payment_schedule" : rule.targetType,
|
|
76
73
|
channel: rule.channel,
|
|
77
74
|
templateId: resolvedTemplateId,
|
|
78
|
-
relativeDaysFromDueDate: rule.relativeDaysFromDueDate,
|
|
79
75
|
});
|
|
80
76
|
return;
|
|
81
77
|
}
|
|
@@ -92,11 +88,8 @@ export function NotificationReminderRuleDialog({ open, onOpenChange, rule, onSuc
|
|
|
92
88
|
targetType: values.targetType,
|
|
93
89
|
channel: values.channel,
|
|
94
90
|
provider: null,
|
|
95
|
-
templateId: values.templateId,
|
|
91
|
+
templateId: values.templateId ? values.templateId : null,
|
|
96
92
|
templateSlug: null,
|
|
97
|
-
relativeDaysFromDueDate: dueDateTargetTypes.has(values.targetType)
|
|
98
|
-
? values.relativeDaysFromDueDate
|
|
99
|
-
: 0,
|
|
100
93
|
isSystem: rule?.isSystem ?? false,
|
|
101
94
|
metadata: rule?.metadata ?? null,
|
|
102
95
|
};
|
|
@@ -117,13 +110,13 @@ export function NotificationReminderRuleDialog({ open, onOpenChange, rule, onSuc
|
|
|
117
110
|
if (!value)
|
|
118
111
|
return;
|
|
119
112
|
form.setValue("status", value);
|
|
120
|
-
}, children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "draft", children: "Draft" }), _jsx(SelectItem, { value: "active", children: "Active" }), _jsx(SelectItem, { value: "archived", children: "Archived" })] })] })] })] }),
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
113
|
+
}, children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "draft", children: "Draft" }), _jsx(SelectItem, { value: "active", children: "Active" }), _jsx(SelectItem, { value: "archived", children: "Archived" })] })] })] })] }), _jsx("div", { className: "grid gap-4", children: _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Channel" }), _jsxs(Select, { value: form.watch("channel"), onValueChange: (value) => {
|
|
114
|
+
if (!value)
|
|
115
|
+
return;
|
|
116
|
+
form.setValue("channel", value);
|
|
117
|
+
}, children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "email", children: "Email" }), _jsx(SelectItem, { value: "sms", children: "SMS" })] })] })] }) }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Default template" }), _jsxs(Select, { value: form.watch("templateId"), onValueChange: (value) => {
|
|
125
118
|
if (!value)
|
|
126
119
|
return;
|
|
127
120
|
form.setValue("templateId", value);
|
|
128
|
-
}, children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, { placeholder: "Select template" }) }), _jsx(SelectContent, { children: (templates?.data ?? []).map((template) => (_jsxs(SelectItem, { value: template.id, children: [template.name, " (", template.slug, ")"] }, template.id))) })] })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }), _jsxs(Button, { type: "submit", disabled: isPending, children: [isPending ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : null, isEditing ? "Save Changes" : "Create Rule"] })] })] })] }) }));
|
|
121
|
+
}, children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, { placeholder: "Select template" }) }), _jsx(SelectContent, { children: (templates?.data ?? []).map((template) => (_jsxs(SelectItem, { value: template.id, children: [template.name, " (", template.slug, ")"] }, template.id))) })] }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Used as a fallback. Per-stage channels can override this." })] }), !isEditing ? (_jsxs("p", { className: "rounded-md border border-dashed bg-muted/40 px-3 py-2 text-xs text-muted-foreground", children: ["After creating the rule, click ", _jsx("strong", { children: "Manage stages" }), " on the row to define when it fires (anchor, window, cadence) and which channels deliver it."] })) : null] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }), _jsxs(Button, { type: "submit", disabled: isPending, children: [isPending ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : null, isEditing ? "Save Changes" : "Create Rule"] })] })] })] }) }));
|
|
129
122
|
}
|
|
@@ -1,2 +1,10 @@
|
|
|
1
|
-
export
|
|
1
|
+
export interface NotificationReminderRulesPageProps {
|
|
2
|
+
/**
|
|
3
|
+
* Path used for the per-rule "Manage stages" link. Receives the rule id and
|
|
4
|
+
* returns the URL the consumer's router understands. Defaults to
|
|
5
|
+
* `/notifications/reminder-rules/<id>`.
|
|
6
|
+
*/
|
|
7
|
+
manageStagesHref?: (ruleId: string) => string;
|
|
8
|
+
}
|
|
9
|
+
export declare function NotificationReminderRulesPage({ manageStagesHref, }?: NotificationReminderRulesPageProps): import("react/jsx-runtime").JSX.Element;
|
|
2
10
|
//# sourceMappingURL=notification-reminder-rules-page.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notification-reminder-rules-page.d.ts","sourceRoot":"","sources":["../../src/components/notification-reminder-rules-page.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"notification-reminder-rules-page.d.ts","sourceRoot":"","sources":["../../src/components/notification-reminder-rules-page.tsx"],"names":[],"mappings":"AA4BA,MAAM,WAAW,kCAAkC;IACjD;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAA;CAC9C;AAED,wBAAgB,6BAA6B,CAAC,EAC5C,gBAAgE,GACjE,GAAE,kCAAuC,2CAoKzC"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useNotificationReminderRules, } from "@voyantjs/notifications-react";
|
|
4
|
-
import { Loader2, Pencil, Plus, Search } from "lucide-react";
|
|
4
|
+
import { Layers, Loader2, Pencil, Plus, Search } from "lucide-react";
|
|
5
5
|
import { useState } from "react";
|
|
6
6
|
import { Badge } from "./badge.js";
|
|
7
|
-
import { Button } from "./button.js";
|
|
7
|
+
import { Button, buttonVariants } from "./button.js";
|
|
8
8
|
import { Input } from "./input.js";
|
|
9
9
|
import { NotificationReminderRuleDialog } from "./notification-reminder-rule-dialog.js";
|
|
10
10
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./select.js";
|
|
@@ -14,22 +14,12 @@ const reminderTargetLabels = {
|
|
|
14
14
|
payment_complete: "Payment complete",
|
|
15
15
|
booking_cancelled_non_payment: "Booking cancelled (non-payment)",
|
|
16
16
|
};
|
|
17
|
-
const dueDateTargetTypes = new Set([
|
|
18
|
-
"booking_payment_schedule",
|
|
19
|
-
]);
|
|
20
17
|
function getReminderTargetLabel(targetType) {
|
|
21
18
|
if (targetType === "invoice")
|
|
22
19
|
return "Invoice";
|
|
23
20
|
return reminderTargetLabels[targetType] ?? targetType;
|
|
24
21
|
}
|
|
25
|
-
function
|
|
26
|
-
if (!dueDateTargetTypes.has(targetType))
|
|
27
|
-
return "Event";
|
|
28
|
-
if (days === 0)
|
|
29
|
-
return "Due date";
|
|
30
|
-
return days < 0 ? `${Math.abs(days)} days before` : `${days} days after`;
|
|
31
|
-
}
|
|
32
|
-
export function NotificationReminderRulesPage() {
|
|
22
|
+
export function NotificationReminderRulesPage({ manageStagesHref = (id) => `/notifications/reminder-rules/${id}`, } = {}) {
|
|
33
23
|
const [search, setSearch] = useState("");
|
|
34
24
|
const [channel, setChannel] = useState("all");
|
|
35
25
|
const [status, setStatus] = useState("all");
|
|
@@ -45,10 +35,10 @@ export function NotificationReminderRulesPage() {
|
|
|
45
35
|
return (_jsxs("div", { className: "flex flex-col gap-6 p-6", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-2xl font-bold tracking-tight", children: "Reminder Rules" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Schedule booking payment reminders and event notifications against templates and channels." })] }), _jsxs(Button, { onClick: () => {
|
|
46
36
|
setEditing(undefined);
|
|
47
37
|
setDialogOpen(true);
|
|
48
|
-
}, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), "New Rule"] })] }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("div", { className: "relative max-w-sm flex-1", children: [_jsx(Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { placeholder: "Search rules...", value: search, onChange: (event) => setSearch(event.target.value), className: "pl-9" })] }), _jsxs(Select, { value: targetType, onValueChange: (value) => setTargetType(value ?? "all"), children: [_jsx(SelectTrigger, { className: "w-[190px]", children: _jsx(SelectValue, { placeholder: "Target" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "All targets" }), Object.entries(reminderTargetLabels).map(([value, label]) => (_jsx(SelectItem, { value: value, children: label }, value)))] })] }), _jsxs(Select, { value: channel, onValueChange: (value) => setChannel(value ?? "all"), children: [_jsx(SelectTrigger, { className: "w-[140px]", children: _jsx(SelectValue, { placeholder: "Channel" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "All channels" }), _jsx(SelectItem, { value: "email", children: "Email" }), _jsx(SelectItem, { value: "sms", children: "SMS" })] })] }), _jsxs(Select, { value: status, onValueChange: (value) => setStatus(value ?? "all"), children: [_jsx(SelectTrigger, { className: "w-[140px]", children: _jsx(SelectValue, { placeholder: "Status" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "All statuses" }), _jsx(SelectItem, { value: "draft", children: "Draft" }), _jsx(SelectItem, { value: "active", children: "Active" }), _jsx(SelectItem, { value: "archived", children: "Archived" })] })] })] }), isPending ? (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground" }) })) : null, !isPending && (!data?.data || data.data.length === 0) ? (_jsx("div", { className: "rounded-md border border-dashed p-8 text-center", children: _jsx("p", { className: "text-sm text-muted-foreground", children: "No reminder rules yet." }) })) : null, !isPending && data?.data && data.data.length > 0 ? (_jsx("div", { className: "rounded-md border", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "bg-muted/40 text-left text-xs uppercase tracking-wide text-muted-foreground", children: _jsxs("tr", { children: [_jsx("th", { className: "px-4 py-3", children: "Rule" }), _jsx("th", { className: "px-4 py-3", children: "Target" }), _jsx("th", { className: "px-4 py-3", children: "Channel" }), _jsx("th", { className: "px-4 py-3", children: "
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
38
|
+
}, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), "New Rule"] })] }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("div", { className: "relative max-w-sm flex-1", children: [_jsx(Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { placeholder: "Search rules...", value: search, onChange: (event) => setSearch(event.target.value), className: "pl-9" })] }), _jsxs(Select, { value: targetType, onValueChange: (value) => setTargetType(value ?? "all"), children: [_jsx(SelectTrigger, { className: "w-[190px]", children: _jsx(SelectValue, { placeholder: "Target" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "All targets" }), Object.entries(reminderTargetLabels).map(([value, label]) => (_jsx(SelectItem, { value: value, children: label }, value)))] })] }), _jsxs(Select, { value: channel, onValueChange: (value) => setChannel(value ?? "all"), children: [_jsx(SelectTrigger, { className: "w-[140px]", children: _jsx(SelectValue, { placeholder: "Channel" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "All channels" }), _jsx(SelectItem, { value: "email", children: "Email" }), _jsx(SelectItem, { value: "sms", children: "SMS" })] })] }), _jsxs(Select, { value: status, onValueChange: (value) => setStatus(value ?? "all"), children: [_jsx(SelectTrigger, { className: "w-[140px]", children: _jsx(SelectValue, { placeholder: "Status" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "All statuses" }), _jsx(SelectItem, { value: "draft", children: "Draft" }), _jsx(SelectItem, { value: "active", children: "Active" }), _jsx(SelectItem, { value: "archived", children: "Archived" })] })] })] }), isPending ? (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground" }) })) : null, !isPending && (!data?.data || data.data.length === 0) ? (_jsx("div", { className: "rounded-md border border-dashed p-8 text-center", children: _jsx("p", { className: "text-sm text-muted-foreground", children: "No reminder rules yet." }) })) : null, !isPending && data?.data && data.data.length > 0 ? (_jsx("div", { className: "rounded-md border", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "bg-muted/40 text-left text-xs uppercase tracking-wide text-muted-foreground", children: _jsxs("tr", { children: [_jsx("th", { className: "px-4 py-3", children: "Rule" }), _jsx("th", { className: "px-4 py-3", children: "Target" }), _jsx("th", { className: "px-4 py-3", children: "Channel" }), _jsx("th", { className: "px-4 py-3", children: "Status" }), _jsx("th", { className: "px-4 py-3 text-right", children: "Actions" })] }) }), _jsx("tbody", { children: data.data.map((rule) => (_jsxs("tr", { className: "border-t", children: [_jsx("td", { className: "px-4 py-3", children: _jsx("div", { className: "font-medium", children: rule.name }) }), _jsx("td", { className: "px-4 py-3", children: getReminderTargetLabel(rule.targetType) }), _jsx("td", { className: "px-4 py-3", children: _jsx(Badge, { variant: "outline", children: rule.channel }) }), _jsx("td", { className: "px-4 py-3", children: _jsx(Badge, { variant: rule.status === "active" ? "default" : "secondary", children: rule.status }) }), _jsx("td", { className: "px-4 py-3 text-right", children: _jsxs("div", { className: "flex items-center justify-end gap-1", children: [_jsxs("a", { href: manageStagesHref(rule.id), className: buttonVariants({ variant: "ghost", size: "sm" }), children: [_jsx(Layers, { className: "mr-2 h-4 w-4" }), "Manage stages"] }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => {
|
|
39
|
+
setEditing(rule);
|
|
40
|
+
setDialogOpen(true);
|
|
41
|
+
}, children: _jsx(Pencil, { className: "h-4 w-4" }) })] }) })] }, rule.id))) })] }) })) : null, _jsx(NotificationReminderRuleDialog, { open: dialogOpen, onOpenChange: setDialogOpen, rule: editing, onSuccess: () => {
|
|
52
42
|
setDialogOpen(false);
|
|
53
43
|
setEditing(undefined);
|
|
54
44
|
void refetch();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Select as SelectPrimitive } from "@base-ui/react/select";
|
|
2
|
-
import
|
|
3
|
-
declare
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
declare function Select<Value, Multiple extends boolean | undefined = false>({ children, items, ...props }: SelectPrimitive.Root.Props<Value, Multiple>): import("react/jsx-runtime").JSX.Element;
|
|
4
4
|
declare function SelectGroup({ className, ...props }: SelectPrimitive.Group.Props): import("react/jsx-runtime").JSX.Element;
|
|
5
5
|
declare function SelectValue({ className, ...props }: SelectPrimitive.Value.Props): import("react/jsx-runtime").JSX.Element;
|
|
6
6
|
declare function SelectTrigger({ className, size, children, ...props }: SelectPrimitive.Trigger.Props & {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../src/components/select.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAEjE,OAAO,KAAK,KAAK,
|
|
1
|
+
{"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../src/components/select.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAEjE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AA0B9B,iBAAS,MAAM,CAAC,KAAK,EAAE,QAAQ,SAAS,OAAO,GAAG,SAAS,GAAG,KAAK,EAAE,EACnE,QAAQ,EACR,KAAK,EACL,GAAG,KAAK,EACT,EAAE,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,2CAY7C;AAED,iBAAS,WAAW,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,eAAe,CAAC,KAAK,CAAC,KAAK,2CAQxE;AAED,iBAAS,WAAW,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,eAAe,CAAC,KAAK,CAAC,KAAK,2CAQxE;AAED,iBAAS,aAAa,CAAC,EACrB,SAAS,EACT,IAAgB,EAChB,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,eAAe,CAAC,OAAO,CAAC,KAAK,GAAG;IACjC,IAAI,CAAC,EAAE,IAAI,GAAG,SAAS,CAAA;CACxB,2CAiBA;AAED,iBAAS,aAAa,CAAC,EACrB,SAAS,EACT,QAAQ,EACR,IAAe,EACf,UAAc,EACd,KAAgB,EAChB,WAAe,EACf,oBAA2B,EAC3B,GAAG,KAAK,EACT,EAAE,eAAe,CAAC,KAAK,CAAC,KAAK,GAC5B,IAAI,CACF,eAAe,CAAC,UAAU,CAAC,KAAK,EAChC,OAAO,GAAG,aAAa,GAAG,MAAM,GAAG,YAAY,GAAG,sBAAsB,CACzE,2CA2BF;AAED,iBAAS,WAAW,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,eAAe,CAAC,UAAU,CAAC,KAAK,2CAQ7E;AAED,iBAAS,UAAU,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,eAAe,CAAC,IAAI,CAAC,KAAK,2CAsBhF;AAED,iBAAS,eAAe,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,eAAe,CAAC,SAAS,CAAC,KAAK,2CAQhF;AAED,iBAAS,oBAAoB,CAAC,EAC5B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,aAAa,CAAC,2CAa5D;AAED,iBAAS,sBAAsB,CAAC,EAC9B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,eAAe,CAAC,2CAa9D;AAED,OAAO,EACL,MAAM,EACN,aAAa,EACb,WAAW,EACX,UAAU,EACV,WAAW,EACX,sBAAsB,EACtB,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,WAAW,GACZ,CAAA"}
|
|
@@ -1,8 +1,35 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Select as SelectPrimitive } from "@base-ui/react/select";
|
|
3
3
|
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
|
|
4
|
+
import * as React from "react";
|
|
4
5
|
import { cn } from "../lib/utils.js";
|
|
5
|
-
|
|
6
|
+
// Walks `SelectContent` children to harvest `<SelectItem value="..."> label </SelectItem>`
|
|
7
|
+
// pairs. Feeds the resulting map to base-ui's `items` prop so `<Select.Value />` renders
|
|
8
|
+
// the label instead of the raw value — matches the shadcn/Radix Select expectation.
|
|
9
|
+
function collectSelectItems(children, out = {}) {
|
|
10
|
+
React.Children.forEach(children, (child) => {
|
|
11
|
+
if (!React.isValidElement(child))
|
|
12
|
+
return;
|
|
13
|
+
const props = child.props;
|
|
14
|
+
if (typeof props.value === "string" && props.children != null) {
|
|
15
|
+
out[props.value] = props.children;
|
|
16
|
+
}
|
|
17
|
+
if (props.children != null) {
|
|
18
|
+
collectSelectItems(props.children, out);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
function Select({ children, items, ...props }) {
|
|
24
|
+
const harvested = React.useMemo(() => {
|
|
25
|
+
if (items !== undefined)
|
|
26
|
+
return undefined;
|
|
27
|
+
const map = collectSelectItems(children);
|
|
28
|
+
return Object.keys(map).length > 0 ? map : undefined;
|
|
29
|
+
}, [items, children]);
|
|
30
|
+
const resolvedItems = (items ?? harvested);
|
|
31
|
+
return (_jsx(SelectPrimitive.Root, { items: resolvedItems, ...props, children: children }));
|
|
32
|
+
}
|
|
6
33
|
function SelectGroup({ className, ...props }) {
|
|
7
34
|
return (_jsx(SelectPrimitive.Group, { "data-slot": "select-group", className: cn("scroll-my-1 p-1", className), ...props }));
|
|
8
35
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voyantjs/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -35,10 +35,10 @@
|
|
|
35
35
|
"tw-animate-css": "^1.3.5",
|
|
36
36
|
"vaul": "^1.1.2",
|
|
37
37
|
"zod": "^4.3.6",
|
|
38
|
-
"@voyantjs/i18n": "0.
|
|
39
|
-
"@voyantjs/notifications": "0.
|
|
40
|
-
"@voyantjs/notifications-react": "0.
|
|
41
|
-
"@voyantjs/utils": "0.
|
|
38
|
+
"@voyantjs/i18n": "0.29.0",
|
|
39
|
+
"@voyantjs/notifications": "0.29.0",
|
|
40
|
+
"@voyantjs/notifications-react": "0.29.0",
|
|
41
|
+
"@voyantjs/utils": "0.29.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@tailwindcss/postcss": "^4.1.11",
|
|
@@ -70,6 +70,11 @@
|
|
|
70
70
|
"import": "./dist/components/*.js",
|
|
71
71
|
"default": "./dist/components/*.js"
|
|
72
72
|
},
|
|
73
|
+
"./components/big-calendar": {
|
|
74
|
+
"types": "./dist/components/big-calendar/index.d.ts",
|
|
75
|
+
"import": "./dist/components/big-calendar/index.js",
|
|
76
|
+
"default": "./dist/components/big-calendar/index.js"
|
|
77
|
+
},
|
|
73
78
|
"./hooks/*": {
|
|
74
79
|
"types": "./dist/hooks/*.d.ts",
|
|
75
80
|
"import": "./dist/hooks/*.js",
|
package/src/styles/globals.css
CHANGED
|
@@ -135,6 +135,10 @@
|
|
|
135
135
|
--color-sidebar-ring: var(--sidebar-ring);
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
@utility bg-calendar-disabled-hour {
|
|
139
|
+
background-color: color-mix(in oklab, var(--muted) 35%, transparent);
|
|
140
|
+
}
|
|
141
|
+
|
|
138
142
|
@layer base {
|
|
139
143
|
* {
|
|
140
144
|
@apply border-border outline-ring/50;
|