PageSourceSearch

https://www.hamienet.com/assets/AdminShared-BP_vxG_A.js

js hamienet.com collected 2026-10-02 05:03:49 UTC 18,295 bytes, 252 lines download raw bytes

1import { r as reactExports, j as jsxRuntimeExports } from "./vendor-react-Bay9Y7Lb.js";
2import { bf as Search, aW as X, cM as RefreshCw, dg as Funnel, bm as ArrowUp, dc as ArrowDown, c9 as ArrowUpDown, aY as TriangleAlert, dV as ChevronsLeft, c_ as ChevronLeft, bF as ChevronRight, dU as ChevronsRight, ci as Database } from "./vendor-D0P4XgNj.js";
3const DevDebugInfo = ({ table, keyName, value, isSuperAdmin }) => {
4  if (!isSuperAdmin) return null;
5  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1 mb-2 p-2 bg-zinc-100 dark:bg-zinc-950 border border-dashed border-zinc-300 dark:border-zinc-800 rounded text-[10px] font-mono text-zinc-500 overflow-hidden", children: [
6    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 items-center mb-0.5", children: [
7      /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-indigo-600 dark:text-indigo-400 font-bold flex items-center gap-1", children: [
8        /* @__PURE__ */ jsxRuntimeExports.jsx(Database, { size: 8 }),
9        " ",
10        table
11      ] }),
12      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400", children: "|" }),
13      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-600 dark:text-zinc-400", children: keyName })
14    ] }),
15    value !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-emerald-600 dark:text-emerald-500 truncate font-medium", children: [
16      "VAL: ",
17      typeof value === "object" ? JSON.stringify(value) : String(value)
18    ] })
19  ] });
20};
21const Drawer = ({ isOpen, onClose, title, children, width = "max-w-2xl" }) => /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
22  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `fixed inset-0 bg-black/50 backdrop-blur-sm z-[60] transition-opacity ${isOpen ? "opacity-100" : "opacity-0 pointer-events-none"}`, onClick: onClose }),
23  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `fixed inset-y-0 right-0 z-[70] w-full ${width} bg-white dark:bg-[#1a1a1a] shadow-2xl transform transition-transform duration-300 ${isOpen ? "translate-x-0" : "translate-x-full"} border-l border-zinc-200 dark:border-zinc-800 flex flex-col`, children: [
24    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between p-6 border-b border-zinc-200 dark:border-zinc-800 shrink-0", children: [
25      /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-xl font-bold text-zinc-900 dark:text-white", children: title }),
26      /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onClose, className: "p-2 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-full transition-colors", children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 20, className: "text-zinc-500" }) })
27    ] }),
28    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-y-auto p-6", children })
29  ] })
30] });
31function DataTable({
32  title,
33  icon: Icon,
34  columns,
35  columnHeaders,
36  filters,
37  onFetch,
38  reloadTrigger,
39  actions,
40  placeholder,
41  onRowClick,
42  selectable,
43  onSelectionChange,
44  onColumnContextMenu,
45  activeColumnFilters = [],
46  onRemoveColumnFilter,
47  dense = false,
48  initialSearch = "",
49  initialFilters = {}
50}) {
51  const [data, setData] = reactExports.useState([]);
52  const [loading, setLoading] = reactExports.useState(false);
53  const [total, setTotal] = reactExports.useState(0);
54  const [page, setPage] = reactExports.useState(1);
55  const [pageSize, setPageSize] = reactExports.useState(10);
56  const [search, setSearch] = reactExports.useState(initialSearch);
57  const [activeFilters, setActiveFilters] = reactExports.useState(initialFilters);
58  const [selected, setSelected] = reactExports.useState(/* @__PURE__ */ new Set());
59  const [sortBy, setSortBy] = reactExports.useState("");
60  const [sortDir, setSortDir] = reactExports.useState("desc");
61  const [error, setError] = reactExports.useState(null);
62  const fetchData = reactExports.useCallback(async () => {
63    var _a;
64    setLoading(true);
65    setError(null);
66    try {
67      const result = await onFetch({
68        page,
69        pageSize,
70        search,
71        filters: activeFilters,
72        sortBy,
73        sortDir
74      });
75      setData((result == null ? void 0 : result.data) || []);
76      setTotal((result == null ? void 0 : result.total) || ((_a = result == null ? void 0 : result.data) == null ? void 0 : _a.length) || 0);
77      setSelected(/* @__PURE__ */ new Set());
78    } catch (e) {
79      console.error("DataTable fetch error:", e);
80      setError(e.message || "Failed to fetch data. Please check Unified Logs for details.");
81      setData([]);
82      setTotal(0);
83    } finally {
84      setLoading(false);
85    }
86  }, [page, pageSize, search, activeFilters, onFetch, reloadTrigger, sortBy, sortDir]);
87  reactExports.useEffect(() => {
88    fetchData();
89  }, [fetchData]);
90  reactExports.useEffect(() => {
91    if (onSelectionChange) onSelectionChange(Array.from(selected));
92  }, [selected]);
93  const handleSearch = (e) => {
94    e.preventDefault();
95    setPage(1);
96    fetchData();
97  };
98  const handleFilterChange = (key, value) => {
99    setActiveFilters((prev) => ({ ...prev, [key]: value }));
100    setPage(1);
101  };
102  const toggleSelect = (id) => {
103    setSelected((prev) => {
104      const next = new Set(prev);
105      if (next.has(id)) next.delete(id);
106      else next.add(id);
107      return next;
108    });
109  };
110  const toggleSelectAll = () => {
111    if (selected.size === data.length && data.length > 0) setSelected(/* @__PURE__ */ new Set());
112    else setSelected(new Set(data.map((d) => d.id)));
113  };
114  const totalPages = Math.ceil(total / pageSize) || 1;
115  const handleSort = (header) => {
116    const key = typeof header === "string" ? header : header.key;
117    if (sortBy === key) {
118      setSortDir((prev) => prev === "asc" ? "desc" : "asc");
119    } else {
120      setSortBy(key);
121      setSortDir("asc");
122    }
123  };
124  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white dark:bg-zinc-900 rounded-xl border border-zinc-200 dark:border-zinc-800 overflow-hidden shadow-sm flex flex-col h-full", children: [
125    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 border-b border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-950/50 flex flex-col gap-4", children: [
126      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center", children: [
127        /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "font-bold text-zinc-900 dark:text-white flex items-center gap-2", children: [
128          /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { size: 18, className: "text-indigo-500" }),
129          " ",
130          title
131        ] }),
132        actions
133      ] }),
134      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
135        /* @__PURE__ */ jsxRuntimeExports.jsxs("form", { onSubmit: handleSearch, className: "relative flex-1 min-w-[200px] max-w-[300px]", children: [
136          /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { size: 16, className: "absolute left-3 top-1/2 -translate-y-1/2 text-zinc-400" }),
137          /* @__PURE__ */ jsxRuntimeExports.jsx("input", { type: "text", placeholder: placeholder || "Search...", className: "w-full bg-white dark:bg-zinc-900 border border-zinc-300 dark:border-zinc-700 rounded-lg pl-9 pr-4 py-2 text-sm outline-none focus:ring-2 focus:ring-indigo-500 text-zinc-900 dark:text-white", value: search, onChange: (e) => setSearch(e.target.value) })
138        ] }),
139        filters && filters.map((filter) => filter.type === "text" ? /* @__PURE__ */ jsxRuntimeExports.jsx(
140          "input",
141          {
142            type: "text",
143            placeholder: filter.placeholder || filter.label,
144            className: "bg-white dark:bg-zinc-900 border border-zinc-300 dark:border-zinc-700 rounded-lg px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-indigo-500 text-zinc-900 dark:text-white min-w-[120px]",
145            value: activeFilters[filter.key] || "",
146            onChange: (e) => handleFilterChange(filter.key, e.target.value)
147          },
148          filter.key
149        ) : /* @__PURE__ */ jsxRuntimeExports.jsxs("select", { className: "bg-white dark:bg-zinc-900 border border-zinc-300 dark:border-zinc-700 rounded-lg px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-indigo-500 text-zinc-700 dark:text-zinc-300 cursor-pointer", value: activeFilters[filter.key] || "ALL", onChange: (e) => handleFilterChange(filter.key, e.target.value), children: [
150          /* @__PURE__ */ jsxRuntimeExports.jsxs("option", { value: "ALL", children: [
151            "All ",
152            filter.label
153          ] }),
154          filter.options.map((opt) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: opt.value, children: opt.label }, opt.value))
155        ] }, filter.key)),
156        activeColumnFilters && activeColumnFilters.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-wrap items-center gap-2", children: activeColumnFilters.map((f, idx) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 bg-indigo-50 dark:bg-indigo-900/30 text-indigo-700 dark:text-indigo-400 px-2 py-1.5 rounded-lg text-xs border border-indigo-200 dark:border-indigo-800/50", children: [
157          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold", children: f.column }),
158          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "opacity-70 font-mono", children: f.operator }),
159          f.value && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono bg-white/50 dark:bg-black/20 px-1 rounded truncate max-w-[150px]", children: f.value }),
160          /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => onRemoveColumnFilter && onRemoveColumnFilter(f.column), className: "ml-1 p-0.5 hover:bg-indigo-200 dark:hover:bg-indigo-800 rounded", children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 12 }) })
161        ] }, idx)) }),
162        /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => fetchData(), className: "p-2 hover:bg-zinc-200 dark:hover:bg-zinc-800 rounded-lg text-zinc-500 ml-auto", title: "Refresh", children: /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 18, className: loading ? "animate-spin" : "" }) })
163      ] })
164    ] }),
165    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-auto relative custom-scrollbar", children: [
166      loading && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-white/50 dark:bg-black/50 z-10 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-8 h-8 border-4 border-indigo-500 border-t-transparent rounded-full animate-spin" }) }),
167      /* @__PURE__ */ jsxRuntimeExports.jsxs("table", { className: "min-w-full text-left text-sm", children: [
168        /* @__PURE__ */ jsxRuntimeExports.jsx("thead", { className: "bg-zinc-50 dark:bg-zinc-950 text-xs font-medium text-zinc-500 uppercase sticky top-0 z-10 border-b border-zinc-200 dark:border-zinc-800 shadow-sm", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { children: [
169          selectable && /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "w-10 px-4 py-3", children: /* @__PURE__ */ jsxRuntimeExports.jsx("input", { type: "checkbox", checked: selected.size === data.length && data.length > 0, onChange: toggleSelectAll, className: "rounded border-zinc-300 dark:border-zinc-600" }) }),
170          columnHeaders && columnHeaders.map((header, index) => {
171            const label = typeof header === "string" ? header : header.label;
172            const key = typeof header === "string" ? header : header.key;
173            const isFiltered = activeColumnFilters == null ? void 0 : activeColumnFilters.some((f) => f.column === key);
174            return /* @__PURE__ */ jsxRuntimeExports.jsx(
175              "th",
176              {
177                className: `px-4 py-3 cursor-pointer hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors select-none group whitespace-nowrap ${dense ? "py-2" : ""} ${isFiltered ? "bg-indigo-50/50 dark:bg-indigo-900/20 text-indigo-600 dark:text-indigo-400" : ""}`,
178                onClick: () => handleSort(header),
179                onContextMenu: (e) => onColumnContextMenu == null ? void 0 : onColumnContextMenu(e, key, data),
180                title: "Right-click to filter",
181                children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [
182                  isFiltered && /* @__PURE__ */ jsxRuntimeExports.jsx(Funnel, { size: 10, className: "text-indigo-500 shrink-0" }),
183                  label,
184                  sortBy === key ? sortDir === "asc" ? /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowUp, { size: 12, className: "text-indigo-500 shrink-0" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowDown, { size: 12, className: "text-indigo-500 shrink-0" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowUpDown, { size: 12, className: "opacity-0 group-hover:opacity-30 transition-opacity shrink-0" })
185                ] })
186              },
187              index
188            );
189          })
190        ] }) }),
191        /* @__PURE__ */ jsxRuntimeExports.jsx("tbody", { className: "divide-y divide-zinc-200 dark:divide-zinc-800 font-mono", children: error ? /* @__PURE__ */ jsxRuntimeExports.jsx("tr", { children: /* @__PURE__ */ jsxRuntimeExports.jsx("td", { colSpan: 100, className: "px-6 py-12 text-center", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center gap-2", children: [
192          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-3 bg-red-50 dark:bg-red-900/20 rounded-full text-red-600", children: /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { size: 24 }) }),
193          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm font-medium text-red-600 dark:text-red-400", children: "Query Failed" }),
194          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-500 max-w-sm", children: error }),
195          /* @__PURE__ */ jsxRuntimeExports.jsx(
196            "button",
197            {
198              onClick: () => fetchData(),
199              className: "mt-2 text-xs font-semibold text-indigo-600 hover:text-indigo-500 underline",
200              children: "Retry"
201            }
202          )
203        ] }) }) }) : data && data.length > 0 ? data.map((item, idx) => /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: `hover:bg-zinc-50 dark:hover:bg-zinc-800/50 border-b border-zinc-100 dark:border-zinc-800 ${onRowClick ? "cursor-pointer" : ""} ${selected.has(item.id) ? "bg-indigo-50 dark:bg-indigo-900/10" : ""}`, onClick: () => onRowClick && onRowClick(item), children: [
204          selectable && /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: `px-4 ${dense ? "py-2" : "py-4"}`, onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxRuntimeExports.jsx("input", { type: "checkbox", checked: selected.has(item.id), onChange: () => toggleSelect(item.id), className: "rounded border-zinc-300 dark:border-zinc-600" }) }),
205          columns(item)
206        ] }, item.id)) : !loading && /* @__PURE__ */ jsxRuntimeExports.jsx("tr", { children: /* @__PURE__ */ jsxRuntimeExports.jsx("td", { colSpan: 100, className: "p-10 text-center text-zinc-500", children: "No records found matching your criteria." }) }) })
207      ] })
208    ] }),
209    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 border-t border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-950/50 flex justify-between items-center text-xs text-zinc-500", children: [
210      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4", children: [
211        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Rows per page:" }),
212        /* @__PURE__ */ jsxRuntimeExports.jsx(
213          "select",
214          {
215            value: pageSize,
216            onChange: (e) => {
217              setPageSize(Number(e.target.value));
218              setPage(1);
219            },
220            className: "bg-white dark:bg-zinc-900 border border-zinc-300 dark:border-zinc-700 rounded px-2 py-1 outline-none focus:ring-1 focus:ring-indigo-500",
221            children: [10, 25, 50, 100, 250].map((size) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: size, children: size }, size))
222          }
223        ),
224        /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
225          "Showing ",
226          total > 0 ? Math.min(total, (page - 1) * pageSize + 1) : 0,
227          " - ",
228          Math.min(total, page * pageSize),
229          " of ",
230          total
231        ] })
232      ] }),
233      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
234        /* @__PURE__ */ jsxRuntimeExports.jsx("button", { disabled: page === 1, onClick: () => setPage(1), className: "p-1.5 rounded hover:bg-zinc-200 dark:hover:bg-zinc-800 disabled:opacity-30", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronsLeft, { size: 16 }) }),
235        /* @__PURE__ */ jsxRuntimeExports.jsx("button", { disabled: page === 1, onClick: () => setPage((p) => p - 1), className: "p-1.5 rounded hover:bg-zinc-200 dark:hover:bg-zinc-800 disabled:opacity-30", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronLeft, { size: 16 }) }),
236        /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-medium text-zinc-700 dark:text-zinc-300 px-2", children: [
237          "Page ",
238          page,
239          " of ",
240          totalPages
241        ] }),
242        /* @__PURE__ */ jsxRuntimeExports.jsx("button", { disabled: page >= totalPages, onClick: () => setPage((p) => p + 1), className: "p-1.5 rounded hover:bg-zinc-200 dark:hover:bg-zinc-800 disabled:opacity-30", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { size: 16 }) }),
243        /* @__PURE__ */ jsxRuntimeExports.jsx("button", { disabled: page >= totalPages, onClick: () => setPage(totalPages), className: "p-1.5 rounded hover:bg-zinc-200 dark:hover:bg-zinc-800 disabled:opacity-30", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronsRight, { size: 16 }) })
244      ] })
245    ] })
246  ] });
247}
248export {
249  DevDebugInfo as D,
250  DataTable as a,
251  Drawer as b
252};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.