@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.cjs +982 -141
- package/dist/index.d.cts +143 -22
- package/dist/index.d.ts +143 -22
- package/dist/index.global.js +1041 -187
- package/dist/index.js +1013 -172
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -5906,11 +5906,77 @@ function FileUpload({
|
|
|
5906
5906
|
// src/components/ui/repeater.tsx
|
|
5907
5907
|
var import_lucide_react15 = require("lucide-react");
|
|
5908
5908
|
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
5909
|
+
function RepeaterFieldRenderer({
|
|
5910
|
+
field,
|
|
5911
|
+
value,
|
|
5912
|
+
onChange
|
|
5913
|
+
}) {
|
|
5914
|
+
if (field.type === "image") {
|
|
5915
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-col gap-1.5", children: [
|
|
5916
|
+
field.label && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "text-xs font-medium text-muted-foreground", children: field.label }),
|
|
5917
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex items-center gap-3", children: [
|
|
5918
|
+
value && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("img", { src: value, alt: field.key, className: "h-10 w-10 rounded-lg object-cover ring-1 ring-border shrink-0" }),
|
|
5919
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
5920
|
+
Input,
|
|
5921
|
+
{
|
|
5922
|
+
inputMode: "text",
|
|
5923
|
+
value: value ?? "",
|
|
5924
|
+
onChange: (e) => onChange(e.target.value),
|
|
5925
|
+
placeholder: field.placeholder ?? "Image URL"
|
|
5926
|
+
}
|
|
5927
|
+
)
|
|
5928
|
+
] })
|
|
5929
|
+
] });
|
|
5930
|
+
}
|
|
5931
|
+
if (field.type === "attachment") {
|
|
5932
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-col gap-1.5", children: [
|
|
5933
|
+
field.label && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "text-xs font-medium text-muted-foreground", children: field.label }),
|
|
5934
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
5935
|
+
value && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
5936
|
+
"a",
|
|
5937
|
+
{
|
|
5938
|
+
href: value,
|
|
5939
|
+
target: "_blank",
|
|
5940
|
+
rel: "noopener noreferrer",
|
|
5941
|
+
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",
|
|
5942
|
+
children: [
|
|
5943
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react15.Paperclip, { className: "h-3 w-3" }),
|
|
5944
|
+
String(value).split("/").pop()
|
|
5945
|
+
]
|
|
5946
|
+
}
|
|
5947
|
+
),
|
|
5948
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
5949
|
+
Input,
|
|
5950
|
+
{
|
|
5951
|
+
inputMode: "text",
|
|
5952
|
+
value: value ?? "",
|
|
5953
|
+
onChange: (e) => onChange(e.target.value),
|
|
5954
|
+
placeholder: field.placeholder ?? "Attachment URL"
|
|
5955
|
+
}
|
|
5956
|
+
)
|
|
5957
|
+
] })
|
|
5958
|
+
] });
|
|
5959
|
+
}
|
|
5960
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-col gap-1.5", children: [
|
|
5961
|
+
field.label && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "text-xs font-medium text-muted-foreground", children: field.label }),
|
|
5962
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
5963
|
+
Input,
|
|
5964
|
+
{
|
|
5965
|
+
inputMode: "text",
|
|
5966
|
+
value: value ?? "",
|
|
5967
|
+
onChange: (e) => onChange(e.target.value),
|
|
5968
|
+
placeholder: field.placeholder ?? field.key
|
|
5969
|
+
}
|
|
5970
|
+
)
|
|
5971
|
+
] });
|
|
5972
|
+
}
|
|
5909
5973
|
function Repeater({
|
|
5910
5974
|
items,
|
|
5911
5975
|
onAdd,
|
|
5912
5976
|
onRemove,
|
|
5913
5977
|
renderItem,
|
|
5978
|
+
fields,
|
|
5979
|
+
onFieldChange,
|
|
5914
5980
|
addButtonText = "Add Item",
|
|
5915
5981
|
className
|
|
5916
5982
|
}) {
|
|
@@ -5923,7 +5989,15 @@ function Repeater({
|
|
|
5923
5989
|
children: [
|
|
5924
5990
|
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "mt-1 cursor-grab text-muted-foreground/30 group-hover:text-muted-foreground/60 transition-colors shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react15.GripVertical, { className: "h-4 w-4" }) }),
|
|
5925
5991
|
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("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 }),
|
|
5926
|
-
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "flex-1 min-w-0", children:
|
|
5992
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "flex-1 min-w-0", children: fields ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("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__ */ (0, import_jsx_runtime29.jsx)(
|
|
5993
|
+
RepeaterFieldRenderer,
|
|
5994
|
+
{
|
|
5995
|
+
field: f,
|
|
5996
|
+
value: item[f.key],
|
|
5997
|
+
onChange: (v) => onFieldChange?.(index, f.key, v)
|
|
5998
|
+
},
|
|
5999
|
+
f.key
|
|
6000
|
+
)) }) : renderItem ? renderItem(item, index) : null }),
|
|
5927
6001
|
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
5928
6002
|
Button,
|
|
5929
6003
|
{
|
|
@@ -6406,17 +6480,7 @@ var import_react_dom2 = require("react-dom");
|
|
|
6406
6480
|
var import_axios3 = __toESM(require("axios"), 1);
|
|
6407
6481
|
var import_lucide_react17 = require("lucide-react");
|
|
6408
6482
|
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
6409
|
-
|
|
6410
|
-
csrfAxios.interceptors.request.use((config) => {
|
|
6411
|
-
const method = (config.method ?? "").toUpperCase();
|
|
6412
|
-
if (["POST", "PUT", "PATCH", "DELETE"].includes(method)) {
|
|
6413
|
-
const token = document.querySelector('meta[name="csrf-token"]')?.getAttribute("content");
|
|
6414
|
-
if (!token) throw new Error('[Table] CSRF token not found. Add <meta name="csrf-token" content="..."> to your HTML <head>.');
|
|
6415
|
-
config.headers.set("X-CSRF-Token", token);
|
|
6416
|
-
}
|
|
6417
|
-
return config;
|
|
6418
|
-
});
|
|
6419
|
-
function useServerTable({ url, params, encrypt, key, decryptPayloadLog, columnOverrides, debounce = 300, transform, manual = false, refresh: refreshEnabled = false, refreshInterval = 0, hardReload, onSuccess, onError }) {
|
|
6483
|
+
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 }) {
|
|
6420
6484
|
const [data, setData] = React28.useState([]);
|
|
6421
6485
|
const [columns, setColumns] = React28.useState([]);
|
|
6422
6486
|
const [currentPage, setCurrentPage] = React28.useState(1);
|
|
@@ -6426,6 +6490,15 @@ function useServerTable({ url, params, encrypt, key, decryptPayloadLog, columnOv
|
|
|
6426
6490
|
const [tick, setTick] = React28.useState(0);
|
|
6427
6491
|
const [searchValue, setSearchValue] = React28.useState("");
|
|
6428
6492
|
const debounceTimer = React28.useRef(void 0);
|
|
6493
|
+
const [filterValues, setFilterValues] = React28.useState(() => {
|
|
6494
|
+
const init = {};
|
|
6495
|
+
filterFields?.forEach((f) => {
|
|
6496
|
+
init[f.key] = f.type === "checkbox" || f.type === "toggle" ? false : "";
|
|
6497
|
+
});
|
|
6498
|
+
return init;
|
|
6499
|
+
});
|
|
6500
|
+
const [sortKey, setSortKey] = React28.useState("");
|
|
6501
|
+
const [sortDir, setSortDir] = React28.useState("asc");
|
|
6429
6502
|
React28.useEffect(() => {
|
|
6430
6503
|
if (hardReload) hardReload.current = () => setTick((t) => t + 1);
|
|
6431
6504
|
}, [hardReload]);
|
|
@@ -6434,13 +6507,32 @@ function useServerTable({ url, params, encrypt, key, decryptPayloadLog, columnOv
|
|
|
6434
6507
|
const id = setInterval(() => setTick((t) => t + 1), refreshInterval);
|
|
6435
6508
|
return () => clearInterval(id);
|
|
6436
6509
|
}, [refreshInterval]);
|
|
6510
|
+
const activeFilterParams = React28.useMemo(() => {
|
|
6511
|
+
const out = {};
|
|
6512
|
+
filterFields?.forEach((f) => {
|
|
6513
|
+
const v = filterValues[f.key];
|
|
6514
|
+
if (f.type === "checkbox" || f.type === "toggle") {
|
|
6515
|
+
if (v) out[f.key] = 1;
|
|
6516
|
+
} else if (f.type === "date-range") {
|
|
6517
|
+
if (v?.from) out[`${f.key}_from`] = v.from;
|
|
6518
|
+
if (v?.to) out[`${f.key}_to`] = v.to;
|
|
6519
|
+
} else if (v !== "" && v !== null && v !== void 0) {
|
|
6520
|
+
out[f.key] = v;
|
|
6521
|
+
}
|
|
6522
|
+
});
|
|
6523
|
+
if (sortKey) {
|
|
6524
|
+
out.sort = sortKey;
|
|
6525
|
+
out.direction = sortDir;
|
|
6526
|
+
}
|
|
6527
|
+
return out;
|
|
6528
|
+
}, [filterValues, sortKey, sortDir, filterFields]);
|
|
6437
6529
|
React28.useEffect(() => {
|
|
6438
6530
|
if (manual && tick === 0) return;
|
|
6439
6531
|
let cancelled = false;
|
|
6440
6532
|
setLoading(true);
|
|
6441
6533
|
setError(null);
|
|
6442
6534
|
import_axios3.default.get(url, {
|
|
6443
|
-
params: { ...params, page: currentPage, search: searchValue }
|
|
6535
|
+
params: { ...params, ...activeFilterParams, page: currentPage, search: searchValue }
|
|
6444
6536
|
}).then(({ data: res }) => {
|
|
6445
6537
|
if (cancelled) return;
|
|
6446
6538
|
const payload = encrypt ? decryptLaravelPayload(res, key) : res;
|
|
@@ -6486,15 +6578,184 @@ function useServerTable({ url, params, encrypt, key, decryptPayloadLog, columnOv
|
|
|
6486
6578
|
return () => {
|
|
6487
6579
|
cancelled = true;
|
|
6488
6580
|
};
|
|
6489
|
-
}, [url, currentPage, tick, JSON.stringify(params), encrypt, decryptPayloadLog, JSON.stringify(columnOverrides), searchValue]);
|
|
6581
|
+
}, [url, currentPage, tick, JSON.stringify(params), JSON.stringify(activeFilterParams), encrypt, decryptPayloadLog, JSON.stringify(columnOverrides), searchValue]);
|
|
6490
6582
|
const handleSearchChange = (value) => {
|
|
6491
6583
|
setSearchValue(value);
|
|
6492
6584
|
setCurrentPage(1);
|
|
6493
6585
|
if (debounceTimer.current) clearTimeout(debounceTimer.current);
|
|
6494
|
-
debounceTimer.current = setTimeout(() =>
|
|
6495
|
-
|
|
6496
|
-
|
|
6586
|
+
debounceTimer.current = setTimeout(() => setTick((t) => t + 1), debounce);
|
|
6587
|
+
};
|
|
6588
|
+
const handleFilterChange = (key2, value) => {
|
|
6589
|
+
setFilterValues((prev) => ({ ...prev, [key2]: value }));
|
|
6590
|
+
setCurrentPage(1);
|
|
6591
|
+
setTick((t) => t + 1);
|
|
6592
|
+
};
|
|
6593
|
+
const handleClearFilters = () => {
|
|
6594
|
+
const reset = {};
|
|
6595
|
+
filterFields?.forEach((f) => {
|
|
6596
|
+
reset[f.key] = f.type === "checkbox" || f.type === "toggle" ? false : "";
|
|
6597
|
+
});
|
|
6598
|
+
setFilterValues(reset);
|
|
6599
|
+
setSortKey("");
|
|
6600
|
+
setSortDir("asc");
|
|
6601
|
+
setCurrentPage(1);
|
|
6602
|
+
setTick((t) => t + 1);
|
|
6497
6603
|
};
|
|
6604
|
+
const hasActiveFilters = filterFields?.some((f) => {
|
|
6605
|
+
const v = filterValues[f.key];
|
|
6606
|
+
return f.type === "checkbox" || f.type === "toggle" ? !!v : v !== "" && v !== null && v !== void 0;
|
|
6607
|
+
}) || !!sortKey;
|
|
6608
|
+
const filterBar = filterFields?.length || sortKeys?.length ? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-wrap items-end gap-3 rounded-xl border border-border bg-muted/30 px-4 py-3", children: [
|
|
6609
|
+
filterFields?.map((f) => {
|
|
6610
|
+
const label = f.label ?? f.key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
6611
|
+
const value = filterValues[f.key];
|
|
6612
|
+
const opts = (f.options ?? []).map(
|
|
6613
|
+
(o) => typeof o === "string" ? { label: o, value: o } : o
|
|
6614
|
+
);
|
|
6615
|
+
if (f.type === "checkbox") return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer select-none", children: [
|
|
6616
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
6617
|
+
"input",
|
|
6618
|
+
{
|
|
6619
|
+
type: "checkbox",
|
|
6620
|
+
checked: !!value,
|
|
6621
|
+
onChange: (e) => handleFilterChange(f.key, e.target.checked),
|
|
6622
|
+
className: "h-4 w-4 rounded accent-primary"
|
|
6623
|
+
}
|
|
6624
|
+
),
|
|
6625
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-xs font-medium text-foreground", children: label })
|
|
6626
|
+
] }, f.key);
|
|
6627
|
+
if (f.type === "toggle") return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer select-none", children: [
|
|
6628
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
6629
|
+
"button",
|
|
6630
|
+
{
|
|
6631
|
+
type: "button",
|
|
6632
|
+
role: "switch",
|
|
6633
|
+
"aria-checked": !!value,
|
|
6634
|
+
onClick: () => handleFilterChange(f.key, !value),
|
|
6635
|
+
className: cn(
|
|
6636
|
+
"relative inline-flex h-5 w-9 shrink-0 rounded-full border-2 border-transparent transition-colors",
|
|
6637
|
+
value ? "bg-primary" : "bg-muted"
|
|
6638
|
+
),
|
|
6639
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: cn(
|
|
6640
|
+
"pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow-sm transition-transform",
|
|
6641
|
+
value ? "translate-x-4" : "translate-x-0"
|
|
6642
|
+
) })
|
|
6643
|
+
}
|
|
6644
|
+
),
|
|
6645
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-xs font-medium text-foreground", children: label })
|
|
6646
|
+
] }, f.key);
|
|
6647
|
+
if (f.type === "select") return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
6648
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: label }),
|
|
6649
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
6650
|
+
"select",
|
|
6651
|
+
{
|
|
6652
|
+
value: value ?? "",
|
|
6653
|
+
onChange: (e) => handleFilterChange(f.key, e.target.value),
|
|
6654
|
+
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",
|
|
6655
|
+
children: [
|
|
6656
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("option", { value: "", children: f.placeholder ?? `All ${label}` }),
|
|
6657
|
+
opts.map((o) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("option", { value: o.value, children: o.label }, o.value))
|
|
6658
|
+
]
|
|
6659
|
+
}
|
|
6660
|
+
)
|
|
6661
|
+
] }, f.key);
|
|
6662
|
+
if (f.type === "date" || f.type === "date-time") return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
6663
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: label }),
|
|
6664
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
6665
|
+
"input",
|
|
6666
|
+
{
|
|
6667
|
+
type: f.type === "date-time" ? "datetime-local" : "date",
|
|
6668
|
+
value: value ?? "",
|
|
6669
|
+
onChange: (e) => handleFilterChange(f.key, e.target.value),
|
|
6670
|
+
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"
|
|
6671
|
+
}
|
|
6672
|
+
)
|
|
6673
|
+
] }, f.key);
|
|
6674
|
+
if (f.type === "date-range") return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
6675
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: label }),
|
|
6676
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center gap-1.5", children: [
|
|
6677
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
6678
|
+
"input",
|
|
6679
|
+
{
|
|
6680
|
+
type: "date",
|
|
6681
|
+
value: value?.from ?? "",
|
|
6682
|
+
onChange: (e) => handleFilterChange(f.key, { ...value, from: e.target.value }),
|
|
6683
|
+
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"
|
|
6684
|
+
}
|
|
6685
|
+
),
|
|
6686
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-xs text-muted-foreground", children: "\u2013" }),
|
|
6687
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
6688
|
+
"input",
|
|
6689
|
+
{
|
|
6690
|
+
type: "date",
|
|
6691
|
+
value: value?.to ?? "",
|
|
6692
|
+
onChange: (e) => handleFilterChange(f.key, { ...value, to: e.target.value }),
|
|
6693
|
+
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"
|
|
6694
|
+
}
|
|
6695
|
+
)
|
|
6696
|
+
] })
|
|
6697
|
+
] }, f.key);
|
|
6698
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
6699
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: label }),
|
|
6700
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
6701
|
+
"input",
|
|
6702
|
+
{
|
|
6703
|
+
type: "text",
|
|
6704
|
+
value: value ?? "",
|
|
6705
|
+
placeholder: f.placeholder ?? `Filter ${label}\u2026`,
|
|
6706
|
+
onChange: (e) => handleFilterChange(f.key, e.target.value),
|
|
6707
|
+
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"
|
|
6708
|
+
}
|
|
6709
|
+
)
|
|
6710
|
+
] }, f.key);
|
|
6711
|
+
}),
|
|
6712
|
+
sortKeys?.length ? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
6713
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: "Sort by" }),
|
|
6714
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center gap-1.5", children: [
|
|
6715
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
6716
|
+
"select",
|
|
6717
|
+
{
|
|
6718
|
+
value: sortKey,
|
|
6719
|
+
onChange: (e) => {
|
|
6720
|
+
setSortKey(e.target.value);
|
|
6721
|
+
setCurrentPage(1);
|
|
6722
|
+
setTick((t) => t + 1);
|
|
6723
|
+
},
|
|
6724
|
+
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",
|
|
6725
|
+
children: [
|
|
6726
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("option", { value: "", children: "Default" }),
|
|
6727
|
+
sortKeys.map((k) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("option", { value: k, children: k.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()) }, k))
|
|
6728
|
+
]
|
|
6729
|
+
}
|
|
6730
|
+
),
|
|
6731
|
+
sortKey && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
6732
|
+
"button",
|
|
6733
|
+
{
|
|
6734
|
+
type: "button",
|
|
6735
|
+
onClick: () => {
|
|
6736
|
+
setSortDir((d) => d === "asc" ? "desc" : "asc");
|
|
6737
|
+
setTick((t) => t + 1);
|
|
6738
|
+
},
|
|
6739
|
+
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",
|
|
6740
|
+
title: sortDir === "asc" ? "Ascending" : "Descending",
|
|
6741
|
+
children: sortDir === "asc" ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.ChevronUp, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.ChevronDown, { className: "h-3.5 w-3.5" })
|
|
6742
|
+
}
|
|
6743
|
+
)
|
|
6744
|
+
] })
|
|
6745
|
+
] }) : null,
|
|
6746
|
+
hasActiveFilters && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
6747
|
+
"button",
|
|
6748
|
+
{
|
|
6749
|
+
type: "button",
|
|
6750
|
+
onClick: handleClearFilters,
|
|
6751
|
+
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",
|
|
6752
|
+
children: [
|
|
6753
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.X, { className: "h-3 w-3" }),
|
|
6754
|
+
" Clear"
|
|
6755
|
+
]
|
|
6756
|
+
}
|
|
6757
|
+
)
|
|
6758
|
+
] }) : null;
|
|
6498
6759
|
return {
|
|
6499
6760
|
data,
|
|
6500
6761
|
columns,
|
|
@@ -6503,11 +6764,18 @@ function useServerTable({ url, params, encrypt, key, decryptPayloadLog, columnOv
|
|
|
6503
6764
|
serverPagination: pagination ? { pagination, currentPage, goToPage: (page) => setCurrentPage(page) } : null,
|
|
6504
6765
|
loading,
|
|
6505
6766
|
error,
|
|
6767
|
+
filterBar,
|
|
6506
6768
|
goToPage: (page) => setCurrentPage(page),
|
|
6507
6769
|
reload: () => setTick((t) => t + 1),
|
|
6508
6770
|
refresh: () => setTick((t) => t + 1),
|
|
6771
|
+
// Passthrough props
|
|
6509
6772
|
searchValue,
|
|
6510
|
-
onSearchChange: handleSearchChange
|
|
6773
|
+
onSearchChange: handleSearchChange,
|
|
6774
|
+
page: currentPage,
|
|
6775
|
+
onPageChange: (page) => setCurrentPage(page),
|
|
6776
|
+
sort: [],
|
|
6777
|
+
onSortChange: () => {
|
|
6778
|
+
}
|
|
6511
6779
|
};
|
|
6512
6780
|
}
|
|
6513
6781
|
var MODAL_WIDTH = {
|
|
@@ -6565,6 +6833,7 @@ function validateField(field, value) {
|
|
|
6565
6833
|
return null;
|
|
6566
6834
|
}
|
|
6567
6835
|
function FieldRenderer({ field, value, onChange }) {
|
|
6836
|
+
if (field.component) return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_jsx_runtime32.Fragment, { children: field.component });
|
|
6568
6837
|
if (field.render) return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_jsx_runtime32.Fragment, { children: field.render(value, onChange) });
|
|
6569
6838
|
const toLabelValue = (o) => {
|
|
6570
6839
|
if (typeof o === "string") return { label: o, value: o };
|
|
@@ -6657,9 +6926,33 @@ function FieldRenderer({ field, value, onChange }) {
|
|
|
6657
6926
|
case "rich-text":
|
|
6658
6927
|
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(RichTextEditor, { value: value ?? "", onChange: (v) => onChange(v) });
|
|
6659
6928
|
case "file-upload":
|
|
6660
|
-
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(FileUpload, {
|
|
6929
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(FileUpload, { onFileSelect: (file) => onChange(file), onFilesChange: (files) => {
|
|
6930
|
+
if (files.length > 1) onChange(files);
|
|
6931
|
+
} });
|
|
6661
6932
|
case "repeater": {
|
|
6662
6933
|
const items = Array.isArray(value) ? value : [];
|
|
6934
|
+
if (field.repeaterFields) {
|
|
6935
|
+
const rows = Array.isArray(value) ? value : [];
|
|
6936
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
6937
|
+
Repeater,
|
|
6938
|
+
{
|
|
6939
|
+
items: rows,
|
|
6940
|
+
fields: field.repeaterFields,
|
|
6941
|
+
onAdd: () => {
|
|
6942
|
+
const blank = {};
|
|
6943
|
+
field.repeaterFields.forEach((f) => {
|
|
6944
|
+
blank[f.key] = "";
|
|
6945
|
+
});
|
|
6946
|
+
onChange([...rows, blank]);
|
|
6947
|
+
},
|
|
6948
|
+
onRemove: (i) => onChange(rows.filter((_, idx) => idx !== i)),
|
|
6949
|
+
onFieldChange: (i, key, val) => {
|
|
6950
|
+
const next = rows.map((r, idx) => idx === i ? { ...r, [key]: val } : r);
|
|
6951
|
+
onChange(next);
|
|
6952
|
+
}
|
|
6953
|
+
}
|
|
6954
|
+
);
|
|
6955
|
+
}
|
|
6663
6956
|
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
6664
6957
|
Repeater,
|
|
6665
6958
|
{
|
|
@@ -6700,8 +6993,172 @@ function ViewModal({
|
|
|
6700
6993
|
item,
|
|
6701
6994
|
fields,
|
|
6702
6995
|
onClose,
|
|
6703
|
-
width
|
|
6996
|
+
width,
|
|
6997
|
+
grid
|
|
6704
6998
|
}) {
|
|
6999
|
+
const renderViewValue = (f, value) => {
|
|
7000
|
+
const sizeStyle = {
|
|
7001
|
+
...f.width ? { width: typeof f.width === "number" ? `${f.width}px` : f.width } : {},
|
|
7002
|
+
...f.height ? { height: typeof f.height === "number" ? `${f.height}px` : f.height } : {}
|
|
7003
|
+
};
|
|
7004
|
+
const vt = f.viewType ?? f.type;
|
|
7005
|
+
const empty = value === null || value === void 0 || value === "";
|
|
7006
|
+
const dash = /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-muted-foreground italic text-sm", children: "\u2014" });
|
|
7007
|
+
if (f.component) return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_jsx_runtime32.Fragment, { children: f.component });
|
|
7008
|
+
if (f.render) return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_jsx_runtime32.Fragment, { children: f.render(value, () => {
|
|
7009
|
+
}) });
|
|
7010
|
+
switch (vt) {
|
|
7011
|
+
case "image":
|
|
7012
|
+
return empty ? dash : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7013
|
+
"img",
|
|
7014
|
+
{
|
|
7015
|
+
src: value,
|
|
7016
|
+
alt: f.label,
|
|
7017
|
+
className: "rounded-xl object-cover ring-1 ring-border",
|
|
7018
|
+
style: { width: sizeStyle.width ?? 128, height: sizeStyle.height ?? 128 }
|
|
7019
|
+
}
|
|
7020
|
+
);
|
|
7021
|
+
case "image-url":
|
|
7022
|
+
return empty ? dash : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7023
|
+
"a",
|
|
7024
|
+
{
|
|
7025
|
+
href: value,
|
|
7026
|
+
onClick: (e) => e.preventDefault(),
|
|
7027
|
+
className: "inline-block rounded-xl overflow-hidden ring-1 ring-border hover:ring-primary transition-all",
|
|
7028
|
+
style: { width: sizeStyle.width ?? 128, height: sizeStyle.height ?? 128 },
|
|
7029
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("img", { src: value, alt: f.label, className: "w-full h-full object-cover" })
|
|
7030
|
+
}
|
|
7031
|
+
);
|
|
7032
|
+
case "image-url-open-other-tabs":
|
|
7033
|
+
return empty ? dash : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7034
|
+
"a",
|
|
7035
|
+
{
|
|
7036
|
+
href: value,
|
|
7037
|
+
target: "_blank",
|
|
7038
|
+
rel: "noopener noreferrer",
|
|
7039
|
+
className: "inline-block rounded-xl overflow-hidden ring-1 ring-border hover:ring-primary transition-all",
|
|
7040
|
+
style: { width: sizeStyle.width ?? 128, height: sizeStyle.height ?? 128 },
|
|
7041
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("img", { src: value, alt: f.label, className: "w-full h-full object-cover" })
|
|
7042
|
+
}
|
|
7043
|
+
);
|
|
7044
|
+
case "text-url":
|
|
7045
|
+
return empty ? dash : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7046
|
+
"a",
|
|
7047
|
+
{
|
|
7048
|
+
href: value,
|
|
7049
|
+
onClick: (e) => e.preventDefault(),
|
|
7050
|
+
className: "text-sm text-primary underline underline-offset-2 hover:text-primary/80 break-all",
|
|
7051
|
+
style: sizeStyle,
|
|
7052
|
+
children: value
|
|
7053
|
+
}
|
|
7054
|
+
);
|
|
7055
|
+
case "text-url-open-other-tabs":
|
|
7056
|
+
return empty ? dash : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7057
|
+
"a",
|
|
7058
|
+
{
|
|
7059
|
+
href: value,
|
|
7060
|
+
target: "_blank",
|
|
7061
|
+
rel: "noopener noreferrer",
|
|
7062
|
+
className: "text-sm text-primary underline underline-offset-2 hover:text-primary/80 break-all",
|
|
7063
|
+
style: sizeStyle,
|
|
7064
|
+
children: value
|
|
7065
|
+
}
|
|
7066
|
+
);
|
|
7067
|
+
case "attachment":
|
|
7068
|
+
return empty ? dash : /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
7069
|
+
"a",
|
|
7070
|
+
{
|
|
7071
|
+
href: value,
|
|
7072
|
+
target: "_blank",
|
|
7073
|
+
rel: "noopener noreferrer",
|
|
7074
|
+
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",
|
|
7075
|
+
style: sizeStyle,
|
|
7076
|
+
children: [
|
|
7077
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("svg", { className: "h-3.5 w-3.5 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("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" }) }),
|
|
7078
|
+
String(value).split("/").pop() ?? "Download"
|
|
7079
|
+
]
|
|
7080
|
+
}
|
|
7081
|
+
);
|
|
7082
|
+
case "repeater": {
|
|
7083
|
+
const rows = Array.isArray(value) ? value : [];
|
|
7084
|
+
if (!rows.length) return dash;
|
|
7085
|
+
const rFields = f.repeaterFields;
|
|
7086
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "space-y-2", children: rows.map((row, ri) => /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-wrap gap-3 rounded-xl border border-border bg-muted/30 px-3 py-2", children: [
|
|
7087
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("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 }),
|
|
7088
|
+
rFields ? rFields.map((rf) => {
|
|
7089
|
+
const v = row[rf.key];
|
|
7090
|
+
if (rf.type === "image") return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
7091
|
+
rf.label && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-[10px] text-muted-foreground", children: rf.label }),
|
|
7092
|
+
v ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("img", { src: v, alt: rf.key, className: "h-10 w-10 rounded-lg object-cover ring-1 ring-border" }) : dash
|
|
7093
|
+
] }, rf.key);
|
|
7094
|
+
if (rf.type === "attachment") return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
7095
|
+
rf.label && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-[10px] text-muted-foreground", children: rf.label }),
|
|
7096
|
+
v ? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
7097
|
+
"a",
|
|
7098
|
+
{
|
|
7099
|
+
href: v,
|
|
7100
|
+
target: "_blank",
|
|
7101
|
+
rel: "noopener noreferrer",
|
|
7102
|
+
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",
|
|
7103
|
+
children: [
|
|
7104
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("svg", { className: "h-3 w-3 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("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" }) }),
|
|
7105
|
+
String(v).split("/").pop()
|
|
7106
|
+
]
|
|
7107
|
+
}
|
|
7108
|
+
) : dash
|
|
7109
|
+
] }, rf.key);
|
|
7110
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
7111
|
+
rf.label && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-[10px] text-muted-foreground", children: rf.label }),
|
|
7112
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-sm", children: v ?? "\u2014" })
|
|
7113
|
+
] }, rf.key);
|
|
7114
|
+
}) : (
|
|
7115
|
+
// payload mode: row has { type, key, value }
|
|
7116
|
+
Object.entries(row).map(([k, v]) => /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
7117
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-[10px] text-muted-foreground", children: k }),
|
|
7118
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-sm", children: String(v) })
|
|
7119
|
+
] }, k))
|
|
7120
|
+
)
|
|
7121
|
+
] }, ri)) });
|
|
7122
|
+
}
|
|
7123
|
+
case "checkbox":
|
|
7124
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7125
|
+
"input",
|
|
7126
|
+
{
|
|
7127
|
+
type: "checkbox",
|
|
7128
|
+
checked: !!value,
|
|
7129
|
+
readOnly: true,
|
|
7130
|
+
className: "h-4 w-4 rounded border-border accent-primary cursor-default",
|
|
7131
|
+
style: sizeStyle
|
|
7132
|
+
}
|
|
7133
|
+
);
|
|
7134
|
+
case "toggle":
|
|
7135
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7136
|
+
"div",
|
|
7137
|
+
{
|
|
7138
|
+
className: cn(
|
|
7139
|
+
"relative inline-flex shrink-0 rounded-full border-2 border-transparent transition-colors",
|
|
7140
|
+
value ? "bg-primary" : "bg-muted"
|
|
7141
|
+
),
|
|
7142
|
+
style: { width: sizeStyle.width ?? 36, height: sizeStyle.height ?? 20 },
|
|
7143
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7144
|
+
"span",
|
|
7145
|
+
{
|
|
7146
|
+
className: cn(
|
|
7147
|
+
"pointer-events-none inline-block rounded-full bg-white shadow-sm transition-transform"
|
|
7148
|
+
),
|
|
7149
|
+
style: {
|
|
7150
|
+
width: typeof (sizeStyle.height ?? 20) === "number" ? sizeStyle.height - 4 : 16,
|
|
7151
|
+
height: typeof (sizeStyle.height ?? 20) === "number" ? sizeStyle.height - 4 : 16,
|
|
7152
|
+
transform: value ? `translateX(${typeof (sizeStyle.width ?? 36) === "number" ? sizeStyle.width - (sizeStyle.height ?? 20) : 16}px)` : "translateX(0)"
|
|
7153
|
+
}
|
|
7154
|
+
}
|
|
7155
|
+
)
|
|
7156
|
+
}
|
|
7157
|
+
);
|
|
7158
|
+
default:
|
|
7159
|
+
return empty ? dash : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { className: "text-sm text-foreground break-words", style: sizeStyle, children: String(value) });
|
|
7160
|
+
}
|
|
7161
|
+
};
|
|
6705
7162
|
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
6706
7163
|
ModalShell,
|
|
6707
7164
|
{
|
|
@@ -6709,11 +7166,27 @@ function ViewModal({
|
|
|
6709
7166
|
onClose,
|
|
6710
7167
|
width,
|
|
6711
7168
|
footer: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Button, { variant: "outline", size: "sm", onClick: onClose, children: "Close" }),
|
|
6712
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
7169
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7170
|
+
"div",
|
|
7171
|
+
{
|
|
7172
|
+
className: grid ? "grid gap-4" : "space-y-3",
|
|
7173
|
+
style: grid ? { gridTemplateColumns: `repeat(${grid}, minmax(0, 1fr))` } : void 0,
|
|
7174
|
+
children: fields.map((f) => /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
7175
|
+
"div",
|
|
7176
|
+
{
|
|
7177
|
+
style: {
|
|
7178
|
+
...f.colSpan ? { gridColumn: `span ${f.colSpan}` } : {},
|
|
7179
|
+
...f.rowSpan ? { gridRow: `span ${f.rowSpan}` } : {}
|
|
7180
|
+
},
|
|
7181
|
+
children: [
|
|
7182
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { className: "text-xs font-semibold text-muted-foreground mb-1", children: f.label }),
|
|
7183
|
+
renderViewValue(f, item[f.key])
|
|
7184
|
+
]
|
|
7185
|
+
},
|
|
7186
|
+
f.key
|
|
7187
|
+
))
|
|
7188
|
+
}
|
|
7189
|
+
)
|
|
6717
7190
|
}
|
|
6718
7191
|
);
|
|
6719
7192
|
}
|
|
@@ -6731,7 +7204,7 @@ function EditModal({
|
|
|
6731
7204
|
const [form, setForm] = React28.useState(() => {
|
|
6732
7205
|
const init = {};
|
|
6733
7206
|
fields.forEach((f) => {
|
|
6734
|
-
init[f.key] = item[f.key] ?? "";
|
|
7207
|
+
init[f.key] = f.type === "file-upload" ? null : item[f.key] ?? "";
|
|
6735
7208
|
});
|
|
6736
7209
|
return init;
|
|
6737
7210
|
});
|
|
@@ -6743,6 +7216,7 @@ function EditModal({
|
|
|
6743
7216
|
e.preventDefault();
|
|
6744
7217
|
const errs = {};
|
|
6745
7218
|
fields.forEach((f) => {
|
|
7219
|
+
if (f.type === "file-upload" && !form[f.key] && item[f.key]) return;
|
|
6746
7220
|
const msg = validateField(f, form[f.key]);
|
|
6747
7221
|
if (msg) errs[f.key] = msg;
|
|
6748
7222
|
});
|
|
@@ -6754,7 +7228,30 @@ function EditModal({
|
|
|
6754
7228
|
setLoading(true);
|
|
6755
7229
|
setError(null);
|
|
6756
7230
|
try {
|
|
6757
|
-
|
|
7231
|
+
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute("content");
|
|
7232
|
+
if (!csrfToken) throw new Error("[Table] CSRF token not found.");
|
|
7233
|
+
const isFile = (v) => v instanceof File;
|
|
7234
|
+
const isFileArray = (v) => Array.isArray(v) && v.length > 0 && v[0] instanceof File;
|
|
7235
|
+
const hasFiles = Object.values(form).some((v) => isFile(v) || isFileArray(v));
|
|
7236
|
+
let body;
|
|
7237
|
+
if (hasFiles) {
|
|
7238
|
+
const fd = new FormData();
|
|
7239
|
+
fd.append("_method", "PUT");
|
|
7240
|
+
Object.entries(form).forEach(([k, v]) => {
|
|
7241
|
+
if (isFileArray(v)) {
|
|
7242
|
+
v.forEach((f) => fd.append(k, f));
|
|
7243
|
+
} else if (isFile(v)) {
|
|
7244
|
+
fd.append(k, v);
|
|
7245
|
+
} else if (v === null || v === void 0) {
|
|
7246
|
+
} else if (!Array.isArray(v)) {
|
|
7247
|
+
fd.append(k, String(v));
|
|
7248
|
+
}
|
|
7249
|
+
});
|
|
7250
|
+
body = fd;
|
|
7251
|
+
} else {
|
|
7252
|
+
body = form;
|
|
7253
|
+
}
|
|
7254
|
+
await import_axios3.default.put(`${baseUrl}/${itemId}/update`, body, { headers: { "X-CSRF-Token": csrfToken } });
|
|
6758
7255
|
const updated = { ...item, ...form };
|
|
6759
7256
|
if (notif && (notif.type ?? "toast") === "notification") {
|
|
6760
7257
|
setBanner(true);
|
|
@@ -6795,14 +7292,14 @@ function EditModal({
|
|
|
6795
7292
|
),
|
|
6796
7293
|
notif.action && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { children: notif.action })
|
|
6797
7294
|
] }),
|
|
6798
|
-
fields.map((f) => /* @__PURE__ */ (0, import_jsx_runtime32.
|
|
7295
|
+
fields.map((f) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
6799
7296
|
"div",
|
|
6800
7297
|
{
|
|
6801
7298
|
style: {
|
|
6802
7299
|
...f.colSpan ? { gridColumn: `span ${f.colSpan}` } : {},
|
|
6803
7300
|
...f.rowSpan ? { gridRow: `span ${f.rowSpan}` } : {}
|
|
6804
7301
|
},
|
|
6805
|
-
children: [
|
|
7302
|
+
children: f.component ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_jsx_runtime32.Fragment, { children: f.component }) : /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_jsx_runtime32.Fragment, { children: [
|
|
6806
7303
|
f.type !== "checkbox" && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("label", { className: "block text-xs font-semibold text-muted-foreground mb-1", children: [
|
|
6807
7304
|
f.label,
|
|
6808
7305
|
f.required && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-danger ml-0.5", children: "*" })
|
|
@@ -6822,7 +7319,7 @@ function EditModal({
|
|
|
6822
7319
|
}
|
|
6823
7320
|
),
|
|
6824
7321
|
fieldErrors[f.key] && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { className: "text-xs text-danger mt-1", children: fieldErrors[f.key] })
|
|
6825
|
-
]
|
|
7322
|
+
] })
|
|
6826
7323
|
},
|
|
6827
7324
|
f.key
|
|
6828
7325
|
)),
|
|
@@ -6845,7 +7342,9 @@ function DeleteModal({
|
|
|
6845
7342
|
setLoading(true);
|
|
6846
7343
|
setError(null);
|
|
6847
7344
|
try {
|
|
6848
|
-
|
|
7345
|
+
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute("content");
|
|
7346
|
+
if (!csrfToken) throw new Error("[Table] CSRF token not found.");
|
|
7347
|
+
await import_axios3.default.delete(`${baseUrl}/${itemId}/delete?csrfToken=${encodeURIComponent(csrfToken)}`, { headers: { "X-CSRF-Token": csrfToken } });
|
|
6849
7348
|
onSuccess?.(item);
|
|
6850
7349
|
onClose();
|
|
6851
7350
|
} catch (err) {
|
|
@@ -6920,26 +7419,89 @@ var BADGE_COLORS = {
|
|
|
6920
7419
|
function badgeClass(value) {
|
|
6921
7420
|
return BADGE_COLORS[value.toLowerCase()] ?? "bg-primary/10 text-primary border-primary/20";
|
|
6922
7421
|
}
|
|
7422
|
+
function deriveField(key, sample) {
|
|
7423
|
+
const label = key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
7424
|
+
const base = { key, label };
|
|
7425
|
+
if (typeof sample === "boolean") return { ...base, type: "toggle", viewType: "toggle" };
|
|
7426
|
+
if (typeof sample === "number") return { ...base, inputType: "number" };
|
|
7427
|
+
if (Array.isArray(sample)) {
|
|
7428
|
+
if (sample.length === 0 || typeof sample[0] === "string")
|
|
7429
|
+
return { ...base, type: "tag-input" };
|
|
7430
|
+
return base;
|
|
7431
|
+
}
|
|
7432
|
+
if (typeof sample === "string") {
|
|
7433
|
+
if (/\.(png|jpe?g|gif|webp|svg|avif)(\?.*)?$/i.test(sample))
|
|
7434
|
+
return { ...base, type: "input", viewType: "image" };
|
|
7435
|
+
if (/\.(pdf|docx?|xlsx?|csv|zip|pptx?)(\?.*)?$/i.test(sample))
|
|
7436
|
+
return { ...base, type: "input", viewType: "attachment" };
|
|
7437
|
+
if (/^https?:\/\//.test(sample))
|
|
7438
|
+
return { ...base, type: "input", viewType: "text-url-open-other-tabs" };
|
|
7439
|
+
if (sample.length > 120 || /\n/.test(sample))
|
|
7440
|
+
return { ...base, type: "textarea" };
|
|
7441
|
+
if (/password|secret|token/i.test(key))
|
|
7442
|
+
return { ...base, type: "password" };
|
|
7443
|
+
if (/email/i.test(key))
|
|
7444
|
+
return { ...base, inputType: "email" };
|
|
7445
|
+
if (/color|colour/i.test(key) && /^#[0-9a-f]{3,8}$/i.test(sample))
|
|
7446
|
+
return { ...base, type: "color-picker", viewType: "text" };
|
|
7447
|
+
}
|
|
7448
|
+
return base;
|
|
7449
|
+
}
|
|
6923
7450
|
function Table({
|
|
6924
7451
|
data,
|
|
6925
7452
|
columns,
|
|
7453
|
+
loading,
|
|
7454
|
+
emptyState,
|
|
7455
|
+
error: errorProp,
|
|
6926
7456
|
searchable = false,
|
|
6927
7457
|
searchPlaceholder = "Search...",
|
|
6928
|
-
|
|
7458
|
+
searchValue: controlledSearch,
|
|
7459
|
+
onSearchChange,
|
|
7460
|
+
clientPagination = false,
|
|
6929
7461
|
itemsPerPage = 10,
|
|
6930
7462
|
selectable = false,
|
|
6931
7463
|
onBulkDelete,
|
|
6932
7464
|
idKey = "id",
|
|
7465
|
+
bulkDeleteBaseUrl,
|
|
6933
7466
|
defaultActions,
|
|
6934
7467
|
serverPagination,
|
|
6935
|
-
|
|
7468
|
+
variant = "default",
|
|
7469
|
+
className,
|
|
7470
|
+
onRowClick,
|
|
7471
|
+
onRowDoubleClick,
|
|
7472
|
+
rowClassName,
|
|
7473
|
+
expandable = false,
|
|
7474
|
+
renderExpanded,
|
|
7475
|
+
columnVisibility,
|
|
7476
|
+
onColumnVisibilityChange,
|
|
7477
|
+
columnVisibilityIcon,
|
|
7478
|
+
filterBar,
|
|
7479
|
+
filterableIcon,
|
|
7480
|
+
exportable = false,
|
|
7481
|
+
onExport,
|
|
7482
|
+
virtualized = false,
|
|
7483
|
+
draggable = false,
|
|
7484
|
+
onRowReorder,
|
|
7485
|
+
keyboardNavigation = false
|
|
6936
7486
|
}) {
|
|
6937
7487
|
const { toast } = useToast();
|
|
6938
|
-
const
|
|
7488
|
+
const isControlledSearch = controlledSearch !== void 0;
|
|
7489
|
+
const [internalSearch, setInternalSearch] = React28.useState("");
|
|
7490
|
+
const search = isControlledSearch ? controlledSearch : internalSearch;
|
|
7491
|
+
const setSearch = (v) => {
|
|
7492
|
+
if (!isControlledSearch) setInternalSearch(v);
|
|
7493
|
+
onSearchChange?.(v);
|
|
7494
|
+
};
|
|
6939
7495
|
const [currentPage, setCurrentPage] = React28.useState(1);
|
|
6940
7496
|
const [selectedIds, setSelectedIds] = React28.useState([]);
|
|
6941
7497
|
const [sortKey, setSortKey] = React28.useState(null);
|
|
6942
7498
|
const [sortDir, setSortDir] = React28.useState(null);
|
|
7499
|
+
const [bulkLoading, setBulkLoading] = React28.useState(false);
|
|
7500
|
+
const [bulkConfirm, setBulkConfirm] = React28.useState(null);
|
|
7501
|
+
const [filterBarOpen, setFilterBarOpen] = React28.useState(false);
|
|
7502
|
+
const [expandedIds, setExpandedIds] = React28.useState(/* @__PURE__ */ new Set());
|
|
7503
|
+
const [dragOverId, setDragOverId] = React28.useState(null);
|
|
7504
|
+
const [focusedRowIdx, setFocusedRowIdx] = React28.useState(-1);
|
|
6943
7505
|
const [viewItem, setViewItem] = React28.useState(null);
|
|
6944
7506
|
const [editItem, setEditItem] = React28.useState(null);
|
|
6945
7507
|
const [deleteItem, setDeleteItem] = React28.useState(null);
|
|
@@ -6951,15 +7513,17 @@ function Table({
|
|
|
6951
7513
|
const safeBaseUrl = defaultActions?.baseUrl.replace(/\/+$/, "") ?? "";
|
|
6952
7514
|
const autoFields = React28.useMemo(() => {
|
|
6953
7515
|
if (!tableData.length) return [];
|
|
6954
|
-
|
|
6955
|
-
|
|
6956
|
-
label: k.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())
|
|
6957
|
-
}));
|
|
7516
|
+
const row = tableData[0];
|
|
7517
|
+
return Object.keys(row).map((k) => deriveField(k, row[k]));
|
|
6958
7518
|
}, [tableData]);
|
|
6959
7519
|
const editFields = defaultActions?.editForm ?? autoFields;
|
|
6960
7520
|
const viewFields = defaultActions?.viewForm ?? autoFields;
|
|
7521
|
+
const visibleColumns = React28.useMemo(() => {
|
|
7522
|
+
if (!columnVisibility) return columns;
|
|
7523
|
+
return columns.filter((col) => columnVisibility[String(col.key)] !== false);
|
|
7524
|
+
}, [columns, columnVisibility]);
|
|
6961
7525
|
const allColumns = React28.useMemo(() => {
|
|
6962
|
-
if (!defaultActions) return
|
|
7526
|
+
if (!defaultActions) return visibleColumns;
|
|
6963
7527
|
const actionsCol = {
|
|
6964
7528
|
key: "__actions__",
|
|
6965
7529
|
title: "Actions",
|
|
@@ -7008,8 +7572,8 @@ function Table({
|
|
|
7008
7572
|
))
|
|
7009
7573
|
] })
|
|
7010
7574
|
};
|
|
7011
|
-
return defaultActions.position === "first" ? [actionsCol, ...
|
|
7012
|
-
}, [
|
|
7575
|
+
return defaultActions.position === "first" ? [actionsCol, ...visibleColumns] : [...visibleColumns, actionsCol];
|
|
7576
|
+
}, [visibleColumns, defaultActions]);
|
|
7013
7577
|
const handleSort = (key) => {
|
|
7014
7578
|
if (sortKey !== key) {
|
|
7015
7579
|
setSortKey(key);
|
|
@@ -7042,16 +7606,74 @@ function Table({
|
|
|
7042
7606
|
const totalPages = Math.max(1, Math.ceil(filteredData.length / itemsPerPage));
|
|
7043
7607
|
const safePage = Math.min(currentPage, totalPages);
|
|
7044
7608
|
const paginatedData = React28.useMemo(() => {
|
|
7045
|
-
if (!
|
|
7609
|
+
if (!clientPagination) return filteredData;
|
|
7046
7610
|
const start = (safePage - 1) * itemsPerPage;
|
|
7047
7611
|
return filteredData.slice(start, start + itemsPerPage);
|
|
7048
|
-
}, [filteredData,
|
|
7612
|
+
}, [filteredData, clientPagination, safePage, itemsPerPage]);
|
|
7049
7613
|
React28.useEffect(() => {
|
|
7050
7614
|
setCurrentPage(1);
|
|
7051
7615
|
}, [search]);
|
|
7616
|
+
React28.useEffect(() => {
|
|
7617
|
+
if (!keyboardNavigation) return;
|
|
7618
|
+
const handler = (e) => {
|
|
7619
|
+
if (e.key === "ArrowDown") {
|
|
7620
|
+
e.preventDefault();
|
|
7621
|
+
setFocusedRowIdx((i) => Math.min(i + 1, paginatedData.length - 1));
|
|
7622
|
+
}
|
|
7623
|
+
if (e.key === "ArrowUp") {
|
|
7624
|
+
e.preventDefault();
|
|
7625
|
+
setFocusedRowIdx((i) => Math.max(i - 1, 0));
|
|
7626
|
+
}
|
|
7627
|
+
};
|
|
7628
|
+
window.addEventListener("keydown", handler);
|
|
7629
|
+
return () => window.removeEventListener("keydown", handler);
|
|
7630
|
+
}, [keyboardNavigation, paginatedData.length]);
|
|
7052
7631
|
const handleSelectAll = (checked) => setSelectedIds(checked ? paginatedData.map((item) => String(item[idKey])) : []);
|
|
7053
7632
|
const handleSelect = (id, checked) => setSelectedIds((prev) => checked ? [...prev, id] : prev.filter((i) => i !== id));
|
|
7054
7633
|
const allSelected = paginatedData.length > 0 && selectedIds.length === paginatedData.length;
|
|
7634
|
+
const totalRows = serverPagination ? serverPagination.pagination.total : filteredData.length;
|
|
7635
|
+
const unselectedCount = totalRows - selectedIds.length;
|
|
7636
|
+
const handleSelectAllRecords = () => setSelectedIds(filteredData.map((item) => String(item[idKey])));
|
|
7637
|
+
const handleUnselectAll = () => setSelectedIds([]);
|
|
7638
|
+
const execBulkDeleteSelected = async () => {
|
|
7639
|
+
if (!bulkDeleteBaseUrl || selectedIds.length === 0) {
|
|
7640
|
+
onBulkDelete?.(selectedIds);
|
|
7641
|
+
setSelectedIds([]);
|
|
7642
|
+
return;
|
|
7643
|
+
}
|
|
7644
|
+
setBulkLoading(true);
|
|
7645
|
+
try {
|
|
7646
|
+
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute("content");
|
|
7647
|
+
if (!csrfToken) throw new Error("[Table] CSRF token not found.");
|
|
7648
|
+
const safeUrl = bulkDeleteBaseUrl.replace(/\/+$/, "");
|
|
7649
|
+
await import_axios3.default.delete(`${safeUrl}/delete/${selectedIds.join(",")}/selected`, { headers: { "X-CSRF-Token": csrfToken } });
|
|
7650
|
+
setTableData((prev) => prev.filter((r) => !selectedIds.includes(String(r[idKey]))));
|
|
7651
|
+
onBulkDelete?.(selectedIds);
|
|
7652
|
+
setSelectedIds([]);
|
|
7653
|
+
defaultActions?.onReload?.();
|
|
7654
|
+
} catch (err) {
|
|
7655
|
+
console.error("[Table] Bulk delete selected failed:", err?.response?.data?.message ?? err.message);
|
|
7656
|
+
} finally {
|
|
7657
|
+
setBulkLoading(false);
|
|
7658
|
+
}
|
|
7659
|
+
};
|
|
7660
|
+
const execDeleteAll = async () => {
|
|
7661
|
+
if (!bulkDeleteBaseUrl) return;
|
|
7662
|
+
setBulkLoading(true);
|
|
7663
|
+
try {
|
|
7664
|
+
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute("content");
|
|
7665
|
+
if (!csrfToken) throw new Error("[Table] CSRF token not found.");
|
|
7666
|
+
const safeUrl = bulkDeleteBaseUrl.replace(/\/+$/, "");
|
|
7667
|
+
await import_axios3.default.delete(`${safeUrl}/delete/all`, { headers: { "X-CSRF-Token": csrfToken } });
|
|
7668
|
+
setTableData([]);
|
|
7669
|
+
setSelectedIds([]);
|
|
7670
|
+
defaultActions?.onReload?.();
|
|
7671
|
+
} catch (err) {
|
|
7672
|
+
console.error("[Table] Delete all failed:", err?.response?.data?.message ?? err.message);
|
|
7673
|
+
} finally {
|
|
7674
|
+
setBulkLoading(false);
|
|
7675
|
+
}
|
|
7676
|
+
};
|
|
7055
7677
|
const pagePills = React28.useMemo(() => {
|
|
7056
7678
|
if (totalPages <= 5) return Array.from({ length: totalPages }, (_, i) => i + 1);
|
|
7057
7679
|
if (safePage <= 3) return [1, 2, 3, 4, 5];
|
|
@@ -7065,6 +7687,7 @@ function Table({
|
|
|
7065
7687
|
};
|
|
7066
7688
|
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_jsx_runtime32.Fragment, { children: [
|
|
7067
7689
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: cn("w-full space-y-3", className), children: [
|
|
7690
|
+
errorProp && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "rounded-xl border border-danger/30 bg-danger/5 px-4 py-3 text-sm text-danger", children: errorProp }),
|
|
7068
7691
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center justify-between gap-3 flex-wrap", children: [
|
|
7069
7692
|
searchable && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "relative w-72", children: [
|
|
7070
7693
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.Search, { className: "absolute text-primary left-3 top-1/2 -translate-y-1/2 h-4 w-4 z-10" }),
|
|
@@ -7086,33 +7709,127 @@ function Table({
|
|
|
7086
7709
|
}
|
|
7087
7710
|
)
|
|
7088
7711
|
] }),
|
|
7089
|
-
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center gap-2 ml-auto", children: [
|
|
7090
|
-
selectable &&
|
|
7712
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center gap-2 ml-auto flex-wrap", children: [
|
|
7713
|
+
selectable && selectedIds.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_jsx_runtime32.Fragment, { children: [
|
|
7714
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
7715
|
+
"button",
|
|
7716
|
+
{
|
|
7717
|
+
onClick: handleUnselectAll,
|
|
7718
|
+
disabled: bulkLoading,
|
|
7719
|
+
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",
|
|
7720
|
+
children: [
|
|
7721
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.X, { className: "h-3.5 w-3.5" }),
|
|
7722
|
+
"Unselect all ",
|
|
7723
|
+
selectedIds.length
|
|
7724
|
+
]
|
|
7725
|
+
}
|
|
7726
|
+
),
|
|
7727
|
+
unselectedCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
7728
|
+
"button",
|
|
7729
|
+
{
|
|
7730
|
+
onClick: handleSelectAllRecords,
|
|
7731
|
+
disabled: bulkLoading,
|
|
7732
|
+
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",
|
|
7733
|
+
children: [
|
|
7734
|
+
"Select all ",
|
|
7735
|
+
unselectedCount
|
|
7736
|
+
]
|
|
7737
|
+
}
|
|
7738
|
+
),
|
|
7739
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
7740
|
+
"button",
|
|
7741
|
+
{
|
|
7742
|
+
onClick: () => setBulkConfirm("selected"),
|
|
7743
|
+
disabled: bulkLoading,
|
|
7744
|
+
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",
|
|
7745
|
+
children: [
|
|
7746
|
+
bulkLoading ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.Trash2, { className: "h-3.5 w-3.5" }),
|
|
7747
|
+
"Delete ",
|
|
7748
|
+
selectedIds.length,
|
|
7749
|
+
" selected"
|
|
7750
|
+
]
|
|
7751
|
+
}
|
|
7752
|
+
),
|
|
7753
|
+
bulkDeleteBaseUrl && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
7754
|
+
"button",
|
|
7755
|
+
{
|
|
7756
|
+
onClick: () => setBulkConfirm("all"),
|
|
7757
|
+
disabled: bulkLoading,
|
|
7758
|
+
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",
|
|
7759
|
+
children: [
|
|
7760
|
+
bulkLoading ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.Trash2, { className: "h-3.5 w-3.5" }),
|
|
7761
|
+
"Delete all"
|
|
7762
|
+
]
|
|
7763
|
+
}
|
|
7764
|
+
)
|
|
7765
|
+
] }),
|
|
7766
|
+
filterBar && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7091
7767
|
"button",
|
|
7092
7768
|
{
|
|
7093
|
-
onClick: () =>
|
|
7094
|
-
|
|
7095
|
-
|
|
7096
|
-
|
|
7097
|
-
|
|
7098
|
-
children:
|
|
7099
|
-
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.Trash2, { className: "h-3.5 w-3.5" }),
|
|
7100
|
-
"Delete ",
|
|
7101
|
-
selectedIds.length,
|
|
7102
|
-
" selected"
|
|
7103
|
-
]
|
|
7769
|
+
onClick: () => setFilterBarOpen((o) => !o),
|
|
7770
|
+
className: cn(
|
|
7771
|
+
"inline-flex items-center gap-1.5 rounded-lg border px-3 py-1.5 text-xs font-medium transition-colors",
|
|
7772
|
+
filterBarOpen ? "border-primary bg-primary/10 text-primary hover:bg-primary/20" : "border-border bg-muted/50 text-muted-foreground hover:bg-muted"
|
|
7773
|
+
),
|
|
7774
|
+
children: filterableIcon ?? "Filter"
|
|
7104
7775
|
}
|
|
7105
7776
|
),
|
|
7777
|
+
exportable && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "relative group", children: [
|
|
7778
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("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" }),
|
|
7779
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("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__ */ (0, import_jsx_runtime32.jsx)(
|
|
7780
|
+
"button",
|
|
7781
|
+
{
|
|
7782
|
+
onClick: () => onExport?.(type),
|
|
7783
|
+
className: "px-4 py-2 text-xs text-left hover:bg-muted transition-colors capitalize",
|
|
7784
|
+
children: type.toUpperCase()
|
|
7785
|
+
},
|
|
7786
|
+
type
|
|
7787
|
+
)) })
|
|
7788
|
+
] }),
|
|
7789
|
+
columnVisibility && onColumnVisibilityChange && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "relative group", children: [
|
|
7790
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("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" }),
|
|
7791
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("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__ */ (0, import_jsx_runtime32.jsxs)("label", { className: "flex items-center gap-2 px-2 py-1 rounded-lg hover:bg-muted cursor-pointer text-xs", children: [
|
|
7792
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7793
|
+
"input",
|
|
7794
|
+
{
|
|
7795
|
+
type: "checkbox",
|
|
7796
|
+
checked: columnVisibility[String(col.key)] !== false,
|
|
7797
|
+
onChange: (e) => onColumnVisibilityChange({ ...columnVisibility, [String(col.key)]: e.target.checked }),
|
|
7798
|
+
className: "accent-primary"
|
|
7799
|
+
}
|
|
7800
|
+
),
|
|
7801
|
+
col.title
|
|
7802
|
+
] }, String(col.key))) })
|
|
7803
|
+
] }),
|
|
7106
7804
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("span", { className: "text-xs text-muted-foreground", children: [
|
|
7107
|
-
|
|
7805
|
+
totalRows,
|
|
7108
7806
|
" ",
|
|
7109
|
-
|
|
7807
|
+
totalRows === 1 ? "row" : "rows",
|
|
7110
7808
|
search && ` \xB7 filtered from ${tableData.length}`
|
|
7111
7809
|
] })
|
|
7112
7810
|
] })
|
|
7113
7811
|
] }),
|
|
7114
|
-
|
|
7115
|
-
|
|
7812
|
+
filterBar && filterBarOpen && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { children: filterBar }),
|
|
7813
|
+
loading && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center justify-center py-12 text-muted-foreground gap-2", children: [
|
|
7814
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.Loader2, { className: "h-5 w-5 animate-spin" }),
|
|
7815
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-sm", children: "Loading\u2026" })
|
|
7816
|
+
] }),
|
|
7817
|
+
!loading && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: cn(
|
|
7818
|
+
variant === "default" && "rounded-xl border border-border overflow-hidden bg-card/50 backdrop-blur-sm shadow-sm",
|
|
7819
|
+
variant === "zebra" && "rounded-xl border border-border overflow-hidden bg-card/50 backdrop-blur-sm shadow-sm",
|
|
7820
|
+
variant === "card" && "space-y-2",
|
|
7821
|
+
variant === "glass" && "rounded-2xl overflow-hidden border border-white/10 bg-background/30 backdrop-blur-xl shadow-xl",
|
|
7822
|
+
variant === "soft" && "rounded-2xl overflow-hidden bg-card",
|
|
7823
|
+
variant === "soft" && "[box-shadow:6px_6px_12px_hsl(var(--foreground)/0.07),-6px_-6px_12px_hsl(var(--background)/0.8)]",
|
|
7824
|
+
virtualized && "max-h-[520px] overflow-y-auto"
|
|
7825
|
+
), children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: cn("w-full overflow-auto", variant === "card" && "space-y-2"), children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("table", { className: cn("w-full caption-bottom text-sm", variant === "card" && "border-separate border-spacing-y-2"), children: [
|
|
7826
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("tr", { className: cn(
|
|
7827
|
+
variant === "default" && "border-b border-border bg-muted/40",
|
|
7828
|
+
variant === "zebra" && "border-b border-border bg-muted/40",
|
|
7829
|
+
variant === "card" && "[&>th]:bg-transparent",
|
|
7830
|
+
variant === "glass" && "border-b border-white/10 bg-white/5",
|
|
7831
|
+
variant === "soft" && "border-b-0 bg-muted/30"
|
|
7832
|
+
), children: [
|
|
7116
7833
|
selectable && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("th", { className: "h-11 w-[46px] px-4 text-left align-middle", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7117
7834
|
Checkbox,
|
|
7118
7835
|
{
|
|
@@ -7120,13 +7837,16 @@ function Table({
|
|
|
7120
7837
|
onChange: (e) => handleSelectAll(e.target.checked)
|
|
7121
7838
|
}
|
|
7122
7839
|
) }),
|
|
7840
|
+
expandable && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("th", { className: "h-11 w-8" }),
|
|
7123
7841
|
allColumns.map((col, ci) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7124
7842
|
"th",
|
|
7125
7843
|
{
|
|
7126
7844
|
onClick: () => col.sortable && handleSort(String(col.key)),
|
|
7127
7845
|
className: cn(
|
|
7128
7846
|
"h-11 px-4 text-left align-middle text-xs font-semibold uppercase tracking-wider text-muted-foreground select-none whitespace-nowrap",
|
|
7129
|
-
col.sortable && "cursor-pointer hover:text-foreground transition-colors"
|
|
7847
|
+
col.sortable && "cursor-pointer hover:text-foreground transition-colors",
|
|
7848
|
+
variant === "glass" && "text-foreground/70",
|
|
7849
|
+
variant === "soft" && "text-muted-foreground/80"
|
|
7130
7850
|
),
|
|
7131
7851
|
children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("span", { className: "inline-flex items-center", children: [
|
|
7132
7852
|
col.title,
|
|
@@ -7139,9 +7859,9 @@ function Table({
|
|
|
7139
7859
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("tbody", { children: paginatedData.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7140
7860
|
"td",
|
|
7141
7861
|
{
|
|
7142
|
-
colSpan: allColumns.length + (selectable ? 1 : 0),
|
|
7862
|
+
colSpan: allColumns.length + (selectable ? 1 : 0) + (expandable ? 1 : 0),
|
|
7143
7863
|
className: "h-32 text-center align-middle",
|
|
7144
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col items-center gap-1 text-muted-foreground", children: [
|
|
7864
|
+
children: emptyState ?? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col items-center gap-1 text-muted-foreground", children: [
|
|
7145
7865
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.Search, { className: "h-8 w-8 opacity-20" }),
|
|
7146
7866
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-sm", children: "No results found" }),
|
|
7147
7867
|
search && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("button", { onClick: () => setSearch(""), className: "text-xs text-primary hover:underline", children: "Clear search" })
|
|
@@ -7150,85 +7870,168 @@ function Table({
|
|
|
7150
7870
|
) }) : paginatedData.map((item, i) => {
|
|
7151
7871
|
const id = String(item[idKey] || i);
|
|
7152
7872
|
const isSelected = selectedIds.includes(id);
|
|
7153
|
-
|
|
7154
|
-
|
|
7155
|
-
|
|
7156
|
-
|
|
7157
|
-
|
|
7158
|
-
|
|
7159
|
-
|
|
7160
|
-
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
|
|
7178
|
-
|
|
7179
|
-
|
|
7180
|
-
|
|
7181
|
-
|
|
7873
|
+
const isExpanded = expandedIds.has(id);
|
|
7874
|
+
const isFocused = keyboardNavigation && focusedRowIdx === i;
|
|
7875
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(React28.Fragment, { children: [
|
|
7876
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
7877
|
+
"tr",
|
|
7878
|
+
{
|
|
7879
|
+
draggable,
|
|
7880
|
+
tabIndex: keyboardNavigation ? 0 : void 0,
|
|
7881
|
+
onDragStart: draggable ? (e) => {
|
|
7882
|
+
e.dataTransfer.setData("text/plain", id);
|
|
7883
|
+
} : void 0,
|
|
7884
|
+
onDragOver: draggable ? (e) => {
|
|
7885
|
+
e.preventDefault();
|
|
7886
|
+
setDragOverId(id);
|
|
7887
|
+
} : void 0,
|
|
7888
|
+
onDragLeave: draggable ? () => setDragOverId(null) : void 0,
|
|
7889
|
+
onDrop: draggable ? (e) => {
|
|
7890
|
+
e.preventDefault();
|
|
7891
|
+
setDragOverId(null);
|
|
7892
|
+
const fromId = e.dataTransfer.getData("text/plain");
|
|
7893
|
+
if (fromId === id) return;
|
|
7894
|
+
setTableData((prev) => {
|
|
7895
|
+
const fromIdx = prev.findIndex((r) => String(r[idKey] || "") === fromId);
|
|
7896
|
+
const toIdx = prev.findIndex((r) => String(r[idKey] || "") === id);
|
|
7897
|
+
if (fromIdx < 0 || toIdx < 0) return prev;
|
|
7898
|
+
const next = [...prev];
|
|
7899
|
+
const [moved] = next.splice(fromIdx, 1);
|
|
7900
|
+
next.splice(toIdx, 0, moved);
|
|
7901
|
+
onRowReorder?.(next);
|
|
7902
|
+
return next;
|
|
7903
|
+
});
|
|
7904
|
+
} : void 0,
|
|
7905
|
+
onClick: () => {
|
|
7906
|
+
if (expandable) setExpandedIds((prev) => {
|
|
7907
|
+
const s = new Set(prev);
|
|
7908
|
+
s.has(id) ? s.delete(id) : s.add(id);
|
|
7909
|
+
return s;
|
|
7910
|
+
});
|
|
7911
|
+
onRowClick?.(item);
|
|
7912
|
+
if (keyboardNavigation) setFocusedRowIdx(i);
|
|
7913
|
+
},
|
|
7914
|
+
onDoubleClick: () => onRowDoubleClick?.(item),
|
|
7915
|
+
className: cn(
|
|
7916
|
+
// default
|
|
7917
|
+
variant === "default" && "border-b border-border/60 transition-colors last:border-0",
|
|
7918
|
+
variant === "default" && (isSelected ? "bg-primary/5 hover:bg-primary/8" : "hover:bg-muted/30"),
|
|
7919
|
+
// zebra
|
|
7920
|
+
variant === "zebra" && "border-b border-border/40 transition-colors last:border-0",
|
|
7921
|
+
variant === "zebra" && (isSelected ? "bg-primary/8" : i % 2 === 0 ? "bg-card" : "bg-muted/40"),
|
|
7922
|
+
variant === "zebra" && !isSelected && "hover:bg-primary/5",
|
|
7923
|
+
// card
|
|
7924
|
+
variant === "card" && "rounded-xl border border-border bg-card shadow-sm transition-all hover:shadow-md hover:-translate-y-px",
|
|
7925
|
+
variant === "card" && (isSelected ? "border-primary/50 bg-primary/5" : ""),
|
|
7926
|
+
variant === "card" && "[&>td:first-child]:rounded-l-xl [&>td:last-child]:rounded-r-xl",
|
|
7927
|
+
// glass
|
|
7928
|
+
variant === "glass" && "border-b border-white/8 transition-colors last:border-0",
|
|
7929
|
+
variant === "glass" && (isSelected ? "bg-primary/15 hover:bg-primary/20" : "hover:bg-white/5"),
|
|
7930
|
+
// soft
|
|
7931
|
+
variant === "soft" && "transition-all",
|
|
7932
|
+
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"),
|
|
7933
|
+
variant === "soft" && "border-b border-border/30 last:border-0",
|
|
7934
|
+
(onRowClick || onRowDoubleClick || expandable) && "cursor-pointer",
|
|
7935
|
+
draggable && dragOverId === id && "ring-2 ring-inset ring-primary/40",
|
|
7936
|
+
isFocused && "ring-2 ring-inset ring-ring",
|
|
7937
|
+
rowClassName?.(item)
|
|
7938
|
+
),
|
|
7939
|
+
children: [
|
|
7940
|
+
selectable && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("td", { className: "px-4 py-3 align-middle", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7941
|
+
Checkbox,
|
|
7942
|
+
{
|
|
7943
|
+
checked: isSelected,
|
|
7944
|
+
onChange: (e) => handleSelect(id, e.target.checked)
|
|
7945
|
+
}
|
|
7182
7946
|
) }),
|
|
7183
|
-
|
|
7184
|
-
|
|
7185
|
-
|
|
7186
|
-
|
|
7187
|
-
|
|
7188
|
-
|
|
7189
|
-
|
|
7190
|
-
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: cn(
|
|
7203
|
-
"pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow-sm transition-transform",
|
|
7204
|
-
item[col.key] ? "translate-x-4" : "translate-x-0"
|
|
7205
|
-
) })
|
|
7206
|
-
}
|
|
7207
|
-
) : col.type === "color" ? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
7208
|
-
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7209
|
-
"input",
|
|
7947
|
+
expandable && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("td", { className: "w-8 px-2 py-3 align-middle", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.ChevronRight, { className: cn("h-3.5 w-3.5 text-muted-foreground transition-transform", isExpanded && "rotate-90") }) }),
|
|
7948
|
+
allColumns.map((col, ci) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("td", { className: "px-4 py-3 align-middle", children: col.render ? col.render(item) : col.type === "image" ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7949
|
+
"img",
|
|
7950
|
+
{
|
|
7951
|
+
src: item[col.key],
|
|
7952
|
+
alt: item[col.key],
|
|
7953
|
+
className: "h-9 w-9 rounded-lg object-cover ring-1 ring-border"
|
|
7954
|
+
}
|
|
7955
|
+
) : col.type === "badge" ? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("span", { className: cn(
|
|
7956
|
+
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-medium",
|
|
7957
|
+
badgeClass(String(item[col.key]))
|
|
7958
|
+
), children: [
|
|
7959
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: cn(
|
|
7960
|
+
"mr-1.5 h-1.5 w-1.5 rounded-full",
|
|
7961
|
+
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"
|
|
7962
|
+
) }),
|
|
7963
|
+
item[col.key]
|
|
7964
|
+
] }) : col.type === "stack" ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(AvatarStack, { images: Array.isArray(item[col.key]) ? item[col.key] : [], ...col.stackProps ?? {} }) : col.type === "icon" ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "flex items-center", children: item[col.key] }) : col.type === "select" ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7965
|
+
"select",
|
|
7210
7966
|
{
|
|
7211
|
-
|
|
7212
|
-
value: item[col.key] || "#000000",
|
|
7967
|
+
value: item[col.key],
|
|
7213
7968
|
onChange: (e) => col.onChange?.(item, e.target.value),
|
|
7214
|
-
className: "h-
|
|
7969
|
+
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",
|
|
7970
|
+
children: (col.selectOptions ?? []).map((opt) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("option", { value: opt, children: opt }, opt))
|
|
7215
7971
|
}
|
|
7216
|
-
),
|
|
7217
|
-
|
|
7218
|
-
|
|
7219
|
-
|
|
7220
|
-
|
|
7221
|
-
|
|
7222
|
-
|
|
7223
|
-
|
|
7224
|
-
|
|
7225
|
-
|
|
7226
|
-
|
|
7227
|
-
|
|
7228
|
-
|
|
7972
|
+
) : col.type === "toggle" ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7973
|
+
"button",
|
|
7974
|
+
{
|
|
7975
|
+
role: "switch",
|
|
7976
|
+
"aria-checked": !!item[col.key],
|
|
7977
|
+
onClick: () => col.onChange?.(item, !item[col.key]),
|
|
7978
|
+
className: cn(
|
|
7979
|
+
"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",
|
|
7980
|
+
item[col.key] ? "bg-primary" : "bg-muted"
|
|
7981
|
+
),
|
|
7982
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: cn(
|
|
7983
|
+
"pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow-sm transition-transform",
|
|
7984
|
+
item[col.key] ? "translate-x-4" : "translate-x-0"
|
|
7985
|
+
) })
|
|
7986
|
+
}
|
|
7987
|
+
) : col.type === "color" ? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
7988
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7989
|
+
"input",
|
|
7990
|
+
{
|
|
7991
|
+
type: "color",
|
|
7992
|
+
value: item[col.key] || "#000000",
|
|
7993
|
+
onChange: (e) => col.onChange?.(item, e.target.value),
|
|
7994
|
+
className: "h-7 w-7 cursor-pointer rounded border border-border bg-transparent p-0.5"
|
|
7995
|
+
}
|
|
7996
|
+
),
|
|
7997
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-xs text-muted-foreground font-mono", children: item[col.key] })
|
|
7998
|
+
] }) : col.type === "checkbox" ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7999
|
+
Checkbox,
|
|
8000
|
+
{
|
|
8001
|
+
checked: !!item[col.key],
|
|
8002
|
+
onChange: (e) => col.onChange?.(item, e.target.checked)
|
|
8003
|
+
}
|
|
8004
|
+
) : col.type === "text-url" ? (() => {
|
|
8005
|
+
const href = col.redirect ? typeof col.redirect === "function" ? col.redirect(item) : col.redirect : String(item[col.key] ?? "");
|
|
8006
|
+
const colorMap = {
|
|
8007
|
+
primary: "var(--primary)",
|
|
8008
|
+
info: "var(--info)",
|
|
8009
|
+
success: "var(--success)",
|
|
8010
|
+
warning: "var(--warning)",
|
|
8011
|
+
danger: "var(--danger)"
|
|
8012
|
+
};
|
|
8013
|
+
const underline = col.underlineColor ? colorMap[col.underlineColor] ?? col.underlineColor : "var(--primary)";
|
|
8014
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
8015
|
+
"a",
|
|
8016
|
+
{
|
|
8017
|
+
href,
|
|
8018
|
+
target: col.openNewTab ? "_blank" : void 0,
|
|
8019
|
+
rel: col.openNewTab ? "noopener noreferrer" : void 0,
|
|
8020
|
+
style: { textDecorationColor: underline },
|
|
8021
|
+
className: "text-sm underline underline-offset-2 hover:opacity-75 transition-opacity break-all",
|
|
8022
|
+
onClick: col.openNewTab ? void 0 : (e) => e.preventDefault(),
|
|
8023
|
+
children: item[col.key]
|
|
8024
|
+
}
|
|
8025
|
+
);
|
|
8026
|
+
})() : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-foreground/90", children: item[col.key] }) }, `${String(col.key)}-${ci}`))
|
|
8027
|
+
]
|
|
8028
|
+
}
|
|
8029
|
+
),
|
|
8030
|
+
expandable && isExpanded && renderExpanded && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("tr", { className: "bg-muted/20 border-b border-border/60", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("td", { colSpan: allColumns.length + (selectable ? 1 : 0) + 1, className: "px-6 py-3", children: renderExpanded(item) }) })
|
|
8031
|
+
] }, id);
|
|
7229
8032
|
}) })
|
|
7230
8033
|
] }) }) }),
|
|
7231
|
-
|
|
8034
|
+
clientPagination && !serverPagination && totalPages > 1 && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
|
|
7232
8035
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("span", { className: "text-xs text-muted-foreground", children: [
|
|
7233
8036
|
"Showing ",
|
|
7234
8037
|
(safePage - 1) * itemsPerPage + 1,
|
|
@@ -7279,8 +8082,8 @@ function Table({
|
|
|
7279
8082
|
] })
|
|
7280
8083
|
] }),
|
|
7281
8084
|
serverPagination && (() => {
|
|
7282
|
-
const { pagination
|
|
7283
|
-
const totalServerPages =
|
|
8085
|
+
const { pagination, currentPage: cp, goToPage } = serverPagination;
|
|
8086
|
+
const totalServerPages = pagination.last_page ?? Math.ceil(pagination.total / pagination.per_page);
|
|
7284
8087
|
const pills = [];
|
|
7285
8088
|
if (totalServerPages <= 7) {
|
|
7286
8089
|
for (let i = 1; i <= totalServerPages; i++) pills.push(i);
|
|
@@ -7293,7 +8096,7 @@ function Table({
|
|
|
7293
8096
|
}
|
|
7294
8097
|
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
|
|
7295
8098
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("span", { className: "text-xs text-muted-foreground", children: [
|
|
7296
|
-
|
|
8099
|
+
pagination.total,
|
|
7297
8100
|
" total rows \xB7 page ",
|
|
7298
8101
|
cp,
|
|
7299
8102
|
" of ",
|
|
@@ -7304,7 +8107,7 @@ function Table({
|
|
|
7304
8107
|
"button",
|
|
7305
8108
|
{
|
|
7306
8109
|
onClick: () => goToPage(cp - 1),
|
|
7307
|
-
disabled: !
|
|
8110
|
+
disabled: !pagination.prev_page_url,
|
|
7308
8111
|
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",
|
|
7309
8112
|
children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.ChevronLeft, { className: "h-4 w-4" })
|
|
7310
8113
|
}
|
|
@@ -7327,7 +8130,7 @@ function Table({
|
|
|
7327
8130
|
"button",
|
|
7328
8131
|
{
|
|
7329
8132
|
onClick: () => goToPage(cp + 1),
|
|
7330
|
-
disabled: !
|
|
8133
|
+
disabled: !pagination.next_page_url,
|
|
7331
8134
|
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",
|
|
7332
8135
|
children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.ChevronRight, { className: "h-4 w-4" })
|
|
7333
8136
|
}
|
|
@@ -7342,6 +8145,7 @@ function Table({
|
|
|
7342
8145
|
item: viewItem,
|
|
7343
8146
|
fields: viewFields,
|
|
7344
8147
|
width: defaultActions.modalWidth,
|
|
8148
|
+
grid: defaultActions.viewFormGrid,
|
|
7345
8149
|
onClose: () => setViewItem(null)
|
|
7346
8150
|
}
|
|
7347
8151
|
),
|
|
@@ -7399,6 +8203,37 @@ function Table({
|
|
|
7399
8203
|
}
|
|
7400
8204
|
}
|
|
7401
8205
|
}
|
|
8206
|
+
),
|
|
8207
|
+
bulkConfirm && (0, import_react_dom2.createPortal)(
|
|
8208
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
8209
|
+
"div",
|
|
8210
|
+
{
|
|
8211
|
+
className: "fixed inset-0 z-50 flex items-center justify-center p-4",
|
|
8212
|
+
style: { background: "rgba(0,0,0,0.5)" },
|
|
8213
|
+
onMouseDown: (e) => {
|
|
8214
|
+
if (e.target === e.currentTarget) setBulkConfirm(null);
|
|
8215
|
+
},
|
|
8216
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "relative w-full max-w-md rounded-2xl border border-border bg-card shadow-2xl flex flex-col", children: [
|
|
8217
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center justify-between px-6 py-4 border-b border-border", children: [
|
|
8218
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("h2", { className: "text-base font-semibold", children: "Confirm Delete" }),
|
|
8219
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("button", { onClick: () => setBulkConfirm(null), className: "text-muted-foreground hover:text-foreground transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.X, { className: "h-4 w-4" }) })
|
|
8220
|
+
] }),
|
|
8221
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "px-6 py-4", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("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." }) }),
|
|
8222
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "px-6 py-4 border-t border-border flex justify-end gap-2", children: [
|
|
8223
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Button, { variant: "outline", size: "sm", onClick: () => setBulkConfirm(null), disabled: bulkLoading, children: "Cancel" }),
|
|
8224
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(Button, { variant: "danger", size: "sm", disabled: bulkLoading, onClick: async () => {
|
|
8225
|
+
if (bulkConfirm === "selected") await execBulkDeleteSelected();
|
|
8226
|
+
else await execDeleteAll();
|
|
8227
|
+
setBulkConfirm(null);
|
|
8228
|
+
}, children: [
|
|
8229
|
+
bulkLoading && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.Loader2, { className: "h-3.5 w-3.5 mr-1.5 animate-spin" }),
|
|
8230
|
+
bulkLoading ? "Deleting\u2026" : "Delete"
|
|
8231
|
+
] })
|
|
8232
|
+
] })
|
|
8233
|
+
] })
|
|
8234
|
+
}
|
|
8235
|
+
),
|
|
8236
|
+
document.body
|
|
7402
8237
|
)
|
|
7403
8238
|
] });
|
|
7404
8239
|
}
|
|
@@ -12853,6 +13688,12 @@ var axiosInstance = import_axios5.default.create({
|
|
|
12853
13688
|
Accept: "application/json"
|
|
12854
13689
|
}
|
|
12855
13690
|
});
|
|
13691
|
+
axiosInstance.interceptors.request.use((config) => {
|
|
13692
|
+
if (config.data instanceof FormData) {
|
|
13693
|
+
delete config.headers["Content-Type"];
|
|
13694
|
+
}
|
|
13695
|
+
return config;
|
|
13696
|
+
});
|
|
12856
13697
|
|
|
12857
13698
|
// src/lib/codego/request.ts
|
|
12858
13699
|
var request = async (config) => {
|