Skip to content

Conversation

@adityachoudhari26
Copy link
Contributor

@adityachoudhari26 adityachoudhari26 commented Nov 29, 2024

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced components for managing job conditions, including RunbookJobConditionDialog, RunbookJobComparisonConditionRender, and RunbookJobConditionRender.
    • Added a RunbookNavBar for navigation related to runbooks.
    • Implemented a RunbookPage to display job lists associated with specific runbooks.
    • Enhanced breadcrumb navigation with a new entry for runbooks.
  • Improvements

    • Updated job fetching logic in JobTable to include a limit parameter for better data management.
    • Added a new nFormatter utility for consistent number formatting across components.
  • Bug Fixes

    • Improved error handling and loading states for job data fetching.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 29, 2024

Walkthrough

This pull request introduces several enhancements and new components related to job conditions and runbooks in the web service application. Key changes include the addition of a limit parameter to the JobTable component's query for job triggers, the introduction of multiple new components for managing job conditions (RunbookJobComparisonConditionRender, RunbookJobConditionDialog, and RunbookJobConditionRender), and the establishment of a new layout and page for displaying runbook details and associated jobs. Additionally, modifications to the runbookRouter enhance job retrieval functionalities.

Changes

File Change Summary
apps/webservice/src/app/[workspaceSlug]/(app)/(job)/jobs/JobTable.tsx Added limit: 0 to job triggers query to modify result fetching behavior.
apps/webservice/src/app/[workspaceSlug]/(app)/_components/job-condition/RunbookJobComparisonConditionRender.tsx New component for rendering job comparison conditions with various internal functions for condition management.
apps/webservice/src/app/[workspaceSlug]/(app)/_components/job-condition/RunbookJobConditionDialog.tsx New dialog component for managing job conditions, integrating RunbookJobConditionRender.
apps/webservice/src/app/[workspaceSlug]/(app)/_components/job-condition/RunbookJobConditionRender.tsx New component for rendering job conditions based on type, utilizing type guards for conditional rendering.
apps/webservice/src/app/[workspaceSlug]/(app)/systems/SystemsBreadcrumb.tsx Updated SystemBreadcrumbNavbar to include optional runbookId and fetch corresponding runbook data.
apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/_components/nFormatter.ts New function nFormatter for formatting numbers with suffixes (e.g., "k", "M").
apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/DeploymentNavBar.tsx Removed local nFormatter function, now importing from nFormatter.ts for modularity.
apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/runbooks/[runbookId]/RunbookNavBar.tsx New component for navigation within runbook pages, utilizing nFormatter for job count display.
apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/runbooks/[runbookId]/layout.tsx New layout component for runbook pages, with metadata generation and job data fetching.
apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/runbooks/[runbookId]/page.tsx New component RunbookPage for displaying jobs associated with a runbook, with filtering capabilities.
packages/api/src/router/runbook.ts Added byId and jobs procedures for runbook and job retrieval, updated existing procedures for consistency.

Possibly related PRs

Suggested reviewers

  • jsbroks: Suggested for review due to familiarity with the components and changes involved.

📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 7799a20 and e5136b3.

📒 Files selected for processing (1)
  • apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/_components/nFormatter.ts (1 hunks)
🧰 Additional context used
📓 Learnings (1)
apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/_components/nFormatter.ts (1)
Learnt from: jsbroks
PR: ctrlplanedev/ctrlplane#241
File: apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/_components/nFormatter.ts:2-10
Timestamp: 2024-11-29T04:21:48.221Z
Learning: Use `Intl.NumberFormat` with `compact` notation for number formatting with suffixes like 'K', 'M' instead of custom functions.
🔇 Additional comments (2)
apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/_components/nFormatter.ts (2)

1-5: Great use of Intl.NumberFormat!

The implementation follows the recommended approach of using Intl.NumberFormat with compact notation for standardized number formatting.


1-5: Add input validation and type constraints.

The function should handle edge cases and add proper type constraints.

-export const nFormatter = (num: number, digits: number) =>
+export const nFormatter = (num: number, digits: number): string => {
+  if (!Number.isFinite(num)) return 'N/A';
+  if (!Number.isInteger(digits) || digits < 0) {
+    throw new Error('digits must be a non-negative integer');
+  }
+  try {
+    return new Intl.NumberFormat("en", {
+      notation: "compact",
+      maximumFractionDigits: digits,
+    }).format(num);
+  } catch (error) {
+    console.error('Error formatting number:', error);
+    return 'Error';
+  }
+};

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.

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

@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: 8

🧹 Outside diff range and nitpick comments (22)
apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/_components/nFormatter.ts (2)

11-15: Optimize performance and improve readability.

The current implementation has several performance and readability issues:

  1. reverse() creates a new array on every call
  2. The regular expression could be pre-compiled
  3. The chained operations could be more readable
