Skip to content

Progress on vendors page and locatlization - #167

Merged
claudfuen merged 17 commits into
mainfrom
claudio/misc-fixes-2
Mar 21, 2025
Merged

claudfuen merged 17 commits into
mainfrom
claudio/misc-fixes-2

Conversation

@claudfuen

@claudfuen claudfuen commented Mar 21, 2025 •

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Introduced enhanced vendor management screens including dashboards, registration forms, and detailed vendor task views.
    • Added a dedicated unauthorized access page for improved user guidance.
    • Rolled out a new tool to audit translation usage for enhanced localization quality.
  • Refactor

    • Streamlined text and labels across dashboards and charts, replacing legacy risk-related terminology with vendor-specific language.
    • Consolidated localization resources for clearer and more maintainable user interface translations.
  • Chores

    • Updated configuration settings to support experimental authentication enhancements and improved version control.

claudfuen and others added 15 commits March 20, 2025 15:05
- Introduced a new `UnauthorizedPage` component for handling unauthorized access, featuring localized messages.
- Updated the `en.ts` locale file with translations for unauthorized access errors.
- Enabled experimental `authInterrupts` in the Next.js configuration for improved authentication handling.
- Removed unused imports for `db` and `cache` in the layout component.
- Improved code clarity by streamlining the import statements.
- Renamed `RiskManagement` to `VendorManagement` for clarity.
- Added `CreateVendor` component for vendor creation with form validation.
- Introduced `createVendorAction` for handling vendor creation logic with type-safe server actions.
- Updated locale file with new translations for vendor-related terms and messages.
- Enhanced the UI to display a message when no vendors are found, prompting users to add a vendor.
- Refactored database queries to count vendors instead of risks.
…oved type safety

- Exported `VendorRegisterTableRow` type for better reusability.
- Updated `columns` definition in `RiskRegisterColumns` to use `VendorRegisterTableRow` type.
- Enhanced link structure in the vendor name cell for improved routing.
- Cleaned up commented-out code for better readability.
… and functionality

- Added `Layout` component for vendor-specific pages, ensuring user authentication and organization validation.
- Created `RiskPage` for displaying risk details, including charts and task management features.
- Introduced `RiskComments` component for managing risk-related comments.
- Implemented task management features with dedicated pages for task details and attachments.
- Established search parameters for tasks to enhance filtering capabilities.
- Developed `VendorRegisterTable` and associated components for vendor registration and management.
- Refactored existing components to improve type safety and code organization.
- Updated script name from `find-unused-translations` to `analyze-locale-usage` in `package.json`.
- Deleted the `find-unused-translations.ts` file as it is no longer needed.
- Deleted the `converter.ts`, `editor.ts`, and `theme.ts` files as they contained unused translations.
- Removed references to the `editor` and `theme` translations from the `en.ts` file to streamline the translation structure.
- Introduced a new `theme.ts` file to define theme options (light, dark, system) for localization.
- Updated `ThemeSwitch` component to utilize the new theme localization structure for improved clarity and maintainability.
- Added configurable search directories for translation key usage analysis.
- Updated grep commands to exclude node_modules and support multiple source directories.
- Improved regex patterns to match files in both src and portal directories.
@vercel

vercel Bot commented Mar 21, 2025 •

Copy link
Copy Markdown

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

Name Status Preview Comments Updated (UTC)
app ❌ Failed (Inspect) Mar 21, 2025 7:10pm
comp-portal ✅ Ready (Inspect) Visit Preview 💬 Add feedback Mar 21, 2025 7:10pm

@CLAassistant

CLAassistant commented Mar 21, 2025 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@coderabbitai

coderabbitai Bot commented Mar 21, 2025 •

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

Walkthrough

This pull request introduces a range of updates affecting configuration files, UI components, and localization. Changes include additions to the .gitignore, a new experimental Next.js config flag, and a new locale analysis script. Multiple pages have their metadata translation keys updated, while risk management components are refactored into vendor management components with new pages, interfaces, and actions, and obsolete files removed. New table components and chart updates for vendor tasks have been added, and the localization system has been overhauled with new modular constants and expanded language support.

Changes

