Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: improved audit table #2757

Merged
merged 28 commits into from
Dec 20, 2024
Merged

refactor: improved audit table #2757

merged 28 commits into from
Dec 20, 2024

Conversation

ogzhanolguncu
Copy link
Contributor

@ogzhanolguncu ogzhanolguncu commented Dec 17, 2024

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

Release Notes

  • New Features

    • Introduced a virtual table component for displaying audit logs with dynamic column rendering.
    • Added a Filters component for enhanced filtering of audit logs, including bucket selection and user filters.
    • Implemented AuditLogTableClient for fetching and displaying logs with pagination support.
    • Added ClearButton for clearing filter selections.
    • Introduced RootKeyFilter and UserFilter components for filtering based on workspace and user data.
  • Improvements

    • Enhanced DatePickerWithRange to accept initial date parameters for filtering.
    • Updated tooltip handling in the RequestResponseDetails component for improved user feedback.
    • Improved layout and styling of various components for better user experience.
    • Streamlined LogHeader and LogFooter components for better organization of log details.
  • Bug Fixes

    • Resolved issues with tooltip rendering and state management in several components.
  • Removals

    • Removed the Row and FilterSingle components to streamline the codebase.

Copy link

vercel bot commented Dec 17, 2024

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

Name Status Preview Comments Updated (UTC)
dashboard ✅ Ready (Inspect) Visit Preview 2 resolved Dec 20, 2024 9:00am
engineering ✅ Ready (Inspect) Visit Preview 💬 Add feedback Dec 20, 2024 9:00am
play ✅ Ready (Inspect) Visit Preview 💬 Add feedback Dec 20, 2024 9:00am
www ✅ Ready (Inspect) Visit Preview 💬 Add feedback Dec 20, 2024 9:00am

Copy link

changeset-bot bot commented Dec 17, 2024

⚠️ No Changeset found

Latest commit: f426f17

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

Copy link
Contributor

coderabbitai bot commented Dec 17, 2024

📝 Walkthrough
📝 Walkthrough

Walkthrough

This pull request introduces a comprehensive enhancement to the audit log functionality in the dashboard application. The changes include creating new components for filtering, displaying, and managing audit logs, implementing a virtual table for log rendering, and adding utility functions for event type classification and parameter parsing. The modifications span multiple files across the dashboard, focusing on improving the user interface, data fetching, and interaction with audit log data.

Changes

File Change Summary
apps/dashboard/app/(app)/audit/[bucket]/components/table/columns.tsx Added column definitions for virtual table, defining rendering logic for timestamp, actor, action, and event details
apps/dashboard/app/(app)/audit/[bucket]/components/table/constants.ts Introduced DEFAULT_FETCH_COUNT and eventGroups for managing event types and fetch limits
apps/dashboard/app/(app)/audit/[bucket]/components/table/table-details.tsx Created a component for displaying detailed log information with resizable panel
apps/dashboard/app/(app)/audit/[bucket]/components/table/types.ts Added new type Data to encapsulate user and audit log properties
apps/dashboard/app/(app)/audit/[bucket]/components/table/utils.ts Added utility function getEventType for event classification
apps/dashboard/app/(app)/audit/[bucket]/page.tsx Refactored workspace fetching and search parameters handling
apps/dashboard/app/(app)/audit/[bucket]/query-state.ts Introduced functionality for managing audit log query parameters
apps/dashboard/app/(app)/audit/[bucket]/components/filters/* Introduced new filter components for users, root keys, and clear button
apps/dashboard/lib/trpc/routers/audit/fetch.ts Enhanced audit log fetching with improved input validation, error handling, and pagination
apps/dashboard/lib/utils.ts Added throttle utility function for controlling function execution rate

Suggested labels

Improvement, Needs Approval

Suggested reviewers

  • mcstepp
  • chronark
  • perkinsjr

Possibly related PRs


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 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.

Copy link
Contributor

github-actions bot commented Dec 17, 2024

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: 11

🔭 Outside diff range comments (1)
apps/dashboard/app/(app)/audit/[bucket]/filter.tsx (1)

Line range hint 85-91: Simplify event handling to avoid potential conflicts

The component has overlapping click and keyboard event handlers. Consider consolidating them into a single handler.

- onClick={() => handleSelection(option.value, isSelected)}
- onKeyDown={(e) => {
-   if (e.key === "Enter" || e.key === " ") {
-     e.preventDefault();
-     handleSelection(option.value, isSelected);
-   }
- }}
+ onClick={(e) => {
+   if (e.type === 'click' || (e.type === 'keydown' && (e.key === 'Enter' || e.key === ' '))) {
+     e.preventDefault();
+     handleSelection(option.value, isSelected);
+   }
+ }}
🧹 Nitpick comments (19)
internal/icons/src/icons/clone-x-mark-2.tsx (1)

13-15: Optimize React import for better tree-shaking

The static analyzer correctly identifies that React is only used for types.

-import React from "react";
+import type React from "react";

 import type { IconProps } from "../props";
🧰 Tools
🪛 Biome (1.9.4)

[error] 13-13: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)

internal/icons/src/icons/clone-plus-2.tsx (1)

26-39: Consider simplifying the rectangle transformation

The rectangle uses a transform to rotate 180 degrees, which could be simplified by directly setting the coordinates.

 <rect
   height="11"
   width="11"
   fill="none"
   rx="2"
   ry="2"
   stroke="currentColor"
   strokeLinecap="round"
   strokeLinejoin="round"
   strokeWidth="1.5"
-  transform="translate(21.5 21.5) rotate(180)"
   x="5.25"
   y="5.25"
 />
apps/dashboard/app/(app)/audit/[bucket]/components/table/types.ts (2)

26-26: Consider stronger typing for the meta field.

The unknown type for meta is too permissive and could lead to runtime type errors. Consider defining a more specific type based on the expected structure of the metadata.


1-30: Add JSDoc documentation for the Data type.

This type is used across multiple components. Adding comprehensive documentation would help other developers understand the purpose and structure of each field.

Example documentation:

/**
 * Represents an audit log entry with associated user information.
 * @property user - Optional user information associated with the audit log
 * @property auditLog - Detailed information about the audit event
 * @property auditLog.targets - Array of affected resources/entities
 */