-  const regexp = /\.0+$|(?<=\.[0-9]*[1-9])0+$/;
-  const item = lookup.reverse().find((item) => num >= item.value);
-  return item
-    ? (num / item.value).toFixed(digits).replace(regexp, "").concat(item.symbol)
-    : "0";
+const TRAILING_ZEROS_REGEX = /\.0+$|(?<=\.[0-9]*[1-9])0+$/;
+
+export const nFormatter = (num: number, digits: number): string => {
+  // ... validation code from previous comment ...
+
+  // Find the largest value that num is greater than or equal to
+  const item = NUMBER_FORMAT_LOOKUP
+    .findLast((item) => num >= item.value);
+
+  if (!item) return "0";
+
+  const scaledNum = num / item.value;
+  const formattedNum = scaledNum.toFixed(digits)
+    .replace(TRAILING_ZEROS_REGEX, '');
+
+  return `${formattedNum}${item.symbol}`;

1-16: Add JSDoc documentation and examples.

The function would benefit from comprehensive documentation explaining its purpose, parameters, and usage examples.

+/**
+ * Formats a number using metric prefixes (k, M, G, etc.)
+ * @param num - The number to format
+ * @param digits - Number of decimal places to show
+ * @returns Formatted string with metric prefix
+ * @example
+ * nFormatter(1234, 1) // "1.2k"
+ * nFormatter(1234567, 2) // "1.23M"
+ * nFormatter(-1234, 1) // "-1.2k"
+ * @throws {Error} If digits is not a non-negative integer
+ */
 export const nFormatter = ...
apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/runbooks/[runbookId]/layout.tsx (2)

5-7: Consider using path aliases for better maintainability

The relative imports using multiple parent directory navigation (../../../) can be fragile and hard to maintain. Consider using path aliases to make imports more maintainable.

Example:

-import { SystemBreadcrumbNavbar } from "../../../SystemsBreadcrumb";
-import { TopNav } from "../../../TopNav";
+import { SystemBreadcrumbNavbar } from "@/components/SystemsBreadcrumb";
+import { TopNav } from "@/components/TopNav";

30-37: Consider optimizing multiple API calls

The component makes three separate API calls that could potentially be parallelized for better performance.

-  const workspace = await api.workspace.bySlug(params.workspaceSlug);
-  if (workspace == null) return notFound();
-
-  const runbook = await api.runbook.byId(params.runbookId);
-  if (runbook == null) return notFound();
-
-  const { total } = await api.runbook.jobs({ runbookId, limit: 0 });
+  const [workspace, runbook, jobStats] = await Promise.all([
+    api.workspace.bySlug(params.workspaceSlug),
+    api.runbook.byId(params.runbookId),
+    api.runbook.jobs({ runbookId: params.runbookId })
+  ]);
+
+  if (workspace == null || runbook == null) return notFound();
+  const { total } = jobStats;
apps/webservice/src/app/[workspaceSlug]/(app)/_components/job-condition/RunbookJobConditionRender.tsx (2)

17-20: Enhance component documentation.

While the width requirement is noted, the documentation could be more comprehensive:

  • Add a description of the component's purpose
  • Document the expected props and their types
  • Explain why the 1000px minimum width is required
  • Consider adding usage examples
 /**
+ * A component that renders different types of job conditions based on their type.
+ * Supports comparison, created at, metadata, and status conditions.
+ *
+ * @param {JobConditionRenderProps<JobCondition>} props
+ * @param {JobCondition} props.condition - The condition to render
+ * @param {Function} props.onChange - Callback when condition changes
+ * @param {number} props.depth - Nesting depth for layout (default: 0)
+ * @param {string} props.className - Additional CSS classes
+ *
  * The parent container should have min width of 1000px
  * to render this component properly.
+ * This is required to ensure proper alignment of nested condition components
+ * and prevent layout shifts.
  */

63-63: Consider adding error handling and fallback UI.

The component silently returns null for invalid conditions. Consider:

  1. Adding error boundaries
  2. Logging invalid conditions for debugging
  3. Rendering a fallback UI to indicate invalid state
- return null;
+ console.warn('Invalid condition type:', condition);
+ return (
+   <div className="text-red-500">
+     Invalid condition type. Please check console for details.
+   </div>
+ );
apps/webservice/src/app/[workspaceSlug]/(app)/_components/job-condition/RunbookJobConditionDialog.tsx (5)

32-36: Consider cleanup on dialog close.

The local state persists even after the dialog is closed. Consider resetting the local state when the dialog closes to ensure a fresh start on next open.

  const [open, setOpen] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [localCondition, setLocalCondition] = useState(
    condition ?? defaultCondition,
  );
+
+ useEffect(() => {
+   if (!open) {
+     setLocalCondition(condition ?? defaultCondition);
+     setError(null);
+   }
+ }, [open, condition]);

41-44: Optimize event handler with useCallback.

The onClick handler should be memoized to prevent unnecessary re-renders.

+ const handleClick = useCallback((e: React.MouseEvent) => {
+   e.stopPropagation();
+ }, []);
+
  <DialogContent
    className="min-w-[1000px]"
-   onClick={(e) => e.stopPropagation()}
+   onClick={handleClick}
  >

68-83: Extract save logic into a separate handler.

The save handler is quite complex and would benefit from being extracted into a separate function for better readability and maintainability.

+ const handleSave = useCallback(() => {
+   if (!isValidJobCondition(localCondition)) {
+     setError(
+       "Invalid job condition, ensure all fields are filled out correctly.",
+     );
+     return;
+   }
+   setOpen(false);
+   setError(null);
+   if (isEmptyCondition(localCondition)) {
+     onChange(null);
+     return;
+   }
+   onChange(localCondition);
+ }, [localCondition, onChange]);
+
  <Button
-   onClick={() => {
-     if (!isValidJobCondition(localCondition)) {
-       setError(
-         "Invalid job condition, ensure all fields are filled out correctly.",
-       );
-       return;
-     }
-     setOpen(false);
-     setError(null);
-     if (isEmptyCondition(localCondition)) {
-       onChange(null);
-       return;
-     }
-     onChange(localCondition);
-   }}
+   onClick={handleSave}
  >

38-89: Enhance accessibility attributes.

The dialog implementation could benefit from additional accessibility attributes.

  <Dialog 
    open={open} 
    onOpenChange={setOpen}
+   aria-describedby="job-condition-dialog-description"
  >
    <DialogTrigger asChild>{children}</DialogTrigger>
    <DialogContent
      className="min-w-[1000px]"
      onClick={(e) => e.stopPropagation()}
+     role="dialog"
+     aria-modal="true"
+     aria-labelledby="job-condition-dialog-title"
    >
      <DialogHeader>
-       <DialogTitle>Edit Job Condition</DialogTitle>
+       <DialogTitle id="job-condition-dialog-title">Edit Job Condition</DialogTitle>
        <DialogDescription
+         id="job-condition-dialog-description"
        >

69-74: Enhance error validation feedback.

The current error message is generic. Consider providing more specific feedback about what exactly is invalid in the condition.

- if (!isValidJobCondition(localCondition)) {
-   setError(
-     "Invalid job condition, ensure all fields are filled out correctly.",
-   );
-   return;
- }
+ const validationError = validateJobCondition(localCondition);
+ if (validationError) {
+   setError(validationError);
+   return;
+ }

Note: This assumes the existence of a validateJobCondition function that returns specific error messages. If it doesn't exist, consider implementing it.

apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/runbooks/[runbookId]/RunbookNavBar.tsx (1)

30-36: Consider using URL utilities for more robust URL handling.

While the current URL construction works, consider using URLPattern or a URL builder utility to handle special characters and ensure URL validity.

Example implementation:

const buildUrl = (base: string, params: Record<string, string>, path?: string) => {
  const url = new URL(base, window.location.origin);
  Object.entries(params).forEach(([key, value]) => {
    url.pathname += `/${encodeURIComponent(value)}`;
  });
  if (path) url.pathname += `/${path}`;
  return url.pathname;
};

const baseUrl = buildUrl("/", {
  workspaceSlug,
  systemSlug,
  runbookId,
});
const settingsUrl = buildUrl("/", {
  workspaceSlug,
  systemSlug,
  runbookId,
}, "settings");
apps/webservice/src/app/[workspaceSlug]/(app)/systems/SystemsBreadcrumb.tsx (1)

42-42: Consider adding error handling for the runbook fetch

While the conditional fetching is good, consider adding error handling to gracefully handle cases where the runbook fetch fails.

-  const runbook = runbookId ? await api.runbook.byId(runbookId) : null;
+  let runbook = null;
+  if (runbookId) {
+    try {
+      runbook = await api.runbook.byId(runbookId);
+    } catch (error) {
+      console.error('Failed to fetch runbook:', error);
+      // Optionally handle the error in the UI
+    }
+  }
apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/DeploymentNavBar.tsx (2)

Line range hint 108-119: Fix incorrect URL for Jobs navigation

The Jobs navigation menu item is currently linking to the variables URL (variablesUrl), which is incorrect and could confuse users.

Apply this fix:

-              <Link href={variablesUrl} legacyBehavior passHref>
+              <Link href={`/${workspaceSlug}/systems/${systemSlug}/deployments/${deploymentSlug}/jobs`} legacyBehavior passHref>

Line range hint 47-53: Consider more explicit Settings active state detection

The current implementation determines Settings as active by the absence of other active states. This could lead to unexpected behavior if new sections are added.

Consider using an explicit path check:

-  const isSettingsActive =
-    !isReleasesActive &&
-    !isVariablesActive &&
-    !isJobsActive &&
-    !isReleaseChannelsActive &&
-    !isHooksActive;
+  const isSettingsActive = pathname === overviewUrl;
apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/runbooks/[runbookId]/page.tsx (1)

121-121: Simplify opacity calculation

The multiplication by 1 in 1 * (1 - i / 10) is unnecessary. You can simplify it to just (1 - i / 10).

Apply this diff to simplify the calculation:

- style={{ opacity: 1 * (1 - i / 10) }}
+ style={{ opacity: 1 - i / 10 }}
packages/api/src/router/runbook.ts (2)

14-25: Handle cases where the runbook is not found

Currently, if no runbook is found with the given ID, the procedure may return null or undefined. Consider returning a more informative error message or throwing an exception to handle this case appropriately.


70-74: Ensure consistent optionality of variables in create and update procedures

In the create procedure, variables is required, whereas in the update procedure, variables is optional. For consistency and better usability, consider making variables optional in both procedures unless it's essential for create.

Also applies to: 107-107

apps/webservice/src/app/[workspaceSlug]/(app)/_components/job-condition/RunbookJobComparisonConditionRender.tsx (4)

189-189: Remove unnecessary key prop from child component

The key prop is used by React for list rendering and is not passed to the child component via props. Passing key={index} to JobConditionRender is unnecessary unless the component explicitly requires a key prop.

Remove the key prop to clean up the code:

- <JobConditionRender
-   key={index}
    condition={subCond}
    onChange={(c) => updateCondition(index, c)}
    depth={depth + 1}
    className={cn(depth === 0 ? "col-span-11" : "col-span-10")}
  />

310-337: Define a constant for maximum depth to avoid magic numbers

Using the hardcoded value 2 for maximum depth in multiple places can make the code less maintainable. Defining a constant improves readability and makes future adjustments easier.

At the top of the file, define the constant:

const MAX_DEPTH = 2;

Update the conditions to use MAX_DEPTH:

- {depth < 2 && (
+ {depth < MAX_DEPTH && (

Apply this change wherever depth < 2 appears.


82-83: Optimize repeated function calls for performance

The function doesConvertingToComparisonRespectMaxDepth is called multiple times with the same arguments, which can be inefficient.

Cache the result within the scope to avoid redundant computations:

// Inside the map function
const canConvertToComparison = doesConvertingToComparisonRespectMaxDepth(
  depth + 1,
  subCond,
);

// Use `canConvertToComparison` instead of calling the function again

This approach improves performance, especially if the component re-renders frequently.

Also applies to: 225-228, 237-241


225-249: Provide user feedback when actions are unavailable

If an action cannot be performed due to depth constraints, the user may not understand why the option is missing.

Consider disabling the menu item with a tooltip explaining why it's unavailable:

<DropdownMenuItem
  onClick={() => canConvertToComparison && convertToComparison(index)}
+ disabled={!canConvertToComparison}
  className="flex items-center gap-2"
>
  <IconRefresh className="h-4 w-4" />
  Turn into group
</DropdownMenuItem>

Add a tooltip or explanatory text to inform the user:

{!canConvertToComparison && (
  <span className="text-xs text-muted-foreground">
    Maximum nesting depth reached.
  </span>
)}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 8a1b142 and 7799a20.

📒 Files selected for processing (11)
  • apps/webservice/src/app/[workspaceSlug]/(app)/(job)/jobs/JobTable.tsx (1 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/_components/job-condition/RunbookJobComparisonConditionRender.tsx (1 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/_components/job-condition/RunbookJobConditionDialog.tsx (1 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/_components/job-condition/RunbookJobConditionRender.tsx (1 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/systems/SystemsBreadcrumb.tsx (3 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/_components/nFormatter.ts (1 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/DeploymentNavBar.tsx (1 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/runbooks/[runbookId]/RunbookNavBar.tsx (1 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/runbooks/[runbookId]/layout.tsx (1 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/runbooks/[runbookId]/page.tsx (1 hunks)
  • packages/api/src/router/runbook.ts (4 hunks)
🔇 Additional comments (17)
apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/_components/nFormatter.ts (1)

1-16: Verify usage in badge components.

The function is used in multiple badge components. Let's verify the implementation matches the requirements.

✅ Verification successful

The nFormatter implementation is correctly used in badge components.

The verification shows that:

  • The nFormatter is used consistently in navigation bars for formatting numbers in badges (RunbookNavBar.tsx and DeploymentNavBar.tsx)
  • The implementation matches the requirements with proper digit formatting and symbol appending
  • No inconsistent number formatting patterns are used in badge components
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all usages of nFormatter to ensure consistent formatting

# Search for imports and usages
echo "Searching for nFormatter imports and usages..."
rg -A 2 "import.*nFormatter|nFormatter\(" --type ts --type tsx

# Search for other number formatting approaches that should be consolidated
echo "Searching for other number formatting patterns..."
rg "toLocaleString|Intl\.NumberFormat|\.toFixed|\.toString" --type ts --type tsx

Length of output: 415


Script:

#!/bin/bash
# Search for nFormatter imports and usages without file type restrictions
echo "Searching for nFormatter imports and usages..."
rg -A 2 "import.*nFormatter|nFormatter\("

# Search for other number formatting patterns
echo "Searching for other number formatting patterns..."
rg "toLocaleString|Intl\.NumberFormat|\.toFixed|\.toString"

# Find all badge components
echo "Searching for badge components..."
fd -e ts -e tsx ".*badge.*"

Length of output: 18522

apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/runbooks/[runbookId]/layout.tsx (2)

48-48: Review the fixed height calculation for responsiveness

The scroll container uses a fixed height calculation (h-[calc(100vh-53px-49px)]) which might break in different viewport sizes or when the nav heights change.

Consider using CSS custom properties or a more flexible layout approach.

✅ Verification successful

Fixed height calculations are consistently used across the application

The codebase shows a consistent pattern of using fixed height calculations with viewport units (vh) for scroll containers. This appears to be an intentional design choice across multiple layouts and components:

  • Multiple layouts use the same h-[calc(100vh-53px-49px)] calculation
  • Similar patterns are used throughout the app with variations like h-[calc(100vh-40px)], h-[calc(100vh-110px)], etc.
  • The calculations account for fixed-height elements like headers and navigation bars

Given this is a deliberate architectural decision implemented consistently across the application, the current implementation aligns with the established pattern.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for other instances of fixed height calculations
rg -g '*.{tsx,css}' 'h-\[calc\(.*\)\]'

Length of output: 4931


36-37: 🛠️ Refactor suggestion

Optimize runbookId usage and verify job counting logic

Two issues to address:

  1. The runbookId extraction is redundant as it's already available in params
  2. Using limit: 0 for counting jobs seems unusual and might not work as intended
-  const { runbookId } = params;
-  const { total } = await api.runbook.jobs({ runbookId, limit: 0 });
+  const { total } = await api.runbook.jobs({ runbookId: params.runbookId });
apps/webservice/src/app/[workspaceSlug]/(app)/_components/job-condition/RunbookJobConditionRender.tsx (3)

1-15: LGTM! Well-organized imports with clear separation.

The imports are logically grouped between external dependencies and internal components/types.


21-23: LGTM! Well-typed component definition.

The component is properly typed with React.FC and includes appropriate props with sensible defaults.


54-61: Ensure consistent prop usage across condition renderers.

The depth prop is passed to all condition renderers except StatusConditionRender. This inconsistency should either be documented or fixed.

 <StatusConditionRender
   condition={condition}
   onChange={onChange}
+  depth={depth}
   className={className}
 />
apps/webservice/src/app/[workspaceSlug]/(app)/_components/job-condition/RunbookJobConditionDialog.tsx (2)

1-21: LGTM! Well-organized imports with clear separation of concerns.

The imports are properly structured, utilizing specific components and utilities from the @ctrlplane ecosystem.


23-27: LGTM! Well-defined prop types with proper typing.

The type definitions are clear and follow React TypeScript best practices.

apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/runbooks/[runbookId]/RunbookNavBar.tsx (3)

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

The imports are well-organized and the props type is properly defined.


24-28: LGTM! Type-safe parameter extraction.

The URL parameter extraction is well-implemented with proper TypeScript types.


23-78: Verify component integration with RunbookLayout.

Let's verify how this component integrates with the parent layout and ensure proper data flow.

✅ Verification successful

Component integration is properly implemented

The RunbookNavBar is correctly integrated within the RunbookLayout component with:

  • Proper data fetching for total jobs count via TRPC
  • Correct prop passing of totalJobs
  • Appropriate positioning in the layout hierarchy
  • Proper usage of the shared nFormatter utility
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for RunbookNavBar usage and RunbookLayout implementation
rg -t tsx "RunbookNavBar|RunbookLayout" --glob "apps/webservice/src/**"

# Verify the implementation of nFormatter
ast-grep --pattern 'export function nFormatter($_,$_) { $$$ }'

Length of output: 166


Script:

#!/bin/bash
# Search for RunbookNavBar usage and RunbookLayout implementation without file type restriction
rg "RunbookNavBar|RunbookLayout" --type-add 'ts:*.{ts,tsx}' --type ts apps/webservice/src/

# Find files containing nFormatter function
rg "nFormatter" --type-add 'ts:*.{ts,tsx}' --type ts

Length of output: 1758


Script:

#!/bin/bash
# Let's check the layout.tsx implementation to verify proper integration
cat "apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/runbooks/[runbookId]/layout.tsx"

# Also check the nFormatter implementation
cat "apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/_components/nFormatter.ts"

Length of output: 2201

apps/webservice/src/app/[workspaceSlug]/(app)/systems/SystemsBreadcrumb.tsx (3)

32-32: LGTM: Parameter changes are well-structured

The addition of the optional runbookId parameter and its destructuring are implemented correctly following TypeScript best practices.

Also applies to: 35-36


92-100: LGTM: Breadcrumb entry is well-integrated

The new runbook breadcrumb entry follows the existing patterns and is properly integrated into the navigation hierarchy. The path construction and conditional rendering are implemented correctly.


99-99: Verify router configuration for runbook paths

Let's ensure the path structure /${workspaceSlug}/systems/${systemSlug}/runbooks/${runbookId} is properly configured in the router.

✅ Verification successful

The previous command failed due to typescript type not being recognized. Let's try with explicit type definition and also look for the directory structure to verify the routing configuration, as Next.js uses file-system based routing.


Path structure is correctly configured across the application

The path structure /${workspaceSlug}/systems/${systemSlug}/runbooks/${runbookId} is properly configured in the Next.js file-system based routing and consistently used across the application:

  • File structure confirms the route exists: apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/runbooks/[runbookId]
  • Path is consistently used in:
    • Navigation components (AppSidebarContent.tsx, AppSidebarPopoverSystem.tsx)
    • Runbook-specific components (RunbookNavBar.tsx, CreateRunbookForm.tsx)
    • Page components (RunbookGettingStarted.tsx, page.tsx)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for router configuration files and route definitions
rg -t typescript "systems.*runbooks.*" --glob "**/*router*"

Length of output: 96


Script:

#!/bin/bash
# Search for route configurations in app directory structure
fd --type directory "runbooks?" --exec ls -R {} \;

# Search for route patterns in TypeScript/JavaScript files
rg "systems.*runbooks.*" --type-add 'ts:*.{ts,tsx}' --type ts

Length of output: 2080

apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/DeploymentNavBar.tsx (2)

14-14: LGTM! Good refactoring of the formatter.

Moving the nFormatter to a shared location improves code reusability and maintainability.


Line range hint 1-143: Verify runbook integration requirements

Given that this PR is about initializing runbooks UI, should there be a runbook-related navigation item in this deployment navigation bar?

Let's check for any runbook-related patterns in the codebase:

packages/api/src/router/runbook.ts (1)

149-225: Enforce a maximum limit on the limit parameter

The limit parameter defaults to 500 but can be set to any number by the client. To prevent potential performance issues due to large data retrievals, consider enforcing a maximum allowable limit.

[performance_issue]

You might implement it like this:

const MAX_LIMIT = 1000; // Set an appropriate maximum limit
const limit = Math.min(input.limit, MAX_LIMIT);

@@ -0,0 +1,16 @@
export const nFormatter = (num: number, digits: number) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add input validation and type constraints.

The function should handle edge cases like negative numbers, NaN, and Infinity. Also, digits should be constrained to non-negative integers.

-export const nFormatter = (num: number, digits: number) => {
+export const nFormatter = (num: number, digits: number): string => {
+  if (!Number.isFinite(num)) return 'N/A';
+  if (num < 0) return '-' + nFormatter(-num, digits);
+  if (!Number.isInteger(digits) || digits < 0) {
+    throw new Error('digits must be a non-negative integer');
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const nFormatter = (num: number, digits: number) => {
export const nFormatter = (num: number, digits: number): string => {
if (!Number.isFinite(num)) return 'N/A';
if (num < 0) return '-' + nFormatter(-num, digits);
if (!Number.isInteger(digits) || digits < 0) {
throw new Error('digits must be a non-negative integer');
}

Comment on lines 2 to 10
const lookup = [
{ value: 1, symbol: "" },
{ value: 1e3, symbol: "k" },
{ value: 1e6, symbol: "M" },
{ value: 1e9, symbol: "G" },
{ value: 1e12, symbol: "T" },
{ value: 1e15, symbol: "P" },
{ value: 1e18, symbol: "E" },
];
Copy link
Contributor

@coderabbitai coderabbitai bot Nov 29, 2024

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Optimize lookup table definition.

The lookup table is recreated on every function call. Consider moving it outside the function scope.

+const NUMBER_FORMAT_LOOKUP = [
+  { value: 1, symbol: "" },
+  { value: 1e3, symbol: "k" },
+  { value: 1e6, symbol: "M" },
+  { value: 1e9, symbol: "G" },
+  { value: 1e12, symbol: "T" },
+  { value: 1e15, symbol: "P" },
+  { value: 1e18, symbol: "E" },
+] as const;
+
 export const nFormatter = (num: number, digits: number) => {
-  const lookup = [
-    { value: 1, symbol: "" },
-    { value: 1e3, symbol: "k" },
-    { value: 1e6, symbol: "M" },
-    { value: 1e9, symbol: "G" },
-    { value: 1e12, symbol: "T" },
-    { value: 1e15, symbol: "P" },
-    { value: 1e18, symbol: "E" },
-  ];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const lookup = [
{ value: 1, symbol: "" },
{ value: 1e3, symbol: "k" },
{ value: 1e6, symbol: "M" },
{ value: 1e9, symbol: "G" },
{ value: 1e12, symbol: "T" },
{ value: 1e15, symbol: "P" },
{ value: 1e18, symbol: "E" },
];
const NUMBER_FORMAT_LOOKUP = [
{ value: 1, symbol: "" },
{ value: 1e3, symbol: "k" },
{ value: 1e6, symbol: "M" },
{ value: 1e9, symbol: "G" },
{ value: 1e12, symbol: "T" },
{ value: 1e15, symbol: "P" },
{ value: 1e18, symbol: "E" },
] as const;
export const nFormatter = (num: number, digits: number) => {

Copy link
Member

Choose a reason for hiding this comment

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

Do we have a library for this? Do we have this logic somewhere else in the code?

Copy link
Contributor

Choose a reason for hiding this comment

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

@jsbroks, I checked the codebase and didn't find any existing library or similar logic for this number formatting functionality. If we need more advanced or consistent number formatting across the application, we might consider adding a library like numeral or numbro. Otherwise, we can proceed with this implementation as it stands.

🔗 Analysis chain
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for existing number formatting functions similar to 'nFormatter' in the codebase.
rg 'function\s+(nFormatter|numberFormatter|formatNumberWithSuffix)\s*\(' --type=typescript --type=javascript

# Search for any imports of number formatting libraries.
rg 'import\s+.*\s+from\s+[\'"](numeral|numbro|number-format|human-number)[\'"]' --type=typescript --type=javascript

# Search `package.json` and `package-lock.json` for dependencies related to number formatting.
rg '"(numeral|numbro|number-format|human-number)"' package.json package-lock.json

# Search for code that includes similar lookup tables for number formatting.
rg 'value:\s*1e3' --type=typescript --type=javascript

Length of output: 263

Copy link
Member

Choose a reason for hiding this comment

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

Intl.NumberFormat?

Copy link
Contributor

Choose a reason for hiding this comment

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

@jsbroks, great point! We can indeed use Intl.NumberFormat with the compact notation to format numbers with suffixes like 'K', 'M', etc. This provides a more standardized and localized approach. Here's how we could implement it:

export const nFormatter = (num: number) => {
  return new Intl.NumberFormat('en', { notation: 'compact' }).format(num);
};

This will format numbers as:

  • 1000'1K'
  • 1500000'1.5M'

Would you like to proceed with this implementation?

(*^_^)/


✏️ Learnings added
Learnt from: jsbroks
PR: ctrlplanedev/ctrlplane#241
File: apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/_components/nFormatter.ts:2-10
Timestamp: 2024-11-29T04:21:47.818Z
Learning: Use `Intl.NumberFormat` with `compact` notation for number formatting with suffixes like 'K', 'M' instead of custom functions.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines +13 to +22
export async function generateMetadata({
params,
}: PageProps): Promise<Metadata> {
const runbook = await api.runbook.byId(params.runbookId);
if (runbook == null) return notFound();

return {
title: `${runbook.name} | Runbooks`,
};
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add explicit error handling for API calls

The metadata generator should handle potential API errors explicitly to prevent unhandled exceptions from propagating to the UI.

 export async function generateMetadata({
   params,
 }: PageProps): Promise<Metadata> {
-  const runbook = await api.runbook.byId(params.runbookId);
-  if (runbook == null) return notFound();
+  try {
+    const runbook = await api.runbook.byId(params.runbookId);
+    if (runbook == null) return notFound();
 
-  return {
-    title: `${runbook.name} | Runbooks`,
-  };
+    return {
+      title: `${runbook.name} | Runbooks`,
+    };
+  } catch (error) {
+    console.error('Failed to fetch runbook metadata:', error);
+    return {
+      title: 'Error | Runbooks',
+    };
+  }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export async function generateMetadata({
params,
}: PageProps): Promise<Metadata> {
const runbook = await api.runbook.byId(params.runbookId);
if (runbook == null) return notFound();
return {
title: `${runbook.name} | Runbooks`,
};
}
export async function generateMetadata({
params,
}: PageProps): Promise<Metadata> {
try {
const runbook = await api.runbook.byId(params.runbookId);
if (runbook == null) return notFound();
return {
title: `${runbook.name} | Runbooks`,
};
} catch (error) {
console.error('Failed to fetch runbook metadata:', error);
return {
title: 'Error | Runbooks',
};
}
}

Comment on lines +72 to +75
<Button variant="secondary" className="flex items-center gap-2">
<IconBolt className="h-4 w-4" />
Trigger runbook
</Button>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add onClick handler for the trigger button.

The "Trigger runbook" button appears to be missing its click handler implementation.

Example implementation:

-<Button variant="secondary" className="flex items-center gap-2">
+<Button 
+  variant="secondary" 
+  className="flex items-center gap-2"
+  onClick={() => {
+    // TODO: Implement runbook trigger logic
+  }}
+>

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +38 to +77
return (
<div className="flex items-center justify-between border-b p-2">
<div>
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<Link href={baseUrl} legacyBehavior passHref>
<NavigationMenuLink
className="group inline-flex h-9 w-max items-center justify-center rounded-md px-4 py-2 text-sm font-medium transition-colors hover:bg-accent/50 hover:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50"
active={isJobsActive}
>
Jobs
<Badge
variant="outline"
className="ml-1.5 rounded-full text-muted-foreground"
>
{nFormatter(totalJobs, 1)}
</Badge>
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
<NavigationMenuItem>
<Link href={settingsUrl} legacyBehavior passHref>
<NavigationMenuLink
className="group inline-flex h-9 w-max items-center justify-center rounded-md px-4 py-2 text-sm font-medium transition-colors hover:bg-accent/50 hover:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50"
active={isSettingsActive}
>
Settings
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
</div>
<Button variant="secondary" className="flex items-center gap-2">
<IconBolt className="h-4 w-4" />
Trigger runbook
</Button>
</div>
);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance accessibility with ARIA attributes.

Consider adding the following accessibility improvements:

-<div className="flex items-center justify-between border-b p-2">
+<nav 
+  className="flex items-center justify-between border-b p-2"
+  aria-label="Runbook navigation"
+>
   <div>
-    <NavigationMenu>
+    <NavigationMenu aria-label="Runbook sections">

Also, consider adding keyboard navigation support for the navigation menu items.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return (
<div className="flex items-center justify-between border-b p-2">
<div>
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<Link href={baseUrl} legacyBehavior passHref>
<NavigationMenuLink
className="group inline-flex h-9 w-max items-center justify-center rounded-md px-4 py-2 text-sm font-medium transition-colors hover:bg-accent/50 hover:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50"
active={isJobsActive}
>
Jobs
<Badge
variant="outline"
className="ml-1.5 rounded-full text-muted-foreground"
>
{nFormatter(totalJobs, 1)}
</Badge>
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
<NavigationMenuItem>
<Link href={settingsUrl} legacyBehavior passHref>
<NavigationMenuLink
className="group inline-flex h-9 w-max items-center justify-center rounded-md px-4 py-2 text-sm font-medium transition-colors hover:bg-accent/50 hover:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50"
active={isSettingsActive}
>
Settings
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
</div>
<Button variant="secondary" className="flex items-center gap-2">
<IconBolt className="h-4 w-4" />
Trigger runbook
</Button>
</div>
);
return (
<nav
className="flex items-center justify-between border-b p-2"
aria-label="Runbook navigation"
>
<div>
<NavigationMenu aria-label="Runbook sections">
<NavigationMenuList>
<NavigationMenuItem>
<Link href={baseUrl} legacyBehavior passHref>
<NavigationMenuLink
className="group inline-flex h-9 w-max items-center justify-center rounded-md px-4 py-2 text-sm font-medium transition-colors hover:bg-accent/50 hover:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50"
active={isJobsActive}
>
Jobs
<Badge
variant="outline"
className="ml-1.5 rounded-full text-muted-foreground"
>
{nFormatter(totalJobs, 1)}
</Badge>
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
<NavigationMenuItem>
<Link href={settingsUrl} legacyBehavior passHref>
<NavigationMenuLink
className="group inline-flex h-9 w-max items-center justify-center rounded-md px-4 py-2 text-sm font-medium transition-colors hover:bg-accent/50 hover:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50"
active={isSettingsActive}
>
Settings
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
</div>
<Button variant="secondary" className="flex items-center gap-2">
<IconBolt className="h-4 w-4" />
Trigger runbook
</Button>
</nav>
);

const { setJobId } = useJobDrawer();
const allReleaseJobTriggers = api.job.config.byWorkspaceId.list.useQuery(
{ workspaceId },
{ workspaceId, limit: 0 },
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

🛠️ Refactor suggestion

Based on the search results and analysis of the codebase, I can now generate the final response:

Consider using pagination instead of limit: 0 for better performance

Setting limit: 0 to fetch all records is not the optimal approach. The codebase shows that pagination is already implemented in other routers (as seen in resources.ts, system.ts, and release.ts) with a pattern that returns both items and total. This pattern should be used here instead.

  • apps/webservice/src/app/[workspaceSlug]/(app)/(job)/jobs/JobTable.tsx: Replace the unlimited query with a paginated one that only fetches the total count
  • Other components like TargetPageContent.tsx and JobHistoryChart.tsx show similar patterns of using limit: 0 which should also be reviewed

Example implementation:

-const allReleaseJobTriggers = api.job.config.byWorkspaceId.list.useQuery(
-  { workspaceId, limit: 0 },
-  { refetchInterval: 60_000, placeholderData: (prev) => prev },
-);
+const jobsTotal = api.job.config.byWorkspaceId.list.useQuery(
+  { workspaceId, limit: 1, page: 0 },
+  { refetchInterval: 60_000, select: (data) => data.total },
+);
🔗 Analysis chain

Consider optimizing the data fetching strategy

The current implementation fetches all records with limit: 0 when only the total count is needed for the "NoFilterMatch" component. This could be inefficient for large datasets.

Consider these improvements:

  1. Add a separate endpoint to fetch only the count
  2. Or modify the existing endpoint to accept a countOnly parameter
  3. Or use the same query instance with different parameters to avoid potential UI inconsistencies

Example implementation:

-const allReleaseJobTriggers = api.job.config.byWorkspaceId.list.useQuery(
-  { workspaceId, limit: 0 },
-  { refetchInterval: 60_000, placeholderData: (prev) => prev },
-);
+const jobCount = api.job.config.byWorkspaceId.count.useQuery(
+  { workspaceId },
+  { refetchInterval: 60_000, placeholderData: (prev) => prev },
+);

Let's verify if there are any existing count-only endpoints or if other components are using similar patterns:


Let me analyze the results and generate additional verification scripts to check for count-specific endpoints in the codebase.

The results will help us determine if there are existing count endpoints or if we need to suggest creating new ones.


Let me try one more search pattern to look for any count-related implementations.


Let me try one final search to specifically look for the job router implementation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for similar query patterns in the codebase
rg -A 2 "byWorkspaceId.list.useQuery" 

# Search for any count-specific endpoints
ast-grep --pattern 'export const count = $$$'

Length of output: 7204


Script:

#!/bin/bash
# Search for count-related router endpoints
rg -A 5 "router.*count" 

# Search for count-related procedures in TRPC
rg -A 5 "procedure.*count"

# Check if there are any existing count implementations
ast-grep --pattern 'count = async ($$$) => {
  $$$
}'

Length of output: 110


Script:

#!/bin/bash
# Search for count-related implementations with broader patterns
rg -i "count.*=" -A 5

# Look for any pagination or total count related code
rg -i "total.*items|pagination" -A 5

# Check the specific router file for the jobs endpoint
fd -e ts -e tsx "job.*router" --exec cat {}

Length of output: 36347


Script:

#!/bin/bash
# Search for job router implementation
rg -A 10 "job.*router.*=" 

# Search for job-related TRPC procedures
rg -A 10 "job.*procedure" 

# Look for any count-related endpoints in job routes
rg -A 10 "job.*count"

Length of output: 995

Comment on lines +147 to +162
{runbookJobs.items.map((job) => (
<TableRow key={job.job.id} className="cursor-pointer">
<TableCell>
<div className="flex items-center gap-1">
<JobTableStatusIcon status={job.job.status} />
{JobStatusReadable[job.job.status]}
</div>
</TableCell>
<TableCell>
{formatDistanceToNowStrict(job.job.createdAt, {
addSuffix: true,
})}
</TableCell>
<VariableCell variables={job.job.variables} />
</TableRow>
))}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix incorrect property access in job mapping

The properties of job are being accessed incorrectly. Instead of job.job.id, job.job.status, and job.job.variables, you should access job.id, job.status, and job.variables.

Apply this diff to correct the property access:

- {runbookJobs.items.map((job) => (
-   <TableRow key={job.job.id} className="cursor-pointer">
-     <TableCell>
-       <div className="flex items-center gap-1">
-         <JobTableStatusIcon status={job.job.status} />
-         {JobStatusReadable[job.job.status]}
-       </div>
-     </TableCell>
-     <TableCell>
-       {formatDistanceToNowStrict(job.job.createdAt, {
-         addSuffix: true,
-       })}
-     </TableCell>
-     <VariableCell variables={job.job.variables} />
-   </TableRow>
- ))}
+ {runbookJobs.items.map((job) => (
+   <TableRow key={job.id} className="cursor-pointer">
+     <TableCell>
+       <div className="flex items-center gap-1">
+         <JobTableStatusIcon status={job.status} />
+         {JobStatusReadable[job.status]}
+       </div>
+     </TableCell>
+     <TableCell>
+       {formatDistanceToNowStrict(job.createdAt, {
+         addSuffix: true,
+       })}
+     </TableCell>
+     <VariableCell variables={job.variables} />
+   </TableRow>
+ ))}

Committable suggestion skipped: line range outside the PR's diff.

)}
<div className="space-y-2">
{condition.conditions.map((subCond, index) => (
<div key={index} className="flex items-start gap-2">
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid using array index as key in list rendering

Using the array index index as a key in list rendering can lead to issues with component state and may cause unnecessary re-renders if the list changes order. It's better to use a unique identifier for each item if available.

If subCond has a unique identifier, consider using it as the key:

- <div key={index} className="flex items-start gap-2">
+ <div key={getUniqueKeyForCondition(subCond)} className="flex items-start gap-2">

Ensure that getUniqueKeyForCondition returns a unique and stable identifier for each condition.

Committable suggestion skipped: line range outside the PR's diff.

@adityachoudhari26 adityachoudhari26 merged commit 06f2d1b into main Nov 29, 2024
4 of 6 checks passed
@adityachoudhari26 adityachoudhari26 deleted the init-runbooks-ui branch November 29, 2024 05:37
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