Progress on vendors page and locatlization - #167
Conversation
- 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.
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
|
Caution Review failedThe pull request is closed. WalkthroughThis pull request introduces a range of updates affecting configuration files, UI components, and localization. Changes include additions to the Changes
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
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
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (15)
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
- 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.
There was a problem hiding this comment.
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 suggestionFilter logic using risk-specific enumeration
This filter logic is still using
RiskStatusfor 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
VendorStatusenum 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) andvendor.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 constAlso 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 instatus(lines 57-61). The top-levelstatusobject 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_assigneeandpolicies_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 likedashboard,overview, etc. For better maintainability, consider organizing all properties into logical groups.For example, properties like
title,policies, andcreate_newcould be grouped under a newgeneralorcommonsection.apps/app/src/locales/core/common.ts (3)
63-68: Consider removing duplicate string in empty states descriptionsThere appears to be duplicate strings in the empty states section:
descriptionanddescription_filtershave identical valuestitleandtitle_taskscould potentially be consolidated with a parameterempty_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 sectionThere's inconsistent naming between
dropFileHereanddropFileHereAlt. If they serve different purposes, consider using more descriptive names that indicate their specific use cases.
102-103: Empty object in fileUrl sectionThe
fileUrlsection 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 implementationapps/app/src/locales/settings/settings.ts (1)
72-74: Consider expanding the billing sectionThe 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 mappingThe
languagesconstant 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 constapps/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
registersection.success: "Test created successfully", - title_field: {apps/app/src/components/tables/vendor-tasks/empty-states.tsx (1)
39-54: NoTasks component with unused propThe component correctly displays a message when there are no tasks, but the
isEmptyprop 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 properlyWhile 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
nullowner 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
vendorsobject 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 theas constassertion ensures type safety.However, I notice there are only two status values defined in the
statussection (lines 80-83):not_assessedandin_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
statussection 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
SelectUsercomponent 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
RiskRegisterFilterstoVendorRegisterFilters, 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
getServerColumnHeadersfunction 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
useColumnsfunction provides well-structured client-side column definitions with proper i18n support. However, there's significant duplication between this file andserver-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 thestatusandcategory, 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-basedVendorStatus.
The selection logic for vendor status is straightforward. Iterating overVendorStatushelps 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:CreateVendorSheetimport 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.
Yourdb.$transactioncall withtx.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.allto 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 forfindManyandcount). 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.owneris defined. If a task has no owner, theownerproperty may benulland 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
⛔ Files ignored due to path filters (1)
apps/app/languine.lockis 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 filesGood addition of patterns to ignore temporary localization files:
- Added a clear comment section for "temp files"
- The wildcard pattern
*/locales/translations.jsonand*/locales/converter.tswill 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 consttyping.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-organizedThe common localization structure is comprehensive and well-organized with logical grouping of related strings. The use of
as constensures type safety and immutability of the object.apps/app/src/locales/settings/settings.ts (2)
1-211: Settings localization structure is comprehensiveThe 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 constassertion 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 4Length 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 4Once 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 goodThe theme localization structure is simple and appropriately defines the three theme options. The use of
as constensures type safety.apps/app/package.json (1)
14-14:Details
✅ Verification successful
New locale analysis script added
The new
analyze-locale-usagescript 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 15Length of output: 685
Locale Analysis Script Comment Verified
The new
analyze-locale-usagescript 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 simplifiedThe translation key has been updated from
sub_pages.tests.test_detailstotests.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 simplifiedThe translation key has been updated from
sub_pages.risk.registertorisk.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 simplifiedThe translation key has been updated from
sub_pages.policies.policy_detailstopolicies.policy_details, following the pattern of removing thesub_pagesprefix throughout the application.apps/app/src/locales/core/errors.ts (1)
1-8: Well-structured error messages implementationGood addition of a centralized error messages structure that will help maintain consistency across the application. The use of
as constensures type safety and immutability of the error message object.apps/app/src/locales/layout/sidebar.ts (1)
1-11: Well-structured localization constants for sidebarThe new sidebar localization constant is well-organized and follows TypeScript best practices with the
as constassertion, 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
authInterruptsfeature is correctly implemented. Since this is an experimental Next.js feature, be aware that:
- It may change or be removed in future releases
- There could be unexpected behavior or bugs
- Documentation might be limited
🌐 Web query:
What is the authInterrupts experimental feature in Next.js?💡 Result:
The
authInterruptsexperimental 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 nextConfigCore 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.tsxandapp/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
- Standardized error handling: Consistent patterns for authorization flows[5]
- Customizable UX: Tailored error pages beyond default HTTP status codes[3][6]
- 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:
- 1: https://nextjs.org/docs/app/api-reference/config/next-config-js/authInterrupts
- 2: vercel/next.js#12054
- 3: https://app.studyraid.com/en/read/13664/458507/experimental-authorization-apis-forbidden-unauthorized
- 4: vercel/next.js#77342
- 5: https://nextjs.org/blog/next-15-1
- 6: https://nextjs.org/docs/app/api-reference/functions/forbidden
- 7: https://nextjs.org/docs/app/api-reference/functions/unauthorized
- 8: https://nextjs.org/docs/app/api-reference/config/next-config-js
- 9: https://nextjs.org/docs/messages/experimental-app-dir-config
- 10: vercel/next.js#30913
Action Required: Verify Experimental Auth Feature Usage
The implementation of the experimental
authInterruptsflag inapps/app/next.config.tsis correct. As verified, this feature—introduced in Next.js 15.1—is intended to streamline authorization error handling via APIs likeforbidden()andunauthorized(). 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 pageThe new not_found localization constant is appropriately structured and uses the
as constassertion 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 structureThe translation key has been appropriately updated from what appears to be
dashboard.vendor_statustovendors.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 constantThe
user_menuconstant is properly structured with clear, concise translation keys for common user menu options. Usingas constassertion 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 patternThe 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 translationsThe
headerconstant 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 textThe
languageconstant 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 keyThe change to simplify the localization key from
t("sub_pages.policies.policy_details")tot("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 keyThe change to simplify the localization key from
t("sub_pages.risk.tasks.task_overview")tot("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 componentThis 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 componentsThe 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 constensures 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 constassertion 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")tot("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")tot("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 optionsThe 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 structureThe change simplifies the translation key from what was likely
sub_pages.risk.risk_overviewtorisk.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 feedbackThe 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 managementThe 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 contextThe 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 goodThe change from
t("sub_pages.risk.risk_comments")tot("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 approachThe change from
riskIdtovendorIdin theLayoutPropsinterface 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 goodThe change from
t("sub_pages.vendors.register")tot("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 frameworksThe new
frameworksconstant is well-organized with clear hierarchical structure and comprehensive coverage of UI text needs. The use ofas constensures 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
Linkwith 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
VendorStatuscomponent 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
marketingvariant 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
VendorRegisterTableRowan 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
VendortoVendorRegisterTableRow, 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
VendorTaskTypeinterface correctly matches the one defined incolumns.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
VendorTaskTypeinterface 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
FilterToolbarPropsinterface 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 onparams.
Havingparams: 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-wrappedparamsobject?
55-67: Metadata generation appears correct.
The use ofsetStaticParamsLocale(locale)andgetI18n()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.organizationIdis 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.
YourcreateVendorSchemaeffectively validates vendor inputs, including optionalwebsiteanddescription. 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 upstreamcreateVendorActionfor 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 inuseAction.
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 ofreact-hook-formwithzodResolver(createVendorSchema)is appropriate for typed validation. TheonSubmitfunction correctly callscreateVendor.execute(data). This is a strong, maintainable approach.
148-165: Optionalwebsitefield.
Markingwebsiteas 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.
Usingt("vendors.actions.create")clarifies the action to the user. Thedisabled={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 inButton,Card, andLinkto 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” toVendorManagementclarifies the new domain focus. Good consistency with your vendor-based approach.
26-26: Consolidated data retrieval withgetVendorOverview.
This direct call to a specialized function for fetching vendor metrics is a clean separation of concerns, making theVendorManagementcomponent 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
searchParamsandparamsas Promises is unconventional in many Next.js setups. Verify that they're intentionally wrapped in promises, as it can lead to extraawaitcalls. 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 ingenerateMetadata.Ensure the
localeparameter is valid or falls back appropriately. Without a fallback, an invalidlocalecould cause unexpected metadata results or errors.apps/app/src/locales/analyze-locale-usage.ts (1)
405-489: Confirm cross-platform compatibility.Reliance on
grepand 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-grepor a Node-based parser?apps/app/src/locales/no.ts (3)
953-959: Localized unauthorized error message acknowledged.The new
unauthorizederror 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
dashboardkey (title: "Oversikt") under thevendorssubsection. Verify it doesn’t conflict or cause confusion with any higher-leveldashboardobjects.
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_departmenttorisk.dashboard.by_departmentaligns 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_statustorisk.dashboard.statusmaintains 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 constassertion 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 = ({ |
There was a problem hiding this comment.
🛠️ 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) => ({| 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, | ||
| } | ||
| }); |
There was a problem hiding this comment.
💡 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 10Length 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 tosession.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.tsorupdate-organization-name-action.ts) to enforce this rule.
| 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 |
There was a problem hiding this comment.
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.
| 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" | |
| }; | |
| } | |
| }); |
| {/* <DataTablePagination | ||
| pageCount={pageCount} | ||
| currentPage={currentPage} | ||
| onPageChange={onPageChange} | ||
| /> */} |
There was a problem hiding this comment.
💡 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
DataTablePaginationcomponent properly (by passing the appropriate props such aspageCount,currentPage, andonPageChange), 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.
| const table = useReactTable({ | ||
| data, | ||
| columns: columnHeaders, | ||
| getCoreRowModel: getCoreRowModel(), | ||
| manualPagination: true, | ||
| pageCount, | ||
| }); |
There was a problem hiding this comment.
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.
| 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 | |
| }, | |
| }, | |
| }); |
| <TableRow> | ||
| <TableCell | ||
| colSpan={columnHeaders.length} | ||
| className="h-24 text-center" | ||
| > | ||
| No results. | ||
| </TableCell> | ||
| </TableRow> |
There was a problem hiding this comment.
🛠️ 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.
| <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> | |
| } |
| const vendor = await db.vendor.findUnique({ | ||
| where: { | ||
| id: vendorId, | ||
| organizationId: session.user.organizationId, | ||
| }, | ||
| include: { | ||
| owner: true, | ||
| comments: { | ||
| orderBy: { | ||
| createdAt: "desc", | ||
| }, | ||
| }, | ||
| }, | ||
| }); | ||
|
|
||
| if (!vendor) { | ||
| redirect("/vendors"); | ||
| } |
There was a problem hiding this comment.
🛠️ 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.
| 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`); | |
| } |
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
apps/app/languine.lockis 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 3Length 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.unauthorizedare 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 3Length 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.
| 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" | ||
| } |
There was a problem hiding this comment.
💡 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 3Length 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.
Summary by CodeRabbit
New Features
Refactor
Chores