@caratlogic/design-system 0.1.10 → 0.1.12
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/layouts/AppShell/AppShell.types.d.ts +14 -1
- package/dist/components/layouts/AppShell/AppShell.types.d.ts.map +1 -1
- package/dist/components/layouts/AppShell/DashboardShell.d.ts.map +1 -1
- package/dist/components/molecules/InAppNotificationCard/InAppNotificationCard.accessibility.d.ts +4 -0
- package/dist/components/molecules/InAppNotificationCard/InAppNotificationCard.accessibility.d.ts.map +1 -0
- package/dist/components/molecules/InAppNotificationCard/InAppNotificationCard.d.ts +8 -0
- package/dist/components/molecules/InAppNotificationCard/InAppNotificationCard.d.ts.map +1 -0
- package/dist/components/molecules/InAppNotificationCard/InAppNotificationCard.types.d.ts +22 -0
- package/dist/components/molecules/InAppNotificationCard/InAppNotificationCard.types.d.ts.map +1 -0
- package/dist/components/molecules/InAppNotificationCard/InAppNotificationCard.utils.d.ts +4 -0
- package/dist/components/molecules/InAppNotificationCard/InAppNotificationCard.utils.d.ts.map +1 -0
- package/dist/components/molecules/InAppNotificationCard/index.d.ts +4 -0
- package/dist/components/molecules/InAppNotificationCard/index.d.ts.map +1 -0
- package/dist/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.accessibility.d.ts +9 -0
- package/dist/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.accessibility.d.ts.map +1 -0
- package/dist/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.d.ts +9 -0
- package/dist/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.d.ts.map +1 -0
- package/dist/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.hooks.d.ts +12 -0
- package/dist/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.hooks.d.ts.map +1 -0
- package/dist/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.types.d.ts +41 -0
- package/dist/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.types.d.ts.map +1 -0
- package/dist/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.utils.d.ts +4 -0
- package/dist/components/organisms/InAppNotificationsPanel/InAppNotificationsPanel.utils.d.ts.map +1 -0
- package/dist/components/organisms/InAppNotificationsPanel/index.d.ts +5 -0
- package/dist/components/organisms/InAppNotificationsPanel/index.d.ts.map +1 -0
- package/dist/components/organisms/MediaGallery/MediaGallery.d.ts +1 -1
- package/dist/components/organisms/MediaGallery/MediaGallery.d.ts.map +1 -1
- package/dist/components/organisms/MediaGallery/MediaGallery.utils.d.ts +6 -0
- package/dist/components/organisms/MediaGallery/MediaGallery.utils.d.ts.map +1 -1
- package/dist/components/organisms/Navbar/Navbar.d.ts +1 -1
- package/dist/components/organisms/Navbar/Navbar.d.ts.map +1 -1
- package/dist/constants/filePatterns.d.ts +3 -0
- package/dist/constants/filePatterns.d.ts.map +1 -1
- package/dist/index.cjs.js +184 -19
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +180 -20
- package/dist/index.esm.js.map +1 -1
- package/dist/utils/relativeTime.d.ts +6 -0
- package/dist/utils/relativeTime.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/index.cjs.js
CHANGED
|
@@ -2698,12 +2698,12 @@ const createLucideIcon = (iconName, iconNode) => {
|
|
|
2698
2698
|
*/
|
|
2699
2699
|
|
|
2700
2700
|
|
|
2701
|
-
const __iconNode$
|
|
2701
|
+
const __iconNode$18 = [
|
|
2702
2702
|
["rect", { width: "20", height: "5", x: "2", y: "3", rx: "1", key: "1wp1u1" }],
|
|
2703
2703
|
["path", { d: "M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8", key: "1s80jp" }],
|
|
2704
2704
|
["path", { d: "M10 12h4", key: "a56b0p" }]
|
|
2705
2705
|
];
|
|
2706
|
-
const Archive = createLucideIcon("archive", __iconNode$
|
|
2706
|
+
const Archive = createLucideIcon("archive", __iconNode$18);
|
|
2707
2707
|
|
|
2708
2708
|
/**
|
|
2709
2709
|
* @license lucide-react v0.539.0 - ISC
|
|
@@ -2713,11 +2713,11 @@ const Archive = createLucideIcon("archive", __iconNode$17);
|
|
|
2713
2713
|
*/
|
|
2714
2714
|
|
|
2715
2715
|
|
|
2716
|
-
const __iconNode$
|
|
2716
|
+
const __iconNode$17 = [
|
|
2717
2717
|
["path", { d: "m12 19-7-7 7-7", key: "1l729n" }],
|
|
2718
2718
|
["path", { d: "M19 12H5", key: "x3x0zl" }]
|
|
2719
2719
|
];
|
|
2720
|
-
const ArrowLeft = createLucideIcon("arrow-left", __iconNode$
|
|
2720
|
+
const ArrowLeft = createLucideIcon("arrow-left", __iconNode$17);
|
|
2721
2721
|
|
|
2722
2722
|
/**
|
|
2723
2723
|
* @license lucide-react v0.539.0 - ISC
|
|
@@ -2727,11 +2727,11 @@ const ArrowLeft = createLucideIcon("arrow-left", __iconNode$16);
|
|
|
2727
2727
|
*/
|
|
2728
2728
|
|
|
2729
2729
|
|
|
2730
|
-
const __iconNode$
|
|
2730
|
+
const __iconNode$16 = [
|
|
2731
2731
|
["path", { d: "M5 12h14", key: "1ays0h" }],
|
|
2732
2732
|
["path", { d: "m12 5 7 7-7 7", key: "xquz4c" }]
|
|
2733
2733
|
];
|
|
2734
|
-
const ArrowRight = createLucideIcon("arrow-right", __iconNode$
|
|
2734
|
+
const ArrowRight = createLucideIcon("arrow-right", __iconNode$16);
|
|
2735
2735
|
|
|
2736
2736
|
/**
|
|
2737
2737
|
* @license lucide-react v0.539.0 - ISC
|
|
@@ -2741,11 +2741,32 @@ const ArrowRight = createLucideIcon("arrow-right", __iconNode$15);
|
|
|
2741
2741
|
*/
|
|
2742
2742
|
|
|
2743
2743
|
|
|
2744
|
-
const __iconNode$
|
|
2744
|
+
const __iconNode$15 = [
|
|
2745
2745
|
["path", { d: "M4.929 4.929 19.07 19.071", key: "196cmz" }],
|
|
2746
2746
|
["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }]
|
|
2747
2747
|
];
|
|
2748
|
-
const Ban = createLucideIcon("ban", __iconNode$
|
|
2748
|
+
const Ban = createLucideIcon("ban", __iconNode$15);
|
|
2749
|
+
|
|
2750
|
+
/**
|
|
2751
|
+
* @license lucide-react v0.539.0 - ISC
|
|
2752
|
+
*
|
|
2753
|
+
* This source code is licensed under the ISC license.
|
|
2754
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
2755
|
+
*/
|
|
2756
|
+
|
|
2757
|
+
|
|
2758
|
+
const __iconNode$14 = [
|
|
2759
|
+
["path", { d: "M10.268 21a2 2 0 0 0 3.464 0", key: "vwvbt9" }],
|
|
2760
|
+
[
|
|
2761
|
+
"path",
|
|
2762
|
+
{
|
|
2763
|
+
d: "M13.916 2.314A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.74 7.327A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673 9 9 0 0 1-.585-.665",
|
|
2764
|
+
key: "1tip0g"
|
|
2765
|
+
}
|
|
2766
|
+
],
|
|
2767
|
+
["circle", { cx: "18", cy: "8", r: "3", key: "1g0gzu" }]
|
|
2768
|
+
];
|
|
2769
|
+
const BellDot = createLucideIcon("bell-dot", __iconNode$14);
|
|
2749
2770
|
|
|
2750
2771
|
/**
|
|
2751
2772
|
* @license lucide-react v0.539.0 - ISC
|
|
@@ -28257,6 +28278,53 @@ const FilterSelect = ({ label, value, onValueChange, options, resetValue = 'all'
|
|
|
28257
28278
|
return (jsxRuntime.jsxs(Select, { value: value, onValueChange: onValueChange, keepOpenOnSelect: true, children: [jsxRuntime.jsxs(SelectTrigger, { className: cn(TRIGGER_BASE, active && TRIGGER_ACTIVE, triggerClassName), children: [jsxRuntime.jsx(Funnel, { className: cn('h-4 w-4 shrink-0', active ? 'text-primary' : 'text-muted-foreground'), "aria-hidden": "true" }), jsxRuntime.jsxs("span", { className: "flex items-center gap-1", children: [jsxRuntime.jsx(SelectValue, { placeholder: label }), active && (jsxRuntime.jsx("span", { className: "ml-1 inline-flex h-4 min-w-4 items-center justify-center rounded bg-primary/10 px-1 text-[10px] font-semibold leading-none text-primary", children: activeCount }))] })] }), jsxRuntime.jsxs(SelectContent, { className: cn(CONTENT_BASE, contentClassName), searchable: searchable, children: [jsxRuntime.jsx(SelectItem, { value: resetValue, withCheckbox: true, children: label }), options.map((opt) => (jsxRuntime.jsx(SelectItem, { value: opt.value, searchValue: opt.searchValue, withCheckbox: true, children: opt.label }, opt.value)))] })] }));
|
|
28258
28279
|
};
|
|
28259
28280
|
|
|
28281
|
+
/**
|
|
28282
|
+
* Compact "time ago" label, e.g. "5m ago". Falls back to a locale date past a
|
|
28283
|
+
* week so older entries stay scannable.
|
|
28284
|
+
*/
|
|
28285
|
+
function formatRelativeTime(value) {
|
|
28286
|
+
if (!value)
|
|
28287
|
+
return '';
|
|
28288
|
+
const then = value instanceof Date ? value.getTime() : new Date(value).getTime();
|
|
28289
|
+
if (Number.isNaN(then))
|
|
28290
|
+
return '';
|
|
28291
|
+
const minutes = Math.floor((Date.now() - then) / 60000);
|
|
28292
|
+
if (minutes < 1)
|
|
28293
|
+
return 'Just now';
|
|
28294
|
+
if (minutes < 60)
|
|
28295
|
+
return `${minutes}m ago`;
|
|
28296
|
+
const hours = Math.floor(minutes / 60);
|
|
28297
|
+
if (hours < 24)
|
|
28298
|
+
return `${hours}h ago`;
|
|
28299
|
+
const days = Math.floor(hours / 24);
|
|
28300
|
+
if (days < 7)
|
|
28301
|
+
return `${days}d ago`;
|
|
28302
|
+
return new Date(then).toLocaleDateString();
|
|
28303
|
+
}
|
|
28304
|
+
|
|
28305
|
+
/** Read entries sit flat on the surface; unread ones get a primary wash. */
|
|
28306
|
+
function getInAppNotificationCardClasses(isRead, className) {
|
|
28307
|
+
return cn('flex h-[5.125rem] w-full cursor-pointer gap-2.5 rounded-lg border px-3 py-3 text-left transition-colors', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50', isRead
|
|
28308
|
+
? 'border-border bg-card hover:bg-muted/40'
|
|
28309
|
+
: 'border-primary/20 bg-primary/5 hover:bg-primary/10', className);
|
|
28310
|
+
}
|
|
28311
|
+
function getInAppNotificationIconClasses(isRead) {
|
|
28312
|
+
return cn('mt-0.5 flex size-7 shrink-0 items-center justify-center rounded-full', isRead ? 'bg-muted text-muted-foreground' : 'bg-primary/10 text-primary');
|
|
28313
|
+
}
|
|
28314
|
+
|
|
28315
|
+
const inAppNotificationCardA11y = {
|
|
28316
|
+
unreadLabel: 'Unread',
|
|
28317
|
+
};
|
|
28318
|
+
|
|
28319
|
+
/**
|
|
28320
|
+
* One row in a notification list. Unread state is signalled by a dot, a tinted
|
|
28321
|
+
* surface and a heavier title, so it never relies on colour alone.
|
|
28322
|
+
*/
|
|
28323
|
+
const InAppNotificationCard = ({ notification, onSelect, defaultIcon, className, }) => {
|
|
28324
|
+
const { title, message, createdAt, isRead, subject, icon } = notification;
|
|
28325
|
+
return (jsxRuntime.jsxs("button", { type: "button", onClick: () => onSelect(notification), className: getInAppNotificationCardClasses(isRead, className), children: [jsxRuntime.jsx("span", { className: getInAppNotificationIconClasses(isRead), children: icon ?? defaultIcon ?? (jsxRuntime.jsx(BellDot, { className: "size-4", "aria-hidden": "true" })) }), jsxRuntime.jsxs("span", { className: "min-w-0 flex-1", children: [jsxRuntime.jsxs("span", { className: "flex items-baseline gap-2", children: [!isRead && (jsxRuntime.jsx("span", { role: "img", "aria-label": inAppNotificationCardA11y.unreadLabel, className: "size-1.5 shrink-0 rounded-full bg-primary" })), jsxRuntime.jsx("span", { className: cn('min-w-0 flex-1 truncate text-sm text-foreground', isRead ? 'font-medium' : 'font-semibold'), children: title }), jsxRuntime.jsx("time", { className: "shrink-0 text-[0.6875rem] text-muted-foreground tabular-nums", children: formatRelativeTime(createdAt) })] }), jsxRuntime.jsx("span", { className: "mt-1 line-clamp-1 text-xs text-secondary-foreground", children: message }), subject && (jsxRuntime.jsx("span", { className: "mt-0.5 block truncate text-[0.6875rem] font-medium text-muted-foreground", children: subject }))] })] }));
|
|
28326
|
+
};
|
|
28327
|
+
|
|
28260
28328
|
const HEADER_TONE_CLASSES = {
|
|
28261
28329
|
neutral: 'bg-[var(--section-header)]',
|
|
28262
28330
|
gem: 'bg-primary/5 dark:bg-[#173626]',
|
|
@@ -64671,6 +64739,8 @@ const FILE_TYPE_PATTERNS = {
|
|
|
64671
64739
|
DOCUMENT: /\.(doc|docx|xls|xlsx|ppt|pptx)($|\?)/i,
|
|
64672
64740
|
VIDEO: /\.(mp4|m4v|avi|mov|wmv|flv|webm|mkv|ogv|mpe?g|3gp)($|\?)/i,
|
|
64673
64741
|
AUDIO: /\.(mp3|wav|flac|aac|ogg)($|\?)/i,
|
|
64742
|
+
/** Interactive viewer pages (supplier 360° spinners), not media files. */
|
|
64743
|
+
EMBED: /\.html?($|\?|#)/i,
|
|
64674
64744
|
};
|
|
64675
64745
|
const isFileType = {
|
|
64676
64746
|
pdf: (url) => FILE_TYPE_PATTERNS.PDF.test(url),
|
|
@@ -64678,6 +64748,7 @@ const isFileType = {
|
|
|
64678
64748
|
document: (url) => FILE_TYPE_PATTERNS.DOCUMENT.test(url),
|
|
64679
64749
|
video: (url) => FILE_TYPE_PATTERNS.VIDEO.test(url),
|
|
64680
64750
|
audio: (url) => FILE_TYPE_PATTERNS.AUDIO.test(url),
|
|
64751
|
+
embed: (url) => FILE_TYPE_PATTERNS.EMBED.test(url),
|
|
64681
64752
|
};
|
|
64682
64753
|
|
|
64683
64754
|
/** Seek to ~1st frame for static video thumbnails (HTML5 media fragment). */
|
|
@@ -64693,6 +64764,12 @@ const createVideoMediaItem = (src, index, idPrefix = 'vid') => ({
|
|
|
64693
64764
|
src,
|
|
64694
64765
|
thumbSrc: videoThumbnailSrc(src),
|
|
64695
64766
|
});
|
|
64767
|
+
/**
|
|
64768
|
+
* True when a "video" is really an interactive viewer page. Supplier 360°
|
|
64769
|
+
* spinners (Vision360, Varni) serve `text/html`, so `<video>` renders nothing
|
|
64770
|
+
* and there is no frame to seek for a thumbnail — they need an iframe.
|
|
64771
|
+
*/
|
|
64772
|
+
const isEmbedMediaItem = (item) => item.kind === 'video' && isFileType.embed(item.src);
|
|
64696
64773
|
const isPdfMediaItem = (item) => item.isPdf === true ||
|
|
64697
64774
|
isFileType.pdf(item.src) ||
|
|
64698
64775
|
(item.kind === 'certificate' && !isFileType.image(item.src));
|
|
@@ -65646,6 +65723,71 @@ const FileUploader = ({ accept, multiple = true, maxSize = 50, maxFiles = 5, lab
|
|
|
65646
65723
|
return (jsxRuntime.jsxs("div", { className: cn('flex flex-col gap-4', className), children: [label && (jsxRuntime.jsx("label", { className: "text-sm font-medium text-foreground", children: label })), jsxRuntime.jsx(DropZone, { uploadSubject: uploadSubject, supportedFormats: supportedFormats, maxSize: maxSize, maxFiles: maxFiles, accept: accept, multiple: multiple, disabled: disabled, onFiles: handlePickerFiles }), files.length > 0 && (jsxRuntime.jsxs("div", { className: "space-y-2", children: [jsxRuntime.jsx("p", { className: "text-[13px] text-label", children: "Uploaded File" }), jsxRuntime.jsx(DndContext, { sensors: sensors, collisionDetection: closestCenter, onDragEnd: onDragEnd, children: jsxRuntime.jsx(SortableContext, { items: files.map((f) => f.id), strategy: rectSortingStrategy, children: jsxRuntime.jsx("div", { className: "flex flex-wrap gap-3", children: files.map((file) => (jsxRuntime.jsx(FileCard, { file: file, sortable: sortable, disabled: disabled, onRemove: handleRemove, onPreview: previewFile }, file.id))) }) }) })] })), enableUrlInput && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-sm font-normal text-label", children: [jsxRuntime.jsx("span", { className: "h-px flex-1 bg-border" }), jsxRuntime.jsx("span", { children: "or" }), jsxRuntime.jsx("span", { className: "h-px flex-1 bg-border" })] }), jsxRuntime.jsx(UrlImportRow, { disabled: disabled, onAdd: handleAddUrl })] })), cropImages && currentCropFile && cropPreviewUrl && (jsxRuntime.jsx(ImageCropModal, { open: true, imageUrl: cropPreviewUrl, fileName: currentCropFile.name, fileType: currentCropFile.type, onCancel: handleCropCancel, onConfirm: handleCropConfirm }))] }));
|
|
65647
65724
|
};
|
|
65648
65725
|
|
|
65726
|
+
/** Falls back to counting the current page when the caller has no total. */
|
|
65727
|
+
function resolveUnreadCount(notifications, unreadCount) {
|
|
65728
|
+
if (typeof unreadCount === 'number')
|
|
65729
|
+
return Math.max(0, unreadCount);
|
|
65730
|
+
return notifications.filter((notification) => !notification.isRead).length;
|
|
65731
|
+
}
|
|
65732
|
+
|
|
65733
|
+
// 1. React
|
|
65734
|
+
/** Derived display state for the panel header and body. */
|
|
65735
|
+
function useInAppNotificationsPanel(notifications, unreadCount) {
|
|
65736
|
+
return React.useMemo(() => {
|
|
65737
|
+
const resolved = resolveUnreadCount(notifications, unreadCount);
|
|
65738
|
+
return {
|
|
65739
|
+
unreadCount: resolved,
|
|
65740
|
+
hasUnread: resolved > 0,
|
|
65741
|
+
isEmpty: notifications.length === 0,
|
|
65742
|
+
};
|
|
65743
|
+
}, [notifications, unreadCount]);
|
|
65744
|
+
}
|
|
65745
|
+
/**
|
|
65746
|
+
* Fires `onLoadMore` when the sentinel scrolls into view inside `rootRef`.
|
|
65747
|
+
* No-ops where IntersectionObserver is unavailable (jsdom, older browsers), so
|
|
65748
|
+
* the list simply stops auto-loading rather than breaking.
|
|
65749
|
+
*/
|
|
65750
|
+
function useInfiniteScrollSentinel(rootRef, onLoadMore, enabled) {
|
|
65751
|
+
const sentinelRef = React.useRef(null);
|
|
65752
|
+
React.useEffect(() => {
|
|
65753
|
+
const sentinel = sentinelRef.current;
|
|
65754
|
+
if (!sentinel || !enabled || !onLoadMore)
|
|
65755
|
+
return;
|
|
65756
|
+
if (typeof IntersectionObserver === 'undefined')
|
|
65757
|
+
return;
|
|
65758
|
+
const observer = new IntersectionObserver((entries) => {
|
|
65759
|
+
if (entries.some((entry) => entry.isIntersecting))
|
|
65760
|
+
onLoadMore();
|
|
65761
|
+
},
|
|
65762
|
+
// Start fetching slightly before the sentinel is actually visible.
|
|
65763
|
+
{ root: rootRef.current, rootMargin: '80px' });
|
|
65764
|
+
observer.observe(sentinel);
|
|
65765
|
+
return () => observer.disconnect();
|
|
65766
|
+
}, [rootRef, onLoadMore, enabled]);
|
|
65767
|
+
return sentinelRef;
|
|
65768
|
+
}
|
|
65769
|
+
|
|
65770
|
+
const inAppNotificationsPanelA11y = {
|
|
65771
|
+
panelLabel: 'Notifications',
|
|
65772
|
+
listLabel: 'Notification list',
|
|
65773
|
+
markAllReadLabel: 'Mark all notifications as read',
|
|
65774
|
+
retryLabel: 'Retry loading notifications',
|
|
65775
|
+
};
|
|
65776
|
+
/** Non-modal dropdown, so `dialog` rather than `alertdialog`. */
|
|
65777
|
+
const IN_APP_NOTIFICATIONS_PANEL_ROLE = 'dialog';
|
|
65778
|
+
|
|
65779
|
+
/**
|
|
65780
|
+
* Notification list with header actions and loading / error / empty states.
|
|
65781
|
+
* Positioning is the caller's concern — render it inside a popover anchored to
|
|
65782
|
+
* whatever opens it (see `DashboardShell`'s `notificationsPanel` slot).
|
|
65783
|
+
*/
|
|
65784
|
+
const InAppNotificationsPanel = ({ notifications, onSelect, unreadCount, totalCount, isLoading = false, hasNextPage = false, isFetchingNextPage = false, onLoadMore, isError = false, onRetry, onMarkAllRead, isMarkingAllRead = false, title = inAppNotificationsPanelA11y.panelLabel, emptyTitle = 'No notifications yet', emptyDescription, emptyIcon, emptyState, errorMessage = "Couldn't load notifications.", defaultIcon, className, }) => {
|
|
65785
|
+
const { unreadCount: resolvedUnread, hasUnread, isEmpty, } = useInAppNotificationsPanel(notifications, unreadCount);
|
|
65786
|
+
const listRef = React.useRef(null);
|
|
65787
|
+
const sentinelRef = useInfiniteScrollSentinel(listRef, onLoadMore, hasNextPage && !isLoading);
|
|
65788
|
+
return (jsxRuntime.jsxs("div", { role: IN_APP_NOTIFICATIONS_PANEL_ROLE, "aria-label": inAppNotificationsPanelA11y.panelLabel, className: cn('flex w-full flex-col overflow-hidden', className), children: [jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-border px-2 py-3", children: [jsxRuntime.jsx("div", { className: "min-w-0", children: jsxRuntime.jsxs("h2", { className: "flex min-w-0 items-center gap-2 text-sm font-semibold text-foreground", children: [jsxRuntime.jsx("span", { className: "truncate", children: title }), typeof totalCount === 'number' && (jsxRuntime.jsx("span", { className: "shrink-0 rounded-full bg-muted px-2 py-0.5 text-[0.6875rem] font-semibold text-muted-foreground tabular-nums", children: totalCount }))] }) }), jsxRuntime.jsx("span", { className: "flex shrink-0 items-center gap-2", children: hasUnread && onMarkAllRead && (jsxRuntime.jsx(Button$1, { variant: "ghost", size: "sm", onClick: onMarkAllRead, disabled: isMarkingAllRead, "aria-label": inAppNotificationsPanelA11y.markAllReadLabel, className: "h-7 px-2 text-xs font-medium text-primary hover:text-primary", children: "Mark all as read" })) })] }), jsxRuntime.jsx("div", { ref: listRef, className: "min-h-0 max-h-[16.75rem] flex-1 overflow-y-auto p-2", children: isLoading ? (jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-2 py-10 text-sm text-muted-foreground", children: [jsxRuntime.jsx(LoaderCircle, { className: "size-4 animate-spin", "aria-hidden": "true" }), "Loading notifications..."] })) : isError ? (jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-2 py-10 text-center", children: [jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: errorMessage }), onRetry && (jsxRuntime.jsx(Button$1, { variant: "outline", size: "sm", onClick: onRetry, "aria-label": inAppNotificationsPanelA11y.retryLabel, className: "h-7 px-3 text-xs", children: "Try again" }))] })) : isEmpty ? ((emptyState ?? (jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-2 px-6 py-10 text-center", children: [jsxRuntime.jsx("span", { className: "flex size-10 items-center justify-center rounded-full bg-muted", children: emptyIcon ?? (jsxRuntime.jsx(Bell, { className: "size-5 text-muted-foreground", "aria-hidden": "true" })) }), jsxRuntime.jsx("p", { className: "text-sm font-medium text-foreground", children: emptyTitle }), emptyDescription && (jsxRuntime.jsx("p", { className: "max-w-[16rem] text-xs text-muted-foreground", children: emptyDescription }))] })))) : (jsxRuntime.jsxs("div", { role: "list", "aria-label": inAppNotificationsPanelA11y.listLabel, className: "flex flex-col gap-1.5", children: [notifications.map((notification) => (jsxRuntime.jsx(InAppNotificationCard, { notification: notification, onSelect: onSelect, defaultIcon: defaultIcon }, notification.id))), hasNextPage && (jsxRuntime.jsx("div", { ref: sentinelRef, "aria-hidden": "true", className: "h-px" })), isFetchingNextPage && (jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-2 py-3 text-xs text-muted-foreground", children: [jsxRuntime.jsx(LoaderCircle, { className: "size-3.5 animate-spin", "aria-hidden": "true" }), "Loading more..."] }))] })) })] }));
|
|
65789
|
+
};
|
|
65790
|
+
|
|
65649
65791
|
const stop = (fn) => (e) => {
|
|
65650
65792
|
e.stopPropagation();
|
|
65651
65793
|
fn?.();
|
|
@@ -65800,9 +65942,22 @@ const PdfPreview = ({ item }) => {
|
|
|
65800
65942
|
const docsViewer = `https://docs.google.com/gview?embedded=1&url=${encodeURIComponent(item.src)}`;
|
|
65801
65943
|
return (jsxRuntime.jsx("object", { data: item.src, type: "application/pdf", className: "h-full w-full bg-card", "aria-label": item.name ?? 'Certificate PDF', children: jsxRuntime.jsx("iframe", { title: item.name ?? 'Certificate PDF', className: "h-full w-full border-0 bg-card", src: docsViewer }) }));
|
|
65802
65944
|
};
|
|
65945
|
+
/**
|
|
65946
|
+
* Videos arrive as either a real file or a supplier's 360° viewer page. The URL
|
|
65947
|
+
* settles most cases, but not every viewer ends in `.html` (v360, loupe360),
|
|
65948
|
+
* so a `<video>` that fails to load falls back to the iframe as well. Remounted
|
|
65949
|
+
* per selection via `key`, which resets that fallback.
|
|
65950
|
+
*/
|
|
65951
|
+
const VideoOrEmbed = ({ item }) => {
|
|
65952
|
+
const [useEmbed, setUseEmbed] = React.useState(() => isEmbedMediaItem(item));
|
|
65953
|
+
if (useEmbed) {
|
|
65954
|
+
return (jsxRuntime.jsx("iframe", { src: item.src, title: item.name ?? '360\u00B0 viewer', className: "h-full w-full border-0 bg-card", allowFullScreen: true }));
|
|
65955
|
+
}
|
|
65956
|
+
return (jsxRuntime.jsx("video", { src: item.src, controls: true, onError: () => setUseEmbed(true), className: "h-full w-full object-cover", "aria-label": item.name ?? 'Video' }));
|
|
65957
|
+
};
|
|
65803
65958
|
const MainMedia = ({ item }) => {
|
|
65804
65959
|
if (item.kind === 'video') {
|
|
65805
|
-
return
|
|
65960
|
+
return jsxRuntime.jsx(VideoOrEmbed, { item: item });
|
|
65806
65961
|
}
|
|
65807
65962
|
if (isPdfMediaItem(item)) {
|
|
65808
65963
|
return jsxRuntime.jsx(PdfPreview, { item: item });
|
|
@@ -65812,7 +65967,7 @@ const MainMedia = ({ item }) => {
|
|
|
65812
65967
|
const Thumbnail = ({ item, isActive, onSelect }) => {
|
|
65813
65968
|
const Icon = KIND_ICON[item.kind];
|
|
65814
65969
|
const isPdf = isPdfMediaItem(item);
|
|
65815
|
-
const isVideoWithoutThumb = item.kind === 'video' && !item.thumbSrc;
|
|
65970
|
+
const isVideoWithoutThumb = item.kind === 'video' && (!item.thumbSrc || isEmbedMediaItem(item));
|
|
65816
65971
|
return (jsxRuntime.jsxs("button", { type: "button", onClick: onSelect, "aria-label": `Select ${item.name ?? item.kind}`, "aria-pressed": isActive, className: cn('group relative flex aspect-square w-full cursor-pointer overflow-hidden rounded-lg border transition-colors', isActive
|
|
65817
65972
|
? 'border-primary ring-2 ring-primary/30'
|
|
65818
65973
|
: 'border-border hover:border-primary/40'), children: [isPdf ? (jsxRuntime.jsxs("span", { className: "flex h-full w-full flex-col items-center justify-center gap-1 bg-muted p-2 text-muted-foreground", children: [jsxRuntime.jsx(Icon, { size: 20 }), jsxRuntime.jsx("span", { className: "text-[10px] font-medium uppercase", children: "PDF" })] })) : isVideoWithoutThumb ? (jsxRuntime.jsx("div", { className: "h-full w-full flex items-center justify-center bg-muted", children: jsxRuntime.jsx(Icon, { size: 24, className: "text-muted-foreground" }) })) : item.kind === 'video' ? (jsxRuntime.jsx("video", { src: item.thumbSrc ?? item.src, muted: true, playsInline: true, preload: "metadata", className: "h-full w-full object-cover", "aria-hidden": "true" })) : (jsxRuntime.jsx("img", { src: item.thumbSrc ?? item.src, alt: "", className: "h-full w-full object-cover" })), !isPdf && item.kind !== 'image' && !isVideoWithoutThumb && (jsxRuntime.jsx("span", { className: "pointer-events-none absolute inset-0 flex items-center justify-center bg-black/30 text-white", children: jsxRuntime.jsx(Icon, { size: 20 }) })), isVideoWithoutThumb && (jsxRuntime.jsx("span", { className: "pointer-events-none absolute inset-0 flex items-center justify-center bg-black/30" }))] }));
|
|
@@ -65833,7 +65988,7 @@ const MediaGallery = ({ items, thumbsVisible = 4, hideTabs = false, mainAspectCl
|
|
|
65833
65988
|
(kinds.length > 0 ? (jsxRuntime.jsx(Tabs, { value: activeKind ?? undefined, onValueChange: (v) => setActiveKind(v), tabStyle: "boxed", size: "sm", children: jsxRuntime.jsx(TabsList, { children: kinds.map((kind) => {
|
|
65834
65989
|
const Icon = KIND_ICON[kind];
|
|
65835
65990
|
return (jsxRuntime.jsx(TabsTrigger, { value: kind, icon: jsxRuntime.jsx(Icon, { size: 14 }), children: MEDIA_KIND_LABEL[kind] }, kind));
|
|
65836
|
-
}) }) })) : (jsxRuntime.jsx("div", { className: "h-9", "aria-hidden": "true" }))), selected && isZoomableImage ? (jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.jsx(ZoomSplitViewer, { src: selected.src, alt: selected.name ?? 'Media' }), !hideDownload && (jsxRuntime.jsx("div", { className: "absolute right-3 top-3 z-20", children: jsxRuntime.jsx("button", { type: "button", onClick: handleDownload, "aria-label": "Download media", className: "inline-flex h-9 w-9 cursor-pointer items-center justify-center rounded-md border border-border bg-card text-foreground transition-all hover:border-primary hover:shadow-[0_0_0_4px_rgba(49,122,81,0.10)] focus-visible:border-primary focus-visible:shadow-[0_0_0_4px_rgba(49,122,81,0.10)] focus-visible:outline-none", children: jsxRuntime.jsx(Download, { className: "h-4 w-4" }) }) }))] })) : selected ? (jsxRuntime.jsxs("div", { className: cn('relative overflow-hidden rounded-xl border border-border bg-muted', mainAspectClassName), children: [jsxRuntime.jsx(MainMedia, { item: selected }), !hideDownload && (jsxRuntime.jsx("div", { className: "absolute right-3 top-3", children: jsxRuntime.jsx("button", { type: "button", onClick: handleDownload, "aria-label": "Download media", className: "inline-flex h-9 w-9 cursor-pointer items-center justify-center rounded-md border border-border bg-card text-foreground transition-all hover:border-primary hover:shadow-[0_0_0_4px_rgba(49,122,81,0.10)] focus-visible:border-primary focus-visible:shadow-[0_0_0_4px_rgba(49,122,81,0.10)] focus-visible:outline-none", children: jsxRuntime.jsx(Download, { className: "h-4 w-4" }) }) }))] })) : (jsxRuntime.jsx("div", { className: cn('flex items-center justify-center rounded-xl border border-dashed border-border bg-muted/40 text-sm text-muted-foreground', mainAspectClassName), children: emptyText })), visibleItems.length > 1 && (jsxRuntime.jsx("div", { className: "grid gap-3", style: {
|
|
65991
|
+
}) }) })) : (jsxRuntime.jsx("div", { className: "h-9", "aria-hidden": "true" }))), selected && isZoomableImage ? (jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.jsx(ZoomSplitViewer, { src: selected.src, alt: selected.name ?? 'Media' }), !hideDownload && (jsxRuntime.jsx("div", { className: "absolute right-3 top-3 z-20", children: jsxRuntime.jsx("button", { type: "button", onClick: handleDownload, "aria-label": "Download media", className: "inline-flex h-9 w-9 cursor-pointer items-center justify-center rounded-md border border-border bg-card text-foreground transition-all hover:border-primary hover:shadow-[0_0_0_4px_rgba(49,122,81,0.10)] focus-visible:border-primary focus-visible:shadow-[0_0_0_4px_rgba(49,122,81,0.10)] focus-visible:outline-none", children: jsxRuntime.jsx(Download, { className: "h-4 w-4" }) }) }))] })) : selected ? (jsxRuntime.jsxs("div", { className: cn('relative overflow-hidden rounded-xl border border-border bg-muted', mainAspectClassName), children: [jsxRuntime.jsx(MainMedia, { item: selected }, selected.id), !hideDownload && !isEmbedMediaItem(selected) && (jsxRuntime.jsx("div", { className: "absolute right-3 top-3", children: jsxRuntime.jsx("button", { type: "button", onClick: handleDownload, "aria-label": "Download media", className: "inline-flex h-9 w-9 cursor-pointer items-center justify-center rounded-md border border-border bg-card text-foreground transition-all hover:border-primary hover:shadow-[0_0_0_4px_rgba(49,122,81,0.10)] focus-visible:border-primary focus-visible:shadow-[0_0_0_4px_rgba(49,122,81,0.10)] focus-visible:outline-none", children: jsxRuntime.jsx(Download, { className: "h-4 w-4" }) }) }))] })) : (jsxRuntime.jsx("div", { className: cn('flex items-center justify-center rounded-xl border border-dashed border-border bg-muted/40 text-sm text-muted-foreground', mainAspectClassName), children: emptyText })), visibleItems.length > 1 && (jsxRuntime.jsx("div", { className: "grid gap-3", style: {
|
|
65837
65992
|
gridTemplateColumns: `repeat(${Math.min(visibleItems.length, thumbsVisible)}, minmax(0, 1fr))`,
|
|
65838
65993
|
}, children: visibleItems.slice(0, thumbsVisible).map((item) => (jsxRuntime.jsx(Thumbnail, { item: item, isActive: selected?.id === item.id, onSelect: () => setSelected(item) }, item.id))) }))] }));
|
|
65839
65994
|
};
|
|
@@ -65896,7 +66051,7 @@ const navbarIconButtonVariants = cva([
|
|
|
65896
66051
|
});
|
|
65897
66052
|
/** Tiny solid-pill badge ("8") absolutely positioned on the icon button. */
|
|
65898
66053
|
const navbarIndicatorCountVariants = cva([
|
|
65899
|
-
'absolute -top-
|
|
66054
|
+
'absolute -top-2.5 -right-2.5 inline-flex items-center justify-center',
|
|
65900
66055
|
'min-w-4 h-4 px-1 rounded-full ring-2 ring-card',
|
|
65901
66056
|
'text-[10px] font-medium leading-none tabular-nums text-white',
|
|
65902
66057
|
].join(' '), {
|
|
@@ -65983,10 +66138,11 @@ const NavbarTitle = ({ title, items, level = 1, className, ...props }) => {
|
|
|
65983
66138
|
// ─── Right-side actions container ────────────────────────────────────────────
|
|
65984
66139
|
const NavbarActions = ({ gap = 'md', className, children, ...props }) => (jsxRuntime.jsx("div", { className: cn(navbarActionsVariants({ gap }), className), ...props, children: children }));
|
|
65985
66140
|
// ─── Icon button (with optional dot or count badge) ──────────────────────────
|
|
65986
|
-
const NavbarIconButton = ({ appearance = 'plain', size = 'md', indicator = false, className, children, type = 'button', ...props }) => {
|
|
66141
|
+
const NavbarIconButton = React.forwardRef(({ appearance = 'plain', size = 'md', indicator = false, className, children, type = 'button', ...props }, ref) => {
|
|
65987
66142
|
const resolved = resolveIndicator(indicator);
|
|
65988
|
-
return (jsxRuntime.jsxs("button", { type: type, className: cn(navbarIconButtonVariants({ appearance, size }), className), ...props, children: [jsxRuntime.jsx("span", { "aria-hidden": "true", className: "inline-flex", children: children }), resolved && resolved.count !== null && (jsxRuntime.jsx("span", { role: "img", "aria-label": navbarA11y.notificationIndicatorLabel, className: cn(navbarIndicatorCountVariants({ tone: resolved.tone })), children: resolved.count })), resolved && resolved.count === null && (jsxRuntime.jsx("span", { role: "img", "aria-label": navbarA11y.notificationIndicatorLabel, className: cn(navbarIndicatorDotVariants({ tone: resolved.tone })) }))] }));
|
|
65989
|
-
};
|
|
66143
|
+
return (jsxRuntime.jsxs("button", { ref: ref, type: type, className: cn(navbarIconButtonVariants({ appearance, size }), className), ...props, children: [jsxRuntime.jsx("span", { "aria-hidden": "true", className: "inline-flex", children: children }), resolved && resolved.count !== null && (jsxRuntime.jsx("span", { role: "img", "aria-label": navbarA11y.notificationIndicatorLabel, className: cn(navbarIndicatorCountVariants({ tone: resolved.tone })), children: resolved.count })), resolved && resolved.count === null && (jsxRuntime.jsx("span", { role: "img", "aria-label": navbarA11y.notificationIndicatorLabel, className: cn(navbarIndicatorDotVariants({ tone: resolved.tone })) }))] }));
|
|
66144
|
+
});
|
|
66145
|
+
NavbarIconButton.displayName = 'NavbarIconButton';
|
|
65990
66146
|
// ─── Search input (matches Figma 288×36 muted pill) ──────────────────────────
|
|
65991
66147
|
const NavbarSearch = ({ placeholder = 'Search', value, onChange, onClear, className, containerClassName, ...rest }) => {
|
|
65992
66148
|
const isControlled = value !== undefined;
|
|
@@ -66070,7 +66226,8 @@ const NavLeaf = ({ item, currentPath, onNavigate }) => {
|
|
|
66070
66226
|
const Icon = item.icon;
|
|
66071
66227
|
const active = isPathActive(currentPath, item.path);
|
|
66072
66228
|
const { collapsed, expandSidebar } = useCollapsedSidebar();
|
|
66073
|
-
return (jsxRuntime.jsxs(SidebarMenuItem, { className: cn(active &&
|
|
66229
|
+
return (jsxRuntime.jsxs(SidebarMenuItem, { className: cn(active &&
|
|
66230
|
+
'flex items-center gap-1.5 group-data-[collapsible=icon]:block'), children: [active && jsxRuntime.jsx(ActiveRail, {}), jsxRuntime.jsxs(SidebarMenuButton, { isActive: active, tooltip: item.label, onClick: () => {
|
|
66074
66231
|
if (collapsed)
|
|
66075
66232
|
expandSidebar();
|
|
66076
66233
|
if (item.path)
|
|
@@ -66120,12 +66277,15 @@ function resolveBreadcrumb(pageMeta, leafOverride) {
|
|
|
66120
66277
|
return crumbs;
|
|
66121
66278
|
return crumbs.map((crumb, index) => index === crumbs.length - 1 ? { ...crumb, label: leafOverride } : crumb);
|
|
66122
66279
|
}
|
|
66123
|
-
const ShellHeader = ({ pageMeta, breadcrumbLeafOverride, user, profileMenuItems, onLogout, showThemeToggle, isDark, onToggleTheme, showNotifications, notificationsIndicator, onNotificationsClick, headerActionsSlot, }) => {
|
|
66280
|
+
const ShellHeader = ({ pageMeta, breadcrumbLeafOverride, user, profileMenuItems, onLogout, showThemeToggle, isDark, onToggleTheme, showNotifications, notificationsIndicator, onNotificationsClick, notificationsPanel, notificationsOpen, onNotificationsOpenChange, headerActionsSlot, }) => {
|
|
66124
66281
|
const { isMobile } = useSidebar();
|
|
66125
66282
|
const breadcrumb = resolveBreadcrumb(pageMeta, breadcrumbLeafOverride);
|
|
66126
|
-
return (jsxRuntime.jsxs(Navbar, { "aria-label": "Top navigation", children: [jsxRuntime.jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [isMobile && jsxRuntime.jsx(SidebarTrigger, {}), breadcrumb ? (jsxRuntime.jsx(NavbarTitle, { items: breadcrumb })) : (pageMeta?.title && jsxRuntime.jsx(NavbarTitle, { title: pageMeta.title }))] }), jsxRuntime.jsxs(NavbarActions, { gap: "lg", children: [headerActionsSlot, showThemeToggle && (jsxRuntime.jsx(NavbarIconButton, { "aria-label": isDark ? 'Switch to light theme' : 'Switch to dark theme', size: "md", onClick: onToggleTheme, children: isDark ? jsxRuntime.jsx(Sun, { size: 20 }) : jsxRuntime.jsx(Moon, { size: 20 }) })), showNotifications &&
|
|
66283
|
+
return (jsxRuntime.jsxs(Navbar, { "aria-label": "Top navigation", children: [jsxRuntime.jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [isMobile && jsxRuntime.jsx(SidebarTrigger, {}), breadcrumb ? (jsxRuntime.jsx(NavbarTitle, { items: breadcrumb })) : (pageMeta?.title && jsxRuntime.jsx(NavbarTitle, { title: pageMeta.title }))] }), jsxRuntime.jsxs(NavbarActions, { gap: "lg", children: [headerActionsSlot, showThemeToggle && (jsxRuntime.jsx(NavbarIconButton, { "aria-label": isDark ? 'Switch to light theme' : 'Switch to dark theme', size: "md", onClick: onToggleTheme, children: isDark ? jsxRuntime.jsx(Sun, { size: 20 }) : jsxRuntime.jsx(Moon, { size: 20 }) })), showNotifications &&
|
|
66284
|
+
(notificationsPanel ? (jsxRuntime.jsxs(Popover, { open: notificationsOpen, onOpenChange: onNotificationsOpenChange, children: [jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: jsxRuntime.jsx(NavbarIconButton, { "aria-label": "Notifications", size: "md", indicator: notificationsIndicator || undefined, onClick: onNotificationsClick, children: jsxRuntime.jsx(Bell, { size: 20 }) }) }), jsxRuntime.jsx(PopoverContent, { align: "end", sideOffset: 8, className: "w-[min(24rem,calc(100vw-2rem))] p-0", children: notificationsPanel })] })) : (jsxRuntime.jsx(NavbarIconButton, { "aria-label": "Notifications", size: "md", indicator: notificationsIndicator || undefined, onClick: onNotificationsClick, children: jsxRuntime.jsx(Bell, { size: 20 }) }))), jsxRuntime.jsx(ProfileMenu, { user: user, profileMenuItems: profileMenuItems, onLogout: onLogout })] })] }));
|
|
66127
66285
|
};
|
|
66128
|
-
const DashboardShell = ({ brand, navGroups, currentPath, onNavigate, pageMeta, breadcrumbLeafOverride, user, profileMenuItems, onLogout, showThemeToggle, isDark, onToggleTheme, showNotifications, notificationsIndicator, onNotificationsClick, headerActionsSlot, sidebarWidth, children, }) => (jsxRuntime.jsxs(SidebarProvider, { className: "h-screen min-h-0", style: {
|
|
66286
|
+
const DashboardShell = ({ brand, navGroups, currentPath, onNavigate, pageMeta, breadcrumbLeafOverride, user, profileMenuItems, onLogout, showThemeToggle, isDark, onToggleTheme, showNotifications, notificationsIndicator, onNotificationsClick, notificationsPanel, notificationsOpen, onNotificationsOpenChange, headerActionsSlot, sidebarWidth, children, }) => (jsxRuntime.jsxs(SidebarProvider, { className: "h-screen min-h-0", style: {
|
|
66287
|
+
'--sidebar-width': sidebarWidth ?? DEFAULT_SIDEBAR_WIDTH,
|
|
66288
|
+
}, children: [jsxRuntime.jsx(ShellSidebar, { brand: brand, navGroups: navGroups, currentPath: currentPath, onNavigate: onNavigate }), jsxRuntime.jsxs(SidebarInset, { className: "flex h-screen flex-col overflow-hidden bg-background", children: [jsxRuntime.jsx(ShellHeader, { pageMeta: pageMeta, breadcrumbLeafOverride: breadcrumbLeafOverride, user: user, profileMenuItems: profileMenuItems, onLogout: onLogout, showThemeToggle: showThemeToggle, isDark: isDark, onToggleTheme: onToggleTheme, showNotifications: showNotifications, notificationsIndicator: notificationsIndicator, onNotificationsClick: onNotificationsClick, notificationsPanel: notificationsPanel, notificationsOpen: notificationsOpen, onNotificationsOpenChange: onNotificationsOpenChange, headerActionsSlot: headerActionsSlot }), jsxRuntime.jsx("main", { className: "flex min-h-0 flex-1 flex-col overflow-y-auto", children: children })] })] }));
|
|
66129
66289
|
|
|
66130
66290
|
function CreateFormPageHeader({ title, onBack, backLabel = 'Go back', }) {
|
|
66131
66291
|
return (jsxRuntime.jsxs("div", { className: "mb-4 flex items-center justify-between gap-4", children: [jsxRuntime.jsx("h1", { className: "min-w-0 text-xl font-semibold leading-7 text-foreground", children: title }), jsxRuntime.jsx("div", { className: "flex shrink-0 items-center gap-2", children: jsxRuntime.jsxs(Button$1, { type: "button", variant: "ghost", onClick: onBack, className: "h-8 shrink-0 gap-1.5 rounded-lg px-3 py-1.5 text-sm font-medium text-secondary-foreground hover:text-secondary-foreground", children: [jsxRuntime.jsx(ChevronLeft, { className: "h-4 w-4", "aria-hidden": true }), backLabel] }) })] }));
|
|
@@ -68206,6 +68366,8 @@ exports.HistoryTimelineShimmer = HistoryTimelineShimmer;
|
|
|
68206
68366
|
exports.INVOICE_AUDIT_CONFIG = INVOICE_AUDIT_CONFIG;
|
|
68207
68367
|
exports.IconButton = IconButton;
|
|
68208
68368
|
exports.IdHyperlinkText = IdHyperlinkText;
|
|
68369
|
+
exports.InAppNotificationCard = InAppNotificationCard;
|
|
68370
|
+
exports.InAppNotificationsPanel = InAppNotificationsPanel;
|
|
68209
68371
|
exports.InfoBanner = InfoBanner;
|
|
68210
68372
|
exports.InventoryComboBox = InventoryComboBox;
|
|
68211
68373
|
exports.InventoryItemCard = InventoryItemCard;
|
|
@@ -68327,6 +68489,8 @@ exports.getBadgeTypeForStatus = getBadgeTypeForStatus;
|
|
|
68327
68489
|
exports.getChangePills = getChangePills;
|
|
68328
68490
|
exports.getInitials = getInitials;
|
|
68329
68491
|
exports.iconButtonVariants = iconButtonVariants;
|
|
68492
|
+
exports.inAppNotificationCardA11y = inAppNotificationCardA11y;
|
|
68493
|
+
exports.inAppNotificationsPanelA11y = inAppNotificationsPanelA11y;
|
|
68330
68494
|
exports.isPathActive = isPathActive;
|
|
68331
68495
|
exports.notify = notify;
|
|
68332
68496
|
exports.releaseStaleOverlayBodyLock = releaseStaleOverlayBodyLock;
|
|
@@ -68335,6 +68499,7 @@ exports.useAuditShowMore = useAuditShowMore;
|
|
|
68335
68499
|
exports.useBadge = useBadge;
|
|
68336
68500
|
exports.useButton = useButton;
|
|
68337
68501
|
exports.useDropdownMenu = useDropdownMenu;
|
|
68502
|
+
exports.useInAppNotificationsPanel = useInAppNotificationsPanel;
|
|
68338
68503
|
exports.useNavbar = useNavbar;
|
|
68339
68504
|
exports.useSidebar = useSidebar;
|
|
68340
68505
|
exports.videoThumbnailSrc = videoThumbnailSrc;
|