@juv/codego-react-ui 3.4.11 → 3.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +989 -144
- package/dist/index.d.cts +143 -22
- package/dist/index.d.ts +143 -22
- package/dist/index.global.js +1048 -190
- package/dist/index.js +1020 -175
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2237,13 +2237,13 @@ function BulletinBoard({
|
|
|
2237
2237
|
const categories = React8.useMemo(() => {
|
|
2238
2238
|
if (categoriesProp) return categoriesProp;
|
|
2239
2239
|
const set = /* @__PURE__ */ new Set();
|
|
2240
|
-
items.forEach((i) => {
|
|
2240
|
+
(items ?? []).forEach((i) => {
|
|
2241
2241
|
if (i.category) set.add(i.category);
|
|
2242
2242
|
});
|
|
2243
2243
|
return Array.from(set);
|
|
2244
2244
|
}, [items, categoriesProp]);
|
|
2245
2245
|
const filtered = React8.useMemo(() => {
|
|
2246
|
-
let list = items;
|
|
2246
|
+
let list = items ?? [];
|
|
2247
2247
|
if (search.trim()) {
|
|
2248
2248
|
const q = search.toLowerCase();
|
|
2249
2249
|
list = list.filter(
|
|
@@ -2252,7 +2252,7 @@ function BulletinBoard({
|
|
|
2252
2252
|
}
|
|
2253
2253
|
if (category) list = list.filter((i) => i.category === category);
|
|
2254
2254
|
return [...list].sort((a, b) => (b.pinned ? 1 : 0) - (a.pinned ? 1 : 0));
|
|
2255
|
-
}, [items, search, category]);
|
|
2255
|
+
}, [items ?? [], search, category]);
|
|
2256
2256
|
const showToolbar = searchable || filterable && categories.length > 0;
|
|
2257
2257
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: cn("flex flex-col gap-4", className), children: [
|
|
2258
2258
|
showHeader && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
|
|
@@ -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,78 @@ 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
|
+
toast({ variant: "success", title: "Deleted", description: `${selectedIds.length} record${selectedIds.length !== 1 ? "s" : ""} deleted successfully.` });
|
|
7655
|
+
} catch (err) {
|
|
7656
|
+
const msg = err?.response?.data?.message ?? err.message ?? "Bulk delete failed";
|
|
7657
|
+
toast({ variant: "error", title: "Delete failed", description: msg });
|
|
7658
|
+
} finally {
|
|
7659
|
+
setBulkLoading(false);
|
|
7660
|
+
}
|
|
7661
|
+
};
|
|
7662
|
+
const execDeleteAll = async () => {
|
|
7663
|
+
if (!bulkDeleteBaseUrl) return;
|
|
7664
|
+
setBulkLoading(true);
|
|
7665
|
+
try {
|
|
7666
|
+
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute("content");
|
|
7667
|
+
if (!csrfToken) throw new Error("[Table] CSRF token not found.");
|
|
7668
|
+
const safeUrl = bulkDeleteBaseUrl.replace(/\/+$/, "");
|
|
7669
|
+
await import_axios3.default.delete(`${safeUrl}/delete/all`, { headers: { "X-CSRF-Token": csrfToken } });
|
|
7670
|
+
setTableData([]);
|
|
7671
|
+
setSelectedIds([]);
|
|
7672
|
+
defaultActions?.onReload?.();
|
|
7673
|
+
toast({ variant: "success", title: "Deleted", description: "All records deleted successfully." });
|
|
7674
|
+
} catch (err) {
|
|
7675
|
+
const msg = err?.response?.data?.message ?? err.message ?? "Delete all failed";
|
|
7676
|
+
toast({ variant: "error", title: "Delete failed", description: msg });
|
|
7677
|
+
} finally {
|
|
7678
|
+
setBulkLoading(false);
|
|
7679
|
+
}
|
|
7680
|
+
};
|
|
7055
7681
|
const pagePills = React28.useMemo(() => {
|
|
7056
7682
|
if (totalPages <= 5) return Array.from({ length: totalPages }, (_, i) => i + 1);
|
|
7057
7683
|
if (safePage <= 3) return [1, 2, 3, 4, 5];
|
|
@@ -7065,6 +7691,7 @@ function Table({
|
|
|
7065
7691
|
};
|
|
7066
7692
|
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_jsx_runtime32.Fragment, { children: [
|
|
7067
7693
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: cn("w-full space-y-3", className), children: [
|
|
7694
|
+
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
7695
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center justify-between gap-3 flex-wrap", children: [
|
|
7069
7696
|
searchable && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "relative w-72", children: [
|
|
7070
7697
|
/* @__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 +7713,127 @@ function Table({
|
|
|
7086
7713
|
}
|
|
7087
7714
|
)
|
|
7088
7715
|
] }),
|
|
7089
|
-
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center gap-2 ml-auto", children: [
|
|
7090
|
-
selectable &&
|
|
7716
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center gap-2 ml-auto flex-wrap", children: [
|
|
7717
|
+
selectable && selectedIds.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_jsx_runtime32.Fragment, { children: [
|
|
7718
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
7719
|
+
"button",
|
|
7720
|
+
{
|
|
7721
|
+
onClick: handleUnselectAll,
|
|
7722
|
+
disabled: bulkLoading,
|
|
7723
|
+
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",
|
|
7724
|
+
children: [
|
|
7725
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.X, { className: "h-3.5 w-3.5" }),
|
|
7726
|
+
"Unselect all ",
|
|
7727
|
+
selectedIds.length
|
|
7728
|
+
]
|
|
7729
|
+
}
|
|
7730
|
+
),
|
|
7731
|
+
unselectedCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
7732
|
+
"button",
|
|
7733
|
+
{
|
|
7734
|
+
onClick: handleSelectAllRecords,
|
|
7735
|
+
disabled: bulkLoading,
|
|
7736
|
+
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",
|
|
7737
|
+
children: [
|
|
7738
|
+
"Select all ",
|
|
7739
|
+
unselectedCount
|
|
7740
|
+
]
|
|
7741
|
+
}
|
|
7742
|
+
),
|
|
7743
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
7744
|
+
"button",
|
|
7745
|
+
{
|
|
7746
|
+
onClick: () => setBulkConfirm("selected"),
|
|
7747
|
+
disabled: bulkLoading,
|
|
7748
|
+
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",
|
|
7749
|
+
children: [
|
|
7750
|
+
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" }),
|
|
7751
|
+
"Delete ",
|
|
7752
|
+
selectedIds.length,
|
|
7753
|
+
" selected"
|
|
7754
|
+
]
|
|
7755
|
+
}
|
|
7756
|
+
),
|
|
7757
|
+
bulkDeleteBaseUrl && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
7758
|
+
"button",
|
|
7759
|
+
{
|
|
7760
|
+
onClick: () => setBulkConfirm("all"),
|
|
7761
|
+
disabled: bulkLoading,
|
|
7762
|
+
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",
|
|
7763
|
+
children: [
|
|
7764
|
+
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" }),
|
|
7765
|
+
"Delete all"
|
|
7766
|
+
]
|
|
7767
|
+
}
|
|
7768
|
+
)
|
|
7769
|
+
] }),
|
|
7770
|
+
filterBar && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7091
7771
|
"button",
|
|
7092
7772
|
{
|
|
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
|
-
]
|
|
7773
|
+
onClick: () => setFilterBarOpen((o) => !o),
|
|
7774
|
+
className: cn(
|
|
7775
|
+
"inline-flex items-center gap-1.5 rounded-lg border px-3 py-1.5 text-xs font-medium transition-colors",
|
|
7776
|
+
filterBarOpen ? "border-primary bg-primary/10 text-primary hover:bg-primary/20" : "border-border bg-muted/50 text-muted-foreground hover:bg-muted"
|
|
7777
|
+
),
|
|
7778
|
+
children: filterableIcon ?? "Filter"
|
|
7104
7779
|
}
|
|
7105
7780
|
),
|
|
7781
|
+
exportable && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "relative group", children: [
|
|
7782
|
+
/* @__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" }),
|
|
7783
|
+
/* @__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)(
|
|
7784
|
+
"button",
|
|
7785
|
+
{
|
|
7786
|
+
onClick: () => onExport?.(type),
|
|
7787
|
+
className: "px-4 py-2 text-xs text-left hover:bg-muted transition-colors capitalize",
|
|
7788
|
+
children: type.toUpperCase()
|
|
7789
|
+
},
|
|
7790
|
+
type
|
|
7791
|
+
)) })
|
|
7792
|
+
] }),
|
|
7793
|
+
columnVisibility && onColumnVisibilityChange && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "relative group", children: [
|
|
7794
|
+
/* @__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" }),
|
|
7795
|
+
/* @__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: [
|
|
7796
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7797
|
+
"input",
|
|
7798
|
+
{
|
|
7799
|
+
type: "checkbox",
|
|
7800
|
+
checked: columnVisibility[String(col.key)] !== false,
|
|
7801
|
+
onChange: (e) => onColumnVisibilityChange({ ...columnVisibility, [String(col.key)]: e.target.checked }),
|
|
7802
|
+
className: "accent-primary"
|
|
7803
|
+
}
|
|
7804
|
+
),
|
|
7805
|
+
col.title
|
|
7806
|
+
] }, String(col.key))) })
|
|
7807
|
+
] }),
|
|
7106
7808
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("span", { className: "text-xs text-muted-foreground", children: [
|
|
7107
|
-
|
|
7809
|
+
totalRows,
|
|
7108
7810
|
" ",
|
|
7109
|
-
|
|
7811
|
+
totalRows === 1 ? "row" : "rows",
|
|
7110
7812
|
search && ` \xB7 filtered from ${tableData.length}`
|
|
7111
7813
|
] })
|
|
7112
7814
|
] })
|
|
7113
7815
|
] }),
|
|
7114
|
-
|
|
7115
|
-
|
|
7816
|
+
filterBar && filterBarOpen && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { children: filterBar }),
|
|
7817
|
+
loading && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center justify-center py-12 text-muted-foreground gap-2", children: [
|
|
7818
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.Loader2, { className: "h-5 w-5 animate-spin" }),
|
|
7819
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-sm", children: "Loading\u2026" })
|
|
7820
|
+
] }),
|
|
7821
|
+
!loading && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: cn(
|
|
7822
|
+
variant === "default" && "rounded-xl border border-border overflow-hidden bg-card/50 backdrop-blur-sm shadow-sm",
|
|
7823
|
+
variant === "zebra" && "rounded-xl border border-border overflow-hidden bg-card/50 backdrop-blur-sm shadow-sm",
|
|
7824
|
+
variant === "card" && "space-y-2",
|
|
7825
|
+
variant === "glass" && "rounded-2xl overflow-hidden border border-white/10 bg-background/30 backdrop-blur-xl shadow-xl",
|
|
7826
|
+
variant === "soft" && "rounded-2xl overflow-hidden bg-card",
|
|
7827
|
+
variant === "soft" && "[box-shadow:6px_6px_12px_hsl(var(--foreground)/0.07),-6px_-6px_12px_hsl(var(--background)/0.8)]",
|
|
7828
|
+
virtualized && "max-h-[520px] overflow-y-auto"
|
|
7829
|
+
), 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: [
|
|
7830
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("tr", { className: cn(
|
|
7831
|
+
variant === "default" && "border-b border-border bg-muted/40",
|
|
7832
|
+
variant === "zebra" && "border-b border-border bg-muted/40",
|
|
7833
|
+
variant === "card" && "[&>th]:bg-transparent",
|
|
7834
|
+
variant === "glass" && "border-b border-white/10 bg-white/5",
|
|
7835
|
+
variant === "soft" && "border-b-0 bg-muted/30"
|
|
7836
|
+
), children: [
|
|
7116
7837
|
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
7838
|
Checkbox,
|
|
7118
7839
|
{
|
|
@@ -7120,13 +7841,16 @@ function Table({
|
|
|
7120
7841
|
onChange: (e) => handleSelectAll(e.target.checked)
|
|
7121
7842
|
}
|
|
7122
7843
|
) }),
|
|
7844
|
+
expandable && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("th", { className: "h-11 w-8" }),
|
|
7123
7845
|
allColumns.map((col, ci) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7124
7846
|
"th",
|
|
7125
7847
|
{
|
|
7126
7848
|
onClick: () => col.sortable && handleSort(String(col.key)),
|
|
7127
7849
|
className: cn(
|
|
7128
7850
|
"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"
|
|
7851
|
+
col.sortable && "cursor-pointer hover:text-foreground transition-colors",
|
|
7852
|
+
variant === "glass" && "text-foreground/70",
|
|
7853
|
+
variant === "soft" && "text-muted-foreground/80"
|
|
7130
7854
|
),
|
|
7131
7855
|
children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("span", { className: "inline-flex items-center", children: [
|
|
7132
7856
|
col.title,
|
|
@@ -7139,9 +7863,9 @@ function Table({
|
|
|
7139
7863
|
/* @__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
7864
|
"td",
|
|
7141
7865
|
{
|
|
7142
|
-
colSpan: allColumns.length + (selectable ? 1 : 0),
|
|
7866
|
+
colSpan: allColumns.length + (selectable ? 1 : 0) + (expandable ? 1 : 0),
|
|
7143
7867
|
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: [
|
|
7868
|
+
children: emptyState ?? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col items-center gap-1 text-muted-foreground", children: [
|
|
7145
7869
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.Search, { className: "h-8 w-8 opacity-20" }),
|
|
7146
7870
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-sm", children: "No results found" }),
|
|
7147
7871
|
search && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("button", { onClick: () => setSearch(""), className: "text-xs text-primary hover:underline", children: "Clear search" })
|
|
@@ -7150,85 +7874,168 @@ function Table({
|
|
|
7150
7874
|
) }) : paginatedData.map((item, i) => {
|
|
7151
7875
|
const id = String(item[idKey] || i);
|
|
7152
7876
|
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
|
-
|
|
7877
|
+
const isExpanded = expandedIds.has(id);
|
|
7878
|
+
const isFocused = keyboardNavigation && focusedRowIdx === i;
|
|
7879
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(React28.Fragment, { children: [
|
|
7880
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
7881
|
+
"tr",
|
|
7882
|
+
{
|
|
7883
|
+
draggable,
|
|
7884
|
+
tabIndex: keyboardNavigation ? 0 : void 0,
|
|
7885
|
+
onDragStart: draggable ? (e) => {
|
|
7886
|
+
e.dataTransfer.setData("text/plain", id);
|
|
7887
|
+
} : void 0,
|
|
7888
|
+
onDragOver: draggable ? (e) => {
|
|
7889
|
+
e.preventDefault();
|
|
7890
|
+
setDragOverId(id);
|
|
7891
|
+
} : void 0,
|
|
7892
|
+
onDragLeave: draggable ? () => setDragOverId(null) : void 0,
|
|
7893
|
+
onDrop: draggable ? (e) => {
|
|
7894
|
+
e.preventDefault();
|
|
7895
|
+
setDragOverId(null);
|
|
7896
|
+
const fromId = e.dataTransfer.getData("text/plain");
|
|
7897
|
+
if (fromId === id) return;
|
|
7898
|
+
setTableData((prev) => {
|
|
7899
|
+
const fromIdx = prev.findIndex((r) => String(r[idKey] || "") === fromId);
|
|
7900
|
+
const toIdx = prev.findIndex((r) => String(r[idKey] || "") === id);
|
|
7901
|
+
if (fromIdx < 0 || toIdx < 0) return prev;
|
|
7902
|
+
const next = [...prev];
|
|
7903
|
+
const [moved] = next.splice(fromIdx, 1);
|
|
7904
|
+
next.splice(toIdx, 0, moved);
|
|
7905
|
+
onRowReorder?.(next);
|
|
7906
|
+
return next;
|
|
7907
|
+
});
|
|
7908
|
+
} : void 0,
|
|
7909
|
+
onClick: () => {
|
|
7910
|
+
if (expandable) setExpandedIds((prev) => {
|
|
7911
|
+
const s = new Set(prev);
|
|
7912
|
+
s.has(id) ? s.delete(id) : s.add(id);
|
|
7913
|
+
return s;
|
|
7914
|
+
});
|
|
7915
|
+
onRowClick?.(item);
|
|
7916
|
+
if (keyboardNavigation) setFocusedRowIdx(i);
|
|
7917
|
+
},
|
|
7918
|
+
onDoubleClick: () => onRowDoubleClick?.(item),
|
|
7919
|
+
className: cn(
|
|
7920
|
+
// default
|
|
7921
|
+
variant === "default" && "border-b border-border/60 transition-colors last:border-0",
|
|
7922
|
+
variant === "default" && (isSelected ? "bg-primary/5 hover:bg-primary/8" : "hover:bg-muted/30"),
|
|
7923
|
+
// zebra
|
|
7924
|
+
variant === "zebra" && "border-b border-border/40 transition-colors last:border-0",
|
|
7925
|
+
variant === "zebra" && (isSelected ? "bg-primary/8" : i % 2 === 0 ? "bg-card" : "bg-muted/40"),
|
|
7926
|
+
variant === "zebra" && !isSelected && "hover:bg-primary/5",
|
|
7927
|
+
// card
|
|
7928
|
+
variant === "card" && "rounded-xl border border-border bg-card shadow-sm transition-all hover:shadow-md hover:-translate-y-px",
|
|
7929
|
+
variant === "card" && (isSelected ? "border-primary/50 bg-primary/5" : ""),
|
|
7930
|
+
variant === "card" && "[&>td:first-child]:rounded-l-xl [&>td:last-child]:rounded-r-xl",
|
|
7931
|
+
// glass
|
|
7932
|
+
variant === "glass" && "border-b border-white/8 transition-colors last:border-0",
|
|
7933
|
+
variant === "glass" && (isSelected ? "bg-primary/15 hover:bg-primary/20" : "hover:bg-white/5"),
|
|
7934
|
+
// soft
|
|
7935
|
+
variant === "soft" && "transition-all",
|
|
7936
|
+
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"),
|
|
7937
|
+
variant === "soft" && "border-b border-border/30 last:border-0",
|
|
7938
|
+
(onRowClick || onRowDoubleClick || expandable) && "cursor-pointer",
|
|
7939
|
+
draggable && dragOverId === id && "ring-2 ring-inset ring-primary/40",
|
|
7940
|
+
isFocused && "ring-2 ring-inset ring-ring",
|
|
7941
|
+
rowClassName?.(item)
|
|
7942
|
+
),
|
|
7943
|
+
children: [
|
|
7944
|
+
selectable && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("td", { className: "px-4 py-3 align-middle", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7945
|
+
Checkbox,
|
|
7946
|
+
{
|
|
7947
|
+
checked: isSelected,
|
|
7948
|
+
onChange: (e) => handleSelect(id, e.target.checked)
|
|
7949
|
+
}
|
|
7182
7950
|
) }),
|
|
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",
|
|
7951
|
+
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") }) }),
|
|
7952
|
+
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)(
|
|
7953
|
+
"img",
|
|
7954
|
+
{
|
|
7955
|
+
src: item[col.key],
|
|
7956
|
+
alt: item[col.key],
|
|
7957
|
+
className: "h-9 w-9 rounded-lg object-cover ring-1 ring-border"
|
|
7958
|
+
}
|
|
7959
|
+
) : col.type === "badge" ? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("span", { className: cn(
|
|
7960
|
+
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-medium",
|
|
7961
|
+
badgeClass(String(item[col.key]))
|
|
7962
|
+
), children: [
|
|
7963
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: cn(
|
|
7964
|
+
"mr-1.5 h-1.5 w-1.5 rounded-full",
|
|
7965
|
+
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"
|
|
7966
|
+
) }),
|
|
7967
|
+
item[col.key]
|
|
7968
|
+
] }) : 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)(
|
|
7969
|
+
"select",
|
|
7210
7970
|
{
|
|
7211
|
-
|
|
7212
|
-
value: item[col.key] || "#000000",
|
|
7971
|
+
value: item[col.key],
|
|
7213
7972
|
onChange: (e) => col.onChange?.(item, e.target.value),
|
|
7214
|
-
className: "h-
|
|
7973
|
+
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",
|
|
7974
|
+
children: (col.selectOptions ?? []).map((opt) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("option", { value: opt, children: opt }, opt))
|
|
7215
7975
|
}
|
|
7216
|
-
),
|
|
7217
|
-
|
|
7218
|
-
|
|
7219
|
-
|
|
7220
|
-
|
|
7221
|
-
|
|
7222
|
-
|
|
7223
|
-
|
|
7224
|
-
|
|
7225
|
-
|
|
7226
|
-
|
|
7227
|
-
|
|
7228
|
-
|
|
7976
|
+
) : col.type === "toggle" ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7977
|
+
"button",
|
|
7978
|
+
{
|
|
7979
|
+
role: "switch",
|
|
7980
|
+
"aria-checked": !!item[col.key],
|
|
7981
|
+
onClick: () => col.onChange?.(item, !item[col.key]),
|
|
7982
|
+
className: cn(
|
|
7983
|
+
"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",
|
|
7984
|
+
item[col.key] ? "bg-primary" : "bg-muted"
|
|
7985
|
+
),
|
|
7986
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: cn(
|
|
7987
|
+
"pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow-sm transition-transform",
|
|
7988
|
+
item[col.key] ? "translate-x-4" : "translate-x-0"
|
|
7989
|
+
) })
|
|
7990
|
+
}
|
|
7991
|
+
) : col.type === "color" ? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
7992
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7993
|
+
"input",
|
|
7994
|
+
{
|
|
7995
|
+
type: "color",
|
|
7996
|
+
value: item[col.key] || "#000000",
|
|
7997
|
+
onChange: (e) => col.onChange?.(item, e.target.value),
|
|
7998
|
+
className: "h-7 w-7 cursor-pointer rounded border border-border bg-transparent p-0.5"
|
|
7999
|
+
}
|
|
8000
|
+
),
|
|
8001
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-xs text-muted-foreground font-mono", children: item[col.key] })
|
|
8002
|
+
] }) : col.type === "checkbox" ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
8003
|
+
Checkbox,
|
|
8004
|
+
{
|
|
8005
|
+
checked: !!item[col.key],
|
|
8006
|
+
onChange: (e) => col.onChange?.(item, e.target.checked)
|
|
8007
|
+
}
|
|
8008
|
+
) : col.type === "text-url" ? (() => {
|
|
8009
|
+
const href = col.redirect ? typeof col.redirect === "function" ? col.redirect(item) : col.redirect : String(item[col.key] ?? "");
|
|
8010
|
+
const colorMap = {
|
|
8011
|
+
primary: "var(--primary)",
|
|
8012
|
+
info: "var(--info)",
|
|
8013
|
+
success: "var(--success)",
|
|
8014
|
+
warning: "var(--warning)",
|
|
8015
|
+
danger: "var(--danger)"
|
|
8016
|
+
};
|
|
8017
|
+
const underline = col.underlineColor ? colorMap[col.underlineColor] ?? col.underlineColor : "var(--primary)";
|
|
8018
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
8019
|
+
"a",
|
|
8020
|
+
{
|
|
8021
|
+
href,
|
|
8022
|
+
target: col.openNewTab ? "_blank" : void 0,
|
|
8023
|
+
rel: col.openNewTab ? "noopener noreferrer" : void 0,
|
|
8024
|
+
style: { textDecorationColor: underline },
|
|
8025
|
+
className: "text-sm underline underline-offset-2 hover:opacity-75 transition-opacity break-all",
|
|
8026
|
+
onClick: col.openNewTab ? void 0 : (e) => e.preventDefault(),
|
|
8027
|
+
children: item[col.key]
|
|
8028
|
+
}
|
|
8029
|
+
);
|
|
8030
|
+
})() : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-foreground/90", children: item[col.key] }) }, `${String(col.key)}-${ci}`))
|
|
8031
|
+
]
|
|
8032
|
+
}
|
|
8033
|
+
),
|
|
8034
|
+
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) }) })
|
|
8035
|
+
] }, id);
|
|
7229
8036
|
}) })
|
|
7230
8037
|
] }) }) }),
|
|
7231
|
-
|
|
8038
|
+
clientPagination && !serverPagination && totalPages > 1 && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
|
|
7232
8039
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("span", { className: "text-xs text-muted-foreground", children: [
|
|
7233
8040
|
"Showing ",
|
|
7234
8041
|
(safePage - 1) * itemsPerPage + 1,
|
|
@@ -7279,8 +8086,8 @@ function Table({
|
|
|
7279
8086
|
] })
|
|
7280
8087
|
] }),
|
|
7281
8088
|
serverPagination && (() => {
|
|
7282
|
-
const { pagination
|
|
7283
|
-
const totalServerPages =
|
|
8089
|
+
const { pagination, currentPage: cp, goToPage } = serverPagination;
|
|
8090
|
+
const totalServerPages = pagination.last_page ?? Math.ceil(pagination.total / pagination.per_page);
|
|
7284
8091
|
const pills = [];
|
|
7285
8092
|
if (totalServerPages <= 7) {
|
|
7286
8093
|
for (let i = 1; i <= totalServerPages; i++) pills.push(i);
|
|
@@ -7293,7 +8100,7 @@ function Table({
|
|
|
7293
8100
|
}
|
|
7294
8101
|
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
|
|
7295
8102
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("span", { className: "text-xs text-muted-foreground", children: [
|
|
7296
|
-
|
|
8103
|
+
pagination.total,
|
|
7297
8104
|
" total rows \xB7 page ",
|
|
7298
8105
|
cp,
|
|
7299
8106
|
" of ",
|
|
@@ -7304,7 +8111,7 @@ function Table({
|
|
|
7304
8111
|
"button",
|
|
7305
8112
|
{
|
|
7306
8113
|
onClick: () => goToPage(cp - 1),
|
|
7307
|
-
disabled: !
|
|
8114
|
+
disabled: !pagination.prev_page_url,
|
|
7308
8115
|
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
8116
|
children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.ChevronLeft, { className: "h-4 w-4" })
|
|
7310
8117
|
}
|
|
@@ -7327,7 +8134,7 @@ function Table({
|
|
|
7327
8134
|
"button",
|
|
7328
8135
|
{
|
|
7329
8136
|
onClick: () => goToPage(cp + 1),
|
|
7330
|
-
disabled: !
|
|
8137
|
+
disabled: !pagination.next_page_url,
|
|
7331
8138
|
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
8139
|
children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.ChevronRight, { className: "h-4 w-4" })
|
|
7333
8140
|
}
|
|
@@ -7342,6 +8149,7 @@ function Table({
|
|
|
7342
8149
|
item: viewItem,
|
|
7343
8150
|
fields: viewFields,
|
|
7344
8151
|
width: defaultActions.modalWidth,
|
|
8152
|
+
grid: defaultActions.viewFormGrid,
|
|
7345
8153
|
onClose: () => setViewItem(null)
|
|
7346
8154
|
}
|
|
7347
8155
|
),
|
|
@@ -7399,6 +8207,37 @@ function Table({
|
|
|
7399
8207
|
}
|
|
7400
8208
|
}
|
|
7401
8209
|
}
|
|
8210
|
+
),
|
|
8211
|
+
bulkConfirm && (0, import_react_dom2.createPortal)(
|
|
8212
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
8213
|
+
"div",
|
|
8214
|
+
{
|
|
8215
|
+
className: "fixed inset-0 z-50 flex items-center justify-center p-4",
|
|
8216
|
+
style: { background: "rgba(0,0,0,0.5)" },
|
|
8217
|
+
onMouseDown: (e) => {
|
|
8218
|
+
if (e.target === e.currentTarget) setBulkConfirm(null);
|
|
8219
|
+
},
|
|
8220
|
+
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: [
|
|
8221
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center justify-between px-6 py-4 border-b border-border", children: [
|
|
8222
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("h2", { className: "text-base font-semibold", children: "Confirm Delete" }),
|
|
8223
|
+
/* @__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" }) })
|
|
8224
|
+
] }),
|
|
8225
|
+
/* @__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." }) }),
|
|
8226
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "px-6 py-4 border-t border-border flex justify-end gap-2", children: [
|
|
8227
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Button, { variant: "outline", size: "sm", onClick: () => setBulkConfirm(null), disabled: bulkLoading, children: "Cancel" }),
|
|
8228
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(Button, { variant: "danger", size: "sm", disabled: bulkLoading, onClick: async () => {
|
|
8229
|
+
if (bulkConfirm === "selected") await execBulkDeleteSelected();
|
|
8230
|
+
else await execDeleteAll();
|
|
8231
|
+
setBulkConfirm(null);
|
|
8232
|
+
}, children: [
|
|
8233
|
+
bulkLoading && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.Loader2, { className: "h-3.5 w-3.5 mr-1.5 animate-spin" }),
|
|
8234
|
+
bulkLoading ? "Deleting\u2026" : "Delete"
|
|
8235
|
+
] })
|
|
8236
|
+
] })
|
|
8237
|
+
] })
|
|
8238
|
+
}
|
|
8239
|
+
),
|
|
8240
|
+
document.body
|
|
7402
8241
|
)
|
|
7403
8242
|
] });
|
|
7404
8243
|
}
|
|
@@ -12853,6 +13692,12 @@ var axiosInstance = import_axios5.default.create({
|
|
|
12853
13692
|
Accept: "application/json"
|
|
12854
13693
|
}
|
|
12855
13694
|
});
|
|
13695
|
+
axiosInstance.interceptors.request.use((config) => {
|
|
13696
|
+
if (config.data instanceof FormData) {
|
|
13697
|
+
delete config.headers["Content-Type"];
|
|
13698
|
+
}
|
|
13699
|
+
return config;
|
|
13700
|
+
});
|
|
12856
13701
|
|
|
12857
13702
|
// src/lib/codego/request.ts
|
|
12858
13703
|
var request = async (config) => {
|