From dbd4a9ea516ac1614e08d6ebab5ef8cc509f2a20 Mon Sep 17 00:00:00 2001 From: x032205 Date: Mon, 3 Nov 2025 13:27:13 -0500 Subject: [PATCH] added log searching, improved minor styling --- .../v2/HighlightText/HighlightText.tsx | 28 ++-------- .../components/PamSessionLogOutput.tsx | 19 +++++-- .../components/PamSessionLogsSection.tsx | 56 +++++++++++++++---- .../components/PamSessionRow.tsx | 34 ++++++----- 4 files changed, 84 insertions(+), 53 deletions(-) diff --git a/frontend/src/components/v2/HighlightText/HighlightText.tsx b/frontend/src/components/v2/HighlightText/HighlightText.tsx index c81dab2df..92fdc1d6d 100644 --- a/frontend/src/components/v2/HighlightText/HighlightText.tsx +++ b/frontend/src/components/v2/HighlightText/HighlightText.tsx @@ -9,22 +9,10 @@ export const HighlightText = ({ }) => { if (!text) return null; - const renderTextWithNewlines = (input: string, baseKeyPrefix: string = ""): React.ReactNode[] => { - if (!input) return []; - const lines = input.split("\n"); - return lines.flatMap((line, index) => { - const nodes: React.ReactNode[] = [line]; - if (index < lines.length - 1) { - nodes.push(
); - } - return nodes; - }); - }; - const searchTerm = highlight.toLowerCase().trim(); if (!searchTerm) { - return {renderTextWithNewlines(text, "full-text")}; + return {text}; } const parts: React.ReactNode[] = []; @@ -36,16 +24,12 @@ export const HighlightText = ({ text.replace(regex, (match: string, offset: number) => { if (offset > lastIndex) { const preMatchText = text.substring(lastIndex, offset); - parts.push( - - {renderTextWithNewlines(preMatchText, `pre-${lastIndex}`)} - - ); + parts.push({preMatchText}); } parts.push( - {renderTextWithNewlines(match, `match-${offset}`)} + {match} ); @@ -56,11 +40,7 @@ export const HighlightText = ({ if (lastIndex < text.length) { const postMatchText = text.substring(lastIndex); - parts.push( - - {renderTextWithNewlines(postMatchText, `post-${lastIndex}`)} - - ); + parts.push({postMatchText}); } return parts; diff --git a/frontend/src/pages/pam/PamSessionsByIDPage/components/PamSessionLogOutput.tsx b/frontend/src/pages/pam/PamSessionsByIDPage/components/PamSessionLogOutput.tsx index a0cfb6cb9..99e30627d 100644 --- a/frontend/src/pages/pam/PamSessionsByIDPage/components/PamSessionLogOutput.tsx +++ b/frontend/src/pages/pam/PamSessionsByIDPage/components/PamSessionLogOutput.tsx @@ -1,5 +1,6 @@ import { useState } from "react"; +import { HighlightText } from "@app/components/v2/HighlightText"; import { PamResourceType } from "@app/hooks/api/pam"; type TableLog = { @@ -10,10 +11,12 @@ type TableLog = { export const PamSessionLogOutput = ({ content, - resourceType + resourceType, + search }: { content: string; resourceType: PamResourceType; + search: string; }) => { const [isRawView, setIsRawView] = useState(false); @@ -45,7 +48,9 @@ export const PamSessionLogOutput = ({ return (
{isRawView ? ( -
{content}
+
+ +
) : ( <> {parsedContent.command && ( @@ -57,7 +62,7 @@ export const PamSessionLogOutput = ({ {headers.map((header) => ( - {header.replace(/_/g, " ")} + ))} @@ -71,7 +76,7 @@ export const PamSessionLogOutput = ({ > {headers.map((header) => ( - {String(row[header] ?? "")} + ))} @@ -103,5 +108,9 @@ export const PamSessionLogOutput = ({ ); } - return
{content}
; + return ( +
+ +
+ ); }; diff --git a/frontend/src/pages/pam/PamSessionsByIDPage/components/PamSessionLogsSection.tsx b/frontend/src/pages/pam/PamSessionsByIDPage/components/PamSessionLogsSection.tsx index b3c287380..0fbccbc9b 100644 --- a/frontend/src/pages/pam/PamSessionsByIDPage/components/PamSessionLogsSection.tsx +++ b/frontend/src/pages/pam/PamSessionsByIDPage/components/PamSessionLogsSection.tsx @@ -1,8 +1,10 @@ -import { useState } from "react"; -import { faChevronRight } from "@fortawesome/free-solid-svg-icons"; +import { useMemo, useState } from "react"; +import { faChevronRight, faMagnifyingGlass } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { twMerge } from "tailwind-merge"; +import { Input } from "@app/components/v2"; +import { HighlightText } from "@app/components/v2/HighlightText"; import { TPamSession } from "@app/hooks/api/pam"; import { PamSessionLogOutput } from "./PamSessionLogOutput"; @@ -14,6 +16,7 @@ type Props = { export const PamSessionLogsSection = ({ session }: Props) => { const [expandedLogTimestamps, setExpandedLogTimestamps] = useState>(new Set()); + const [search, setSearch] = useState(""); const toggleExpand = (timestamp: string) => { setExpandedLogTimestamps((prev) => { @@ -24,15 +27,43 @@ export const PamSessionLogsSection = ({ session }: Props) => { }); }; + const filteredLogs = useMemo( + () => + session.commandLogs.filter((log) => { + const { input, output } = log; + + const searchValue = search.trim().toLowerCase(); + + return ( + input.toLowerCase().includes(searchValue) || output.toLowerCase().includes(searchValue) + ); + }), + [session.commandLogs, search] + ); + return (

Session Logs

-
- {session.commandLogs.length > 0 ? ( - session.commandLogs.map((log) => { - const isExpanded = expandedLogTimestamps.has(log.timestamp); + +
+ { + const newSearch = e.target.value; + setSearch(newSearch); + }} + leftIcon={} + placeholder="Search logs..." + className="flex-1 bg-mineshaft-800" + containerClassName="bg-transparent" + /> +
+
+ {filteredLogs.length > 0 ? ( + filteredLogs.map((log) => { + const isExpanded = search.length || expandedLogTimestamps.has(log.timestamp); const formattedInput = formatLogContent(log.input); return ( @@ -62,7 +93,7 @@ export const PamSessionLogsSection = ({ session }: Props) => { isExpanded ? "break-all whitespace-pre-wrap" : "truncate" }`} > - {formattedInput} +
{
@@ -93,8 +125,12 @@ export const PamSessionLogsSection = ({ session }: Props) => { ); }) ) : ( -
- {session.startedAt && session.endedAt ? ( +
+ {search.length ? ( +
+
No logs match search criteria
+
+ ) : (
Session logs are not yet available
@@ -103,8 +139,6 @@ export const PamSessionLogsSection = ({ session }: Props) => { If logs do not appear after some time, please contact your Gateway administrators.
- ) : ( - "No session logs" )}
)} diff --git a/frontend/src/pages/pam/PamSessionsPage/components/PamSessionRow.tsx b/frontend/src/pages/pam/PamSessionsPage/components/PamSessionRow.tsx index f730e331d..8721560c4 100644 --- a/frontend/src/pages/pam/PamSessionsPage/components/PamSessionRow.tsx +++ b/frontend/src/pages/pam/PamSessionsPage/components/PamSessionRow.tsx @@ -27,6 +27,7 @@ import { HighlightText } from "@app/components/v2/HighlightText"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context"; import { PAM_RESOURCE_TYPE_MAP, TPamSession } from "@app/hooks/api/pam"; +import { formatLogContent } from "../../PamSessionsByIDPage/components/PamSessionLogsSection.utils"; import { PamSessionStatusBadge } from "./PamSessionStatusBadge"; type Props = { @@ -159,21 +160,28 @@ export const PamSessionRow = ({ session, search, filteredCommandLogs }: Props) = {filteredCommandLogs.length > 0 && ( - {logsToShow.map((log) => ( -
-
- - {new Date(log.timestamp).toLocaleString()} -
+ {logsToShow.map((log) => { + const formattedInput = formatLogContent(log.input); -
- + return ( +
+
+ + {new Date(log.timestamp).toLocaleString()} +
+ +
+ +
+
+ +
-
- -
-
- ))} + ); + })} {filteredCommandLogs.length > LOGS_TO_SHOW && (