Skip to content

Policy Archiving Should not be a Status it should be its own value, in db, and button with confirmation#222

Merged
Marfuen merged 13 commits into
mainfrom
mariano/cleanup
Mar 28, 2025
Merged

Policy Archiving Should not be a Status it should be its own value, in db, and button with confirmation#222
Marfuen merged 13 commits into
mainfrom
mariano/cleanup

Conversation

@Marfuen

@Marfuen Marfuen commented Mar 28, 2025

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Introduced archive and restore options for policies, with clear archived badges and filtering capabilities.
    • Added an inline policy creation modal and improved compliance metrics based on controls.
    • Enhanced employee onboarding by sending email invitations with a refreshed portal invitation template.
    • Updated the policy signature setting, replacing a dropdown with an intuitive toggle switch.
  • Style

    • Refreshed UI elements including charts and cards for improved visual clarity.
  • Chores

    • Temporarily disabled the Notification Center in the header.

@coderabbitai

coderabbitai Bot commented Mar 28, 2025

Copy link
Copy Markdown

Walkthrough

This PR introduces a set of changes that add and refine archiving capabilities for organization policies. A new archive action is implemented with input validation and authorization checks, and the UI is updated to reflect policy archive states. The changes further extend filtering and state management in policy tables, update compliance calculations, and adjust styling in various components. Additionally, the employee onboarding process is reworked to send portal invitations instead of creating default tasks. Database schemas and migrations have been updated accordingly, and a new email template for portal invites has been added.

Changes

