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 && (