Files Change Summary
apps/app/.gitignore
apps/app/next.config.ts
apps/app/package.json
Added two ignore entries for translation/converter files; introduced experimental authInterrupts: true flag in Next.js config; added an analyze-locale-usage script.
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/**/*page.tsx Updated metadata functions to use new translation keys (e.g. from sub_pages.* to simplified keys for people, policies, risk, tests, etc.).
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/** Refactored risk management to vendor management: renamed components (e.g. RiskManagement → VendorManagement), replaced data retrieval functions, updated UI prompts, removed obsolete risk pages and actions, and added new vendor pages (including VendorPage) and server actions.
apps/app/src/components/tables/vendor-tasks/**
apps/app/src/components/vendors/charts/**
apps/app/src/components/vendors/vendor-overview.tsx
Introduced new vendor task table components (columns, DataTable, empty states, filter toolbar, server columns) and updated vendor charts and overview component using revised translation keys.
apps/app/src/locales/** Added and restructured localization constants across modules (auth, onboarding, core, features, layout, settings, etc.) with new error messages, vendor strings, and language support (en, es, fr, no, pt).
apps/app/src/app/[locale]/unauthorized.tsx Introduced a new UnauthorizedPage component for handling access control.
apps/portal/src/app/components/theme-switch.tsx Modified translation logic in theme switch to use explicit conditional checks for “dark”, “light”, and “system” options.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant VendorForm
  participant createVendorAction
  participant Database

  User->>VendorForm: Fill & submit vendor details
  VendorForm->>createVendorAction: Trigger vendor creation
  createVendorAction->>Database: Insert vendor record
  Database-->>createVendorAction: Return created vendor data
  createVendorAction-->>VendorForm: Send success response
  VendorForm-->>User: Display confirmation message
Loading
sequenceDiagram
  participant Browser
  participant VendorPage
  participant Database
  participant UnauthorizedPage

  Browser->>VendorPage: Request vendor page (with vendorId)
  VendorPage->>Database: Query vendor and user information
  alt Vendor found and authorized
    Database-->>VendorPage: Return vendor details
    VendorPage-->>Browser: Render vendor overview and tasks
  else Vendor missing or unauthorized
    VendorPage-->>Browser: Redirect to UnauthorizedPage
    UnauthorizedPage-->>Browser: Display access error UI
  end
Loading

Poem

I’m a bunny who hops through rows and keys,
Coding changes bloom like clover in the breeze.
Risk becomes vendor with a blink and a skip,
New pages and actions on each data trip.
With translations refreshed and features so neat,
I nibble on bugs and celebrate each beat!
Hoppy code days make my circuits complete!


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 859630a and adcd868.

⛔ Files ignored due to path filters (1)
  • apps/app/languine.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/[policyId]/page.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/[riskId]/comments/page.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/[riskId]/page.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/[riskId]/tasks/[taskId]/page.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/tests/all/[testId]/page.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/(overview)/page.tsx (3 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[vendorId]/layout.tsx (2 hunks)
  • apps/app/src/components/risks/charts/risks-by-department.tsx (1 hunks)
  • apps/app/src/components/risks/charts/risks-by-status.tsx (1 hunks)
  • apps/app/src/components/vendors/charts/vendors-by-category.tsx (1 hunks)
  • apps/app/src/components/vendors/charts/vendors-by-status.tsx (1 hunks)
  • apps/app/src/locales/es.ts (2 hunks)
  • apps/app/src/locales/fr.ts (2 hunks)
  • apps/app/src/locales/no.ts (2 hunks)
  • apps/app/src/locales/pt.ts (2 hunks)
✨ 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 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.

- Added new onboarding translations for organization switching and member statuses.
- Implemented organization member invitation logic with email notifications.
- Updated organization change action to include authorization checks.
- Refactored components for better organization management, including `OrgMenu` and `OrganizationSwitcher`.
- Improved error handling and user feedback during organization-related actions.
- Optimized API key retrieval logic for better session management.

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

🔭 Outside diff range comments (1)
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/register/components/table/VendorRegisterFilters.tsx (1)

30-42: 🛠️ Refactor suggestion

Filter logic using risk-specific enumeration

This filter logic is still using RiskStatus for generating filter options, which isn't aligned with the vendor management context of the renamed component.

Update the filter generation to use vendor-specific status values:

- Object.values(RiskStatus).map((filter) => ({
+ Object.values(VendorStatus).map((filter) => ({

You'll also need to create and define the VendorStatus enum if it doesn't already exist in your types.

🧹 Nitpick comments (30)
apps/app/src/locales/features/risk.ts (1)

18-25: Consider consolidating duplicate table definitions.

There appears to be duplicate table column definitions. The vendor.register.table (lines 18-25) and vendor.table (lines 46-51) objects contain identical properties. Consider consolidating these into a single reusable definition to avoid potential maintenance issues if one section gets updated but the other is missed.

For example, you could refactor like this:

export const risk = {
  risks: "risks",
  overview: "Overview",
  create: "Create New Risk",
  vendor: {
    title: "Vendor Management",
    dashboard: {
      // ...existing code
    },
+   table: {
+     name: "Name",
+     category: "Category",
+     status: "Status",
+     owner: "Owner",
+   },
    register: {
      title: "Vendor Register",
-     table: {
-       name: "Name",
-       category: "Category",
-       status: "Status",
-       owner: "Owner",
-     },
    },
    assessment: {
      // ...existing code
    },
    form: {
      // ...existing code
    },
-   table: {
-     name: "Name",
-     category: "Category",
-     status: "Status",
-     owner: "Owner",
-   },
    // ...remaining code
  },
} as const

Also applies to: 46-51

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

40-44: Consider consolidating duplicate status definitions.

There are two separate status definition objects in the file: one in table.statuses (lines 40-44) and another at the top level in status (lines 57-61). The top-level status object includes additional states (needs_review) not present in the table definition.

Consider consolidating these into a single status definition to ensure consistency and easier maintenance.

Also applies to: 57-61


5-7: Consider consistent naming for dashboard metrics.

The naming convention for dashboard metrics could be more consistent:

  • Line 6: policy_status (singular "policy")
  • Lines 7-8: policies_by_assignee and policies_by_framework (plural "policies")

Consider standardizing either all singular or all plural for better consistency.


1-73: Consider restructuring some top-level properties into logical groups.

Some properties like title, create_new, search_placeholder, etc. are defined at the root level while others are grouped into logical sections like dashboard, overview, etc. For better maintainability, consider organizing all properties into logical groups.

For example, properties like title, policies, and create_new could be grouped under a new general or common section.

apps/app/src/locales/core/common.ts (3)

63-68: Consider removing duplicate string in empty states descriptions

There appears to be duplicate strings in the empty states section:

  • description and description_filters have identical values
  • title and title_tasks could potentially be consolidated with a parameter
  empty_states: {
    no_results: {
      title: "No results found",
      title_tasks: "No tasks found",
-     description: "Try another search, or adjusting the filters",
-     description_filters: "Try another search, or adjusting the filters",
+     description: "Try another search, or adjusting the filters",
      description_no_tasks: "Create a task to get started",
    },

93-96: Inconsistent naming pattern in upload section

There's inconsistent naming between dropFileHere and dropFileHereAlt. If they serve different purposes, consider using more descriptive names that indicate their specific use cases.


102-103: Empty object in fileUrl section

The fileUrl section is defined but contains no properties. Consider either removing this empty section or adding a comment explaining why it's reserved for future use.

-    fileUrl: {
-    },
+    // fileUrl section reserved for future implementation
apps/app/src/locales/settings/settings.ts (1)

72-74: Consider expanding the billing section

The billing section currently only contains a title. Since other sections are quite detailed, this might indicate incomplete implementation. Consider adding more localization strings for billing-related UI elements if they exist in the application.

apps/app/src/locales/core/language.ts (1)

7-13: Well-defined language mapping

The languages constant appropriately maps language codes to their English names. Consider using standardized locale codes if you plan to expand language support in the future (e.g., "nb" for Norwegian Bokmål is sometimes preferred over "no").

 export const languages = {
   en: "English",
   es: "Spanish",
   fr: "French",
-  no: "Norwegian",
+  nb: "Norwegian",
   pt: "Portuguese",
 } as const 
apps/app/src/locales/features/tests.ts (1)

46-46: Minor: Consider removing this empty line for consistency.

There's an unnecessary empty line between fields in the register section.

    success: "Test created successfully",
-
    title_field: {
apps/app/src/components/tables/vendor-tasks/empty-states.tsx (1)

39-54: NoTasks component with unused prop

The component correctly displays a message when there are no tasks, but the isEmpty prop doesn't appear to be used in the component's logic. Consider removing it if it's not needed, or use it to conditionally render content.

-export function NoTasks({ isEmpty }: { isEmpty: boolean }) {
+export function NoTasks() {
   const t = useI18n();

   return (
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[vendorId]/layout.tsx (1)

22-32: Consider destructuring vendorId properly

While the vendor fetching logic works, the extraction of vendorId could be improved for consistency.

-  const vendorId = await params;
-  const vendor = await db.vendor.findUnique({
-    where: {
-      id: vendorId.vendorId,
-      organizationId: orgId,
-    },
-  });
+  const { vendorId } = await params;
+  const vendor = await db.vendor.findUnique({
+    where: {
+      id: vendorId,
+      organizationId: orgId,
+    },
+  });

Additionally, consider including the organization ID in the redirect path:

-    redirect("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/vendors/register");
+    redirect(`/${orgId}/vendors/register`);
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/register/components/table/VendorRegisterColumns.tsx (1)

38-58: Consider displaying a clear label for unassigned owners.

Currently, the fallback logic gracefully handles null owner fields, but it might be beneficial to show "Unassigned" or similar to improve clarity.

Possible approach:

  <p className="text-sm font-medium">
-    {row.original.owner?.name}
+    {row.original.owner?.name ?? "Unassigned"}
  </p>
apps/app/src/locales/features/vendors.ts (1)

1-84: Well-structured localization object with comprehensive strings.

The vendors object is well-organized with logical grouping of related strings, covering all aspects of vendor management from form labels to empty states and task management. The naming conventions are consistent (using snake_case), and the as const assertion ensures type safety.

However, I notice there are only two status values defined in the status section (lines 80-83): not_assessed and in_progress. Depending on the application's requirements, you might need additional status values like "completed" or "approved" to match those used in other parts of the code.

Consider expanding the status section to include all possible vendor statuses used throughout the application to ensure consistent localization. For example:

  status: {
    not_assessed: "Not Assessed",
    in_progress: "In Progress",
+   completed: "Completed",
+   approved: "Approved",
  },
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/actions/create-vendor-action.ts (1)

26-28: Consider additional permission checks.

The code verifies that the user belongs to an organization but doesn't check if they have permission to create vendors.

Consider adding permission checks to ensure the user has the appropriate rights to create vendors:

  if (!session?.user?.organizationId) {
    throw new Error("Unauthorized");
  }
+
+  // Verify user has permission to create vendors
+  const hasPermission = await checkUserPermission(session.user.id, 'create_vendor');
+  if (!hasPermission) {
+    throw new Error("Insufficient permissions to create vendors");
+  }
apps/app/src/components/vendors/vendor-overview.tsx (2)

17-69: Reduce duplicated formatting logic by creating a utility function.

The vendor category and status formatting logic is duplicated in two places.

Extract the formatting logic into a utility function to improve maintainability:

export function VendorOverview({ vendor, users }: VendorOverviewProps) {
  const t = useI18n();

+  // Utility function to format enum values from snake_case to Title Case
+  const formatEnumValue = (value: string) => {
+    return value
+      .toLowerCase()
+      .split("_")
+      .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
+      .join(" ");
+  };

  return (
    <Card>
      <CardHeader>
        <CardTitle>{vendor.name}</CardTitle>
      </CardHeader>
      <CardContent>
        <div className="grid gap-4">
          <div className="grid gap-2">
            <Label>{t("vendors.form.vendor_description")}</Label>
            <p className="text-sm text-muted-foreground">
              {vendor.description || t("vendors.empty_states.no_results.description")}
            </p>
          </div>
          <Separator />
          <div className="grid gap-2">
            <Label>{t("vendors.form.vendor_category")}</Label>
            <p className="text-sm text-muted-foreground">
-              {vendor.category
-                .toLowerCase()
-                .split("_")
-                .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
-                .join(" ")}
+              {formatEnumValue(vendor.category)}
            </p>
          </div>
          <Separator />
          <div className="grid gap-2">
            <Label>{t("vendors.form.vendor_status")}</Label>
            <p className="text-sm text-muted-foreground">
-              {vendor.status
-                .toLowerCase()
-                .split("_")
-                .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
-                .join(" ")}
+              {formatEnumValue(vendor.status)}
            </p>
          </div>

58-63: Address commented-out SelectUser component.

There's a commented-out SelectUser component that suggests owner selection functionality was planned but not implemented.

Either implement the owner selection functionality or remove the commented code to keep the codebase clean:

          <div className="grid gap-2">
            <Label>{t("vendors.table.owner")}</Label>
-            {/* <SelectUser
-              users={users}
-              selectedId={vendor.owner?.id}
-              isLoading={false}
-              onSelect={() => {}}
-            /> */}
+            <p className="text-sm text-muted-foreground">
+              {vendor.owner?.name || t("vendors.empty_states.no_results.description")}
+            </p>
          </div>
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/register/VendorRegisterTable.tsx (1)