export type Data = {
  // ... rest of the type definition
};
apps/dashboard/app/(app)/audit/[bucket]/components/log-header.tsx (2)

20-22: Improve button accessibility.

The close button lacks proper accessibility attributes.

Add aria-label to make the button's purpose clear to screen readers:

-<Button shape="square" variant="ghost" onClick={onClose}>
+<Button 
+  shape="square" 
+  variant="ghost" 
+  onClick={onClose}
+  aria-label="Close audit log details"
+>
   <X size="22" strokeWidth="1.5" className="text-content/65 cursor-pointer" />
 </Button>

11-26: Consider wrapping component in error boundary.

The component directly accesses nested properties (log.auditLog.event) without any error handling. Consider adding an error boundary to gracefully handle potential runtime errors.

Example implementation:

import { ErrorBoundary } from '@/components/error-boundary';

export const LogHeader = ({ onClose, log }: Props) => {
  return (
    <ErrorBoundary fallback={<div>Error loading audit log header</div>}>
      {/* existing component code */}
    </ErrorBoundary>
  );
};
apps/dashboard/app/(app)/audit/[bucket]/query-state.ts (2)

3-16: Add JSDoc comments to improve type documentation.

The Cursor and AuditLogQueryParams types would benefit from JSDoc comments explaining their purpose and the significance of each field.

Example improvement:

+/**
+ * Represents a pagination cursor for audit log entries
+ * @property time - Timestamp of the current position
+ * @property id - Unique identifier at the current position
+ */
 export type Cursor = {
   time: number;
   id: string;
 };

