@juv/codego-react-ui 3.4.11 → 3.5.1

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
@@ -2116,13 +2116,13 @@ function BulletinBoard({
2116
2116
  const categories = React8.useMemo(() => {
2117
2117
  if (categoriesProp) return categoriesProp;
2118
2118
  const set = /* @__PURE__ */ new Set();
2119
- items.forEach((i) => {
2119
+ (items ?? []).forEach((i) => {
2120
2120
  if (i.category) set.add(i.category);
2121
2121
  });
2122
2122
  return Array.from(set);
2123
2123
  }, [items, categoriesProp]);
2124
2124
  const filtered = React8.useMemo(() => {
2125
- let list = items;
2125
+ let list = items ?? [];
2126
2126
  if (search.trim()) {
2127
2127
  const q = search.toLowerCase();
2128
2128
  list = list.filter(
@@ -2131,7 +2131,7 @@ function BulletinBoard({
2131
2131
  }
2132
2132
  if (category) list = list.filter((i) => i.category === category);
2133
2133
  return [...list].sort((a, b) => (b.pinned ? 1 : 0) - (a.pinned ? 1 : 0));
2134
- }, [items, search, category]);
2134
+ }, [items ?? [], search, category]);
2135
2135
  const showToolbar = searchable || filterable && categories.length > 0;
2136
2136
  return /* @__PURE__ */ jsxs9("div", { className: cn("flex flex-col gap-4", className), children: [
2137
2137
  showHeader && /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between gap-2", children: [
@@ -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,18 +6358,8 @@ 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";
6288
- var csrfAxios = axios3.create();
6289
- csrfAxios.interceptors.request.use((config) => {
6290
- const method = (config.method ?? "").toUpperCase();
6291
- if (["POST", "PUT", "PATCH", "DELETE"].includes(method)) {
6292
- const token = document.querySelector('meta[name="csrf-token"]')?.getAttribute("content");
6293
- if (!token) throw new Error('[Table] CSRF token not found. Add <meta name="csrf-token" content="..."> to your HTML <head>.');
6294
- config.headers.set("X-CSRF-Token", token);
6295
- }
6296
- return config;
6297
- });
6298
- function useServerTable({ url, params, encrypt, key, decryptPayloadLog, columnOverrides, debounce = 300, transform, manual = false, refresh: refreshEnabled = false, refreshInterval = 0, hardReload, onSuccess, onError }) {
6361
+ import { Fragment as Fragment12, jsx as jsx32, jsxs as jsxs30 } from "react/jsx-runtime";
6362
+ function useServerTable({ url, params, encrypt, key, decryptPayloadLog, columnOverrides, debounce = 300, transform, manual = false, refresh: refreshEnabled = false, refreshInterval = 0, hardReload, onSuccess, onError, filter: filterFields, sort: sortKeys }) {
6299
6363
  const [data, setData] = React28.useState([]);
6300
6364
  const [columns, setColumns] = React28.useState([]);
6301
6365
  const [currentPage, setCurrentPage] = React28.useState(1);
@@ -6305,6 +6369,15 @@ function useServerTable({ url, params, encrypt, key, decryptPayloadLog, columnOv
6305
6369
  const [tick, setTick] = React28.useState(0);
6306
6370
  const [searchValue, setSearchValue] = React28.useState("");
6307
6371
  const debounceTimer = React28.useRef(void 0);
6372
+ const [filterValues, setFilterValues] = React28.useState(() => {
6373
+ const init = {};
6374
+ filterFields?.forEach((f) => {
6375
+ init[f.key] = f.type === "checkbox" || f.type === "toggle" ? false : "";
6376
+ });
6377
+ return init;
6378
+ });
6379
+ const [sortKey, setSortKey] = React28.useState("");
6380
+ const [sortDir, setSortDir] = React28.useState("asc");
6308
6381
  React28.useEffect(() => {
6309
6382
  if (hardReload) hardReload.current = () => setTick((t) => t + 1);
6310
6383
  }, [hardReload]);
@@ -6313,13 +6386,32 @@ function useServerTable({ url, params, encrypt, key, decryptPayloadLog, columnOv
6313
6386
  const id = setInterval(() => setTick((t) => t + 1), refreshInterval);
6314
6387
  return () => clearInterval(id);
6315
6388
  }, [refreshInterval]);
6389
+ const activeFilterParams = React28.useMemo(() => {
6390
+ const out = {};
6391
+ filterFields?.forEach((f) => {
6392
+ const v = filterValues[f.key];
6393
+ if (f.type === "checkbox" || f.type === "toggle") {
6394
+ if (v) out[f.key] = 1;
6395
+ } else if (f.type === "date-range") {
6396
+ if (v?.from) out[`${f.key}_from`] = v.from;
6397
+ if (v?.to) out[`${f.key}_to`] = v.to;
6398
+ } else if (v !== "" && v !== null && v !== void 0) {
6399
+ out[f.key] = v;
6400
+ }
6401
+ });
6402
+ if (sortKey) {
6403
+ out.sort = sortKey;
6404
+ out.direction = sortDir;
6405
+ }
6406
+ return out;
6407
+ }, [filterValues, sortKey, sortDir, filterFields]);
6316
6408
  React28.useEffect(() => {
6317
6409
  if (manual && tick === 0) return;
6318
6410
  let cancelled = false;
6319
6411
  setLoading(true);
6320
6412
  setError(null);
6321
6413
  axios3.get(url, {
6322
- params: { ...params, page: currentPage, search: searchValue }
6414
+ params: { ...params, ...activeFilterParams, page: currentPage, search: searchValue }
6323
6415
  }).then(({ data: res }) => {
6324
6416
  if (cancelled) return;
6325
6417
  const payload = encrypt ? decryptLaravelPayload(res, key) : res;
@@ -6365,15 +6457,184 @@ function useServerTable({ url, params, encrypt, key, decryptPayloadLog, columnOv
6365
6457
  return () => {
6366
6458
  cancelled = true;
6367
6459
  };
6368
- }, [url, currentPage, tick, JSON.stringify(params), encrypt, decryptPayloadLog, JSON.stringify(columnOverrides), searchValue]);
6460
+ }, [url, currentPage, tick, JSON.stringify(params), JSON.stringify(activeFilterParams), encrypt, decryptPayloadLog, JSON.stringify(columnOverrides), searchValue]);
6369
6461
  const handleSearchChange = (value) => {
6370
6462
  setSearchValue(value);
6371
6463
  setCurrentPage(1);
6372
6464
  if (debounceTimer.current) clearTimeout(debounceTimer.current);
6373
- debounceTimer.current = setTimeout(() => {
6374
- setTick((t) => t + 1);
6375
- }, debounce);
6465
+ debounceTimer.current = setTimeout(() => setTick((t) => t + 1), debounce);
6466
+ };
6467
+ const handleFilterChange = (key2, value) => {
6468
+ setFilterValues((prev) => ({ ...prev, [key2]: value }));
6469
+ setCurrentPage(1);
6470
+ setTick((t) => t + 1);
6471
+ };
6472
+ const handleClearFilters = () => {
6473
+ const reset = {};
6474
+ filterFields?.forEach((f) => {
6475
+ reset[f.key] = f.type === "checkbox" || f.type === "toggle" ? false : "";
6476
+ });
6477
+ setFilterValues(reset);
6478
+ setSortKey("");
6479
+ setSortDir("asc");
6480
+ setCurrentPage(1);
6481
+ setTick((t) => t + 1);
6376
6482
  };
6483
+ const hasActiveFilters = filterFields?.some((f) => {
6484
+ const v = filterValues[f.key];
6485
+ return f.type === "checkbox" || f.type === "toggle" ? !!v : v !== "" && v !== null && v !== void 0;
6486
+ }) || !!sortKey;
6487
+ const filterBar = filterFields?.length || sortKeys?.length ? /* @__PURE__ */ jsxs30("div", { className: "flex flex-wrap items-end gap-3 rounded-xl border border-border bg-muted/30 px-4 py-3", children: [
6488
+ filterFields?.map((f) => {
6489
+ const label = f.label ?? f.key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
6490
+ const value = filterValues[f.key];
6491
+ const opts = (f.options ?? []).map(
6492
+ (o) => typeof o === "string" ? { label: o, value: o } : o
6493
+ );
6494
+ if (f.type === "checkbox") return /* @__PURE__ */ jsxs30("label", { className: "flex items-center gap-2 cursor-pointer select-none", children: [
6495
+ /* @__PURE__ */ jsx32(
6496
+ "input",
6497
+ {
6498
+ type: "checkbox",
6499
+ checked: !!value,
6500
+ onChange: (e) => handleFilterChange(f.key, e.target.checked),
6501
+ className: "h-4 w-4 rounded accent-primary"
6502
+ }
6503
+ ),
6504
+ /* @__PURE__ */ jsx32("span", { className: "text-xs font-medium text-foreground", children: label })
6505
+ ] }, f.key);
6506
+ if (f.type === "toggle") return /* @__PURE__ */ jsxs30("label", { className: "flex items-center gap-2 cursor-pointer select-none", children: [
6507
+ /* @__PURE__ */ jsx32(
6508
+ "button",
6509
+ {
6510
+ type: "button",
6511
+ role: "switch",
6512
+ "aria-checked": !!value,
6513
+ onClick: () => handleFilterChange(f.key, !value),
6514
+ className: cn(
6515
+ "relative inline-flex h-5 w-9 shrink-0 rounded-full border-2 border-transparent transition-colors",
6516
+ value ? "bg-primary" : "bg-muted"
6517
+ ),
6518
+ children: /* @__PURE__ */ jsx32("span", { className: cn(
6519
+ "pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow-sm transition-transform",
6520
+ value ? "translate-x-4" : "translate-x-0"
6521
+ ) })
6522
+ }
6523
+ ),
6524
+ /* @__PURE__ */ jsx32("span", { className: "text-xs font-medium text-foreground", children: label })
6525
+ ] }, f.key);
6526
+ if (f.type === "select") return /* @__PURE__ */ jsxs30("div", { className: "flex flex-col gap-1", children: [
6527
+ /* @__PURE__ */ jsx32("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: label }),
6528
+ /* @__PURE__ */ jsxs30(
6529
+ "select",
6530
+ {
6531
+ value: value ?? "",
6532
+ onChange: (e) => handleFilterChange(f.key, e.target.value),
6533
+ className: "h-8 min-w-[120px] rounded-lg border border-border bg-background px-2 text-xs text-foreground focus:outline-none focus:ring-2 focus:ring-ring transition-colors",
6534
+ children: [
6535
+ /* @__PURE__ */ jsx32("option", { value: "", children: f.placeholder ?? `All ${label}` }),
6536
+ opts.map((o) => /* @__PURE__ */ jsx32("option", { value: o.value, children: o.label }, o.value))
6537
+ ]
6538
+ }
6539
+ )
6540
+ ] }, f.key);
6541
+ if (f.type === "date" || f.type === "date-time") return /* @__PURE__ */ jsxs30("div", { className: "flex flex-col gap-1", children: [
6542
+ /* @__PURE__ */ jsx32("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: label }),
6543
+ /* @__PURE__ */ jsx32(
6544
+ "input",
6545
+ {
6546
+ type: f.type === "date-time" ? "datetime-local" : "date",
6547
+ value: value ?? "",
6548
+ onChange: (e) => handleFilterChange(f.key, e.target.value),
6549
+ className: "h-8 rounded-lg border border-border bg-background px-2 text-xs text-foreground focus:outline-none focus:ring-2 focus:ring-ring transition-colors"
6550
+ }
6551
+ )
6552
+ ] }, f.key);
6553
+ if (f.type === "date-range") return /* @__PURE__ */ jsxs30("div", { className: "flex flex-col gap-1", children: [
6554
+ /* @__PURE__ */ jsx32("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: label }),
6555
+ /* @__PURE__ */ jsxs30("div", { className: "flex items-center gap-1.5", children: [
6556
+ /* @__PURE__ */ jsx32(
6557
+ "input",
6558
+ {
6559
+ type: "date",
6560
+ value: value?.from ?? "",
6561
+ onChange: (e) => handleFilterChange(f.key, { ...value, from: e.target.value }),
6562
+ className: "h-8 rounded-lg border border-border bg-background px-2 text-xs text-foreground focus:outline-none focus:ring-2 focus:ring-ring transition-colors"
6563
+ }
6564
+ ),
6565
+ /* @__PURE__ */ jsx32("span", { className: "text-xs text-muted-foreground", children: "\u2013" }),
6566
+ /* @__PURE__ */ jsx32(
6567
+ "input",
6568
+ {
6569
+ type: "date",
6570
+ value: value?.to ?? "",
6571
+ onChange: (e) => handleFilterChange(f.key, { ...value, to: e.target.value }),
6572
+ className: "h-8 rounded-lg border border-border bg-background px-2 text-xs text-foreground focus:outline-none focus:ring-2 focus:ring-ring transition-colors"
6573
+ }
6574
+ )
6575
+ ] })
6576
+ ] }, f.key);
6577
+ return /* @__PURE__ */ jsxs30("div", { className: "flex flex-col gap-1", children: [
6578
+ /* @__PURE__ */ jsx32("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: label }),
6579
+ /* @__PURE__ */ jsx32(
6580
+ "input",
6581
+ {
6582
+ type: "text",
6583
+ value: value ?? "",
6584
+ placeholder: f.placeholder ?? `Filter ${label}\u2026`,
6585
+ onChange: (e) => handleFilterChange(f.key, e.target.value),
6586
+ className: "h-8 min-w-[140px] rounded-lg border border-border bg-background px-3 text-xs text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring transition-colors"
6587
+ }
6588
+ )
6589
+ ] }, f.key);
6590
+ }),
6591
+ sortKeys?.length ? /* @__PURE__ */ jsxs30("div", { className: "flex flex-col gap-1", children: [
6592
+ /* @__PURE__ */ jsx32("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: "Sort by" }),
6593
+ /* @__PURE__ */ jsxs30("div", { className: "flex items-center gap-1.5", children: [
6594
+ /* @__PURE__ */ jsxs30(
6595
+ "select",
6596
+ {
6597
+ value: sortKey,
6598
+ onChange: (e) => {
6599
+ setSortKey(e.target.value);
6600
+ setCurrentPage(1);
6601
+ setTick((t) => t + 1);
6602
+ },
6603
+ className: "h-8 min-w-[120px] rounded-lg border border-border bg-background px-2 text-xs text-foreground focus:outline-none focus:ring-2 focus:ring-ring transition-colors",
6604
+ children: [
6605
+ /* @__PURE__ */ jsx32("option", { value: "", children: "Default" }),
6606
+ sortKeys.map((k) => /* @__PURE__ */ jsx32("option", { value: k, children: k.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()) }, k))
6607
+ ]
6608
+ }
6609
+ ),
6610
+ sortKey && /* @__PURE__ */ jsx32(
6611
+ "button",
6612
+ {
6613
+ type: "button",
6614
+ onClick: () => {
6615
+ setSortDir((d) => d === "asc" ? "desc" : "asc");
6616
+ setTick((t) => t + 1);
6617
+ },
6618
+ className: "flex h-8 w-8 items-center justify-center rounded-lg border border-border bg-background text-muted-foreground hover:text-foreground hover:bg-muted transition-colors",
6619
+ title: sortDir === "asc" ? "Ascending" : "Descending",
6620
+ children: sortDir === "asc" ? /* @__PURE__ */ jsx32(ChevronUp, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx32(ChevronDown4, { className: "h-3.5 w-3.5" })
6621
+ }
6622
+ )
6623
+ ] })
6624
+ ] }) : null,
6625
+ hasActiveFilters && /* @__PURE__ */ jsxs30(
6626
+ "button",
6627
+ {
6628
+ type: "button",
6629
+ onClick: handleClearFilters,
6630
+ className: "flex h-8 items-center gap-1.5 self-end rounded-lg border border-border bg-background px-3 text-xs font-medium text-muted-foreground hover:bg-muted hover:text-foreground transition-colors",
6631
+ children: [
6632
+ /* @__PURE__ */ jsx32(X9, { className: "h-3 w-3" }),
6633
+ " Clear"
6634
+ ]
6635
+ }
6636
+ )
6637
+ ] }) : null;
6377
6638
  return {
6378
6639
  data,
6379
6640
  columns,
@@ -6382,11 +6643,18 @@ function useServerTable({ url, params, encrypt, key, decryptPayloadLog, columnOv
6382
6643
  serverPagination: pagination ? { pagination, currentPage, goToPage: (page) => setCurrentPage(page) } : null,
6383
6644
  loading,
6384
6645
  error,
6646
+ filterBar,
6385
6647
  goToPage: (page) => setCurrentPage(page),
6386
6648
  reload: () => setTick((t) => t + 1),
6387
6649
  refresh: () => setTick((t) => t + 1),
6650
+ // Passthrough props
6388
6651
  searchValue,
6389
- onSearchChange: handleSearchChange
6652
+ onSearchChange: handleSearchChange,
6653
+ page: currentPage,
6654
+ onPageChange: (page) => setCurrentPage(page),
6655
+ sort: [],
6656
+ onSortChange: () => {
6657
+ }
6390
6658
  };
6391
6659
  }
6392
6660
  var MODAL_WIDTH = {
@@ -6444,7 +6712,8 @@ function validateField(field, value) {
6444
6712
  return null;
6445
6713
  }
6446
6714
  function FieldRenderer({ field, value, onChange }) {
6447
- if (field.render) return /* @__PURE__ */ jsx32(Fragment11, { children: field.render(value, onChange) });
6715
+ if (field.component) return /* @__PURE__ */ jsx32(Fragment12, { children: field.component });
6716
+ if (field.render) return /* @__PURE__ */ jsx32(Fragment12, { children: field.render(value, onChange) });
6448
6717
  const toLabelValue = (o) => {
6449
6718
  if (typeof o === "string") return { label: o, value: o };
6450
6719
  if (Array.isArray(o)) return { label: o[0], value: o[1] };
@@ -6536,9 +6805,33 @@ function FieldRenderer({ field, value, onChange }) {
6536
6805
  case "rich-text":
6537
6806
  return /* @__PURE__ */ jsx32(RichTextEditor, { value: value ?? "", onChange: (v) => onChange(v) });
6538
6807
  case "file-upload":
6539
- return /* @__PURE__ */ jsx32(FileUpload, { onChange: (files) => onChange(files) });
6808
+ return /* @__PURE__ */ jsx32(FileUpload, { onFileSelect: (file) => onChange(file), onFilesChange: (files) => {
6809
+ if (files.length > 1) onChange(files);
6810
+ } });
6540
6811
  case "repeater": {
6541
6812
  const items = Array.isArray(value) ? value : [];
6813
+ if (field.repeaterFields) {
6814
+ const rows = Array.isArray(value) ? value : [];
6815
+ return /* @__PURE__ */ jsx32(
6816
+ Repeater,
6817
+ {
6818
+ items: rows,
6819
+ fields: field.repeaterFields,
6820
+ onAdd: () => {
6821
+ const blank = {};
6822
+ field.repeaterFields.forEach((f) => {
6823
+ blank[f.key] = "";
6824
+ });
6825
+ onChange([...rows, blank]);
6826
+ },
6827
+ onRemove: (i) => onChange(rows.filter((_, idx) => idx !== i)),
6828
+ onFieldChange: (i, key, val) => {
6829
+ const next = rows.map((r, idx) => idx === i ? { ...r, [key]: val } : r);
6830
+ onChange(next);
6831
+ }
6832
+ }
6833
+ );
6834
+ }
6542
6835
  return /* @__PURE__ */ jsx32(
6543
6836
  Repeater,
6544
6837
  {
@@ -6579,8 +6872,172 @@ function ViewModal({
6579
6872
  item,
6580
6873
  fields,
6581
6874
  onClose,
6582
- width
6875
+ width,
6876
+ grid
6583
6877
  }) {
6878
+ const renderViewValue = (f, value) => {
6879
+ const sizeStyle = {
6880
+ ...f.width ? { width: typeof f.width === "number" ? `${f.width}px` : f.width } : {},
6881
+ ...f.height ? { height: typeof f.height === "number" ? `${f.height}px` : f.height } : {}
6882
+ };
6883
+ const vt = f.viewType ?? f.type;
6884
+ const empty = value === null || value === void 0 || value === "";
6885
+ const dash = /* @__PURE__ */ jsx32("span", { className: "text-muted-foreground italic text-sm", children: "\u2014" });
6886
+ if (f.component) return /* @__PURE__ */ jsx32(Fragment12, { children: f.component });
6887
+ if (f.render) return /* @__PURE__ */ jsx32(Fragment12, { children: f.render(value, () => {
6888
+ }) });
6889
+ switch (vt) {
6890
+ case "image":
6891
+ return empty ? dash : /* @__PURE__ */ jsx32(
6892
+ "img",
6893
+ {
6894
+ src: value,
6895
+ alt: f.label,
6896
+ className: "rounded-xl object-cover ring-1 ring-border",
6897
+ style: { width: sizeStyle.width ?? 128, height: sizeStyle.height ?? 128 }
6898
+ }
6899
+ );
6900
+ case "image-url":
6901
+ return empty ? dash : /* @__PURE__ */ jsx32(
6902
+ "a",
6903
+ {
6904
+ href: value,
6905
+ onClick: (e) => e.preventDefault(),
6906
+ className: "inline-block rounded-xl overflow-hidden ring-1 ring-border hover:ring-primary transition-all",
6907
+ style: { width: sizeStyle.width ?? 128, height: sizeStyle.height ?? 128 },
6908
+ children: /* @__PURE__ */ jsx32("img", { src: value, alt: f.label, className: "w-full h-full object-cover" })
6909
+ }
6910
+ );
6911
+ case "image-url-open-other-tabs":
6912
+ return empty ? dash : /* @__PURE__ */ jsx32(
6913
+ "a",
6914
+ {
6915
+ href: value,
6916
+ target: "_blank",
6917
+ rel: "noopener noreferrer",
6918
+ className: "inline-block rounded-xl overflow-hidden ring-1 ring-border hover:ring-primary transition-all",
6919
+ style: { width: sizeStyle.width ?? 128, height: sizeStyle.height ?? 128 },
6920
+ children: /* @__PURE__ */ jsx32("img", { src: value, alt: f.label, className: "w-full h-full object-cover" })
6921
+ }
6922
+ );
6923
+ case "text-url":
6924
+ return empty ? dash : /* @__PURE__ */ jsx32(
6925
+ "a",
6926
+ {
6927
+ href: value,
6928
+ onClick: (e) => e.preventDefault(),
6929
+ className: "text-sm text-primary underline underline-offset-2 hover:text-primary/80 break-all",
6930
+ style: sizeStyle,
6931
+ children: value
6932
+ }
6933
+ );
6934
+ case "text-url-open-other-tabs":
6935
+ return empty ? dash : /* @__PURE__ */ jsx32(
6936
+ "a",
6937
+ {
6938
+ href: value,
6939
+ target: "_blank",
6940
+ rel: "noopener noreferrer",
6941
+ className: "text-sm text-primary underline underline-offset-2 hover:text-primary/80 break-all",
6942
+ style: sizeStyle,
6943
+ children: value
6944
+ }
6945
+ );
6946
+ case "attachment":
6947
+ return empty ? dash : /* @__PURE__ */ jsxs30(
6948
+ "a",
6949
+ {
6950
+ href: value,
6951
+ target: "_blank",
6952
+ rel: "noopener noreferrer",
6953
+ 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-foreground hover:bg-muted transition-colors",
6954
+ style: sizeStyle,
6955
+ children: [
6956
+ /* @__PURE__ */ jsx32("svg", { className: "h-3.5 w-3.5 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" }) }),
6957
+ String(value).split("/").pop() ?? "Download"
6958
+ ]
6959
+ }
6960
+ );
6961
+ case "repeater": {
6962
+ const rows = Array.isArray(value) ? value : [];
6963
+ if (!rows.length) return dash;
6964
+ const rFields = f.repeaterFields;
6965
+ 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: [
6966
+ /* @__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 }),
6967
+ rFields ? rFields.map((rf) => {
6968
+ const v = row[rf.key];
6969
+ if (rf.type === "image") return /* @__PURE__ */ jsxs30("div", { className: "flex flex-col gap-1", children: [
6970
+ rf.label && /* @__PURE__ */ jsx32("span", { className: "text-[10px] text-muted-foreground", children: rf.label }),
6971
+ v ? /* @__PURE__ */ jsx32("img", { src: v, alt: rf.key, className: "h-10 w-10 rounded-lg object-cover ring-1 ring-border" }) : dash
6972
+ ] }, rf.key);
6973
+ if (rf.type === "attachment") return /* @__PURE__ */ jsxs30("div", { className: "flex flex-col gap-1", children: [
6974
+ rf.label && /* @__PURE__ */ jsx32("span", { className: "text-[10px] text-muted-foreground", children: rf.label }),
6975
+ v ? /* @__PURE__ */ jsxs30(
6976
+ "a",
6977
+ {
6978
+ href: v,
6979
+ target: "_blank",
6980
+ rel: "noopener noreferrer",
6981
+ 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",
6982
+ children: [
6983
+ /* @__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" }) }),
6984
+ String(v).split("/").pop()
6985
+ ]
6986
+ }
6987
+ ) : dash
6988
+ ] }, rf.key);
6989
+ return /* @__PURE__ */ jsxs30("div", { className: "flex flex-col gap-1", children: [
6990
+ rf.label && /* @__PURE__ */ jsx32("span", { className: "text-[10px] text-muted-foreground", children: rf.label }),
6991
+ /* @__PURE__ */ jsx32("span", { className: "text-sm", children: v ?? "\u2014" })
6992
+ ] }, rf.key);
6993
+ }) : (
6994
+ // payload mode: row has { type, key, value }
6995
+ Object.entries(row).map(([k, v]) => /* @__PURE__ */ jsxs30("div", { className: "flex flex-col gap-1", children: [
6996
+ /* @__PURE__ */ jsx32("span", { className: "text-[10px] text-muted-foreground", children: k }),
6997
+ /* @__PURE__ */ jsx32("span", { className: "text-sm", children: String(v) })
6998
+ ] }, k))
6999
+ )
7000
+ ] }, ri)) });
7001
+ }
7002
+ case "checkbox":
7003
+ return /* @__PURE__ */ jsx32(
7004
+ "input",
7005
+ {
7006
+ type: "checkbox",
7007
+ checked: !!value,
7008
+ readOnly: true,
7009
+ className: "h-4 w-4 rounded border-border accent-primary cursor-default",
7010
+ style: sizeStyle
7011
+ }
7012
+ );
7013
+ case "toggle":
7014
+ return /* @__PURE__ */ jsx32(
7015
+ "div",
7016
+ {
7017
+ className: cn(
7018
+ "relative inline-flex shrink-0 rounded-full border-2 border-transparent transition-colors",
7019
+ value ? "bg-primary" : "bg-muted"
7020
+ ),
7021
+ style: { width: sizeStyle.width ?? 36, height: sizeStyle.height ?? 20 },
7022
+ children: /* @__PURE__ */ jsx32(
7023
+ "span",
7024
+ {
7025
+ className: cn(
7026
+ "pointer-events-none inline-block rounded-full bg-white shadow-sm transition-transform"
7027
+ ),
7028
+ style: {
7029
+ width: typeof (sizeStyle.height ?? 20) === "number" ? sizeStyle.height - 4 : 16,
7030
+ height: typeof (sizeStyle.height ?? 20) === "number" ? sizeStyle.height - 4 : 16,
7031
+ transform: value ? `translateX(${typeof (sizeStyle.width ?? 36) === "number" ? sizeStyle.width - (sizeStyle.height ?? 20) : 16}px)` : "translateX(0)"
7032
+ }
7033
+ }
7034
+ )
7035
+ }
7036
+ );
7037
+ default:
7038
+ return empty ? dash : /* @__PURE__ */ jsx32("p", { className: "text-sm text-foreground break-words", style: sizeStyle, children: String(value) });
7039
+ }
7040
+ };
6584
7041
  return /* @__PURE__ */ jsx32(
6585
7042
  ModalShell,
6586
7043
  {
@@ -6588,11 +7045,27 @@ function ViewModal({
6588
7045
  onClose,
6589
7046
  width,
6590
7047
  footer: /* @__PURE__ */ jsx32(Button, { variant: "outline", size: "sm", onClick: onClose, children: "Close" }),
6591
- children: /* @__PURE__ */ jsx32("div", { className: "space-y-3", children: fields.map((f) => /* @__PURE__ */ jsxs30("div", { children: [
6592
- /* @__PURE__ */ jsx32("p", { className: "text-xs font-semibold text-muted-foreground mb-1", children: f.label }),
6593
- f.render ? /* @__PURE__ */ jsx32(Fragment11, { children: f.render(item[f.key], () => {
6594
- }) }) : /* @__PURE__ */ jsx32("p", { className: "text-sm text-foreground break-words", children: item[f.key] === null || item[f.key] === void 0 || item[f.key] === "" ? /* @__PURE__ */ jsx32("span", { className: "text-muted-foreground italic", children: "\u2014" }) : String(item[f.key]) })
6595
- ] }, f.key)) })
7048
+ children: /* @__PURE__ */ jsx32(
7049
+ "div",
7050
+ {
7051
+ className: grid ? "grid gap-4" : "space-y-3",
7052
+ style: grid ? { gridTemplateColumns: `repeat(${grid}, minmax(0, 1fr))` } : void 0,
7053
+ children: fields.map((f) => /* @__PURE__ */ jsxs30(
7054
+ "div",
7055
+ {
7056
+ style: {
7057
+ ...f.colSpan ? { gridColumn: `span ${f.colSpan}` } : {},
7058
+ ...f.rowSpan ? { gridRow: `span ${f.rowSpan}` } : {}
7059
+ },
7060
+ children: [
7061
+ /* @__PURE__ */ jsx32("p", { className: "text-xs font-semibold text-muted-foreground mb-1", children: f.label }),
7062
+ renderViewValue(f, item[f.key])
7063
+ ]
7064
+ },
7065
+ f.key
7066
+ ))
7067
+ }
7068
+ )
6596
7069
  }
6597
7070
  );
6598
7071
  }
@@ -6610,7 +7083,7 @@ function EditModal({
6610
7083
  const [form, setForm] = React28.useState(() => {
6611
7084
  const init = {};
6612
7085
  fields.forEach((f) => {
6613
- init[f.key] = item[f.key] ?? "";
7086
+ init[f.key] = f.type === "file-upload" ? null : item[f.key] ?? "";
6614
7087
  });
6615
7088
  return init;
6616
7089
  });
@@ -6622,6 +7095,7 @@ function EditModal({
6622
7095
  e.preventDefault();
6623
7096
  const errs = {};
6624
7097
  fields.forEach((f) => {
7098
+ if (f.type === "file-upload" && !form[f.key] && item[f.key]) return;
6625
7099
  const msg = validateField(f, form[f.key]);
6626
7100
  if (msg) errs[f.key] = msg;
6627
7101
  });
@@ -6633,7 +7107,30 @@ function EditModal({
6633
7107
  setLoading(true);
6634
7108
  setError(null);
6635
7109
  try {
6636
- await csrfAxios.put(`${baseUrl}/${itemId}/update`, form);
7110
+ const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute("content");
7111
+ if (!csrfToken) throw new Error("[Table] CSRF token not found.");
7112
+ const isFile = (v) => v instanceof File;
7113
+ const isFileArray = (v) => Array.isArray(v) && v.length > 0 && v[0] instanceof File;
7114
+ const hasFiles = Object.values(form).some((v) => isFile(v) || isFileArray(v));
7115
+ let body;
7116
+ if (hasFiles) {
7117
+ const fd = new FormData();
7118
+ fd.append("_method", "PUT");
7119
+ Object.entries(form).forEach(([k, v]) => {
7120
+ if (isFileArray(v)) {
7121
+ v.forEach((f) => fd.append(k, f));
7122
+ } else if (isFile(v)) {
7123
+ fd.append(k, v);
7124
+ } else if (v === null || v === void 0) {
7125
+ } else if (!Array.isArray(v)) {
7126
+ fd.append(k, String(v));
7127
+ }
7128
+ });
7129
+ body = fd;
7130
+ } else {
7131
+ body = form;
7132
+ }
7133
+ await axios3.put(`${baseUrl}/${itemId}/update`, body, { headers: { "X-CSRF-Token": csrfToken } });
6637
7134
  const updated = { ...item, ...form };
6638
7135
  if (notif && (notif.type ?? "toast") === "notification") {
6639
7136
  setBanner(true);
@@ -6654,7 +7151,7 @@ function EditModal({
6654
7151
  title: "Edit Record",
6655
7152
  onClose,
6656
7153
  width,
6657
- footer: /* @__PURE__ */ jsxs30(Fragment11, { children: [
7154
+ footer: /* @__PURE__ */ jsxs30(Fragment12, { children: [
6658
7155
  /* @__PURE__ */ jsx32(Button, { variant: "outline", size: "sm", onClick: onClose, disabled: loading, children: "Cancel" }),
6659
7156
  /* @__PURE__ */ jsxs30(Button, { size: "sm", onClick: handleSubmit, disabled: loading, children: [
6660
7157
  loading && /* @__PURE__ */ jsx32(Loader22, { className: "h-3.5 w-3.5 mr-1.5 animate-spin" }),
@@ -6674,14 +7171,14 @@ function EditModal({
6674
7171
  ),
6675
7172
  notif.action && /* @__PURE__ */ jsx32("div", { children: notif.action })
6676
7173
  ] }),
6677
- fields.map((f) => /* @__PURE__ */ jsxs30(
7174
+ fields.map((f) => /* @__PURE__ */ jsx32(
6678
7175
  "div",
6679
7176
  {
6680
7177
  style: {
6681
7178
  ...f.colSpan ? { gridColumn: `span ${f.colSpan}` } : {},
6682
7179
  ...f.rowSpan ? { gridRow: `span ${f.rowSpan}` } : {}
6683
7180
  },
6684
- children: [
7181
+ children: f.component ? /* @__PURE__ */ jsx32(Fragment12, { children: f.component }) : /* @__PURE__ */ jsxs30(Fragment12, { children: [
6685
7182
  f.type !== "checkbox" && /* @__PURE__ */ jsxs30("label", { className: "block text-xs font-semibold text-muted-foreground mb-1", children: [
6686
7183
  f.label,
6687
7184
  f.required && /* @__PURE__ */ jsx32("span", { className: "text-danger ml-0.5", children: "*" })
@@ -6701,7 +7198,7 @@ function EditModal({
6701
7198
  }
6702
7199
  ),
6703
7200
  fieldErrors[f.key] && /* @__PURE__ */ jsx32("p", { className: "text-xs text-danger mt-1", children: fieldErrors[f.key] })
6704
- ]
7201
+ ] })
6705
7202
  },
6706
7203
  f.key
6707
7204
  )),
@@ -6724,7 +7221,9 @@ function DeleteModal({
6724
7221
  setLoading(true);
6725
7222
  setError(null);
6726
7223
  try {
6727
- await csrfAxios.delete(`${baseUrl}/${itemId}/delete`);
7224
+ const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute("content");
7225
+ if (!csrfToken) throw new Error("[Table] CSRF token not found.");
7226
+ await axios3.delete(`${baseUrl}/${itemId}/delete?csrfToken=${encodeURIComponent(csrfToken)}`, { headers: { "X-CSRF-Token": csrfToken } });
6728
7227
  onSuccess?.(item);
6729
7228
  onClose();
6730
7229
  } catch (err) {
@@ -6739,7 +7238,7 @@ function DeleteModal({
6739
7238
  title: "Confirm Delete",
6740
7239
  onClose,
6741
7240
  width: "lg",
6742
- footer: /* @__PURE__ */ jsxs30(Fragment11, { children: [
7241
+ footer: /* @__PURE__ */ jsxs30(Fragment12, { children: [
6743
7242
  /* @__PURE__ */ jsx32(Button, { variant: "outline", size: "sm", onClick: onClose, disabled: loading, children: "Cancel" }),
6744
7243
  /* @__PURE__ */ jsxs30(Button, { variant: "danger", size: "sm", onClick: handleDelete, disabled: loading, children: [
6745
7244
  loading && /* @__PURE__ */ jsx32(Loader22, { className: "h-3.5 w-3.5 mr-1.5 animate-spin" }),
@@ -6799,26 +7298,89 @@ var BADGE_COLORS = {
6799
7298
  function badgeClass(value) {
6800
7299
  return BADGE_COLORS[value.toLowerCase()] ?? "bg-primary/10 text-primary border-primary/20";
6801
7300
  }
7301
+ function deriveField(key, sample) {
7302
+ const label = key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
7303
+ const base = { key, label };
7304
+ if (typeof sample === "boolean") return { ...base, type: "toggle", viewType: "toggle" };
7305
+ if (typeof sample === "number") return { ...base, inputType: "number" };
7306
+ if (Array.isArray(sample)) {
7307
+ if (sample.length === 0 || typeof sample[0] === "string")
7308
+ return { ...base, type: "tag-input" };
7309
+ return base;
7310
+ }
7311
+ if (typeof sample === "string") {
7312
+ if (/\.(png|jpe?g|gif|webp|svg|avif)(\?.*)?$/i.test(sample))
7313
+ return { ...base, type: "input", viewType: "image" };
7314
+ if (/\.(pdf|docx?|xlsx?|csv|zip|pptx?)(\?.*)?$/i.test(sample))
7315
+ return { ...base, type: "input", viewType: "attachment" };
7316
+ if (/^https?:\/\//.test(sample))
7317
+ return { ...base, type: "input", viewType: "text-url-open-other-tabs" };
7318
+ if (sample.length > 120 || /\n/.test(sample))
7319
+ return { ...base, type: "textarea" };
7320
+ if (/password|secret|token/i.test(key))
7321
+ return { ...base, type: "password" };
7322
+ if (/email/i.test(key))
7323
+ return { ...base, inputType: "email" };
7324
+ if (/color|colour/i.test(key) && /^#[0-9a-f]{3,8}$/i.test(sample))
7325
+ return { ...base, type: "color-picker", viewType: "text" };
7326
+ }
7327
+ return base;
7328
+ }
6802
7329
  function Table({
6803
7330
  data,
6804
7331
  columns,
7332
+ loading,
7333
+ emptyState,
7334
+ error: errorProp,
6805
7335
  searchable = false,
6806
7336
  searchPlaceholder = "Search...",
6807
- pagination = false,
7337
+ searchValue: controlledSearch,
7338
+ onSearchChange,
7339
+ clientPagination = false,
6808
7340
  itemsPerPage = 10,
6809
7341
  selectable = false,
6810
7342
  onBulkDelete,
6811
7343
  idKey = "id",
7344
+ bulkDeleteBaseUrl,
6812
7345
  defaultActions,
6813
7346
  serverPagination,
6814
- className
7347
+ variant = "default",
7348
+ className,
7349
+ onRowClick,
7350
+ onRowDoubleClick,
7351
+ rowClassName,
7352
+ expandable = false,
7353
+ renderExpanded,
7354
+ columnVisibility,
7355
+ onColumnVisibilityChange,
7356
+ columnVisibilityIcon,
7357
+ filterBar,
7358
+ filterableIcon,
7359
+ exportable = false,
7360
+ onExport,
7361
+ virtualized = false,
7362
+ draggable = false,
7363
+ onRowReorder,
7364
+ keyboardNavigation = false
6815
7365
  }) {
6816
7366
  const { toast } = useToast();
6817
- const [search, setSearch] = React28.useState("");
7367
+ const isControlledSearch = controlledSearch !== void 0;
7368
+ const [internalSearch, setInternalSearch] = React28.useState("");
7369
+ const search = isControlledSearch ? controlledSearch : internalSearch;
7370
+ const setSearch = (v) => {
7371
+ if (!isControlledSearch) setInternalSearch(v);
7372
+ onSearchChange?.(v);
7373
+ };
6818
7374
  const [currentPage, setCurrentPage] = React28.useState(1);
6819
7375
  const [selectedIds, setSelectedIds] = React28.useState([]);
6820
7376
  const [sortKey, setSortKey] = React28.useState(null);
6821
7377
  const [sortDir, setSortDir] = React28.useState(null);
7378
+ const [bulkLoading, setBulkLoading] = React28.useState(false);
7379
+ const [bulkConfirm, setBulkConfirm] = React28.useState(null);
7380
+ const [filterBarOpen, setFilterBarOpen] = React28.useState(false);
7381
+ const [expandedIds, setExpandedIds] = React28.useState(/* @__PURE__ */ new Set());
7382
+ const [dragOverId, setDragOverId] = React28.useState(null);
7383
+ const [focusedRowIdx, setFocusedRowIdx] = React28.useState(-1);
6822
7384
  const [viewItem, setViewItem] = React28.useState(null);
6823
7385
  const [editItem, setEditItem] = React28.useState(null);
6824
7386
  const [deleteItem, setDeleteItem] = React28.useState(null);
@@ -6830,15 +7392,17 @@ function Table({
6830
7392
  const safeBaseUrl = defaultActions?.baseUrl.replace(/\/+$/, "") ?? "";
6831
7393
  const autoFields = React28.useMemo(() => {
6832
7394
  if (!tableData.length) return [];
6833
- return Object.keys(tableData[0]).map((k) => ({
6834
- key: k,
6835
- label: k.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())
6836
- }));
7395
+ const row = tableData[0];
7396
+ return Object.keys(row).map((k) => deriveField(k, row[k]));
6837
7397
  }, [tableData]);
6838
7398
  const editFields = defaultActions?.editForm ?? autoFields;
6839
7399
  const viewFields = defaultActions?.viewForm ?? autoFields;
7400
+ const visibleColumns = React28.useMemo(() => {
7401
+ if (!columnVisibility) return columns;
7402
+ return columns.filter((col) => columnVisibility[String(col.key)] !== false);
7403
+ }, [columns, columnVisibility]);
6840
7404
  const allColumns = React28.useMemo(() => {
6841
- if (!defaultActions) return columns;
7405
+ if (!defaultActions) return visibleColumns;
6842
7406
  const actionsCol = {
6843
7407
  key: "__actions__",
6844
7408
  title: "Actions",
@@ -6887,8 +7451,8 @@ function Table({
6887
7451
  ))
6888
7452
  ] })
6889
7453
  };
6890
- return defaultActions.position === "first" ? [actionsCol, ...columns] : [...columns, actionsCol];
6891
- }, [columns, defaultActions]);
7454
+ return defaultActions.position === "first" ? [actionsCol, ...visibleColumns] : [...visibleColumns, actionsCol];
7455
+ }, [visibleColumns, defaultActions]);
6892
7456
  const handleSort = (key) => {
6893
7457
  if (sortKey !== key) {
6894
7458
  setSortKey(key);
@@ -6921,16 +7485,78 @@ function Table({
6921
7485
  const totalPages = Math.max(1, Math.ceil(filteredData.length / itemsPerPage));
6922
7486
  const safePage = Math.min(currentPage, totalPages);
6923
7487
  const paginatedData = React28.useMemo(() => {
6924
- if (!pagination) return filteredData;
7488
+ if (!clientPagination) return filteredData;
6925
7489
  const start = (safePage - 1) * itemsPerPage;
6926
7490
  return filteredData.slice(start, start + itemsPerPage);
6927
- }, [filteredData, pagination, safePage, itemsPerPage]);
7491
+ }, [filteredData, clientPagination, safePage, itemsPerPage]);
6928
7492
  React28.useEffect(() => {
6929
7493
  setCurrentPage(1);
6930
7494
  }, [search]);
7495
+ React28.useEffect(() => {
7496
+ if (!keyboardNavigation) return;
7497
+ const handler = (e) => {
7498
+ if (e.key === "ArrowDown") {
7499
+ e.preventDefault();
7500
+ setFocusedRowIdx((i) => Math.min(i + 1, paginatedData.length - 1));
7501
+ }
7502
+ if (e.key === "ArrowUp") {
7503
+ e.preventDefault();
7504
+ setFocusedRowIdx((i) => Math.max(i - 1, 0));
7505
+ }
7506
+ };
7507
+ window.addEventListener("keydown", handler);
7508
+ return () => window.removeEventListener("keydown", handler);
7509
+ }, [keyboardNavigation, paginatedData.length]);
6931
7510
  const handleSelectAll = (checked) => setSelectedIds(checked ? paginatedData.map((item) => String(item[idKey])) : []);
6932
7511
  const handleSelect = (id, checked) => setSelectedIds((prev) => checked ? [...prev, id] : prev.filter((i) => i !== id));
6933
7512
  const allSelected = paginatedData.length > 0 && selectedIds.length === paginatedData.length;
7513
+ const totalRows = serverPagination ? serverPagination.pagination.total : filteredData.length;
7514
+ const unselectedCount = totalRows - selectedIds.length;
7515
+ const handleSelectAllRecords = () => setSelectedIds(filteredData.map((item) => String(item[idKey])));
7516
+ const handleUnselectAll = () => setSelectedIds([]);
7517
+ const execBulkDeleteSelected = async () => {
7518
+ if (!bulkDeleteBaseUrl || selectedIds.length === 0) {
7519
+ onBulkDelete?.(selectedIds);
7520
+ setSelectedIds([]);
7521
+ return;
7522
+ }
7523
+ setBulkLoading(true);
7524
+ try {
7525
+ const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute("content");
7526
+ if (!csrfToken) throw new Error("[Table] CSRF token not found.");
7527
+ const safeUrl = bulkDeleteBaseUrl.replace(/\/+$/, "");
7528
+ await axios3.delete(`${safeUrl}/delete/${selectedIds.join(",")}/selected`, { headers: { "X-CSRF-Token": csrfToken } });
7529
+ setTableData((prev) => prev.filter((r) => !selectedIds.includes(String(r[idKey]))));
7530
+ onBulkDelete?.(selectedIds);
7531
+ setSelectedIds([]);
7532
+ defaultActions?.onReload?.();
7533
+ toast({ variant: "success", title: "Deleted", description: `${selectedIds.length} record${selectedIds.length !== 1 ? "s" : ""} deleted successfully.` });
7534
+ } catch (err) {
7535
+ const msg = err?.response?.data?.message ?? err.message ?? "Bulk delete failed";
7536
+ toast({ variant: "error", title: "Delete failed", description: msg });
7537
+ } finally {
7538
+ setBulkLoading(false);
7539
+ }
7540
+ };
7541
+ const execDeleteAll = async () => {
7542
+ if (!bulkDeleteBaseUrl) return;
7543
+ setBulkLoading(true);
7544
+ try {
7545
+ const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute("content");
7546
+ if (!csrfToken) throw new Error("[Table] CSRF token not found.");
7547
+ const safeUrl = bulkDeleteBaseUrl.replace(/\/+$/, "");
7548
+ await axios3.delete(`${safeUrl}/delete/all`, { headers: { "X-CSRF-Token": csrfToken } });
7549
+ setTableData([]);
7550
+ setSelectedIds([]);
7551
+ defaultActions?.onReload?.();
7552
+ toast({ variant: "success", title: "Deleted", description: "All records deleted successfully." });
7553
+ } catch (err) {
7554
+ const msg = err?.response?.data?.message ?? err.message ?? "Delete all failed";
7555
+ toast({ variant: "error", title: "Delete failed", description: msg });
7556
+ } finally {
7557
+ setBulkLoading(false);
7558
+ }
7559
+ };
6934
7560
  const pagePills = React28.useMemo(() => {
6935
7561
  if (totalPages <= 5) return Array.from({ length: totalPages }, (_, i) => i + 1);
6936
7562
  if (safePage <= 3) return [1, 2, 3, 4, 5];
@@ -6942,8 +7568,9 @@ function Table({
6942
7568
  if (sortKey !== String(col.key)) return /* @__PURE__ */ jsx32(ChevronsUpDown, { className: "ml-1.5 h-3.5 w-3.5 opacity-40" });
6943
7569
  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" });
6944
7570
  };
6945
- return /* @__PURE__ */ jsxs30(Fragment11, { children: [
7571
+ return /* @__PURE__ */ jsxs30(Fragment12, { children: [
6946
7572
  /* @__PURE__ */ jsxs30("div", { className: cn("w-full space-y-3", className), children: [
7573
+ errorProp && /* @__PURE__ */ jsx32("div", { className: "rounded-xl border border-danger/30 bg-danger/5 px-4 py-3 text-sm text-danger", children: errorProp }),
6947
7574
  /* @__PURE__ */ jsxs30("div", { className: "flex items-center justify-between gap-3 flex-wrap", children: [
6948
7575
  searchable && /* @__PURE__ */ jsxs30("div", { className: "relative w-72", children: [
6949
7576
  /* @__PURE__ */ jsx32(Search5, { className: "absolute text-primary left-3 top-1/2 -translate-y-1/2 h-4 w-4 z-10" }),
@@ -6965,33 +7592,127 @@ function Table({
6965
7592
  }
6966
7593
  )
6967
7594
  ] }),
6968
- /* @__PURE__ */ jsxs30("div", { className: "flex items-center gap-2 ml-auto", children: [
6969
- selectable && onBulkDelete && selectedIds.length > 0 && /* @__PURE__ */ jsxs30(
7595
+ /* @__PURE__ */ jsxs30("div", { className: "flex items-center gap-2 ml-auto flex-wrap", children: [
7596
+ selectable && selectedIds.length > 0 && /* @__PURE__ */ jsxs30(Fragment12, { children: [
7597
+ /* @__PURE__ */ jsxs30(
7598
+ "button",
7599
+ {
7600
+ onClick: handleUnselectAll,
7601
+ disabled: bulkLoading,
7602
+ 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 disabled:opacity-40",
7603
+ children: [
7604
+ /* @__PURE__ */ jsx32(X9, { className: "h-3.5 w-3.5" }),
7605
+ "Unselect all ",
7606
+ selectedIds.length
7607
+ ]
7608
+ }
7609
+ ),
7610
+ unselectedCount > 0 && /* @__PURE__ */ jsxs30(
7611
+ "button",
7612
+ {
7613
+ onClick: handleSelectAllRecords,
7614
+ disabled: bulkLoading,
7615
+ 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 disabled:opacity-40",
7616
+ children: [
7617
+ "Select all ",
7618
+ unselectedCount
7619
+ ]
7620
+ }
7621
+ ),
7622
+ /* @__PURE__ */ jsxs30(
7623
+ "button",
7624
+ {
7625
+ onClick: () => setBulkConfirm("selected"),
7626
+ disabled: bulkLoading,
7627
+ className: "inline-flex items-center gap-1.5 rounded-lg bg-danger/10 border border-danger/20 px-3 py-1.5 text-xs font-medium text-danger hover:bg-danger/20 transition-colors disabled:opacity-40",
7628
+ children: [
7629
+ bulkLoading ? /* @__PURE__ */ jsx32(Loader22, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx32(Trash22, { className: "h-3.5 w-3.5" }),
7630
+ "Delete ",
7631
+ selectedIds.length,
7632
+ " selected"
7633
+ ]
7634
+ }
7635
+ ),
7636
+ bulkDeleteBaseUrl && /* @__PURE__ */ jsxs30(
7637
+ "button",
7638
+ {
7639
+ onClick: () => setBulkConfirm("all"),
7640
+ disabled: bulkLoading,
7641
+ className: "inline-flex items-center gap-1.5 rounded-lg bg-danger/10 border border-danger/20 px-3 py-1.5 text-xs font-medium text-danger hover:bg-danger/20 transition-colors disabled:opacity-40",
7642
+ children: [
7643
+ bulkLoading ? /* @__PURE__ */ jsx32(Loader22, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx32(Trash22, { className: "h-3.5 w-3.5" }),
7644
+ "Delete all"
7645
+ ]
7646
+ }
7647
+ )
7648
+ ] }),
7649
+ filterBar && /* @__PURE__ */ jsx32(
6970
7650
  "button",
6971
7651
  {
6972
- onClick: () => {
6973
- onBulkDelete(selectedIds);
6974
- setSelectedIds([]);
6975
- },
6976
- className: "inline-flex items-center gap-1.5 rounded-lg bg-danger/10 border border-danger/20 px-3 py-1.5 text-xs font-medium text-danger hover:bg-danger/20 transition-colors",
6977
- children: [
6978
- /* @__PURE__ */ jsx32(Trash22, { className: "h-3.5 w-3.5" }),
6979
- "Delete ",
6980
- selectedIds.length,
6981
- " selected"
6982
- ]
7652
+ onClick: () => setFilterBarOpen((o) => !o),
7653
+ className: cn(
7654
+ "inline-flex items-center gap-1.5 rounded-lg border px-3 py-1.5 text-xs font-medium transition-colors",
7655
+ filterBarOpen ? "border-primary bg-primary/10 text-primary hover:bg-primary/20" : "border-border bg-muted/50 text-muted-foreground hover:bg-muted"
7656
+ ),
7657
+ children: filterableIcon ?? "Filter"
6983
7658
  }
6984
7659
  ),
7660
+ exportable && /* @__PURE__ */ jsxs30("div", { className: "relative group", children: [
7661
+ /* @__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" }),
7662
+ /* @__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(
7663
+ "button",
7664
+ {
7665
+ onClick: () => onExport?.(type),
7666
+ className: "px-4 py-2 text-xs text-left hover:bg-muted transition-colors capitalize",
7667
+ children: type.toUpperCase()
7668
+ },
7669
+ type
7670
+ )) })
7671
+ ] }),
7672
+ columnVisibility && onColumnVisibilityChange && /* @__PURE__ */ jsxs30("div", { className: "relative group", children: [
7673
+ /* @__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: columnVisibilityIcon ?? "Columns" }),
7674
+ /* @__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: [
7675
+ /* @__PURE__ */ jsx32(
7676
+ "input",
7677
+ {
7678
+ type: "checkbox",
7679
+ checked: columnVisibility[String(col.key)] !== false,
7680
+ onChange: (e) => onColumnVisibilityChange({ ...columnVisibility, [String(col.key)]: e.target.checked }),
7681
+ className: "accent-primary"
7682
+ }
7683
+ ),
7684
+ col.title
7685
+ ] }, String(col.key))) })
7686
+ ] }),
6985
7687
  /* @__PURE__ */ jsxs30("span", { className: "text-xs text-muted-foreground", children: [
6986
- serverPagination ? serverPagination.pagination.total : filteredData.length,
7688
+ totalRows,
6987
7689
  " ",
6988
- (serverPagination ? serverPagination.pagination.total : filteredData.length) === 1 ? "row" : "rows",
7690
+ totalRows === 1 ? "row" : "rows",
6989
7691
  search && ` \xB7 filtered from ${tableData.length}`
6990
7692
  ] })
6991
7693
  ] })
6992
7694
  ] }),
6993
- /* @__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: [
6994
- /* @__PURE__ */ jsx32("thead", { children: /* @__PURE__ */ jsxs30("tr", { className: "border-b border-border bg-muted/40", children: [
7695
+ filterBar && filterBarOpen && /* @__PURE__ */ jsx32("div", { children: filterBar }),
7696
+ loading && /* @__PURE__ */ jsxs30("div", { className: "flex items-center justify-center py-12 text-muted-foreground gap-2", children: [
7697
+ /* @__PURE__ */ jsx32(Loader22, { className: "h-5 w-5 animate-spin" }),
7698
+ /* @__PURE__ */ jsx32("span", { className: "text-sm", children: "Loading\u2026" })
7699
+ ] }),
7700
+ !loading && /* @__PURE__ */ jsx32("div", { className: cn(
7701
+ variant === "default" && "rounded-xl border border-border overflow-hidden bg-card/50 backdrop-blur-sm shadow-sm",
7702
+ variant === "zebra" && "rounded-xl border border-border overflow-hidden bg-card/50 backdrop-blur-sm shadow-sm",
7703
+ variant === "card" && "space-y-2",
7704
+ variant === "glass" && "rounded-2xl overflow-hidden border border-white/10 bg-background/30 backdrop-blur-xl shadow-xl",
7705
+ variant === "soft" && "rounded-2xl overflow-hidden bg-card",
7706
+ variant === "soft" && "[box-shadow:6px_6px_12px_hsl(var(--foreground)/0.07),-6px_-6px_12px_hsl(var(--background)/0.8)]",
7707
+ virtualized && "max-h-[520px] overflow-y-auto"
7708
+ ), 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: [
7709
+ /* @__PURE__ */ jsx32("thead", { children: /* @__PURE__ */ jsxs30("tr", { className: cn(
7710
+ variant === "default" && "border-b border-border bg-muted/40",
7711
+ variant === "zebra" && "border-b border-border bg-muted/40",
7712
+ variant === "card" && "[&>th]:bg-transparent",
7713
+ variant === "glass" && "border-b border-white/10 bg-white/5",
7714
+ variant === "soft" && "border-b-0 bg-muted/30"
7715
+ ), children: [
6995
7716
  selectable && /* @__PURE__ */ jsx32("th", { className: "h-11 w-[46px] px-4 text-left align-middle", children: /* @__PURE__ */ jsx32(
6996
7717
  Checkbox,
6997
7718
  {
@@ -6999,13 +7720,16 @@ function Table({
6999
7720
  onChange: (e) => handleSelectAll(e.target.checked)
7000
7721
  }
7001
7722
  ) }),
7723
+ expandable && /* @__PURE__ */ jsx32("th", { className: "h-11 w-8" }),
7002
7724
  allColumns.map((col, ci) => /* @__PURE__ */ jsx32(
7003
7725
  "th",
7004
7726
  {
7005
7727
  onClick: () => col.sortable && handleSort(String(col.key)),
7006
7728
  className: cn(
7007
7729
  "h-11 px-4 text-left align-middle text-xs font-semibold uppercase tracking-wider text-muted-foreground select-none whitespace-nowrap",
7008
- col.sortable && "cursor-pointer hover:text-foreground transition-colors"
7730
+ col.sortable && "cursor-pointer hover:text-foreground transition-colors",
7731
+ variant === "glass" && "text-foreground/70",
7732
+ variant === "soft" && "text-muted-foreground/80"
7009
7733
  ),
7010
7734
  children: /* @__PURE__ */ jsxs30("span", { className: "inline-flex items-center", children: [
7011
7735
  col.title,
@@ -7018,9 +7742,9 @@ function Table({
7018
7742
  /* @__PURE__ */ jsx32("tbody", { children: paginatedData.length === 0 ? /* @__PURE__ */ jsx32("tr", { children: /* @__PURE__ */ jsx32(
7019
7743
  "td",
7020
7744
  {
7021
- colSpan: allColumns.length + (selectable ? 1 : 0),
7745
+ colSpan: allColumns.length + (selectable ? 1 : 0) + (expandable ? 1 : 0),
7022
7746
  className: "h-32 text-center align-middle",
7023
- children: /* @__PURE__ */ jsxs30("div", { className: "flex flex-col items-center gap-1 text-muted-foreground", children: [
7747
+ children: emptyState ?? /* @__PURE__ */ jsxs30("div", { className: "flex flex-col items-center gap-1 text-muted-foreground", children: [
7024
7748
  /* @__PURE__ */ jsx32(Search5, { className: "h-8 w-8 opacity-20" }),
7025
7749
  /* @__PURE__ */ jsx32("span", { className: "text-sm", children: "No results found" }),
7026
7750
  search && /* @__PURE__ */ jsx32("button", { onClick: () => setSearch(""), className: "text-xs text-primary hover:underline", children: "Clear search" })
@@ -7029,85 +7753,168 @@ function Table({
7029
7753
  ) }) : paginatedData.map((item, i) => {
7030
7754
  const id = String(item[idKey] || i);
7031
7755
  const isSelected = selectedIds.includes(id);
7032
- return /* @__PURE__ */ jsxs30(
7033
- "tr",
7034
- {
7035
- className: cn(
7036
- "border-b border-border/60 transition-colors last:border-0",
7037
- isSelected ? "bg-primary/5 hover:bg-primary/8" : "hover:bg-muted/30"
7038
- ),
7039
- children: [
7040
- selectable && /* @__PURE__ */ jsx32("td", { className: "px-4 py-3 align-middle", children: /* @__PURE__ */ jsx32(
7041
- Checkbox,
7042
- {
7043
- checked: isSelected,
7044
- onChange: (e) => handleSelect(id, e.target.checked)
7045
- }
7046
- ) }),
7047
- 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(
7048
- "img",
7049
- {
7050
- src: item[col.key],
7051
- alt: item[col.key],
7052
- className: "h-9 w-9 rounded-lg object-cover ring-1 ring-border"
7053
- }
7054
- ) : col.type === "badge" ? /* @__PURE__ */ jsxs30("span", { className: cn(
7055
- "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-medium",
7056
- badgeClass(String(item[col.key]))
7057
- ), children: [
7058
- /* @__PURE__ */ jsx32("span", { className: cn(
7059
- "mr-1.5 h-1.5 w-1.5 rounded-full",
7060
- 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"
7756
+ const isExpanded = expandedIds.has(id);
7757
+ const isFocused = keyboardNavigation && focusedRowIdx === i;
7758
+ return /* @__PURE__ */ jsxs30(React28.Fragment, { children: [
7759
+ /* @__PURE__ */ jsxs30(
7760
+ "tr",
7761
+ {
7762
+ draggable,
7763
+ tabIndex: keyboardNavigation ? 0 : void 0,
7764
+ onDragStart: draggable ? (e) => {
7765
+ e.dataTransfer.setData("text/plain", id);
7766
+ } : void 0,
7767
+ onDragOver: draggable ? (e) => {
7768
+ e.preventDefault();
7769
+ setDragOverId(id);
7770
+ } : void 0,
7771
+ onDragLeave: draggable ? () => setDragOverId(null) : void 0,
7772
+ onDrop: draggable ? (e) => {
7773
+ e.preventDefault();
7774
+ setDragOverId(null);
7775
+ const fromId = e.dataTransfer.getData("text/plain");
7776
+ if (fromId === id) return;
7777
+ setTableData((prev) => {
7778
+ const fromIdx = prev.findIndex((r) => String(r[idKey] || "") === fromId);
7779
+ const toIdx = prev.findIndex((r) => String(r[idKey] || "") === id);
7780
+ if (fromIdx < 0 || toIdx < 0) return prev;
7781
+ const next = [...prev];
7782
+ const [moved] = next.splice(fromIdx, 1);
7783
+ next.splice(toIdx, 0, moved);
7784
+ onRowReorder?.(next);
7785
+ return next;
7786
+ });
7787
+ } : void 0,
7788
+ onClick: () => {
7789
+ if (expandable) setExpandedIds((prev) => {
7790
+ const s = new Set(prev);
7791
+ s.has(id) ? s.delete(id) : s.add(id);
7792
+ return s;
7793
+ });
7794
+ onRowClick?.(item);
7795
+ if (keyboardNavigation) setFocusedRowIdx(i);
7796
+ },
7797
+ onDoubleClick: () => onRowDoubleClick?.(item),
7798
+ className: cn(
7799
+ // default
7800
+ variant === "default" && "border-b border-border/60 transition-colors last:border-0",
7801
+ variant === "default" && (isSelected ? "bg-primary/5 hover:bg-primary/8" : "hover:bg-muted/30"),
7802
+ // zebra
7803
+ variant === "zebra" && "border-b border-border/40 transition-colors last:border-0",
7804
+ variant === "zebra" && (isSelected ? "bg-primary/8" : i % 2 === 0 ? "bg-card" : "bg-muted/40"),
7805
+ variant === "zebra" && !isSelected && "hover:bg-primary/5",
7806
+ // card
7807
+ variant === "card" && "rounded-xl border border-border bg-card shadow-sm transition-all hover:shadow-md hover:-translate-y-px",
7808
+ variant === "card" && (isSelected ? "border-primary/50 bg-primary/5" : ""),
7809
+ variant === "card" && "[&>td:first-child]:rounded-l-xl [&>td:last-child]:rounded-r-xl",
7810
+ // glass
7811
+ variant === "glass" && "border-b border-white/8 transition-colors last:border-0",
7812
+ variant === "glass" && (isSelected ? "bg-primary/15 hover:bg-primary/20" : "hover:bg-white/5"),
7813
+ // soft
7814
+ variant === "soft" && "transition-all",
7815
+ 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"),
7816
+ variant === "soft" && "border-b border-border/30 last:border-0",
7817
+ (onRowClick || onRowDoubleClick || expandable) && "cursor-pointer",
7818
+ draggable && dragOverId === id && "ring-2 ring-inset ring-primary/40",
7819
+ isFocused && "ring-2 ring-inset ring-ring",
7820
+ rowClassName?.(item)
7821
+ ),
7822
+ children: [
7823
+ selectable && /* @__PURE__ */ jsx32("td", { className: "px-4 py-3 align-middle", children: /* @__PURE__ */ jsx32(
7824
+ Checkbox,
7825
+ {
7826
+ checked: isSelected,
7827
+ onChange: (e) => handleSelect(id, e.target.checked)
7828
+ }
7061
7829
  ) }),
7062
- item[col.key]
7063
- ] }) : 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(
7064
- "select",
7065
- {
7066
- value: item[col.key],
7067
- onChange: (e) => col.onChange?.(item, e.target.value),
7068
- 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",
7069
- children: (col.selectOptions ?? []).map((opt) => /* @__PURE__ */ jsx32("option", { value: opt, children: opt }, opt))
7070
- }
7071
- ) : col.type === "toggle" ? /* @__PURE__ */ jsx32(
7072
- "button",
7073
- {
7074
- role: "switch",
7075
- "aria-checked": !!item[col.key],
7076
- onClick: () => col.onChange?.(item, !item[col.key]),
7077
- className: cn(
7078
- "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",
7079
- item[col.key] ? "bg-primary" : "bg-muted"
7080
- ),
7081
- children: /* @__PURE__ */ jsx32("span", { className: cn(
7082
- "pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow-sm transition-transform",
7083
- item[col.key] ? "translate-x-4" : "translate-x-0"
7084
- ) })
7085
- }
7086
- ) : col.type === "color" ? /* @__PURE__ */ jsxs30("div", { className: "flex items-center gap-2", children: [
7087
- /* @__PURE__ */ jsx32(
7088
- "input",
7830
+ 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") }) }),
7831
+ 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(
7832
+ "img",
7833
+ {
7834
+ src: item[col.key],
7835
+ alt: item[col.key],
7836
+ className: "h-9 w-9 rounded-lg object-cover ring-1 ring-border"
7837
+ }
7838
+ ) : col.type === "badge" ? /* @__PURE__ */ jsxs30("span", { className: cn(
7839
+ "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-medium",
7840
+ badgeClass(String(item[col.key]))
7841
+ ), children: [
7842
+ /* @__PURE__ */ jsx32("span", { className: cn(
7843
+ "mr-1.5 h-1.5 w-1.5 rounded-full",
7844
+ 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"
7845
+ ) }),
7846
+ item[col.key]
7847
+ ] }) : 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(
7848
+ "select",
7089
7849
  {
7090
- type: "color",
7091
- value: item[col.key] || "#000000",
7850
+ value: item[col.key],
7092
7851
  onChange: (e) => col.onChange?.(item, e.target.value),
7093
- className: "h-7 w-7 cursor-pointer rounded border border-border bg-transparent p-0.5"
7852
+ 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",
7853
+ children: (col.selectOptions ?? []).map((opt) => /* @__PURE__ */ jsx32("option", { value: opt, children: opt }, opt))
7094
7854
  }
7095
- ),
7096
- /* @__PURE__ */ jsx32("span", { className: "text-xs text-muted-foreground font-mono", children: item[col.key] })
7097
- ] }) : col.type === "checkbox" ? /* @__PURE__ */ jsx32(
7098
- Checkbox,
7099
- {
7100
- checked: !!item[col.key],
7101
- onChange: (e) => col.onChange?.(item, e.target.checked)
7102
- }
7103
- ) : /* @__PURE__ */ jsx32("span", { className: "text-foreground/90", children: item[col.key] }) }, `${String(col.key)}-${ci}`))
7104
- ]
7105
- },
7106
- id
7107
- );
7855
+ ) : col.type === "toggle" ? /* @__PURE__ */ jsx32(
7856
+ "button",
7857
+ {
7858
+ role: "switch",
7859
+ "aria-checked": !!item[col.key],
7860
+ onClick: () => col.onChange?.(item, !item[col.key]),
7861
+ className: cn(
7862
+ "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",
7863
+ item[col.key] ? "bg-primary" : "bg-muted"
7864
+ ),
7865
+ children: /* @__PURE__ */ jsx32("span", { className: cn(
7866
+ "pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow-sm transition-transform",
7867
+ item[col.key] ? "translate-x-4" : "translate-x-0"
7868
+ ) })
7869
+ }
7870
+ ) : col.type === "color" ? /* @__PURE__ */ jsxs30("div", { className: "flex items-center gap-2", children: [
7871
+ /* @__PURE__ */ jsx32(
7872
+ "input",
7873
+ {
7874
+ type: "color",
7875
+ value: item[col.key] || "#000000",
7876
+ onChange: (e) => col.onChange?.(item, e.target.value),
7877
+ className: "h-7 w-7 cursor-pointer rounded border border-border bg-transparent p-0.5"
7878
+ }
7879
+ ),
7880
+ /* @__PURE__ */ jsx32("span", { className: "text-xs text-muted-foreground font-mono", children: item[col.key] })
7881
+ ] }) : col.type === "checkbox" ? /* @__PURE__ */ jsx32(
7882
+ Checkbox,
7883
+ {
7884
+ checked: !!item[col.key],
7885
+ onChange: (e) => col.onChange?.(item, e.target.checked)
7886
+ }
7887
+ ) : col.type === "text-url" ? (() => {
7888
+ const href = col.redirect ? typeof col.redirect === "function" ? col.redirect(item) : col.redirect : String(item[col.key] ?? "");
7889
+ const colorMap = {
7890
+ primary: "var(--primary)",
7891
+ info: "var(--info)",
7892
+ success: "var(--success)",
7893
+ warning: "var(--warning)",
7894
+ danger: "var(--danger)"
7895
+ };
7896
+ const underline = col.underlineColor ? colorMap[col.underlineColor] ?? col.underlineColor : "var(--primary)";
7897
+ return /* @__PURE__ */ jsx32(
7898
+ "a",
7899
+ {
7900
+ href,
7901
+ target: col.openNewTab ? "_blank" : void 0,
7902
+ rel: col.openNewTab ? "noopener noreferrer" : void 0,
7903
+ style: { textDecorationColor: underline },
7904
+ className: "text-sm underline underline-offset-2 hover:opacity-75 transition-opacity break-all",
7905
+ onClick: col.openNewTab ? void 0 : (e) => e.preventDefault(),
7906
+ children: item[col.key]
7907
+ }
7908
+ );
7909
+ })() : /* @__PURE__ */ jsx32("span", { className: "text-foreground/90", children: item[col.key] }) }, `${String(col.key)}-${ci}`))
7910
+ ]
7911
+ }
7912
+ ),
7913
+ 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) }) })
7914
+ ] }, id);
7108
7915
  }) })
7109
7916
  ] }) }) }),
7110
- pagination && !serverPagination && totalPages > 1 && /* @__PURE__ */ jsxs30("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
7917
+ clientPagination && !serverPagination && totalPages > 1 && /* @__PURE__ */ jsxs30("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
7111
7918
  /* @__PURE__ */ jsxs30("span", { className: "text-xs text-muted-foreground", children: [
7112
7919
  "Showing ",
7113
7920
  (safePage - 1) * itemsPerPage + 1,
@@ -7126,7 +7933,7 @@ function Table({
7126
7933
  children: /* @__PURE__ */ jsx32(ChevronLeft6, { className: "h-4 w-4" })
7127
7934
  }
7128
7935
  ),
7129
- pagePills[0] > 1 && /* @__PURE__ */ jsxs30(Fragment11, { children: [
7936
+ pagePills[0] > 1 && /* @__PURE__ */ jsxs30(Fragment12, { children: [
7130
7937
  /* @__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" }),
7131
7938
  pagePills[0] > 2 && /* @__PURE__ */ jsx32("span", { className: "px-1 text-muted-foreground text-xs", children: "\u2026" })
7132
7939
  ] }),
@@ -7142,7 +7949,7 @@ function Table({
7142
7949
  },
7143
7950
  p
7144
7951
  )),
7145
- pagePills[pagePills.length - 1] < totalPages && /* @__PURE__ */ jsxs30(Fragment11, { children: [
7952
+ pagePills[pagePills.length - 1] < totalPages && /* @__PURE__ */ jsxs30(Fragment12, { children: [
7146
7953
  pagePills[pagePills.length - 1] < totalPages - 1 && /* @__PURE__ */ jsx32("span", { className: "px-1 text-muted-foreground text-xs", children: "\u2026" }),
7147
7954
  /* @__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 })
7148
7955
  ] }),
@@ -7158,8 +7965,8 @@ function Table({
7158
7965
  ] })
7159
7966
  ] }),
7160
7967
  serverPagination && (() => {
7161
- const { pagination: pagination2, currentPage: cp, goToPage } = serverPagination;
7162
- const totalServerPages = pagination2.last_page ?? Math.ceil(pagination2.total / pagination2.per_page);
7968
+ const { pagination, currentPage: cp, goToPage } = serverPagination;
7969
+ const totalServerPages = pagination.last_page ?? Math.ceil(pagination.total / pagination.per_page);
7163
7970
  const pills = [];
7164
7971
  if (totalServerPages <= 7) {
7165
7972
  for (let i = 1; i <= totalServerPages; i++) pills.push(i);
@@ -7172,7 +7979,7 @@ function Table({
7172
7979
  }
7173
7980
  return /* @__PURE__ */ jsxs30("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
7174
7981
  /* @__PURE__ */ jsxs30("span", { className: "text-xs text-muted-foreground", children: [
7175
- pagination2.total,
7982
+ pagination.total,
7176
7983
  " total rows \xB7 page ",
7177
7984
  cp,
7178
7985
  " of ",
@@ -7183,7 +7990,7 @@ function Table({
7183
7990
  "button",
7184
7991
  {
7185
7992
  onClick: () => goToPage(cp - 1),
7186
- disabled: !pagination2.prev_page_url,
7993
+ disabled: !pagination.prev_page_url,
7187
7994
  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",
7188
7995
  children: /* @__PURE__ */ jsx32(ChevronLeft6, { className: "h-4 w-4" })
7189
7996
  }
@@ -7206,7 +8013,7 @@ function Table({
7206
8013
  "button",
7207
8014
  {
7208
8015
  onClick: () => goToPage(cp + 1),
7209
- disabled: !pagination2.next_page_url,
8016
+ disabled: !pagination.next_page_url,
7210
8017
  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",
7211
8018
  children: /* @__PURE__ */ jsx32(ChevronRight8, { className: "h-4 w-4" })
7212
8019
  }
@@ -7221,6 +8028,7 @@ function Table({
7221
8028
  item: viewItem,
7222
8029
  fields: viewFields,
7223
8030
  width: defaultActions.modalWidth,
8031
+ grid: defaultActions.viewFormGrid,
7224
8032
  onClose: () => setViewItem(null)
7225
8033
  }
7226
8034
  ),
@@ -7278,12 +8086,43 @@ function Table({
7278
8086
  }
7279
8087
  }
7280
8088
  }
8089
+ ),
8090
+ bulkConfirm && createPortal3(
8091
+ /* @__PURE__ */ jsx32(
8092
+ "div",
8093
+ {
8094
+ className: "fixed inset-0 z-50 flex items-center justify-center p-4",
8095
+ style: { background: "rgba(0,0,0,0.5)" },
8096
+ onMouseDown: (e) => {
8097
+ if (e.target === e.currentTarget) setBulkConfirm(null);
8098
+ },
8099
+ children: /* @__PURE__ */ jsxs30("div", { className: "relative w-full max-w-md rounded-2xl border border-border bg-card shadow-2xl flex flex-col", children: [
8100
+ /* @__PURE__ */ jsxs30("div", { className: "flex items-center justify-between px-6 py-4 border-b border-border", children: [
8101
+ /* @__PURE__ */ jsx32("h2", { className: "text-base font-semibold", children: "Confirm Delete" }),
8102
+ /* @__PURE__ */ jsx32("button", { onClick: () => setBulkConfirm(null), className: "text-muted-foreground hover:text-foreground transition-colors", children: /* @__PURE__ */ jsx32(X9, { className: "h-4 w-4" }) })
8103
+ ] }),
8104
+ /* @__PURE__ */ jsx32("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx32("p", { className: "text-sm text-muted-foreground", children: bulkConfirm === "selected" ? `Are you sure you want to delete ${selectedIds.length} selected record${selectedIds.length !== 1 ? "s" : ""}? This action cannot be undone.` : "Are you sure you want to delete all records? This action cannot be undone." }) }),
8105
+ /* @__PURE__ */ jsxs30("div", { className: "px-6 py-4 border-t border-border flex justify-end gap-2", children: [
8106
+ /* @__PURE__ */ jsx32(Button, { variant: "outline", size: "sm", onClick: () => setBulkConfirm(null), disabled: bulkLoading, children: "Cancel" }),
8107
+ /* @__PURE__ */ jsxs30(Button, { variant: "danger", size: "sm", disabled: bulkLoading, onClick: async () => {
8108
+ if (bulkConfirm === "selected") await execBulkDeleteSelected();
8109
+ else await execDeleteAll();
8110
+ setBulkConfirm(null);
8111
+ }, children: [
8112
+ bulkLoading && /* @__PURE__ */ jsx32(Loader22, { className: "h-3.5 w-3.5 mr-1.5 animate-spin" }),
8113
+ bulkLoading ? "Deleting\u2026" : "Delete"
8114
+ ] })
8115
+ ] })
8116
+ ] })
8117
+ }
8118
+ ),
8119
+ document.body
7281
8120
  )
7282
8121
  ] });
7283
8122
  }
7284
8123
 
7285
8124
  // src/components/ui/data-grid.tsx
7286
- import { Fragment as Fragment12, jsx as jsx33, jsxs as jsxs31 } from "react/jsx-runtime";
8125
+ import { Fragment as Fragment13, jsx as jsx33, jsxs as jsxs31 } from "react/jsx-runtime";
7287
8126
  function useServerDataGrid({ url, params, encrypt, key, decryptPayloadLog, columnOverrides }) {
7288
8127
  const [data, setData] = React29.useState([]);
7289
8128
  const [columns, setColumns] = React29.useState([]);
@@ -7411,7 +8250,7 @@ function DGModalShell({ title, onClose, children, footer, width = "lg" }) {
7411
8250
  );
7412
8251
  }
7413
8252
  function DGFieldRenderer({ field, value, onChange }) {
7414
- if (field.render) return /* @__PURE__ */ jsx33(Fragment12, { children: field.render(value, onChange) });
8253
+ if (field.render) return /* @__PURE__ */ jsx33(Fragment13, { children: field.render(value, onChange) });
7415
8254
  const toLabelValue = (o) => {
7416
8255
  if (typeof o === "string") return { label: o, value: o };
7417
8256
  if (Array.isArray(o)) return { label: o[0], value: o[1] };
@@ -7491,7 +8330,7 @@ function DGViewModal({ item, fields, onClose, width }) {
7491
8330
  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" }),
7492
8331
  children: /* @__PURE__ */ jsx33("div", { className: "space-y-3", children: fields.map((f) => /* @__PURE__ */ jsxs31("div", { children: [
7493
8332
  /* @__PURE__ */ jsx33("p", { className: "text-xs font-semibold text-muted-foreground mb-1", children: f.label }),
7494
- f.render ? /* @__PURE__ */ jsx33(Fragment12, { children: f.render(item[f.key], () => {
8333
+ f.render ? /* @__PURE__ */ jsx33(Fragment13, { children: f.render(item[f.key], () => {
7495
8334
  }) }) : /* @__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]) })
7496
8335
  ] }, f.key)) })
7497
8336
  }
@@ -7555,7 +8394,7 @@ function DGEditModal({
7555
8394
  title: "Edit Record",
7556
8395
  onClose,
7557
8396
  width,
7558
- footer: /* @__PURE__ */ jsxs31(Fragment12, { children: [
8397
+ footer: /* @__PURE__ */ jsxs31(Fragment13, { children: [
7559
8398
  /* @__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" }),
7560
8399
  /* @__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: [
7561
8400
  loading && /* @__PURE__ */ jsx33(Loader23, { className: "h-3.5 w-3.5 animate-spin" }),
@@ -7615,7 +8454,7 @@ function DGDeleteModal({
7615
8454
  title: "Confirm Delete",
7616
8455
  onClose,
7617
8456
  width: "lg",
7618
- footer: /* @__PURE__ */ jsxs31(Fragment12, { children: [
8457
+ footer: /* @__PURE__ */ jsxs31(Fragment13, { children: [
7619
8458
  /* @__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" }),
7620
8459
  /* @__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: [
7621
8460
  loading && /* @__PURE__ */ jsx33(Loader23, { className: "h-3.5 w-3.5 animate-spin" }),
@@ -8547,7 +9386,7 @@ function KanbanBoard({ columns: controlled, onChange, onAddCard, className }) {
8547
9386
  import * as React33 from "react";
8548
9387
  import { MapContainer, TileLayer, Marker, Popup, Polyline, useMap, CircleMarker } from "react-leaflet";
8549
9388
  import L from "leaflet";
8550
- import { Fragment as Fragment14, jsx as jsx39, jsxs as jsxs35 } from "react/jsx-runtime";
9389
+ import { Fragment as Fragment15, jsx as jsx39, jsxs as jsxs35 } from "react/jsx-runtime";
8551
9390
  L.Icon.Default.mergeOptions({
8552
9391
  iconUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png",
8553
9392
  iconRetinaUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png",
@@ -8653,7 +9492,7 @@ function RouteLayer({ routes }) {
8653
9492
  });
8654
9493
  }, [routes]);
8655
9494
  if (loading) return null;
8656
- return /* @__PURE__ */ jsx39(Fragment14, { children: resolved.map((r, i) => {
9495
+ return /* @__PURE__ */ jsx39(Fragment15, { children: resolved.map((r, i) => {
8657
9496
  const color = r.route.color ?? (r.route.routeType === "walk" ? "#22c55e" : "#6366f1");
8658
9497
  const isDrive = (r.route.routeType ?? "drive") === "drive";
8659
9498
  const midIdx = Math.floor(r.coords.length / 2);
@@ -8693,7 +9532,7 @@ function RouteLayer({ routes }) {
8693
9532
  zIndexOffset: 100,
8694
9533
  children: /* @__PURE__ */ jsx39(Popup, { children: /* @__PURE__ */ jsxs35("div", { className: "text-xs space-y-0.5 min-w-[120px]", children: [
8695
9534
  /* @__PURE__ */ jsx39("p", { className: "font-semibold", children: r.route.label ? `${r.route.label} \u2014 Start` : "Start" }),
8696
- r.distance > 0 && /* @__PURE__ */ jsxs35(Fragment14, { children: [
9535
+ r.distance > 0 && /* @__PURE__ */ jsxs35(Fragment15, { children: [
8697
9536
  /* @__PURE__ */ jsx39("p", { className: "text-muted-foreground", children: fmtDistance(r.distance) }),
8698
9537
  /* @__PURE__ */ jsxs35("p", { className: "text-muted-foreground", children: [
8699
9538
  fmtDuration(r.duration),
@@ -8712,7 +9551,7 @@ function RouteLayer({ routes }) {
8712
9551
  zIndexOffset: 100,
8713
9552
  children: /* @__PURE__ */ jsx39(Popup, { children: /* @__PURE__ */ jsxs35("div", { className: "text-xs space-y-0.5 min-w-[120px]", children: [
8714
9553
  /* @__PURE__ */ jsx39("p", { className: "font-semibold", children: r.route.label ? `${r.route.label} \u2014 End` : "End" }),
8715
- r.distance > 0 && /* @__PURE__ */ jsxs35(Fragment14, { children: [
9554
+ r.distance > 0 && /* @__PURE__ */ jsxs35(Fragment15, { children: [
8716
9555
  /* @__PURE__ */ jsx39("p", { className: "text-muted-foreground", children: fmtDistance(r.distance) }),
8717
9556
  /* @__PURE__ */ jsxs35("p", { className: "text-muted-foreground", children: [
8718
9557
  fmtDuration(r.duration),
@@ -8827,7 +9666,7 @@ function ClusterLayer({ markers, variant, onMarkerClick }) {
8827
9666
  });
8828
9667
  return groups;
8829
9668
  }, [markers, zoom, map]);
8830
- return /* @__PURE__ */ jsx39(Fragment14, { children: clusters.map((g, gi) => {
9669
+ return /* @__PURE__ */ jsx39(Fragment15, { children: clusters.map((g, gi) => {
8831
9670
  if (g.items.length === 1) {
8832
9671
  const m = g.items[0];
8833
9672
  const color2 = resolveColor(m.color);
@@ -9696,7 +10535,7 @@ function Modal({
9696
10535
  // src/components/ui/modal-variants.tsx
9697
10536
  import * as React36 from "react";
9698
10537
  import { AlertTriangle as AlertTriangle3, CheckCircle as CheckCircle2, Trash2 as Trash23, X as X13 } from "lucide-react";
9699
- import { Fragment as Fragment15, jsx as jsx42, jsxs as jsxs38 } from "react/jsx-runtime";
10538
+ import { Fragment as Fragment16, jsx as jsx42, jsxs as jsxs38 } from "react/jsx-runtime";
9700
10539
  function ModalBase({
9701
10540
  isOpen,
9702
10541
  onClose,
@@ -9847,7 +10686,7 @@ function ModalWithForms({
9847
10686
  onSubmit(values);
9848
10687
  }
9849
10688
  function renderField(field) {
9850
- if (field.render) return /* @__PURE__ */ jsx42(Fragment15, { children: field.render(values[field.name], (v) => handleChange(field.name, v)) });
10689
+ if (field.render) return /* @__PURE__ */ jsx42(Fragment16, { children: field.render(values[field.name], (v) => handleChange(field.name, v)) });
9851
10690
  const strOptions = (field.options ?? []).map(
9852
10691
  (o) => typeof o === "string" ? { label: o, value: o } : o
9853
10692
  );
@@ -10195,7 +11034,7 @@ import { PanelLeftClose, PanelLeftOpen, Sun as Sun3, Moon as Moon2, Loader2 as L
10195
11034
  // src/components/ui/tooltip.tsx
10196
11035
  import * as React38 from "react";
10197
11036
  import * as ReactDOM2 from "react-dom";
10198
- import { Fragment as Fragment16, jsx as jsx44, jsxs as jsxs40 } from "react/jsx-runtime";
11037
+ import { Fragment as Fragment17, jsx as jsx44, jsxs as jsxs40 } from "react/jsx-runtime";
10199
11038
  function Tooltip({
10200
11039
  content,
10201
11040
  children,
@@ -10206,7 +11045,7 @@ function Tooltip({
10206
11045
  const [visible, setVisible] = React38.useState(false);
10207
11046
  const [coords, setCoords] = React38.useState({ top: 0, left: 0 });
10208
11047
  const ref = React38.useRef(null);
10209
- if (!enabled) return /* @__PURE__ */ jsx44(Fragment16, { children });
11048
+ if (!enabled) return /* @__PURE__ */ jsx44(Fragment17, { children });
10210
11049
  function calcCoords() {
10211
11050
  const r = ref.current?.getBoundingClientRect();
10212
11051
  if (!r) return;
@@ -10803,7 +11642,7 @@ var useTheme = () => {
10803
11642
  };
10804
11643
 
10805
11644
  // src/components/ui/panel.tsx
10806
- import { Fragment as Fragment17, jsx as jsx46, jsxs as jsxs41 } from "react/jsx-runtime";
11645
+ import { Fragment as Fragment18, jsx as jsx46, jsxs as jsxs41 } from "react/jsx-runtime";
10807
11646
  var PanelCollapsedContext = React40.createContext(false);
10808
11647
  var PanelGroupsContext = React40.createContext({ expandedGroups: /* @__PURE__ */ new Set(), onGroupToggle: () => {
10809
11648
  } });
@@ -10965,7 +11804,7 @@ function Panel({
10965
11804
  "shrink-0 border-b border-border",
10966
11805
  effectiveCollapsed ? "flex items-center justify-center py-3" : "flex items-center gap-2 px-4 py-3"
10967
11806
  ),
10968
- 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: [
11807
+ 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: [
10969
11808
  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 }),
10970
11809
  sidebarBrand.title && /* @__PURE__ */ jsx46("span", { className: "flex-1 truncate text-sm font-semibold", children: sidebarBrand.title }),
10971
11810
  sidebarBrand.trailing && /* @__PURE__ */ jsx46("span", { className: "shrink-0", children: sidebarBrand.trailing })
@@ -12044,13 +12883,13 @@ function Timeline({ items, align = "left", className }) {
12044
12883
 
12045
12884
  // src/components/ui/tree-view.tsx
12046
12885
  import * as React48 from "react";
12047
- import { ChevronRight as ChevronRight11, Folder, FolderOpen, File } from "lucide-react";
12886
+ import { ChevronRight as ChevronRight11, Folder, FolderOpen, File as File2 } from "lucide-react";
12048
12887
  import { jsx as jsx60, jsxs as jsxs53 } from "react/jsx-runtime";
12049
12888
  function TreeNodeItem({ node, depth, selected, expanded, onToggleExpand, onSelect, multiple }) {
12050
12889
  const hasChildren = !!node.children?.length;
12051
12890
  const isExpanded = expanded.includes(node.id);
12052
12891
  const isSelected = selected.includes(node.id);
12053
- const defaultIcon = hasChildren ? isExpanded ? /* @__PURE__ */ jsx60(FolderOpen, { className: "h-4 w-4 text-warning" }) : /* @__PURE__ */ jsx60(Folder, { className: "h-4 w-4 text-warning" }) : /* @__PURE__ */ jsx60(File, { className: "h-4 w-4 text-muted-foreground" });
12892
+ const defaultIcon = hasChildren ? isExpanded ? /* @__PURE__ */ jsx60(FolderOpen, { className: "h-4 w-4 text-warning" }) : /* @__PURE__ */ jsx60(Folder, { className: "h-4 w-4 text-warning" }) : /* @__PURE__ */ jsx60(File2, { className: "h-4 w-4 text-muted-foreground" });
12054
12893
  return /* @__PURE__ */ jsxs53("div", { children: [
12055
12894
  /* @__PURE__ */ jsxs53(
12056
12895
  "div",
@@ -12274,7 +13113,7 @@ function Widget({
12274
13113
  // src/components/ui/wizard.tsx
12275
13114
  import * as React50 from "react";
12276
13115
  import { Check as Check7, X as X15, ChevronLeft as ChevronLeft8, ChevronRight as ChevronRight12, AlertCircle as AlertCircle2 } from "lucide-react";
12277
- import { Fragment as Fragment20, jsx as jsx62, jsxs as jsxs55 } from "react/jsx-runtime";
13116
+ import { Fragment as Fragment21, jsx as jsx62, jsxs as jsxs55 } from "react/jsx-runtime";
12278
13117
  var SIZE_MAP = {
12279
13118
  sm: "max-w-sm",
12280
13119
  md: "max-w-lg",
@@ -12484,10 +13323,10 @@ function DefaultActions({
12484
13323
  /* @__PURE__ */ jsx62("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8v8z" })
12485
13324
  ] }),
12486
13325
  "Processing..."
12487
- ] }) : isLast ? /* @__PURE__ */ jsxs55(Fragment20, { children: [
13326
+ ] }) : isLast ? /* @__PURE__ */ jsxs55(Fragment21, { children: [
12488
13327
  /* @__PURE__ */ jsx62(Check7, { className: "h-4 w-4" }),
12489
13328
  finishLabel ?? "Finish"
12490
- ] }) : /* @__PURE__ */ jsxs55(Fragment20, { children: [
13329
+ ] }) : /* @__PURE__ */ jsxs55(Fragment21, { children: [
12491
13330
  nextLabel ?? "Next",
12492
13331
  /* @__PURE__ */ jsx62(ChevronRight12, { className: "h-4 w-4" })
12493
13332
  ] })
@@ -12732,6 +13571,12 @@ var axiosInstance = axios5.create({
12732
13571
  Accept: "application/json"
12733
13572
  }
12734
13573
  });
13574
+ axiosInstance.interceptors.request.use((config) => {
13575
+ if (config.data instanceof FormData) {
13576
+ delete config.headers["Content-Type"];
13577
+ }
13578
+ return config;
13579
+ });
12735
13580
 
12736
13581
  // src/lib/codego/request.ts
12737
13582
  var request = async (config) => {
@@ -12790,13 +13635,13 @@ var setupInterceptors = () => {
12790
13635
 
12791
13636
  // src/lib/codego/provider.tsx
12792
13637
  import * as React51 from "react";
12793
- import { Fragment as Fragment21, jsx as jsx63 } from "react/jsx-runtime";
13638
+ import { Fragment as Fragment22, jsx as jsx63 } from "react/jsx-runtime";
12794
13639
  function CodegoApiProvider({ children }) {
12795
13640
  const { toast } = useToast();
12796
13641
  React51.useEffect(() => {
12797
13642
  setToastFunction(toast);
12798
13643
  }, [toast]);
12799
- return /* @__PURE__ */ jsx63(Fragment21, { children });
13644
+ return /* @__PURE__ */ jsx63(Fragment22, { children });
12800
13645
  }
12801
13646
 
12802
13647
  // src/lib/codego/index.ts