71-81: Commenting style maintains code history.

Although commented out, the code correctly updates the reference from RiskRegisterFilters to VendorRegisterFilters, maintaining consistency with the vendor management workflow. Consider either removing the commented code or adding a TODO comment explaining why it's preserved.

apps/app/src/components/tables/vendor-tasks/server-columns.tsx (1)

20-88: Well-structured server-side column definitions with proper i18n support.

The getServerColumnHeaders function correctly implements server-side internationalization and provides comprehensive column definitions with appropriate cell renderers for each data type. The status, date, and owner columns include proper formatting and fallback handling.

Consider extracting common formatting logic.

The status text formatting logic (lines 47-53) is duplicated in both client and server components. Consider extracting this into a shared utility function.

+// In a shared utility file like utils/formatting.ts
+export function formatStatus(status: string): string {
+  return status
+    .toLowerCase()
+    .split("_")
+    .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
+    .join(" ");
+}

// Then in both column files:
-{status
-  .toLowerCase()
-  .split("_")
-  .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
-  .join(" ")}
+{formatStatus(status)}
apps/app/src/components/tables/vendor-tasks/columns.tsx (1)

22-92: Consider reducing duplication between client and server column definitions.

The useColumns function provides well-structured client-side column definitions with proper i18n support. However, there's significant duplication between this file and server-columns.tsx. Consider creating a shared factory function to generate the column definitions, with only the i18n implementation differing between client and server versions.

+// In a shared file, e.g., column-factory.ts
+import type { VendorTaskStatus } from "@bubba/db/types";
+import type { ColumnDef } from "@tanstack/react-table";
+import { format } from "date-fns";
+
+export function createVendorTaskColumns(t: (key: string) => string): ColumnDef<VendorTaskType>[] {
+  return [
+    {
+      accessorKey: "title",
+      header: t("vendors.tasks.columns.title"),
+    },
+    // ... other columns
+  ];
+}

// Then in client columns.tsx:
export function useColumns() {
  const t = useI18n();
-  const columns: ColumnDef<VendorTaskType>[] = [ ... ];
+  return createVendorTaskColumns(t);
-  return columns;
}

// And in server-columns.tsx:
export async function getServerColumnHeaders(): Promise<ColumnDef<VendorTaskType>[]> {
  const t = await getI18n();
-  return [ ... ];
+  return createVendorTaskColumns(t);
}
apps/app/src/components/tables/vendor-tasks/filter-toolbar.tsx (1)

71-136: Responsive filter UI with conditional clear button.

The filter UI is well-designed with a responsive layout that works on different screen sizes. The clear filters button only shows when filters are active and results aren't empty, which is good UX.

Consider adding debounce to the search input.

For better performance, consider adding debounce to the search input handler to avoid excessive API calls when typing quickly.

+import { useDebounce } from "use-debounce";