+/**
+ * Query parameters for filtering and paginating audit logs
+ * @property before - Timestamp to filter entries before
+ * @property events - Array of event types to filter
+ * @property users - Array of user IDs to filter
+ * @property rootKeys - Array of root keys to filter
+ * @property bucket - Bucket identifier
+ * @property cursorTime - Cursor timestamp for pagination
+ * @property cursorId - Cursor ID for pagination
+ */
 export type AuditLogQueryParams = {

28-43: Add error handling for invalid cursor values.

The setCursor function should validate the cursor values before setting them.

Consider this improvement:

 const setCursor = (cursor?: Cursor) => {
+  if (cursor && (isNaN(cursor.time) || !cursor.id)) {
+    console.warn('Invalid cursor values provided:', cursor);
+    return;
+  }
   setSearchParams({
     cursorTime: cursor?.time ?? null,
     cursorId: cursor?.id ?? null,
   });
 };
apps/dashboard/app/(app)/audit/[bucket]/components/table/constants.ts (1)

3-59: Enhance type safety and maintainability of event groups.

While the event grouping is comprehensive, it could benefit from:

  1. Type safety using string literals
  2. Centralized event name constants
  3. Helper functions for type checking

Consider this improvement:

// Define event names as string literals for type safety
export const EventNames = {
  WORKSPACE: {
    CREATE: 'workspace.create',
    DELETE: 'workspace.delete',
    UPDATE: 'workspace.update',
    OPT_IN: 'workspace.opt_in'
  },
  // ... similar for other categories
} as const;

// Type-safe event groups
export const eventGroups = {
  create: [
    EventNames.WORKSPACE.CREATE,
    // ... other create events
  ],
  // ... other groups
} as const;

// Helper function for type checking
export const isValidEventType = (event: string): event is typeof eventGroups[keyof typeof eventGroups][number] => {
  return Object.values(eventGroups).some(group => group.includes(event as any));
};
apps/dashboard/app/(app)/audit/[bucket]/components/table/table-details.tsx (1)

20-26: Consider using ResizeObserver instead of debounce.

The debounced width update might cause jittery resizing. ResizeObserver would provide smoother updates.

-const [panelWidth, setPanelWidth] = useState(DEFAULT_DRAGGABLE_WIDTH);
-const debouncedSetPanelWidth = useDebounceCallback((newWidth) => {
-  setPanelWidth(newWidth);
-}, PANEL_WIDTH_SET_DELAY);
+const panelRef = useRef<HTMLDivElement>(null);
+const [panelWidth, setPanelWidth] = useState(DEFAULT_DRAGGABLE_WIDTH);
+
+useEffect(() => {
+  if (!panelRef.current) return;
+  const observer = new ResizeObserver((entries) => {
+    const width = entries[0]?.contentRect.width;
+    if (width) setPanelWidth(width);
+  });
+  observer.observe(panelRef.current);
+  return () => observer.disconnect();
+}, []);
apps/dashboard/app/(app)/audit/[bucket]/components/log-section.tsx (2)

10-11: Consider adding type validation for the details prop

The details prop accepts either a string or string array, but there's no validation to ensure non-empty values.

Consider adding runtime validation or TypeScript constraints:

-  details: string | string[];
+  details: NonNullable<string | string[]>;

61-72: Optimize getFormattedContent function

The function could be simplified and made more robust.

 const getFormattedContent = (details: string | string[]) => {
   if (Array.isArray(details)) {
-    return details
+    return details.filter(Boolean)
       .map((header) => {
         const [key, ...valueParts] = header.split(":");
         const value = valueParts.join(":").trim();
         return `${key}: ${value}`;
       })
       .join("\n");
   }
-  return details;
+  return details?.trim() || "";
 };
apps/dashboard/app/(app)/audit/[bucket]/components/table/columns.tsx (2)

52-59: Consider extracting badge styling logic

The badge styling logic could be moved to a separate utility function for better maintainability.

+ const getBadgeClassName = (eventType: string) => 
+   cn("font-mono capitalize", {
+     "bg-error-3 text-error-11 hover:bg-error-4": eventType === "delete",
+     "bg-warning-3 text-warning-11 hover:bg-warning-4": eventType === "update",
+     "bg-success-3 text-success-11 hover:bg-success-4": eventType === "create",
+     "bg-accent-3 text-accent-11 hover:bg-accent-4": eventType === "other",
+   });

  render: (log) => {
    const eventType = getEventType(log.auditLog.event);
-   const badgeClassName = cn("font-mono capitalize", {
-     "bg-error-3 text-error-11 hover:bg-error-4": eventType === "delete",
-     "bg-warning-3 text-warning-11 hover:bg-warning-4": eventType === "update",
-     "bg-success-3 text-success-11 hover:bg-success-4": eventType === "create",
-     "bg-accent-3 text-accent-11 hover:bg-accent-4": eventType === "other",
-   });
+   const badgeClassName = getBadgeClassName(eventType);
    return <Badge className={badgeClassName}>{eventType}</Badge>;
  },

1-8: Consider adding column sorting capability

The table could benefit from sorting functionality, especially for time-based entries.

Consider implementing sort handlers in the Column type and adding sort indicators to column headers. Would you like me to provide an example implementation?

apps/dashboard/app/(app)/audit/[bucket]/components/log-footer.tsx (2)

19-20: Memoize date formatting to prevent unnecessary recalculations

The date formatting operation is performed on every render. Consider memoizing this value.

+ import { useMemo } from "react";
// ...
- content: format(log.auditLog.time, "MMM dd HH:mm:ss.SS"),
+ content: useMemo(() => format(log.auditLog.time, "MMM dd HH:mm:ss.SS"), [log.auditLog.time]),

16-94: Extract field configurations for better maintainability

The fields array is quite large and could be extracted into a separate configuration file or constant.

Consider moving the fields configuration to a separate file like log-footer.config.ts:

// log-footer.config.ts
export const getLogFields = (log: Data) => [
  {
    label: "Time",
    description: (content: string) => <span className="text-[13px] font-mono">{content}</span>,
    content: format(log.auditLog.time, "MMM dd HH:mm:ss.SS"),
    tooltipContent: "Copy Time",
    tooltipSuccessMessage: "Time copied to clipboard",
  },
  // ... other fields
];
apps/dashboard/app/(app)/audit/[bucket]/filter.tsx (1)

46-73: Consider accessibility improvements for the filter trigger

The filter trigger button should have proper ARIA attributes for better screen reader support.

 <Button className="flex items-center h-8 gap-2 "
+  aria-label={`${title} filter${selected.length > 0 ? `: ${selected.length} selected` : ''}`}
+  role="combobox"
+  aria-expanded={open}
 >
apps/dashboard/app/(app)/audit/[bucket]/page.tsx (2)

Line range hint 76-93: Consider extracting the complex query logic

The audit log query is quite complex with multiple conditions. Consider extracting it into a separate database utility function for better maintainability and reusability.

Example refactor:

// db/queries/audit-logs.ts
export async function getAuditLogs({
  workspaceId,
  bucketName,
  selectedEvents,
  selectedActorIds,
  retentionCutoff,
  limit
}: AuditLogQueryParams) {
  return db.query.auditLogBucket.findFirst({
    where: (table, { eq, and }) =>
      and(eq(table.workspaceId, workspaceId), eq(table.name, bucketName)),
    with: {
      logs: {
        where: (table, { and, inArray, gte }) =>
          and(
            selectedEvents.length > 0 ? inArray(table.event, selectedEvents) : undefined,
            gte(table.createdAt, retentionCutoff),
            selectedActorIds.length > 0 ? inArray(table.actorId, selectedActorIds) : undefined,
          ),
        with: {
          targets: true,
        },
        orderBy: (table, { desc }) => desc(table.time),
        limit,
      },
    },
  });
}

Line range hint 239-285: Consider adding error boundaries for filter components

While the components handle null checks well, they could benefit from error boundaries to gracefully handle potential failures in data fetching or processing.

Example implementation:

class FilterErrorBoundary extends React.Component<{ children: React.ReactNode }> {
  state = { hasError: false };
  
  static getDerivedStateFromError() {
    return { hasError: true };
  }
  
  render() {
    if (this.state.hasError) {
      return <Filter param="" title="Error" options={[]} />;
    }
    return this.props.children;
  }
}

// Usage:
<FilterErrorBoundary>
  <UserFilter tenantId={tenantId} />
</FilterErrorBoundary>
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3f8d078 and 2c939dc.

📒 Files selected for processing (24)
  • apps/dashboard/app/(app)/audit/[bucket]/components/log-footer.tsx (1 hunks)
  • apps/dashboard/app/(app)/audit/[bucket]/components/log-header.tsx (1 hunks)
  • apps/dashboard/app/(app)/audit/[bucket]/components/log-section.tsx (1 hunks)
  • apps/dashboard/app/(app)/audit/[bucket]/components/table/columns.tsx (1 hunks)
  • apps/dashboard/app/(app)/audit/[bucket]/components/table/constants.ts (1 hunks)
  • apps/dashboard/app/(app)/audit/[bucket]/components/table/index.tsx (1 hunks)
  • apps/dashboard/app/(app)/audit/[bucket]/components/table/table-details.tsx (1 hunks)
  • apps/dashboard/app/(app)/audit/[bucket]/components/table/types.ts (1 hunks)
  • apps/dashboard/app/(app)/audit/[bucket]/components/table/utils.ts (1 hunks)
  • apps/dashboard/app/(app)/audit/[bucket]/filter.tsx (2 hunks)
  • apps/dashboard/app/(app)/audit/[bucket]/page.tsx (8 hunks)
  • apps/dashboard/app/(app)/audit/[bucket]/query-state.ts (1 hunks)
  • apps/dashboard/app/(app)/audit/[bucket]/row.tsx (0 hunks)
  • apps/dashboard/app/(app)/logs/components/table/log-details/components/request-response-details.tsx (1 hunks)
  • apps/dashboard/components/virtual-table.tsx (7 hunks)
  • apps/dashboard/lib/trpc/routers/audit/fetch.ts (1 hunks)
  • apps/dashboard/lib/trpc/routers/index.ts (2 hunks)
  • apps/dashboard/lib/utils.ts (1 hunks)
  • internal/db/src/schema/audit_logs.ts (4 hunks)
  • internal/icons/src/icons/clone-plus-2.tsx (1 hunks)
  • internal/icons/src/icons/clone-x-mark-2.tsx (1 hunks)
  • internal/icons/src/icons/code-action.tsx (1 hunks)
  • internal/icons/src/icons/folder-feather.tsx (1 hunks)
  • internal/icons/src/index.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/dashboard/app/(app)/audit/[bucket]/row.tsx
✅ Files skipped from review due to trivial changes (1)
  • internal/db/src/schema/audit_logs.ts
🧰 Additional context used
🪛 Biome (1.9.4)
internal/icons/src/icons/code-action.tsx

[error] 13-13: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)

