@airoom/nextmin-react 2.0.1 → 2.0.3

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.
Files changed (54) hide show
  1. package/README.md +57 -0
  2. package/dist/components/AddressAutocomplete.d.ts +4 -1
  3. package/dist/components/AddressAutocomplete.js +2 -2
  4. package/dist/components/AdminApp.js +86 -1
  5. package/dist/components/FileUploader.d.ts +3 -0
  6. package/dist/components/FileUploader.js +84 -28
  7. package/dist/components/PasswordInput.d.ts +2 -0
  8. package/dist/components/PasswordInput.js +2 -2
  9. package/dist/components/PhoneInput.d.ts +2 -0
  10. package/dist/components/PhoneInput.js +2 -2
  11. package/dist/components/QRCodeModal.d.ts +10 -0
  12. package/dist/components/QRCodeModal.js +153 -0
  13. package/dist/components/RefSelect.d.ts +4 -1
  14. package/dist/components/RefSelect.js +163 -65
  15. package/dist/components/SchemaForm.d.ts +2 -1
  16. package/dist/components/SchemaForm.js +132 -55
  17. package/dist/components/Sidebar.js +96 -7
  18. package/dist/components/TableFilters.js +1 -1
  19. package/dist/components/editor/TiptapEditor.js +46 -13
  20. package/dist/components/editor/Toolbar.js +195 -62
  21. package/dist/components/editor/components/CustomFontSizeModal.d.ts +13 -0
  22. package/dist/components/editor/components/CustomFontSizeModal.js +81 -0
  23. package/dist/components/editor/components/DistrictGridModal.js +1 -1
  24. package/dist/components/editor/components/SchemaInsertionModal.js +1 -1
  25. package/dist/components/editor/editor.css +2 -0
  26. package/dist/components/editor/extensions/FontSize.d.ts +19 -0
  27. package/dist/components/editor/extensions/FontSize.js +41 -0
  28. package/dist/components/viewer/DynamicViewer.js +50 -15
  29. package/dist/components/viewer/useViewDrawer.js +42 -2
  30. package/dist/hooks/useAppNavigation.d.ts +3 -0
  31. package/dist/hooks/useAppNavigation.js +37 -0
  32. package/dist/index.d.ts +2 -0
  33. package/dist/index.js +1 -0
  34. package/dist/lib/QueryBuilder.d.ts +3 -1
  35. package/dist/lib/QueryBuilder.js +10 -0
  36. package/dist/lib/api.d.ts +6 -0
  37. package/dist/lib/api.js +45 -24
  38. package/dist/lib/schemaUtils.js +1 -1
  39. package/dist/lib/types.d.ts +6 -1
  40. package/dist/nextmin.css +1 -1
  41. package/dist/providers/NextMinProvider.d.ts +5 -1
  42. package/dist/providers/NextMinProvider.js +12 -2
  43. package/dist/providers/themeResolver.d.ts +25 -0
  44. package/dist/providers/themeResolver.js +174 -0
  45. package/dist/router/NextMinRouter.d.ts +0 -1
  46. package/dist/router/NextMinRouter.js +0 -1
  47. package/dist/views/CreateEditPage.js +20 -11
  48. package/dist/views/DashboardPage.js +1 -1
  49. package/dist/views/ListPage.js +37 -6
  50. package/dist/views/SettingsEdit.js +1 -1
  51. package/dist/views/list/DataTableHero.d.ts +5 -5
  52. package/dist/views/list/DataTableHero.js +154 -41
  53. package/dist/views/list/ListHeader.js +9 -1
  54. package/package.json +5 -2
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { QRCodeModal } from '../../components/QRCodeModal';
3
4
  import React from 'react';
4
5
  import Link from 'next/link';
5
6
  import { Table, TableHeader, TableBody, TableColumn, TableRow, TableCell, Pagination, PaginationItem, Select, SelectItem, Skeleton, Button, Tooltip, Spinner, } from '@heroui/react';
@@ -9,6 +10,7 @@ import { formatCell } from './formatters';
9
10
  import { summarizeAny } from './jsonSummary';
10
11
  import { api } from '../../lib/api';