export function FilterToolbar({ isEmpty, users }: FilterToolbarProps) {
  // ...existing code
  const [searchInput, setSearchInput] = useState(search);
+  const [debouncedSearch] = useDebounce(searchInput, 300);
+
+  useEffect(() => {
+    handleSearch(debouncedSearch);
+  }, [debouncedSearch]);

  // ...in the render function
  <Input
    placeholder={t("vendors.tasks.filters.search")}
-    value={search}
-    onChange={(e) => handleSearch(e.target.value)}
+    value={searchInput}
+    onChange={(e) => setSearchInput(e.target.value)}
    className="max-w-sm"
  />
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[vendorId]/comments/page.tsx (1)

50-50: Revisit the commented-out <VendorComments> component.
Currently, there is a placeholder <div> instead of showing the vendor’s comments. If you haven’t yet implemented the component, consider adding a TODO comment or stub for clarity, or remove it for now.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/components/create-vendor-form.tsx (2)

70-74: Query states appear unused for status and category.
Although you dynamically parse the status and category, the form defaults do not appear to rely on preexisting query parameters. If these states are needed, confirm that they're integrated into your form's default values or remove them to avoid confusion.


226-263: Enum-based VendorStatus.
The selection logic for vendor status is straightforward. Iterating over VendorStatus helps keep the UI in sync with the domain model, though consider partial states or sub-statuses if your application flow becomes more granular.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/(overview)/page.tsx (2)

10-10: CreateVendorSheet import is introduced.
Ensure this component is rendered or utilized, if needed, to maintain consistent vendor creation flow. If it’s only for dev testing, mark it as such or remove it.


67-79: Efficient transaction usage for overview stats.
Your db.$transaction call with tx.vendor.count() is straightforward. Consider capturing any DB errors or edge cases (e.g., large data volumes or org restrictions).
Also, verify that your metadata title references “risk” in lines 91–92 is intentional, as you have transitioned to vendor management.

-    title: t("sidebar.risk"),
+    title: t("sidebar.vendor"),
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[vendorId]/page.tsx (3)

74-77: Consider parallelizing DB queries.

Fetching the list of users here is independent of the vendor or task queries. You can improve performance by using Promise.all to run these in parallel, reducing overall load time.

-  const users = await db.user.findMany({ /* ... */ });
-  const tasks = await db.vendorTask.findMany({ /* ... */ });
-  const total = await db.vendorTask.count({ /* ... */ });
+  const [users, tasks, total] = await Promise.all([
+    db.user.findMany({ /* ... */ }),
+    db.vendorTask.findMany({ /* ... */ }),
+    db.vendorTask.count({ /* ... */ })
+  ]);

78-110: Avoid repeating the same logic for filtering tasks.

The search and status filters are specified twice (in the where: { AND: [...] } blocks for findMany and count). Extracting the filter object into a shared constant or helper function can reduce duplication and make the logic more maintainable.


129-136: Handle edge cases for unowned tasks.

When mapping tasks, you assume task.owner is defined. If a task has no owner, the owner property may be null and might break the mapping logic. Consider using optional chaining or default values.

-    owner: {
-      name: task.owner?.name ?? "",
-      image: task.owner?.image ?? "",
-    },
+    owner: {
+      name: task.owner?.name || "Unassigned",
+      image: task.owner?.image || "",
+    },
apps/app/src/locales/analyze-locale-usage.ts (1)

412-464: Watch out for performance on large codebases.

Running multiple grep commands for each translation key can be slow in large repositories. You might consider caching or building an index of usage to avoid repeated full scans.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 5ef36fb and f3170f3.

⛔ Files ignored due to path filters (1)
  • apps/app/languine.lock is excluded by !**/*.lock
📒 Files selected for processing (69)
  • apps/app/.gitignore (1 hunks)
  • apps/app/next.config.ts (1 hunks)
  • apps/app/package.json (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/layout.tsx (0 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/people/[employeeId]/page.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/[policyId]/editor/page.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/[policyId]/page.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/[riskId]/comments/page.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/[riskId]/page.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/[riskId]/tasks/[taskId]/page.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/register/page.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/settings/members/page.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/tests/all/[testId]/page.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/(overview)/page.tsx (3 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[riskId]/comments/page.tsx (0 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[riskId]/page.tsx (0 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[riskId]/tasks/[taskId]/actions/getTaskAttachments.ts (0 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[riskId]/tasks/[taskId]/hooks/useTaskAttachments.ts (0 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[riskId]/tasks/[taskId]/page.tsx (0 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[riskId]/tasks/search-params.ts (0 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[vendorId]/comments/page.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[vendorId]/layout.tsx (2 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[vendorId]/page.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/actions/create-vendor-action.ts (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/components/create-vendor-form.tsx (8 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/components/create-vendor-sheet.tsx (4 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/register/VendorRegisterTable.tsx (3 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/register/components/table/RiskRegisterColumns.tsx (0 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/register/components/table/VendorRegisterColumns.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/register/components/table/VendorRegisterFilters.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/register/page.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/register/search-params.ts (0 hunks)
  • apps/app/src/app/[locale]/unauthorized.tsx (1 hunks)
  • apps/app/src/components/risks/charts/risks-by-department.tsx (1 hunks)
  • apps/app/src/components/risks/charts/risks-by-status.tsx (1 hunks)
  • apps/app/src/components/tables/vendor-tasks/columns.tsx (1 hunks)
  • apps/app/src/components/tables/vendor-tasks/data-table.tsx (1 hunks)
  • apps/app/src/components/tables/vendor-tasks/empty-states.tsx (1 hunks)
  • apps/app/src/components/tables/vendor-tasks/filter-toolbar.tsx (1 hunks)
  • apps/app/src/components/tables/vendor-tasks/server-columns.tsx (1 hunks)
  • apps/app/src/components/vendors/charts/vendors-by-category.tsx (1 hunks)
  • apps/app/src/components/vendors/charts/vendors-by-status.tsx (1 hunks)
  • apps/app/src/components/vendors/vendor-overview.tsx (1 hunks)
  • apps/app/src/locales/analyze-locale-usage.ts (1 hunks)
  • apps/app/src/locales/auth/auth.ts (1 hunks)
  • apps/app/src/locales/auth/onboarding.ts (1 hunks)
  • apps/app/src/locales/core/common.ts (1 hunks)
  • apps/app/src/locales/core/errors.ts (1 hunks)
  • apps/app/src/locales/core/language.ts (1 hunks)
  • apps/app/src/locales/en.ts (1 hunks)
  • apps/app/src/locales/es.ts (2 hunks)
  • apps/app/src/locales/features/evidence.ts (1 hunks)
  • apps/app/src/locales/features/frameworks.ts (1 hunks)
  • apps/app/src/locales/features/overview.ts (1 hunks)
  • apps/app/src/locales/features/people.ts (1 hunks)
  • apps/app/src/locales/features/policies.ts (1 hunks)
  • apps/app/src/locales/features/risk.ts (1 hunks)
  • apps/app/src/locales/features/tests.ts (1 hunks)
  • apps/app/src/locales/features/vendors.ts (1 hunks)
  • apps/app/src/locales/fr.ts (2 hunks)
  • apps/app/src/locales/layout/header.ts (1 hunks)
  • apps/app/src/locales/layout/not-found.ts (1 hunks)
  • apps/app/src/locales/layout/sidebar.ts (1 hunks)
  • apps/app/src/locales/layout/theme.ts (1 hunks)
  • apps/app/src/locales/layout/user-menu.ts (1 hunks)
  • apps/app/src/locales/no.ts (2 hunks)
  • apps/app/src/locales/pt.ts (2 hunks)
  • apps/app/src/locales/settings/settings.ts (1 hunks)
  • apps/portal/src/app/components/theme-switch.tsx (1 hunks)
💤 Files with no reviewable changes (9)
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/layout.tsx
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/register/search-params.ts
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/register/components/table/RiskRegisterColumns.tsx
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[riskId]/tasks/search-params.ts
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[riskId]/tasks/[taskId]/actions/getTaskAttachments.ts
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[riskId]/comments/page.tsx
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[riskId]/tasks/[taskId]/page.tsx
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[riskId]/page.tsx
  • apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[riskId]/tasks/[taskId]/hooks/useTaskAttachments.ts
🧰 Additional context used
🧬 Code Definitions (6)
apps/portal/src/app/components/theme-switch.tsx (1)
apps/app/src/locales/layout/theme.ts (1) (1)
  • theme (1-7)
apps/app/src/components/tables/vendor-tasks/server-columns.tsx (1)
apps/app/src/components/tables/vendor-tasks/columns.tsx (1) (1)
  • VendorTaskType (10-20)
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/register/components/table/VendorRegisterColumns.tsx (1)
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/register/VendorRegisterTable.tsx (1) (1)
  • VendorRegisterTableRow (13-13)
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/components/create-vendor-sheet.tsx (1)
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/components/create-vendor-form.tsx (1) (1)
  • CreateVendor (56-317)
apps/app/src/components/tables/vendor-tasks/data-table.tsx (2)
apps/app/src/components/tables/vendor-tasks/columns.tsx (1) (1)
  • VendorTaskType (10-20)
apps/app/src/components/tables/vendor-tasks/server-columns.tsx (1) (1)
  • VendorTaskType (8-18)
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/components/create-vendor-form.tsx (1)
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/actions/create-vendor-action.ts (1) (1)
  • createVendorAction (20-50)
🔇 Additional comments (78)
apps/app/.gitignore (1)

43-47: Appropriate gitignore entries for localization temp files

Good addition of patterns to ignore temporary localization files:

  • Added a clear comment section for "temp files"
  • The wildcard pattern */locales/translations.json and */locales/converter.ts will properly exclude these files at any level in the project
  • These are likely files generated during the localization process that don't need to be committed

This change supports the localization work in the PR.

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

1-36: LGTM! Well-structured localization constants.

This new file adds clear, well-organized localization constants for evidence management. The structure is consistent with other localization files and follows best practices with as const typing.

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

1-55: LGTM! Well-structured localization constants.

This file adds clear, well-organized localization constants for people management. The structure is consistent with other localization files, with a logical organization of properties into relevant sections and proper typing with as const.

apps/app/src/locales/core/common.ts (1)

1-145: The localization structure is well-organized

The common localization structure is comprehensive and well-organized with logical grouping of related strings. The use of as const ensures type safety and immutability of the object.

apps/app/src/locales/settings/settings.ts (2)

1-211: Settings localization structure is comprehensive

The settings localization structure is well-organized with a clear hierarchy and comprehensive coverage of different sections. The usage of nested objects helps maintain clarity and the as const assertion ensures type safety.


155-163:

Details

❓ Verification inconclusive

Consider using enum values for departments

The department values are hard-coded strings. Consider using enum values that match backend values to ensure consistency and prevent potential issues when these values are used in API calls.


🏁 Script executed:

#!/bin/bash
# Look for department enum or type definitions in the codebase
echo "Searching for department type definitions in the codebase..."
rg -t ts -t tsx "enum.*Department|type.*Department|interface.*Department" --max-depth 4

Length of output: 248


Below is a revised shell script to search for any Department enum/type/interface definitions in both *.ts and *.tsx files. The previous command encountered an error with the tsx file type, so this updated script uses file globs instead:

#!/bin/bash
# Revised script: Search for 'Department' enum/type/interface definitions across TypeScript files (*.ts and *.tsx)
rg "enum.*Department|type.*Department|interface.*Department" -g "*.ts" -g "*.tsx" --max-depth 4

Once you run this script and review the output:

  • If a matching Department definition is found, it may be possible to refactor the code to use that instead of hard-coded strings.
  • If no matching definition exists, the suggestion to establish an enum for departments remains valid to ensure consistency with backend values.

Please run the revised script above and verify the output.

apps/app/src/locales/layout/theme.ts (1)

1-7: Theme options look good

The theme localization structure is simple and appropriately defines the three theme options. The use of as const ensures type safety.

apps/app/package.json (1)

14-14:

Details

✅ Verification successful

New locale analysis script added

The new analyze-locale-usage script is a good addition for identifying and managing localization strings usage across the application.

Consider adding a small comment in the script explaining its purpose for better discoverability.


🏁 Script executed:

#!/bin/bash
# Check if the analyze-locale-usage.ts file exists and what it does
echo "Checking the implementation of the locale analysis script..."
fd "analyze-locale-usage.ts" -x cat {} 2>/dev/null | head -n 15

Length of output: 685


Locale Analysis Script Comment Verified

The new analyze-locale-usage script already includes an inline comment at the top of the file that clearly explains its purpose and the main features it offers (i.e., identifying unused keys, detecting duplicates, and displaying where translations are used). No further changes are needed.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/tests/all/[testId]/page.tsx (1)

41-41: Localization key simplified

The translation key has been updated from sub_pages.tests.test_details to tests.test_details, which aligns with the PR's localization improvements.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/register/page.tsx (1)

20-20: Localization key simplified

The translation key has been updated from sub_pages.risk.register to risk.register, maintaining consistency with other localization key changes in this PR.

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

28-28: Localization key simplified

The translation key has been updated from sub_pages.policies.policy_details to policies.policy_details, following the pattern of removing the sub_pages prefix throughout the application.

apps/app/src/locales/core/errors.ts (1)

1-8: Well-structured error messages implementation

Good addition of a centralized error messages structure that will help maintain consistency across the application. The use of as const ensures type safety and immutability of the error message object.

apps/app/src/locales/layout/sidebar.ts (1)

1-11: Well-structured localization constants for sidebar

The new sidebar localization constant is well-organized and follows TypeScript best practices with the as const assertion, which ensures type safety when these constants are used throughout the application.

apps/app/next.config.ts (1)

20-22:

Details

✅ Verification successful

Verify implications of enabling experimental auth feature

The addition of the experimental authInterrupts feature is correctly implemented. Since this is an experimental Next.js feature, be aware that:

  1. It may change or be removed in future releases
  2. There could be unexpected behavior or bugs
  3. Documentation might be limited

🌐 Web query:

What is the authInterrupts experimental feature in Next.js?

💡 Result:

The authInterrupts experimental feature in Next.js enables developers to handle authorization errors more effectively using specialized APIs and customizable error pages. Introduced in Next.js 15.1, this feature requires enabling in your configuration:

// next.config.js or next.config.ts
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  experimental: {
    authInterrupts: true,
  },
}