internal/icons/src/icons/clone-plus-2.tsx

[error] 13-13: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)

internal/icons/src/icons/folder-feather.tsx

[error] 13-13: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)

internal/icons/src/icons/clone-x-mark-2.tsx

[error] 13-13: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)

🔇 Additional comments (17)
internal/icons/src/index.ts (1)

20-23: LGTM! New icon exports follow consistent pattern

The new exports maintain the established pattern and alphabetical ordering.

internal/icons/src/icons/code-action.tsx (3)

16-60: LGTM! Well-structured SVG icon implementation

The component:

  • Properly spreads props for customization
  • Uses semantic SVG elements
  • Maintains consistent styling with currentColor
  • Has appropriate viewBox and dimensions

13-15: 🛠️ Refactor suggestion

Use import type for better tree-shaking

The React import is used for JSX transformation, but IconProps is only used as a type.

-import React from "react";
+import React from "react";
+import type { IconProps } from "../props";
-import type { IconProps } from "../props";

Likely invalid or redundant comment.

🧰 Tools
🪛 Biome (1.9.4)

[error] 13-13: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


1-11: Verify license compliance

The component includes Nucleo's license which restricts redistribution and usage. Ensure compliance with these terms:

  • No redistribution of icons
  • Icons restricted to bundled product use only
✅ Verification successful

I'll verify if all icon-related files consistently include the Nucleo license and if there are any icon files that might be missing it.


License compliance verified for Nucleo icons

The license compliance check shows proper implementation:

  • All icons under internal/icons/src/icons/ consistently include the Nucleo license header
  • The LICENSE file is present in the internal/icons/ directory
  • Other icon-related files outside the internal/icons/ directory are separate components and don't contain Nucleo assets
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if other files use the same license header
rg -l "Copyright © Nucleo" | wc -l

Length of output: 39


Script:

#!/bin/bash
# Let's check which files contain the Nucleo license to ensure consistent usage
rg -l "Copyright © Nucleo"

# Also check if there are any other icon files without this license
fd -e tsx -e ts "icon|Icon" | grep -v node_modules

Length of output: 1241

internal/icons/src/icons/folder-feather.tsx (2)

13-15: Use import type for better tree-shaking

Same improvement as in code-action.tsx for better tree-shaking.

🧰 Tools
🪛 Biome (1.9.4)

[error] 13-13: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


16-53: LGTM! Well-structured complex SVG implementation

The folder icon implementation:

  • Follows consistent component structure
  • Uses appropriate SVG path elements for complex shapes
  • Maintains proper styling and scaling
internal/icons/src/icons/clone-x-mark-2.tsx (2)

16-72: SVG implementation looks good!

The implementation follows best practices:

  • Uses currentColor for theming compatibility
  • Consistent stroke widths (1.5)
  • Proper viewBox and dimensions
  • Well-structured paths with appropriate attributes

1-11: Verify compliance with Nucleo license terms

The icon is under Nucleo's license which restricts redistribution and usage. Please ensure:

  1. The project has a valid Nucleo license
  2. Usage complies with the bundling restrictions
✅ Verification successful

License compliance appears to be in order

The codebase shows proper implementation of Nucleo icons:

  1. There's a dedicated LICENSE file in the internal/icons directory containing Nucleo terms
  2. The engineering documentation explicitly mentions using "Nucleo UI Essential" collection
  3. All icon files consistently maintain the required copyright notices
  4. Icons are properly bundled within the product as part of the internal/icons package
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if Nucleo license exists in the project
fd -g "*license*" -i | grep -i nucleo

Length of output: 39


Script:

#!/bin/bash
# Let's check for any Nucleo-related files or references in the codebase
rg -i "nucleo" -A 3

# Also check package.json for any Nucleo dependencies
fd package.json --exec cat {} | grep -i "nucleo"

Length of output: 18912

internal/icons/src/icons/clone-plus-2.tsx (2)

13-15: Optimize React import for better tree-shaking

The static analyzer correctly identifies that React is only used for types.

-import React from "react";
+import type React from "react";

 import type { IconProps } from "../props";
🧰 Tools
🪛 Biome (1.9.4)

[error] 13-13: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)


16-73: SVG implementation looks good!

The implementation follows the same best practices as clone-x-mark-2:

  • Uses currentColor for theming compatibility
  • Consistent stroke widths (1.5)
  • Proper viewBox and dimensions
  • Well-structured paths with appropriate attributes
apps/dashboard/lib/trpc/routers/audit/fetch.ts (1)

1-97: Efficient Implementation of fetchAuditLog Procedure

The fetchAuditLog procedure is well-implemented with proper input validation, error handling, and pagination logic. The use of rate limiting and careful construction of database queries ensures both performance and security.

apps/dashboard/lib/utils.ts (1)

26-130: Well-Structured Throttle Utility Function

The throttle function is effectively implemented, providing configurable leading and trailing options, along with proper state management and helper methods. The detailed comments and type definitions enhance readability and maintainability.

apps/dashboard/app/(app)/logs/components/table/log-details/components/request-response-details.tsx (1)

70-70: LGTM: Style improvements enhance readability

The border and padding changes improve the visual hierarchy and spacing.

Also applies to: 75-75

apps/dashboard/app/(app)/audit/[bucket]/components/log-footer.tsx (1)

65-65: Review truncated text accessibility

