Skip to content

Commit

Permalink
feat: logs v2 data fetching (#2825)
Browse files Browse the repository at this point in the history
* fix: style issues

* fix: focus issues of filters

* fix: search focus issue

* feat: add focus to control cloud

* feat: adjust rows for dynamic selection

* feat: add accessbility to display

* fix: show xx instead of direct status

* fix: duplicate filter when querying openai

* feat: add optional bottom content for table

* feat: add initial paginated data fetching

* fix: no need to send all the status variations its already handled on the backend

* feat: add different variants for searching path

* refactor: improve ai query

* chore: run formatter

* feat: add ability to return datetimes from ai

* fix: overflow issue of log details

* fix: update prompt and allow multiple requestID and host pass

* tests: add for filters

* feat: fetch paths for logs page

* chore: get rid of old logs page

* refactor: move datetime components to audit for temporarily

* refactor: add components to audit

* feat: add realtime data fetching

* feat: add new param for relative time search

* fix: filter reset

* feat: fix rendering issues when live enabled

* refacor: use maps to prevent array merges

* refactor: only allow refresh if relative time is around

* refactor: move since logic to trpc

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* [autofix.ci] apply automated fixes (attempt 3/3)

* refactor: allow llm to return relative times

* [autofix.ci] apply automated fixes

* feat: introduce new params for timeseries fetch

* feat: add proper data fetching

* test: add tests for hooks

* feat: add timestamp explainer for control pills

* fix: detail dock issue

* feat: add a hook to track applied filters

* fix: visual bugs

* fix: ui issues

* fix: audit style issues after logs page refactor

* chore: enable logs page flag

* fix: use same window for reseting and initial fetch

* working filter

* gh comment changes

* style changes

* refactor: improve search prompt

* [autofix.ci] apply automated fixes

* styles and icon

* fix: calendar range selection

* fix: bunch of fixes

* refactor: change how we show since filter

* refactor: if

* fix: wrap function call

* fix: datetime input alignment

* rename logs

* [autofix.ci] apply automated fixes

* fix: empty content

* fix: import paths

* [autofix.ci] apply automated fixes

* fix: broken tests

* fix: minor grammar issues

* fix: table spacing issues

* feat: add outcomes to table

* fix: spacing

* fix: wrap issue for outcome

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Michael Silva <[email protected]>
Co-authored-by: MichaelUnkey <[email protected]>
Co-authored-by: chronark <[email protected]>
  • Loading branch information
5 people authored Jan 31, 2025
1 parent 7fee06a commit 76d337a
Show file tree
Hide file tree
Showing 121 changed files with 4,605 additions and 3,534 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ArrowRight, Calendar as CalendarIcon } from "lucide-react";
import { parseAsInteger, useQueryStates } from "nuqs";
import { useEffect, useState } from "react";
import type { DateRange } from "react-day-picker";
import TimeSplitInput from "./time-split";
import { TimeSplitInput } from "./timesplit-input";