export default nextConfig

Core Functionality

The feature provides two key APIs:

  • forbidden(): Triggers a 403 Forbidden error when users lack resource permissions[1][6]
  • unauthorized(): Triggers a 401 Unauthorized error for unauthenticated access attempts[1][7]

Both APIs work in:

  • Server Components
  • Client Components
  • Server Actions
  • Route Handlers[6][7]

Implementation Examples

Basic usage in a Server Component:

import { verifySession } from '@/app/auth'
import { forbidden } from 'next/navigation'

export default async function AdminPage() {
  const session = await verifySession()
  
  if (session.role !== 'admin') {
    forbidden() // Triggers custom 403 page
  }
  
  return <AdminDashboard />
}

Custom error pages (app/forbidden.tsx and app/unauthorized.tsx):

// app/forbidden.tsx
export default function ForbiddenPage() {
  return (
    <div>
      <h2>Access Denied</h2>
      <p>Contact administrator for permissions</p>
    </div>
  )
}

// app/unauthorized.tsx
export default function UnauthorizedPage() {
  return (
    <div>
      <h2>Login Required</h2>
      <a href="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/login">Sign In</a>
    </div>
  )
}

Key Benefits

  1. Standardized error handling: Consistent patterns for authorization flows[5]
  2. Customizable UX: Tailored error pages beyond default HTTP status codes[3][6]
  3. Granular control: Intercept auth failures at any application layer[5][7]

Current Limitations

  • Experimental status (requires opt-in via config)[1][8]
  • Cannot be used in root layout components[6][7]
  • Status code becomes 200 if headers already sent[5]

The feature builds on Next.js's existing notFound() pattern for 404 handling, extending it to authorization scenarios. While still experimental, it provides a structured approach to auth error management compared to manual status code checks[5][7]. Developers are encouraged to test and provide feedback before its planned stabilization in future releases[3][5].

Citations:


Action Required: Verify Experimental Auth Feature Usage

The implementation of the experimental authInterrupts flag in apps/app/next.config.ts is correct. As verified, this feature—introduced in Next.js 15.1—is intended to streamline authorization error handling via APIs like forbidden() and unauthorized(). Please note:

  • Since it is experimental, its behavior may evolve or be removed in future Next.js releases.
  • Unexpected behavior or bugs might occur; thorough testing is recommended.
  • Limited documentation means you should keep an eye on official updates and community feedback.
apps/app/src/locales/layout/not-found.ts (1)

1-5: Well-structured localization constants for 404 page

The new not_found localization constant is appropriately structured and uses the as const assertion for proper type safety. The included messages cover all essential elements of a 404 error page.

apps/app/src/components/vendors/charts/vendors-by-status.tsx (1)

23-23: Translation key updated to follow new hierarchical structure

The translation key has been appropriately updated from what appears to be dashboard.vendor_status to vendors.dashboard.status, which follows a more logical hierarchical structure in your localization system. This change aligns with the broader reorganization of translation keys mentioned in the PR objectives.

Make sure to verify that this new translation key exists in all your locale files to avoid missing translations.

apps/app/src/locales/layout/user-menu.ts (1)

1-9: Well-structured localization constant

The user_menu constant is properly structured with clear, concise translation keys for common user menu options. Using as const assertion ensures type safety and immutability, which is a good practice for localization data.

apps/app/src/components/vendors/charts/vendors-by-category.tsx (1)

49-49: Updated localization key follows new pattern

The translation key has been updated from the previous pattern to a more structured, hierarchical format (vendors.dashboard.by_category). This change aligns with the broader localization restructuring effort mentioned in the PR description.

apps/app/src/locales/layout/header.ts (1)

1-14: Comprehensive feedback-related translations

The header constant provides a well-structured set of localization strings, particularly for the feedback feature which covers all user interaction states (button, title, description, placeholder, success, and error messages). This thorough approach to feedback messaging will improve user experience.

apps/app/src/locales/core/language.ts (1)

1-5: Clear language selection UI text

The language constant provides appropriate text for the language selection UI components, with clear title, description, and placeholder.

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

44-44: LGTM! Simplified localization key

The change to simplify the localization key from t("sub_pages.policies.policy_details") to t("policies.policy_details") is part of a consistent localization structure improvement across the application.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/[riskId]/tasks/[taskId]/page.tsx (1)

85-85: LGTM! Simplified localization key

The change to simplify the localization key from t("sub_pages.risk.tasks.task_overview") to t("risk.tasks.task_overview") is part of a consistent localization structure improvement across the application.

apps/app/src/app/[locale]/unauthorized.tsx (2)

1-29: LGTM! Well-structured unauthorized page component

This new component is well-implemented with proper internationalization, responsive layout, and clear user guidance for unauthorized access scenarios.


11-27: Good use of semantic structure and UI components

The component effectively uses Card, proper heading hierarchy, and semantic markup. The button as a Link is also well-implemented for navigation back to the home page.

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

1-79: Well-structured localization constants for the cloud tests feature.

The organization of keys follows a logical hierarchy, making it easy to locate specific translations. The use of as const ensures type safety, which is a good practice.

apps/app/src/locales/auth/auth.ts (1)

1-19: Well-organized authentication localization constants.

The structure is clear and comprehensive, covering all aspects of the authentication flow including email login, magic links, and terms acceptance. The as const assertion ensures type safety.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/people/[employeeId]/page.tsx (1)

37-37: Simplified localization key structure for employee details title.

The change from t("sub_pages.people.employee_details") to t("people.employee_details") follows the updated localization pattern being implemented across the application.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/settings/members/page.tsx (1)

31-31: Simplified localization key structure for members settings title.

The change from t("sub_pages.settings.members") to t("settings.members") aligns with the localization key restructuring being applied consistently throughout the application.

apps/portal/src/app/components/theme-switch.tsx (1)

50-52: Improved readability with explicit conditional rendering for theme options

The change improves code clarity by replacing what was likely a dynamic translation key with explicit conditional checks for each theme option. This approach makes the code more maintainable and easier to understand, while aligning well with the theme structure defined in the localization files.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/[riskId]/page.tsx (1)

249-249: Consistent localization key structure

The change simplifies the translation key from what was likely sub_pages.risk.risk_overview to risk.risk_overview, which aligns with the broader localization restructuring effort across the application. This promotes consistency in how UI strings are organized.

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

9-37: Well-structured NoResults component with clear user feedback

The NoResults component effectively handles the empty state with proper internationalization and user feedback. The "Clear Filters" button provides a good user experience by making it easy to reset the search.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/components/create-vendor-sheet.tsx (2)

11-11: Import updated for vendor management

The import has been correctly updated to use the CreateVendor component instead of CreateRisk, aligning with the shift from risk management to vendor management.


28-28: UI text and component references updated for vendor context

The changes correctly update all references from risk to vendor, ensuring consistency in both the UI text and component usage. This maintains a cohesive user experience across the vendor management workflow.

Also applies to: 40-40, 49-49, 51-51

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/[riskId]/comments/page.tsx (1)

83-85: Translation key update looks good

The change from t("sub_pages.risk.risk_comments") to t("risk.risk_comments") aligns with the broader effort to simplify the localization structure mentioned in the PR objectives.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[vendorId]/layout.tsx (1)

7-10: Interface update correctly reflects the vendor-focused approach

The change from riskId to vendorId in the LayoutProps interface properly aligns with the transition from risk management to vendor management mentioned in the PR objectives.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/register/page.tsx (1)

74-76: Translation key update looks good

The change from t("sub_pages.vendors.register") to t("vendors.register.title") is consistent with the localization improvements mentioned in the PR objectives and follows the same pattern as other files.

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

1-49: Well-structured localization constants for frameworks

The new frameworks constant is well-organized with clear hierarchical structure and comprehensive coverage of UI text needs. The use of as const ensures type safety and immutability, which is excellent.

This addition aligns well with the localization efforts mentioned in the PR objectives. The organization into sections like overview, controls, and their nested elements provides a maintainable structure for translations.

apps/app/src/locales/auth/onboarding.ts (1)

1-34: Well-structured localization object.

No issues found with these entries. The keys, placeholders, and text strings are clear, consistent, and well-suited for user onboarding steps.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/register/components/table/VendorRegisterColumns.tsx (3)

