@juv/codego-react-ui 3.4.7 → 3.4.8

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/index.js CHANGED
@@ -5783,13 +5783,79 @@ function FileUpload({
5783
5783
  }
5784
5784
 
5785
5785
  // src/components/ui/repeater.tsx
5786
- import { Plus as Plus3, Trash2, GripVertical as GripVertical3 } from "lucide-react";
5786
+ import { Plus as Plus3, Trash2, GripVertical as GripVertical3, Paperclip } from "lucide-react";
5787
5787
  import { jsx as jsx29, jsxs as jsxs27 } from "react/jsx-runtime";
5788
+ function RepeaterFieldRenderer({
5789
+ field,
5790
+ value,
5791
+ onChange
5792
+ }) {
5793
+ if (field.type === "image") {
5794
+ return /* @__PURE__ */ jsxs27("div", { className: "flex flex-col gap-1.5", children: [
5795
+ field.label && /* @__PURE__ */ jsx29("span", { className: "text-xs font-medium text-muted-foreground", children: field.label }),
5796
+ /* @__PURE__ */ jsxs27("div", { className: "flex items-center gap-3", children: [
5797
+ value && /* @__PURE__ */ jsx29("img", { src: value, alt: field.key, className: "h-10 w-10 rounded-lg object-cover ring-1 ring-border shrink-0" }),
5798
+ /* @__PURE__ */ jsx29(
5799
+ Input,
5800
+ {
5801
+ inputMode: "text",
5802
+ value: value ?? "",
5803
+ onChange: (e) => onChange(e.target.value),
5804
+ placeholder: field.placeholder ?? "Image URL"
5805
+ }
5806
+ )
5807
+ ] })
5808
+ ] });
5809
+ }
5810
+ if (field.type === "attachment") {
5811
+ return /* @__PURE__ */ jsxs27("div", { className: "flex flex-col gap-1.5", children: [
5812
+ field.label && /* @__PURE__ */ jsx29("span", { className: "text-xs font-medium text-muted-foreground", children: field.label }),
5813
+ /* @__PURE__ */ jsxs27("div", { className: "flex items-center gap-2", children: [
5814
+ value && /* @__PURE__ */ jsxs27(
5815
+ "a",
5816
+ {
5817
+ href: value,
5818
+ target: "_blank",
5819
+ rel: "noopener noreferrer",
5820
+ className: "inline-flex items-center gap-1.5 rounded-lg border border-border bg-muted/50 px-2.5 py-1 text-xs font-medium text-foreground hover:bg-muted transition-colors shrink-0",
5821
+ children: [
5822
+ /* @__PURE__ */ jsx29(Paperclip, { className: "h-3 w-3" }),
5823
+ String(value).split("/").pop()
5824
+ ]
5825
+ }
5826
+ ),
5827
+ /* @__PURE__ */ jsx29(
5828
+ Input,
5829
+ {
5830
+ inputMode: "text",
5831
+ value: value ?? "",
5832
+ onChange: (e) => onChange(e.target.value),
5833
+ placeholder: field.placeholder ?? "Attachment URL"
5834
+ }
5835
+ )
5836
+ ] })
5837
+ ] });
5838
+ }
5839
+ return /* @__PURE__ */ jsxs27("div", { className: "flex flex-col gap-1.5", children: [
5840
+ field.label && /* @__PURE__ */ jsx29("span", { className: "text-xs font-medium text-muted-foreground", children: field.label }),
5841
+ /* @__PURE__ */ jsx29(
5842
+ Input,
5843
+ {
5844
+ inputMode: "text",
5845
+ value: value ?? "",
5846
+ onChange: (e) => onChange(e.target.value),
5847
+ placeholder: field.placeholder ?? field.key
5848
+ }
5849
+ )
5850
+ ] });
5851
+ }
5788
5852
  function Repeater({
5789
5853
  items,
5790
5854
  onAdd,
5791
5855
  onRemove,
5792
5856
  renderItem,
5857
+ fields,
5858
+ onFieldChange,
5793
5859
  addButtonText = "Add Item",
5794
5860
  className
5795
5861
  }) {
@@ -5802,7 +5868,15 @@ function Repeater({
5802
5868
  children: [
5803
5869
  /* @__PURE__ */ jsx29("div", { className: "mt-1 cursor-grab text-muted-foreground/30 group-hover:text-muted-foreground/60 transition-colors shrink-0", children: /* @__PURE__ */ jsx29(GripVertical3, { className: "h-4 w-4" }) }),
5804
5870
  /* @__PURE__ */ jsx29("div", { className: "mt-1 flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-primary/10 text-[10px] font-semibold text-primary", children: index + 1 }),
5805
- /* @__PURE__ */ jsx29("div", { className: "flex-1 min-w-0", children: renderItem(item, index) }),
5871
+ /* @__PURE__ */ jsx29("div", { className: "flex-1 min-w-0", children: fields ? /* @__PURE__ */ jsx29("div", { className: "grid gap-3", style: { gridTemplateColumns: fields.length > 1 ? `repeat(${Math.min(fields.length, 3)}, minmax(0, 1fr))` : "1fr" }, children: fields.map((f) => /* @__PURE__ */ jsx29(
5872
+ RepeaterFieldRenderer,
5873
+ {
5874
+ field: f,
5875
+ value: item[f.key],
5876
+ onChange: (v) => onFieldChange?.(index, f.key, v)
5877
+ },
5878
+ f.key
5879
+ )) }) : renderItem ? renderItem(item, index) : null }),
5806
5880
  /* @__PURE__ */ jsxs27(
5807
5881
  Button,
5808
5882
  {
@@ -6284,7 +6358,7 @@ import * as React28 from "react";
6284
6358
  import { createPortal as createPortal3 } from "react-dom";
6285
6359
  import axios3 from "axios";
6286
6360
  import { ChevronLeft as ChevronLeft6, ChevronRight as ChevronRight8, Search as Search5, Trash2 as Trash22, ChevronsUpDown, ChevronUp, ChevronDown as ChevronDown4, X as X9, Eye as Eye2, Pencil as Pencil2, Trash as Trash3, Loader2 as Loader22 } from "lucide-react";
6287
- import { Fragment as Fragment11, jsx as jsx32, jsxs as jsxs30 } from "react/jsx-runtime";
6361
+ import { Fragment as Fragment12, jsx as jsx32, jsxs as jsxs30 } from "react/jsx-runtime";
6288
6362
  function useServerTable({ url, params, encrypt, key, decryptPayloadLog, columnOverrides, debounce = 300, transform, manual = false, refresh: refreshEnabled = false, refreshInterval = 0, hardReload, onSuccess, onError }) {
6289
6363
  const [data, setData] = React28.useState([]);
6290
6364
  const [columns, setColumns] = React28.useState([]);
@@ -6375,8 +6449,14 @@ function useServerTable({ url, params, encrypt, key, decryptPayloadLog, columnOv
6375
6449
  goToPage: (page) => setCurrentPage(page),
6376
6450
  reload: () => setTick((t) => t + 1),
6377
6451
  refresh: () => setTick((t) => t + 1),
6452
+ // Passthrough props
6378
6453
  searchValue,
6379
- onSearchChange: handleSearchChange
6454
+ onSearchChange: handleSearchChange,
6455
+ page: currentPage,
6456
+ onPageChange: (page) => setCurrentPage(page),
6457
+ sort: [],
6458
+ onSortChange: () => {
6459
+ }
6380
6460
  };
6381
6461
  }
6382
6462
  var MODAL_WIDTH = {
@@ -6434,8 +6514,8 @@ function validateField(field, value) {
6434
6514
  return null;
6435
6515
  }
6436
6516
  function FieldRenderer({ field, value, onChange }) {
6437
- if (field.component) return /* @__PURE__ */ jsx32(Fragment11, { children: field.component });
6438
- if (field.render) return /* @__PURE__ */ jsx32(Fragment11, { children: field.render(value, onChange) });
6517
+ if (field.component) return /* @__PURE__ */ jsx32(Fragment12, { children: field.component });
6518
+ if (field.render) return /* @__PURE__ */ jsx32(Fragment12, { children: field.render(value, onChange) });
6439
6519
  const toLabelValue = (o) => {
6440
6520
  if (typeof o === "string") return { label: o, value: o };
6441
6521
  if (Array.isArray(o)) return { label: o[0], value: o[1] };
@@ -6532,6 +6612,28 @@ function FieldRenderer({ field, value, onChange }) {
6532
6612
  } });
6533
6613
  case "repeater": {
6534
6614
  const items = Array.isArray(value) ? value : [];
6615
+ if (field.repeaterFields) {
6616
+ const rows = Array.isArray(value) ? value : [];
6617
+ return /* @__PURE__ */ jsx32(
6618
+ Repeater,
6619
+ {
6620
+ items: rows,
6621
+ fields: field.repeaterFields,
6622
+ onAdd: () => {
6623
+ const blank = {};
6624
+ field.repeaterFields.forEach((f) => {
6625
+ blank[f.key] = "";
6626
+ });
6627
+ onChange([...rows, blank]);
6628
+ },
6629
+ onRemove: (i) => onChange(rows.filter((_, idx) => idx !== i)),
6630
+ onFieldChange: (i, key, val) => {
6631
+ const next = rows.map((r, idx) => idx === i ? { ...r, [key]: val } : r);
6632
+ onChange(next);
6633
+ }
6634
+ }
6635
+ );
6636
+ }
6535
6637
  return /* @__PURE__ */ jsx32(
6536
6638
  Repeater,
6537
6639
  {
@@ -6583,8 +6685,8 @@ function ViewModal({
6583
6685
  const vt = f.viewType ?? f.type;
6584
6686
  const empty = value === null || value === void 0 || value === "";
6585
6687
  const dash = /* @__PURE__ */ jsx32("span", { className: "text-muted-foreground italic text-sm", children: "\u2014" });
6586
- if (f.component) return /* @__PURE__ */ jsx32(Fragment11, { children: f.component });
6587
- if (f.render) return /* @__PURE__ */ jsx32(Fragment11, { children: f.render(value, () => {
6688
+ if (f.component) return /* @__PURE__ */ jsx32(Fragment12, { children: f.component });
6689
+ if (f.render) return /* @__PURE__ */ jsx32(Fragment12, { children: f.render(value, () => {
6588
6690
  }) });
6589
6691
  switch (vt) {
6590
6692
  case "image":
@@ -6658,6 +6760,47 @@ function ViewModal({
6658
6760
  ]
6659
6761
  }
6660
6762
  );
6763
+ case "repeater": {
6764
+ const rows = Array.isArray(value) ? value : [];
6765
+ if (!rows.length) return dash;
6766
+ const rFields = f.repeaterFields;
6767
+ return /* @__PURE__ */ jsx32("div", { className: "space-y-2", children: rows.map((row, ri) => /* @__PURE__ */ jsxs30("div", { className: "flex flex-wrap gap-3 rounded-xl border border-border bg-muted/30 px-3 py-2", children: [
6768
+ /* @__PURE__ */ jsx32("span", { className: "flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-primary/10 text-[10px] font-semibold text-primary", children: ri + 1 }),
6769
+ rFields ? rFields.map((rf) => {
6770
+ const v = row[rf.key];
6771
+ if (rf.type === "image") return /* @__PURE__ */ jsxs30("div", { className: "flex flex-col gap-1", children: [
6772
+ rf.label && /* @__PURE__ */ jsx32("span", { className: "text-[10px] text-muted-foreground", children: rf.label }),
6773
+ v ? /* @__PURE__ */ jsx32("img", { src: v, alt: rf.key, className: "h-10 w-10 rounded-lg object-cover ring-1 ring-border" }) : dash
6774
+ ] }, rf.key);
6775
+ if (rf.type === "attachment") return /* @__PURE__ */ jsxs30("div", { className: "flex flex-col gap-1", children: [
6776
+ rf.label && /* @__PURE__ */ jsx32("span", { className: "text-[10px] text-muted-foreground", children: rf.label }),
6777
+ v ? /* @__PURE__ */ jsxs30(
6778
+ "a",
6779
+ {
6780
+ href: v,
6781
+ target: "_blank",
6782
+ rel: "noopener noreferrer",
6783
+ className: "inline-flex items-center gap-1 rounded-lg border border-border bg-muted/50 px-2 py-1 text-xs font-medium hover:bg-muted transition-colors",
6784
+ children: [
6785
+ /* @__PURE__ */ jsx32("svg", { className: "h-3 w-3 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx32("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15.172 7l-6.586 6.586a2 2 0 102.828 2.828l6.414-6.586a4 4 0 00-5.656-5.656l-6.415 6.585a6 6 0 108.486 8.486L20.5 13" }) }),
6786
+ String(v).split("/").pop()
6787
+ ]
6788
+ }
6789
+ ) : dash
6790
+ ] }, rf.key);
6791
+ return /* @__PURE__ */ jsxs30("div", { className: "flex flex-col gap-1", children: [
6792
+ rf.label && /* @__PURE__ */ jsx32("span", { className: "text-[10px] text-muted-foreground", children: rf.label }),
6793
+ /* @__PURE__ */ jsx32("span", { className: "text-sm", children: v ?? "\u2014" })
6794
+ ] }, rf.key);
6795
+ }) : (
6796
+ // payload mode: row has { type, key, value }
6797
+ Object.entries(row).map(([k, v]) => /* @__PURE__ */ jsxs30("div", { className: "flex flex-col gap-1", children: [
6798
+ /* @__PURE__ */ jsx32("span", { className: "text-[10px] text-muted-foreground", children: k }),
6799
+ /* @__PURE__ */ jsx32("span", { className: "text-sm", children: String(v) })
6800
+ ] }, k))
6801
+ )
6802
+ ] }, ri)) });
6803
+ }
6661
6804
  case "checkbox":
6662
6805
  return /* @__PURE__ */ jsx32(
6663
6806
  "input",
@@ -6810,7 +6953,7 @@ function EditModal({
6810
6953
  title: "Edit Record",
6811
6954
  onClose,
6812
6955
  width,
6813
- footer: /* @__PURE__ */ jsxs30(Fragment11, { children: [
6956
+ footer: /* @__PURE__ */ jsxs30(Fragment12, { children: [
6814
6957
  /* @__PURE__ */ jsx32(Button, { variant: "outline", size: "sm", onClick: onClose, disabled: loading, children: "Cancel" }),
6815
6958
  /* @__PURE__ */ jsxs30(Button, { size: "sm", onClick: handleSubmit, disabled: loading, children: [
6816
6959
  loading && /* @__PURE__ */ jsx32(Loader22, { className: "h-3.5 w-3.5 mr-1.5 animate-spin" }),
@@ -6837,7 +6980,7 @@ function EditModal({
6837
6980
  ...f.colSpan ? { gridColumn: `span ${f.colSpan}` } : {},
6838
6981
  ...f.rowSpan ? { gridRow: `span ${f.rowSpan}` } : {}
6839
6982
  },
6840
- children: f.component ? /* @__PURE__ */ jsx32(Fragment11, { children: f.component }) : /* @__PURE__ */ jsxs30(Fragment11, { children: [
6983
+ children: f.component ? /* @__PURE__ */ jsx32(Fragment12, { children: f.component }) : /* @__PURE__ */ jsxs30(Fragment12, { children: [
6841
6984
  f.type !== "checkbox" && /* @__PURE__ */ jsxs30("label", { className: "block text-xs font-semibold text-muted-foreground mb-1", children: [
6842
6985
  f.label,
6843
6986
  f.required && /* @__PURE__ */ jsx32("span", { className: "text-danger ml-0.5", children: "*" })
@@ -6897,7 +7040,7 @@ function DeleteModal({
6897
7040
  title: "Confirm Delete",
6898
7041
  onClose,
6899
7042
  width: "lg",
6900
- footer: /* @__PURE__ */ jsxs30(Fragment11, { children: [
7043
+ footer: /* @__PURE__ */ jsxs30(Fragment12, { children: [
6901
7044
  /* @__PURE__ */ jsx32(Button, { variant: "outline", size: "sm", onClick: onClose, disabled: loading, children: "Cancel" }),
6902
7045
  /* @__PURE__ */ jsxs30(Button, { variant: "danger", size: "sm", onClick: handleDelete, disabled: loading, children: [
6903
7046
  loading && /* @__PURE__ */ jsx32(Loader22, { className: "h-3.5 w-3.5 mr-1.5 animate-spin" }),
@@ -6957,12 +7100,45 @@ var BADGE_COLORS = {
6957
7100
  function badgeClass(value) {
6958
7101
  return BADGE_COLORS[value.toLowerCase()] ?? "bg-primary/10 text-primary border-primary/20";
6959
7102
  }
7103
+ function deriveField(key, sample) {
7104
+ const label = key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
7105
+ const base = { key, label };
7106
+ if (typeof sample === "boolean") return { ...base, type: "toggle", viewType: "toggle" };
7107
+ if (typeof sample === "number") return { ...base, inputType: "number" };
7108
+ if (Array.isArray(sample)) {
7109
+ if (sample.length === 0 || typeof sample[0] === "string")
7110
+ return { ...base, type: "tag-input" };
7111
+ return base;
7112
+ }
7113
+ if (typeof sample === "string") {
7114
+ if (/\.(png|jpe?g|gif|webp|svg|avif)(\?.*)?$/i.test(sample))
7115
+ return { ...base, type: "input", viewType: "image" };
7116
+ if (/\.(pdf|docx?|xlsx?|csv|zip|pptx?)(\?.*)?$/i.test(sample))
7117
+ return { ...base, type: "input", viewType: "attachment" };
7118
+ if (/^https?:\/\//.test(sample))
7119
+ return { ...base, type: "input", viewType: "text-url-open-other-tabs" };
7120
+ if (sample.length > 120 || /\n/.test(sample))
7121
+ return { ...base, type: "textarea" };
7122
+ if (/password|secret|token/i.test(key))
7123
+ return { ...base, type: "password" };
7124
+ if (/email/i.test(key))
7125
+ return { ...base, inputType: "email" };
7126
+ if (/color|colour/i.test(key) && /^#[0-9a-f]{3,8}$/i.test(sample))
7127
+ return { ...base, type: "color-picker", viewType: "text" };
7128
+ }
7129
+ return base;
7130
+ }
6960
7131
  function Table({
6961
7132
  data,
6962
7133
  columns,
7134
+ loading,
7135
+ emptyState,
7136
+ error: errorProp,
6963
7137
  searchable = false,
6964
7138
  searchPlaceholder = "Search...",
6965
- pagination = false,
7139
+ searchValue: controlledSearch,
7140
+ onSearchChange,
7141
+ clientPagination = false,
6966
7142
  itemsPerPage = 10,
6967
7143
  selectable = false,
6968
7144
  onBulkDelete,
@@ -6970,15 +7146,38 @@ function Table({
6970
7146
  bulkDeleteBaseUrl,
6971
7147
  defaultActions,
6972
7148
  serverPagination,
6973
- className
7149
+ variant = "default",
7150
+ className,
7151
+ onRowClick,
7152
+ onRowDoubleClick,
7153
+ rowClassName,
7154
+ expandable = false,
7155
+ renderExpanded,
7156
+ columnVisibility,
7157
+ onColumnVisibilityChange,
7158
+ exportable = false,
7159
+ onExport,
7160
+ virtualized = false,
7161
+ draggable = false,
7162
+ onRowReorder,
7163
+ keyboardNavigation = false
6974
7164
  }) {
6975
7165
  const { toast } = useToast();
6976
- const [search, setSearch] = React28.useState("");
7166
+ const isControlledSearch = controlledSearch !== void 0;
7167
+ const [internalSearch, setInternalSearch] = React28.useState("");
7168
+ const search = isControlledSearch ? controlledSearch : internalSearch;
7169
+ const setSearch = (v) => {
7170
+ if (!isControlledSearch) setInternalSearch(v);
7171
+ onSearchChange?.(v);
7172
+ };
6977
7173
  const [currentPage, setCurrentPage] = React28.useState(1);
6978
7174
  const [selectedIds, setSelectedIds] = React28.useState([]);
6979
7175
  const [sortKey, setSortKey] = React28.useState(null);
6980
7176
  const [sortDir, setSortDir] = React28.useState(null);
6981
7177
  const [bulkLoading, setBulkLoading] = React28.useState(false);
7178
+ const [expandedIds, setExpandedIds] = React28.useState(/* @__PURE__ */ new Set());
7179
+ const [dragOverId, setDragOverId] = React28.useState(null);
7180
+ const [focusedRowIdx, setFocusedRowIdx] = React28.useState(-1);
6982
7181
  const [viewItem, setViewItem] = React28.useState(null);
6983
7182
  const [editItem, setEditItem] = React28.useState(null);
6984
7183
  const [deleteItem, setDeleteItem] = React28.useState(null);
@@ -6990,15 +7189,17 @@ function Table({
6990
7189
  const safeBaseUrl = defaultActions?.baseUrl.replace(/\/+$/, "") ?? "";
6991
7190
  const autoFields = React28.useMemo(() => {
6992
7191
  if (!tableData.length) return [];
6993
- return Object.keys(tableData[0]).map((k) => ({
6994
- key: k,
6995
- label: k.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())
6996
- }));
7192
+ const row = tableData[0];
7193
+ return Object.keys(row).map((k) => deriveField(k, row[k]));
6997
7194
  }, [tableData]);
6998
7195
  const editFields = defaultActions?.editForm ?? autoFields;
6999
7196
  const viewFields = defaultActions?.viewForm ?? autoFields;
7197
+ const visibleColumns = React28.useMemo(() => {
7198
+ if (!columnVisibility) return columns;
7199
+ return columns.filter((col) => columnVisibility[String(col.key)] !== false);
7200
+ }, [columns, columnVisibility]);
7000
7201
  const allColumns = React28.useMemo(() => {
7001
- if (!defaultActions) return columns;
7202
+ if (!defaultActions) return visibleColumns;
7002
7203
  const actionsCol = {
7003
7204
  key: "__actions__",
7004
7205
  title: "Actions",
@@ -7047,8 +7248,8 @@ function Table({
7047
7248
  ))
7048
7249
  ] })
7049
7250
  };
7050
- return defaultActions.position === "first" ? [actionsCol, ...columns] : [...columns, actionsCol];
7051
- }, [columns, defaultActions]);
7251
+ return defaultActions.position === "first" ? [actionsCol, ...visibleColumns] : [...visibleColumns, actionsCol];
7252
+ }, [visibleColumns, defaultActions]);
7052
7253
  const handleSort = (key) => {
7053
7254
  if (sortKey !== key) {
7054
7255
  setSortKey(key);
@@ -7081,13 +7282,28 @@ function Table({
7081
7282
  const totalPages = Math.max(1, Math.ceil(filteredData.length / itemsPerPage));
7082
7283
  const safePage = Math.min(currentPage, totalPages);
7083
7284
  const paginatedData = React28.useMemo(() => {
7084
- if (!pagination) return filteredData;
7285
+ if (!clientPagination) return filteredData;
7085
7286
  const start = (safePage - 1) * itemsPerPage;
7086
7287
  return filteredData.slice(start, start + itemsPerPage);
7087
- }, [filteredData, pagination, safePage, itemsPerPage]);
7288
+ }, [filteredData, clientPagination, safePage, itemsPerPage]);
7088
7289
  React28.useEffect(() => {
7089
7290
  setCurrentPage(1);
7090
7291
  }, [search]);
7292
+ React28.useEffect(() => {
7293
+ if (!keyboardNavigation) return;
7294
+ const handler = (e) => {
7295
+ if (e.key === "ArrowDown") {
7296
+ e.preventDefault();
7297
+ setFocusedRowIdx((i) => Math.min(i + 1, paginatedData.length - 1));
7298
+ }
7299
+ if (e.key === "ArrowUp") {
7300
+ e.preventDefault();
7301
+ setFocusedRowIdx((i) => Math.max(i - 1, 0));
7302
+ }
7303
+ };
7304
+ window.addEventListener("keydown", handler);
7305
+ return () => window.removeEventListener("keydown", handler);
7306
+ }, [keyboardNavigation, paginatedData.length]);
7091
7307
  const handleSelectAll = (checked) => setSelectedIds(checked ? paginatedData.map((item) => String(item[idKey])) : []);
7092
7308
  const handleSelect = (id, checked) => setSelectedIds((prev) => checked ? [...prev, id] : prev.filter((i) => i !== id));
7093
7309
  const allSelected = paginatedData.length > 0 && selectedIds.length === paginatedData.length;
@@ -7145,8 +7361,9 @@ function Table({
7145
7361
  if (sortKey !== String(col.key)) return /* @__PURE__ */ jsx32(ChevronsUpDown, { className: "ml-1.5 h-3.5 w-3.5 opacity-40" });
7146
7362
  return sortDir === "asc" ? /* @__PURE__ */ jsx32(ChevronUp, { className: "ml-1.5 h-3.5 w-3.5 text-primary" }) : /* @__PURE__ */ jsx32(ChevronDown4, { className: "ml-1.5 h-3.5 w-3.5 text-primary" });
7147
7363
  };
7148
- return /* @__PURE__ */ jsxs30(Fragment11, { children: [
7364
+ return /* @__PURE__ */ jsxs30(Fragment12, { children: [
7149
7365
  /* @__PURE__ */ jsxs30("div", { className: cn("w-full space-y-3", className), children: [
7366
+ errorProp && /* @__PURE__ */ jsx32("div", { className: "rounded-xl border border-danger/30 bg-danger/5 px-4 py-3 text-sm text-danger", children: errorProp }),
7150
7367
  /* @__PURE__ */ jsxs30("div", { className: "flex items-center justify-between gap-3 flex-wrap", children: [
7151
7368
  searchable && /* @__PURE__ */ jsxs30("div", { className: "relative w-72", children: [
7152
7369
  /* @__PURE__ */ jsx32(Search5, { className: "absolute text-primary left-3 top-1/2 -translate-y-1/2 h-4 w-4 z-10" }),
@@ -7169,7 +7386,7 @@ function Table({
7169
7386
  )
7170
7387
  ] }),
7171
7388
  /* @__PURE__ */ jsxs30("div", { className: "flex items-center gap-2 ml-auto flex-wrap", children: [
7172
- selectable && selectedIds.length > 0 && /* @__PURE__ */ jsxs30(Fragment11, { children: [
7389
+ selectable && selectedIds.length > 0 && /* @__PURE__ */ jsxs30(Fragment12, { children: [
7173
7390
  /* @__PURE__ */ jsxs30(
7174
7391
  "button",
7175
7392
  {
@@ -7222,6 +7439,33 @@ function Table({
7222
7439
  ]
7223
7440
  }
7224
7441
  ),
7442
+ exportable && /* @__PURE__ */ jsxs30("div", { className: "relative group", children: [
7443
+ /* @__PURE__ */ jsx32("button", { className: "inline-flex items-center gap-1.5 rounded-lg border border-border bg-muted/50 px-3 py-1.5 text-xs font-medium text-muted-foreground hover:bg-muted transition-colors", children: "Export" }),
7444
+ /* @__PURE__ */ jsx32("div", { className: "absolute right-0 top-full mt-1 z-20 hidden group-hover:flex flex-col min-w-[110px] rounded-xl border border-border bg-card shadow-lg overflow-hidden", children: ["csv", "excel", "pdf"].map((type) => /* @__PURE__ */ jsx32(
7445
+ "button",
7446
+ {
7447
+ onClick: () => onExport?.(type),
7448
+ className: "px-4 py-2 text-xs text-left hover:bg-muted transition-colors capitalize",
7449
+ children: type.toUpperCase()
7450
+ },
7451
+ type
7452
+ )) })
7453
+ ] }),
7454
+ columnVisibility && onColumnVisibilityChange && /* @__PURE__ */ jsxs30("div", { className: "relative group", children: [
7455
+ /* @__PURE__ */ jsx32("button", { className: "inline-flex items-center gap-1.5 rounded-lg border border-border bg-muted/50 px-3 py-1.5 text-xs font-medium text-muted-foreground hover:bg-muted transition-colors", children: "Columns" }),
7456
+ /* @__PURE__ */ jsx32("div", { className: "absolute right-0 top-full mt-1 z-20 hidden group-hover:flex flex-col min-w-[150px] rounded-xl border border-border bg-card shadow-lg overflow-hidden p-2 gap-1", children: columns.map((col) => /* @__PURE__ */ jsxs30("label", { className: "flex items-center gap-2 px-2 py-1 rounded-lg hover:bg-muted cursor-pointer text-xs", children: [
7457
+ /* @__PURE__ */ jsx32(
7458
+ "input",
7459
+ {
7460
+ type: "checkbox",
7461
+ checked: columnVisibility[String(col.key)] !== false,
7462
+ onChange: (e) => onColumnVisibilityChange({ ...columnVisibility, [String(col.key)]: e.target.checked }),
7463
+ className: "accent-primary"
7464
+ }
7465
+ ),
7466
+ col.title
7467
+ ] }, String(col.key))) })
7468
+ ] }),
7225
7469
  /* @__PURE__ */ jsxs30("span", { className: "text-xs text-muted-foreground", children: [
7226
7470
  totalRows,
7227
7471
  " ",
@@ -7230,8 +7474,26 @@ function Table({
7230
7474
  ] })
7231
7475
  ] })
7232
7476
  ] }),
7233
- /* @__PURE__ */ jsx32("div", { className: "rounded-xl border border-border overflow-hidden bg-card/50 backdrop-blur-sm shadow-sm", children: /* @__PURE__ */ jsx32("div", { className: "w-full overflow-auto", children: /* @__PURE__ */ jsxs30("table", { className: "w-full caption-bottom text-sm", children: [
7234
- /* @__PURE__ */ jsx32("thead", { children: /* @__PURE__ */ jsxs30("tr", { className: "border-b border-border bg-muted/40", children: [
7477
+ loading && /* @__PURE__ */ jsxs30("div", { className: "flex items-center justify-center py-12 text-muted-foreground gap-2", children: [
7478
+ /* @__PURE__ */ jsx32(Loader22, { className: "h-5 w-5 animate-spin" }),
7479
+ /* @__PURE__ */ jsx32("span", { className: "text-sm", children: "Loading\u2026" })
7480
+ ] }),
7481
+ !loading && /* @__PURE__ */ jsx32("div", { className: cn(
7482
+ variant === "default" && "rounded-xl border border-border overflow-hidden bg-card/50 backdrop-blur-sm shadow-sm",
7483
+ variant === "zebra" && "rounded-xl border border-border overflow-hidden bg-card/50 backdrop-blur-sm shadow-sm",
7484
+ variant === "card" && "space-y-2",
7485
+ variant === "glass" && "rounded-2xl overflow-hidden border border-white/10 bg-background/30 backdrop-blur-xl shadow-xl",
7486
+ variant === "soft" && "rounded-2xl overflow-hidden bg-card",
7487
+ variant === "soft" && "[box-shadow:6px_6px_12px_hsl(var(--foreground)/0.07),-6px_-6px_12px_hsl(var(--background)/0.8)]",
7488
+ virtualized && "max-h-[520px] overflow-y-auto"
7489
+ ), children: /* @__PURE__ */ jsx32("div", { className: cn("w-full overflow-auto", variant === "card" && "space-y-2"), children: /* @__PURE__ */ jsxs30("table", { className: cn("w-full caption-bottom text-sm", variant === "card" && "border-separate border-spacing-y-2"), children: [
7490
+ /* @__PURE__ */ jsx32("thead", { children: /* @__PURE__ */ jsxs30("tr", { className: cn(
7491
+ variant === "default" && "border-b border-border bg-muted/40",
7492
+ variant === "zebra" && "border-b border-border bg-muted/40",
7493
+ variant === "card" && "[&>th]:bg-transparent",
7494
+ variant === "glass" && "border-b border-white/10 bg-white/5",
7495
+ variant === "soft" && "border-b-0 bg-muted/30"
7496
+ ), children: [
7235
7497
  selectable && /* @__PURE__ */ jsx32("th", { className: "h-11 w-[46px] px-4 text-left align-middle", children: /* @__PURE__ */ jsx32(
7236
7498
  Checkbox,
7237
7499
  {
@@ -7239,13 +7501,16 @@ function Table({
7239
7501
  onChange: (e) => handleSelectAll(e.target.checked)
7240
7502
  }
7241
7503
  ) }),
7504
+ expandable && /* @__PURE__ */ jsx32("th", { className: "h-11 w-8" }),
7242
7505
  allColumns.map((col, ci) => /* @__PURE__ */ jsx32(
7243
7506
  "th",
7244
7507
  {
7245
7508
  onClick: () => col.sortable && handleSort(String(col.key)),
7246
7509
  className: cn(
7247
7510
  "h-11 px-4 text-left align-middle text-xs font-semibold uppercase tracking-wider text-muted-foreground select-none whitespace-nowrap",
7248
- col.sortable && "cursor-pointer hover:text-foreground transition-colors"
7511
+ col.sortable && "cursor-pointer hover:text-foreground transition-colors",
7512
+ variant === "glass" && "text-foreground/70",
7513
+ variant === "soft" && "text-muted-foreground/80"
7249
7514
  ),
7250
7515
  children: /* @__PURE__ */ jsxs30("span", { className: "inline-flex items-center", children: [
7251
7516
  col.title,
@@ -7258,9 +7523,9 @@ function Table({
7258
7523
  /* @__PURE__ */ jsx32("tbody", { children: paginatedData.length === 0 ? /* @__PURE__ */ jsx32("tr", { children: /* @__PURE__ */ jsx32(
7259
7524
  "td",
7260
7525
  {
7261
- colSpan: allColumns.length + (selectable ? 1 : 0),
7526
+ colSpan: allColumns.length + (selectable ? 1 : 0) + (expandable ? 1 : 0),
7262
7527
  className: "h-32 text-center align-middle",
7263
- children: /* @__PURE__ */ jsxs30("div", { className: "flex flex-col items-center gap-1 text-muted-foreground", children: [
7528
+ children: emptyState ?? /* @__PURE__ */ jsxs30("div", { className: "flex flex-col items-center gap-1 text-muted-foreground", children: [
7264
7529
  /* @__PURE__ */ jsx32(Search5, { className: "h-8 w-8 opacity-20" }),
7265
7530
  /* @__PURE__ */ jsx32("span", { className: "text-sm", children: "No results found" }),
7266
7531
  search && /* @__PURE__ */ jsx32("button", { onClick: () => setSearch(""), className: "text-xs text-primary hover:underline", children: "Clear search" })
@@ -7269,107 +7534,168 @@ function Table({
7269
7534
  ) }) : paginatedData.map((item, i) => {
7270
7535
  const id = String(item[idKey] || i);
7271
7536
  const isSelected = selectedIds.includes(id);
7272
- return /* @__PURE__ */ jsxs30(
7273
- "tr",
7274
- {
7275
- className: cn(
7276
- "border-b border-border/60 transition-colors last:border-0",
7277
- isSelected ? "bg-primary/5 hover:bg-primary/8" : "hover:bg-muted/30"
7278
- ),
7279
- children: [
7280
- selectable && /* @__PURE__ */ jsx32("td", { className: "px-4 py-3 align-middle", children: /* @__PURE__ */ jsx32(
7281
- Checkbox,
7282
- {
7283
- checked: isSelected,
7284
- onChange: (e) => handleSelect(id, e.target.checked)
7285
- }
7286
- ) }),
7287
- allColumns.map((col, ci) => /* @__PURE__ */ jsx32("td", { className: "px-4 py-3 align-middle", children: col.render ? col.render(item) : col.type === "image" ? /* @__PURE__ */ jsx32(
7288
- "img",
7289
- {
7290
- src: item[col.key],
7291
- alt: item[col.key],
7292
- className: "h-9 w-9 rounded-lg object-cover ring-1 ring-border"
7293
- }
7294
- ) : col.type === "badge" ? /* @__PURE__ */ jsxs30("span", { className: cn(
7295
- "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-medium",
7296
- badgeClass(String(item[col.key]))
7297
- ), children: [
7298
- /* @__PURE__ */ jsx32("span", { className: cn(
7299
- "mr-1.5 h-1.5 w-1.5 rounded-full",
7300
- badgeClass(String(item[col.key])).includes("success") ? "bg-success" : badgeClass(String(item[col.key])).includes("warning") ? "bg-warning" : badgeClass(String(item[col.key])).includes("danger") ? "bg-danger" : badgeClass(String(item[col.key])).includes("info") ? "bg-info" : "bg-primary"
7537
+ const isExpanded = expandedIds.has(id);
7538
+ const isFocused = keyboardNavigation && focusedRowIdx === i;
7539
+ return /* @__PURE__ */ jsxs30(React28.Fragment, { children: [
7540
+ /* @__PURE__ */ jsxs30(
7541
+ "tr",
7542
+ {
7543
+ draggable,
7544
+ tabIndex: keyboardNavigation ? 0 : void 0,
7545
+ onDragStart: draggable ? (e) => {
7546
+ e.dataTransfer.setData("text/plain", id);
7547
+ } : void 0,
7548
+ onDragOver: draggable ? (e) => {
7549
+ e.preventDefault();
7550
+ setDragOverId(id);
7551
+ } : void 0,
7552
+ onDragLeave: draggable ? () => setDragOverId(null) : void 0,
7553
+ onDrop: draggable ? (e) => {
7554
+ e.preventDefault();
7555
+ setDragOverId(null);
7556
+ const fromId = e.dataTransfer.getData("text/plain");
7557
+ if (fromId === id) return;
7558
+ setTableData((prev) => {
7559
+ const fromIdx = prev.findIndex((r) => String(r[idKey] || "") === fromId);
7560
+ const toIdx = prev.findIndex((r) => String(r[idKey] || "") === id);
7561
+ if (fromIdx < 0 || toIdx < 0) return prev;
7562
+ const next = [...prev];
7563
+ const [moved] = next.splice(fromIdx, 1);
7564
+ next.splice(toIdx, 0, moved);
7565
+ onRowReorder?.(next);
7566
+ return next;
7567
+ });
7568
+ } : void 0,
7569
+ onClick: () => {
7570
+ if (expandable) setExpandedIds((prev) => {
7571
+ const s = new Set(prev);
7572
+ s.has(id) ? s.delete(id) : s.add(id);
7573
+ return s;
7574
+ });
7575
+ onRowClick?.(item);
7576
+ if (keyboardNavigation) setFocusedRowIdx(i);
7577
+ },
7578
+ onDoubleClick: () => onRowDoubleClick?.(item),
7579
+ className: cn(
7580
+ // default
7581
+ variant === "default" && "border-b border-border/60 transition-colors last:border-0",
7582
+ variant === "default" && (isSelected ? "bg-primary/5 hover:bg-primary/8" : "hover:bg-muted/30"),
7583
+ // zebra
7584
+ variant === "zebra" && "border-b border-border/40 transition-colors last:border-0",
7585
+ variant === "zebra" && (isSelected ? "bg-primary/8" : i % 2 === 0 ? "bg-card" : "bg-muted/40"),
7586
+ variant === "zebra" && !isSelected && "hover:bg-primary/5",
7587
+ // card
7588
+ variant === "card" && "rounded-xl border border-border bg-card shadow-sm transition-all hover:shadow-md hover:-translate-y-px",
7589
+ variant === "card" && (isSelected ? "border-primary/50 bg-primary/5" : ""),
7590
+ variant === "card" && "[&>td:first-child]:rounded-l-xl [&>td:last-child]:rounded-r-xl",
7591
+ // glass
7592
+ variant === "glass" && "border-b border-white/8 transition-colors last:border-0",
7593
+ variant === "glass" && (isSelected ? "bg-primary/15 hover:bg-primary/20" : "hover:bg-white/5"),
7594
+ // soft
7595
+ variant === "soft" && "transition-all",
7596
+ variant === "soft" && (isSelected ? "bg-primary/8 [box-shadow:inset_2px_2px_5px_hsl(var(--foreground)/0.06),inset_-2px_-2px_5px_hsl(var(--background)/0.7)]" : "hover:bg-muted/20"),
7597
+ variant === "soft" && "border-b border-border/30 last:border-0",
7598
+ (onRowClick || onRowDoubleClick || expandable) && "cursor-pointer",
7599
+ draggable && dragOverId === id && "ring-2 ring-inset ring-primary/40",
7600
+ isFocused && "ring-2 ring-inset ring-ring",
7601
+ rowClassName?.(item)
7602
+ ),
7603
+ children: [
7604
+ selectable && /* @__PURE__ */ jsx32("td", { className: "px-4 py-3 align-middle", children: /* @__PURE__ */ jsx32(
7605
+ Checkbox,
7606
+ {
7607
+ checked: isSelected,
7608
+ onChange: (e) => handleSelect(id, e.target.checked)
7609
+ }
7301
7610
  ) }),
7302
- item[col.key]
7303
- ] }) : col.type === "stack" ? /* @__PURE__ */ jsx32(AvatarStack, { images: Array.isArray(item[col.key]) ? item[col.key] : [], ...col.stackProps ?? {} }) : col.type === "icon" ? /* @__PURE__ */ jsx32("span", { className: "flex items-center", children: item[col.key] }) : col.type === "select" ? /* @__PURE__ */ jsx32(
7304
- "select",
7305
- {
7306
- value: item[col.key],
7307
- onChange: (e) => col.onChange?.(item, e.target.value),
7308
- className: "h-8 rounded-lg border border-border bg-background/50 px-2 text-xs text-foreground focus:outline-none focus:ring-2 focus:ring-ring transition-colors",
7309
- children: (col.selectOptions ?? []).map((opt) => /* @__PURE__ */ jsx32("option", { value: opt, children: opt }, opt))
7310
- }
7311
- ) : col.type === "toggle" ? /* @__PURE__ */ jsx32(
7312
- "button",
7313
- {
7314
- role: "switch",
7315
- "aria-checked": !!item[col.key],
7316
- onClick: () => col.onChange?.(item, !item[col.key]),
7317
- className: cn(
7318
- "relative inline-flex h-5 w-9 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
7319
- item[col.key] ? "bg-primary" : "bg-muted"
7320
- ),
7321
- children: /* @__PURE__ */ jsx32("span", { className: cn(
7322
- "pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow-sm transition-transform",
7323
- item[col.key] ? "translate-x-4" : "translate-x-0"
7324
- ) })
7325
- }
7326
- ) : col.type === "color" ? /* @__PURE__ */ jsxs30("div", { className: "flex items-center gap-2", children: [
7327
- /* @__PURE__ */ jsx32(
7328
- "input",
7611
+ expandable && /* @__PURE__ */ jsx32("td", { className: "w-8 px-2 py-3 align-middle", children: /* @__PURE__ */ jsx32(ChevronRight8, { className: cn("h-3.5 w-3.5 text-muted-foreground transition-transform", isExpanded && "rotate-90") }) }),
7612
+ allColumns.map((col, ci) => /* @__PURE__ */ jsx32("td", { className: "px-4 py-3 align-middle", children: col.render ? col.render(item) : col.type === "image" ? /* @__PURE__ */ jsx32(
7613
+ "img",
7329
7614
  {
7330
- type: "color",
7331
- value: item[col.key] || "#000000",
7615
+ src: item[col.key],
7616
+ alt: item[col.key],
7617
+ className: "h-9 w-9 rounded-lg object-cover ring-1 ring-border"
7618
+ }
7619
+ ) : col.type === "badge" ? /* @__PURE__ */ jsxs30("span", { className: cn(
7620
+ "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-medium",
7621
+ badgeClass(String(item[col.key]))
7622
+ ), children: [
7623
+ /* @__PURE__ */ jsx32("span", { className: cn(
7624
+ "mr-1.5 h-1.5 w-1.5 rounded-full",
7625
+ badgeClass(String(item[col.key])).includes("success") ? "bg-success" : badgeClass(String(item[col.key])).includes("warning") ? "bg-warning" : badgeClass(String(item[col.key])).includes("danger") ? "bg-danger" : badgeClass(String(item[col.key])).includes("info") ? "bg-info" : "bg-primary"
7626
+ ) }),
7627
+ item[col.key]
7628
+ ] }) : col.type === "stack" ? /* @__PURE__ */ jsx32(AvatarStack, { images: Array.isArray(item[col.key]) ? item[col.key] : [], ...col.stackProps ?? {} }) : col.type === "icon" ? /* @__PURE__ */ jsx32("span", { className: "flex items-center", children: item[col.key] }) : col.type === "select" ? /* @__PURE__ */ jsx32(
7629
+ "select",
7630
+ {
7631
+ value: item[col.key],
7332
7632
  onChange: (e) => col.onChange?.(item, e.target.value),
7333
- className: "h-7 w-7 cursor-pointer rounded border border-border bg-transparent p-0.5"
7633
+ className: "h-8 rounded-lg border border-border bg-background/50 px-2 text-xs text-foreground focus:outline-none focus:ring-2 focus:ring-ring transition-colors",
7634
+ children: (col.selectOptions ?? []).map((opt) => /* @__PURE__ */ jsx32("option", { value: opt, children: opt }, opt))
7334
7635
  }
7335
- ),
7336
- /* @__PURE__ */ jsx32("span", { className: "text-xs text-muted-foreground font-mono", children: item[col.key] })
7337
- ] }) : col.type === "checkbox" ? /* @__PURE__ */ jsx32(
7338
- Checkbox,
7339
- {
7340
- checked: !!item[col.key],
7341
- onChange: (e) => col.onChange?.(item, e.target.checked)
7342
- }
7343
- ) : col.type === "text-url" ? (() => {
7344
- const href = col.redirect ? typeof col.redirect === "function" ? col.redirect(item) : col.redirect : String(item[col.key] ?? "");
7345
- const colorMap = {
7346
- primary: "var(--primary)",
7347
- info: "var(--info)",
7348
- success: "var(--success)",
7349
- warning: "var(--warning)",
7350
- danger: "var(--danger)"
7351
- };
7352
- const underline = col.underlineColor ? colorMap[col.underlineColor] ?? col.underlineColor : "var(--primary)";
7353
- return /* @__PURE__ */ jsx32(
7354
- "a",
7636
+ ) : col.type === "toggle" ? /* @__PURE__ */ jsx32(
7637
+ "button",
7355
7638
  {
7356
- href,
7357
- target: col.openNewTab ? "_blank" : void 0,
7358
- rel: col.openNewTab ? "noopener noreferrer" : void 0,
7359
- style: { textDecorationColor: underline },
7360
- className: "text-sm underline underline-offset-2 hover:opacity-75 transition-opacity break-all",
7361
- onClick: col.openNewTab ? void 0 : (e) => e.preventDefault(),
7362
- children: item[col.key]
7639
+ role: "switch",
7640
+ "aria-checked": !!item[col.key],
7641
+ onClick: () => col.onChange?.(item, !item[col.key]),
7642
+ className: cn(
7643
+ "relative inline-flex h-5 w-9 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
7644
+ item[col.key] ? "bg-primary" : "bg-muted"
7645
+ ),
7646
+ children: /* @__PURE__ */ jsx32("span", { className: cn(
7647
+ "pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow-sm transition-transform",
7648
+ item[col.key] ? "translate-x-4" : "translate-x-0"
7649
+ ) })
7363
7650
  }
7364
- );
7365
- })() : /* @__PURE__ */ jsx32("span", { className: "text-foreground/90", children: item[col.key] }) }, `${String(col.key)}-${ci}`))
7366
- ]
7367
- },
7368
- id
7369
- );
7651
+ ) : col.type === "color" ? /* @__PURE__ */ jsxs30("div", { className: "flex items-center gap-2", children: [
7652
+ /* @__PURE__ */ jsx32(
7653
+ "input",
7654
+ {
7655
+ type: "color",
7656
+ value: item[col.key] || "#000000",
7657
+ onChange: (e) => col.onChange?.(item, e.target.value),
7658
+ className: "h-7 w-7 cursor-pointer rounded border border-border bg-transparent p-0.5"
7659
+ }
7660
+ ),
7661
+ /* @__PURE__ */ jsx32("span", { className: "text-xs text-muted-foreground font-mono", children: item[col.key] })
7662
+ ] }) : col.type === "checkbox" ? /* @__PURE__ */ jsx32(
7663
+ Checkbox,
7664
+ {
7665
+ checked: !!item[col.key],
7666
+ onChange: (e) => col.onChange?.(item, e.target.checked)
7667
+ }
7668
+ ) : col.type === "text-url" ? (() => {
7669
+ const href = col.redirect ? typeof col.redirect === "function" ? col.redirect(item) : col.redirect : String(item[col.key] ?? "");
7670
+ const colorMap = {
7671
+ primary: "var(--primary)",
7672
+ info: "var(--info)",
7673
+ success: "var(--success)",
7674
+ warning: "var(--warning)",
7675
+ danger: "var(--danger)"
7676
+ };
7677
+ const underline = col.underlineColor ? colorMap[col.underlineColor] ?? col.underlineColor : "var(--primary)";
7678
+ return /* @__PURE__ */ jsx32(
7679
+ "a",
7680
+ {
7681
+ href,
7682
+ target: col.openNewTab ? "_blank" : void 0,
7683
+ rel: col.openNewTab ? "noopener noreferrer" : void 0,
7684
+ style: { textDecorationColor: underline },
7685
+ className: "text-sm underline underline-offset-2 hover:opacity-75 transition-opacity break-all",
7686
+ onClick: col.openNewTab ? void 0 : (e) => e.preventDefault(),
7687
+ children: item[col.key]
7688
+ }
7689
+ );
7690
+ })() : /* @__PURE__ */ jsx32("span", { className: "text-foreground/90", children: item[col.key] }) }, `${String(col.key)}-${ci}`))
7691
+ ]
7692
+ }
7693
+ ),
7694
+ expandable && isExpanded && renderExpanded && /* @__PURE__ */ jsx32("tr", { className: "bg-muted/20 border-b border-border/60", children: /* @__PURE__ */ jsx32("td", { colSpan: allColumns.length + (selectable ? 1 : 0) + 1, className: "px-6 py-3", children: renderExpanded(item) }) })
7695
+ ] }, id);
7370
7696
  }) })
7371
7697
  ] }) }) }),
7372
- pagination && !serverPagination && totalPages > 1 && /* @__PURE__ */ jsxs30("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
7698
+ clientPagination && !serverPagination && totalPages > 1 && /* @__PURE__ */ jsxs30("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
7373
7699
  /* @__PURE__ */ jsxs30("span", { className: "text-xs text-muted-foreground", children: [
7374
7700
  "Showing ",
7375
7701
  (safePage - 1) * itemsPerPage + 1,
@@ -7388,7 +7714,7 @@ function Table({
7388
7714
  children: /* @__PURE__ */ jsx32(ChevronLeft6, { className: "h-4 w-4" })
7389
7715
  }
7390
7716
  ),
7391
- pagePills[0] > 1 && /* @__PURE__ */ jsxs30(Fragment11, { children: [
7717
+ pagePills[0] > 1 && /* @__PURE__ */ jsxs30(Fragment12, { children: [
7392
7718
  /* @__PURE__ */ jsx32("button", { onClick: () => setCurrentPage(1), className: "flex h-8 w-8 items-center justify-center rounded-lg border border-border text-xs text-muted-foreground hover:bg-muted transition-colors", children: "1" }),
7393
7719
  pagePills[0] > 2 && /* @__PURE__ */ jsx32("span", { className: "px-1 text-muted-foreground text-xs", children: "\u2026" })
7394
7720
  ] }),
@@ -7404,7 +7730,7 @@ function Table({
7404
7730
  },
7405
7731
  p
7406
7732
  )),
7407
- pagePills[pagePills.length - 1] < totalPages && /* @__PURE__ */ jsxs30(Fragment11, { children: [
7733
+ pagePills[pagePills.length - 1] < totalPages && /* @__PURE__ */ jsxs30(Fragment12, { children: [
7408
7734
  pagePills[pagePills.length - 1] < totalPages - 1 && /* @__PURE__ */ jsx32("span", { className: "px-1 text-muted-foreground text-xs", children: "\u2026" }),
7409
7735
  /* @__PURE__ */ jsx32("button", { onClick: () => setCurrentPage(totalPages), className: "flex h-8 w-8 items-center justify-center rounded-lg border border-border text-xs text-muted-foreground hover:bg-muted transition-colors", children: totalPages })
7410
7736
  ] }),
@@ -7420,8 +7746,8 @@ function Table({
7420
7746
  ] })
7421
7747
  ] }),
7422
7748
  serverPagination && (() => {
7423
- const { pagination: pagination2, currentPage: cp, goToPage } = serverPagination;
7424
- const totalServerPages = pagination2.last_page ?? Math.ceil(pagination2.total / pagination2.per_page);
7749
+ const { pagination, currentPage: cp, goToPage } = serverPagination;
7750
+ const totalServerPages = pagination.last_page ?? Math.ceil(pagination.total / pagination.per_page);
7425
7751
  const pills = [];
7426
7752
  if (totalServerPages <= 7) {
7427
7753
  for (let i = 1; i <= totalServerPages; i++) pills.push(i);
@@ -7434,7 +7760,7 @@ function Table({
7434
7760
  }
7435
7761
  return /* @__PURE__ */ jsxs30("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
7436
7762
  /* @__PURE__ */ jsxs30("span", { className: "text-xs text-muted-foreground", children: [
7437
- pagination2.total,
7763
+ pagination.total,
7438
7764
  " total rows \xB7 page ",
7439
7765
  cp,
7440
7766
  " of ",
@@ -7445,7 +7771,7 @@ function Table({
7445
7771
  "button",
7446
7772
  {
7447
7773
  onClick: () => goToPage(cp - 1),
7448
- disabled: !pagination2.prev_page_url,
7774
+ disabled: !pagination.prev_page_url,
7449
7775
  className: "flex h-8 w-8 items-center justify-center rounded-lg border border-border text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:opacity-40 disabled:pointer-events-none",
7450
7776
  children: /* @__PURE__ */ jsx32(ChevronLeft6, { className: "h-4 w-4" })
7451
7777
  }
@@ -7468,7 +7794,7 @@ function Table({
7468
7794
  "button",
7469
7795
  {
7470
7796
  onClick: () => goToPage(cp + 1),
7471
- disabled: !pagination2.next_page_url,
7797
+ disabled: !pagination.next_page_url,
7472
7798
  className: "flex h-8 w-8 items-center justify-center rounded-lg border border-border text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:opacity-40 disabled:pointer-events-none",
7473
7799
  children: /* @__PURE__ */ jsx32(ChevronRight8, { className: "h-4 w-4" })
7474
7800
  }
@@ -7546,7 +7872,7 @@ function Table({
7546
7872
  }
7547
7873
 
7548
7874
  // src/components/ui/data-grid.tsx
7549
- import { Fragment as Fragment12, jsx as jsx33, jsxs as jsxs31 } from "react/jsx-runtime";
7875
+ import { Fragment as Fragment13, jsx as jsx33, jsxs as jsxs31 } from "react/jsx-runtime";
7550
7876
  function useServerDataGrid({ url, params, encrypt, key, decryptPayloadLog, columnOverrides }) {
7551
7877
  const [data, setData] = React29.useState([]);
7552
7878
  const [columns, setColumns] = React29.useState([]);
@@ -7674,7 +8000,7 @@ function DGModalShell({ title, onClose, children, footer, width = "lg" }) {
7674
8000
  );
7675
8001
  }
7676
8002
  function DGFieldRenderer({ field, value, onChange }) {
7677
- if (field.render) return /* @__PURE__ */ jsx33(Fragment12, { children: field.render(value, onChange) });
8003
+ if (field.render) return /* @__PURE__ */ jsx33(Fragment13, { children: field.render(value, onChange) });
7678
8004
  const toLabelValue = (o) => {
7679
8005
  if (typeof o === "string") return { label: o, value: o };
7680
8006
  if (Array.isArray(o)) return { label: o[0], value: o[1] };
@@ -7754,7 +8080,7 @@ function DGViewModal({ item, fields, onClose, width }) {
7754
8080
  footer: /* @__PURE__ */ jsx33("button", { onClick: onClose, className: "px-4 py-1.5 text-sm rounded-xl border border-border hover:bg-accent transition-colors", children: "Close" }),
7755
8081
  children: /* @__PURE__ */ jsx33("div", { className: "space-y-3", children: fields.map((f) => /* @__PURE__ */ jsxs31("div", { children: [
7756
8082
  /* @__PURE__ */ jsx33("p", { className: "text-xs font-semibold text-muted-foreground mb-1", children: f.label }),
7757
- f.render ? /* @__PURE__ */ jsx33(Fragment12, { children: f.render(item[f.key], () => {
8083
+ f.render ? /* @__PURE__ */ jsx33(Fragment13, { children: f.render(item[f.key], () => {
7758
8084
  }) }) : /* @__PURE__ */ jsx33("p", { className: "text-sm text-foreground break-words", children: item[f.key] == null || item[f.key] === "" ? /* @__PURE__ */ jsx33("span", { className: "text-muted-foreground italic", children: "\u2014" }) : String(item[f.key]) })
7759
8085
  ] }, f.key)) })
7760
8086
  }
@@ -7818,7 +8144,7 @@ function DGEditModal({
7818
8144
  title: "Edit Record",
7819
8145
  onClose,
7820
8146
  width,
7821
- footer: /* @__PURE__ */ jsxs31(Fragment12, { children: [
8147
+ footer: /* @__PURE__ */ jsxs31(Fragment13, { children: [
7822
8148
  /* @__PURE__ */ jsx33("button", { onClick: onClose, disabled: loading, className: "px-4 py-1.5 text-sm rounded-xl border border-border hover:bg-accent transition-colors", children: "Cancel" }),
7823
8149
  /* @__PURE__ */ jsxs31("button", { onClick: handleSubmit, disabled: loading, className: "px-4 py-1.5 text-sm rounded-xl bg-primary text-primary-foreground hover:bg-primary-hover transition-colors flex items-center gap-1.5", children: [
7824
8150
  loading && /* @__PURE__ */ jsx33(Loader23, { className: "h-3.5 w-3.5 animate-spin" }),
@@ -7878,7 +8204,7 @@ function DGDeleteModal({
7878
8204
  title: "Confirm Delete",
7879
8205
  onClose,
7880
8206
  width: "lg",
7881
- footer: /* @__PURE__ */ jsxs31(Fragment12, { children: [
8207
+ footer: /* @__PURE__ */ jsxs31(Fragment13, { children: [
7882
8208
  /* @__PURE__ */ jsx33("button", { onClick: onClose, disabled: loading, className: "px-4 py-1.5 text-sm rounded-xl border border-border hover:bg-accent transition-colors", children: "Cancel" }),
7883
8209
  /* @__PURE__ */ jsxs31("button", { onClick: handleDelete, disabled: loading, className: "px-4 py-1.5 text-sm rounded-xl bg-danger text-danger-foreground hover:bg-danger-hover transition-colors flex items-center gap-1.5", children: [
7884
8210
  loading && /* @__PURE__ */ jsx33(Loader23, { className: "h-3.5 w-3.5 animate-spin" }),
@@ -8810,7 +9136,7 @@ function KanbanBoard({ columns: controlled, onChange, onAddCard, className }) {
8810
9136
  import * as React33 from "react";
8811
9137
  import { MapContainer, TileLayer, Marker, Popup, Polyline, useMap, CircleMarker } from "react-leaflet";
8812
9138
  import L from "leaflet";
8813
- import { Fragment as Fragment14, jsx as jsx39, jsxs as jsxs35 } from "react/jsx-runtime";
9139
+ import { Fragment as Fragment15, jsx as jsx39, jsxs as jsxs35 } from "react/jsx-runtime";
8814
9140
  L.Icon.Default.mergeOptions({
8815
9141
  iconUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png",
8816
9142
  iconRetinaUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png",
@@ -8916,7 +9242,7 @@ function RouteLayer({ routes }) {
8916
9242
  });
8917
9243
  }, [routes]);
8918
9244
  if (loading) return null;
8919
- return /* @__PURE__ */ jsx39(Fragment14, { children: resolved.map((r, i) => {
9245
+ return /* @__PURE__ */ jsx39(Fragment15, { children: resolved.map((r, i) => {
8920
9246
  const color = r.route.color ?? (r.route.routeType === "walk" ? "#22c55e" : "#6366f1");
8921
9247
  const isDrive = (r.route.routeType ?? "drive") === "drive";
8922
9248
  const midIdx = Math.floor(r.coords.length / 2);
@@ -8956,7 +9282,7 @@ function RouteLayer({ routes }) {
8956
9282
  zIndexOffset: 100,
8957
9283
  children: /* @__PURE__ */ jsx39(Popup, { children: /* @__PURE__ */ jsxs35("div", { className: "text-xs space-y-0.5 min-w-[120px]", children: [
8958
9284
  /* @__PURE__ */ jsx39("p", { className: "font-semibold", children: r.route.label ? `${r.route.label} \u2014 Start` : "Start" }),
8959
- r.distance > 0 && /* @__PURE__ */ jsxs35(Fragment14, { children: [
9285
+ r.distance > 0 && /* @__PURE__ */ jsxs35(Fragment15, { children: [
8960
9286
  /* @__PURE__ */ jsx39("p", { className: "text-muted-foreground", children: fmtDistance(r.distance) }),
8961
9287
  /* @__PURE__ */ jsxs35("p", { className: "text-muted-foreground", children: [
8962
9288
  fmtDuration(r.duration),
@@ -8975,7 +9301,7 @@ function RouteLayer({ routes }) {
8975
9301
  zIndexOffset: 100,
8976
9302
  children: /* @__PURE__ */ jsx39(Popup, { children: /* @__PURE__ */ jsxs35("div", { className: "text-xs space-y-0.5 min-w-[120px]", children: [
8977
9303
  /* @__PURE__ */ jsx39("p", { className: "font-semibold", children: r.route.label ? `${r.route.label} \u2014 End` : "End" }),
8978
- r.distance > 0 && /* @__PURE__ */ jsxs35(Fragment14, { children: [
9304
+ r.distance > 0 && /* @__PURE__ */ jsxs35(Fragment15, { children: [
8979
9305
  /* @__PURE__ */ jsx39("p", { className: "text-muted-foreground", children: fmtDistance(r.distance) }),
8980
9306
  /* @__PURE__ */ jsxs35("p", { className: "text-muted-foreground", children: [
8981
9307
  fmtDuration(r.duration),
@@ -9090,7 +9416,7 @@ function ClusterLayer({ markers, variant, onMarkerClick }) {
9090
9416
  });
9091
9417
  return groups;
9092
9418
  }, [markers, zoom, map]);
9093
- return /* @__PURE__ */ jsx39(Fragment14, { children: clusters.map((g, gi) => {
9419
+ return /* @__PURE__ */ jsx39(Fragment15, { children: clusters.map((g, gi) => {
9094
9420
  if (g.items.length === 1) {
9095
9421
  const m = g.items[0];
9096
9422
  const color2 = resolveColor(m.color);
@@ -9959,7 +10285,7 @@ function Modal({
9959
10285
  // src/components/ui/modal-variants.tsx
9960
10286
  import * as React36 from "react";
9961
10287
  import { AlertTriangle as AlertTriangle3, CheckCircle as CheckCircle2, Trash2 as Trash23, X as X13 } from "lucide-react";
9962
- import { Fragment as Fragment15, jsx as jsx42, jsxs as jsxs38 } from "react/jsx-runtime";
10288
+ import { Fragment as Fragment16, jsx as jsx42, jsxs as jsxs38 } from "react/jsx-runtime";
9963
10289
  function ModalBase({
9964
10290
  isOpen,
9965
10291
  onClose,
@@ -10110,7 +10436,7 @@ function ModalWithForms({
10110
10436
  onSubmit(values);
10111
10437
  }
10112
10438
  function renderField(field) {
10113
- if (field.render) return /* @__PURE__ */ jsx42(Fragment15, { children: field.render(values[field.name], (v) => handleChange(field.name, v)) });
10439
+ if (field.render) return /* @__PURE__ */ jsx42(Fragment16, { children: field.render(values[field.name], (v) => handleChange(field.name, v)) });
10114
10440
  const strOptions = (field.options ?? []).map(
10115
10441
  (o) => typeof o === "string" ? { label: o, value: o } : o
10116
10442
  );
@@ -10458,7 +10784,7 @@ import { PanelLeftClose, PanelLeftOpen, Sun as Sun3, Moon as Moon2, Loader2 as L
10458
10784
  // src/components/ui/tooltip.tsx
10459
10785
  import * as React38 from "react";
10460
10786
  import * as ReactDOM2 from "react-dom";
10461
- import { Fragment as Fragment16, jsx as jsx44, jsxs as jsxs40 } from "react/jsx-runtime";
10787
+ import { Fragment as Fragment17, jsx as jsx44, jsxs as jsxs40 } from "react/jsx-runtime";
10462
10788
  function Tooltip({
10463
10789
  content,
10464
10790
  children,
@@ -10469,7 +10795,7 @@ function Tooltip({
10469
10795
  const [visible, setVisible] = React38.useState(false);
10470
10796
  const [coords, setCoords] = React38.useState({ top: 0, left: 0 });
10471
10797
  const ref = React38.useRef(null);
10472
- if (!enabled) return /* @__PURE__ */ jsx44(Fragment16, { children });
10798
+ if (!enabled) return /* @__PURE__ */ jsx44(Fragment17, { children });
10473
10799
  function calcCoords() {
10474
10800
  const r = ref.current?.getBoundingClientRect();
10475
10801
  if (!r) return;
@@ -11066,7 +11392,7 @@ var useTheme = () => {
11066
11392
  };
11067
11393
 
11068
11394
  // src/components/ui/panel.tsx
11069
- import { Fragment as Fragment17, jsx as jsx46, jsxs as jsxs41 } from "react/jsx-runtime";
11395
+ import { Fragment as Fragment18, jsx as jsx46, jsxs as jsxs41 } from "react/jsx-runtime";
11070
11396
  var PanelCollapsedContext = React40.createContext(false);
11071
11397
  var PanelGroupsContext = React40.createContext({ expandedGroups: /* @__PURE__ */ new Set(), onGroupToggle: () => {
11072
11398
  } });
@@ -11228,7 +11554,7 @@ function Panel({
11228
11554
  "shrink-0 border-b border-border",
11229
11555
  effectiveCollapsed ? "flex items-center justify-center py-3" : "flex items-center gap-2 px-4 py-3"
11230
11556
  ),
11231
- children: sidebarBrand ? effectiveCollapsed ? sidebarBrand.image ? /* @__PURE__ */ jsx46("img", { src: sidebarBrand.image, alt: "logo", className: "h-7 w-7 rounded-md object-cover shrink-0" }) : /* @__PURE__ */ jsx46("span", { className: "shrink-0", children: sidebarBrand.icon }) : /* @__PURE__ */ jsxs41(Fragment17, { children: [
11557
+ children: sidebarBrand ? effectiveCollapsed ? sidebarBrand.image ? /* @__PURE__ */ jsx46("img", { src: sidebarBrand.image, alt: "logo", className: "h-7 w-7 rounded-md object-cover shrink-0" }) : /* @__PURE__ */ jsx46("span", { className: "shrink-0", children: sidebarBrand.icon }) : /* @__PURE__ */ jsxs41(Fragment18, { children: [
11232
11558
  sidebarBrand.image ? /* @__PURE__ */ jsx46("img", { src: sidebarBrand.image, alt: "logo", className: "h-7 w-7 rounded-md object-cover shrink-0" }) : sidebarBrand.icon && /* @__PURE__ */ jsx46("span", { className: "shrink-0", children: sidebarBrand.icon }),
11233
11559
  sidebarBrand.title && /* @__PURE__ */ jsx46("span", { className: "flex-1 truncate text-sm font-semibold", children: sidebarBrand.title }),
11234
11560
  sidebarBrand.trailing && /* @__PURE__ */ jsx46("span", { className: "shrink-0", children: sidebarBrand.trailing })
@@ -12537,7 +12863,7 @@ function Widget({
12537
12863
  // src/components/ui/wizard.tsx
12538
12864
  import * as React50 from "react";
12539
12865
  import { Check as Check7, X as X15, ChevronLeft as ChevronLeft8, ChevronRight as ChevronRight12, AlertCircle as AlertCircle2 } from "lucide-react";
12540
- import { Fragment as Fragment20, jsx as jsx62, jsxs as jsxs55 } from "react/jsx-runtime";
12866
+ import { Fragment as Fragment21, jsx as jsx62, jsxs as jsxs55 } from "react/jsx-runtime";
12541
12867
  var SIZE_MAP = {
12542
12868
  sm: "max-w-sm",
12543
12869
  md: "max-w-lg",
@@ -12747,10 +13073,10 @@ function DefaultActions({
12747
13073
  /* @__PURE__ */ jsx62("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8v8z" })
12748
13074
  ] }),
12749
13075
  "Processing..."
12750
- ] }) : isLast ? /* @__PURE__ */ jsxs55(Fragment20, { children: [
13076
+ ] }) : isLast ? /* @__PURE__ */ jsxs55(Fragment21, { children: [
12751
13077
  /* @__PURE__ */ jsx62(Check7, { className: "h-4 w-4" }),
12752
13078
  finishLabel ?? "Finish"
12753
- ] }) : /* @__PURE__ */ jsxs55(Fragment20, { children: [
13079
+ ] }) : /* @__PURE__ */ jsxs55(Fragment21, { children: [
12754
13080
  nextLabel ?? "Next",
12755
13081
  /* @__PURE__ */ jsx62(ChevronRight12, { className: "h-4 w-4" })
12756
13082
  ] })
@@ -13059,13 +13385,13 @@ var setupInterceptors = () => {
13059
13385
 
13060
13386
  // src/lib/codego/provider.tsx
13061
13387
  import * as React51 from "react";
13062
- import { Fragment as Fragment21, jsx as jsx63 } from "react/jsx-runtime";
13388
+ import { Fragment as Fragment22, jsx as jsx63 } from "react/jsx-runtime";
13063
13389
  function CodegoApiProvider({ children }) {
13064
13390
  const { toast } = useToast();
13065
13391
  React51.useEffect(() => {
13066
13392
  setToastFunction(toast);
13067
13393
  }, [toast]);
13068
- return /* @__PURE__ */ jsx63(Fragment21, { children });
13394
+ return /* @__PURE__ */ jsx63(Fragment22, { children });
13069
13395
  }
13070
13396
 
13071
13397
  // src/lib/codego/index.ts