interface DatePickerWithRangeProps extends React.HTMLAttributes<HTMLDivElement> {
initialParams: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { DatePickerWithRange } from "@/app/(app)/logs/components/filters/components/custom-date-filter";
import { DEFAULT_BUCKET_NAME } from "@/lib/trpc/routers/audit/fetch";
import type { auditLogBucket, workspaces } from "@unkey/db/src/schema";
import { unkeyAuditLogEvents } from "@unkey/schema/src/auditlog";
Expand All @@ -7,6 +6,7 @@ import { Suspense } from "react";
import type { ParsedParams } from "../../actions";
import { BucketSelect } from "./bucket-select";
import { ClearButton } from "./clear-button";
import { DatePickerWithRange } from "./datepicker-with-range";
import { Filter } from "./filter";
import { RootKeyFilter } from "./root-key-filter";
import { UserFilter } from "./user-filter";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export interface TimeSplitInputProps {
endDate: Date;
}

const TimeSplitInput = ({
export const TimeSplitInput = ({
type,
time,
setTime,
Expand Down Expand Up @@ -272,5 +272,3 @@ const TimeSplitInput = ({
</div>
);
};

export default TimeSplitInput;
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,32 @@ import { LogDetails } from "./table-details";
import type { Data } from "./types";
import { getEventType } from "./utils";

const STATUS_STYLES: Record<
"create" | "update" | "delete" | "other",
{ base: string; hover: string; selected: string }
> = {
create: {
base: "text-accent-11 ",
hover: "hover:bg-accent-3",
selected: "bg-accent-3",
},
other: {
base: "text-accent-11 ",
hover: "hover:bg-accent-3",
selected: "bg-accent-3",
},
update: {
base: "text-warning-11 ",
hover: "hover:bg-warning-3",
selected: "bg-warning-3",
},
delete: {
base: "text-error-11",
hover: "hover:bg-error-3",
selected: "bg-error-3",
},
};

export const AuditLogTableClient = () => {
const [selectedLog, setSelectedLog] = useState<Data | null>(null);
const { setCursor, searchParams } = useAuditLogParams();
Expand All @@ -28,9 +54,7 @@ export const AuditLogTableClient = () => {
endTime: searchParams.endTime,
},
{
getNextPageParam: (lastPage) => {
return lastPage.nextCursor;
},
getNextPageParam: (lastPage) => lastPage.nextCursor,
initialCursor: searchParams.cursor,
staleTime: Number.POSITIVE_INFINITY,
refetchOnMount: false,
Expand All @@ -42,38 +66,37 @@ export const AuditLogTableClient = () => {

const handleLoadMore = () => {
if (hasNextPage && !isFetchingNextPage && data?.pages.length) {
// Get the current last page before fetching next
const currentLastPage = data.pages[data.pages.length - 1];

fetchNextPage().then(() => {
// Set the cursor to the last page we had before fetching
if (currentLastPage.nextCursor) {
setCursor(currentLastPage.nextCursor);
}
});
}
};

const getRowClassName = (item: Data) => {
const eventType = getEventType(item.auditLog.event);
return cn({
"hover:bg-error-3": eventType === "delete",
"hover:bg-warning-3": eventType === "update",
"hover:bg-success-3": eventType === "create",
});
const style = STATUS_STYLES[eventType];

return cn(
style.base,
style.hover,
"group rounded-md",
"focus:outline-none focus:ring-1 focus:ring-opacity-40 px-1",
selectedLog && {
"opacity-50 z-0": selectedLog.auditLog.id !== item.auditLog.id,
"opacity-100 z-10": selectedLog.auditLog.id === item.auditLog.id,
},
);
};

const getSelectedClassName = (item: Data, isSelected: boolean) => {
if (!isSelected) {
return "";
}

const eventType = getEventType(item.auditLog.event);
return cn({
"bg-error-3": eventType === "delete",
"bg-warning-3": eventType === "update",
"bg-success-3": eventType === "create",
"bg-accent-3": eventType === "other",
});
const style = STATUS_STYLES[getEventType(item.auditLog.event)];
return style.selected;
};

if (isError) {
Expand Down
33 changes: 18 additions & 15 deletions apps/dashboard/app/(app)/audit/components/table/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ export const columns: Column<Data>[] = [
{
key: "time",
header: "Time",
headerClassName: "pl-3",
width: "150px",
headerClassName: "pl-3",
noTruncate: true,
render: (log) => (
<div className="flex items-center gap-3 px-2">
<div className="flex items-center gap-3">
<TimestampInfo
value={log.auditLog.time}
className="font-mono group-hover:underline decoration-dotted"
Expand All @@ -24,25 +25,26 @@ export const columns: Column<Data>[] = [
{
key: "actor",
header: "Actor",
width: "15%",
headerClassName: "pl-3",
width: "7%",
noTruncate: true,
render: (log) => (
<div className="flex items-center gap-3 px-2">
<div className="flex items-center gap-3 truncate">
{log.auditLog.actor.type === "user" && log.user ? (
<div className="flex items-center w-full gap-2 max-sm:m-0 max-sm:gap-1 max-sm:text-xs">
<span className="text-xs whitespace-nowrap">{`${log.user.firstName ?? ""} ${
log.user.lastName ?? ""
}`}</span>
<span className="text-xs whitespace-nowrap">
{`${log.user.firstName ?? ""} ${log.user.lastName ?? ""}`}
</span>
</div>
) : log.auditLog.actor.type === "key" ? (
<div className="flex items-center w-full gap-2 max-sm:m-0 max-sm:gap-1 max-sm:text-xs">
<KeySquare className="w-4 h-4" />
<span className="font-mono text-xs">{log.auditLog.actor.id}</span>
<span className="font-mono text-xs truncate">{log.auditLog.actor.id}</span>
</div>
) : (
<div className="flex items-center w-full gap-2 max-sm:m-0 max-sm:gap-1 max-sm:text-xs">
<FunctionSquare className="w-4 h-4" />
<span className="font-mono text-xs">{log.auditLog.actor.id}</span>
<span className="font-mono text-xs truncate">{log.auditLog.actor.id}</span>
</div>
)}
</div>
Expand All @@ -51,8 +53,9 @@ export const columns: Column<Data>[] = [
{
key: "action",
header: "Action",
width: "15%",
headerClassName: "pl-3",
width: "7%",
noTruncate: true,
render: (log) => {
const eventType = getEventType(log.auditLog.event);
const badgeClassName = cn("font-mono capitalize", {
Expand All @@ -62,7 +65,7 @@ export const columns: Column<Data>[] = [
"bg-accent-3 text-accent-11 hover:bg-accent-4": eventType === "other",
});
return (
<div className="flex items-center gap-3 px-2">
<div className="flex items-center gap-3">
<Badge className={badgeClassName}>{eventType}</Badge>
</div>
);
Expand All @@ -71,21 +74,21 @@ export const columns: Column<Data>[] = [
{
key: "event",
header: "Event",
headerClassName: "pl-2",
width: "20%",
render: (log) => (
<div className="flex items-center gap-2 text-current font-mono text-xs px-2">
<div className="flex items-center gap-2 text-current font-mono text-xs truncate">
<span>{log.auditLog.event}</span>
</div>
),
},
{
key: "event-description",
header: "Description",
headerClassName: "pl-1",
width: "auto",
render: (log) => (
<div className="text-current font-mono px-2 text-xs">{log.auditLog.description}</div>
<div className="text-current font-mono text-xs truncate w-[200px]">
{log.auditLog.description}
</div>
),
},
];
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export const LogFooter = ({ log }: Props) => {
{
label: "Description",
description: (content) => (
<span className="text-[13px] font-mono text-end">{content}</span>
<span className="text-[13px] font-mono flex text-end">{content}</span>
),
content: log.auditLog.description,
tooltipContent: "Copy Description",
Expand Down
19 changes: 11 additions & 8 deletions apps/dashboard/app/(app)/audit/components/table/log-header.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Badge } from "@/components/ui/badge";
import { XMark } from "@unkey/icons";
import { Button } from "@unkey/ui";
import { X } from "lucide-react";
import type { Data } from "./types";

type Props = {
Expand All @@ -10,16 +10,19 @@ type Props = {

export const LogHeader = ({ onClose, log }: Props) => {
return (
<div className="border-b-[1px] px-3 py-4 flex justify-between border-border items-center">
<div className="flex gap-2 items-center overflow-hidden">
<Badge variant="secondary" className="bg-transparent shrink-0">
<div className="border-b-[1px] flex justify-between items-center border-gray-4 pb-3 w-full">
<div className="flex gap-2 items-center flex-1 min-w-0">
<Badge className="uppercase px-[6px] rounded-md font-mono bg-accent-3 text-accent-11 hover:bg-accent-4">
{log.auditLog.event}
</Badge>
</div>
<div className="flex gap-1 items-center shrink-0">
<Button shape="square" variant="ghost" onClick={onClose}>
<X size="22" strokeWidth="1.5" className="text-content/65 cursor-pointer" />
</Button>

<div className="flex gap-1 items-center ">
<div className="flex gap-3">
<Button size="icon" variant="ghost" onClick={onClose} className="[&_svg]:size-3">
<XMark className="text-gray-12 stroke-2" />
</Button>
</div>
</div>
</div>
);
Expand Down
42 changes: 15 additions & 27 deletions apps/dashboard/app/(app)/audit/components/table/table-details.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
"use client";

import { LogSection } from "@/app/(app)/logs/components/table/log-details/components/log-section";
import { memo, useMemo, useState } from "react";
import { useDebounceCallback } from "usehooks-ts";
import ResizablePanel from "../../../logs/components/table/log-details/resizable-panel";
import { ResizablePanel } from "@/app/(app)/logs/components/table/log-details/resizable-panel";
import { useMemo } from "react";
import { LogFooter } from "./log-footer";
import { LogHeader } from "./log-header";
import type { Data } from "./types";
Expand All @@ -14,35 +13,29 @@ type Props = {
distanceToTop: number;
};

const DEFAULT_DRAGGABLE_WIDTH = 450;
const PANEL_WIDTH_SET_DELAY = 150;
const PANEL_MAX_WIDTH = 600;
const PANEL_MIN_WIDTH = 400;

const _LogDetails = ({ log, onClose, distanceToTop }: Props) => {
const [panelWidth, setPanelWidth] = useState(DEFAULT_DRAGGABLE_WIDTH);
const createPanelStyle = (distanceToTop: number) => ({
top: `${distanceToTop}px`,
width: "500px",
height: `calc(100vh - ${distanceToTop}px)`,
paddingBottom: "1rem",
});

const debouncedSetPanelWidth = useDebounceCallback((newWidth) => {
setPanelWidth(newWidth);
}, PANEL_WIDTH_SET_DELAY);

const panelStyle = useMemo(
() => ({
top: `${distanceToTop}px`,
width: `${panelWidth}px`,
height: `calc(100vh - ${distanceToTop}px)`,
paddingBottom: "1rem",
}),
[distanceToTop, panelWidth],
);
export const LogDetails = ({ log, onClose, distanceToTop }: Props) => {
const panelStyle = useMemo(() => createPanelStyle(distanceToTop), [distanceToTop]);

if (!log) {
return null;
}

return (
<ResizablePanel
onResize={debouncedSetPanelWidth}
minW={PANEL_MIN_WIDTH}
maxW={PANEL_MAX_WIDTH}
onClose={onClose}
className="absolute right-0 bg-background border-l border-t border-solid font-mono border-border shadow-md overflow-y-auto z-[3]"
className="absolute right-0 bg-gray-1 dark:bg-black font-mono drop-shadow-2xl overflow-y-auto z-20 p-4"
style={panelStyle}
>
<LogHeader log={log} onClose={onClose} />
Expand All @@ -61,8 +54,3 @@ const _LogDetails = ({ log, onClose, distanceToTop }: Props) => {
</ResizablePanel>
);
};

export const LogDetails = memo(
_LogDetails,
(prev, next) => prev.log?.auditLog.id === next.log?.auditLog.id,
);
Loading

0 comments on commit 76d337a

Please sign in to comment.