Skip to content

refactor: Audit filter#2915

Merged
chronark merged 18 commits intomainfrom
audit-filter-refactor
Feb 26, 2025
Merged

refactor: Audit filter#2915
chronark merged 18 commits intomainfrom
audit-filter-refactor

Conversation

@ogzhanolguncu
Copy link
Contributor

@ogzhanolguncu ogzhanolguncu commented Feb 25, 2025

What does this PR do?

Fixes # (issue)

If there is not an issue for this, please create one first. This is used to tracking purposes and also helps use understand why this PR exists

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Chore (refactoring code, technical debt, workflow improvements)
  • Enhancement (small improvements)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How should this be tested?

  • Test A
  • Test B

Checklist

Required

  • Filled out the "How to test" section in this PR
  • Read Contributing Guide
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand areas
  • Ran pnpm build
  • Ran pnpm fmt
  • Checked for warnings, there are none
  • Removed all console.logs
  • Merged the latest changes from main onto my branch with git pull origin main
  • My changes don't cause any responsiveness issues

Appreciated

  • If a UI change was made: Added a screen recording or screenshots to this PR
  • Updated the Unkey Docs if changes were necessary

Summary by CodeRabbit

  • New Features

    • Enhanced the audit logs interface with dynamic filtering, search, date/time selection, and refresh controls.
    • Introduced a natural language search that provides immediate feedback and refined query results.
    • Added new components for filtering logs by events, users, root keys, and buckets.
    • Integrated organization membership retrieval into the workspace fetching process.
  • Refactor

    • Streamlined the audit logs experience by deprecating legacy filtering and display components.
    • Revamped state management and table responsiveness for a cleaner, more intuitive user interface.
    • Updated the structure and types for audit log data and query parameters for improved type safety.
    • Converted the VirtualTable component to a forward-ref component for better ref handling.
  • Bug Fixes

    • Resolved issues related to the handling of filter states and selection modes in checkbox filters.

@changeset-bot
Copy link

changeset-bot bot commented Feb 25, 2025

⚠️ No Changeset found

Latest commit: 7abc880

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 25, 2025

📝 Walkthrough

Walkthrough

This pull request introduces extensive restructuring and enhancements across the audit logging system. The changes integrate organization member retrieval into workspace fetching, add several new React components for audit log controls (such as filters, search, date-time, and refresh), update table rendering with new types and hooks, refactor TRPC procedures to support structured audit log queries and LLM-based search, and remove deprecated components and types. Imports, constants, and utility functions have also been updated or relocated to support these modifications.

Changes