11
12
  import { ConfirmDialog } from '../../components/ConfirmDialog';
13
+ import { useAppNavigation } from '../../hooks/useAppNavigation';
12
14
  /** ---------- helpers: image detection/rendering ---------- */
13
15
  const IMG_EXT_RE = /\.(png|jpe?g|gif|webp|bmp|svg|avif|heic|heif)$/i;
14
16
  const IMG_COLUMN_RE = /(image|photo|avatar|logo|picture|thumbnail|icon)/i;
@@ -27,7 +29,13 @@ function isLikelyImageUrl(s, columnKey, attr) {
27
29
  const a = Array.isArray(attr) ? attr[0] : attr;
28
30
  const fmt = String(a?.format || '').toLowerCase();
29
31
  const type = String(a?.type || '').toLowerCase();
30
- if (fmt === 'file' || type === 'file' || fmt === 'image' || type === 'image')
32
+ const fieldType = String(a?.fieldType || a?.filedType || '').toLowerCase();
33
+ if (fmt === 'file' ||
34
+ type === 'file' ||
35
+ fmt === 'image' ||
36
+ type === 'image' ||
37
+ fieldType === 'file' ||
38
+ fieldType === 'image')
31
39
  return true;
32
40
  // 3) Extension check (permissive like DynamicViewer)
33
41
  const pure = s.split('?')[0].split('#')[0];
@@ -308,12 +316,94 @@ function formatDateLocal(d) {
308
316
  return d.toString();
309
317
  }
310
318
  }
319
+ function isSystemRow(item, columns) {
320
+ if (!item || typeof item !== 'object')
321
+ return false;
322
+ const typeCol = columns.find((c) => c.toLowerCase() === 'type');
323
+ const typeVal = typeCol ? item?.[typeCol] : undefined;
324
+ const typeString = typeof typeVal === 'string'
325
+ ? typeVal
326
+ : typeof typeVal?.value === 'string'
327
+ ? typeVal.value
328
+ : typeof typeVal?.name === 'string'
329
+ ? typeVal.name
330
+ : '';
331
+ return String(typeString ?? '').toLowerCase() === 'system';
332
+ }
311
333
  /** ---------- component ---------- */
