Skip to content

Commit

Permalink
feat: logs
Browse files Browse the repository at this point in the history
  • Loading branch information
shahargl committed Jan 21, 2025
1 parent d20ddeb commit 7d0bdb4
Show file tree
Hide file tree
Showing 17 changed files with 611 additions and 240 deletions.
2 changes: 1 addition & 1 deletion keep-ui/app/(keep)/alerts/alert-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ export function AlertTable({

return (
// Add h-screen to make it full height and remove the default flex-col gap
<div className="h-screen flex flex-col gap-4">
<div className="h-full flex flex-col gap-4">
{/* Add padding to account for any top nav/header */}
<div className="px-4 flex-none">
<TitleAndFilters
Expand Down
226 changes: 151 additions & 75 deletions keep-ui/app/(keep)/incidents/[id]/alerts/incident-alerts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ import clsx from "clsx";
import { IncidentAlertsTableBodySkeleton } from "./incident-alert-table-body-skeleton";
import { IncidentAlertsActions } from "./incident-alert-actions";
import { DynamicImageProviderIcon } from "@/components/ui";

import { useRef, useCallback } from "react";
import { AlertNameWithDescription } from "@/entities/alerts/ui";
interface Props {
incident: IncidentDto;
}
Expand All @@ -51,19 +52,99 @@ interface Pagination {
offset: number;
}

function useOptimalTableRows(
containerRef: RefObject<HTMLElement>,
{ minRows = 3, maxRows = 20, rowHeight = 110, uiElementsHeight = 136 } = {}
) {
const [availableHeight, setAvailableHeight] = useState(0);
const initialHeightSet = useRef(false);

useEffect(() => {
if (!containerRef.current || initialHeightSet.current) return;

let resizeObserver: ResizeObserver; // Declare here so we can use in cleanup

// Function to calculate and update height - only once
const updateHeight = () => {
if (initialHeightSet.current) return;

const rect = containerRef.current?.getBoundingClientRect();
if (rect && rect.height > 0) {
setAvailableHeight(rect.height);
initialHeightSet.current = true;

// Cleanup listeners once we have our height
if (resizeObserver) {
resizeObserver.disconnect();
}
window.removeEventListener("resize", updateHeight);
}
};

// Initial calculation
updateHeight();

// Create resize observer for initial size detection
resizeObserver = new ResizeObserver(updateHeight);
resizeObserver.observe(containerRef.current);

// Add window resize listener for initial zoom state
window.addEventListener("resize", updateHeight);

// Cleanup
return () => {
if (resizeObserver) {
resizeObserver.disconnect();
}
window.removeEventListener("resize", updateHeight);
};
}, []);

return useMemo(() => {
const tableHeight = availableHeight - uiElementsHeight;
const optimalRows = Math.max(minRows, Math.floor(tableHeight / rowHeight));
return Math.min(optimalRows, maxRows);
}, [availableHeight, minRows, maxRows, rowHeight, uiElementsHeight]);
}

const columnHelper = createColumnHelper<AlertDto>();

export default function IncidentAlerts({ incident }: Props) {
const containerRef = useRef<HTMLDivElement>(null);
const optimalPageSize = useOptimalTableRows(containerRef);

const [alertsPagination, setAlertsPagination] = useState<Pagination>({
limit: 20,
limit: optimalPageSize,
offset: 0,
});

// Keep pagination in sync with optimal page size
const [pagination, setTablePagination] = useState({
pageIndex: 0,
pageSize: 20,
pageSize: optimalPageSize,
});

useEffect(() => {
setTablePagination((prev) => ({
pageIndex: 0,
pageSize: optimalPageSize,
}));

setAlertsPagination((prev) => ({
limit: optimalPageSize,
offset: 0,
}));
}, [optimalPageSize]);

useEffect(() => {
if (pagination.pageSize !== alertsPagination.limit) {
setAlertsPagination({
limit: pagination.pageSize,
offset: pagination.pageIndex * pagination.pageSize,
});
}
}, [pagination]);

const {
data: alerts,
isLoading: _alertsLoading,
Expand Down Expand Up @@ -139,17 +220,7 @@ export default function IncidentAlerts({ incident }: Props) {
minSize: 100,
cell: (context) => (
<div className="max-w-[300px]">
<AlertName alert={context.row.original} />
</div>
),
}),
columnHelper.accessor("description", {
id: "description",
header: "Description",
minSize: 100,
cell: (context) => (
<div title={context.getValue()}>
<div className="truncate">{context.getValue()}</div>
<AlertNameWithDescription alert={context.row.original} />
</div>
),
}),
Expand Down Expand Up @@ -267,94 +338,99 @@ export default function IncidentAlerts({ incident }: Props) {
const selectedFingerprints = Object.keys(rowSelection);

return (
<>
<div className="flex flex-col h-full" ref={containerRef}>
<div className="flex justify-between items-center mb-2.5">
<h2 className="text-tremor-default font-medium text-tremor-content-strong dark:text-dark-tremor-content-strong">
Alerts
<h2 className="text-tremor-default font-medium text-tremor-content-subtle dark:text-dark-tremor-content-subtle">
ALERTS
</h2>
<IncidentAlertsActions
incidentId={incident.id}
selectedFingerprints={selectedFingerprints}
resetAlertsSelection={() => table.resetRowSelection()}
/>
</div>
<Card className="p-0 overflow-x-auto h-[calc(100vh-28rem)]">
<Table className="[&>table]:table-fixed">
<TableHead>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow
key={headerGroup.id}
className="border-b border-tremor-border dark:border-dark-tremor-border"
>
{headerGroup.headers.map((header, index) => {
const { style, className } =
getCommonPinningStylesAndClassNames(
header.column,
table.getState().columnPinning.left?.length,
table.getState().columnPinning.right?.length
);
return (
<TableHeaderCell
key={`header-${header.id}-${index}`}
style={style}
className={clsx(
header.column.columnDef.meta?.thClassName,
className
)}
>
{flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHeaderCell>
);
})}
</TableRow>
))}
</TableHead>
{alerts && alerts?.items?.length > 0 && (
<TableBody>
{table.getRowModel().rows.map((row, index) => (
<TableRow key={`row-${row.id}-${index}`}>
{row.getVisibleCells().map((cell, index) => {

{/* Table container with flex-1 and overflow handling */}
<div className="flex-1 min-h-0 overflow-hidden">
<div className="h-full overflow-auto">
<Table>
<TableHead>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow
key={headerGroup.id}
className="border-b border-tremor-border dark:border-dark-tremor-border"
>
{headerGroup.headers.map((header, index) => {
const { style, className } =
getCommonPinningStylesAndClassNames(
cell.column,
header.column,
table.getState().columnPinning.left?.length,
table.getState().columnPinning.right?.length
);
return (
<TableCell
key={`cell-${cell.id}-${index}`}
<TableHeaderCell
key={`header-${header.id}-${index}`}
style={style}
className={clsx(
cell.column.columnDef.meta?.tdClassName,
header.column.columnDef.meta?.thClassName,
className
)}
>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
header.column.columnDef.header,
header.getContext()
)}
</TableCell>
</TableHeaderCell>
);
})}
</TableRow>
))}
</TableBody>
)}
{isLoading && (
<IncidentAlertsTableBodySkeleton
table={table}
pageSize={pagination.pageSize - 10}
/>
)}
</Table>
</Card>
</TableHead>
{alerts && alerts?.items?.length > 0 && (
<TableBody>
{table.getRowModel().rows.map((row, index) => (
<TableRow key={`row-${row.id}-${index}`}>
{row.getVisibleCells().map((cell, index) => {
const { style, className } =
getCommonPinningStylesAndClassNames(
cell.column,
table.getState().columnPinning.left?.length,
table.getState().columnPinning.right?.length
);
return (
<TableCell
key={`cell-${cell.id}-${index}`}
style={style}
className={clsx(
cell.column.columnDef.meta?.tdClassName,
className
)}
>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
);
})}
</TableRow>
))}
</TableBody>
)}
{isLoading && (
<IncidentAlertsTableBodySkeleton
table={table}
pageSize={optimalPageSize}
/>
)}
</Table>
</div>
</div>

<div className="mt-4 mb-8">
{/* Pagination at the bottom */}
<div className="mt-4">
<TablePagination table={table} />
</div>
</>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function ChatFooter({
};

return (
<div className="border-t bg-white p-4">
<div className="border-t bg-white p-2">
<form onSubmit={onSubmit} className="flex items-center gap-4">
<UserAvatar user={user} className="h-8 w-8" />
<div className="flex-1 relative">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const components: Components = {
// Renders paragraphs of text with bottom margin
// Used for standard text blocks in markdown
p({ children }) {
return <p className="mb-2">{children}</p>;
return <p>{children}</p>;
},

// Renders hyperlinks that open in new tab
Expand Down Expand Up @@ -77,19 +77,19 @@ const components: Components = {
// Renders unordered lists with bullet points
// Used when markdown contains * or - list items
ul({ children }) {
return <ul className="list-disc pl-6">{children}</ul>;
return <ul className="list-disc pl-6 my-0">{children}</ul>;
},

// Renders ordered lists with numbers
// Used when markdown contains 1. 2. 3. list items
ol({ children }) {
return <ol className="list-decimal pl-6">{children}</ol>;
return <ol className="list-decimal pl-6 my-0">{children}</ol>;
},

// Renders list items within ul/ol
// Used for each item in a list
li({ children }) {
return <li>{children}</li>;
return <li className="my-0">{children}</li>;
},

// Renders heading level 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ export function MessageRenderer({
isLastMessage,
isLoading,
}: MessageRendererProps) {
const messageWrapper = "relative px-4 py-6";
const messageContent = "pl-6"; // Left padding for the line
const verticalLine = "absolute top-0 left-0 w-[2px] h-full bg-gray-200";
const messageWrapper = "relative px-4";
const messageContent = "pl-8"; // Increased left padding from 6 to 8
const verticalLine = "absolute top-0 left-[10px] w-[2px] h-full bg-gray-200"; // Added left offset

if (message instanceof TextMessage) {
return (
<div className={messageWrapper}>
<div className={verticalLine} />
<div className={messageContent}>
<div className="message-text break-words">
<div className="max-w-[95%] whitespace-pre-wrap prose prose-sm [&>*]:!m-0 [&_p]:!m-0 [&_ol]:!m-0 [&_ul]:!m-0 [&_li]:!m-0">
<div className="max-w-[95%] prose prose-sm [&>*]:!m-0 [&_p]:!m-0 [&_ol]:!m-0 [&_ul]:!m-0 [&_li]:!m-0">
<ChatMarkdown>{message.content}</ChatMarkdown>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export function UserAvatar({ user, className = "" }: UserAvatarProps) {
alt={user.name || "User"}
className="object-cover"
fill
sizes="(max-width: 32px) 32px"
sizes="32px 32px"
/>
</div>
);
Expand Down
Loading

0 comments on commit 7d0bdb4

Please sign in to comment.