Rb nov 11 policy templates library#2716
Conversation
…into rb-nov-11-policy-templates-library
…into rb-nov-11-policy-templates-library
…into rb-nov-11-policy-templates-library
…into rb-nov-11-policy-templates-library
- Update all policy template tags to use official predefined tags from backend - Create reusable TagChip component with color-coded badge styling - Change default pagination to 10 items per page with localStorage persistence - Convert category labels from title case to sentence case - Update Policy Manager descriptions to highlight template library features - Add tag color mappings for all 15 official tags (Fairness, Privacy, Security, etc.) - Improve helper drawer content with template browsing guidance 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…ble styling - Implement three-state column sorting (asc/desc/none) with localStorage persistence - Add sort icons and visual indicators matching incident management table UX - Highlight sorted columns with background color for better visibility - Fix policy table header styling bug (style to sx) to enable hover effects - Add click handlers to all table headers for intuitive sorting interaction - Persist sort preferences across sessions using localStorage 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…orting - Remove sorting functionality from TAGS column as requested - Fix header text visibility issue by adding whiteSpace nowrap - Prevent header text and icons from shrinking with flexShrink 0 - Remove background highlighting from TAGS column cells - Clean up sorting logic to exclude TAGS handling 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Remove sorting state, handlers, and localStorage persistence - Remove sort icons and click handlers from table headers - Revert table body to use filteredPolicyTemplates instead of sortedPolicyTemplates - Remove column highlighting for sorted columns - Revert PolicyTable component sx fix back to style 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
WalkthroughRefactors policies routing to nested Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Router as App Router
participant Dashboard as PolicyDashboard
participant Manager as PolicyManager
participant Templates as PolicyTemplates
participant Modal as PolicyDetailModal
participant API
User->>Router: Navigate to /policies or /policies/templates
Router->>Dashboard: Render with path-based tab
alt /policies
Dashboard->>Manager: render PolicyManager(policies,tags,fetchAll)
Manager->>API: fetchAll()
Manager->>Manager: filter/search, open PolicyDetailModal (create/edit)
Manager->>API: save/delete actions
else /policies/templates
Dashboard->>Templates: render PolicyTemplates(tags,fetchAll)
Templates->>Templates: load PolicyTemplates.json
User->>Templates: Filter / Search / Paginate
User->>Templates: Click template -> Templates->>Modal: open(template)
Modal->>Modal: prefill editor from template or policy
User->>Modal: Save -> Modal->>API: create policy
API-->>Modal: success -> Modal->>Templates: onSaved() -> Templates refresh
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Clients/src/presentation/components/Policies/PolicyDetailsModal.tsx (1)
227-235: The review comment is accurate and identifies a real major issue. Data loss is confirmed possible when editing policies.The code at lines 287–320 confirms the vulnerability:
Policy editing scenario: When a user is editing an existing policy and the
usershook refreshes (or any re-render causesusersto be a new reference), theuseEffectre-runs becauseusersis in the dependency array[policy, template, users]. This executes theif (policy)branch and callssetFormData()with the original policy data, discarding any unsaved form edits (title, tags, content, reviewers).Template initialization: While the template branch uses spread (
...formData), the effect still unnecessarily re-runs on everyuserschange, which is inefficient and adds risk.Root cause: The
usersdependency is necessary for the reviewer mapping (policy.assigned_reviewer_ids.map((i) => users.find(...))), but including it in the dependency array causes full form resets instead of just updating reviewers.The suggested refactoring—separating into two effects with tighter dependency scopes—correctly resolves this:
- Policy effect:
[policy, users]only- Template effect:
[template, policy]onlyThis prevents user edits from being lost when
usersrefreshes during editing or template creation.
🧹 Nitpick comments (12)
Clients/src/domain/enums/policy.enum.ts (1)
1-8: Enum definition looks good; just ensure backend/category alignment.The
PolicyTemplateCategoryenum is well-typed and readable. Please double-check that these string values exactly match whatever the backend or template JSON expects (if any mapping is done there), so you don’t end up with subtle mismatches at integration time.Clients/src/presentation/components/Tags/TagChip.tsx (1)
8-53: Tag-to-style mapping is solid; consider hoisting and simplifying lookup.Logic and defaults look good, and the case-insensitive behavior matches the comment about official tags.
As optional cleanup:
- Move
tagStylesoutsidegetTagStyleso it isn’t reallocated on every call.- Since you normalize to
tagLower, you can avoid the loop and just do a direct lookup:const TAG_STYLES: Record<string, { bg: string; color: string }> = { /* ... */ }; const getTagStyle = (tag: string) => TAG_STYLES[tag.toLowerCase()] ?? { bg: "#F5F5F5", color: "#616161", };Clients/src/application/config/routes.tsx (1)
82-85: Nested/policiesroutes are redundant without an<Outlet />; consider simplifying.Right now you have:
<Route path="/policies" element={<PolicyDashboard />}> <Route index element={<PolicyDashboard />} /> <Route path="templates" element={<PolicyDashboard />} /> </Route>Since
PolicyDashboarddoesn’t render an<Outlet />, the child route elements are effectively unused; navigation to/policiesand/policies/templatesis already handled by the parentelementand yourlocation.pathnamelogic insidePolicyDashboard.You can simplify and avoid confusion by flattening this to:
<Route path="/policies" element={<PolicyDashboard />} /> <Route path="/policies/templates" element={<PolicyDashboard />} />This preserves behavior while making the routing intent clearer.
Clients/src/domain/interfaces/IPolicy.ts (1)
66-75: Props for PolicyManager/PolicyTemplates are reasonable; refinefetchAlltyping.
PolicyManagerPropsandPolicyTemplatesPropsare clear and map well onto howPoliciesDashboardpasses data down.Since
fetchAllis implemented as anasyncfunction inPoliciesDashboard, it would be slightly more accurate to type it as returning a promise:export interface PolicyManagerProps { policies: PolicyManagerModel[]; tags: string[]; fetchAll: () => Promise<void>; } export interface PolicyTemplatesProps { tags: string[]; fetchAll: () => Promise<void>; }This keeps call sites unchanged but better documents the async nature of the reload.
Clients/src/presentation/pages/PolicyDashboard/PoliciesDashboard.tsx (1)
21-30: PreferuseLocationover the globallocationfor tab state.Using
location.pathnamedirectly works in the browser, but relying on the globallocationcan be brittle (e.g., SSR, tests, or if react-router location ever diverges fromwindow.location).You can make this more idiomatic and robust by switching to
useLocation:import { useNavigate, useLocation } from "react-router-dom"; const PolicyDashboard: React.FC = () => { const navigate = useNavigate(); const { pathname } = useLocation(); const isPolicyTemplateTab = pathname.includes("/policies/templates"); const activeTab = isPolicyTemplateTab ? "templates" : "policies"; // ... };This keeps tab selection in lockstep with the router state.
Clients/src/presentation/components/Policies/PolicyDetailsModal.tsx (1)
111-117: Template-aware close handling is consistent; minor scope consideration.Wiring
handleClosethroughuseModalKeyHandling, the DraweronClose, and the close icon ensures that all close paths resetformDataand then delegate to the parent viaonClose. That’s a good centralization of close logic.One thing to be aware of:
handleClosecurrently only resetsformData. If the parent ever chooses to keep this component mounted and just toggle visibility (vs unmounting it), you may also want to reseterrorsand other local state there to avoid leaking old validation errors into a fresh open.Also applies to: 167-182, 600-605, 632-636
Clients/src/presentation/pages/PolicyDashboard/PolicyTemplates.tsx (5)
46-48: AvoidanyforSelectorVerticalprops; use a typed alias
SelectorVerticalcurrently usesprops: any, which drops type safety for the pagination select icon.You can keep it typed while staying simple:
-const SelectorVertical = (props: any) => ( - <ChevronsUpDown size={16} {...props} /> -); +const SelectorVertical = (props: React.ComponentProps<typeof ChevronsUpDown>) => ( + <ChevronsUpDown size={16} {...props} /> +);Based on learnings.
72-86: Remove debugconsole.logfrom template selection
console.log(selectedPolicy);inhandleSelectPolicyTemplateis a debug artifact and will spam the console every row click in production.Consider removing it:
- const selectedPolicy = policyTemplates.find((policy) => policy.id === id); - console.log(selectedPolicy); + const selectedPolicy = policyTemplates.find((policy) => policy.id === id);
203-215: Tune empty-state copy for templates contextThe empty state message says "No policies found in database", but this view is for policy templates loaded from static JSON and/or filters.
Consider a more specific message such as:
- <EmptyState message="No policies found in database" /> + <EmptyState message="No policy templates match your filters" />This better reflects what the user is seeing.
220-234: Add keyboard activation for clickable table rowsRows are focusable (
tabIndex={0}) and haverole="button", but there is no keyboard handler, so pressing Enter/Space does nothing.Example:
- <TableRow + <TableRow key={policy.id} onClick={() => handleSelectPolicyTemplate(policy.id)} + onKeyDown={(e) => { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + handleSelectPolicyTemplate(policy.id); + } + }} sx={{ cursor: "pointer",This improves accessibility for keyboard-only users.
140-169: Hardcoded white background may not match theme (optional)Both the filter
Selectand search container usebgcolor: "#fff". This can clash with non-light themes and bypass theming.Consider using theme values instead, e.g.:
- sx={{ - minWidth: "225px", - height: "34px", - bgcolor: "#fff", - }} + sx={{ + minWidth: "225px", + height: "34px", + bgcolor: theme.palette.background.paper, + }}Same idea for other
"#fff"usages in this file.Clients/src/presentation/pages/PolicyDashboard/PolicyManager.tsx (1)
19-236: PolicyManager behavior and structure look correct; consider deduplicating shared patternsThe component cleanly handles syncing incoming policies, auto-opening the create modal from navigation state, filter + search, delete flow with alerts, and conditional modal rendering based on tags. No functional issues stand out.
You might later factor shared pieces (e.g.,
handleSavedalert pattern and filter/search logic) into small helpers or hooks shared withPolicyTemplates, but that’s optional.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
Clients/src/application/config/routes.tsx(1 hunks)Clients/src/domain/enums/policy.enum.ts(1 hunks)Clients/src/domain/interfaces/IPolicy.ts(2 hunks)Clients/src/presentation/components/Policies/PolicyDetailsModal.tsx(8 hunks)Clients/src/presentation/components/Tags/TagChip.tsx(1 hunks)Clients/src/presentation/components/Tags/index.ts(1 hunks)Clients/src/presentation/pages/PolicyDashboard/PoliciesDashboard.tsx(2 hunks)Clients/src/presentation/pages/PolicyDashboard/PolicyManager.tsx(1 hunks)Clients/src/presentation/pages/PolicyDashboard/PolicyTemplates.tsx(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-11-18T05:28:01.115Z
Learnt from: MuhammadKhalilzadeh
Repo: bluewave-labs/verifywise PR: 254
File: Clients/src/presentation/components/Modals/Controlpane/index.tsx:22-24
Timestamp: 2024-11-18T05:28:01.115Z
Learning: In `Clients/src/presentation/components/Modals/Controlpane/index.tsx` (TypeScript React component), avoid changing prop types from specific types like `string` to `any` when the exact type is known. Maintaining explicit types ensures better type safety and code reliability.
Applied to files:
Clients/src/presentation/pages/PolicyDashboard/PoliciesDashboard.tsxClients/src/presentation/components/Policies/PolicyDetailsModal.tsxClients/src/presentation/pages/PolicyDashboard/PolicyManager.tsxClients/src/presentation/pages/PolicyDashboard/PolicyTemplates.tsx
🧬 Code graph analysis (5)
Clients/src/domain/interfaces/IPolicy.ts (1)
Clients/src/domain/models/Common/policy/policyManager.model.ts (1)
PolicyManagerModel(25-55)
Clients/src/presentation/components/Tags/TagChip.tsx (1)
Clients/src/presentation/components/Tags/index.ts (1)
TagChipProps(2-2)
Clients/src/presentation/pages/PolicyDashboard/PoliciesDashboard.tsx (1)
Clients/src/domain/models/Common/policy/policyManager.model.ts (1)
PolicyManagerModel(25-55)
Clients/src/presentation/pages/PolicyDashboard/PolicyManager.tsx (5)
Clients/src/domain/interfaces/IPolicy.ts (1)
PolicyManagerProps(66-70)Clients/src/domain/models/Common/policy/policyManager.model.ts (1)
PolicyManagerModel(25-55)Clients/src/domain/interfaces/iAlert.ts (1)
AlertProps(22-31)Clients/src/application/tools/alertUtils.ts (1)
handleAlert(32-32)Clients/src/application/repository/policy.repository.ts (1)
deletePolicy(58-64)
Clients/src/presentation/pages/PolicyDashboard/PolicyTemplates.tsx (2)
Clients/src/domain/interfaces/IPolicy.ts (2)
PolicyTemplatesProps(72-75)PolicyTemplate(35-39)Clients/src/domain/interfaces/iAlert.ts (1)
AlertProps(22-31)
🪛 GitHub Actions: Frontend Checks
Clients/src/presentation/pages/PolicyDashboard/PolicyTemplates.tsx
[error] 14-14: TypeScript error TS6133: 'Typography' is declared but its value is never read.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (7)
Clients/src/presentation/components/Tags/TagChip.tsx (1)
55-75: Chip rendering is straightforward and consistent with MUI patterns.The
TagChipcomponent is appropriately typed, keeps styling encapsulated viasx, and uppercases the visible text while preserving the originaltagvalue. No issues from a UI/props perspective.Clients/src/presentation/components/Tags/index.ts (1)
1-2: Barrel export is clean and ergonomic.Re-exporting both
TagChipandTagChipPropsfrom the index keeps imports tidy for consumers (import { TagChip } from "…/Tags"). No issues here.Clients/src/domain/interfaces/IPolicy.ts (2)
35-39: PolicyTemplate shape matches the modal/template flow.
PolicyTemplatehavingtitle,tags, andcontentis consistent with howPolicyDetailsModaluses templates to prefill the form and editor. No structural issues from a typing standpoint.
41-47: Template support on the detail modal props is well-scoped.Adding
template?: PolicyTemplatetoPolicyDetailModalPropskeeps the existing API intact while enabling template-based prefills. This is a good, minimal surface-area change.Clients/src/presentation/pages/PolicyDashboard/PoliciesDashboard.tsx (1)
52-140: Tabbed composition and helper UX wiring look coherent.The
TabContext+TabBarsetup, conditional rendering ofPolicyManagervsPolicyTemplates, andPageTourdriven byisInitialLoadCompleteform a clear, maintainable structure. PassingfetchAllandtagsdown gives both tabs a single source of truth for data.No blocking issues in this section.
Clients/src/presentation/components/Policies/PolicyDetailsModal.tsx (1)
426-501: Content initialization from policy/template is correct and reasonably safe.Using
policy?.content_html || template?.contentwith DOMPurify sanitization before deserializing into the Plate editor is a good approach. The allowed tags/attrs list looks strict enough to avoid obvious XSS vectors while still supporting rich text, links, and images.Behaviorally, this means:
- Editing an existing policy loads its saved HTML.
- Creating from a template loads the template’s content.
No functional issues here given the current sanitize configuration.
Clients/src/presentation/pages/PolicyDashboard/PolicyTemplates.tsx (1)
140-379: Overall PolicyTemplates implementation looks solidSearch + category filter, pagination (with persisted rowsPerPage), tag chips, and template-driven modal opening are wired cleanly. Aside from the minor issues above, the component is cohesive and matches the described UX.
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
Clients/src/presentation/pages/PolicyDashboard/PolicyTemplates.tsx (1)
282-288: Fix pagination summary for zero items case.This issue was previously flagged: when there are no filtered results, the summary incorrectly displays "Showing 1 - 0 of 0 items" because the start index always computes as
page * rowsPerPage + 1. The start should be 0 when the list is empty.Apply this diff:
- Showing {page * rowsPerPage + 1} -{" "} + Showing{" "} + {filteredPolicyTemplates.length === 0 + ? 0 + : page * rowsPerPage + 1}{" "} + -{" "} {Math.min( page * rowsPerPage + rowsPerPage, filteredPolicyTemplates.length, )}{" "} of {filteredPolicyTemplates.length} items
🧹 Nitpick comments (1)
Clients/src/presentation/pages/PolicyDashboard/PolicyTemplates.tsx (1)
45-47: Consider typing the props parameter.The
anytype reduces type safety. For better type inference, consider usingReact.ComponentProps<typeof ChevronsUpDown>orReact.SVGProps<SVGSVGElement>.Apply this diff:
-const SelectorVertical = (props: any) => ( +const SelectorVertical = (props: React.ComponentProps<typeof ChevronsUpDown>) => ( <ChevronsUpDown size={16} {...props} /> );
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
Clients/src/presentation/pages/PolicyDashboard/PolicyTemplates.tsx(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-11-18T05:28:01.115Z
Learnt from: MuhammadKhalilzadeh
Repo: bluewave-labs/verifywise PR: 254
File: Clients/src/presentation/components/Modals/Controlpane/index.tsx:22-24
Timestamp: 2024-11-18T05:28:01.115Z
Learning: In `Clients/src/presentation/components/Modals/Controlpane/index.tsx` (TypeScript React component), avoid changing prop types from specific types like `string` to `any` when the exact type is known. Maintaining explicit types ensures better type safety and code reliability.
Applied to files:
Clients/src/presentation/pages/PolicyDashboard/PolicyTemplates.tsx
🧬 Code graph analysis (1)
Clients/src/presentation/pages/PolicyDashboard/PolicyTemplates.tsx (3)
Clients/src/domain/interfaces/IPolicy.ts (2)
PolicyTemplatesProps(72-75)PolicyTemplate(35-39)Clients/src/domain/interfaces/iAlert.ts (1)
AlertProps(22-31)Clients/src/application/tools/alertUtils.ts (1)
handleAlert(32-32)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Frontend Checks / build-check
🔇 Additional comments (10)
Clients/src/presentation/pages/PolicyDashboard/PolicyTemplates.tsx (10)
1-35: LGTM - Imports are clean and well-organized.All imports are used appropriately in the component. The previous
Typographyimport issue has been resolved.
49-64: LGTM - State management is well-structured.The component properly initializes state with appropriate types and retrieves pagination preferences from storage.
87-122: LGTM - Filtering and callback logic is correct.The filtering logic efficiently handles both category and search filters with appropriate memoization. The page reset on filter change prevents showing empty pages.
124-137: LGTM - Pagination handlers are well-implemented.The handlers correctly manage pagination state and persist user preferences to storage.
170-201: LGTM - Table structure and sticky header are well-implemented.The scrollable container with sticky header provides good UX. The styling appropriately uses theme values for colors and spacing.
215-263: LGTM - Table rows are well-implemented with good accessibility.The row implementation includes proper hover states, keyboard navigation support, and semantic ARIA labels. The ellipsis handling for long text is appropriate.
289-349: LGTM - Pagination controls are properly styled with theme values.The custom pagination dropdown configuration is thorough and correctly uses theme values for consistent styling.
367-375: LGTM - Alert implementation is correct.The alert component properly displays success messages with toast behavior and clears on click.
380-380: LGTM - Proper default export.
357-365: Modal won't render if user clicks template row before tags finish loading.The guard
tags.length > 0is intentional—the modal needs available tags to populate a selector. However, this creates a UX issue: if the API fetching tags is slow or fails, clicking a template row will silently fail (setShowModal(true) executes but the modal never renders because the condition fails). Users get no feedback.Recommend one of:
- Disable template rows until tags are loaded
- Show a loading indicator while tags are fetching
- Handle the empty tags case gracefully in the modal instead of blocking it
|
LGTM. |
|
@MuhammadKhalilzadeh LGTM! |
Br0wnHammer
left a comment
There was a problem hiding this comment.
LGTM!
One nit: please break large PRs into smaller chunks so as to make it easier to review them.
Describe your changes
Created a Tab named "Policy Templates" under the policy page to display a list of policy templates. Upon clicking the template, the "Create new Policy" drawer opens with the template data filled in the form. The user can create a new policy based on those templates.
Write your issue number after "Fixes "
Fixes #2652
Screenshots
Please ensure all items are checked off before requesting a review: