@juv/codego-react-ui 3.4.11 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -5783,13 +5783,79 @@ function FileUpload({
5783
5783
  }
5784
5784
 
5785
5785
  // src/components/ui/repeater.tsx
5786
- import { Plus as Plus3, Trash2, GripVertical as GripVertical3 } from "lucide-react";
5786
+ import { Plus as Plus3, Trash2, GripVertical as GripVertical3, Paperclip } from "lucide-react";
5787
5787
  import { jsx as jsx29, jsxs as jsxs27 } from "react/jsx-runtime";
5788
+ function RepeaterFieldRenderer({
5789
+ field,
5790
+ value,
5791
+ onChange
5792
+ }) {
5793
+ if (field.type === "image") {
5794
+ return /* @__PURE__ */ jsxs27("div", { className: "flex flex-col gap-1.5", children: [
5795
+ field.label && /* @__PURE__ */ jsx29("span", { className: "text-xs font-medium text-muted-foreground", children: field.label }),
5796
+ /* @__PURE__ */ jsxs27("div", { className: "flex items-center gap-3", children: [
5797
+ value && /* @__PURE__ */ jsx29("img", { src: value, alt: field.key, className: "h-10 w-10 rounded-lg object-cover ring-1 ring-border shrink-0" }),
5798
+ /* @__PURE__ */ jsx29(
5799
+ Input,
5800
+ {
5801
+ inputMode: "text",
5802
+ value: value ?? "",
5803
+ onChange: (e) => onChange(e.target.value),
5804
+ placeholder: field.placeholder ?? "Image URL"
5805
+ }
5806
+ )
5807
+ ] })
5808
+ ] });
5809
+ }
5810
+ if (field.type === "attachment") {
5811
+ return /* @__PURE__ */ jsxs27("div", { className: "flex flex-col gap-1.5", children: [
5812
+ field.label && /* @__PURE__ */ jsx29("span", { className: "text-xs font-medium text-muted-foreground", children: field.label }),
5813
+ /* @__PURE__ */ jsxs27("div", { className: "flex items-center gap-2", children: [
5814
+ value && /* @__PURE__ */ jsxs27(
5815
+ "a",
5816
+ {
5817
+ href: value,
5818
+ target: "_blank",
5819
+ rel: "noopener noreferrer",
5820
+ className: "inline-flex items-center gap-1.5 rounded-lg border border-border bg-muted/50 px-2.5 py-1 text-xs font-medium text-foreground hover:bg-muted transition-colors shrink-0",
5821
+ children: [
5822
+ /* @__PURE__ */ jsx29(Paperclip, { className: "h-3 w-3" }),
5823
+ String(value).split("/").pop()
5824
+ ]
5825
+ }
5826
+ ),
5827
+ /* @__PURE__ */ jsx29(
5828
+ Input,
5829
+ {
5830
+ inputMode: "text",
5831
+ value: value ?? "",
5832
+ onChange: (e) => onChange(e.target.value),
5833
+ placeholder: field.placeholder ?? "Attachment URL"
5834
+ }
5835
+ )
5836
+ ] })
5837
+ ] });
5838
+ }
5839
+ return /* @__PURE__ */ jsxs27("div", { className: "flex flex-col gap-1.5", children: [
5840
+ field.label && /* @__PURE__ */ jsx29("span", { className: "text-xs font-medium text-muted-foreground", children: field.label }),
5841
+ /* @__PURE__ */ jsx29(
5842
+ Input,
5843
+ {
5844
+ inputMode: "text",
5845
+ value: value ?? "",
5846
+ onChange: (e) => onChange(e.target.value),
5847
+ placeholder: field.placeholder ?? field.key
5848
+ }
5849
+ )
5850
+ ] });
5851
+ }
5788
5852
  function Repeater({
5789
5853
  items,
5790
5854
  onAdd,
5791
5855
  onRemove,
5792
5856
  renderItem,
5857
+ fields,
5858
+ onFieldChange,
5793
5859
  addButtonText = "Add Item",
5794
5860
  className
5795
5861
  }) {
@@ -5802,7 +5868,15 @@ function Repeater({
5802
5868
  children: [
5803
5869
  /* @__PURE__ */ jsx29("div", { className: "mt-1 cursor-grab text-muted-foreground/30 group-hover:text-muted-foreground/60 transition-colors shrink-0", children: /* @__PURE__ */ jsx29(GripVertical3, { className: "h-4 w-4" }) }),
5804
5870
  /* @__PURE__ */ jsx29("div", { className: "mt-1 flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-primary/10 text-[10px] font-semibold text-primary", children: index + 1 }),
5805
- /* @__PURE__ */ jsx29("div", { className: "flex-1 min-w-0", children: renderItem(item, index) }),
5871
+ /* @__PURE__ */ jsx29("div", { className: "flex-1 min-w-0", children: fields ? /* @__PURE__ */ jsx29("div", { className: "grid gap-3", style: { gridTemplateColumns: fields.length > 1 ? `repeat(${Math.min(fields.length, 3)}, minmax(0, 1fr))` : "1fr" }, children: fields.map((f) => /* @__PURE__ */ jsx29(
5872
+ RepeaterFieldRenderer,
5873
+ {
5874
+ field: f,
5875
+ value: item[f.key],
5876
+ onChange: (v) => onFieldChange?.(index, f.key, v)
5877
+ },
5878
+ f.key
5879
+ )) }) : renderItem ? renderItem(item, index) : null }),
5806
5880
  /* @__PURE__ */ jsxs27(
5807
5881
  Button,
5808
5882
  {
@@ -6284,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,74 @@ 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
+ } catch (err) {
7534
+ console.error("[Table] Bulk delete selected failed:", err?.response?.data?.message ?? err.message);
7535
+ } finally {
7536
+ setBulkLoading(false);
7537
+ }
7538
+ };
7539
+ const execDeleteAll = async () => {
7540
+ if (!bulkDeleteBaseUrl) return;
7541
+ setBulkLoading(true);
7542
+ try {
7543
+ const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute("content");
7544
+ if (!csrfToken) throw new Error("[Table] CSRF token not found.");
7545
+ const safeUrl = bulkDeleteBaseUrl.replace(/\/+$/, "");
7546
+ await axios3.delete(`${safeUrl}/delete/all`, { headers: { "X-CSRF-Token": csrfToken } });
7547
+ setTableData([]);
7548
+ setSelectedIds([]);
7549
+ defaultActions?.onReload?.();
7550
+ } catch (err) {
7551
+ console.error("[Table] Delete all failed:", err?.response?.data?.message ?? err.message);
7552
+ } finally {
7553
+ setBulkLoading(false);
7554
+ }
7555
+ };
6934
7556
  const pagePills = React28.useMemo(() => {
6935
7557
  if (totalPages <= 5) return Array.from({ length: totalPages }, (_, i) => i + 1);
6936
7558
  if (safePage <= 3) return [1, 2, 3, 4, 5];
@@ -6942,8 +7564,9 @@ function Table({
6942
7564
  if (sortKey !== String(col.key)) return /* @__PURE__ */ jsx32(ChevronsUpDown, { className: "ml-1.5 h-3.5 w-3.5 opacity-40" });
6943
7565
  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
7566
  };
6945
- return /* @__PURE__ */ jsxs30(Fragment11, { children: [
7567
+ return /* @__PURE__ */ jsxs30(Fragment12, { children: [
6946
7568
  /* @__PURE__ */ jsxs30("div", { className: cn("w-full space-y-3", className), children: [
7569
+ 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
7570
  /* @__PURE__ */ jsxs30("div", { className: "flex items-center justify-between gap-3 flex-wrap", children: [
6948
7571
  searchable && /* @__PURE__ */ jsxs30("div", { className: "relative w-72", children: [
6949
7572
  /* @__PURE__ */ jsx32(Search5, { className: "absolute text-primary left-3 top-1/2 -translate-y-1/2 h-4 w-4 z-10" }),
@@ -6965,33 +7588,127 @@ function Table({
6965
7588
  }
6966
7589
  )
6967
7590
  ] }),
6968
- /* @__PURE__ */ jsxs30("div", { className: "flex items-center gap-2 ml-auto", children: [
6969
- selectable && onBulkDelete && selectedIds.length > 0 && /* @__PURE__ */ jsxs30(
7591
+ /* @__PURE__ */ jsxs30("div", { className: "flex items-center gap-2 ml-auto flex-wrap", children: [
7592
+ selectable && selectedIds.length > 0 && /* @__PURE__ */ jsxs30(Fragment12, { children: [
7593
+ /* @__PURE__ */ jsxs30(
7594
+ "button",
7595
+ {
7596
+ onClick: handleUnselectAll,
7597
+ disabled: bulkLoading,
7598
+ 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",
7599
+ children: [
7600
+ /* @__PURE__ */ jsx32(X9, { className: "h-3.5 w-3.5" }),
7601
+ "Unselect all ",
7602
+ selectedIds.length
7603
+ ]
7604
+ }
7605
+ ),
7606
+ unselectedCount > 0 && /* @__PURE__ */ jsxs30(
7607
+ "button",
7608
+ {
7609
+ onClick: handleSelectAllRecords,
7610
+ disabled: bulkLoading,
7611
+ 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",
7612
+ children: [
7613
+ "Select all ",
7614
+ unselectedCount
7615
+ ]
7616
+ }
7617
+ ),
7618
+ /* @__PURE__ */ jsxs30(
7619
+ "button",
7620
+ {
7621
+ onClick: () => setBulkConfirm("selected"),
7622
+ disabled: bulkLoading,
7623
+ 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",
7624
+ children: [
7625
+ bulkLoading ? /* @__PURE__ */ jsx32(Loader22, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx32(Trash22, { className: "h-3.5 w-3.5" }),
7626
+ "Delete ",
7627
+ selectedIds.length,
7628
+ " selected"
7629
+ ]
7630
+ }
7631
+ ),
7632
+ bulkDeleteBaseUrl && /* @__PURE__ */ jsxs30(
7633
+ "button",
7634
+ {
7635
+ onClick: () => setBulkConfirm("all"),
7636
+ disabled: bulkLoading,
7637
+ 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",
7638
+ children: [
7639
+ bulkLoading ? /* @__PURE__ */ jsx32(Loader22, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx32(Trash22, { className: "h-3.5 w-3.5" }),
7640
+ "Delete all"
7641
+ ]
7642
+ }
7643
+ )
7644
+ ] }),
7645
+ filterBar && /* @__PURE__ */ jsx32(
6970
7646
  "button",
6971
7647
  {
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
- ]
7648
+ onClick: () => setFilterBarOpen((o) => !o),
7649
+ className: cn(
7650
+ "inline-flex items-center gap-1.5 rounded-lg border px-3 py-1.5 text-xs font-medium transition-colors",
7651
+ filterBarOpen ? "border-primary bg-primary/10 text-primary hover:bg-primary/20" : "border-border bg-muted/50 text-muted-foreground hover:bg-muted"
7652
+ ),
7653
+ children: filterableIcon ?? "Filter"
6983
7654
  }
6984
7655
  ),
7656
+ exportable && /* @__PURE__ */ jsxs30("div", { className: "relative group", children: [
7657
+ /* @__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" }),
7658
+ /* @__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(
7659
+ "button",
7660
+ {
7661
+ onClick: () => onExport?.(type),
7662
+ className: "px-4 py-2 text-xs text-left hover:bg-muted transition-colors capitalize",
7663
+ children: type.toUpperCase()
7664
+ },
7665
+ type
7666
+ )) })
7667
+ ] }),
7668
+ columnVisibility && onColumnVisibilityChange && /* @__PURE__ */ jsxs30("div", { className: "relative group", children: [
7669
+ /* @__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" }),
7670
+ /* @__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: [
7671
+ /* @__PURE__ */ jsx32(
7672
+ "input",
7673
+ {
7674
+ type: "checkbox",
7675
+ checked: columnVisibility[String(col.key)] !== false,
7676
+ onChange: (e) => onColumnVisibilityChange({ ...columnVisibility, [String(col.key)]: e.target.checked }),
7677
+ className: "accent-primary"
7678
+ }
7679
+ ),
7680
+ col.title
7681
+ ] }, String(col.key))) })
7682
+ ] }),
6985
7683
  /* @__PURE__ */ jsxs30("span", { className: "text-xs text-muted-foreground", children: [
6986
- serverPagination ? serverPagination.pagination.total : filteredData.length,
7684
+ totalRows,
6987
7685
  " ",
6988
- (serverPagination ? serverPagination.pagination.total : filteredData.length) === 1 ? "row" : "rows",
7686
+ totalRows === 1 ? "row" : "rows",
6989
7687
  search && ` \xB7 filtered from ${tableData.length}`
6990
7688
  ] })
6991
7689
  ] })
6992
7690
  ] }),
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: [
7691
+ filterBar && filterBarOpen && /* @__PURE__ */ jsx32("div", { children: filterBar }),
7692
+ loading && /* @__PURE__ */ jsxs30("div", { className: "flex items-center justify-center py-12 text-muted-foreground gap-2", children: [
7693
+ /* @__PURE__ */ jsx32(Loader22, { className: "h-5 w-5 animate-spin" }),
7694
+ /* @__PURE__ */ jsx32("span", { className: "text-sm", children: "Loading\u2026" })
7695
+ ] }),
7696
+ !loading && /* @__PURE__ */ jsx32("div", { className: cn(
7697
+ variant === "default" && "rounded-xl border border-border overflow-hidden bg-card/50 backdrop-blur-sm shadow-sm",
7698
+ variant === "zebra" && "rounded-xl border border-border overflow-hidden bg-card/50 backdrop-blur-sm shadow-sm",
7699
+ variant === "card" && "space-y-2",
7700
+ variant === "glass" && "rounded-2xl overflow-hidden border border-white/10 bg-background/30 backdrop-blur-xl shadow-xl",
7701
+ variant === "soft" && "rounded-2xl overflow-hidden bg-card",
7702
+ variant === "soft" && "[box-shadow:6px_6px_12px_hsl(var(--foreground)/0.07),-6px_-6px_12px_hsl(var(--background)/0.8)]",
7703
+ virtualized && "max-h-[520px] overflow-y-auto"
7704
+ ), 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: [
7705
+ /* @__PURE__ */ jsx32("thead", { children: /* @__PURE__ */ jsxs30("tr", { className: cn(
7706
+ variant === "default" && "border-b border-border bg-muted/40",
7707
+ variant === "zebra" && "border-b border-border bg-muted/40",
7708
+ variant === "card" && "[&>th]:bg-transparent",
7709
+ variant === "glass" && "border-b border-white/10 bg-white/5",
7710
+ variant === "soft" && "border-b-0 bg-muted/30"
7711
+ ), children: [
6995
7712
  selectable && /* @__PURE__ */ jsx32("th", { className: "h-11 w-[46px] px-4 text-left align-middle", children: /* @__PURE__ */ jsx32(
6996
7713
  Checkbox,
6997
7714
  {
@@ -6999,13 +7716,16 @@ function Table({
6999
7716
  onChange: (e) => handleSelectAll(e.target.checked)
7000
7717
  }
7001
7718
  ) }),
7719
+ expandable && /* @__PURE__ */ jsx32("th", { className: "h-11 w-8" }),
7002
7720
  allColumns.map((col, ci) => /* @__PURE__ */ jsx32(
7003
7721
  "th",
7004
7722
  {
7005
7723
  onClick: () => col.sortable && handleSort(String(col.key)),
7006
7724
  className: cn(
7007
7725
  "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"
7726
+ col.sortable && "cursor-pointer hover:text-foreground transition-colors",
7727
+ variant === "glass" && "text-foreground/70",
7728
+ variant === "soft" && "text-muted-foreground/80"
7009
7729
  ),
7010
7730
  children: /* @__PURE__ */ jsxs30("span", { className: "inline-flex items-center", children: [
7011
7731
  col.title,
@@ -7018,9 +7738,9 @@ function Table({
7018
7738
  /* @__PURE__ */ jsx32("tbody", { children: paginatedData.length === 0 ? /* @__PURE__ */ jsx32("tr", { children: /* @__PURE__ */ jsx32(
7019
7739
  "td",
7020
7740
  {
7021
- colSpan: allColumns.length + (selectable ? 1 : 0),
7741
+ colSpan: allColumns.length + (selectable ? 1 : 0) + (expandable ? 1 : 0),
7022
7742
  className: "h-32 text-center align-middle",
7023
- children: /* @__PURE__ */ jsxs30("div", { className: "flex flex-col items-center gap-1 text-muted-foreground", children: [
7743
+ children: emptyState ?? /* @__PURE__ */ jsxs30("div", { className: "flex flex-col items-center gap-1 text-muted-foreground", children: [
7024
7744
  /* @__PURE__ */ jsx32(Search5, { className: "h-8 w-8 opacity-20" }),
7025
7745
  /* @__PURE__ */ jsx32("span", { className: "text-sm", children: "No results found" }),
7026
7746
  search && /* @__PURE__ */ jsx32("button", { onClick: () => setSearch(""), className: "text-xs text-primary hover:underline", children: "Clear search" })
@@ -7029,85 +7749,168 @@ function Table({
7029
7749
  ) }) : paginatedData.map((item, i) => {
7030
7750
  const id = String(item[idKey] || i);
7031
7751
  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"
7752
+ const isExpanded = expandedIds.has(id);
7753
+ const isFocused = keyboardNavigation && focusedRowIdx === i;
7754
+ return /* @__PURE__ */ jsxs30(React28.Fragment, { children: [
7755
+ /* @__PURE__ */ jsxs30(
7756
+ "tr",
7757
+ {
7758
+ draggable,
7759
+ tabIndex: keyboardNavigation ? 0 : void 0,
7760
+ onDragStart: draggable ? (e) => {
7761
+ e.dataTransfer.setData("text/plain", id);
7762
+ } : void 0,
7763
+ onDragOver: draggable ? (e) => {
7764
+ e.preventDefault();
7765
+ setDragOverId(id);
7766
+ } : void 0,
7767
+ onDragLeave: draggable ? () => setDragOverId(null) : void 0,
7768
+ onDrop: draggable ? (e) => {
7769
+ e.preventDefault();
7770
+ setDragOverId(null);
7771
+ const fromId = e.dataTransfer.getData("text/plain");
7772
+ if (fromId === id) return;
7773
+ setTableData((prev) => {
7774
+ const fromIdx = prev.findIndex((r) => String(r[idKey] || "") === fromId);
7775
+ const toIdx = prev.findIndex((r) => String(r[idKey] || "") === id);
7776
+ if (fromIdx < 0 || toIdx < 0) return prev;
7777
+ const next = [...prev];
7778
+ const [moved] = next.splice(fromIdx, 1);
7779
+ next.splice(toIdx, 0, moved);
7780
+ onRowReorder?.(next);
7781
+ return next;
7782
+ });
7783
+ } : void 0,
7784
+ onClick: () => {
7785
+ if (expandable) setExpandedIds((prev) => {
7786
+ const s = new Set(prev);
7787
+ s.has(id) ? s.delete(id) : s.add(id);
7788
+ return s;
7789
+ });
7790
+ onRowClick?.(item);
7791
+ if (keyboardNavigation) setFocusedRowIdx(i);
7792
+ },
7793
+ onDoubleClick: () => onRowDoubleClick?.(item),
7794
+ className: cn(
7795
+ // default
7796
+ variant === "default" && "border-b border-border/60 transition-colors last:border-0",
7797
+ variant === "default" && (isSelected ? "bg-primary/5 hover:bg-primary/8" : "hover:bg-muted/30"),
7798
+ // zebra
7799
+ variant === "zebra" && "border-b border-border/40 transition-colors last:border-0",
7800
+ variant === "zebra" && (isSelected ? "bg-primary/8" : i % 2 === 0 ? "bg-card" : "bg-muted/40"),
7801
+ variant === "zebra" && !isSelected && "hover:bg-primary/5",
7802
+ // card
7803
+ variant === "card" && "rounded-xl border border-border bg-card shadow-sm transition-all hover:shadow-md hover:-translate-y-px",
7804
+ variant === "card" && (isSelected ? "border-primary/50 bg-primary/5" : ""),
7805
+ variant === "card" && "[&>td:first-child]:rounded-l-xl [&>td:last-child]:rounded-r-xl",
7806
+ // glass
7807
+ variant === "glass" && "border-b border-white/8 transition-colors last:border-0",
7808
+ variant === "glass" && (isSelected ? "bg-primary/15 hover:bg-primary/20" : "hover:bg-white/5"),
7809
+ // soft
7810
+ variant === "soft" && "transition-all",
7811
+ 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"),
7812
+ variant === "soft" && "border-b border-border/30 last:border-0",
7813
+ (onRowClick || onRowDoubleClick || expandable) && "cursor-pointer",
7814
+ draggable && dragOverId === id && "ring-2 ring-inset ring-primary/40",
7815
+ isFocused && "ring-2 ring-inset ring-ring",
7816
+ rowClassName?.(item)
7817
+ ),
7818
+ children: [
7819
+ selectable && /* @__PURE__ */ jsx32("td", { className: "px-4 py-3 align-middle", children: /* @__PURE__ */ jsx32(
7820
+ Checkbox,
7821
+ {
7822
+ checked: isSelected,
7823
+ onChange: (e) => handleSelect(id, e.target.checked)
7824
+ }
7061
7825
  ) }),
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",
7826
+ 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") }) }),
7827
+ 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(
7828
+ "img",
7829
+ {
7830
+ src: item[col.key],
7831
+ alt: item[col.key],
7832
+ className: "h-9 w-9 rounded-lg object-cover ring-1 ring-border"
7833
+ }
7834
+ ) : col.type === "badge" ? /* @__PURE__ */ jsxs30("span", { className: cn(
7835
+ "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-medium",
7836
+ badgeClass(String(item[col.key]))
7837
+ ), children: [
7838
+ /* @__PURE__ */ jsx32("span", { className: cn(
7839
+ "mr-1.5 h-1.5 w-1.5 rounded-full",
7840
+ 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"
7841
+ ) }),
7842
+ item[col.key]
7843
+ ] }) : 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(
7844
+ "select",
7089
7845
  {
7090
- type: "color",
7091
- value: item[col.key] || "#000000",
7846
+ value: item[col.key],
7092
7847
  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"
7848
+ 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",
7849
+ children: (col.selectOptions ?? []).map((opt) => /* @__PURE__ */ jsx32("option", { value: opt, children: opt }, opt))
7094
7850
  }
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
- );
7851
+ ) : col.type === "toggle" ? /* @__PURE__ */ jsx32(
7852
+ "button",
7853
+ {
7854
+ role: "switch",
7855
+ "aria-checked": !!item[col.key],
7856
+ onClick: () => col.onChange?.(item, !item[col.key]),
7857
+ className: cn(
7858
+ "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",
7859
+ item[col.key] ? "bg-primary" : "bg-muted"
7860
+ ),
7861
+ children: /* @__PURE__ */ jsx32("span", { className: cn(
7862
+ "pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow-sm transition-transform",
7863
+ item[col.key] ? "translate-x-4" : "translate-x-0"
7864
+ ) })
7865
+ }
7866
+ ) : col.type === "color" ? /* @__PURE__ */ jsxs30("div", { className: "flex items-center gap-2", children: [
7867
+ /* @__PURE__ */ jsx32(
7868
+ "input",
7869
+ {
7870
+ type: "color",
7871
+ value: item[col.key] || "#000000",
7872
+ onChange: (e) => col.onChange?.(item, e.target.value),
7873
+ className: "h-7 w-7 cursor-pointer rounded border border-border bg-transparent p-0.5"
7874
+ }
7875
+ ),
7876
+ /* @__PURE__ */ jsx32("span", { className: "text-xs text-muted-foreground font-mono", children: item[col.key] })
7877
+ ] }) : col.type === "checkbox" ? /* @__PURE__ */ jsx32(
7878
+ Checkbox,
7879
+ {
7880
+ checked: !!item[col.key],
7881
+ onChange: (e) => col.onChange?.(item, e.target.checked)
7882
+ }
7883
+ ) : col.type === "text-url" ? (() => {
7884
+ const href = col.redirect ? typeof col.redirect === "function" ? col.redirect(item) : col.redirect : String(item[col.key] ?? "");
7885
+ const colorMap = {
7886
+ primary: "var(--primary)",
7887
+ info: "var(--info)",
7888
+ success: "var(--success)",
7889
+ warning: "var(--warning)",
7890
+ danger: "var(--danger)"
7891
+ };
7892
+ const underline = col.underlineColor ? colorMap[col.underlineColor] ?? col.underlineColor : "var(--primary)";
7893
+ return /* @__PURE__ */ jsx32(
7894
+ "a",
7895
+ {
7896
+ href,
7897
+ target: col.openNewTab ? "_blank" : void 0,
7898
+ rel: col.openNewTab ? "noopener noreferrer" : void 0,
7899
+ style: { textDecorationColor: underline },
7900
+ className: "text-sm underline underline-offset-2 hover:opacity-75 transition-opacity break-all",
7901
+ onClick: col.openNewTab ? void 0 : (e) => e.preventDefault(),
7902
+ children: item[col.key]
7903
+ }
7904
+ );
7905
+ })() : /* @__PURE__ */ jsx32("span", { className: "text-foreground/90", children: item[col.key] }) }, `${String(col.key)}-${ci}`))
7906
+ ]
7907
+ }
7908
+ ),
7909
+ 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) }) })
7910
+ ] }, id);
7108
7911
  }) })
7109
7912
  ] }) }) }),
7110
- pagination && !serverPagination && totalPages > 1 && /* @__PURE__ */ jsxs30("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
7913
+ clientPagination && !serverPagination && totalPages > 1 && /* @__PURE__ */ jsxs30("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
7111
7914
  /* @__PURE__ */ jsxs30("span", { className: "text-xs text-muted-foreground", children: [
7112
7915
  "Showing ",
7113
7916
  (safePage - 1) * itemsPerPage + 1,
@@ -7126,7 +7929,7 @@ function Table({
7126
7929
  children: /* @__PURE__ */ jsx32(ChevronLeft6, { className: "h-4 w-4" })
7127
7930
  }
7128
7931
  ),
7129
- pagePills[0] > 1 && /* @__PURE__ */ jsxs30(Fragment11, { children: [
7932
+ pagePills[0] > 1 && /* @__PURE__ */ jsxs30(Fragment12, { children: [
7130
7933
  /* @__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
7934
  pagePills[0] > 2 && /* @__PURE__ */ jsx32("span", { className: "px-1 text-muted-foreground text-xs", children: "\u2026" })
7132
7935
  ] }),
@@ -7142,7 +7945,7 @@ function Table({
7142
7945
  },
7143
7946
  p
7144
7947
  )),
7145
- pagePills[pagePills.length - 1] < totalPages && /* @__PURE__ */ jsxs30(Fragment11, { children: [
7948
+ pagePills[pagePills.length - 1] < totalPages && /* @__PURE__ */ jsxs30(Fragment12, { children: [
7146
7949
  pagePills[pagePills.length - 1] < totalPages - 1 && /* @__PURE__ */ jsx32("span", { className: "px-1 text-muted-foreground text-xs", children: "\u2026" }),
7147
7950
  /* @__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
7951
  ] }),
@@ -7158,8 +7961,8 @@ function Table({
7158
7961
  ] })
7159
7962
  ] }),
7160
7963
  serverPagination && (() => {
7161
- const { pagination: pagination2, currentPage: cp, goToPage } = serverPagination;
7162
- const totalServerPages = pagination2.last_page ?? Math.ceil(pagination2.total / pagination2.per_page);
7964
+ const { pagination, currentPage: cp, goToPage } = serverPagination;
7965
+ const totalServerPages = pagination.last_page ?? Math.ceil(pagination.total / pagination.per_page);
7163
7966
  const pills = [];
7164
7967
  if (totalServerPages <= 7) {
7165
7968
  for (let i = 1; i <= totalServerPages; i++) pills.push(i);
@@ -7172,7 +7975,7 @@ function Table({
7172
7975
  }
7173
7976
  return /* @__PURE__ */ jsxs30("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
7174
7977
  /* @__PURE__ */ jsxs30("span", { className: "text-xs text-muted-foreground", children: [
7175
- pagination2.total,
7978
+ pagination.total,
7176
7979
  " total rows \xB7 page ",
7177
7980
  cp,
7178
7981
  " of ",
@@ -7183,7 +7986,7 @@ function Table({
7183
7986
  "button",
7184
7987
  {
7185
7988
  onClick: () => goToPage(cp - 1),
7186
- disabled: !pagination2.prev_page_url,
7989
+ disabled: !pagination.prev_page_url,
7187
7990
  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
7991
  children: /* @__PURE__ */ jsx32(ChevronLeft6, { className: "h-4 w-4" })
7189
7992
  }
@@ -7206,7 +8009,7 @@ function Table({
7206
8009
  "button",
7207
8010
  {
7208
8011
  onClick: () => goToPage(cp + 1),
7209
- disabled: !pagination2.next_page_url,
8012
+ disabled: !pagination.next_page_url,
7210
8013
  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
8014
  children: /* @__PURE__ */ jsx32(ChevronRight8, { className: "h-4 w-4" })
7212
8015
  }
@@ -7221,6 +8024,7 @@ function Table({
7221
8024
  item: viewItem,
7222
8025
  fields: viewFields,
7223
8026
  width: defaultActions.modalWidth,
8027
+ grid: defaultActions.viewFormGrid,
7224
8028
  onClose: () => setViewItem(null)
7225
8029
  }
7226
8030
  ),
@@ -7278,12 +8082,43 @@ function Table({
7278
8082
  }
7279
8083
  }
7280
8084
  }
8085
+ ),
8086
+ bulkConfirm && createPortal3(
8087
+ /* @__PURE__ */ jsx32(
8088
+ "div",
8089
+ {
8090
+ className: "fixed inset-0 z-50 flex items-center justify-center p-4",
8091
+ style: { background: "rgba(0,0,0,0.5)" },
8092
+ onMouseDown: (e) => {
8093
+ if (e.target === e.currentTarget) setBulkConfirm(null);
8094
+ },
8095
+ children: /* @__PURE__ */ jsxs30("div", { className: "relative w-full max-w-md rounded-2xl border border-border bg-card shadow-2xl flex flex-col", children: [
8096
+ /* @__PURE__ */ jsxs30("div", { className: "flex items-center justify-between px-6 py-4 border-b border-border", children: [
8097
+ /* @__PURE__ */ jsx32("h2", { className: "text-base font-semibold", children: "Confirm Delete" }),
8098
+ /* @__PURE__ */ jsx32("button", { onClick: () => setBulkConfirm(null), className: "text-muted-foreground hover:text-foreground transition-colors", children: /* @__PURE__ */ jsx32(X9, { className: "h-4 w-4" }) })
8099
+ ] }),
8100
+ /* @__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." }) }),
8101
+ /* @__PURE__ */ jsxs30("div", { className: "px-6 py-4 border-t border-border flex justify-end gap-2", children: [
8102
+ /* @__PURE__ */ jsx32(Button, { variant: "outline", size: "sm", onClick: () => setBulkConfirm(null), disabled: bulkLoading, children: "Cancel" }),
8103
+ /* @__PURE__ */ jsxs30(Button, { variant: "danger", size: "sm", disabled: bulkLoading, onClick: async () => {
8104
+ if (bulkConfirm === "selected") await execBulkDeleteSelected();
8105
+ else await execDeleteAll();
8106
+ setBulkConfirm(null);
8107
+ }, children: [
8108
+ bulkLoading && /* @__PURE__ */ jsx32(Loader22, { className: "h-3.5 w-3.5 mr-1.5 animate-spin" }),
8109
+ bulkLoading ? "Deleting\u2026" : "Delete"
8110
+ ] })
8111
+ ] })
8112
+ ] })
8113
+ }
8114
+ ),
8115
+ document.body
7281
8116
  )
7282
8117
  ] });
7283
8118
  }
7284
8119
 
7285
8120
  // src/components/ui/data-grid.tsx
7286
- import { Fragment as Fragment12, jsx as jsx33, jsxs as jsxs31 } from "react/jsx-runtime";
8121
+ import { Fragment as Fragment13, jsx as jsx33, jsxs as jsxs31 } from "react/jsx-runtime";
7287
8122
  function useServerDataGrid({ url, params, encrypt, key, decryptPayloadLog, columnOverrides }) {
7288
8123
  const [data, setData] = React29.useState([]);
7289
8124
  const [columns, setColumns] = React29.useState([]);
@@ -7411,7 +8246,7 @@ function DGModalShell({ title, onClose, children, footer, width = "lg" }) {
7411
8246
  );
7412
8247
  }
7413
8248
  function DGFieldRenderer({ field, value, onChange }) {
7414
- if (field.render) return /* @__PURE__ */ jsx33(Fragment12, { children: field.render(value, onChange) });
8249
+ if (field.render) return /* @__PURE__ */ jsx33(Fragment13, { children: field.render(value, onChange) });
7415
8250
  const toLabelValue = (o) => {
7416
8251
  if (typeof o === "string") return { label: o, value: o };
7417
8252
  if (Array.isArray(o)) return { label: o[0], value: o[1] };
@@ -7491,7 +8326,7 @@ function DGViewModal({ item, fields, onClose, width }) {
7491
8326
  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
8327
  children: /* @__PURE__ */ jsx33("div", { className: "space-y-3", children: fields.map((f) => /* @__PURE__ */ jsxs31("div", { children: [
7493
8328
  /* @__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], () => {
8329
+ f.render ? /* @__PURE__ */ jsx33(Fragment13, { children: f.render(item[f.key], () => {
7495
8330
  }) }) : /* @__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
8331
  ] }, f.key)) })
7497
8332
  }
@@ -7555,7 +8390,7 @@ function DGEditModal({
7555
8390
  title: "Edit Record",
7556
8391
  onClose,
7557
8392
  width,
7558
- footer: /* @__PURE__ */ jsxs31(Fragment12, { children: [
8393
+ footer: /* @__PURE__ */ jsxs31(Fragment13, { children: [
7559
8394
  /* @__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
8395
  /* @__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
8396
  loading && /* @__PURE__ */ jsx33(Loader23, { className: "h-3.5 w-3.5 animate-spin" }),
@@ -7615,7 +8450,7 @@ function DGDeleteModal({
7615
8450
  title: "Confirm Delete",
7616
8451
  onClose,
7617
8452
  width: "lg",
7618
- footer: /* @__PURE__ */ jsxs31(Fragment12, { children: [
8453
+ footer: /* @__PURE__ */ jsxs31(Fragment13, { children: [
7619
8454
  /* @__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
8455
  /* @__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
8456
  loading && /* @__PURE__ */ jsx33(Loader23, { className: "h-3.5 w-3.5 animate-spin" }),
@@ -8547,7 +9382,7 @@ function KanbanBoard({ columns: controlled, onChange, onAddCard, className }) {
8547
9382
  import * as React33 from "react";
8548
9383
  import { MapContainer, TileLayer, Marker, Popup, Polyline, useMap, CircleMarker } from "react-leaflet";
8549
9384
  import L from "leaflet";
8550
- import { Fragment as Fragment14, jsx as jsx39, jsxs as jsxs35 } from "react/jsx-runtime";
9385
+ import { Fragment as Fragment15, jsx as jsx39, jsxs as jsxs35 } from "react/jsx-runtime";
8551
9386
  L.Icon.Default.mergeOptions({
8552
9387
  iconUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png",
8553
9388
  iconRetinaUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png",
@@ -8653,7 +9488,7 @@ function RouteLayer({ routes }) {
8653
9488
  });
8654
9489
  }, [routes]);
8655
9490
  if (loading) return null;
8656
- return /* @__PURE__ */ jsx39(Fragment14, { children: resolved.map((r, i) => {
9491
+ return /* @__PURE__ */ jsx39(Fragment15, { children: resolved.map((r, i) => {
8657
9492
  const color = r.route.color ?? (r.route.routeType === "walk" ? "#22c55e" : "#6366f1");
8658
9493
  const isDrive = (r.route.routeType ?? "drive") === "drive";
8659
9494
  const midIdx = Math.floor(r.coords.length / 2);
@@ -8693,7 +9528,7 @@ function RouteLayer({ routes }) {
8693
9528
  zIndexOffset: 100,
8694
9529
  children: /* @__PURE__ */ jsx39(Popup, { children: /* @__PURE__ */ jsxs35("div", { className: "text-xs space-y-0.5 min-w-[120px]", children: [
8695
9530
  /* @__PURE__ */ jsx39("p", { className: "font-semibold", children: r.route.label ? `${r.route.label} \u2014 Start` : "Start" }),
8696
- r.distance > 0 && /* @__PURE__ */ jsxs35(Fragment14, { children: [
9531
+ r.distance > 0 && /* @__PURE__ */ jsxs35(Fragment15, { children: [
8697
9532
  /* @__PURE__ */ jsx39("p", { className: "text-muted-foreground", children: fmtDistance(r.distance) }),
8698
9533
  /* @__PURE__ */ jsxs35("p", { className: "text-muted-foreground", children: [
8699
9534
  fmtDuration(r.duration),
@@ -8712,7 +9547,7 @@ function RouteLayer({ routes }) {
8712
9547
  zIndexOffset: 100,
8713
9548
  children: /* @__PURE__ */ jsx39(Popup, { children: /* @__PURE__ */ jsxs35("div", { className: "text-xs space-y-0.5 min-w-[120px]", children: [
8714
9549
  /* @__PURE__ */ jsx39("p", { className: "font-semibold", children: r.route.label ? `${r.route.label} \u2014 End` : "End" }),
8715
- r.distance > 0 && /* @__PURE__ */ jsxs35(Fragment14, { children: [
9550
+ r.distance > 0 && /* @__PURE__ */ jsxs35(Fragment15, { children: [
8716
9551
  /* @__PURE__ */ jsx39("p", { className: "text-muted-foreground", children: fmtDistance(r.distance) }),
8717
9552
  /* @__PURE__ */ jsxs35("p", { className: "text-muted-foreground", children: [
8718
9553
  fmtDuration(r.duration),
@@ -8827,7 +9662,7 @@ function ClusterLayer({ markers, variant, onMarkerClick }) {
8827
9662
  });
8828
9663
  return groups;
8829
9664
  }, [markers, zoom, map]);
8830
- return /* @__PURE__ */ jsx39(Fragment14, { children: clusters.map((g, gi) => {
9665
+ return /* @__PURE__ */ jsx39(Fragment15, { children: clusters.map((g, gi) => {
8831
9666
  if (g.items.length === 1) {
8832
9667
  const m = g.items[0];
8833
9668
  const color2 = resolveColor(m.color);
@@ -9696,7 +10531,7 @@ function Modal({
9696
10531
  // src/components/ui/modal-variants.tsx
9697
10532
  import * as React36 from "react";
9698
10533
  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";
10534
+ import { Fragment as Fragment16, jsx as jsx42, jsxs as jsxs38 } from "react/jsx-runtime";
9700
10535
  function ModalBase({
9701
10536
  isOpen,
9702
10537
  onClose,
@@ -9847,7 +10682,7 @@ function ModalWithForms({
9847
10682
  onSubmit(values);
9848
10683
  }
9849
10684
  function renderField(field) {
9850
- if (field.render) return /* @__PURE__ */ jsx42(Fragment15, { children: field.render(values[field.name], (v) => handleChange(field.name, v)) });
10685
+ if (field.render) return /* @__PURE__ */ jsx42(Fragment16, { children: field.render(values[field.name], (v) => handleChange(field.name, v)) });
9851
10686
  const strOptions = (field.options ?? []).map(
9852
10687
  (o) => typeof o === "string" ? { label: o, value: o } : o
9853
10688
  );
@@ -10195,7 +11030,7 @@ import { PanelLeftClose, PanelLeftOpen, Sun as Sun3, Moon as Moon2, Loader2 as L
10195
11030
  // src/components/ui/tooltip.tsx
10196
11031
  import * as React38 from "react";
10197
11032
  import * as ReactDOM2 from "react-dom";
10198
- import { Fragment as Fragment16, jsx as jsx44, jsxs as jsxs40 } from "react/jsx-runtime";
11033
+ import { Fragment as Fragment17, jsx as jsx44, jsxs as jsxs40 } from "react/jsx-runtime";
10199
11034
  function Tooltip({
10200
11035
  content,
10201
11036
  children,
@@ -10206,7 +11041,7 @@ function Tooltip({
10206
11041
  const [visible, setVisible] = React38.useState(false);
10207
11042
  const [coords, setCoords] = React38.useState({ top: 0, left: 0 });
10208
11043
  const ref = React38.useRef(null);
10209
- if (!enabled) return /* @__PURE__ */ jsx44(Fragment16, { children });
11044
+ if (!enabled) return /* @__PURE__ */ jsx44(Fragment17, { children });
10210
11045
  function calcCoords() {
10211
11046
  const r = ref.current?.getBoundingClientRect();
10212
11047
  if (!r) return;
@@ -10803,7 +11638,7 @@ var useTheme = () => {
10803
11638
  };
10804
11639
 
10805
11640
  // src/components/ui/panel.tsx
10806
- import { Fragment as Fragment17, jsx as jsx46, jsxs as jsxs41 } from "react/jsx-runtime";
11641
+ import { Fragment as Fragment18, jsx as jsx46, jsxs as jsxs41 } from "react/jsx-runtime";
10807
11642
  var PanelCollapsedContext = React40.createContext(false);
10808
11643
  var PanelGroupsContext = React40.createContext({ expandedGroups: /* @__PURE__ */ new Set(), onGroupToggle: () => {
10809
11644
  } });
@@ -10965,7 +11800,7 @@ function Panel({
10965
11800
  "shrink-0 border-b border-border",
10966
11801
  effectiveCollapsed ? "flex items-center justify-center py-3" : "flex items-center gap-2 px-4 py-3"
10967
11802
  ),
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: [
11803
+ 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
11804
  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
11805
  sidebarBrand.title && /* @__PURE__ */ jsx46("span", { className: "flex-1 truncate text-sm font-semibold", children: sidebarBrand.title }),
10971
11806
  sidebarBrand.trailing && /* @__PURE__ */ jsx46("span", { className: "shrink-0", children: sidebarBrand.trailing })
@@ -12044,13 +12879,13 @@ function Timeline({ items, align = "left", className }) {
12044
12879
 
12045
12880
  // src/components/ui/tree-view.tsx
12046
12881
  import * as React48 from "react";
12047
- import { ChevronRight as ChevronRight11, Folder, FolderOpen, File } from "lucide-react";
12882
+ import { ChevronRight as ChevronRight11, Folder, FolderOpen, File as File2 } from "lucide-react";
12048
12883
  import { jsx as jsx60, jsxs as jsxs53 } from "react/jsx-runtime";
12049
12884
  function TreeNodeItem({ node, depth, selected, expanded, onToggleExpand, onSelect, multiple }) {
12050
12885
  const hasChildren = !!node.children?.length;
12051
12886
  const isExpanded = expanded.includes(node.id);
12052
12887
  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" });
12888
+ 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
12889
  return /* @__PURE__ */ jsxs53("div", { children: [
12055
12890
  /* @__PURE__ */ jsxs53(
12056
12891
  "div",
@@ -12274,7 +13109,7 @@ function Widget({
12274
13109
  // src/components/ui/wizard.tsx
12275
13110
  import * as React50 from "react";
12276
13111
  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";
13112
+ import { Fragment as Fragment21, jsx as jsx62, jsxs as jsxs55 } from "react/jsx-runtime";
12278
13113
  var SIZE_MAP = {
12279
13114
  sm: "max-w-sm",
12280
13115
  md: "max-w-lg",
@@ -12484,10 +13319,10 @@ function DefaultActions({
12484
13319
  /* @__PURE__ */ jsx62("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8v8z" })
12485
13320
  ] }),
12486
13321
  "Processing..."
12487
- ] }) : isLast ? /* @__PURE__ */ jsxs55(Fragment20, { children: [
13322
+ ] }) : isLast ? /* @__PURE__ */ jsxs55(Fragment21, { children: [
12488
13323
  /* @__PURE__ */ jsx62(Check7, { className: "h-4 w-4" }),
12489
13324
  finishLabel ?? "Finish"
12490
- ] }) : /* @__PURE__ */ jsxs55(Fragment20, { children: [
13325
+ ] }) : /* @__PURE__ */ jsxs55(Fragment21, { children: [
12491
13326
  nextLabel ?? "Next",
12492
13327
  /* @__PURE__ */ jsx62(ChevronRight12, { className: "h-4 w-4" })
12493
13328
  ] })
@@ -12732,6 +13567,12 @@ var axiosInstance = axios5.create({
12732
13567
  Accept: "application/json"
12733
13568
  }
12734
13569
  });
13570
+ axiosInstance.interceptors.request.use((config) => {
13571
+ if (config.data instanceof FormData) {
13572
+ delete config.headers["Content-Type"];
13573
+ }
13574
+ return config;
13575
+ });
12735
13576
 
12736
13577
  // src/lib/codego/request.ts
12737
13578
  var request = async (config) => {
@@ -12790,13 +13631,13 @@ var setupInterceptors = () => {
12790
13631
 
12791
13632
  // src/lib/codego/provider.tsx
12792
13633
  import * as React51 from "react";
12793
- import { Fragment as Fragment21, jsx as jsx63 } from "react/jsx-runtime";
13634
+ import { Fragment as Fragment22, jsx as jsx63 } from "react/jsx-runtime";
12794
13635
  function CodegoApiProvider({ children }) {
12795
13636
  const { toast } = useToast();
12796
13637
  React51.useEffect(() => {
12797
13638
  setToastFunction(toast);
12798
13639
  }, [toast]);
12799
- return /* @__PURE__ */ jsx63(Fragment21, { children });
13640
+ return /* @__PURE__ */ jsx63(Fragment22, { children });
12800
13641
  }
12801
13642
 
12802
13643
  // src/lib/codego/index.ts