8-19: Vendor column implementation looks good.

Utilizing Link with dynamic IDs is straightforward and matches expected usage for a vendor detail page. No changes needed here.


20-26: Status column is fine as-is.

Good use of VendorStatus component to standardize status display. Ensure unrecognized statuses are handled within the component itself.


27-37: Category column correctly leverages the Badge component.

Converting the category text to uppercase is consistent, and the chosen marketing variant suits the styling. No concerns.

apps/app/src/locales/en.ts (1)

1-67: Modularized imports and deep-readonly export are commendable.

This refactor significantly improves maintainability and ensures a cohesive localization structure. No further concerns.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/actions/create-vendor-action.ts (1)

11-18: Strong schema validation with zod.

The schema validation is well-defined with appropriate constraints for each field. The required fields have proper validation messages, and optional fields are clearly marked.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/register/VendorRegisterTable.tsx (3)

11-11: Updated import path aligns with vendor management refactoring.

The import path has been updated from risk-related to vendor-related, which is consistent with the PR objective of transitioning from risk management to vendor management functionality.


13-13: Good change: Type is now exported for reuse.

Making VendorRegisterTableRow an exported type rather than an internal type improves code modularity and allows reuse across components.


85-85: DataTable generic type properly updated.

The DataTable component's generic type has been correctly updated from Vendor to VendorRegisterTableRow, aligning with the exported type and ensuring type safety.

apps/app/src/components/tables/vendor-tasks/server-columns.tsx (1)

8-18: Interface definition is consistent with client counterpart.

The VendorTaskType interface correctly matches the one defined in columns.tsx, ensuring consistency between server and client components. This is good practice for maintaining a shared data model.

apps/app/src/components/tables/vendor-tasks/columns.tsx (1)

10-20: Interface definition matches server-side counterpart.

The VendorTaskType interface correctly defines the structure needed for vendor tasks and is consistent with its server-side counterpart, which is good practice.

apps/app/src/components/tables/vendor-tasks/filter-toolbar.tsx (2)

17-20: Clear interface definition with proper typing.

The FilterToolbarProps interface is well-defined with proper types for the component props.


22-70: Well-implemented filter handlers with URL search param management.

The filter handlers properly manage URL search parameters, ensuring filter state is preserved in the URL. Each handler correctly resets the page to 1 when filters change, which is good UX to avoid empty result pages.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[vendorId]/comments/page.tsx (3)

9-12: Consider renaming or removing the Promise wrapper on params.
Having params: Promise<{ vendorId: string }> instead of a direct object may be unusual, unless there is a specific reason based on Next.js route conventions. If it’s not strictly required, simplify the typing to avoid unnecessary awaits or confusion.

Would you like me to generate a quick script to confirm if other pages in the same directory are also using a Promise-wrapped params object?


55-67: Metadata generation appears correct.
The use of setStaticParamsLocale(locale) and getI18n() is consistent with the existing pattern to localize page metadata. The returned title references "vendors.vendor_comments", which should accurately describe this page’s content.


21-23:

Details

✅ Verification successful

Confirm redirect path with organization context.
When !session.user.organizationId || !vendorId, you are redirecting to /. If your application requires an org-specific path, consider redirecting to /${session.user.organizationId} or a relevant fallback to maintain consistency.

Please verify whether your organization-specific routes are intended here by searching for similar usage patterns:


🏁 Script executed:

#!/bin/bash
rg -A 3 'redirect\("/"\)' 

Length of output: 9758


Action: Confirm Redirect Behavior Consistency

After reviewing similar patterns across the codebase, it appears that using redirect("/") is consistent throughout organization-specific routes (e.g., in layouts, evidence, tests, etc.). The vendors comments page follows this established pattern. If no specific requirement mandates an org-contextual redirect (such as /${session.user.organizationId}), then the current behavior is in line with the rest of the application.

  • Verify that a fallback to / meets your business requirements.
  • If an organization-specific URL is expected when session.user.organizationId is present, consider updating the redirect accordingly.
apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/components/create-vendor-form.tsx (6)

47-54: Schema definition is solid.
Your createVendorSchema effectively validates vendor inputs, including optional website and description. This helps ensure data integrity before creating a vendor in the database.


56-58: Check user permissions before creation.
The code references a session but relies on the upstream createVendorAction for authorization. Ensure that users who load this form are properly authorized to create vendors, or at least see a graceful error if not.


86-101: Robust success & error handling in useAction.
You display toast messages upon success or error and reset the sheet. This is good. Just ensure you handle potential concurrency or partial form states if creation is initiated multiple times or if the user lacks permissions.


103-115: Zod resolver usage.
The combination of react-hook-form with zodResolver(createVendorSchema) is appropriate for typed validation. The onSubmit function correctly calls createVendor.execute(data). This is a strong, maintainable approach.


148-165: Optional website field.
Marking website as optional with .url() effectively allows empty values but still enforces the correct pattern if provided. This approach is well-aligned with typical usage guidelines.


305-308: Well-labeled create button.
Using t("vendors.actions.create") clarifies the action to the user. The disabled={createVendor.status === "executing"} helps prevent duplicate submissions. Good job.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/(overview)/page.tsx (4)

4-6: Imports look consistent.
You’re bringing in Button, Card, and Link to display a no-vendor fallback UI. This is aligned with your new vendor management flow.


12-16: Function name aligns with vendor management.
Renaming from something “Risk” to VendorManagement clarifies the new domain focus. Good consistency with your vendor-based approach.


26-26: Consolidated data retrieval with getVendorOverview.
This direct call to a specialized function for fetching vendor metrics is a clean separation of concerns, making the VendorManagement component more readable.


28-50: No vendor fallback UI.
Nicely handled. The user sees an immediate CTA to add a vendor. Just ensure the path "/{orgId}/vendors/register" is valid for your chosen approach. Good user experience for empty states.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[vendorId]/page.tsx (2)

25-34: Check for potential confusion with promise-based props.

Defining searchParams and params as Promises is unconventional in many Next.js setups. Verify that they're intentionally wrapped in promises, as it can lead to extra await calls. Consider removing the promise requirement or using standard props if not strictly needed.

Do you want to confirm via a quick usage check throughout the codebase?


175-187: Validate locale in generateMetadata.

Ensure the locale parameter is valid or falls back appropriately. Without a fallback, an invalid locale could cause unexpected metadata results or errors.

apps/app/src/locales/analyze-locale-usage.ts (1)

405-489: Confirm cross-platform compatibility.

Reliance on grep and shell features may cause issues on non-UNIX systems (e.g., Windows). Consider using cross-platform Node libraries or AST-based approaches to ensure consistent results across environments.

Would you like to explore a cross-platform solution for scanning code, possibly using ast-grep or a Node-based parser?

apps/app/src/locales/no.ts (3)

953-959: Localized unauthorized error message acknowledged.

The new unauthorized error structure appears consistent with existing error objects. Good job ensuring descriptive messages and clarifying user action.


1179-1180: Check for overlap with existing “dashboard” keys.

You introduced a dashboard key (title: "Oversikt") under the vendors subsection. Verify it doesn’t conflict or cause confusion with any higher-level dashboard objects.


1181-1199: Keep vendor placeholders consistent with existing patterns.

The placeholders, e.g. "Skriv inn leverandørnavn", match the style of other form placeholders. This looks coherent. Make sure to also unify naming across existing “vendor” vs. “vendors” sections to avoid confusion.

apps/app/src/locales/pt.ts (2)

954-959: Well-structured unauthorized error messages added.

The addition of the unauthorized error object provides a clear error message structure with a title, description, and back action. This improves the user experience when handling permission issues.


1151-1211: Complete vendor management localization strings added.

The vendor management section is well-structured with comprehensive localization coverage including:

  • Proper form field labels and placeholders
  • Table column headers
  • Filter options
  • Empty state messages
  • Status and category taxonomies

All translations appear to be correctly formatted for Portuguese.

apps/app/src/locales/es.ts (2)

954-959: Well-structured Spanish unauthorized error messages.

The unauthorized error messages are properly localized in Spanish, maintaining consistency with other language files while providing clear user guidance.


1180-1240: Comprehensive Spanish vendor management translations.

The vendor management section provides complete Spanish translations for all UI elements, maintaining structural consistency with the Portuguese version while correctly adapting the language.

apps/app/src/locales/fr.ts (2)

954-959: French unauthorized error messages properly implemented.

The unauthorized error structure has been correctly added with appropriate French translations that maintain consistency with other language files.


1180-1240: Complete French vendor management localization.

