import { Divider, Space, Tag, Timeline } from "antd";
import { useEffect, useMemo, useState } from "react";
import dayjs from "../../utils/day";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectDarkMode } from "../../redux/application/application.selectors.js";
const mapStateToProps = createStructuredSelector({
isDarkMode: selectDarkMode
});
const mapDispatchToProps = () => ({});
export default connect(mapStateToProps, mapDispatchToProps)(DmsLogEvents);
export function DmsLogEvents({
logs,
detailsOpen,
detailsNonce,
isDarkMode,
colorizeJson = false,
showDetails = true
}) {
const [openSet, setOpenSet] = useState(() => new Set());
// Inject JSON highlight styles once (only when colorize is enabled)
useEffect(() => {
if (!colorizeJson) return;
if (typeof document === "undefined") return;
if (document.getElementById("json-highlight-styles")) return;
const style = document.createElement("style");
style.id = "json-highlight-styles";
style.textContent = `
.json-key { color: #fa8c16; }
.json-string { color: #52c41a; }
.json-number { color: #722ed1; }
.json-boolean { color: #1890ff; }
.json-null { color: #faad14; }
`;
document.head.appendChild(style);
}, [colorizeJson]);
// Trim openSet if logs shrink
useEffect(() => {
const len = (logs || []).length;
setOpenSet((prev) => {
const next = new Set();
for (let i = 0; i < len; i++) if (prev.has(i)) next.add(i);
return next;
});
}, [logs?.length]);
// Respond to global toggle button
useEffect(() => {
if (detailsNonce == null) return;
const len = (logs || []).length;
setOpenSet(detailsOpen ? new Set(Array.from({ length: len }, (_, i) => i)) : new Set());
}, [detailsNonce, detailsOpen, logs?.length]);
const items = useMemo(
() =>
(logs || []).map((raw, idx) => {
const { level, message, timestamp, meta } = normalizeLog(raw);
// Only treat meta as "present" when we are allowed to show details
const hasMeta = !isEmpty(meta) && showDetails;
const isOpen = hasMeta && openSet.has(idx);
return {
key: idx,
color: logLevelColor(level),
children: (
{jsonText};
};
/**
* Syntax highlight JSON text for HTML display.
* @param jsonText
* @returns {*}
*/
const syntaxHighlight = (jsonText) => {
const esc = jsonText.replace(/&/g, "&").replace(//g, ">");
return esc.replace(
/("(?:\\u[\da-fA-F]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(?:true|false|null)\b|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/g,
(match) => {
let cls = "json-number";
if (match.startsWith('"')) {
cls = match.endsWith(":") ? "json-key" : "json-string";
} else if (match === "true" || match === "false") {
cls = "json-boolean";
} else if (match === "null") {
cls = "json-null";
}
return `${match}`;
}
);
};