312
- export function DataTableHero({ modelName, columns, rows, total, page, pageSize, onPageChange, onPageSizeChange, baseHref, loading, error, onDeleted, topContent, schema, onRequestView, }) {
334
+ export function DataTableHero({ modelName, columns, rows, total, page, pageSize, onPageChange, onPageSizeChange, baseHref, loading, error, onDeleted, topContent, schema, onRequestView, sort, sortType, onSortChange, }) {
335
+ const navigateTo = useAppNavigation();
313
336
  const pageCount = Math.max(1, Math.ceil(total / Math.max(1, pageSize)));
314
337
  const [pendingDeleteId, setPendingDeleteId] = React.useState(null);
315
338
  const [isDeleting, setIsDeleting] = React.useState(false);
316
339
  const [isDialogOpen, setIsDialogOpen] = React.useState(false);
340
+ const [qrModalState, setQrModalState] = React.useState(null);
341
+ const [selectedKeys, setSelectedKeys] = React.useState(new Set());
342
+ const [isBulkDeleting, setIsBulkDeleting] = React.useState(false);
343
+ const [isBulkDialogOpen, setIsBulkDialogOpen] = React.useState(false);
344
+ const canDelete = schema?.allowedMethods?.delete !== false;
345
+ const selectionMode = !loading && canDelete ? 'multiple' : undefined;
346
+ React.useEffect(() => {
347
+ setSelectedKeys(new Set());
348
+ }, [page, pageSize, modelName, rows]);
349
+ const selectedIds = React.useMemo(() => {
350
+ if (!canDelete || loading)
351
+ return [];
352
+ if (selectedKeys === 'all') {
353
+ return rows
354
+ .filter((r) => !isSystemRow(r, columns))
355
+ .map((r) => String(r.id || r._id));
356
+ }
357
+ if (selectedKeys instanceof Set) {
358
+ const keysArr = Array.from(selectedKeys);
359
+ return keysArr
360
+ .map((k) => String(k))
361
+ .filter((id) => {
362
+ const row = rows.find((r) => String(r.id || r._id) === id);
363
+ return row ? !isSystemRow(row, columns) : true;
364
+ });
365
+ }
366
+ return [];
367
+ }, [selectedKeys, rows, columns, canDelete, loading]);
368
+ const disabledKeys = React.useMemo(() => {
369
+ const disabled = new Set();
370
+ rows.forEach((r) => {
371
+ if (isSystemRow(r, columns)) {
372
+ disabled.add(String(r.id || r._id));
373
+ }
374
+ });
375
+ return disabled;
376
+ }, [rows, columns]);
377
+ const handleConfirmBulkDelete = async () => {
378
+ if (selectedIds.length === 0) {
379
+ setIsBulkDialogOpen(false);
380
+ return;
381
+ }
382
+ try {
383
+ setIsBulkDeleting(true);
384
+ await Promise.all(selectedIds.map((id) => api.remove(modelName, id)));
385
+ setSelectedKeys(new Set());
386
+ onDeleted?.();
387
+ }
388
+ catch (err) {
389
+ console.error('Bulk delete failed:', err);
390
+ }
391
+ finally {
392
+ setIsBulkDeleting(false);
393
+ setIsBulkDialogOpen(false);
394
+ }
395
+ };
396
+ const combinedTopContent = React.useMemo(() => {
397
+ return (_jsxs("div", { className: "flex flex-col gap-3", children: [topContent, selectedIds.length > 0 && (_jsxs("div", { className: "flex items-center justify-between px-4 py-2.5 rounded-medium bg-danger-50 dark:bg-danger-900/20 border border-danger-200 dark:border-danger-800", children: [_jsxs("div", { className: "flex items-center gap-2 text-sm font-medium text-danger-700 dark:text-danger-300", children: [_jsx(LucideIcons.CheckSquare, { size: 18 }), _jsxs("span", { children: [selectedIds.length, " ", selectedIds.length === 1 ? 'item' : 'items', " selected"] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { size: "sm", variant: "flat", color: "default", onPress: () => setSelectedKeys(new Set()), children: "Cancel" }), _jsxs(Button, { size: "sm", color: "danger", variant: "solid", startContent: !isBulkDeleting ? _jsx(LucideIcons.Trash2, { size: 16 }) : undefined, isLoading: isBulkDeleting, onPress: () => setIsBulkDialogOpen(true), children: ["Delete Selected (", selectedIds.length, ")"] })] })] }))] }));
398
+ }, [topContent, selectedIds.length, isBulkDeleting]);
399
+ const sortDescriptor = React.useMemo(() => {
400
+ if (!sort)
401
+ return undefined;
402
+ return {
403
+ column: sort,
404
+ direction: sortType === 'asc' ? 'ascending' : 'descending',
405
+ };
406
+ }, [sort, sortType]);
317
407
  const headerItems = React.useMemo(() => [
318
408
  ...columns.map((c) => ({ key: c, label: c })),
319
409
  { key: 'actions', label: 'Actions' },
@@ -342,72 +432,95 @@ export function DataTableHero({ modelName, columns, rows, total, page, pageSize,
342
432
  setIsDialogOpen(false);
343
433
  }
344
434
  };
345
- return (_jsxs("div", { className: "flex-grow overflow-hidden flex flex-col min-h-0", children: [_jsxs(Table, { "aria-label": "Model table", isHeaderSticky: true, removeWrapper: false, classNames: {
435
+ return (_jsxs("div", { className: "flex-grow overflow-hidden flex flex-col min-h-0", children: [_jsxs(Table, { "aria-label": "Model table", isHeaderSticky: true, removeWrapper: false, selectionMode: selectionMode, selectedKeys: selectedKeys, onSelectionChange: setSelectedKeys, disabledKeys: disabledKeys, sortDescriptor: sortDescriptor, onSortChange: (descriptor) => {
436
+ if (onSortChange && descriptor.column) {
437
+ const dir = descriptor.direction === 'ascending' ? 'asc' : 'desc';
438
+ onSortChange(String(descriptor.column), dir);
439
+ }
440
+ }, classNames: {
346
441
  base: 'max-h-full flex flex-col min-h-0 overflow-visible',
347
442
  wrapper: 'flex-grow min-h-0 overflow-auto !p-0 !bg-transparent !shadow-none !rounded-none relative',
348
443
  table: 'min-w-full', // Allow auto-sizing to prevent clipping
349
444
  thead: '[&>tr]:first:rounded-none',
350
- th: 'bg-default-100 text-default-600',
445
+ th: 'bg-default-100 text-default-600 [&[data-selection-cell=true]]:w-12 [&[data-selection-cell=true]]:min-w-[48px] [&[data-selection-cell=true]]:max-w-[48px]',
446
+ td: '[&[data-selection-cell=true]]:w-12 [&[data-selection-cell=true]]:min-w-[48px] [&[data-selection-cell=true]]:max-w-[48px]',
351
447
  }, bottomContent: _jsxs("div", { className: "flex items-center justify-between px-2 py-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-sm text-foreground/70", children: "Rows per page:" }), _jsx(Select, { size: "sm", "aria-label": "Rows per page", selectedKeys: new Set([String(pageSize)]), onSelectionChange: (keys) => {
352
448
  const val = Array.from(keys)[0];
353
449
  if (val)
354
450
  onPageSizeChange(Number(val));
355
- }, className: "w-24", children: [10, 20, 50, 100].map((n) => (_jsx(SelectItem, { textValue: String(n), children: n }, String(n)))) })] }), _jsx(Pagination, { size: "sm", total: pageCount, page: page, onChange: onPageChange, showControls: true, variant: "flat", renderItem: (props) => {
356
- if (props.value === 'prev') {
357
- return (_jsx(Button, { isIconOnly: true, size: "sm", variant: "flat", onPress: props.onPress, isDisabled: props.isDisabled, children: _jsx(ChevronLeft, { size: 16 }) }));
451
+ }, className: "w-24", children: [10, 20, 50, 100].map((n) => (_jsx(SelectItem, { textValue: String(n), children: n }, String(n)))) })] }), _jsx(Pagination, { size: "sm", total: pageCount, page: page, onChange: onPageChange, showControls: true, variant: "flat", renderItem: ({ key, ...itemProps }) => {
452
+ if (itemProps.value === 'prev') {
453
+ return (_jsx(Button, { isIconOnly: true, size: "sm", variant: "flat", onPress: itemProps.onPress, isDisabled: itemProps.isDisabled, children: _jsx(ChevronLeft, { size: 16 }) }, key));
358
454
  }
359
- if (props.value === 'next') {
360
- return (_jsx(Button, { isIconOnly: true, size: "sm", variant: "flat", onPress: props.onPress, isDisabled: props.isDisabled, children: _jsx(ChevronRight, { size: 16 }) }));
455
+ if (itemProps.value === 'next') {
456
+ return (_jsx(Button, { isIconOnly: true, size: "sm", variant: "flat", onPress: itemProps.onPress, isDisabled: itemProps.isDisabled, children: _jsx(ChevronRight, { size: 16 }) }, key));
361
457
  }
362
- return _jsx(PaginationItem, { ...props });
363
- } })] }), bottomContentPlacement: "outside", topContent: topContent, topContentPlacement: "outside", children: [_jsx(TableHeader, { columns: headerItems, children: (col) => (_jsx(TableColumn, { className: `text-xs uppercase tracking-wide ${col.key === 'actions'
458
+ return _jsx(PaginationItem, { ...itemProps }, key);
459
+ } })] }), bottomContentPlacement: "outside", topContent: combinedTopContent, topContentPlacement: "outside", children: [_jsx(TableHeader, { columns: headerItems, children: (col) => (_jsx(TableColumn, { allowsSorting: col.key !== 'actions', className: `text-xs uppercase tracking-wide ${col.key === 'actions'
364
460
  ? 'sticky right-0 bg-default-100 z-30 shadow-[-1px_0_2px_rgba(0,0,0,0.05)] border-l border-default-200 text-center'
365
461
  : ''}`, width: col.key === 'actions' ? 130 : 200, children: col.label }, col.key)) }), _jsx(TableBody, { items: bodyItems, emptyContent: loading ? undefined : error || 'No data', children: (item) => (_jsx(TableRow, { children: (columnKey) => {
366
462
  const key = String(columnKey);
463
+ const stopRowSelectionProps = {
464
+ onPointerDown: (e) => e.stopPropagation(),
465
+ onMouseDown: (e) => e.stopPropagation(),
466
+ onClick: (e) => e.stopPropagation(),
467
+ };
367
468
  if (loading) {
368
- return (_jsx(TableCell, { children: _jsx(Skeleton, { className: "h-3 w-3/4 rounded-md" }) }, `${item.id}-${key}`));
469
+ return (_jsx(TableCell, { ...stopRowSelectionProps, children: _jsx(Skeleton, { className: "h-3 w-3/4 rounded-md" }) }, `${item.id}-${key}`));
369
470
  }
370
471
  if (key === 'actions') {
371
- const typeCol = columns.find((c) => c.toLowerCase() === 'type');
372
- const typeVal = typeCol ? item?.[typeCol] : undefined;
373
- const typeString = typeof typeVal === 'string'
374
- ? typeVal
375
- : typeof typeVal?.value === 'string'
376
- ? typeVal.value
377
- : typeof typeVal?.name === 'string'
378
- ? typeVal.name
379
- : '';
380
- const isSystemRow = String(typeString ?? '').toLowerCase() === 'system';
381
- return (_jsx(TableCell, { className: "sticky right-0 !bg-background dark:!bg-content1 z-20 shadow-[-1px_0_2px_rgba(0,0,0,0.05)] border-l border-default-200", children: _jsxs("div", { className: "flex items-center justify-center gap-1", children: [schema?.actions?.map((action, idx) => {
382
- let href = action.href;
472
+ const isSystemRowItem = isSystemRow(item, columns);
473
+ return (_jsx(TableCell, { ...stopRowSelectionProps, className: "sticky right-0 !bg-background dark:!bg-content1 z-20 shadow-[-1px_0_2px_rgba(0,0,0,0.05)] border-l border-default-200", children: _jsxs("div", { className: "flex items-center justify-center gap-1", children: [schema?.actions?.map((action, idx) => {
474
+ const isQr = action?.type === 'qr' ||
475
+ action?.actionType === 'qr' ||
476
+ action.icon === 'QrCode' ||
477
+ action.label?.toLowerCase() === 'qr code';
478
+ let href = action.href || '';
383
479
  href = href.replace('{id}', String(item.id || item._id || ''));
384
480
  const relatedId = item._actionsData?.[action.relatedModel || ''];
385
481
  if (relatedId) {
386
482
  href = href.replace('{relatedId}', relatedId);
387
483
  }
388
- // Condition: if {relatedId} placeholder is used but NO relatedId found, hide it
389
- if (action.href.includes('{relatedId}') &&
484
+ if (!isQr &&
485
+ action.href &&
486
+ action.href.includes('{relatedId}') &&
390
487
  !relatedId) {
391
488
  return null;
392
489
  }
393
- // Or if strictly specified relatedModel but no data
394
- if (action.relatedModel && !relatedId) {
490
+ if (!isQr && action.relatedModel && !relatedId) {
395
491
  return null;
396
492
  }
397
493
  const IconComponent = action.icon
398
494
  ? LucideIcons[action.icon]
399
495
  : null;
400
- return (_jsx(Tooltip, { content: action.label, children: _jsx(Button, { as: Link, href: href, size: "sm", variant: action.variant || 'light', color: action.color || 'default', isIconOnly: !!IconComponent, className: IconComponent ? '' : 'min-w-fit h-8 px-3', children: IconComponent ? (_jsx(IconComponent, { size: 18 })) : (action.label) }) }, `custom-action-${idx}`));
401
- }), _jsx(Tooltip, { content: "View", children: _jsx(Button, { isIconOnly: true, size: "sm", variant: "light", "aria-label": "View", onPress: () => onRequestView?.(item), children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", focusable: "false", children: [_jsx("path", { d: "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" }), _jsx("circle", { cx: "12", cy: "12", r: "3" })] }) }) }), _jsx(Tooltip, { content: isSystemRow
496
+ return (_jsx(Tooltip, { content: action.label, children: _jsx(Button, { as: isQr ? undefined : Link, href: isQr ? undefined : href, onPress: () => {
497
+ if (isQr) {
498
+ setQrModalState({ item, action });
499
+ }
500
+ else if (href) {
501
+ navigateTo(href);
502
+ }
503
+ }, size: "sm", variant: action.variant || 'light', color: action.color || 'default', isIconOnly: !!IconComponent, className: IconComponent ? '' : 'min-w-fit h-8 px-3', children: IconComponent ? (_jsx(IconComponent, { size: 18 })) : (action.label) }) }, `custom-action-${idx}`));
504
+ }), _jsx(Tooltip, { content: "View", children: _jsx(Button, { isIconOnly: true, size: "sm", variant: "light", "aria-label": "View", onPress: () => onRequestView?.(item), children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", focusable: "false", children: [_jsx("path", { d: "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" }), _jsx("circle", { cx: "12", cy: "12", r: "3" })] }) }) }), schema?.allowedMethods?.update !== false && (_jsx(Tooltip, { content: isSystemRowItem
402
505
  ? 'System record — editing disabled'
403
- : 'Edit', children: _jsx(Button, { as: isSystemRow ? undefined : Link, href: isSystemRow ? undefined : `${baseHref}/${item.id}`, isIconOnly: true, size: "sm", variant: "light", isDisabled: isSystemRow, "aria-disabled": isSystemRow, children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", focusable: "false", children: [_jsx("path", { d: "M12 20h9" }), _jsx("path", { d: "M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5z" })] }) }) }), _jsx(Tooltip, { content: isSystemRow
506
+ : 'Edit', children: isSystemRowItem ? (_jsx("span", { className: "inline-flex items-center justify-center min-w-8 w-8 h-8 rounded-small text-default-400 opacity-50 cursor-not-allowed", "aria-disabled": "true", children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", focusable: "false", children: [_jsx("path", { d: "M12 20h9" }), _jsx("path", { d: "M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5z" })] }) })) : (_jsx(Link, { href: `${baseHref}/${item.id ?? item._id}`, onClick: (e) => {
507
+ if (e.metaKey ||
508
+ e.ctrlKey ||
509
+ e.shiftKey ||
510
+ e.altKey ||
511
+ e.button !== 0) {
512
+ return;
513
+ }
514
+ e.preventDefault();
515
+ navigateTo(`${baseHref}/${item.id ?? item._id}`);
516
+ }, className: "inline-flex items-center justify-center min-w-8 w-8 h-8 rounded-small text-default-foreground hover:bg-default/40 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-focus", "aria-label": "Edit", children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", focusable: "false", children: [_jsx("path", { d: "M12 20h9" }), _jsx("path", { d: "M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5z" })] }) })) })), schema?.allowedMethods?.delete !== false && (_jsx(Tooltip, { content: isSystemRowItem
404
517
  ? 'System record — deletion disabled'
405
- : 'Delete', children: _jsx(Button, { isIconOnly: true, size: "sm", variant: "light", color: "danger", isDisabled: isSystemRow, "aria-disabled": isSystemRow, onPress: () => {
406
- if (!isSystemRow)
518
+ : 'Delete', children: _jsx(Button, { isIconOnly: true, size: "sm", variant: "light", color: "danger", isDisabled: isSystemRowItem, "aria-disabled": isSystemRowItem, onPress: () => {
519
+ if (!isSystemRowItem)
407
520
  requestDelete(item.id);
408
521
  }, children: isDeleting && pendingDeleteId === item.id ? (_jsx(Spinner, { size: "sm" })) : (
409
522
  // Trash icon
410
- _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", focusable: "false", children: [_jsx("polyline", { points: "3 6 5 6 21 6" }), _jsx("path", { d: "M19 6l-1 14H6L5 6" }), _jsx("path", { d: "M10 11v6" }), _jsx("path", { d: "M14 11v6" })] })) }) })] }) }, `${item.id}-actions`));
523
+ _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", focusable: "false", children: [_jsx("polyline", { points: "3 6 5 6 21 6" }), _jsx("path", { d: "M19 6l-1 14H6L5 6" }), _jsx("path", { d: "M10 11v6" }), _jsx("path", { d: "M14 11v6" })] })) }) }))] }) }, `${item.id}-actions`));
411
524
  }
412
525
  // Prepare raw value (parse JSON-like strings first)
413
526
  let rawRawVal = item[key];
@@ -419,31 +532,31 @@ export function DataTableHero({ modelName, columns, rows, total, page, pageSize,
419
532
  const attributes = (schema?.attributes ?? {});
420
533
  const maybeImg = maybeRenderImageCell(rawVal, key, attributes[key]);
421
534
  if (maybeImg) {
422
- return (_jsx(TableCell, { children: maybeImg }, `${item.id ?? item._id}-${key}`));
535
+ return (_jsx(TableCell, { ...stopRowSelectionProps, children: maybeImg }, `${item.id ?? item._id}-${key}`));
423
536
  }
424
537
  // 0) Date fields (createdAt, updatedAt, deletedAt)
425
538
  if (isDateField(key)) {
426
539
  const d = tryParseDate(rawVal);
427
540
  if (d) {
428
- return (_jsx(TableCell, { children: formatDateLocal(d) }, `${item.id ?? item._id}-${key}`));
541
+ return (_jsx(TableCell, { ...stopRowSelectionProps, children: formatDateLocal(d) }, `${item.id ?? item._id}-${key}`));
429
542
  }
430
543
  }
431
544
  // ... the rest of the formatters ...
432
545
  // 1) Time-only ranges like "15:00..00:00 17:00..23:00"
433
546
  const timeRangePretty = formatTimeOnlyRanges(rawVal);
434
547
  if (timeRangePretty) {
435
- return (_jsx(TableCell, { children: timeRangePretty }, `${item.id ?? item._id}-${key}`));
548
+ return (_jsx(TableCell, { ...stopRowSelectionProps, children: timeRangePretty }, `${item.id ?? item._id}-${key}`));
436
549
  }
437
550
  // 2) Object/array with "show"/"showKey" or common label fields
438
551
  const showPretty = formatShowValue(rawVal, attributes[key]);
439
552
  if (showPretty) {
440
- return (_jsx(TableCell, { children: truncateText(showPretty, 35) }, `${item.id ?? item._id}-${key}`));
553
+ return (_jsx(TableCell, { ...stopRowSelectionProps, children: truncateText(showPretty, 35) }, `${item.id ?? item._id}-${key}`));
441
554
  }
442
555
  // 3) Generic JSON/array/object summarizer (supports raw JSON string too)
443
556
  const showKey = (Array.isArray(attributes[key]) ? attributes[key][0] : attributes[key])?.show;
444
557
  const jsonSummary = summarizeAny(rawVal, showKey);
445
558
  if (typeof jsonSummary === 'string' && jsonSummary.length) {
446
- return (_jsx(TableCell, { children: truncateText(jsonSummary, 35) }, `${item.id ?? item._id}-${key}`));
559
+ return (_jsx(TableCell, { ...stopRowSelectionProps, children: truncateText(jsonSummary, 35) }, `${item.id ?? item._id}-${key}`));
447
560
  }
448
561
  // 4) Existing formatter pipeline
449
562
  const formatted = formatCell(rawVal, key);
@@ -452,6 +565,6 @@ export function DataTableHero({ modelName, columns, rows, total, page, pageSize,
452
565
  const displayValue = isSimple
453
566
  ? truncateText(String(formatted), 35)
454
567
  : formatted;
455
- return (_jsx(TableCell, { children: displayValue }, `${item.id ?? item._id}-${key}`));
456
- } }, item.id)) })] }), _jsx(ConfirmDialog, { isOpen: isDialogOpen, onClose: () => setIsDialogOpen(false), onConfirm: handleConfirmDelete, isLoading: isDeleting, title: "Delete this record?", description: "This action cannot be undone. The record will be permanently removed.", confirmText: "Delete", cancelText: "Cancel" })] }));
568
+ return (_jsx(TableCell, { ...stopRowSelectionProps, children: displayValue }, `${item.id ?? item._id}-${key}`));
569
+ } }, item.id)) })] }), _jsx(ConfirmDialog, { isOpen: isDialogOpen, onClose: () => setIsDialogOpen(false), onConfirm: handleConfirmDelete, isLoading: isDeleting, title: "Delete this record?", description: "This action cannot be undone. The record will be permanently removed.", confirmText: "Delete", cancelText: "Cancel" }), _jsx(QRCodeModal, { isOpen: !!qrModalState, onClose: () => setQrModalState(null), item: qrModalState?.item, action: qrModalState?.action, modelName: modelName }), _jsx(ConfirmDialog, { isOpen: isBulkDialogOpen, onClose: () => setIsBulkDialogOpen(false), onConfirm: handleConfirmBulkDelete, isLoading: isBulkDeleting, title: `Delete ${selectedIds.length} selected ${selectedIds.length === 1 ? 'record' : 'records'}?`, description: `Are you sure you want to delete ${selectedIds.length} selected ${selectedIds.length === 1 ? 'record' : 'records'}? This action cannot be undone.`, confirmText: `Delete ${selectedIds.length} ${selectedIds.length === 1 ? 'record' : 'records'}`, cancelText: "Cancel" })] }));
457
570
  }
@@ -2,6 +2,14 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import Link from 'next/link';
4
4
  import { Button, Divider } from '@heroui/react';
5
+ import { useAppNavigation } from '../../hooks/useAppNavigation';
5
6
  export function ListHeader({ title, createHref, hideCreate = false, loading, children, }) {
6
- return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between", children: [_jsx("h2", { className: "m-0 text-xl font-semibold", children: title }), _jsxs("div", { className: "flex items-center gap-3", children: [!hideCreate && createHref ? (_jsx(Link, { href: createHref, children: _jsx(Button, { size: "sm", color: "primary", isDisabled: loading, children: "Create" }) })) : null, children] })] }), _jsx(Divider, { className: "my-3" })] }));
7
+ const navigateTo = useAppNavigation();
8
+ const handleCreate = (e) => {
9
+ if (!createHref)
10
+ return;
11
+ e.preventDefault();
12
+ navigateTo(createHref);
13
+ };
14
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between", children: [_jsx("h2", { className: "m-0 text-xl font-semibold", children: title }), _jsxs("div", { className: "flex items-center gap-3", children: [!hideCreate && createHref ? (_jsx(Link, { href: createHref, onClick: handleCreate, children: _jsx(Button, { size: "sm", color: "primary", isDisabled: loading, onPress: () => navigateTo(createHref), children: "Create" }) })) : null, children] })] }), _jsx(Divider, { className: "my-3" })] }));
7
15
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@airoom/nextmin-react",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -40,6 +40,7 @@
40
40
  "@tailwindcss/typography": "^0.5.19",
41
41
  "@tiptap/extension-bubble-menu": "^3.13.0",
42
42
  "@tiptap/extension-color": "^3.13.0",
43
+ "@tiptap/extension-font-family": "^3.23.4",
43
44
  "@tiptap/extension-highlight": "^3.13.0",
44
45
  "@tiptap/extension-image": "^3.13.0",
45
46
  "@tiptap/extension-link": "^3.13.0",
@@ -55,11 +56,13 @@
55
56
  "@tiptap/react": "^3.13.0",
56
57
  "@tiptap/starter-kit": "^3.13.0",
57
58
  "@tiptap/suggestion": "^3.13.0",
59
+ "@types/qrcode": "^1.5.6",
58
60
  "@types/react-resizable": "^3.0.8",
59
61
  "clsx": "^2.1.1",
60
62
  "dompurify": "^3.3.0",
61
63
  "framer-motion": "^12.23.12",
62
64
  "lucide-react": "^0.556.0",
65
+ "qrcode": "^1.5.4",
63
66
  "react": "^19.2.1",
64
67
  "react-dom": "^19.2.1",
65
68
  "react-redux": ">=9",
@@ -78,4 +81,4 @@
78
81
  "publishConfig": {
79
82
  "access": "public"
80
83
  }
81
- }
84
+ }