The vendor management translations are comprehensive and follow the same structure as other language files, ensuring consistent multilingual support throughout the application.

apps/app/src/components/risks/charts/risks-by-department.tsx (1)

48-48: Translation key update looks good.

The change from dashboard.risks_by_department to risk.dashboard.by_department aligns well with the localization restructuring mentioned in the PR objectives. This provides better organization by using "risk" as the top-level namespace.

apps/app/src/components/risks/charts/risks-by-status.tsx (1)

24-24: Translation key update is consistent with project standards.

The update from dashboard.risk_status to risk.dashboard.status maintains consistency with the localization restructuring pattern seen across other files in this PR.

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

1-9: Localization structure looks good.

The introduction of this new localization file with proper structure and TypeScript's as const assertion ensures type safety while providing translations for the overview feature. This is consistent with the modular approach to localization mentioned in the PR objectives.

import { AssigneeAvatar } from "@/app/[locale]/(app)/(dashboard)/[orgId]/evidence/list/components/table/components/AssigneeAvatar";

export const RiskRegisterFilters = ({
export const VendorRegisterFilters = ({

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

Component renamed but still using risk-related types

While the component name has been updated from RiskRegisterFilters to VendorRegisterFilters, it still uses risk-related types and constants throughout the implementation (e.g., RiskStatus in props and filter logic).

For complete migration to vendor management, consider updating these risk-related dependencies:

- import { type Departments, RiskStatus } from "@bubba/db/types";
+ import { type Departments, VendorStatus } from "@bubba/db/types";

// Update in props
- status: RiskStatus | null;
- setStatus: (status: RiskStatus | null) => void;
+ status: VendorStatus | null;
+ setStatus: (status: VendorStatus | null) => void;

// Update in filter logic
- Object.values(RiskStatus).map((filter) => ({
+ Object.values(VendorStatus).map((filter) => ({

Comment on lines +30 to +39
const vendor = await db.vendor.create({
data: {
name: input.parsedInput.name,
description: input.parsedInput.description || "",
category: input.parsedInput.category,
status: input.parsedInput.status,
ownerId: input.parsedInput.ownerId,
organizationId: session.user.organizationId,
}
});

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

🧩 Analysis chain

Validate ownerId belongs to the organization.

The ownerId is passed directly from the input to the database without verification.

It's important to verify that the specified ownerId (if provided) belongs to the same organization to prevent cross-organization assignments.


🏁 Script executed:

#!/bin/bash
# Check if there are any validation for ownerId in the codebase

# Look for user validation in relation to organizations
rg -A 5 -B 5 "ownerId.*organizationId" --glob "*.ts" --glob "*.tsx"

# Check if there are any other permission or validation utilities
fd -e ts -e tsx | xargs grep -l "permission\|validate\|authorization" | head -n 10

Length of output: 785


Action Required: Validate ownerId Against the Session’s Organization

The current implementation in create-vendor-action.ts passes input.parsedInput.ownerId directly to the database without ensuring it belongs to the organization of the currently logged-in user. This leaves the door open for potential cross-organization assignments. For consistency and security, similar to other organization-related actions in the codebase, please add a check to verify that if an ownerId is provided, it must match session.user.organizationId.

Suggested Fixes:

  • Validation Check: Before creating the vendor, confirm that input.parsedInput.ownerId (if provided) is equal to session.user.organizationId. If the IDs differ, return an appropriate error.
  • Consistency: Consider using or extending the existing validation or authorization utilities (as seen in organization actions like in invite-member.ts or update-organization-name-action.ts) to enforce this rule.

Comment on lines +20 to +50
export const createVendorAction = createSafeActionClient()
.schema(schema)
.action(async (input): Promise<ActionResponse> => {
try {
const session = await auth();

if (!session?.user?.organizationId) {
throw new Error("Unauthorized");
}

const vendor = await db.vendor.create({
data: {
name: input.parsedInput.name,
description: input.parsedInput.description || "",
category: input.parsedInput.category,
status: input.parsedInput.status,
ownerId: input.parsedInput.ownerId,
organizationId: session.user.organizationId,
}
});

revalidatePath(`/${session.user.organizationId}/vendors`);

return { success: true, data: vendor };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Failed to create vendor"
};
}
}); No newline at end of file

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

Fix revalidation path to include locale segment.

The action function is well-structured with proper error handling and authentication checks. However, there's an issue with the revalidation path.

The revalidation path doesn't include the locale segment that's part of your URL structure. Based on the file path ([locale]/(app)/(dashboard)/[orgId]/vendors), the revalidation path should include the locale:

-  revalidatePath(`/${session.user.organizationId}/vendors`);
+  revalidatePath(`/${session.user.locale}/${session.user.organizationId}/vendors`);

If session.user.locale is not available, you'll need to modify the approach to include the correct locale information.

📝 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 createVendorAction = createSafeActionClient()
.schema(schema)
.action(async (input): Promise<ActionResponse> => {
try {
const session = await auth();
if (!session?.user?.organizationId) {
throw new Error("Unauthorized");
}
const vendor = await db.vendor.create({
data: {
name: input.parsedInput.name,
description: input.parsedInput.description || "",
category: input.parsedInput.category,
status: input.parsedInput.status,
ownerId: input.parsedInput.ownerId,
organizationId: session.user.organizationId,
}
});
revalidatePath(`/${session.user.organizationId}/vendors`);
return { success: true, data: vendor };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Failed to create vendor"
};
}
});
export const createVendorAction = createSafeActionClient()
.schema(schema)
.action(async (input): Promise<ActionResponse> => {
try {
const session = await auth();
if (!session?.user?.organizationId) {
throw new Error("Unauthorized");
}
const vendor = await db.vendor.create({
data: {
name: input.parsedInput.name,
description: input.parsedInput.description || "",
category: input.parsedInput.category,
status: input.parsedInput.status,
ownerId: input.parsedInput.ownerId,
organizationId: session.user.organizationId,
}
});
revalidatePath(`/${session.user.locale}/${session.user.organizationId}/vendors`);
return { success: true, data: vendor };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Failed to create vendor"
};
}
});

Comment on lines +103 to +107
{/* <DataTablePagination
pageCount={pageCount}
currentPage={currentPage}
onPageChange={onPageChange}
/> */}

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

🧩 Analysis chain

Implement or remove commented pagination component.

The commented-out DataTablePagination component suggests pagination UI was planned but not implemented.

Either implement the pagination component or remove the commented code to keep the codebase clean.


🏁 Script executed:

#!/bin/bash
# Check if DataTablePagination component exists

# Look for DataTablePagination component
fd -e ts -e tsx | xargs grep -l "DataTablePagination"

# Check other data tables for pagination patterns
fd -e ts -e tsx | xargs grep -l "Pagination" | grep -i "table"

Length of output: 1738


Action Required: Resolve the pagination implementation in vendor-tasks data table

The vendor-tasks table currently includes commented-out pagination code (lines 103–107). Since similar tables in the project are either using or have dedicated implementations of pagination (see, for example, the shared component at apps/app/src/components/ui/data-table/DataTablePagination.tsx), please address this by either:

  • Implementing pagination: If vendor-tasks is designed to support pagination, integrate the DataTablePagination component properly (by passing the appropriate props such as pageCount, currentPage, and onPageChange), following the pattern established in other tables (e.g., people, policies, risk-tasks).
  • Removing the commented code: If pagination is not required for this table, delete the commented block to clean up the codebase.

Review and update the code accordingly to ensure consistency and maintainability.

Comment on lines +37 to +43
const table = useReactTable({
data,
columns: columnHeaders,
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
pageCount,
});

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

Configure current page in the table instance.

The component receives a currentPage prop but doesn't use it in the table configuration.

Configure the current page in the table instance to ensure pagination works correctly:

  const table = useReactTable({
    data,
    columns: columnHeaders,
    getCoreRowModel: getCoreRowModel(),
    manualPagination: true,
    pageCount,
+   state: {
+     pagination: {
+       pageIndex: currentPage - 1, // ReactTable uses 0-indexed pages
+       pageSize: 10, // Set your preferred page size
+     },
+   },
  });
📝 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 table = useReactTable({
data,
columns: columnHeaders,
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
pageCount,
});
const table = useReactTable({
data,
columns: columnHeaders,
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
pageCount,
state: {
pagination: {
pageIndex: currentPage - 1, // ReactTable uses 0-indexed pages
pageSize: 10, // Set your preferred page size
},
},
});

Comment on lines +91 to +98
<TableRow>
<TableCell
colSpan={columnHeaders.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>

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

Use localized string for empty state messages.

The "No results." message is hardcoded, which is problematic for internationalization.

Replace the hardcoded text with a localized string:

import { useI18n } from "@/locales/client";

export function DataTable({
  data,
  columnHeaders,
  pageCount,
  currentPage,
}: DataTableProps) {
  const router = useRouter();
  const pathname = usePathname();
  const searchParams = useSearchParams();
+ const t = useI18n();

  // Rest of the component...

  <TableRow>
    <TableCell
      colSpan={columnHeaders.length}
      className="h-24 text-center"
    >
-      No results.
+      {t("vendors.tasks.empty.no_results")}
    </TableCell>
  </TableRow>
📝 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
<TableRow>
<TableCell
colSpan={columnHeaders.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
import { useI18n } from "@/locales/client";
export function DataTable({
data,
columnHeaders,
pageCount,
currentPage,
}: DataTableProps) {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const t = useI18n();
// Rest of the component...
<TableRow>
<TableCell
colSpan={columnHeaders.length}
className="h-24 text-center"
>
{t("vendors.tasks.empty.no_results")}
</TableCell>
</TableRow>
}

Comment on lines +25 to +42
const vendor = await db.vendor.findUnique({
where: {
id: vendorId,
organizationId: session.user.organizationId,
},
include: {
owner: true,
comments: {
orderBy: {
createdAt: "desc",
},
},
},
});

if (!vendor) {
redirect("/vendors");
}

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

Validate vendor not found scenario.
When !vendor, you redirect to "/vendors", but it may cause confusion if the user belongs to multiple organizations or if the path should include /${session.user.organizationId}/vendors. Ensure you preserve the org context to avoid potential dead-end pages.

-    redirect("/vendors");
+    redirect(`/${session.user.organizationId}/vendors`);
📝 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 vendor = await db.vendor.findUnique({
where: {
id: vendorId,
organizationId: session.user.organizationId,
},
include: {
owner: true,
comments: {
orderBy: {
createdAt: "desc",
},
},
},
});
if (!vendor) {
redirect("/vendors");
}
const vendor = await db.vendor.findUnique({
where: {
id: vendorId,
organizationId: session.user.organizationId,
},
include: {
owner: true,
comments: {
orderBy: {
createdAt: "desc",
},
},
},
});
if (!vendor) {
redirect(`/${session.user.organizationId}/vendors`);
}

@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

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between f3170f3 and 859630a.

⛔ Files ignored due to path filters (1)
  • apps/app/languine.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • apps/app/src/locales/es.ts (2 hunks)
  • apps/app/src/locales/fr.ts (2 hunks)
  • apps/app/src/locales/no.ts (2 hunks)
  • apps/app/src/locales/pt.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/app/src/locales/es.ts
🔇 Additional comments (5)
apps/app/src/locales/pt.ts (2)

959-964: Clear and helpful unauthorized access message.

These newly added translations for unauthorized access enrich the user experience. Double-check that any upstream error-handling code references this new key correctly and that the "Voltar para a Página Inicial" action is properly hooked.


1155-1216:

Details

✅ Verification successful

Comprehensive vendor-related additions.

All these vendor section strings and placeholders align well with established naming conventions. Ensure that any existing vendor-related UI references are updated to use these new keys. Also consider verifying their usage:


🏁 Script executed:

#!/bin/bash
# Search for references to the newly introduced vendor keys in Portuguese locale
rg 'vendors?\.(create|form|filters|table|actions|empty_states|category|status)' -A 3

Length of output: 22108


Vendor Keys and Usage Verified

The new vendor locale keys (including those for forms, tables, filters, empty states, actions, status, and category) are correctly implemented and referenced throughout the vendor-related components (e.g., in vendor-overview.tsx, create-vendor-sheet.tsx, and create-vendor-form.tsx). All usages align with the naming conventions, and the updated strings are effectively integrated into the UI. Please continue to ensure that any future changes to vendor keys remain consistent across related components.

apps/app/src/locales/no.ts (2)

959-964: Localized unauthorized error handling.

The introduction of these unauthorized strings is coherent with the existing structure, improving clarity for Norwegian users. Confirm that all calls to errors.unauthorized are fully integrated in the codebase.


1185-1245:

Details

✅ Verification successful

Extended vendor management translations.

These new vendor translations, statuses, and placeholders look consistent with the rest of the Norwegian locale. Consider ensuring that your front-end components leverage these newly added keys:


🏁 Script executed:

#!/bin/bash
# Verify that the new vendor keys in Norwegian are referenced correctly
rg 'vendors?\.(create|form|filters|table|actions|empty_states|category|status)' -A 3

Length of output: 22108


Vendor Translations Verified and Integrated

The new vendor translation keys (e.g., for form fields, table headers, filters, statuses, and actions) are correctly referenced across the front-end components. The shell script output confirms they’re consistently used in files like the vendor overview, create vendor sheet/form, and related chart components.

  • The keys under the "vendors" namespace are being used as expected in components.
  • No discrepancies were found between the extended translations and their usage in the codebase.

Please ensure that any legacy or unused keys (such as those under the "risk.vendor" namespace) are cleaned up if they are no longer needed.

apps/app/src/locales/fr.ts (1)

959-964: Helpful unauthorized error message.

These newly introduced strings provide a clear indication of unauthorized access in French. Make sure the “Retour à l'accueil” prompt correctly redirects users to the appropriate page.

Comment on lines +1185 to 1245
create: "Créer un fournisseur",
form: {
vendor_details: "Détails du fournisseur",
vendor_name: "Nom",
vendor_name_placeholder: "Entrez le nom du fournisseur",
vendor_website: "Site Web",
vendor_website_placeholder: "Entrez le site Web du fournisseur",
vendor_description: "Description",
vendor_description_placeholder: "Entrez la description du fournisseur",
vendor_category: "Catégorie",
vendor_category_placeholder: "Sélectionner une catégorie",
vendor_status: "Statut",
vendor_status_placeholder: "Sélectionner un statut",
create_vendor_success: "Fournisseur créé avec succès",
create_vendor_error: "Échec de la création du fournisseur",
update_vendor: "Mettre à jour le fournisseur",
update_vendor_success: "Fournisseur mis à jour avec succès",
update_vendor_error: "Échec de la mise à jour du fournisseur",
add_comment: "Ajouter un commentaire"
},
table: {
name: "Nom",
category: "Catégorie",
status: "Statut",
owner: "Propriétaire"
},
filters: {
search_placeholder: "Rechercher des fournisseurs...",
status_placeholder: "Filtrer par statut",
category_placeholder: "Filtrer par catégorie",
owner_placeholder: "Filtrer par propriétaire"
},
empty_states: {
no_vendors: {
title: "Aucun fournisseur pour le moment",
description: "Commencez par créer votre premier fournisseur"
},
no_results: {
title: "Aucun résultat trouvé",
description: "Aucun fournisseur ne correspond à votre recherche",
description_with_filters: "Essayez d'ajuster vos filtres"
}
},
actions: {
create: "Créer un fournisseur"
},
status: {
not_assessed: "Non évalué",
in_progress: "En cours",
assessed: "Évalué"
},
category: {
cloud: "Cloud",
infrastructure: "Infrastructure",
software_as_a_service: "Logiciel en tant que service",
finance: "Finance",
marketing: "Marketing",
sales: "Ventes",
hr: "Ressources humaines",
other: "Autre"
}

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

🧩 Analysis chain

Robust vendor translation block.

This newly added block for vendor management in French includes descriptive placeholders and success/error messages, aligning with the localization pattern. Validate that any existing references to vendor keys are replaced with these new items:


🏁 Script executed:

#!/bin/bash
# Confirm usage of the new vendor translations in the French locale
rg 'vendors?\.(create|form|filters|table|actions|empty_states|category|status)' -A 3

Length of output: 22108


Consistent Vendor Translation Usage

The new French vendor translation block in apps/app/src/locales/fr.ts is robust and covers all necessary keys, with vendor screens (e.g., in vendor-overview, create-vendor-sheet, and create-vendor-form) now correctly using the new vendors.* keys. However, the verification revealed that a couple of components in the risk dashboard still reference the legacy keys (e.g., in apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/overview/frameworks/[frameworkId]/components/table/FrameworkControlsTableHeader.tsx and FrameworkControlsTableColumns.tsx where t("risk.vendor.table.category") is used).

Please update these references to use the new vendors.table.category (and any other relevant) key(s) to ensure consistency across the application.

This branch had an error being deployed

1 failed and 1 active deployments
Preview – app — adcd8683 Deployed Mar 21, 2025 by vercel[bot]
Preview – comp-portal — adcd8683 Deployed Mar 21, 2025 by vercel[bot]
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