The truncated text in User Agent and Description fields might affect keyboard navigation and screen reader accessibility. Consider adding a title attribute or aria-label.

Also applies to: 81-81

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

122-124: LGTM: Clean router syntax update

The simplified syntax for logs router methods is cleaner and consistent with modern JavaScript practices.


125-126: Verify fetchAuditLog implementation and error handling

The new audit router looks good, but let's ensure proper error handling in the fetchAuditLog implementation.

✅ Verification successful

Error handling is properly implemented in fetchAuditLog

The implementation includes comprehensive error handling:

  • Database errors are caught and wrapped with appropriate TRPC errors
  • Proper validation of workspace existence
  • Clear error messages with support contact information
  • Input validation using Zod schema
  • Proper cursor-based pagination with limit checks
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check fetchAuditLog implementation for error handling
ast-grep --pattern 'export const fetchAuditLog = $_ => {
  try {
    $$$
  } catch $_
}'

Length of output: 90


Script:

#!/bin/bash
# Let's first locate the fetchAuditLog implementation
rg "fetchAuditLog" -A 10

Length of output: 2358


Script:

#!/bin/bash
# Let's check the complete implementation of fetchAuditLog
cat apps/dashboard/lib/trpc/routers/audit/fetch.ts

Length of output: 3336

apps/dashboard/app/(app)/audit/[bucket]/page.tsx (1)

37-39: LGTM: Well-structured type definition

The AuditLogWithTargets type effectively combines the audit log with its targets, providing good type safety for the audit log data structure.

Copy link

pullflow-com bot commented Dec 19, 2024

From Oguzhan Olguncu ‣ Instead of another PR, I added them back into this one. Both Andreas's comment and your datePicker issue is here

Copy link
Collaborator

Awesome let me test it again and we can go from there.

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

🔭 Outside diff range comments (1)
apps/dashboard/components/timestamp-info.tsx (1)

Line range hint 73-90: Improve accessibility and maintainability of the copy functionality

The current implementation has several areas for improvement:

  1. Missing keyboard accessibility for copy functionality
  2. Hardcoded timeout value
  3. Missing ARIA labels for screen readers

Consider these improvements:

+const COPY_FEEDBACK_TIMEOUT = 1000;
+
 return (
-  //biome-ignore lint/a11y/useKeyWithClickEvents: no need
   <span
+    role="button"
+    tabIndex={0}
+    aria-label={`Copy ${label} value: ${value}`}
     onClick={(e) => {
       e.stopPropagation();
       navigator.clipboard.writeText(value);
       setCopied(true);
       setTimeout(() => {
         setCopied(false);
-      }, 1000);
+      }, COPY_FEEDBACK_TIMEOUT);
     }}