File(s) Change Summary
apps/dashboard/app/(app)/audit/actions.ts Enhanced getWorkspace to fetch organization members via clerkClient; removed export of SearchParams, ParsedParams, and parseFilterParams.
apps/dashboard/app/(app)/audit/audit.type.ts Removed file and the AuditData type definition.
apps/dashboard/app/(app)/audit/components/control-cloud/index.tsx
apps/dashboard/app/(app)/audit/components/controls/components/logs-datetime/index.tsx
apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/components/{bucket-filter,events-filter,root-keys-filter,users-filter}.tsx
apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/index.tsx
apps/dashboard/app/(app)/audit/components/controls/components/logs-refresh.tsx
apps/dashboard/app/(app)/audit/components/controls/components/logs-search/index.tsx
apps/dashboard/app/(app)/audit/components/controls/index.tsx
Added several new components: AuditLogsControlCloud, LogsDateTime, BucketFilter, EventsFilter, RootKeysFilter, UsersFilter, LogsFilters, LogsRefresh, LogsSearch, and AuditLogsControls to provide refined control and filtering of audit log data.
apps/dashboard/app/(app)/audit/components/filters/* Removed legacy filter components: BucketSelect, ClearButton, DatePickerWithRange, Filter/CustomFilter, Filters, RootKeyFilter, TimeSplitInput, and UserFilter.
apps/dashboard/app/(app)/audit/components/logs-client.tsx
apps/dashboard/app/(app)/audit/components/table/*
apps/dashboard/app/(app)/audit/components/table/hooks/use-logs-query.ts
(log-detail files update)
Updated LogsClient to use new WorkspaceProps and state management; removed obsolete table components (audit-logs-table.tsx, columns.tsx, constants.ts, resizable-panel.tsx) and replaced AuditData with new AuditLog in log details. Added a new hook (useAuditLogsQuery) for fetching audit logs.
apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/* Updated import paths (e.g. for RequestResponseDetails), removed constants (MAX_DRAGGABLE_WIDTH, MIN_DRAGGABLE_WIDTH), and eliminated deprecated resizable panel components.
apps/dashboard/components/virtual-table/index.tsx Refactored VirtualTable into a forward-ref component; added new exported type VirtualTableRef.
apps/dashboard/lib/trpc/routers/audit/fetch.ts Modified fetchAuditLog to use auditQueryLogsPayload and output an AuditLogsResponse; updated the query parameter structure and renamed the endpoint to be accessed via logs.
apps/dashboard/lib/trpc/routers/audit/llm-search/* Added new files for LLM-based audit log search, including an endpoint (auditLogsSearch) and utilities (getStructuredAuditSearchFromLLM, getAuditSystemPrompt).
apps/dashboard/lib/trpc/routers/audit/schema.ts Introduced new Zod schemas and TypeScript types for audit logs and query parameters.
apps/dashboard/lib/trpc/routers/audit/utils.ts Added a new transformFilters function to process and transform audit log query parameters.
apps/dashboard/lib/trpc/routers/index.ts Updated audit router: renamed fetch to logs and added new llmSearch endpoint.
apps/dashboard/app/(app)/audit/page.tsx Simplified the AuditPage component by removing PageContent and Filters, destructuring workspace and members from getWorkspace, and directly rendering LogsClient.
apps/dashboard/app/(app)/audit/query-state.ts Removed the file that managed audit log query parameters.
Other files in apps/dashboard/app/(app)/logs/* and ratelimit logs Updated import paths (e.g., using absolute paths for RequestResponseDetails or ResizablePanel) and removed obsolete constants.
apps/dashboard/components/logs/details/resizable-panel.tsx Added local constants (MAX_DRAGGABLE_WIDTH, MIN_DRAGGABLE_WIDTH) to replace external dependency.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Audit Client
  participant WorkspaceFn as getWorkspace()
  participant Clerk as clerkClient
  participant Logger as Error Logger

  Client->>WorkspaceFn: Call getWorkspace(tenantId,...)
  WorkspaceFn->>WorkspaceFn: Check if tenantId starts with "user_"
  alt Organization Tenant
    WorkspaceFn->>Clerk: Call clerkClient.organizations.getOrganizationMembershipList
    Clerk-->>WorkspaceFn: Return membership list
    WorkspaceFn->>WorkspaceFn: Filter members with publicUserData
  else User Tenant
    WorkspaceFn->>WorkspaceFn: Set members as null
  end
  WorkspaceFn->>Client: Return { workspace, members }
  Note over WorkspaceFn: Error handling logs issues via Logger
Loading
sequenceDiagram
  participant User as End User
  participant SearchComp as LogsSearch Component
  participant TRPC as TRPC Mutation (queryLLMForStructuredOutput)
  participant LLM as OpenAI API
  participant Filters as Filter Update Handler

  User->>SearchComp: Submit search query
  SearchComp->>TRPC: Trigger mutation with query and timestamp
  TRPC->>LLM: Call getStructuredAuditSearchFromLLM(query, timestamp)
  LLM-->>TRPC: Return structured audit search result
  TRPC->>SearchComp: Return response (filters or error)
  alt Successful Response
    SearchComp->>Filters: Update filters with structured output
  else Error Response
    SearchComp->>User: Display error toast
  end
Loading

Possibly related PRs

  • refactor: query audit logs from planetscale #2181: The changes in the main PR, which enhance the getWorkspace function and modify the structure of audit log data, are related to the retrieved PR as both involve updates to the handling and structure of audit logs, specifically in how logs are fetched and represented in the application.
  • fix: audit log filters use search params #2769: The changes in the main PR, specifically the enhancements to the getWorkspace function and the introduction of organization membership retrieval, are related to the modifications in the retrieved PR, which also involves updates to the bucket parameter and its handling in the context of audit logs, indicating a shared focus on filtering and managing audit-related data.
  • fix: use workspace name in team switcher #2719: The changes in the main PR, which enhance the getWorkspace function to include organization membership retrieval, are related to the retrieved PR as both involve modifications to the handling of workspace data, specifically through the introduction of a workspace prop in the WorkspaceSwitcher component.

Suggested labels

Needs Approval

Suggested reviewers

  • mcstepp
  • chronark
  • perkinsjr

Tip

CodeRabbit's docstrings feature is now available as part of our Pro Plan! Simply use the command @coderabbitai generate docstrings to have CodeRabbit automatically generate docstrings for your pull request. We would love to hear your feedback on Discord.

✨ Finishing Touches
  • 📝 Generate Docstrings

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@vercel
Copy link

vercel bot commented Feb 25, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
engineering ✅ Ready (Inspect) Visit Preview 💬 Add feedback Feb 26, 2025 3:09pm
play ✅ Ready (Inspect) Visit Preview 💬 Add feedback Feb 26, 2025 3:09pm
www ✅ Ready (Inspect) Visit Preview 💬 Add feedback Feb 26, 2025 3:09pm
1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
dashboard ⬜️ Ignored (Inspect) Visit Preview Feb 26, 2025 3:09pm

@github-actions
Copy link
Contributor

github-actions bot commented Feb 25, 2025

Thank you for following the naming conventions for pull request titles! 🙏

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (26)
apps/dashboard/app/(app)/audit/components/control-cloud/index.tsx (1)

26-37: Add error handling for possible undefined filter values.

The component utilizes values from the useFilters hook without checking if they're undefined. Consider adding default values or null checks to prevent potential runtime errors.

export const AuditLogsControlCloud = () => {
-  const { filters, updateFilters, removeFilter } = useFilters();
+  const { filters = {}, updateFilters = () => {}, removeFilter = () => {} } = useFilters();
  return (
    <ControlCloud
      historicalWindow={HISTORICAL_DATA_WINDOW}
      formatFieldName={formatFieldName}
      filters={filters}
      removeFilter={removeFilter}
      updateFilters={updateFilters}
    />
  );
};
apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/components/events-filter.tsx (1)

14-19: Add error handling for empty events array.

The component doesn't handle the case where unkeyAuditLogEvents.Values might be undefined or empty. Consider adding a fallback or check to prevent potential errors.

      options={Object.values(unkeyAuditLogEvents.Values).map<EventsOption>((value, index) => ({
        id: index,
        display: value,
        label: value,
        checked: false,
-      }))}
+      })) || []}
apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/components/root-keys-filter.tsx (1)

14-19: Defensive null check is redundant with TypeScript definition.

The code includes a null check (rootKeys ?? []) but the prop type doesn't allow for null. Either update the type to include null or remove the null check.

-      options={(rootKeys ?? []).map((rootKey, index) => ({
+      options={rootKeys.map((rootKey, index) => ({

Or update the type definition to:

rootKeys: {
  id: string;
  name: string | null;
}[] | null;
apps/dashboard/lib/trpc/routers/audit/llm-search/index.ts (1)

15-39: Consider adding specific error handling for OpenAI API errors.

The implementation calls getStructuredAuditSearchFromLLM but doesn't provide specific error handling for potential OpenAI API errors (rate limiting, token limits, etc.).

Consider wrapping the OpenAI call with specific error handling:

-    return await getStructuredAuditSearchFromLLM(openai, input.query, input.timestamp);
+    try {
+      return await getStructuredAuditSearchFromLLM(openai, input.query, input.timestamp);
+    } catch (error) {
+      if (error instanceof OpenAI.APIError) {
+        throw new TRPCError({
+          code: "INTERNAL_SERVER_ERROR",
+          message: `OpenAI API error: ${error.message}`,
+          cause: error,
+        });
+      }
+      throw error;
+    }
apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/components/bucket-filter.tsx (1)

1-30: Add better handling for empty bucket filters.

The component correctly handles null bucketFilter with the nullish coalescing operator, but could improve the user experience when there are no options.

Consider enhancing the component with empty state handling:

 export const BucketFilter = ({
   bucketFilter,
 }: {
   bucketFilter: {
     id: string;
     name: string | null;
   }[];
 }) => {
+  const options = (bucketFilter ?? []).map((rootKey, index) => ({
+    label: rootKey.name,
+    value: rootKey.id,
+    checked: false,
+    id: index,
+  }));
+
+  if (options.length === 0) {
+    return <div className="text-muted-foreground text-xs py-2">No bucket filters available</div>;
+  }

   return (
     <FilterCheckbox
       showScroll
-      options={(bucketFilter ?? []).map((rootKey, index) => ({
-        label: rootKey.name,
-        value: rootKey.id,
-        checked: false,
-        id: index,
-      }))}
+      options={options}
       filterField="status"
       checkPath="status"
       renderOptionContent={(checkbox) => (
         <div className="text-accent-12 text-xs">{checkbox.label}</div>
       )}
       createFilterValue={(option) => ({
         value: option.value,
       })}
     />
   );
 };
apps/dashboard/app/(app)/audit/components/controls/components/logs-search/index.tsx (1)

7-56: Consider debouncing search requests.

There's no mechanism to prevent rapid successive search requests, which could lead to rate limiting or unnecessary API calls.

Add debouncing to the search function:

 import { LogsLLMSearch } from "@/components/logs/llm-search";
 import { transformStructuredOutputToFilters } from "@/components/logs/validation/utils/transform-structured-output-filter-format";
 import { toast } from "@/components/ui/toaster";
 import { trpc } from "@/lib/trpc/client";
+import { useDebouncedCallback } from "use-debounce";
 import { useFilters } from "../../../../hooks/use-filters";

 export const LogsSearch = () => {
   const { filters, updateFilters } = useFilters();
   const queryLLMForStructuredOutput = trpc.audit.llmSearch.useMutation({
     // ... existing code ...
   });

+  const debouncedSearch = useDebouncedCallback((query: string) => {
+    queryLLMForStructuredOutput.mutateAsync({
+      query,
+      timestamp: Date.now(),
+    });
+  }, 500);

   return (
     <LogsLLMSearch
       isLoading={queryLLMForStructuredOutput.isLoading}
-      onSearch={(query) =>
-        queryLLMForStructuredOutput.mutateAsync({
-          query,
-          timestamp: Date.now(),
-        })
-      }
+      onSearch={debouncedSearch}
     />
   );
 };
apps/dashboard/lib/trpc/routers/audit/utils.ts (1)

16-32: Consistent filter mapping implementation

The implementation for mapping events, users, and rootKeys filters follows the same pattern, which is good for consistency and maintainability. However, consider using a helper function to reduce repetition.

+// Helper function to map filters
+const mapFilters = (filtersObj?: { filters: Array<{ operator: string; value: string }> }) => 
+  filtersObj?.filters.map((f) => ({
+    operator: f.operator,
+    value: f.value,
+  })) ?? [];
+
 export function transformFilters(
   params: AuditQueryLogsPayload,
 ): Omit<AuditQueryLogsParams, "workspaceId"> {
   // ... existing code ...
 
-  const events =
-    params.events?.filters.map((f) => ({
-      operator: f.operator,
-      value: f.value,
-    })) ?? [];
-
-  const users =
-    params.users?.filters.map((f) => ({
-      operator: f.operator,
-      value: f.value,
-    })) ?? [];
-
-  const rootKeys =
-    params.rootKeys?.filters.map((f) => ({
-      operator: f.operator,
-      value: f.value,
-    })) ?? [];
+  const events = mapFilters(params.events);
+  const users = mapFilters(params.users);
+  const rootKeys = mapFilters(params.rootKeys);
apps/dashboard/app/(app)/audit/components/table/query-logs.schema.ts (1)

10-19: Repeated filter structure pattern

The schema definition for events, users, and rootKeys follows the same pattern. Consider extracting a reusable filter schema to reduce duplication.

+const filterSchema = z.object({
+  filters: z.array(
+    z.object({
+      operator: z.literal("is"),
+      value: z.string(),
+    }),
+  ),
+});
+
 export const auditQueryLogsPayload = z.object({
   limit: z.number().int(),
   startTime: z.number().int(),
   endTime: z.number().int(),
   since: z.string(),
   bucket: z.string().default(DEFAULT_BUCKET_NAME),
-  events: z
-    .object({
-      filters: z.array(
-        z.object({
-          operator: z.literal("is"),
-          value: z.string(),
-        }),
-      ),
-    })
-    .nullable(),
-  users: z
-    .object({
-      filters: z.array(
-        z.object({
-          operator: z.literal("is"),
-          value: z.string(),
-        }),
-      ),
-    })
-    .nullable(),
-  rootKeys: z
-    .object({
-      filters: z.array(
-        z.object({
-          operator: z.literal("is"),
-          value: z.string(),
-        }),
-      ),
-    })
-    .nullable(),
+  events: filterSchema.nullable(),
+  users: filterSchema.nullable(),
+  rootKeys: filterSchema.nullable(),

Also applies to: 20-29, 30-39

apps/dashboard/app/(app)/audit/components/controls/components/logs-datetime/index.tsx (1)

9-16: Simplify state initialization

The useEffect hook is being used to set a default value for the title state when it could be done directly in the useState call. This would simplify the code and avoid an unnecessary effect run.

-  const [title, setTitle] = useState<string | null>(null);
+  const [title, setTitle] = useState<string>("Last 12 hours");

-  useEffect(() => {
-    if (!title) {
-      setTitle("Last 12 hours");
-    }
-  }, [title]);
apps/dashboard/app/(app)/audit/components/table/logs-table.tsx (2)

78-96: Extract actor rendering logic to a separate function

The actor rendering logic is complex with multiple conditions. Consider extracting this to a separate function to improve readability and maintainability.

// Add this function above the columns definition
+const renderActor = (log: AuditLog) => {
+  if (log.auditLog.actor.type === "user" && log.user) {
+    return (
+      <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>
+      </div>
+    );
+  } else if (log.auditLog.actor.type === "key") {
+    return (
+      <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 truncate">{log.auditLog.actor.id}</span>
+      </div>
+    );
+  } else {
+    return (
+      <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 truncate">{log.auditLog.actor.id}</span>
+      </div>
+    );
+  }
+};

// Then use it in the column definition
   render: (log) => (
     <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>
-        </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 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 truncate">{log.auditLog.actor.id}</span>
-        </div>
-      )}
+      {renderActor(log)}
     </div>
   ),

136-138: Consider using text overflow styling instead of fixed width

The description column has a fixed width of 200px which might truncate longer descriptions. Consider using CSS text overflow properties instead of a fixed width to allow the description to take up available space while still truncating when necessary.

   render: (log) => (
-      <div className="font-mono text-xs truncate w-[200px]">{log.auditLog.description}</div>
+      <div className="font-mono text-xs truncate max-w-full">{log.auditLog.description}</div>
   ),
apps/dashboard/app/(app)/audit/hooks/use-filters.ts (2)

70-80: Use type guards for safer type casting

The code uses type casting for fields and values which could lead to runtime errors. Consider using type guards for safer type handling.

   ["startTime", "endTime", "since"].forEach((field) => {
      const value = searchParams[field as keyof QuerySearchParams];
      if (value !== null && value !== undefined) {
+       // Check if field is a valid AuditLogsFilterField
+       if (!["startTime", "endTime", "since"].includes(field)) {
+         console.error(`Invalid field: ${field}`);
+         return;
+       }
        activeFilters.push({
-         field: field as AuditLogsFilterField,
+         field: field as AuditLogsFilterField, // Now safer with validation
          operator: "is",
-         value: value as string | number,
+         value: value,
        });
      }
    });

102-133: Add a default case to the switch statement

The switch statement doesn't have a default case to handle unexpected filter field values. This could lead to silent failures if a new filter field is added but the switch statement isn't updated.

      newFilters.forEach((filter) => {
        switch (filter.field) {
          case "events":
            // existing code...
            break;
          // other cases...
          case "bucket":
            newParams.bucket = filter.value as string;
            break;
          case "startTime":
          case "endTime":
            newParams[filter.field] = filter.value as number;
            break;
          case "since":
            newParams.since = filter.value as string;
            break;
+         default:
+           console.warn(`Unhandled filter field: ${filter.field}`);
+           break;
        }
      });
apps/dashboard/app/(app)/audit/components/table/hooks/use-logs-query.ts (2)

73-73: Fix incorrect error message

The error message for startTime/endTime filter says the value type should be 'string', but the type check is for 'number'.

-            console.error(`${filter.field} filter value type has to be 'string'`);
+            console.error(`${filter.field} filter value type has to be 'number'`);

33-98: Add a default case to the switch statement

The switch statement doesn't handle unexpected filter field values. Adding a default case would make the code more robust.

      switch (filter.field) {
        case "events": {
          // existing code...
        }
        // other cases...
        case "bucket": {
          // existing code...
          break;
        }
+       default: {
+         console.warn(`Unhandled filter field: ${filter.field}`);
+         break;
+       }
      }
apps/dashboard/app/(app)/audit/actions.ts (2)

7-30: Handle potential partial failure more explicitly.

When membership fetching fails (lines 23–29), only a console error is logged, and the code continues with members = null. This can be acceptable, but returning a partial or null membership list might confuse downstream logic. Consider returning an empty array instead of null for better consistency.

- let members = null;
+ let members: { label: string; value: string }[] = [];

56-65: Return structure looks coherent, but ensure consistent handling for members.

Appending keys to the workspace object is well-organized. However, consider returning [] instead of null for members to avoid null-check overhead in the consumer.

apps/dashboard/app/(app)/audit/components/logs-client.tsx (2)

10-25: Prefer using an empty array for members instead of null.

Defining members as | null can force consumers to do null checks. Consider always returning a non-null, possibly empty, array for simpler handling.


37-43: Spreading props is convenient but may pass unneeded fields.

<AuditLogsControls {...props} /> ensures the component has everything it needs. This is fine as a short-term solution, but if the prop set grows, consider passing only relevant fields explicitly for clarity.

apps/dashboard/lib/trpc/routers/audit/llm-search/utils.ts (1)

82-259: Large inline prompt is fine but may be extracted into a config for maintainability.

getAuditSystemPrompt is well-documented. For large strings, consider placing them in a separate config or storing them as template files to simplify future changes.

apps/dashboard/lib/trpc/routers/audit/schema.ts (1)

4-35: Ensure consistent usage of user object
You may consider providing a default user object or ensuring the user property is always defined. This can simplify consumer code and reduce null checks at runtime.

apps/dashboard/app/(app)/audit/components/table/utils/get-row-class.ts (2)

7-27: Consider broader coverage of synonyms
getEventType handles "create", "update", and "delete" with synonyms like "add", "edit", and "remove". Consider including other potential synonyms ("rename", "archive", etc.) if needed later.


87-103: Refine row selection styling
The current approach toggles opacity when logSelected is true but isSelected is false. Consider a more distinct visual indicator for clarity.

apps/dashboard/app/(app)/audit/filters.schema.ts (1)

8-37: Operator variety
Currently, only the "is" operator is supported. In future, you might add "contains", "in", or other operators to expand filter capabilities.

apps/dashboard/lib/trpc/routers/audit/fetch.ts (2)

87-135: Potential indexing for performance
The approach to filtering by actorId and event is solid. Depending on volume, adding database indexes could boost query performance for large datasets.


136-142: Simple pagination logic
omitLastItemForPagination is straightforward and effective for detecting hasMore. Just confirm the UI handles the final page properly (e.g., no extra empty state).

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b043686 and de81457.

📒 Files selected for processing (53)
  • apps/dashboard/app/(app)/audit/actions.ts (2 hunks)
  • apps/dashboard/app/(app)/audit/audit.type.ts (0 hunks)
  • apps/dashboard/app/(app)/audit/components/control-cloud/index.tsx (1 hunks)
  • apps/dashboard/app/(app)/audit/components/controls/components/logs-datetime/index.tsx (1 hunks)
  • apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/components/bucket-filter.tsx (1 hunks)
  • apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/components/events-filter.tsx (1 hunks)
  • apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/components/root-keys-filter.tsx (1 hunks)
  • apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/components/users-filter.tsx (1 hunks)
  • apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/index.tsx (1 hunks)
  • apps/dashboard/app/(app)/audit/components/controls/components/logs-refresh.tsx (1 hunks)
  • apps/dashboard/app/(app)/audit/components/controls/components/logs-search/index.tsx (1 hunks)
  • apps/dashboard/app/(app)/audit/components/controls/index.tsx (1 hunks)
  • apps/dashboard/app/(app)/audit/components/filters/bucket-select.tsx (0 hunks)
  • apps/dashboard/app/(app)/audit/components/filters/clear-button.tsx (0 hunks)
  • apps/dashboard/app/(app)/audit/components/filters/datepicker-with-range.tsx (0 hunks)
  • apps/dashboard/app/(app)/audit/components/filters/filter.tsx (0 hunks)
  • apps/dashboard/app/(app)/audit/components/filters/index.tsx (0 hunks)
  • apps/dashboard/app/(app)/audit/components/filters/root-key-filter.tsx (0 hunks)
  • apps/dashboard/app/(app)/audit/components/filters/timesplit-input.tsx (0 hunks)
  • apps/dashboard/app/(app)/audit/components/filters/user-filter.tsx (0 hunks)
  • apps/dashboard/app/(app)/audit/components/logs-client.tsx (1 hunks)
  • apps/dashboard/app/(app)/audit/components/table/audit-logs-table.tsx (0 hunks)
  • apps/dashboard/app/(app)/audit/components/table/columns.tsx (0 hunks)
  • apps/dashboard/app/(app)/audit/components/table/constants.ts (0 hunks)
  • apps/dashboard/app/(app)/audit/components/table/hooks/use-logs-query.ts (1 hunks)
  • apps/dashboard/app/(app)/audit/components/table/log-details/components/log-footer.tsx (1 hunks)
  • apps/dashboard/app/(app)/audit/components/table/log-details/components/log-header.tsx (1 hunks)
  • apps/dashboard/app/(app)/audit/components/table/log-details/index.tsx (2 hunks)
  • apps/dashboard/app/(app)/audit/components/table/log-details/resizable-panel.tsx (0 hunks)
  • apps/dashboard/app/(app)/audit/components/table/logs-table.tsx (1 hunks)
  • apps/dashboard/app/(app)/audit/components/table/query-logs.schema.ts (1 hunks)
  • apps/dashboard/app/(app)/audit/components/table/utils.ts (0 hunks)
  • apps/dashboard/app/(app)/audit/components/table/utils/get-row-class.ts (1 hunks)
  • apps/dashboard/app/(app)/audit/filters.schema.ts (1 hunks)
  • apps/dashboard/app/(app)/audit/hooks/use-filters.ts (1 hunks)
  • apps/dashboard/app/(app)/audit/page.tsx (2 hunks)
  • apps/dashboard/app/(app)/audit/query-state.ts (0 hunks)
  • apps/dashboard/app/(app)/logs/components/table/log-details/components/log-footer.tsx (2 hunks)
  • apps/dashboard/app/(app)/logs/components/table/log-details/index.tsx (1 hunks)
  • apps/dashboard/app/(app)/logs/constants.ts (0 hunks)
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/components/table/log-details/components/log-footer.tsx (1 hunks)
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/components/table/log-details/components/request-response-details.tsx (0 hunks)
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/components/table/log-details/index.tsx (1 hunks)
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/components/table/log-details/resizable-panel.tsx (0 hunks)
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/constants.ts (0 hunks)
  • apps/dashboard/components/logs/details/resizable-panel.tsx (1 hunks)
  • apps/dashboard/components/virtual-table/index.tsx (2 hunks)
  • apps/dashboard/lib/trpc/routers/audit/fetch.ts (3 hunks)
  • apps/dashboard/lib/trpc/routers/audit/llm-search/index.ts (1 hunks)
  • apps/dashboard/lib/trpc/routers/audit/llm-search/utils.ts (1 hunks)
  • apps/dashboard/lib/trpc/routers/audit/schema.ts (1 hunks)
  • apps/dashboard/lib/trpc/routers/audit/utils.ts (1 hunks)
  • apps/dashboard/lib/trpc/routers/index.ts (2 hunks)
💤 Files with no reviewable changes (19)
  • apps/dashboard/app/(app)/audit/components/table/utils.ts
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/constants.ts
  • apps/dashboard/app/(app)/audit/components/filters/filter.tsx
  • apps/dashboard/app/(app)/audit/components/filters/root-key-filter.tsx
  • apps/dashboard/app/(app)/audit/components/filters/bucket-select.tsx
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/components/table/log-details/resizable-panel.tsx
  • apps/dashboard/app/(app)/audit/components/filters/user-filter.tsx
  • apps/dashboard/app/(app)/audit/audit.type.ts
  • apps/dashboard/app/(app)/audit/components/table/constants.ts
  • apps/dashboard/app/(app)/audit/components/filters/index.tsx
  • apps/dashboard/app/(app)/audit/components/filters/clear-button.tsx
  • apps/dashboard/app/(app)/audit/components/table/columns.tsx
  • apps/dashboard/app/(app)/audit/components/filters/timesplit-input.tsx
  • apps/dashboard/app/(app)/audit/components/table/log-details/resizable-panel.tsx
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/components/table/log-details/components/request-response-details.tsx
  • apps/dashboard/app/(app)/audit/components/filters/datepicker-with-range.tsx
  • apps/dashboard/app/(app)/logs/constants.ts
  • apps/dashboard/app/(app)/audit/query-state.ts
  • apps/dashboard/app/(app)/audit/components/table/audit-logs-table.tsx
✅ Files skipped from review due to trivial changes (2)
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/components/table/log-details/components/log-footer.tsx
  • apps/dashboard/components/logs/details/resizable-panel.tsx
🧰 Additional context used
🧠 Learnings (2)
apps/dashboard/app/(app)/audit/components/controls/components/logs-datetime/index.tsx (1)
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#2825
File: apps/dashboard/app/(app)/logs-v2/components/controls/components/logs-datetime/index.tsx:0-0
Timestamp: 2025-01-30T20:38:00.058Z
Learning: In the logs dashboard, keyboard shortcuts that toggle UI elements (like popovers) should be implemented in the component that owns the state being toggled, not in the presentational wrapper components. For example, the 'T' shortcut for toggling the datetime filter is implemented in DatetimePopover, not in LogsDateTime.
apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/index.tsx (1)
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#2801
File: apps/dashboard/app/(app)/logs-v2/components/controls/components/logs-filters/components/methods-filter.tsx:39-62
Timestamp: 2025-01-10T10:09:42.433Z
Learning: In the logs-v2 filters implementation, handler improvements and type safety enhancements should be deferred until real data integration, as the handlers will need to be modified to work with the actual data source.
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: Test Packages / Test ./internal/clickhouse
  • GitHub Check: Test Go API Local / test_agent_local
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Agent Local / test_agent_local
  • GitHub Check: Build / Build
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (62)
apps/dashboard/components/virtual-table/index.tsx (7)

3-3: Updated imports to support ref forwarding

The import statement has been modified to include React features needed for forwarding refs: Ref, forwardRef, and useImperativeHandle. These are necessary for the component refactoring to expose internal references to parent components.


30-33: Good type definition for exposed refs

The VirtualTableRef type clearly defines what internal references will be exposed to parent components, making the component API more explicit and type-safe.


35-53: Well-implemented forwardRef pattern

The component has been properly converted to use the forwardRef pattern, which allows parent components to access internal references. The function signature maintains the same props interface while adding ref forwarding capabilities.


70-78: Correct implementation of useImperativeHandle

The useImperativeHandle hook is well-implemented to expose the internal refs to parent components. The empty dependency array is appropriate since the refs themselves don't change.


82-118: Preserved empty state rendering logic

The empty state rendering is properly maintained in the refactored component, ensuring consistent behavior when there's no data to display. The component correctly references the new refs.


120-282: Maintained component functionality with ref integration

The main rendering logic has been preserved while integrating the new ref functionality. The virtual table implementation maintains all its previous capabilities including:

  • Virtual row rendering
  • Keyboard navigation
  • Row selection
  • Loading states

The structure adjustments support the ref forwarding without changing the component's core behavior.


283-284: Properly closed the forwardRef component

The component declaration is correctly closed with the appropriate syntax for forwardRef components.

apps/dashboard/app/(app)/logs/components/table/log-details/index.tsx (2)

2-2: Good move centralizing the ResizablePanel component import

Moving the import from a local path to a shared component path (@/components/logs/details/resizable-panel) improves component reusability across the application.


35-40:

Details

✅ Verification successful

Verify ResizablePanel props after moving the component

The ResizablePanel component is now being used without minW/maxW props that were previously set with PANEL_MIN_WIDTH and PANEL_MAX_WIDTH constants. Ensure that the central component still handles width constraints appropriately.


🏁 Script executed:

#!/bin/bash
# Check if the centralized ResizablePanel component has width constraint props
grep -A 10 "export.*ResizablePanel" apps/dashboard/components/logs/details/resizable-panel.tsx || echo "File not found"

Length of output: 333


Width constraints are properly applied
The centralized ResizablePanel now includes minW and maxW props with defaults set to MIN_DRAGGABLE_WIDTH and MAX_DRAGGABLE_WIDTH, ensuring that width constraints are still enforced. No further modifications are required in the logs component.

apps/dashboard/app/(app)/audit/components/controls/index.tsx (1)

1-29: Well-structured control component with good separation of concerns

This new AuditLogsControls component nicely organizes all the audit log filtering controls in a clean, structured layout. The component follows good practices:

  • Proper type usage with WorkspaceProps
  • Logical grouping of related controls
  • Clean flexbox layout with appropriate spacing
  • Props correctly passed to the LogsFilters component

The structure makes the UI more maintainable and provides a consistent user experience.

apps/dashboard/app/(app)/audit/components/controls/components/logs-refresh.tsx (1)

5-14: Good implementation with conditional enablement

The LogsRefresh component is well-implemented with:

  • Appropriate use of the useFilters hook to access filter state
  • Smart conditional enabling of the refresh button based on filter presence
  • Clean integration with TRPC for cache invalidation

The decision to only enable refresh when a relative "since" filter exists is a good UX choice that prevents unnecessary refreshes.

apps/dashboard/app/(app)/audit/components/table/log-details/components/log-header.tsx (2)

2-2: Type update ensures consistency across components

Updating the log prop type from AuditData to AuditLog standardizes the log data structure across components, improving type safety and reducing potential errors.

Also applies to: 7-7


15-15: Badge styling update for improved visual hierarchy

The Badge component styling has been updated from bg-accent-3/hover:bg-accent-4 to bg-accent-4/hover:bg-accent-5, which slightly darkens the appearance. This likely improves visual contrast for better readability.

apps/dashboard/lib/trpc/routers/index.ts (1)

143-144:

Details

❓ Verification inconclusive

Verify existing references to the renamed audit router method.

The fetch method has been renamed to logs, which is a breaking change for any code that references the old method name. Ensure all consumers of this API are updated accordingly.


🏁 Script executed:

#!/bin/bash
# Search for any references to the old method name that might need updating
echo "Searching for references to audit.fetch that need to be updated to audit.logs..."
rg -A 2 -B 2 "audit\.fetch" --type-not test

Length of output: 245


Ensure API references are updated for the renamed audit router method

The fetch method in the audit router was renamed to logs—a breaking change that may affect any consumers still referencing the old name. Please verify that there are no remaining occurrences of audit.fetch in the codebase (using the revised search script above). If any are found, update them to use audit.logs.


🏁 Script executed:

[run_scripts]
#!/bin/bash
echo "Searching for references to audit.fetch that need to be updated to audit.logs..."
rg -A 2 -B 2 "audit\.fetch"

Length of output: 322


Breaking API Change – Verify Consumer Updates for Renamed Audit Method

The fetch method in the audit router has been renamed to logs, and a new llmSearch method has been added. This change is potentially breaking since consumers referencing the old fetch method must now switch to logs. An automated search for occurrences of audit.fetch using rg did not reveal any matches. However, please verify manually that no consumers still reference the deprecated method.

  • File: apps/dashboard/lib/trpc/routers/index.ts (Lines 143-144)
    • Old API: fetch: fetchAuditLog
    • New API:
      logs: fetchAuditLog,
      llmSearch: auditLogsSearch,

Please double-check your local environment for any lingering references to audit.fetch and update them to audit.logs to ensure full compatibility.

apps/dashboard/app/(app)/audit/components/control-cloud/index.tsx (1)

5-24: Well-structured field name formatter.

The formatFieldName function is well implemented with proper handling for specific fields and a sensible default case that capitalizes the first letter. This improves the user experience by showing user-friendly labels.

apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/components/events-filter.tsx (1)

13-21: Review field name consistency in filter configuration.

There appears to be a mismatch between the data being filtered and the filter field name. The component maps over event values from unkeyAuditLogEvents.Values, but sets filterField and checkPath to "status". Consider using "events" instead to maintain consistency with the data structure.

      options={Object.values(unkeyAuditLogEvents.Values).map<EventsOption>((value, index) => ({
        id: index,
        display: value,
        label: value,
        checked: false,
      }))}
-      filterField="status"
-      checkPath="status"
+      filterField="events"
+      checkPath="events"
apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/components/users-filter.tsx (2)

16-20: Good null handling for users prop.

The component properly handles the case where the users prop is null by defaulting to an empty array. This prevents runtime errors when mapping over potentially undefined values.


21-22: Review field name consistency in filter configuration.

There appears to be a mismatch between the data being filtered and the filter field name. The component filters user data but sets filterField and checkPath to "status". Consider using "users" instead to maintain consistency with the data structure.

-      filterField="status"
-      checkPath="status"
+      filterField="users"
+      checkPath="users"
apps/dashboard/app/(app)/audit/components/table/log-details/components/log-footer.tsx (2)

3-6: Type import refactor looks good.

The change from AuditData to AuditLog aligns with the broader refactoring of audit logs across the system. The import paths have been updated appropriately.


9-11: Type definition update is consistent.

The Props type has been properly updated to use the new AuditLog type, ensuring consistency with the rest of the audit components.

apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/components/root-keys-filter.tsx (2)

1-2: Import looks good.

The import of the FilterCheckbox component is correctly defined.


3-10: Props and types are well-defined.

The component has clear and descriptive prop types, which will help with type safety during usage.

apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/components/table/log-details/index.tsx (2)

4-4: ResizablePanel import path updated correctly.

The import path for ResizablePanel has been updated to use a centralized component path, which improves component reusability.


37-41:

Details

❓ Verification inconclusive

ResizablePanel usage simplified.

The removal of minW and maxW props from the ResizablePanel indicates a move toward a more standardized implementation. This change should be verified to ensure the panel still resizes appropriately.


🏁 Script executed:

#!/bin/bash
# Description: Check if ResizablePanel interface has changed to remove minW and maxW props

# Look for ResizablePanel props type definition
ast-grep --pattern 'type $ResizablePanelProps = {
  $$$
}'

# Look for ResizablePanel component implementation
ast-grep --pattern 'export const ResizablePanel = ({ $$$, minW, $$$, maxW, $$ }: $ResizablePanelProps) => {
  $$$
}'

# Look for other usages of ResizablePanel to confirm consistency
rg "ResizablePanel" --type tsx -A 2 -B 2 | grep -E "minW|maxW"

Length of output: 270


Below is an updated shell script that narrows the search to the specific file and uses explicit globbing (via direct file paths) to check if there are any occurrences of "minW" or "maxW" in the file. This will help verify that the new, simplified usage of ResizablePanel no longer includes these props.

Please run the following script:

#!/bin/bash
TARGET_FILE="apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/components/table/log-details/index.tsx"

echo "Checking for 'minW' occurrences in $TARGET_FILE:"
rg -o "minW" "$TARGET_FILE"

echo "Checking for 'maxW' occurrences in $TARGET_FILE:"
rg -o "maxW" "$TARGET_FILE"

echo "Showing context around ResizablePanel usage in $TARGET_FILE:"
rg "ResizablePanel" "$TARGET_FILE" -A 5 -B 5

If this script confirms that neither "minW" nor "maxW" is present and the context around ResizablePanel is correct, then the removal of those props is validated.

apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/index.tsx (3)

12-66: LogsFilters component structure looks good.

The component is well-structured and follows a consistent pattern with other filter components in the system. The FiltersPopover implementation with proper keyboard shortcuts enhances accessibility.

Based on the retrieved learning from PR #2801, it's appropriate to defer handler improvements and type safety enhancements until real data integration is complete.


47-50: Active filter state UI enhancement is well implemented.

The conditional styling based on filter state provides clear visual feedback to users when filters are active.


57-61: Filter count badge is a good UX feature.

Displaying the count of active filters provides users with immediate feedback about their current filtering state.

apps/dashboard/app/(app)/logs/components/table/log-details/components/log-footer.tsx (2)

4-4: LGTM! Import path updated to absolute path.

The import for RequestResponseDetails was updated to use an absolute path, which is consistent with the project's architecture.


52-52: Text alignment improved for user agent display.

Adding text-right class to the user agent display improves the UI layout when displaying long user agent strings.

apps/dashboard/app/(app)/audit/components/table/log-details/index.tsx (4)

3-4: Import path updates for better organization

The import paths have been updated to reflect a more structured component organization. The ResizablePanel is now imported from a dedicated logs components directory, and the AuditLog type is imported instead of the previous AuditData type.


6-6: Improved constants management

The component now imports the DEFAULT_DRAGGABLE_WIDTH constant from a centralized location instead of using local constants like the removed PANEL_MAX_WIDTH and PANEL_MIN_WIDTH.


20-21: Type update for better type safety

The component now properly uses the AuditLog type for the selected log prop, providing better type safety and consistency with the rest of the application.


36-40:

Details

✅ Verification successful

Verify the panel's resizing behavior

The ResizablePanel component no longer receives the minW and maxW props that were previously set using the removed constants. Ensure this change doesn't affect the panel's resizing behavior.


🏁 Script executed:

#!/bin/bash
# Check if the ResizablePanel component in the new location has default min and max width values
grep -A 15 "export const ResizablePanel" apps/dashboard/components/logs/details/resizable-panel.tsx

Length of output: 436


Verify Panel’s Resizing Behavior with Internal Defaults

The ResizablePanel component now defines default values for minW and maxW (using MIN_DRAGGABLE_WIDTH and MAX_DRAGGABLE_WIDTH respectively) within its implementation in apps/dashboard/components/logs/details/resizable-panel.tsx. This means that even though the parent component in apps/dashboard/app/(app)/audit/components/table/log-details/index.tsx no longer passes these props explicitly, the panel’s resizing behavior should remain consistent.

  • Confirm that the default width constraints meet the design requirements.
  • If any unexpected resizing behavior is observed, consider revisiting the default constants in the ResizablePanel component.
apps/dashboard/lib/trpc/routers/audit/utils.ts (2)

5-45: Well-structured filter transformation function

The new transformFilters function cleanly separates the concern of transforming client-side filter formats to server-side formats. It properly handles:

  • Time range calculations using relative time expressions
  • Mapping of filter arrays while preserving operator and value pairs
  • Setting appropriate default values for missing filters
  • Structuring cursor information for pagination

This is a good example of the Single Responsibility Principle in action.


11-14:

Details

❓ Verification inconclusive

Verify time conversion accuracy

The getTimestampFromRelative function is used to convert relative time expressions to timestamps. Ensure this function correctly handles all expected relative time formats used in the application.


🏁 Script executed:

#!/bin/bash
# Check the implementation of getTimestampFromRelative
grep -A 20 "export function getTimestampFromRelative" apps/dashboard/lib/utils.ts

Length of output: 83


I'll need to gather more details on the implementation of getTimestampFromRelative, as the initial grep command produced no output. It seems that either the function isn’t defined in the expected file (apps/dashboard/lib/utils.ts) or it might exist elsewhere. Please manually verify the following:

  • Location Check: Confirm that getTimestampFromRelative is defined somewhere in the codebase (e.g., using a repository-wide search like rg "getTimestampFromRelative" -n .).
  • Implementation Verification: Ensure that the implementation correctly parses all the relative time formats expected in our application.

Once verified, update or document the function as needed to avoid any potential time conversion issues.

Next Steps for the Developer:

  • Search for the function: Run a grep (or similar) command to locate its definition.
  • Confirm correctness: Double-check that the logic properly handles all anticipated relative time inputs.
apps/dashboard/app/(app)/audit/page.tsx (3)

5-6: Simplified import structure

The imports have been simplified by removing unused components and directly importing from the actions file.


11-13: Enhanced data retrieval

The component now destructures both workspace and members from the getWorkspace response, enabling access to organization members data which is likely needed for the logs client component.


22-37: Simplified component structure

The page structure has been simplified by:

  1. Removing the PageContent and Filters components
  2. Directly using the LogsClient component with necessary props
  3. Maintaining the empty state rendering

This refactoring improves code readability and centralizes more logic in the LogsClient component.

apps/dashboard/app/(app)/audit/components/table/query-logs.schema.ts (3)

3-4: Defined constant for default bucket

Good practice to define the default bucket name as a constant, making it easier to update if needed and providing clarity about its purpose.


4-47: Well-structured schema definition

The Zod schema for audit query logs is comprehensive and well-structured:

  • It defines required fields with appropriate types (limit, startTime, endTime, since)
  • Sets default values where appropriate (bucket)
  • Properly structures filter objects with operators and values
  • Handles nullable fields appropriately
  • Includes cursor information for pagination

This schema will provide both runtime validation and clear type information.


49-49: Type export for TypeScript integration

Exporting the TypeScript type inferred from the Zod schema ensures type consistency across the application.

apps/dashboard/app/(app)/audit/components/controls/components/logs-datetime/index.tsx (1)

79-79:

Details

❓ Verification inconclusive

Implement keyboard shortcut as mentioned in the title attribute

The button has a title attribute indicating "Press 'T' to toggle filters", but there's no implementation of this shortcut in the component. Based on past learnings, keyboard shortcuts should be implemented in the component that owns the state.

Could you confirm if the 'T' shortcut is implemented in the DatetimePopover component?


🏁 Script executed:

#!/bin/bash
# Check if the DatetimePopover component implements the 'T' keyboard shortcut
rg -A 10 -B 10 "function DatetimePopover" --include="*.tsx"

Length of output: 503


Action Required: Verify 'T' Shortcut Implementation in DatetimePopover

  • The title "Press 'T' to toggle filters" in this logs-datetime component suggests that the 'T' key should toggle filters.
  • However, no keyboard event handling is implemented here.
  • Based on our established guidelines, keyboard shortcuts must be managed in the stateful component (e.g., DatetimePopover), not in the presentational wrapper.
  • Please confirm if the 'T' shortcut is implemented in the DatetimePopover component.
    • If it is, consider removing or revising the title here.
    • If it isn’t, please implement the shortcut within DatetimePopover.
apps/dashboard/app/(app)/audit/components/table/hooks/use-logs-query.ts (1)

109-115:

Details

❓ Verification inconclusive

Consider periodic refetching for better data freshness

The query configuration disables refetching on mount and window focus, which could affect data freshness. Consider enabling periodic refetching or providing a manual refresh option to ensure users see up-to-date data.

Is there a manual refresh option available elsewhere in the UI? If not, consider adding refetch capability or enabling periodic refetching.


🏁 Script executed:

#!/bin/bash
# Check if there's a refresh component or button in the audit logs UI
rg -A 5 -B 5 "LogsRefresh" --include="*.tsx"

Length of output: 486


Audit Logs Refetch and Refresh Strategy

The current query configuration disables both mount and window-focus refetching, which may lead to stale audit log data. Our initial check for a manual refresh component (specifically a LogsRefresh element) encountered issues and did not provide conclusive results. Please verify directly whether a manual refresh button exists elsewhere in the UI. If not, consider one of the following improvements:

  • Add a manual refresh control: Implement a dedicated refresh component to allow users to actively update the audit logs.
  • Enable periodic refresh: Configure the query (or use an alternative strategy) to periodically refetch data for improved data freshness.
apps/dashboard/app/(app)/audit/actions.ts (2)

2-2: Import statement looks good.

Bringing in clerkClient from @clerk/nextjs appears correct for organization membership retrieval.


43-48: Exposing keys from the workspace query is properly structured.

Selecting ID and name for the workspace keys is concise and consistent with the code’s style.

apps/dashboard/app/(app)/audit/components/logs-client.tsx (3)

3-4: Imports are well organized and minimal.

The addition of the AuditLog import and related state hooks is clear and logical.


27-33: Cleanly encapsulated callback for layout adjustments.

Using useCallback to update tableDistanceToTop is both performant and readable.


45-45: Good alignment of the distanceToTop prop with updated state.

Ensuring the AuditLogDetails component references the same state variable for layout painting is a neat approach.

apps/dashboard/lib/trpc/routers/audit/llm-search/utils.ts (5)

1-9: Importing schemas and types is correct.

All dependencies look properly declared, supporting the LLM-based structured audit search logic.


10-18: Null return for missing openai keys might cause confusion.

You return null if openai is not configured. Ensure callers gracefully handle this scenario, possibly gating LLM-based features or displaying an appropriate message to the user.


20-39: Using beta.chat.completions.parse is fine, but verify model availability.

model: "gpt-4o-mini" is presumably a specialized or hosted model. Double-check that the environment supports this model.


41-53: Graceful handling of unparsed responses.

Throwing a TRPCError when no parsed result ensures consistent fallback messaging for the user. The instructions for phrasing queries are helpful.


67-79: Robust error handling covers rate limits and unknown errors.

Your approach ensures relevant, user-friendly TRPC errors are thrown, with fallback to INTERNAL_SERVER_ERROR if no subtype matches.

apps/dashboard/lib/trpc/routers/audit/schema.ts (3)

13-15: Clarify whether time property is seconds or milliseconds
The time property is a number, but it's unclear if it represents a Unix timestamp in milliseconds or seconds. Adding explicit naming or documentation can help avoid confusion and future bugs.

Also applies to: 37-37


39-67: Structured approach for joined data
Defining a separate AuditLogWithTargets type is a clean solution for managing expanded log data. This approach looks maintainable.


69-101: Verify handling of nullable cursor fields
Fields like cursorTime and cursorAuditId are nullable. Ensure the query and pagination logic handle these values gracefully if they’re omitted or null.

apps/dashboard/app/(app)/audit/components/table/utils/get-row-class.ts (2)

29-74: Well-structured style objects
Defining color-coded style objects for each event type is organized and enhances readability.


79-82: Handle undefined or empty events
getAuditStatusStyle defaults to "other" if no pattern is found. Ensure auditLog.event is never an empty or undefined string, or handle that case explicitly.

apps/dashboard/app/(app)/audit/filters.schema.ts (2)

51-55: Structured output schema usage
Using createFilterOutputSchema is a succinct approach to maintain type safety and clarity in generating filter outputs. No issues found.


77-85: Confirm optional fields coverage
QuerySearchParams includes several nullable fields. Verify code that consumes them accounts for possible null or undefined values.

apps/dashboard/lib/trpc/routers/audit/fetch.ts (2)

10-19: Clear and concise response schema
Defining the AuditLogsResponse Zod schema ensures consumers receive a well-structured response. This is good for maintainability.


23-85: Consider edge cases for empty results
If queryAuditLogs returns an empty set, ensure your UI and downstream usage handle it properly. The error handling looks good, but verify partial data scenarios.

@chronark chronark changed the title Audit filter refactor refactor: Audit filter Feb 25, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
apps/dashboard/app/(app)/audit/actions.ts (1)

50-57: ⚠️ Potential issue

Avoid loading ALL keys of a workspace unnecessarily.

This query fetches all keys for the workspace without any filtering or limit, which could be an expensive operation if there are many keys.

Consider one of these approaches:

  1. Add pagination or limit if only a subset of keys is needed
  2. Remove this query entirely if the keys aren't being used downstream
  3. Add filtering if only specific keys are needed
- const rootKeys = await db.query.keys.findMany({
-   where: (table, { eq }) => eq(table.forWorkspaceId, workspace?.id),
-
-   columns: {
-     id: true,
-     name: true,
-   },
- });
+ // Only fetch keys if needed and consider adding limits
+ const rootKeys = [];
+ // If keys are needed, consider:
+ // const rootKeys = await db.query.keys.findMany({
+ //   where: (table, { eq }) => eq(table.forWorkspaceId, workspace?.id),
+ //   columns: {
+ //     id: true,
+ //     name: true,
+ //   },
+ //   limit: 100, // Add appropriate limit
+ // });
🧹 Nitpick comments (7)
apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/components/events-filter.tsx (3)

16-21: Consider using the event value as ID instead of array index.

Using array indices for IDs could cause issues if the order of events changes in future updates. Using the event values themselves as unique identifiers would be more robust.

-      options={Object.values(unkeyAuditLogEvents.Values).map<EventsOption>((value, index) => ({
-        id: index,
+      options={Object.values(unkeyAuditLogEvents.Values).map<EventsOption>((value) => ({
+        id: value,
         display: value,
         label: value,
         checked: false,
      }))}

5-10: Consider adding alphabetical sorting for better usability.

The events are displayed in the order they appear in the schema, which might make it difficult for users to find specific events in a long list. Consider sorting them alphabetically.

      options={Object.values(unkeyAuditLogEvents.Values)
+        .sort((a, b) => a.localeCompare(b))
         .map<EventsOption>((value) => ({

Also applies to: 16-21


24-26: Enhance accessibility for the option content.

The rendered option content doesn't include any accessibility attributes. Consider adding appropriate ARIA attributes to improve accessibility.

      renderOptionContent={(checkbox) => (
-        <div className="text-accent-12 text-xs">{checkbox.display}</div>
+        <div 
+          className="text-accent-12 text-xs" 
+          aria-label={`Event type: ${checkbox.display}`}
+        >
+          {checkbox.display}
+        </div>
      )}
apps/dashboard/app/(app)/logs/components/controls/components/logs-filters/components/methods-filter.tsx (1)

19-19: Consider deferring any major type-safety changes.
Per the retrieved learnings, further handler improvements may be needed once real data is integrated, so keep this flexible.

apps/dashboard/components/logs/checkbox/filter-checkbox.tsx (3)

21-47: Helpful expansion of props for flexible filtering.
Defining single vs. multiple selection modes, optional default selection, and external filters/updateFilters creates a comprehensive interface.

Consider adding a quick doc comment explaining each prop’s intended usage for better discoverability.


82-186: Robust handling of single vs. multiple selection.
The combination of handleSingleSelection, default selection logic, and handleApplyFilter covers most use cases. A minor safeguard to check if defaultSelectionIndex exceeds options.length could prevent out-of-bounds access.

+ if (defaultSelectionIndex !== undefined && defaultSelectionIndex < checkboxes.length) {
    handleSingleSelection(defaultSelectionIndex);
+ }

227-228: Improve keyboard interaction for checkboxes.
As with the select-all label, using native <input type="checkbox" /> references would automatically handle keyboard interactions, simplifying your event logic.

Also applies to: 233-233

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8ff35fc and bc1072b.

📒 Files selected for processing (11)
  • apps/dashboard/app/(app)/audit/actions.ts (2 hunks)
  • apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/components/bucket-filter.tsx (1 hunks)
  • apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/components/events-filter.tsx (1 hunks)
  • apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/components/root-keys-filter.tsx (1 hunks)
  • apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/components/users-filter.tsx (1 hunks)
  • apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/index.tsx (1 hunks)
  • apps/dashboard/app/(app)/logs/components/controls/components/logs-filters/components/methods-filter.tsx (3 hunks)
  • apps/dashboard/app/(app)/logs/components/controls/components/logs-filters/components/status-filter.tsx (3 hunks)
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/components/controls/components/logs-filters/components/status-filter.tsx (3 hunks)
  • apps/dashboard/components/logs/checkbox/filter-checkbox.tsx (4 hunks)
  • apps/dashboard/components/logs/refresh-button/index.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/components/bucket-filter.tsx
  • apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/index.tsx
  • apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/components/root-keys-filter.tsx
  • apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/components/users-filter.tsx
🧰 Additional context used
🧠 Learnings (1)
apps/dashboard/app/(app)/logs/components/controls/components/logs-filters/components/methods-filter.tsx (1)
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#2801
File: apps/dashboard/app/(app)/logs-v2/components/controls/components/logs-filters/components/methods-filter.tsx:39-62
Timestamp: 2025-01-10T10:09:42.433Z
Learning: In the logs-v2 filters implementation, handler improvements and type safety enhancements should be deferred until real data integration, as the handlers will need to be modified to work with the actual data source.
🪛 Biome (1.9.4)
apps/dashboard/components/logs/checkbox/filter-checkbox.tsx

[error] 200-216: A form label must be associated with an input.

Consider adding a for or htmlFor attribute to the label element or moving the input element to inside the label element.

(lint/a11y/noLabelWithoutControl)


[error] 201-203: The elements with the following roles can be changed to the following elements:

For examples and more information, see WAI-ARIA Roles

(lint/a11y/useSemanticElements)


[error] 223-225: The elements with the following roles can be changed to the following elements:

For examples and more information, see WAI-ARIA Roles

(lint/a11y/useSemanticElements)

⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: Test Packages / Test ./internal/clickhouse
  • GitHub Check: Test Packages / Test ./internal/resend
  • GitHub Check: Test Go API Local / test_agent_local
  • GitHub Check: Test Agent Local / test_agent_local
  • GitHub Check: Build / Build
  • GitHub Check: Test API / API Test Local
  • GitHub Check: autofix
  • GitHub Check: Docs
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (15)
apps/dashboard/components/logs/refresh-button/index.tsx (1)

64-64: Improved keyboard shortcut representation.

Changing from the symbol representation "⌃" to the text-based "CTRL" makes the keyboard shortcut more accessible and platform-agnostic. This update aligns with similar changes in other components, ensuring consistent keyboard shortcut display across the application.

apps/dashboard/app/(app)/audit/actions.ts (3)

2-2: Added new Clerk integration.

Good addition of the Clerk client import to support fetching organization members.


7-30: Well-structured organization members retrieval.

The implementation correctly:

  • Only fetches members for organizational tenants (not user tenants)
  • Filters out members without public user data
  • Creates a structured format with appropriate labels
  • Implements proper error handling

The error message is also descriptive and includes the tenant ID for easier debugging.


59-59: Enhanced return structure to support new filtering capabilities.

The updated return structure now includes both workspace data (with keys) and organization members, which enables the audit log controls to filter by user.

apps/dashboard/app/(app)/audit/components/controls/components/logs-filters/components/events-filter.tsx (2)

5-10: Good type definition for EventsOption.

The type is well-structured and clearly defines the expected shape of each filter option with appropriate properties.


1-34: Well-structured component with good separation of concerns.

The component follows good practices by leveraging the useFilters hook for state management and the FilterCheckbox component for rendering. This maintains a clean separation of concerns and makes the code more maintainable.

apps/dashboard/app/(app)/logs/components/controls/components/logs-filters/components/methods-filter.tsx (2)

1-1: Looks good.
Importing useFilters here ensures we can synchronize filter states seamlessly.


31-32: Great integration with the FilterCheckbox component.
Passing filters and updateFilters aligns with the standard filter pattern used throughout the codebase.

apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/components/controls/components/logs-filters/components/status-filter.tsx (3)

2-2: Neat import.
Bringing in useFilters from a central location promotes consistent filter management across the app.


30-30: Well-structured usage of useFilters.
Destructuring the filter state here keeps the component concise and maintainable.


45-46: Consistent props for FilterCheckbox.
Using filters and updateFilters ensures compatibility with other filter components.

apps/dashboard/app/(app)/logs/components/controls/components/logs-filters/components/status-filter.tsx (3)

1-1: Nice addition of the useFilters hook.
This approach standardizes the filtering logic across multiple components.


42-42: Efficient access to filter state.
Extracting filters and updateFilters lets you inject them directly into the UI without extra boilerplate.


66-67: Seamless integration with FilterCheckbox.
Passing along filters and updateFilters ensures that changes remain in sync with other filters.

apps/dashboard/components/logs/checkbox/filter-checkbox.tsx (1)

70-80: Smooth integration with useCheckboxState.
By embedding filter synchronization logic in the hook call, you keep this component’s logic simpler and more testable.

chronark
chronark previously approved these changes Feb 26, 2025
@chronark chronark self-requested a review February 26, 2025 08:06
@chronark chronark dismissed their stale review February 26, 2025 08:06

too early

@chronark
Copy link
Collaborator

CleanShot 2025-02-26 at 09 06 13@2x
why is the entire row green now?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (6)
apps/dashboard/app/(app)/audit/components/table/logs-table.tsx (4)

28-31: Consider safeguarding against potential layout shifts

The measurement of table position happens only once on mount. This could lead to inaccurate positioning if the layout shifts after initial render.

Consider adding a resize observer or re-measuring on window resize events:

useEffect(() => {
  const distanceToTop = tableRef.current?.containerRef?.getBoundingClientRect().top ?? 0;
  onMount(distanceToTop);
+  
+  const handleResize = () => {
+    const newDistanceToTop = tableRef.current?.containerRef?.getBoundingClientRect().top ?? 0;
+    onMount(newDistanceToTop);
+  };
+  
+  window.addEventListener('resize', handleResize);
+  return () => window.removeEventListener('resize', handleResize);
}, [onMount]);

79-97: Consider simplifying the conditional actor rendering logic

The current implementation uses nested conditional rendering with repeated structure, which could be simplified.

Consider using a more declarative approach:

render: (log) => {
  const actorType = log.auditLog.actor.type;
  let icon = <FunctionSquare className="w-4 h-4" />;
  let content = log.auditLog.actor.id;
  
  if (actorType === "user" && log.user) {
    icon = null;
    content = `${log.user.firstName ?? ""} ${log.user.lastName ?? ""}`;
  } else if (actorType === "key") {
    icon = <KeySquare className="w-4 h-4" />;
  }
  
  return (
    <div className="flex items-center gap-3 truncate">
      <div className="flex items-center w-full gap-2 max-sm:m-0 max-sm:gap-1 max-sm:text-xs">
        {icon}
        <span className={cn("text-xs", actorType !== "user" && "font-mono truncate")}>
          {content}
        </span>
      </div>
    </div>
  );
},

128-131: Consider handling long event names more gracefully

The current truncation of event names might cut off important information.

Consider adding a tooltip or hover state to show the full event name:

render: (log) => (
  <div className="flex items-center gap-2 font-mono text-xs truncate">
-   <span>{log.auditLog.event}</span>
+   <span title={log.auditLog.event}>{log.auditLog.event}</span>
  </div>
),

137-139: Same truncation issue applies to the description field

The description field is fixed at 200px width with truncation, which might hide important information.

Add a tooltip to show the full description:

render: (log) => (
-  <div className="font-mono text-xs truncate w-[200px]">{log.auditLog.description}</div>
+  <div className="font-mono text-xs truncate w-[200px]" title={log.auditLog.description}>
+    {log.auditLog.description}
+  </div>
),
apps/dashboard/app/(app)/audit/components/table/utils/get-row-class.ts (2)

7-27: Consider making event type classification more robust

The current string-based classification might be fragile if event naming conventions change. A more structured approach would be more maintainable.

Consider using a pattern matching approach or predefined event types:

+ type EventCategory = "create" | "update" | "delete" | "other";
+ 
+ // Map of keywords to event categories for more maintainable classification
+ const EVENT_CATEGORY_KEYWORDS: Record<EventCategory, string[]> = {
+   create: ["create", "add", "new"],
+   update: ["update", "edit", "modify", "change"],
+   delete: ["delete", "remove", "destroy"],
+   other: []
+ };

export const getEventType = (event: string): "create" | "update" | "delete" | "other" => {
  const eventLower = event.toLowerCase();
  
+  // Find the first category where any of its keywords match the event name
+  const category = Object.entries(EVENT_CATEGORY_KEYWORDS).find(
+    ([_, keywords]) => keywords.some(keyword => eventLower.includes(keyword))
+  )?.[0] as EventCategory;
+  
+  return category || "other";

-  if (eventLower.includes("create") || eventLower.includes("add")) {
-    return "create";
-  }
-
-  if (
-    eventLower.includes("update") ||
-    eventLower.includes("edit") ||
-    eventLower.includes("modify")
-  ) {
-    return "update";
-  }
-
-  if (eventLower.includes("delete") || eventLower.includes("remove")) {
-    return "delete";
-  }
-
-  return "other";
};

108-115: Consider handling undefined or null event types

The function doesn't handle cases where the event might be undefined or null.

Add a safety check:

export const getAuditSelectedClassName = (item: AuditLog, isSelected: boolean) => {
  if (!isSelected) {
    return "";
  }

-  const style = AUDIT_STATUS_STYLES[getEventType(item.auditLog.event)];
+  const eventType = item.auditLog.event ? getEventType(item.auditLog.event) : "other";
+  const style = AUDIT_STATUS_STYLES[eventType];
  return style.selected;
};
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc1072b and 4c34411.

📒 Files selected for processing (4)
  • apps/dashboard/app/(app)/audit/components/table/log-details/components/log-header.tsx (1 hunks)
  • apps/dashboard/app/(app)/audit/components/table/logs-table.tsx (1 hunks)
  • apps/dashboard/app/(app)/audit/components/table/utils.ts (0 hunks)
  • apps/dashboard/app/(app)/audit/components/table/utils/get-row-class.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/dashboard/app/(app)/audit/components/table/utils.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/dashboard/app/(app)/audit/components/table/log-details/components/log-header.tsx
🧰 Additional context used
🧠 Learnings (1)
apps/dashboard/app/(app)/audit/components/table/logs-table.tsx (1)
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#2143
File: apps/dashboard/app/(app)/logs/logs-page.tsx:77-83
Timestamp: 2024-12-03T14:17:08.016Z
Learning: The `<LogsTable />` component already implements virtualization to handle large datasets efficiently.
⏰ Context from checks skipped due to timeout of 90000ms (18)
  • GitHub Check: Test Packages / Test ./packages/rbac
  • GitHub Check: Test Packages / Test ./packages/nextjs
  • GitHub Check: Test Packages / Test ./packages/hono
  • GitHub Check: Test Packages / Test ./packages/cache
  • GitHub Check: Test Packages / Test ./packages/api
  • GitHub Check: Test Packages / Test ./internal/clickhouse
  • GitHub Check: Test Packages / Test ./internal/resend
  • GitHub Check: Test Packages / Test ./internal/keys
  • GitHub Check: Test Packages / Test ./internal/id
  • GitHub Check: Test Packages / Test ./internal/hash
  • GitHub Check: Test Packages / Test ./internal/encryption
  • GitHub Check: Test Packages / Test ./internal/billing
  • GitHub Check: Test Go API Local / test_agent_local
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Agent Local / test_agent_local
  • GitHub Check: Build / Build
  • GitHub Check: autofix
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (3)
apps/dashboard/app/(app)/audit/components/table/logs-table.tsx (1)

1-57: Virtual table implementation looks solid with good component structure

The AuditLogsTable component is well-structured with clean separation of concerns, proper TypeScript typing, and effective use of the virtual table pattern for performance. The distance calculation in the useEffect hook ensures proper positioning, and the props interface provides clear expectations for parent components.

apps/dashboard/app/(app)/audit/components/table/utils/get-row-class.ts (2)

29-74: This explains the green row UI comment in the PR

The styling configuration explains why rows might appear green - "create" events use a green color scheme. This addresses the PR comment about "why is the entire row green now?"

The color system is comprehensive and follows a consistent pattern across different event types. The use of semantic color variables (success, warning, etc.) is a good practice.


87-103:

Details

❓ Verification inconclusive

Ensure accessibility with proper color contrast for selected states

The styling looks visually appealing but should be verified for proper accessibility contrast ratios, especially for selected states.


🌐 Web query:

What are the WCAG contrast guidelines for interactive UI elements?

💡 Result:

WCAG guidelines require interactive UI elements like buttons, form controls, and indicators to maintain a minimum 3:1 contrast ratio against adjacent colors under Success Criterion 1.4.11 (Level AA) [1][3][7]. Here's a detailed breakdown:

Core Requirements for UI Components

  • Visual boundaries: Borders/edges of input fields, buttons, or tabs must have ≥3:1 contrast against backgrounds ([Example of insufficient 1.5:1 borders in forms][1]).
  • State indicators: Hover, focus, and active states (e.g., button pressed) require ≥3:1 contrast [3][7].
  • Graphical controls: Icons in toolbars, toggle switches, and checkboxes need ≥3:1 against their backgrounds ([Example of failing 2.7:1 checkbox borders][9]).

Text Within UI Components

While UI elements themselves follow the 3:1 rule:

  • Button labels/input text must meet standard text contrast:
    • 4.5:1 for normal text (≤18pt regular/≤14pt bold)
    • 3:1 for large text (>18pt regular/>14pt bold) [3][4][8].

Exceptions

  • Disabled components: Inactive elements (e.g., grayed-out buttons) are exempt but should still be detectable if their presence conveys information [1][9].
  • Decorative elements: Non-informative graphics/logos don’t require compliance [7][8].

Enhanced Guidelines (Level AAA)

For stricter compliance:

  • Text contrast rises to 7:1 (normal) or 4.5:1 (large), but UI components remain at ≥3:1 [2][4][8].

Common Failures and Fixes

Issue Example Solution
Low-contrast borders Input field borders at 1.5:1 ([Fig. 2][1]) Use darker borders (e.g., #767676 on white = 4.6:1)
Subtle focus rings Gray (#AAA) focus indicator at 2.3:1 ([Fig. 10][9]) Add high-contrast outlines (e.g., black on white)
Icon-only buttons Dropdown arrows at <3:1 ([Fig. 10][9]) Use solid shapes instead of outlines

Designers should validate contrasts using tools like WebAIM’s Contrast Checker or Stark plugin [6][8].

Citations:


Ensure proper WCAG compliance for selected state styling

The function currently conditionally applies styling via style.selected and adjusts opacity based on the state. Please verify that these styles (including any focus ring or hover effects) meet the WCAG minimum contrast ratio of 3:1 for interactive UI elements (per WCAG 1.4.11 Level AA). We recommend using a tool like WebAIM’s Contrast Checker to confirm that both the selected and unselected states, as well as any focus indicators, provide sufficient contrast.

@chronark chronark enabled auto-merge February 26, 2025 14:54
@chronark chronark added this pull request to the merge queue Feb 26, 2025
Merged via the queue into main with commit 19e2203 Feb 26, 2025
28 of 29 checks passed
@chronark chronark deleted the audit-filter-refactor branch February 26, 2025 15:13
@coderabbitai coderabbitai bot mentioned this pull request Jul 4, 2025
18 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants