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.