+    onKeyDown={(e) => {
+      if (e.key === 'Enter' || e.key === ' ') {
+        e.preventDefault();
+        e.currentTarget.click();
+      }
+    }}
     className={cn(
       "flex items-center hover:bg-background-subtle text-left px-3 py-2",
       {
🧹 Nitpick comments (7)
apps/dashboard/components/virtual-table.tsx (4)

71-97: Consider memoizing the calculateHeight function

While the height calculation logic is well-implemented, the calculateHeight function is recreated on every render.

Consider using useCallback:

+ const calculateHeight = useCallback(() => {
-  const calculateHeight = () => {
    if (!containerRef.current) {
      return;
    }

    const rect = containerRef.current.getBoundingClientRect();
    const headerHeight = HEADER_HEIGHT + TABLE_BORDER_THICKNESS;
    const availableHeight = window.innerHeight - rect.top - headerHeight;

    setFixedHeight(Math.max(availableHeight, 0));
-  };
+ }, []);

99-111: Improve documentation for dependency exclusion

The biome-ignore comment could be more descriptive about why certain dependencies are excluded.

Update the comment to be more informative:

- // biome-ignore lint/correctness/useExhaustiveDependencies: No need to add more deps
+ // biome-ignore lint/correctness/useExhaustiveDependencies: throttle wrapper is stable and doesn't need deps

135-144: Extract scroll threshold logic for better maintainability

The scroll threshold calculation logic could be more readable and maintainable.

Consider extracting the logic into a separate function:

+ const isNearBottom = (scrollElement: HTMLElement, threshold: number) => {
+   const scrollOffset = scrollElement.scrollTop + scrollElement.clientHeight;
+   return scrollOffset >= scrollElement.scrollHeight - threshold;
+ };

  if (
    !isLoading &&
    !isFetchingNextPage &&
    lastItem.index >= data.length - 1 - instance.options.overscan &&
-   scrollOffset >= scrollThreshold
+   isNearBottom(scrollElement, rowHeight * 3)
  ) {
    throttledLoadMore();
  }

334-341: Enhance loading indicator accessibility

The loading indicator should have proper ARIA attributes for screen readers.

Add ARIA attributes to the loading indicator:

  {isFetchingNextPage && (
    <div 
      className="fixed bottom-0 left-0 right-0 py-2 bg-background/90 backdrop-blur-sm border-t border-border"
+     role="status"
+     aria-live="polite"
    >
      <div className="flex items-center justify-center gap-2 text-sm text-accent-11">
        <div 
          className="h-1.5 w-1.5 rounded-full bg-accent-11 animate-pulse"
+         aria-hidden="true" 
        />
        Loading more data
      </div>
    </div>
  )}
apps/dashboard/app/(app)/logs/components/filters/components/time-split.tsx (3)

Line range hint 37-143: Consider refactoring time validation logic for better maintainability

The time conflict resolution logic is complex and could benefit from refactoring:

  1. Extract date comparison into a helper function
  2. Simplify nested conditions using early returns
  3. Create separate functions for start/end time validations

Consider this approach:

const isSameDay = (startDate: Date, endDate: Date) => 
  format(new Date(startDate), "dd/mm/yyyy") === format(new Date(endDate), "dd/mm/yyyy");

const validateStartTime = (startTime: Time, endTime: Time): Time | null => {
  if (Number(startTime.HH) > Number(endTime.HH)) {
    return { ...startTime };
  }
  if (Number(startTime.HH) === Number(endTime.HH) && 
      Number(startTime.mm) > Number(endTime.mm)) {
    return { ...startTime, ss: endTime.ss };
  }
  // ... similar for seconds
  return null;
};

Line range hint 145-182: Enhance input validation robustness

The current validation could be improved to handle non-numeric inputs and simplified using a validation map.

Consider this enhancement:

const TIME_CONSTRAINTS = {
  HH: { max: 23 },
  mm: { max: 59 },
  ss: { max: 59 }
} as const;

function handleOnChange(value: string, valueType: TimeType) {
  if (!/^\d*$/.test(value) || value.length > 2) return;
  
  const numValue = Number(value);
  if (value && numValue > TIME_CONSTRAINTS[valueType].max) return;
  
  setTime(prev => ({ ...prev, [valueType]: value }));
}

Line range hint 184-279: Enhance UI accessibility and maintainability

While the UI implementation is solid, consider these improvements:

  1. The pattern attribute could be more specific for better validation
  2. Add role="group" and aria-labelledby for better screen reader support
  3. Consider extracting common input styles to a shared class

Example improvements:

-  pattern="[0-23]*"
+  pattern="([0-1][0-9])|(2[0-3])"
+  inputMode="numeric"

-  <div className={`flex h-7 items-center justify-center w-fit px-4...`}>
+  <div 
+    role="group"
+    aria-labelledby="time-input-label"
+    className={`flex h-7 items-center justify-center w-fit px-4...`}
+  >
+    <span id="time-input-label" className="sr-only">Time input</span>
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between d7ef66d and b561a82.

📒 Files selected for processing (3)
  • apps/dashboard/app/(app)/logs/components/filters/components/time-split.tsx (1 hunks)
  • apps/dashboard/components/timestamp-info.tsx (4 hunks)
  • apps/dashboard/components/virtual-table.tsx (8 hunks)
🔇 Additional comments (4)
apps/dashboard/components/virtual-table.tsx (1)

2-2: LGTM: Type definitions and imports are well-structured

The addition of isFetchingNextPage to VirtualTableProps and the throttle utility import enhance the component's functionality for handling paginated data loading.

Also applies to: 30-30

apps/dashboard/components/timestamp-info.tsx (2)

6-7: LGTM: Import changes align with the new timestamp formatting approach

The replacement of formatRelative with ms library is appropriate for the new relative time implementation.


Line range hint 98-107: Address hover state concerns mentioned in PR comments

Based on the PR comments, there are unresolved concerns about the timestamp hover state from Vercel. Consider reviewing the tooltip trigger hover behavior and ensuring it works consistently across different screen sizes.

Let's check for any related hover state issues:

apps/dashboard/app/(app)/logs/components/filters/components/time-split.tsx (1)

5-5: Verify the removal of useEffect's impact on time synchronization

The removal of the useEffect hook means that time values won't automatically update when dates change. This could lead to invalid time states if not properly handled elsewhere in the parent components.

apps/dashboard/components/virtual-table.tsx Outdated Show resolved Hide resolved
apps/dashboard/components/timestamp-info.tsx Show resolved Hide resolved
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: 1

🔭 Outside diff range comments (1)
apps/dashboard/components/virtual-table.tsx (1)

Line range hint 217-230: Enhance accessibility with proper ARIA attributes

While keyboard navigation is implemented, the table could benefit from additional accessibility attributes:

  <div className="w-full flex flex-col" ref={containerRef}>
-   <TableHeader />
+   <div role="table" aria-label="Virtual Table">
+     <div role="rowgroup">
+       <TableHeader />
+     </div>
      <div
        ref={(el) => {
          if (el) {
            //@ts-expect-error safe to bypass
            parentRef.current = el;
            //@ts-expect-error safe to bypass
            tableRef.current = el;
          }
        }}
        data-table-container="true"
+       role="rowgroup"
        className="overflow-auto pb-10"
        style={{ height: `${fixedHeight}px` }}
      >
🧹 Nitpick comments (7)
apps/dashboard/app/(app)/logs/components/filters/components/custom-date-filter.tsx (2)

30-33: Consider handling zero timestamp edge case.

The current initialization using nullish coalescing (??) with 0 as default might mask legitimate timestamp values of 0. Consider using a more explicit check:

- startTime: parseAsInteger.withDefault(initialParams.startTime ?? 0),
- endTime: parseAsInteger.withDefault(initialParams.endTime ?? 0),
+ startTime: parseAsInteger.withDefault(
+   initialParams.startTime === null ? 0 : initialParams.startTime
+ ),
+ endTime: parseAsInteger.withDefault(
+   initialParams.endTime === null ? 0 : initialParams.endTime
+ ),

Line range hint 20-156: Consider enhancing accessibility for the date picker.

Since this component is part of an audit table, consider adding the following accessibility improvements:

  • Add aria-label to the date picker trigger
  • Include screen reader text for the time inputs
  • Add aria-live region for announcing selected date ranges

Example improvements:

 <PopoverTrigger asChild>
   <div
     id="date"
+    aria-label="Select date range"
     className={cn(
       "justify-start text-left font-normal flex gap-2 items-center",
       !finalDate && "text-muted-foreground",
     )}
   >
apps/dashboard/app/(app)/audit/[bucket]/components/table/log-footer.tsx (2)

35-64: Consider extracting Actor Details into a separate component.

The Actor Details field contains complex conditional rendering logic that would be better managed in a dedicated component.

+ // ActorDetails.tsx
+ type ActorDetailsProps = {
+   log: typeof log.auditLog;
+   user: typeof log.user;
+ };
+ 
+ const ActorDetails = ({ log, user }: ActorDetailsProps) => {
+   if (log.actor.type === "user" && user?.imageUrl) {
+     return (
+       <div className="flex justify-end items-center w-full gap-2 max-sm:m-0 max-sm:gap-1 max-sm:text-xs md:flex-grow">
+         <Avatar className="w-6 h-6">
+           <AvatarImage src={user.imageUrl} />
+           <AvatarFallback>{user?.username?.slice(0, 2)}</AvatarFallback>
+         </Avatar>
+         <span className="text-sm text-content whitespace-nowrap">
+           {`${user?.firstName ?? ""} ${user?.lastName ?? ""}`}
+         </span>
+       </div>
+     );
+   }
+   
+   const Icon = log.actor.type === "key" ? KeySquare : FunctionSquare;
+   return (
+     <div className="flex items-center w-full gap-2 max-sm:m-0 max-sm:gap-1 max-sm:text-xs md:flex-grow">
+       <Icon className="w-4 h-4" />
+       <span className="font-mono text-xs text-content">{log.actor.id}</span>
+     </div>
+   );
+ };

  // In LogFooter.tsx
  {
    label: "Actor Details",
    description: (content) => <ActorDetails log={content.log} user={content.user} />,
    content: { log: log.auditLog, user: log.user },
    className: "whitespace-pre",
    tooltipContent: "Copy Actor",
    tooltipSuccessMessage: "Actor copied to clipboard",
  },

18-26: Document the skipTooltip flag usage.

The Time field has skipTooltip: true but it's not clear why this is needed when other fields have tooltips.

Consider adding a comment explaining why the Time field skips the tooltip functionality.

apps/dashboard/components/virtual-table.tsx (3)

38-38: Document the rationale for THROTTLE_DELAY value

Consider adding a comment explaining why 350ms was chosen as the throttle delay. This helps future maintainers understand if this value needs adjustment based on specific performance requirements.


138-140: Extract magic number into a named constant

The calculation uses rowHeight * 3 as a threshold, but the '3' is a magic number. Consider extracting this into a named constant for better maintainability.

+ const SCROLL_THRESHOLD_ROWS = 3;
  const scrollOffset = scrollElement.scrollTop + scrollElement.clientHeight;
- const scrollThreshold = scrollElement.scrollHeight - rowHeight * 3;
+ const scrollThreshold = scrollElement.scrollHeight - rowHeight * SCROLL_THRESHOLD_ROWS;

323-330: Consider relative positioning for loading indicator

The current fixed positioning of the loading indicator could cause issues with multiple tables on the same page. Consider using relative positioning within the table container.

- <div className="fixed bottom-0 left-0 right-0 py-2 bg-background/90 backdrop-blur-sm border-t border-border">
+ <div className="sticky bottom-0 left-0 right-0 py-2 bg-background/90 backdrop-blur-sm border-t border-border">
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between b561a82 and b237b96.

📒 Files selected for processing (4)
  • apps/dashboard/app/(app)/audit/[bucket]/components/table/log-footer.tsx (1 hunks)
  • apps/dashboard/app/(app)/logs/components/filters/components/custom-date-filter.tsx (3 hunks)
  • apps/dashboard/app/(app)/logs/components/table/log-details/components/request-response-details.tsx (3 hunks)
  • apps/dashboard/components/virtual-table.tsx (8 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/dashboard/app/(app)/logs/components/table/log-details/components/request-response-details.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
apps/dashboard/components/virtual-table.tsx

[error] 68-68: Invalid typeof comparison value: this expression is not a string literal

not a string literal
Unsafe fix: Compare the result of typeof with a valid type name

(lint/suspicious/useValidTypeof)

🔇 Additional comments (6)
apps/dashboard/app/(app)/logs/components/filters/components/custom-date-filter.tsx (2)

6-6: LGTM! Well-structured interface and imports.

The new interface DatePickerWithRangeProps is well-defined and properly extends React's HTML attributes. The imports are appropriate for the component's functionality.

Also applies to: 8-8, 13-18


81-82: LGTM! Explicit null values for cleared state.

Using null instead of undefined is more explicit and aligns well with the interface type definition.

apps/dashboard/app/(app)/audit/[bucket]/components/table/log-footer.tsx (4)

1-11: LGTM! Clean imports and type definitions.

The imports are well-organized and the Props type is clearly defined.


13-16: LGTM! Clean component structure.

The component follows React best practices with proper prop destructuring and component composition.


66-73: User Agent display needs improvement.

The current implementation truncates the User Agent without a way to view the full content.


82-89: Description field needs formatting improvements.

The description should not be truncated and should support multi-line display per PR feedback.

apps/dashboard/components/virtual-table.tsx Outdated Show resolved Hide resolved
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

♻️ Duplicate comments (1)
apps/dashboard/components/virtual-table.tsx (1)

65-71: ⚠️ Potential issue

Fix scroll lock implementation issues

The current implementation needs improvement for better error handling and type safety.

Apply this diff to fix both issues:

  useScrollLock({
    autoLock: true,
    lockTarget:
-     typeof window !== "undefined"
-       ? (document.querySelector("#layout-wrapper") as HTMLElement)
-       : undefined,
+     typeof window !== 'undefined'
+       ? document.querySelector("#layout-wrapper") || document.body
+       : undefined,
  });
🧹 Nitpick comments (7)
apps/dashboard/app/(app)/logs/components/filters/components/custom-date-filter.tsx (3)

81-82: Consider adding validation for date range boundaries.

While using null for cleared dates is more explicit, consider adding validation to ensure:

  1. Start time is not after end time
  2. Date ranges don't exceed reasonable limits (e.g., max 30 days)
 setSearchParams({
-  startTime: null,
-  endTime: null,
+  startTime: null,
+  endTime: null,
+  validate: () => {
+    if (!startTime || !endTime) return true;
+    const diffInDays = Math.abs(endTime - startTime) / (1000 * 60 * 60 * 24);
+    return diffInDays <= 30;
+  }
 });

122-122: Consider adding error feedback for invalid date selections.

While preventing future date selection is good, consider adding visual feedback or a tooltip to explain why certain dates are disabled.

 disabled={(date) => {
   const isDisabled = date > new Date();
+  // Add aria-label for accessibility
+  if (isDisabled) {
+    date.setAttribute('aria-label', 'Future dates cannot be selected');
+  }
   return isDisabled;
 }}

Line range hint 20-165: Consider implementing a reusable date filter hook.

The component handles complex date filtering logic that could be useful in other parts of the application. Consider extracting the core date handling logic into a custom hook for reusability.

Example structure:

// useDateRangeFilter.ts
export function useDateRangeFilter(initialParams: DateRangeParams) {
  // Move state and date handling logic here
  return {
    dateRange,
    timeRange,
    handleDateChange,
    handleTimeChange,
    // ... other utilities
  };
}

This would:

  1. Improve maintainability
  2. Make the logic reusable across different features
  3. Simplify testing
apps/dashboard/components/virtual-table.tsx (4)

74-100: Optimize cleanup handling in resize effect

The resize handling implementation is good, but we can optimize the cleanup by combining the cleanup operations.

  useEffect(() => {
    const calculateHeight = () => {
      if (!containerRef.current) return;
      
      const rect = containerRef.current.getBoundingClientRect();
      const headerHeight = HEADER_HEIGHT + TABLE_BORDER_THICKNESS;
      const availableHeight = window.innerHeight - rect.top - headerHeight;
      setFixedHeight(Math.max(availableHeight, 0));
    };

    calculateHeight();
    
    const resizeObserver = new ResizeObserver(calculateHeight);
    const cleanup = () => {
      resizeObserver.disconnect();
      window.removeEventListener("resize", calculateHeight);
    };

    window.addEventListener("resize", calculateHeight);
    if (containerRef.current) {
      resizeObserver.observe(containerRef.current);
    }

-   return () => {
-     resizeObserver.disconnect();
-     window.removeEventListener("resize", calculateHeight);
-   };
+   return cleanup;
  }, []);

38-38: Consider making THROTTLE_DELAY configurable

The throttle delay is hardcoded to 350ms. Consider making this configurable through props to allow adjustment based on use case.

+ export type VirtualTableProps<T> = {
+   // ... existing props
+   throttleDelay?: number;
+ };

- const THROTTLE_DELAY = 350;
+ const DEFAULT_THROTTLE_DELAY = 350;

  export function VirtualTable<T>({
    // ... existing props
+   throttleDelay = DEFAULT_THROTTLE_DELAY,
  }: VirtualTableProps<T>) {
    // ... rest of the implementation
    const throttledLoadMore = useCallback(
      throttle(
        () => {
          if (onLoadMore) {
            onLoadMore();
          }
        },
-       THROTTLE_DELAY,
+       throttleDelay,
        { leading: true, trailing: false },
      ),
-     [onLoadMore],
+     [onLoadMore, throttleDelay],
    );

Also applies to: 102-114


141-147: Extract scroll threshold multiplier as a constant

The scroll threshold calculation uses a magic number (3) for row multiplication. This should be extracted as a named constant for better maintainability.

+ const SCROLL_THRESHOLD_ROW_MULTIPLIER = 3;

  if (
    !isLoading &&
    !isFetchingNextPage &&
    lastItem.index >= data.length - 1 - instance.options.overscan &&
-   scrollOffset >= scrollElement.scrollHeight - rowHeight * 3
+   scrollOffset >= scrollElement.scrollHeight - rowHeight * SCROLL_THRESHOLD_ROW_MULTIPLIER
  ) {
    throttledLoadMore();
  }

323-330: Enhance loading indicator accessibility

The loading indicator should have proper ARIA attributes for better screen reader support.

- {isFetchingNextPage && (
+ {isFetchingNextPage && (
    <div 
      className="fixed bottom-0 left-0 right-0 py-2 bg-background/90 backdrop-blur-sm border-t border-border"
+     role="status"
+     aria-live="polite"
    >
      <div className="flex items-center justify-center gap-2 text-sm text-accent-11">
        <div 
          className="h-1.5 w-1.5 rounded-full bg-accent-11 animate-pulse"
+         aria-hidden="true" 
        />
-       Loading more data
+       <span>Loading more data</span>
      </div>
    </div>
  )}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between b237b96 and 399f03a.

📒 Files selected for processing (3)
  • apps/dashboard/app/(app)/logs/components/filters/components/custom-date-filter.tsx (4 hunks)
  • apps/dashboard/components/virtual-table.tsx (8 hunks)
  • apps/planetfall/next-env.d.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/planetfall/next-env.d.ts
🔇 Additional comments (3)
apps/dashboard/app/(app)/logs/components/filters/components/custom-date-filter.tsx (2)

6-18: LGTM! Well-structured interface and imports.

The new interface DatePickerWithRangeProps properly types the component props and follows TypeScript best practices.


30-33: Consider timezone handling in date operations.

While the state initialization is correct, the component might benefit from explicit timezone handling to ensure consistent behavior across different user locations. Consider using UTC for internal operations and converting to local time only for display.

Let's check for any timezone-related issues in the codebase:

apps/dashboard/components/virtual-table.tsx (1)

2-2: Verify throttle implementation

The throttle utility is being imported from local utils. Let's verify its implementation to ensure it properly handles cleanup and edge cases.

✅ Verification successful

Let me gather more information about the throttle implementation to ensure it properly handles cleanup and edge cases.


Let me get the remaining part of the throttle implementation to verify the cleanup and edge cases handling.


Throttle implementation is well-implemented and safe to use

The throttle utility implementation is robust and includes all necessary features:

  • Proper cleanup through cancel method that clears timeout and resets state
  • Edge case handling with leading and trailing options
  • Memory leak prevention by clearing timeouts
  • Type-safe implementation with proper TypeScript generics
  • Proper debounce timing with remainingWait calculations
  • Flush capability to force execution of pending calls
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check throttle implementation
rg -A 10 "export.*function.*throttle" 

Length of output: 699


Script:

#!/bin/bash
# Get the complete throttle implementation
rg -A 50 "export.*function.*throttle" apps/dashboard/lib/utils.ts

Length of output: 1516


Script:

#!/bin/bash
# Get the complete throttle implementation including the shouldInvoke function
rg -B 10 -A 50 "function shouldInvoke" apps/dashboard/lib/utils.ts

Length of output: 949

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.

3 participants