File(s) Change Summary
apps/app/src/actions/policies/archive-policy.ts,
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/actions/get-policies.ts,
.../columns.tsx,
.../components/table/components/filterCategories.tsx,
.../components/table/components/filterConfigs.tsx,
.../components/table/hooks/usePoliciesTableContext.tsx,
.../hooks/usePolicies.ts,
.../types/index.ts,
apps/app/src/components/policies/policy-overview.tsx,
apps/app/src/components/policies/sheets/policy-archive-sheet.tsx,
apps/app/src/locales/features/policies.ts,
packages/db/prisma/migrations/*,
packages/db/prisma/schema/policy.prisma,
packages/db/prisma/schema/schema.prisma
Adds archiving/restoration functionality for policies; introduces isArchived and archivedAt fields, new filtering logic, UI updates for archive status, and adjusts database enums and schemas.
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/overview/components/RequirementStatusChart.tsx,
apps/app/src/components/forms/policies/policy-overview.tsx,
apps/app/src/components/header.tsx
Updates styling and UI elements; refactors form controls (switch instead of select) and comments out the NotificationCenter.
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/employees/all/components/table/EmployeesTable.tsx,
apps/app/src/components/tables/people/empty-states.tsx,
apps/app/src/components/tables/people/filter-toolbar.tsx,
apps/app/src/lib/db/employee.ts,
apps/app/src/locales/features/people.ts,
packages/email/emails/invite-portal.tsx
Updates import paths for the EmployeeInviteSheet; shifts employee onboarding from task creation to sending email invitations; adds a new email template for portal invites.
apps/app/src/jobs/tasks/organization/create-organization.ts Updates organization creation tasks to include a userId parameter, processes content placeholders using organization-specific data, and assigns user IDs in control requirements and evidence creation.
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/components/PoliciesList.tsx,
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/components/table/PoliciesTable.tsx
Enhances the policies list and table view by adding a CreatePolicySheet component and new state management (including archived filter state and updated CTA behavior).
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/overview/frameworks/[frameworkId]/components/FrameworkOverview.tsx Changes the compliance percentage calculation from a requirements-based ratio to a controls-based ratio.
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/evidence/hooks/useEvidenceTasks.ts,
apps/app/src/editor/actions/update-policy.ts
Refactors import formatting, removes a redundant console log, and adjusts code readability in policy update functions.

Sequence Diagram(s)

sequenceDiagram
  participant U as User
  participant PO as PolicyOverview
  participant PAS as PolicyArchiveSheet
  participant API as ArchivePolicy API
  participant DB as Database

  U->>PO: Click archive/restore button
  PO->>PAS: Open archive sheet
  PAS->>API: Submit archive/restore request
  API->>DB: Verify user authorization and fetch policy
  DB-->>API: Return policy data/error
  API->>DB: Update `isArchived` and `archivedAt` fields
  DB-->>API: Return update result
  API-->>PAS: Respond with success/failure
  PAS->>PO: Notify user and update UI
Loading
sequenceDiagram
  participant U as User
  participant PT as PoliciesTable
  participant Ctx as PoliciesTableContext
  participant Hook as usePolicies Hook
  participant API as GetPolicies API

  U->>PT: Toggle archived filter
  PT->>Ctx: Update `isArchived` state
  Ctx->>Hook: Trigger data fetch with new filter
  Hook->>API: Request policies with `isArchived` parameter
  API-->>Hook: Return filtered policies
  Hook-->>Ctx: Update context state
  Ctx-->>PT: Render updated policies list
Loading

Possibly related PRs

  • Improvements #124: The changes in the main PR, which introduce archiving functionality for policies, are related to the retrieved PR as both involve modifications to the handling of archived states, specifically through the introduction of the isArchived property in various components and functions.
  • Mariano/stuff #45: The changes in the main PR regarding the introduction of archiving functionality for policies are related to the retrieved PR, which includes a new action for publishing policies, as both involve modifications to policy management and utilize similar structures for handling policy states.

Poem

I'm just a bunny, hopping through code,
Archiving policies in a neat new mode.
Restoring and filtering with a clever little twist,
Inviting employees with a digital kiss.
With every change, my code garden grows,
As I nibble on bugs and watch solutions compose.
Hop on, let's celebrate these lines in prose!

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

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

CodeRabbit Commands (Invoked using PR comments)

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

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

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

Documentation and Community

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

@vercel

vercel Bot commented Mar 28, 2025

Copy link
Copy Markdown

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

Name Status Preview Comments Updated (UTC)
app ✅ Ready (Inspect) Visit Preview 💬 Add feedback Mar 28, 2025 8:05pm
1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
comp-portal ⬜️ Skipped (Inspect) Mar 28, 2025 8:05pm

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (13)
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/evidence/hooks/useEvidenceTasks.ts (1)

54-54: Consider removing console.log statement

There's a console.log statement that outputs evidence task results. While useful during development, consider removing it in production code.

- console.log("Evidence tasks result:", result);
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/types/index.ts (1)

21-21: Type inconsistency between schema and input.

The isArchived field is defined as a boolean in the policySchema but as a string in the policiesInputSchema. While this is likely because inputs often come as strings from query parameters, consider adding a transformation to ensure consistent typing.

-  isArchived: z.string().optional(),
+  isArchived: z.string().optional().transform((val) => 
+    val ? val === 'true' : undefined
+  ),

This would transform string values like "true" to actual boolean values, making the types more consistent across your application.

packages/email/emails/invite-portal.tsx (2)

72-86: Consider adding more context to the invitation link

The link is presented with minimal context. For better usability, you might want to include information about link expiration time (if applicable) or further instructions.

- or copy and paste this URL into your browser{" "}
+ or copy and paste this URL into your browser if the button doesn't work. This link will expire in 24 hours.{" "}

90-94: Security enhancement for email presentation

Including the email in the template is good for clarity, but consider obfuscating part of the email for added security in case the email is forwarded.

- <span className="text-[#121212] ">{email}</span>.{" "}
+ <span className="text-[#121212] ">{email.substring(0, 2)}...{email.substring(email.indexOf('@'))}</span>.{" "}
apps/app/src/actions/policies/archive-policy.ts (2)

48-50: Optimize state change determination

The current implementation doesn't check if the policy is already in the desired state, which could lead to unnecessary database operations.

- const shouldArchive =
-   action === "archive" || (action === undefined && !policy.isArchived);
+ // Determine if we should change the current state
+ const shouldArchive =
+   action === "archive" || (action === undefined && !policy.isArchived);
+ 
+ // Skip update if policy is already in the desired state
+ if (shouldArchive === policy.isArchived) {
+   return {
+     success: true,
+     isArchived: shouldArchive,
+   };
+ }

69-75: Enhance error logging and handling

While the error handling is good, consider including more specific error details in the log and potentially differentiating between different types of errors.

- console.error(error);
+ console.error("Failed to update policy archive status:", error);
  return {
    success: false,
-   error: "Failed to update policy archive status",
+   error: error instanceof Error 
+     ? `Failed to update policy archive status: ${error.message}` 
+     : "Failed to update policy archive status",
  };
apps/app/src/components/policies/policy-overview.tsx (1)

33-60: Add null check for archivedAt

While you do check if archivedAt exists before formatting the date, the conditional expression could be simplified for better readability.

- {policy.archivedAt && (
-   <>
-     {t("policies.archive.archived_on")}{" "}
-     {format(new Date(policy.archivedAt), "PPP")}
-   </>
- )}
+ {policy.archivedAt 
+   ? `${t("policies.archive.archived_on")} ${format(new Date(policy.archivedAt), "PPP")}` 
+   : t("policies.archive.archived_no_date")}
apps/app/src/components/forms/policies/policy-overview.tsx (1)

26-26: Unused import detected

The FormDescription component is imported but not used in this file.

-	FormMessage,
-	FormDescription,
+	FormMessage,
 } from "@bubba/ui/form";
apps/app/src/lib/db/employee.ts (1)

3-8: Improve environment variable validation handling.

The environment variable check is performed at the module level which will cause the app to crash during initialization if NEXT_PUBLIC_PORTAL_URL is missing. This approach prevents the application from starting without the variable, which might be appropriate, but it's worth considering if this check should be moved into the relevant function instead.

Consider moving this check inside the inviteEmployeeToPortal function instead of at module level, which would provide better error handling and allow the rest of the application to function even if this specific feature is unavailable:

-if (!process.env.NEXT_PUBLIC_PORTAL_URL) {
-  throw new Error("NEXT_PUBLIC_PORTAL_URL is not set");
-}

const inviteEmployeeToPortal = async (params: {
  email: string;
  organizationName: string;
  inviteLink: string;
}) => {
+  if (!process.env.NEXT_PUBLIC_PORTAL_URL) {
+    throw new Error("NEXT_PUBLIC_PORTAL_URL is not set");
+  }
  const { email, organizationName, inviteLink } = params;
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/hooks/usePoliciesTableContext.tsx (1)

1-170: LGTM - Well implemented context provider for policies table.

This implementation of the PoliciesTableContext is well structured with proper separation of concerns, good state management, and appropriate error handling when the hook is used outside of its provider. The addition of the archive state is integrated consistently throughout the component.

A few minor improvements to consider:

  1. The policies type is currently any[] - consider using a more specific type to improve type safety:
- policies: any[] | undefined;
+ policies: PolicyType[] | undefined;
  1. Consider using AbortController for cleanup in useEffect instead of setTimeout which could lead to race conditions:
useEffect(() => {
  if (policies && isSearching) {
-   const timer = setTimeout(() => {
-     setIsSearching(false);
-   }, 100);
-   return () => clearTimeout(timer);
+   const controller = new AbortController();
+   const timeoutId = setTimeout(() => {
+     if (!controller.signal.aborted) {
+       setIsSearching(false);
+     }
+   }, 100);
+   return () => {
+     controller.abort();
+     clearTimeout(timeoutId);
+   };
  }
}, [policies, isSearching]);
apps/app/src/jobs/tasks/organization/create-organization.ts (1)

223-231: Handle potential null organization safely.

The code retrieves the organization for placeholder replacement but doesn't handle the case where the organization may not be found.

Add error handling to ensure organization exists before proceeding:

  const organization = await db.organization.findUnique({
    where: {
      id: organizationId,
    },
  });

+  if (!organization) {
+    logger.warn("Organization not found when creating organization policy", {
+      organizationId,
+    });
+  }

  const currentDate = new Date();
  const formattedDate = currentDate.toISOString().split("T")[0]; // YYYY-MM-DD format
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/[policyId]/editor/actions/update-policy.ts (2)

49-49: Consider removing console.log statements for production.

There are several console.log statements throughout the code that appear to be for debugging purposes. These should generally be removed before pushing to production or replaced with proper logging utilities.

-        console.log("CONTENT TYPE:", typeof content);
+        // Remove or replace with proper logging

Similarly for lines 59, 63, and 101.

Also applies to: 59-59, 63-63, 101-101


46-46: Consider using a more specific type for updateData.

Using Record<string, any> reduces type safety. Consider creating a more specific type that reflects the possible fields of the update operation.

-      const updateData: Record<string, any> = {};
+      const updateData: Partial<{
+        content: any;
+        status: string;
+        // Add other possible fields
+      }> = {};
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 45dab10 and 1ad45dc.

📒 Files selected for processing (30)
  • apps/app/src/actions/policies/archive-policy.ts (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/employees/all/components/table/EmployeesTable.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/evidence/hooks/useEvidenceTasks.ts (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/overview/components/RequirementStatusChart.tsx (3 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/overview/frameworks/[frameworkId]/components/FrameworkOverview.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/actions/get-policies.ts (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/components/PoliciesList.tsx (2 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/components/table/PoliciesTable.tsx (4 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/components/table/columns.tsx (3 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/components/table/components/filterCategories.tsx (3 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/components/table/components/filterConfigs.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/components/table/hooks/usePoliciesTableContext.tsx (6 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/hooks/usePolicies.ts (2 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/hooks/usePoliciesTableContext.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/types/index.ts (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/[policyId]/editor/actions/update-policy.ts (1 hunks)
  • apps/app/src/components/forms/policies/policy-overview.tsx (4 hunks)
  • apps/app/src/components/header.tsx (1 hunks)
  • apps/app/src/components/policies/policy-overview.tsx (3 hunks)
  • apps/app/src/components/policies/sheets/policy-archive-sheet.tsx (1 hunks)
  • apps/app/src/components/tables/people/empty-states.tsx (1 hunks)
  • apps/app/src/components/tables/people/filter-toolbar.tsx (1 hunks)
  • apps/app/src/jobs/tasks/organization/create-organization.ts (5 hunks)
  • apps/app/src/lib/db/employee.ts (3 hunks)
  • apps/app/src/locales/features/people.ts (1 hunks)
  • apps/app/src/locales/features/policies.ts (2 hunks)
  • packages/db/prisma/migrations/20250328192535_move_archive_to_its_own_column_in_policies/migration.sql (1 hunks)
  • packages/db/prisma/schema/policy.prisma (1 hunks)
  • packages/db/prisma/schema/schema.prisma (0 hunks)
  • packages/email/emails/invite-portal.tsx (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/db/prisma/schema/schema.prisma
🧰 Additional context used
🧬 Code Definitions (4)
apps/app/src/components/policies/policy-overview.tsx (1)
apps/app/src/components/policies/sheets/policy-archive-sheet.tsx (1)
  • PolicyArchiveSheet (22-160)
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/components/table/components/filterCategories.tsx (1)
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/components/table/components/filterConfigs.tsx (1)
  • ARCHIVED_FILTER (21-25)
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/hooks/usePoliciesTableContext.tsx (2)
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/components/table/hooks/usePoliciesTableContext.tsx (2)
  • PoliciesTableProvider (49-157)
  • usePoliciesTable (159-169)
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/hooks/usePolicies.ts (1)
  • usePolicies (29-68)
apps/app/src/lib/db/employee.ts (1)
packages/email/emails/invite-portal.tsx (1)
  • InvitePortalEmail (23-104)
🔇 Additional comments (68)
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/evidence/hooks/useEvidenceTasks.ts (1)

6-10: Improved import formatting for better readability

The multi-line import style for types from @bubba/db/types improves code readability, especially when importing multiple items.

apps/app/src/locales/features/people.ts (1)

59-60: Improved success message clarity

The updated success message now properly informs users that an email invitation will be sent when adding an employee, which aligns with the new portal invitation workflow.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/components/PoliciesList.tsx (2)

19-19: Added import for CreatePolicySheet component

The new import adds the policy creation sheet component which will be used in the policies list interface.


37-37: Integrated CreatePolicySheet into the PoliciesList component

The CreatePolicySheet component has been added to the PoliciesList UI, enabling users to create new policies within the context of the policies list view.

apps/app/src/components/tables/people/filter-toolbar.tsx (1)

3-3:

Details

✅ Verification successful

Updated import path for EmployeeInviteSheet

The import path has been updated to reflect the new location/name of the employee invite component. This change aligns with the reworked employee onboarding process mentioned in the PR summary.

Make sure this import path change has been consistently applied across all components that use the EmployeeInviteSheet.


🏁 Script executed:

#!/bin/bash
# Verify that all imports of the EmployeeInviteSheet component use the new path
rg -l "invite-user-sheet" --type ts --type tsx

Length of output: 74


Updated Verification Script Needed for EmployeeInviteSheet Import Path

The initial script failed because the specified tsx file type wasn’t recognized by rg. Please run the following updated shell script—which omits explicit file type filters—to verify that the new import path ("invite-user-sheet") is used consistently across the codebase.

Once you verify the output, please confirm that the new import path has been applied to all relevant components.


🏁 Script executed:

#!/bin/bash
# Verify updated import path usage for EmployeeInviteSheet across the codebase.
rg -l "invite-user-sheet"

Length of output: 415


Import path update verified across the codebase

The updated import path, now using the keyword "invite-user-sheet", was found in the following files:

  • apps/app/src/components/tables/people/empty-states.tsx
  • apps/app/src/components/sheets/add-employee-sheet.tsx
  • apps/app/src/components/tables/people/filter-toolbar.tsx
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/employees/all/components/table/EmployeesTable.tsx

This confirms that the import path change for EmployeeInviteSheet has been applied consistently. Please ensure that any future references follow this updated pattern.

apps/app/src/components/tables/people/empty-states.tsx (1)

9-9: Component source has been updated.

The import path for EmployeeInviteSheet has been updated from "invite-user-sheet" to "add-employee-sheet", which seems to align with a refactoring effort across the application. This change is consistent with similar updates in other files.

apps/app/src/components/header.tsx (1)

51-51: Is disabling the NotificationCenter intentional in this PR?

The NotificationCenter component has been commented out, which seems unrelated to the main purpose of this PR (policy archiving). Please verify if this change was intentional or if it was accidentally included in this PR.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/employees/all/components/table/EmployeesTable.tsx (1)

3-3: Component source has been updated.

The import path for EmployeeInviteSheet has been updated from "invite-user-sheet" to "add-employee-sheet", consistent with similar changes in other files. This appears to be part of a broader refactoring effort.

packages/db/prisma/schema/policy.prisma (1)

13-14: Added fields for policy archiving.

These new fields properly implement the PR objective of treating policy archiving as its own value rather than a status. The isArchived boolean field with a default value of false ensures existing policies aren't automatically marked as archived, and the nullable archivedAt timestamp will track when policies were archived.

After this change is merged, make sure to run the appropriate Prisma migration commands to update your database schema.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/types/index.ts (1)

14-14: Added isArchived field to policySchema.

This addition correctly aligns with the Prisma schema changes and allows for representing the archive state of policies in the application.

apps/app/src/locales/features/policies.ts (3)

70-70: Improved code style consistency with trailing comma

Good practice adding the trailing comma after "not-relevant" property. This makes future additions cleaner and reduces diffs when new items are added to this object.


84-99: Well-structured archive functionality strings

The archive object with comprehensive message strings covers all UI aspects of the archiving flow including tooltips, confirmation dialogs, success/error messages, and status indicators. This provides a complete localization solution for the new archiving functionality.


100-102: Appropriate tooltip for edit action

Adding the edit tooltip string complements the archive action and provides a consistent UI experience for policy management actions.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/actions/get-policies.ts (4)

20-20: Added isArchived parameter for policy filtering

Good addition of the isArchived parameter to the destructured parsedInput, which aligns with the PR objective of treating archiving as a separate property.


54-57: Clear implementation of archive filtering logic

The isArchivedFilter implementation correctly handles the boolean conversion from the string parameter and builds the appropriate filter object. The comment also clearly explains the logic.


63-63: Consistent application of archive filter

The archive filter is correctly applied to both the findMany and count queries, ensuring consistent results between the policy list and total count.

Also applies to: 106-106


88-89: Added archived-related fields to query selection

Properly including the isArchived and archivedAt fields in the database query selection ensures the UI has all necessary data to show archive status.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/overview/frameworks/[frameworkId]/components/FrameworkOverview.tsx (1)

66-70: Improved compliance calculation based on controls

The revised calculation methodology properly shifts focus from requirements to controls for determining compliance percentage. The null check for totalControls > 0 prevents division by zero errors, and the Math.round function ensures a clean integer percentage value.

This change makes the compliance metrics more meaningful as it now measures fully compliant controls rather than just counting individual requirements.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/components/table/PoliciesTable.tsx (6)

10-10: Added useQueryState for policy sheet management

Good addition of the useQueryState hook for managing the create policy sheet state in the URL query string, making the UI state shareable via URL.


15-15: Updated create policy sheet state management

The underscore variable naming convention clearly indicates the unused first return value from useQueryState, following good React practices.


29-30: Added isArchived state management

Properly destructuring the isArchived state and its setter from the context ensures consistent archive filtering across components.


41-43: Updated filter count to include archive status

The activeFilterCount calculation now correctly includes the isArchived filter, ensuring the UI accurately reflects all active filters.


50-51: Passed archive state to filter categories

Properly passing isArchived state and setter to the filter categories ensures the archive filter can be managed through the UI.


90-94: Updated CTA button to use sheet instead of redirect

The implementation now correctly uses setCreatePolicySheet instead of direct navigation, providing a more modern UX with an in-page sheet instead of a full page navigation.

The history: "push" option ensures proper navigation history handling when opening the sheet.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/components/table/components/filterConfigs.tsx (1)

21-25: Good implementation of archiving as a separate filter

The new ARCHIVED_FILTER constant aligns well with the PR objective of making policy archiving its own value rather than a status. Moving this from the STATUS_FILTERS array to a standalone constant properly separates the archived state from policy statuses, and using the Archive component for the icon provides clear visual semantics.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/hooks/usePolicies.ts (2)

33-33: Appropriate addition of isArchived parameter

Adding the isArchived parameter with an empty string default value is consistent with how other filter parameters are handled in this function.


44-54: Correctly integrates isArchived parameter in SWR hook and API call

The implementation properly includes the isArchived parameter in both the SWR cache key and the parameters passed to fetchPolicies, ensuring consistent caching and proper filtering of archived policies.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/overview/components/RequirementStatusChart.tsx (2)

37-37: Good transition to semantic color classes

Replacing hardcoded color values with semantic class names (bg-muted, text-muted-foreground, bg-secondary, bg-primary) improves theme consistency and maintainability.

Also applies to: 39-40, 53-53


70-70: Card styling simplification

Simplifying the Card className to just select-none reduces redundancy, assuming the default Card component already provides the necessary styling.

packages/email/emails/invite-portal.tsx (4)

1-16: Well-structured imports with appropriate components

The imports are organized neatly, separating the external react-email components from internal components. This makes the file more readable and maintainable.


17-21: Well-defined Props interface with appropriate types

The Props interface clearly defines the required properties (email and inviteLink) and the optional property (organizationName) with proper typing.


23-71: Good conditional rendering based on organizationName presence

The component effectively handles both cases where organization name is present or absent, providing a personalized message to the recipient.


106-107: Default export matches named export

Having both named and default exports of the same component is a good practice for module flexibility.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/components/table/components/filterCategories.tsx (4)

3-3: Appropriate import for ARCHIVED_FILTER

The import statement now correctly includes the ARCHIVED_FILTER constant, which is used in the new filter category.


11-12: Well-defined props for archived filtering

The interface now includes the necessary properties for managing archived filter state, following the same pattern as other filter states.


22-23: Consistent parameter destructuring

The function parameter destructuring has been updated to include the new props, maintaining consistency with the existing code structure.


39-51: Well-implemented archived filter category

The new filter category follows the same pattern as existing categories, making it consistent and easy to understand. The implementation correctly handles state updates and pagination reset.

apps/app/src/actions/policies/archive-policy.ts (5)

8-11: Well-defined schema with appropriate validation

The zod schema correctly defines the required id property and an optional action property with enum constraints to ensure only valid values are provided.


26-31: Proper authorization check

The function correctly verifies that the user is authorized by checking for the presence of user ID and organization ID before proceeding.


33-46: Good policy existence verification

The function appropriately checks if the policy exists and belongs to the user's organization before attempting to modify it.


52-58: Good data integrity with archivedAt field

The update operation correctly sets both the isArchived flag and the archivedAt timestamp, maintaining data integrity.


60-63: Comprehensive path revalidation

The function revalidates all relevant paths to ensure that the UI is updated appropriately after the change.

apps/app/src/components/policies/policy-overview.tsx (5)

9-9: Consistent icon imports

The icon imports are now properly organized, importing the needed archive and restore icons from Lucide.


13-15: Essential imports for new functionality

The new imports for the PolicyArchiveSheet, date formatting, and routing are essential for the archive functionality.


29-29: Well-integrated state management

The archiveOpen state is managed using useQueryState, consistent with the existing state management pattern for the edit functionality.


67-94: Well-implemented conditional rendering for archive/restore actions

The button group effectively toggles between archive and restore functionality based on the policy's current state, with appropriate tooltips and icons.


116-116: Properly integrated archive sheet component

The PolicyArchiveSheet component is correctly included in the render output, receiving the necessary props.

packages/db/prisma/migrations/20250328192535_move_archive_to_its_own_column_in_policies/migration.sql (1)

1-20: Well-structured migration for archive functionality

The migration properly moves the "archived" concept from being a value in the PolicyStatus enum to dedicated fields (isArchived and archivedAt) in the OrganizationPolicy table. The transaction ensures atomicity, and the approach of creating a new enum, switching columns, and then renaming follows best practices.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/components/table/columns.tsx (4)

6-8: LGTM! Required imports for new archive functionality

The imports for Archive icon, Badge component, and the cn utility function are appropriate for implementing the new archive-related UI elements.


15-16: LGTM! Interface updates for archive properties

The PolicyRow interface correctly adds isArchived and archivedAt properties to support the new archiving functionality.


35-56: Good UI implementation for archived policies

The policy name cell now properly indicates archived status with visual cues - muted text and an "Archived" badge with an icon. This provides clear differentiation for archived policies in the table.


81-97: Good display of archive date information

The implementation correctly shows "Archived on [date]" for archived policies instead of the last updated date, which provides better context to users about when the policy was archived.

apps/app/src/components/forms/policies/policy-overview.tsx (4)

44-44: LGTM! Import for new UI component

Adding the Switch component import is appropriate for the enhanced form field implementation.


46-50: LGTM! Updated policy statuses

The removal of "archived" from policyStatuses is consistent with the database migration changes, properly implementing the move from status-based archiving to field-based archiving.


89-91: LGTM! Simplified defaultValues logic

The defaultValues implementation for isRequiredToSign is now cleaner and directly uses the organizationPolicy property.


293-306: Improved UX with Switch component

Replacing the Select dropdown with a Switch component for the isRequiredToSign boolean property provides a better user experience. The implementation correctly handles the value transformation between UI state and form values.

apps/app/src/components/policies/sheets/policy-archive-sheet.tsx (5)

1-26: LGTM! Well-structured component setup

The imports, props definition, and component structure follow best practices for a React component. The component properly accepts a policy object combining OrganizationPolicy and Policy types.


27-55: Good implementation of hooks and state management

The component effectively uses hooks for internationalization, media queries, and query state. The open state management and router navigation are well-implemented. The useAction hook provides proper handling of success and error cases with appropriate user feedback.


56-62: LGTM! Properly implemented archive/restore action

The handleAction function correctly sends the appropriate action based on the current archive status and passes the required policy ID.


63-108: Well-designed UI content with appropriate feedback

The content section properly handles both archive and restore scenarios with appropriate text and icons. The loading state is well-implemented, and buttons are disabled during execution to prevent duplicate submissions.


110-160: Good responsive implementation

The component correctly renders either a Sheet (desktop) or Drawer (mobile) based on screen size, providing a consistent user experience across devices. Both implementations share the same content while adapting the container to the device type.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/components/table/hooks/usePoliciesTableContext.tsx (5)

15-43: LGTM - Well structured policy table context interface.

The interface definition is well-organized with clear sections for state, setters, data, and actions. The addition of the isArchived property and related setter follows the same pattern as the other filter properties.


53-77: LGTM - Good implementation of the archived filter state.

The implementation properly integrates the isArchived state using useQueryState and passes it to the usePolicies hook for data fetching. This approach is consistent with how other filters are managed.


79-111: LGTM - Properly updated effect dependencies and filter checks.

You've correctly updated the dependencies array for the search parameter tracking effect and the active filter check to include the new isArchived state.


113-120: LGTM - Properly updated filter clearing.

The clearFilters function now correctly resets the isArchived state to null when clearing all filters.


122-150: LGTM - Context value properly updated.

The context value provided to consumers now includes the isArchived state and its setter, making them available to all components that use the usePoliciesTable hook.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/[policyId]/editor/actions/update-policy.ts (2)

87-89: Verify the impact of clearing signedBy array on policy updates.

The code now resets the signedBy array to empty on every policy update. This could potentially erase important signature records when updating other aspects of a policy.

Is this intentional behavior? This change seems related to the PR objective of separating policy archiving from status, but please ensure it aligns with the expected business logic for policy signatures.


10-18: Code readability improvement looks good.

The reformatting of method chaining and function logic has improved the code's readability while maintaining the same functionality. The clear separation of concerns makes the code easier to understand and maintain.

Also applies to: 19-29, 31-44, 46-80, 82-94, 96-107

Comment on lines +116 to +137
const inviteEmployeeToPortal = async (params: {
email: string;
organizationName: string;
inviteLink: string;
}) => {
const { email, organizationName, inviteLink } = params;

await sendEmail({
to: email,
subject: `You've been invited to join ${organizationName || "an organization"} on Comp AI`,
react: InvitePortalEmail({
email,
organizationName,
inviteLink,
}),
});

return {
success: true,
message: "Employee invited to portal",
};
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling and export the invitation function.

The inviteEmployeeToPortal function lacks error handling for the email sending process, and it's defined as a private constant despite being a core utility.

Add error handling and export the function to improve its robustness and reusability:

-const inviteEmployeeToPortal = async (params: {
+export async function inviteEmployeeToPortal(params: {
  email: string;
  organizationName: string;
  inviteLink: string;
}) => {
  const { email, organizationName, inviteLink } = params;

+  try {
    await sendEmail({
      to: email,
      subject: `You've been invited to join ${organizationName || "an organization"} on Comp AI`,
      react: InvitePortalEmail({
        email,
        organizationName,
        inviteLink,
      }),
    });

    return {
      success: true,
      message: "Employee invited to portal",
    };
+  } catch (error) {
+    console.error("Failed to send invitation email:", error);
+    return {
+      success: false,
+      message: "Failed to send invitation email",
+    };
+  }
-};
+}
📝 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 inviteEmployeeToPortal = async (params: {
email: string;
organizationName: string;
inviteLink: string;
}) => {
const { email, organizationName, inviteLink } = params;
await sendEmail({
to: email,
subject: `You've been invited to join ${organizationName || "an organization"} on Comp AI`,
react: InvitePortalEmail({
email,
organizationName,
inviteLink,
}),
});
return {
success: true,
message: "Employee invited to portal",
};
};
export async function inviteEmployeeToPortal(params: {
email: string;
organizationName: string;
inviteLink: string;
}) {
const { email, organizationName, inviteLink } = params;
try {
await sendEmail({
to: email,
subject: `You've been invited to join ${organizationName || "an organization"} on Comp AI`,
react: InvitePortalEmail({
email,
organizationName,
inviteLink,
}),
});
return {
success: true,
message: "Employee invited to portal",
};
} catch (error) {
console.error("Failed to send invitation email:", error);
return {
success: false,
message: "Failed to send invitation email",
};
}
}

Comment on lines +166 to +170
await inviteEmployeeToPortal({
email: params.email,
organizationName: organization?.name || "an organization",
inviteLink: `${process.env.NEXT_PUBLIC_PORTAL_URL}`,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add a unique token to the invitation link.

The current invitation link doesn't include any employee-specific identification, which is required for a secure and personalized invitation process.

Update the inviteLink to include an employee identifier or token:

await inviteEmployeeToPortal({
  email: params.email,
  organizationName: organization?.name || "an organization",
-  inviteLink: `${process.env.NEXT_PUBLIC_PORTAL_URL}`,
+  inviteLink: `${process.env.NEXT_PUBLIC_PORTAL_URL}/invite?email=${encodeURIComponent(params.email)}&orgId=${params.organizationId}`,
});

Additionally, consider creating a separate token with expiration for improved security.

📝 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
await inviteEmployeeToPortal({
email: params.email,
organizationName: organization?.name || "an organization",
inviteLink: `${process.env.NEXT_PUBLIC_PORTAL_URL}`,
});
await inviteEmployeeToPortal({
email: params.email,
organizationName: organization?.name || "an organization",
inviteLink: `${process.env.NEXT_PUBLIC_PORTAL_URL}/invite?email=${encodeURIComponent(params.email)}&orgId=${params.organizationId}`,
});

Comment on lines +250 to +268
/**
* Processes content placeholders by converting to string, replacing values, and converting back
*/
function processContentPlaceholders(
content: InputJsonValue[],
organizationName: string,
formattedDate: string
): InputJsonValue[] {
// Convert the content to a string
const contentString = JSON.stringify(content);

// Replace all occurrences of the placeholders
const processedString = contentString
.replace(/{{organization}}/g, organizationName)
.replace(/{{date}}/g, formattedDate);

// Convert back to an object
return JSON.parse(processedString);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling to content placeholder processing.

The current implementation of processContentPlaceholders lacks error handling for potential JSON parsing errors, which could occur if the replacements introduce invalid JSON.

Add try-catch block to handle potential JSON parsing errors:

function processContentPlaceholders(
  content: InputJsonValue[],
  organizationName: string,
  formattedDate: string
): InputJsonValue[] {
+ try {
    // Convert the content to a string
    const contentString = JSON.stringify(content);

    // Replace all occurrences of the placeholders
    const processedString = contentString
      .replace(/{{organization}}/g, organizationName)
      .replace(/{{date}}/g, formattedDate);

    // Convert back to an object
    return JSON.parse(processedString);
+ } catch (error) {
+   logger.error("Error processing content placeholders", {
+     error,
+     organizationName,
+   });
+   // Return the original content if parsing fails
+   return content;
+ }
}

Additionally, consider a more robust approach for complex replacements:

function safeReplace(str, placeholder, value) {
  // Escape any special characters that could break JSON
  const safeValue = value.replace(/[\\"\u0000-\u001F\u007F-\u009F]/g, 
    char => "\\u" + ("0000" + char.charCodeAt(0).toString(16)).slice(-4));
  return str.replace(new RegExp(`{{${placeholder}}}`, 'g'), safeValue);
}

@vercel vercel Bot temporarily deployed to Preview – comp-portal March 28, 2025 20:02 Inactive

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 (3)
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/(overview)/page.tsx (2)

129-138: ⚠️ Potential issue

Status handling for archived policies needs updating.

While the query for counting archived policies has been updated to use isArchived: true, the status handling logic still looks for status === "archived". This inconsistency could lead to inaccurate counts in the assignee chart.

Apply this fix:

const status = policy.status as
  | "published"
  | "draft"
- | "archived"
  | "needs_review";
if (status === "published") ownerData.published += 1;
else if (status === "draft") ownerData.draft += 1;
-else if (status === "archived") ownerData.archived += 1;
else if (status === "needs_review") ownerData.needs_review += 1;

+// Check isArchived property separately from status
+if (policy.isArchived) ownerData.archived += 1;

109-111: 💡 Verification agent

🧩 Analysis chain

Update policy object type to include isArchived property.

The policiesByAssigneeStatus items need to include the isArchived property to properly count archived policies in the status chart.


🏁 Script executed:

#!/bin/bash
# Check if the query for policiesByAssigneeStatus includes isArchived field
rg "isArchived.*select:" -A 20 -g "*.tsx" | grep -A 20 "policiesByAssigneeStatus"

Length of output: 81


I'm not seeing any evidence that the isArchived field is being selected with the current query. To better verify whether the Policy type already includes (or is missing) the isArchived property, please run an additional search. For example, you could check for the Policy interface definitions across the codebase:

This will help confirm whether the isArchived property is defined. Once you review the output, we can determine if the type update is still required.


🏁 Script executed:

#!/bin/bash
# Search for 'interface Policy' definitions and check if the isArchived property is present
rg "interface Policy" -n -A 30 .

Length of output: 45774


Action Required: Update the Policy Type Definition

The UI relies on the isArchived property (as seen in the table columns and status chart components) to correctly handle archived policies. However, our current Policy interface in packages/db/prisma/seedTypes.ts does not include this property. Please update the Policy object type to add the isArchived (and if needed, a corresponding archivedAt) field and ensure that any queries populating policiesByAssigneeStatus include it.

  • File to update: packages/db/prisma/seedTypes.ts (and any related types where Policy is defined)
  • Action: Add the isArchived: boolean property and update related queries/mutations accordingly.
apps/app/src/components/policies/charts/policies-by-assignee.tsx (1)

23-28: 💡 Verification agent

🧩 Analysis chain

Consider updating policyStatus object.

The policyStatus object still contains "archived" as a status type, but according to the PR objective, archiving is now a separate property rather than a status. You might want to update this object to reflect the new architecture.


🏁 Script executed:

#!/bin/bash
# Check if "archived" is still used as a status elsewhere in the codebase
rg "status\s*===\s*['\"]archived['\"]" --type=ts --type=tsx

Length of output: 93


The previous command errored out due to an unrecognized file type. Please run the above shell script to verify whether any instances of status === 'archived' still exist anywhere in the codebase. If the search confirms that archived isn’t used as a status (i.e. no matches returned), then the policyStatus object should be updated by removing the archived key to align with the new architecture where archiving is handled as a separate property.

Once you verify that there are no other dependencies on "archived" as a status, proceed with updating the code accordingly.


🏁 Script executed:

#!/bin/bash
# Re-run the search for occurrences of "archived" used as a status check
# Applying a glob pattern to cover both .ts and .tsx files.
rg "status\s*===\s*['\"]archived['\"]" --glob "*.ts*"

Length of output: 461


Action Required: Review and Align Archived Policy Handling Across Modules

  • The policyStatus object in apps/app/src/components/policies/charts/policies-by-assignee.tsx still defines an "archived" key.
  • The PR objective indicates that archiving should be managed as a separate property rather than as part of the status.
  • Our search reveals that some parts of the codebase—specifically in apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/(overview)/page.tsx—are still checking for status === "archived".
  • Please verify the intended design for handling archived policies and update the code consistently. This may involve:
    • Removing the "archived" key from the policyStatus object in the policies charts.
    • Updating any other modules (e.g., the policies overview page) that still use "archived" as a status to reflect the new architecture.
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1ad45dc and 3656ab7.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (5)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/(overview)/page.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[vendorId]/actions/update-vendor-inherent-risk.ts (2 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[vendorId]/actions/update-vendor-residual-risk.ts (2 hunks)
  • apps/app/src/components/mobile-menu.tsx (1 hunks)
  • apps/app/src/components/policies/charts/policies-by-assignee.tsx (2 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
apps/app/src/components/mobile-menu.tsx (1)
apps/app/src/components/main-menu.tsx (1)
  • MainMenu (105-247)
🔇 Additional comments (7)
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[vendorId]/actions/update-vendor-inherent-risk.ts (2)

3-3: Import path updated from local to package reference.

The import statement has been changed from @/db to @bubba/db, which appears to be part of a broader restructuring to use a more centralized database module.


29-30: Improved error handling readability.

Breaking the error handling ternary operator across multiple lines enhances code readability.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[vendorId]/actions/update-vendor-residual-risk.ts (2)

7-7: Import path updated from local to package reference.

The import statement has been changed from @/db to @bubba/db, consistent with the same change in other files.


28-29: Improved error handling readability.

Breaking the error handling ternary operator across multiple lines enhances code readability.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/(overview)/page.tsx (1)

68-73: Update query for archived policies to match new data model.

The query now correctly uses isArchived: true instead of status: "archived" to identify archived policies, which aligns with the PR's objective of moving archive state from status to its own field.

apps/app/src/components/policies/charts/policies-by-assignee.tsx (2)

48-48: Filtering condition updated correctly.

The change from policy.status === "archived" to policy.isArchived correctly implements the PR objective of making archiving a separate property rather than a status.


224-224: Database selection properly updated.

Adding isArchived to the select statement ensures the property is available for the updated filtering logic.

organizationId={organizationId}
userIsAdmin={isAdmin}
/>
<MainMenu organizationId={organizationId} userIsAdmin={isAdmin} />

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

❓ Verification inconclusive

Potential UX regression: mobile menu might not close on item selection.

The removal of the onSelect prop (previously onSelect={() => setOpen(false)}) means the mobile menu may no longer automatically close when a user selects a menu item. This could create a suboptimal user experience where users must manually close the menu after navigation.

Consider restoring the auto-close behavior:

-<MainMenu organizationId={organizationId} userIsAdmin={isAdmin} />
+<MainMenu organizationId={organizationId} userIsAdmin={isAdmin} onSelect={() => setOpen(false)} />

🏁 Script executed:

#!/bin/bash
# Check if MainMenu component still accepts onSelect prop
ast-grep --pattern $'export function MainMenu({ $props }) { $$$1 }' apps/app/src/components/main-menu.tsx

Length of output: 106


I'll run an additional check to see if the MainMenu component still references or destructures an onSelect prop. This additional verification will help determine whether the auto-close behavior was removed accidentally. For example, we can search for "onSelect" within the MainMenu file:

Please run this script manually (or share its output) to confirm if the MainMenu component still accepts an onSelect prop. If no references to onSelect are found, it’s likely that the mobile menu auto-close behavior no longer exists, and you should restore the behavior as specified:

-<MainMenu organizationId={organizationId} userIsAdmin={isAdmin} />
+<MainMenu organizationId={organizationId} userIsAdmin={isAdmin} onSelect={() => setOpen(false)} />

Action Required:

  • Verify manually whether the MainMenu component in apps/app/src/components/main-menu.tsx handles the onSelect prop.
  • If it does not, please restore the auto-close behavior by adding the onSelect={() => setOpen(false)} prop.

🏁 Script executed:

#!/bin/bash
# Search for any usage of "onSelect" within MainMenu to verify if it supports the onSelect prop.
rg "onSelect" apps/app/src/components/main-menu.tsx

Length of output: 173


Action Required: Restore Mobile Menu Auto-Close Functionality

After verifying via the absence of any "onSelect" references in the MainMenu component, it appears that the component no longer supports an auto-close behavior. To ensure the mobile menu closes on item selection, please restore the previously removed behavior by reintroducing the onSelect prop.

  • Affected File: apps/app/src/components/mobile-menu.tsx at line 35
  • Required Change:
-<MainMenu organizationId={organizationId} userIsAdmin={isAdmin} />
+<MainMenu organizationId={organizationId} userIsAdmin={isAdmin} onSelect={() => setOpen(false)} />

Please verify manually in the MainMenu component if any handling for onSelect exists. If not, applying this change will restore the intended UX behavior.

📝 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
<MainMenu organizationId={organizationId} userIsAdmin={isAdmin} />
<MainMenu organizationId={organizationId} userIsAdmin={isAdmin} onSelect={() => setOpen(false)} />

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.

1 participant