PageSourceSearch

https://www.hamienet.com/assets/UnifiedLogs-BQXbYxtq.js

js hamienet.com collected 2026-09-24 19:01:07 UTC 32,636 bytes, 534 lines download raw bytes

1import { r as reactExports, j as jsxRuntimeExports, R as React } from "./vendor-react-Bay9Y7Lb.js";
2import { a as api } from "./index-D0Bmh-UX.js";
3import { cf as Activity, cL as RefreshCw, b5 as Trash2, a$ as Shield, ch as Database, dX as Server, bb as User, eu as Terminal, aY as TriangleAlert, g6 as ChartNoAxesColumn, de as Funnel, bf as Search, dM as Calendar, bL as Clock, ba as Info } from "./vendor-dVNaLJx_.js";
4import "./vendor-i18n-R-bMoYp_.js";
5const LOG_CATEGORIES = [
6  { id: "ALL", label: "All Logs", icon: Activity },
7  { id: "admin", label: "Admin Actions", icon: Shield },
8  { id: "database", label: "Database Actions", icon: Database },
9  { id: "system", label: "System Events", icon: Server },
10  { id: "user", label: "User Actions", icon: User },
11  { id: "worker", label: "Worker Logs", icon: Terminal },
12  { id: "error", label: "Errors & Crashes", icon: TriangleAlert },
13  { id: "analytics", label: "Analytics", icon: ChartNoAxesColumn }
14];
15const UnifiedLogs = () => {
16  const [logs, setLogs] = reactExports.useState([]);
17  const [total, setTotal] = reactExports.useState(0);
18  const [loading, setLoading] = reactExports.useState(false);
19  const [activeTab, setActiveTab] = reactExports.useState("ALL");
20  const [search, setSearch] = reactExports.useState("");
21  const [page, setPage] = reactExports.useState(1);
22  const [dateRange, setDateRange] = reactExports.useState({ start: "", end: "" });
23  const [isClearing, setIsClearing] = reactExports.useState(false);
24  const [expandedLogs, setExpandedLogs] = reactExports.useState(/* @__PURE__ */ new Set());
25  const [groupWorkerLogs, setGroupWorkerLogs] = reactExports.useState(true);
26  const [clearOption, setClearOption] = reactExports.useState("all");
27  const toggleLog = (logId) => {
28    setExpandedLogs((prev) => {
29      const next = new Set(prev);
30      if (next.has(logId)) next.delete(logId);
31      else next.add(logId);
32      return next;
33    });
34  };
35  reactExports.useEffect(() => {
36    fetchLogs();
37  }, [page, activeTab, search, dateRange]);
38  const fetchLogs = async () => {
39    setLoading(true);
40    try {
41      const res = await api.getActivityLogs({
42        page,
43        pageSize: 100,
44        category: activeTab,
45        search,
46        startDate: dateRange.start,
47        endDate: dateRange.end
48      });
49      setLogs(res.data);
50      setTotal(res.total);
51    } catch (e) {
52      console.error("Failed to fetch logs", e);
53    } finally {
54      setLoading(false);
55    }
56  };
57  const handleClearLogs = async () => {
58    let confirmMsg = `Are you sure you want to clear ${activeTab === "ALL" ? "ALL" : activeTab} logs`;
59    if (clearOption === "all") confirmMsg += "? This cannot be undone.";
60    else if (clearOption === "24h") confirmMsg += " older than 24 hours?";
61    else if (clearOption === "3d") confirmMsg += " older than 3 days?";
62    else if (clearOption === "7d") confirmMsg += " older than 7 days?";
63    else if (clearOption === "30d") confirmMsg += " older than 30 days?";
64    if (!confirm(confirmMsg)) return;
65    setIsClearing(true);
66    try {
67      let options = { category: activeTab };
68      if (clearOption === "30d") {
69        options.clearLegacy = true;
70      } else if (clearOption !== "all") {
71        const now = /* @__PURE__ */ new Date();
72        let days = 0;
73        if (clearOption === "24h") days = 1;
74        else if (clearOption === "3d") days = 3;
75        else if (clearOption === "7d") days = 7;
76        const beforeDate = new Date(now.getTime() - days * 24 * 60 * 60 * 1e3);
77        options.beforeDate = beforeDate.toISOString();
78      }
79      await api.clearActivityLogs(options);
80      fetchLogs();
81      alert("Logs cleared successfully.");
82    } catch (e) {
83      alert("Failed to clear logs.");
84    } finally {
85      setIsClearing(false);
86    }
87  };
88  const getSeverityColor = (severity) => {
89    switch (severity) {
90      case "danger":
91        return "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400 border-red-200 dark:border-red-800";
92      case "warning":
93        return "bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-400 border-orange-200 dark:border-orange-800";
94      default:
95        return "bg-zinc-100 text-zinc-700 dark:bg-zinc-800 dark:text-zinc-400 border-zinc-200 dark:border-zinc-700";
96    }
97  };
98  const [expandedGroups, setExpandedGroups] = reactExports.useState(/* @__PURE__ */ new Set());
99  const toggleGroup = (groupId) => {
100    setExpandedGroups((prev) => {
101      const next = new Set(prev);
102      if (next.has(groupId)) {
103        next.delete(groupId);
104      } else {
105        next.add(groupId);
106      }
107      return next;
108    });
109  };
110  const processLogsForGrouping = (logs2) => {
111    let currentGroupId = null;
112    let groupColorIndex = 0;
113    const colors = [
114      "border-l-blue-500 bg-blue-50/50 dark:bg-blue-900/20",
115      "border-l-purple-500 bg-purple-50/50 dark:bg-purple-900/20",
116      "border-l-emerald-500 bg-emerald-50/50 dark:bg-emerald-900/20",
117      "border-l-amber-500 bg-amber-50/50 dark:bg-amber-900/20",
118      "border-l-pink-500 bg-pink-50/50 dark:bg-pink-900/20"
119    ];
120    const processed = [...logs2].reverse().map((log) => {
121      let groupId = currentGroupId;
122      let groupColor = currentGroupId ? colors[groupColorIndex] : "";
123      if (log.category === "worker" && (log.action === "WORKER_LOG" || log.action.startsWith("worker/"))) {
124        const details = log.details || "";
125        const startMatch = details.match(/\[\*\] Processing MIDI (\d+)/);
126        const endMatch = details.match(/\[\+\] Success (\d+)/) || details.match(/\[-\] Failed (\d+)/);
127        if (startMatch) {
128          groupColorIndex = (groupColorIndex + 1) % colors.length;
129          currentGroupId = `midi-${startMatch[1]}-${log.id}`;
130          groupId = currentGroupId;
131          groupColor = colors[groupColorIndex];
132        } else if (endMatch) {
133          if (!currentGroupId) {
134            groupColorIndex = (groupColorIndex + 1) % colors.length;
135            currentGroupId = `midi-${endMatch[1]}-${log.id}`;
136          }
137          groupId = currentGroupId;
138          groupColor = colors[groupColorIndex];
139          currentGroupId = null;
140        } else if (details.startsWith("    ")) {
141          if (!currentGroupId) {
142            groupColorIndex = (groupColorIndex + 1) % colors.length;
143            currentGroupId = `unknown-group-${log.id}`;
144          }
145          groupId = currentGroupId;
146          groupColor = colors[groupColorIndex];
147        } else {
148          currentGroupId = null;
149          groupId = null;
150          groupColor = "";
151        }
152      } else {
153        currentGroupId = null;
154        groupId = null;
155        groupColor = "";
156      }
157      return {
158        ...log,
159        groupId,
160        groupColorClass: groupColor
161      };
162    }).reverse();
163    const grouped = [];
164    let currentGroup = [];
165    let lastGroupId = null;
166    for (const log of processed) {
167      if (log.groupId) {
168        if (log.groupId === lastGroupId) {
169          currentGroup.push(log);
170        } else {
171          if (currentGroup.length > 0) {
172            grouped.push(currentGroup);
173          }
174          currentGroup = [log];
175          lastGroupId = log.groupId;
176        }
177      } else {
178        if (currentGroup.length > 0) {
179          grouped.push(currentGroup);
180          currentGroup = [];
181          lastGroupId = null;
182        }
183        grouped.push([log]);
184      }
185    }
186    if (currentGroup.length > 0) {
187      grouped.push(currentGroup);
188    }
189    const finalLogs = [];
190    for (const group of grouped) {
191      if (group[0].groupId && group.length > 1) {
192        group.reverse();
193        group[0].isGroupStart = true;
194        group[0].groupCount = group.length;
195        for (let i = 1; i < group.length; i++) {
196          group[i].isGroupStart = false;
197        }
198      } else if (group[0].groupId) {
199        group[0].isGroupStart = true;
200        group[0].groupCount = 1;
201      }
202      finalLogs.push(...group);
203    }
204    return finalLogs;
205  };
206  const augmentedLogs = processLogsForGrouping(logs);
207  const visibleLogs = augmentedLogs.filter((log) => {
208    if (!groupWorkerLogs) return true;
209    if (!log.groupId) return true;
210    if (log.isGroupStart) return true;
211    return expandedGroups.has(log.groupId);
212  });
213  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col h-full space-y-4", children: [
214    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center bg-white dark:bg-zinc-900 p-4 rounded-xl border border-zinc-200 dark:border-zinc-800 shadow-sm shrink-0", children: [
215      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
216        /* @__PURE__ */ jsxRuntimeExports.jsxs("h2", { className: "text-xl font-bold text-zinc-900 dark:text-white flex items-center gap-2", children: [
217          /* @__PURE__ */ jsxRuntimeExports.jsx(Activity, { className: "text-indigo-500" }),
218          " Unified System Logs"
219        ] }),
220        /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-zinc-500 dark:text-zinc-400", children: "Monitor system activity, errors, and user actions." })
221      ] }),
222      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
223        /* @__PURE__ */ jsxRuntimeExports.jsx(
224          "button",
225          {
226            onClick: () => fetchLogs(),
227            className: "p-2 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-lg text-zinc-500 transition-colors",
228            title: "Refresh",
229            children: /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 18, className: loading ? "animate-spin" : "" })
230          }
231        ),
232        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 items-center", children: [
233          /* @__PURE__ */ jsxRuntimeExports.jsxs(
234            "select",
235            {
236              value: clearOption,
237              onChange: (e) => setClearOption(e.target.value),
238              className: "px-3 py-2 bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-lg text-sm text-zinc-800 dark:text-zinc-200 outline-none focus:ring-2 focus:ring-red-500/50 cursor-pointer",
239              children: [
240                /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "all", className: "text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-800", children: "All Time" }),
241                /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "24h", className: "text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-800", children: "Older than 24 Hours" }),
242                /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "3d", className: "text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-800", children: "Older than 3 Days" }),
243                /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "7d", className: "text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-800", children: "Older than 7 Days" }),
244                /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "30d", className: "text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-800", children: "Older than 30 Days (Legacy)" })
245              ]
246            }
247          ),
248          /* @__PURE__ */ jsxRuntimeExports.jsxs(
249            "button",
250            {
251              onClick: handleClearLogs,
252              disabled: isClearing,
253              className: "flex items-center gap-2 px-4 py-2 bg-red-50 hover:bg-red-100 dark:bg-red-900/20 dark:hover:bg-red-900/40 text-red-600 dark:text-red-400 rounded-lg text-sm font-bold border border-red-200 dark:border-red-800 transition-colors disabled:opacity-50",
254              children: [
255                /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { size: 16 }),
256                isClearing ? "Clearing..." : "Clear Logs"
257              ]
258            }
259          )
260        ] })
261      ] })
262    ] }),
263    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col md:flex-row gap-4", children: [
264      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex bg-white dark:bg-zinc-900 p-1 rounded-lg border border-zinc-200 dark:border-zinc-800 overflow-x-auto custom-scrollbar", children: LOG_CATEGORIES.map((cat) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
265        "button",
266        {
267          onClick: () => {
268            setActiveTab(cat.id);
269            setPage(1);
270          },
271          className: `flex items-center gap-2 px-3 py-2 rounded-md text-sm font-medium whitespace-nowrap transition-all ${activeTab === cat.id ? "bg-indigo-50 dark:bg-indigo-900/30 text-indigo-600 dark:text-indigo-400 shadow-sm" : "text-zinc-500 hover:bg-zinc-50 dark:hover:bg-zinc-800"}`,
272          children: [
273            /* @__PURE__ */ jsxRuntimeExports.jsx(cat.icon, { size: 14 }),
274            cat.label
275          ]
276        },
277        cat.id
278      )) }),
279      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex gap-2", children: [
280        /* @__PURE__ */ jsxRuntimeExports.jsxs(
281          "button",
282          {
283            onClick: () => setGroupWorkerLogs((prev) => !prev),
284            className: `h-10 flex items-center gap-1.5 px-3 rounded-lg text-xs font-bold border transition-colors whitespace-nowrap ${groupWorkerLogs ? "bg-indigo-50 border-indigo-200 text-indigo-600 dark:bg-indigo-900/30 dark:border-indigo-800 dark:text-indigo-400" : "bg-white border-zinc-200 text-zinc-500 dark:bg-zinc-900 dark:border-zinc-800 dark:text-zinc-400"}`,
285            title: "Toggle collapsing of repetitive worker step logs",
286            children: [
287              /* @__PURE__ */ jsxRuntimeExports.jsx(Funnel, { size: 14 }),
288              groupWorkerLogs ? "Group Worker Steps" : "Raw Logs"
289            ]
290          }
291        ),
292        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative min-w-[250px]", children: [
293          /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { size: 16, className: "absolute left-3 top-1/2 -translate-y-1/2 text-zinc-400" }),
294          /* @__PURE__ */ jsxRuntimeExports.jsx(
295            "input",
296            {
297              type: "text",
298              placeholder: "Search logs...",
299              value: search,
300              onChange: (e) => setSearch(e.target.value),
301              className: "w-full h-10 pl-9 pr-4 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg text-sm outline-none focus:border-indigo-500"
302            }
303          )
304        ] }),
305        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "h-10 flex items-center gap-2 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg px-3", children: [
306          /* @__PURE__ */ jsxRuntimeExports.jsx(Calendar, { size: 16, className: "text-zinc-400" }),
307          /* @__PURE__ */ jsxRuntimeExports.jsx(
308            "input",
309            {
310              type: "date",
311              value: dateRange.start,
312              onChange: (e) => setDateRange({ ...dateRange, start: e.target.value }),
313              className: "bg-transparent text-xs outline-none w-24 dark:text-white"
314            }
315          ),
316          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400", children: "-" }),
317          /* @__PURE__ */ jsxRuntimeExports.jsx(
318            "input",
319            {
320              type: "date",
321              value: dateRange.end,
322              onChange: (e) => setDateRange({ ...dateRange, end: e.target.value }),
323              className: "bg-transparent text-xs outline-none w-24 dark:text-white"
324            }
325          )
326        ] })
327      ] })
328    ] }),
329    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 min-h-0 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-xl overflow-hidden flex flex-col shadow-sm", children: [
330      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "overflow-x-auto flex-1 custom-scrollbar", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("table", { className: "w-full text-left border-collapse", children: [
331        /* @__PURE__ */ jsxRuntimeExports.jsx("thead", { className: "bg-zinc-50 dark:bg-zinc-950 sticky top-0 z-10", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { children: [
332          /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "px-3 py-2 text-xs font-bold text-zinc-500 uppercase border-b border-zinc-200 dark:border-zinc-800 w-48", children: "Timestamp" }),
333          /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "px-3 py-2 text-xs font-bold text-zinc-500 uppercase border-b border-zinc-200 dark:border-zinc-800 w-32", children: "Level" }),
334          /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "px-3 py-2 text-xs font-bold text-zinc-500 uppercase border-b border-zinc-200 dark:border-zinc-800 w-72", children: "Source" }),
335          /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "px-3 py-2 text-xs font-bold text-zinc-500 uppercase border-b border-zinc-200 dark:border-zinc-800", children: "Action & Info" }),
336          /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "px-3 py-2 text-xs font-bold text-zinc-500 uppercase border-b border-zinc-200 dark:border-zinc-800 w-24 text-right", children: "Actions" })
337        ] }) }),
338        /* @__PURE__ */ jsxRuntimeExports.jsx("tbody", { className: "divide-y divide-zinc-100 dark:divide-zinc-800", children: visibleLogs.length > 0 ? visibleLogs.map((log) => {
339          var _a, _b, _c, _d, _e, _f, _g, _h;
340          const { detailsData, statusCode } = (() => {
341            if (!log.details) return { detailsData: {}, statusCode: null };
342            try {
343              const d = log.details.startsWith("{") ? JSON.parse(log.details) : { message: log.details };
344              return { detailsData: d, statusCode: d.statusCode };
345            } catch (e) {
346              return { detailsData: { message: log.details }, statusCode: null };
347            }
348          })();
349          const fileMatch = ((_a = log.details) == null ? void 0 : _a.match(/MIDI (\d+)/)) || ((_b = log.details) == null ? void 0 : _b.match(/track (\d+)/i)) || ((_c = log.details) == null ? void 0 : _c.match(/Success (\d+)/)) || ((_d = log.details) == null ? void 0 : _d.match(/Failed (\d+)/));
350          const fileId = fileMatch ? fileMatch[1] : null;
351          return /* @__PURE__ */ jsxRuntimeExports.jsxs(React.Fragment, { children: [
352            /* @__PURE__ */ jsxRuntimeExports.jsxs(
353              "tr",
354              {
355                onClick: () => toggleLog(log.id),
356                className: `cursor-pointer hover:bg-zinc-50 dark:hover:bg-zinc-800/50 transition-colors ${log.groupColorClass ? "border-l-4 " + log.groupColorClass : "border-l-4 border-transparent"}`,
357                children: [
358                  /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-1.5 text-sm font-mono text-zinc-500 whitespace-nowrap align-top", children: new Date(log.timestamp).toLocaleString() }),
359                  /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-1.5 align-top", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `px-1.5 py-0.5 rounded text-[10px] font-bold uppercase border ${getSeverityColor(log.severity)}`, children: log.severity }) }),
360                  /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-1.5 text-sm text-zinc-700 dark:text-zinc-300 align-top", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5", children: [
361                    log.category === "worker" ? /* @__PURE__ */ jsxRuntimeExports.jsx(Terminal, { size: 14, className: "text-zinc-400" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(User, { size: 14, className: "text-zinc-400" }),
362                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium whitespace-nowrap", title: log.userName || "System", children: log.userName || "System" })
363                  ] }) }),
364                  /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-1.5 text-sm align-top", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1", children: [
365                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
366                      groupWorkerLogs && log.isGroupStart && log.groupId && /* @__PURE__ */ jsxRuntimeExports.jsx(
367                        "button",
368                        {
369                          onClick: (e) => {
370                            e.stopPropagation();
371                            toggleGroup(log.groupId);
372                          },
373                          className: "p-0.5 hover:bg-zinc-200 dark:hover:bg-zinc-700 rounded text-zinc-500 flex items-center gap-1",
374                          children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-4 h-4 flex items-center justify-center font-bold", children: expandedGroups.has(log.groupId) ? "-" : "+" })
375                        }
376                      ),
377                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold text-indigo-600 dark:text-indigo-400 font-mono text-xs", children: log.action }),
378                      statusCode && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-[10px] px-1 rounded font-bold ${statusCode >= 400 ? "bg-red-100 text-red-600" : "bg-emerald-100 text-emerald-600"}`, children: statusCode }),
379                      groupWorkerLogs && log.isGroupStart && log.groupId && log.groupCount && log.groupCount > 1 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "bg-indigo-100 text-indigo-700 dark:bg-indigo-900/40 dark:text-indigo-300 text-[10px] px-1.5 py-0.5 rounded font-bold", children: [
380                        "+",
381                        log.groupCount - 1,
382                        " steps collapsed"
383                      ] })
384                    ] }),
385                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
386                      /* @__P
386URE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-zinc-500 line-clamp-1 italic", children: detailsData.message || (((_e = log.details) == null ? void 0 : _e.length) > 0 && !log.details.startsWith("{") ? log.details.substring(0, 100) : "") }),
387                      fileId && /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: `/track/${fileId}`, target: "_blank", rel: "noreferrer", onClick: (e) => e.stopPropagation(), className: "text-[10px] text-indigo-500 hover:underline", children: "View MIDI" })
388                    ] })
389                  ] }) }),
390                  /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-1.5 text-right align-top", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-end gap-1", children: [
391                    /* @__PURE__ */ jsxRuntimeExports.jsx(
392                      "button",
393                      {
394                        onClick: () => {
395                          if (confirm(`Clear all logs before ${new Date(log.timestamp).toLocaleString()}?`)) {
396                            api.clearActivityLogs({ beforeDate: log.timestamp }).then(() => fetchLogs());
397                          }
398                        },
399                        className: "p-1 text-zinc-400 hover:text-purple-500 hover:bg-purple-50 dark:hover:bg-purple-900/20 rounded",
400                        title: "Clear all logs before this time",
401                        children: /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { size: 14 })
402                      }
403                    ),
404                    /* @__PURE__ */ jsxRuntimeExports.jsx(
405                      "button",
406                      {
407                        onClick: () => {
408                          if (confirm(`Clear all logs from user '${log.userName}'?`)) {
409                            api.clearActivityLogs({ userId: log.userId }).then(() => fetchLogs());
410                          }
411                        },
412                        className: "p-1 text-zinc-400 hover:text-blue-500 hover:bg-blue-50 dark:hover:bg-blue-900/20 rounded",
413                        title: "Clear all from this user",
414                        children: /* @__PURE__ */ jsxRuntimeExports.jsx(User, { size: 14 })
415                      }
416                    ),
417                    /* @__PURE__ */ jsxRuntimeExports.jsx(
418                      "button",
419                      {
420                        onClick: () => {
421                          if (confirm(`Clear all logs from source '${log.action}'?`)) {
422                            api.clearActivityLogs({ source: log.action }).then(() => fetchLogs());
423                          }
424                        },
425                        className: "p-1 text-zinc-400 hover:text-orange-500 hover:bg-orange-50 dark:hover:bg-orange-900/20 rounded",
426                        title: "Clear all from this source",
427                        children: /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 14 })
428                      }
429                    ),
430                    /* @__PURE__ */ jsxRuntimeExports.jsx(
431                      "button",
432                      {
433                        onClick: () => {
434                          if (confirm("Delete this log entry?")) {
435                            setLogs((prev) => prev.filter((l) => l.id !== log.id));
436                            api.deleteActivityLog(log.id).catch((e) => {
437                              alert("Failed to delete log: " + e.message);
438                              fetchLogs();
439                            });
440                          }
441                        },
442                        className: "p-1 text-zinc-400 hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20 rounded",
443                        title: "Delete log",
444                        children: /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { size: 14 })
445                      }
446                    )
447                  ] }) })
448                ]
449              }
450            ),
451            expandedLogs.has(log.id) && /* @__PURE__ */ jsxRuntimeExports.jsx("tr", { className: `bg-zinc-50/50 dark:bg-zinc-900/50 ${log.groupColorClass ? "border-l-4 " + log.groupColorClass : "border-l-4 border-transparent"}`, children: /* @__PURE__ */ jsxRuntimeExports.jsx("td", { colSpan: 5, className: "px-4 py-3 border-b border-zinc-100 dark:border-zinc-800", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "pl-12", children: [
452              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 mb-4 text-zinc-400 uppercase font-bold text-[10px] tracking-wider", children: [
453                /* @__PURE__ */ jsxRuntimeExports.jsx(Info, { size: 12 }),
454                " Log Properties"
455              ] }),
456              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-4 gap-4 mb-6", children: [
457                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white dark:bg-zinc-950 p-3 rounded-lg border border-zinc-200 dark:border-zinc-800 shadow-sm", children: [
458                  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-400 uppercase font-bold mb-1", children: "Entry ID / User ID" }),
459                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-sm font-bold text-zinc-700 dark:text-zinc-300", children: [
460                    "#",
461                    log.id,
462                    " / ",
463                    log.userId && log.userId !== -1 ? `User#${log.userId}` : "Guest"
464                  ] })
465                ] }),
466                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white dark:bg-zinc-950 p-3 rounded-lg border border-zinc-200 dark:border-zinc-800 shadow-sm", children: [
467                  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-400 uppercase font-bold mb-1", children: "Source Name" }),
468                  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-bold text-zinc-700 dark:text-zinc-300", children: log.userName || "System" })
469                ] }),
470                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white dark:bg-zinc-950 p-3 rounded-lg border border-zinc-200 dark:border-zinc-800 shadow-sm", children: [
471                  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-400 uppercase font-bold mb-1", children: "IP Address" }),
472                  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-bold text-zinc-700 dark:text-zinc-300", children: log.ipAddress || ((_g = (_f = log.userName) == null ? void 0 : _f.match(/\((.*?)\)/)) == null ? void 0 : _g[1]) || "N/A" })
473                ] }),
474                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white dark:bg-zinc-950 p-3 rounded-lg border border-zinc-200 dark:border-zinc-800 shadow-sm", children: [
475                  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-400 uppercase font-bold mb-1", children: "Category / Group" }),
476                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-sm font-bold text-zinc-700 dark:text-zinc-300 capitalize", children: [
477                    log.category,
478                    " ",
479                    log.groupId ? `(${log.groupId})` : ""
480                  ] })
481                ] })
482              ] }),
483              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 mb-2 text-zinc-400 uppercase font-bold text-[10px] tracking-wider", children: [
484                /* @__PURE__ */ jsxRuntimeExports.jsx(Terminal, { size: 12 }),
485                " Full Log Context"
486              ] }),
487              /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "whitespace-pre-wrap break-words bg-white dark:bg-zinc-950 p-4 rounded-xl border border-zinc-200 dark:border-zinc-800 shadow-inner text-xs font-mono text-zinc-600 dark:text-zinc-400 overflow-auto max-h-96", children: ((_h = log.details) == null ? void 0 : _h.startsWith("{")) ? JSON.stringify(JSON.parse(log.details), null, 2) : log.details })
488            ] }) }) })
489          ] }, log.id);
490        }) : /* @__PURE__ */ jsxRuntimeExports.jsx("tr", { children: /* @__PURE__ */ jsxRuntimeExports.jsx("td", { colSpan: 5, className: "p-8 text-center text-zinc-500", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center gap-2", children: [
491          /* @__PURE__ */ jsxRuntimeExports.jsx(Info, { size: 24, className: "opacity-50" }),
492          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "No logs found matching your criteria." })
493        ] }) }) }) })
494      ] }) }),
495      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-3 border-t border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-950 flex justify-between items-center", children: [
496        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-zinc-500", children: total > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
497          "Showing ",
498          /* @__PURE__ */ jsxRuntimeExports.jsxs("strong", { className: "text-zinc-700 dark:text-zinc-300", children: [
499            (page - 1) * 100 + 1,
500            "-",
501            Math.min(page * 100, total)
502          ] }),
503          " of ",
504          /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "text-zinc-700 dark:text-zinc-300", children: total }),
505          " logs ",
506          groupWorkerLogs && visibleLogs.length < logs.length ? ` (${visibleLogs.length} rows collapsed)` : ""
507        ] }) : "No logs found" }),
508        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
509          /* @__PURE__ */ jsxRuntimeExports.jsx(
510            "button",
511            {
512              disabled: page === 1,
513              onClick: () => setPage((p) => p - 1),
514              className: "px-3 py-1 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded text-xs hover:bg-zinc-100 dark:hover:bg-zinc-800 disabled:opacity-50",
515              children: "Previous"
516            }
517          ),
518          /* @__PURE__ */ jsxRuntimeExports.jsx(
519            "button",
520            {
521              disabled: logs.length < 100,
522              onClick: () => setPage((p) => p + 1),
523              className: "px-3 py-1 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded text-xs hover:bg-zinc-100 dark:hover:bg-zinc-800 disabled:opacity-50",
524              children: "Next"
525            }
526          )
527        ] })
528      ] })
529    ] })
530  ] });
531};
532export {
533  UnifiedLogs as default
534};

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.