Credential Actions Implementation and WIP#20
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. WalkthroughAdds credential duplication backend and client hook; introduces ARCHIVED account status across DB/schema/entities/UI; refactors item actions into credential-scoped components (adds Unarchive); adds Move/Delete/QR dialogs and a responsive alert dialog; centralizes date formatting; enhances platform lookup utilities; expands encryption legacy decryption; adds breadcrumb Zod schemas and docs. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as User
participant UI as CredentialActions (Dropdown/Context)
participant H as useDuplicateCredential Hook
participant R as credential.duplicate Router
participant DB as Database
Note over UI: Menu includes Duplicate action
U->>UI: Click "Duplicate"
UI->>H: mutate({ credentialId })
H->>R: POST duplicateCredential(input)
R->>DB: Begin transaction
R->>DB: Read original credential, metadata, KVPs
R->>DB: Create duplicated credential + metadata + empty encrypted KVPs
DB-->>R: New credential row
R-->>H: CredentialOutput
H-->>UI: onSuccess (invalidate + cache update)
UI-->>U: Show success toast and refreshed list
sequenceDiagram
autonumber
actor U as User
participant UI as CredentialActions
participant P as Permissions
participant D as Dialogs (Move/Delete/QR)
U->>UI: Open menu
UI->>P: Check permission (e.g., canMove)
alt Move selected & permitted
UI->>D: Open Move dialog
D->>DB: Update credential container
DB-->>D: Success/Failure
D-->>U: Toast + close/reset
else Move selected & not permitted
UI-->>U: Show permission error toast
else Delete selected
UI->>D: Open Delete dialog
D->>DB: Delete mutation
DB-->>D: Success/Failure
else QR selected
UI->>D: Open QR dialog
D-->>U: Configure/Save/Download
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (7)
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 |
…ption (#21) * chore: preserve 'iv' in duplicate * fix: resolved 'eslint' errors * fix: resolved 'entity' missing 'AccountStatus' * chore: removed 'default' case * fix: resolved 'encrypt' * fix: resolved 'conflict' duplicate
Co-authored-by: Malek Gara-Hellal <malek.gara@JF-QPY7J407RM.local>
|
@coderabbitai please give this a review |
|
@FindMalek: I'll review the changes in this pull request right away! ✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
components/app/dashboard-credential-cards-view.tsx (1)
93-103: Prevent navigation when pressing “Copy”Removing the dedicated handler also dropped the event.stopPropagation(). The button click now bubbles to the card’s onClick, so copying immediately navigates to the detail view. Wrap the handler to stop propagation before calling copy so the button remains usable.
- <Button + <Button variant="ghost" size="sm" className="hover:text-primary opacity-0 transition-all group-hover/identifier:opacity-100" - onClick={async () => await copy(credential.identifier)} + onClick={async (event) => { + event.stopPropagation() + await copy(credential.identifier) + }} >
🧹 Nitpick comments (14)
components/ui/responsive-alert-dialog.tsx (2)
65-69: Consider adding displayName for better debugging.Adding a display name to the context improves the debugging experience in React DevTools.
Apply this diff:
const ResponsiveAlertDialogContext = React.createContext<{ isMobile: boolean }>({ isMobile: false, }) + +ResponsiveAlertDialogContext.displayName = "ResponsiveAlertDialogContext"
100-206: Well-structured subcomponents with consistent API.All subcomponents correctly consume context and conditionally render appropriate variants. The mobile variants have sensible default styles that can be overridden via className.
Consider adding JSDoc comments to document usage and the requirement that subcomponents must be used within
ResponsiveAlertDialog:/** * A responsive alert dialog that renders as a Drawer on mobile and AlertDialog on desktop. * * @example * <ResponsiveAlertDialog open={open} onOpenChange={setOpen}> * <ResponsiveAlertDialogContent> * <ResponsiveAlertDialogHeader> * <ResponsiveAlertDialogTitle>Title</ResponsiveAlertDialogTitle> * <ResponsiveAlertDialogDescription>Description</ResponsiveAlertDialogDescription> * </ResponsiveAlertDialogHeader> * <ResponsiveAlertDialogFooter> * <ResponsiveAlertDialogCancel>Cancel</ResponsiveAlertDialogCancel> * </ResponsiveAlertDialogFooter> * </ResponsiveAlertDialogContent> * </ResponsiveAlertDialog> */ function ResponsiveAlertDialog({ open, onOpenChange, children }: ResponsiveAlertDialogProps) { // ... existing implementation }components/app/dashboard-credential-key-value-pairs.tsx (1)
27-31: Consider wrapping in React.memo for performance.Since the component already uses multiple optimization hooks (
useCallback,useMemo), wrapping it inReact.memocould prevent unnecessary re-renders when parent props haven't changed.-export function CredentialKeyValuePairs({ +export const CredentialKeyValuePairs = React.memo(function CredentialKeyValuePairs({ credentialId, onFormChange, onDataChange, -}: CredentialKeyValuePairsProps) { +}: CredentialKeyValuePairsProps) { + // ... component body +})components/app/dashboard-qr-code-dialog.tsx (2)
46-46: Avoid hardcoding the domain in the default URL.The default URL uses a hardcoded domain (
https://zero-locker.app). Consider using an environment variable or configuration constant to make this flexible across environments (development, staging, production).+import { siteConfig } from "@/config/site" + const form = useForm<QrCodeFormData>({ resolver: zodResolver(qrCodeFormSchema), defaultValues: { - url: `https://zero-locker.app/credential/${credentialId}`, + url: `${siteConfig.url}/credential/${credentialId}`, requirePassword: false, password: "", }, })
54-68: Implement QR code generation functionality.The placeholder handlers for download and copy are incomplete. Consider implementing the actual QR code generation logic using a library like
qrcode.reactor similar.Do you want me to generate an implementation for QR code generation, download, and copy functionality?
components/app/dashboard-credential-delete-dialog.tsx (1)
104-104: Replaceconsole.logwithconsole.errorin error handler.In error handling paths, use
console.errorinstead ofconsole.logto properly categorize the log level and make errors more visible in production monitoring.} catch (error) { - console.log(error) + console.error("Failed to delete credential:", error) toast("Failed to delete credential. Please try again later.", "error") }components/app/dashboard-credential-move-dialog.tsx (2)
182-201: Add explicit button types (accessibility/guidelines)Buttons should declare type. Add type="button" to both.
- <Button + <Button + type="button" variant="outline" onClick={() => handleOpenChange(false)} disabled={isMoving} className="order-2 sm:order-1" > Cancel </Button> - <Button + <Button + type="button" onClick={form.handleSubmit(handleMove)} disabled={ isMoving || selectedContainerId === credential.containerId || !canMoveCredential } className="order-1 disabled:opacity-50 sm:order-2" >[Based on coding guidelines]
98-101: Avoid console in production pathsReplace console.error with your logging utility or remove it; rely on toast and mutation error handling.
- console.error("Failed to move credential:", error) toast("Failed to move credential. Please try again later.", "error")lib/date-utils.ts (1)
117-137: Handle future dates in getRelativeTimeFuture timestamps produce “-Xd ago”. Clamp or return “in Xd/Today”.
export function getRelativeTime(date: Date | null): string { if (!date) return "Never" const now = new Date() // Use date-fns timezone-aware functions for today/yesterday detection if (isToday(date)) return "Today" if (isYesterday(date)) return "Yesterday" // Use date-fns precise difference calculations - const days = differenceInDays(now, date) - const weeks = differenceInWeeks(now, date) - const months = differenceInMonths(now, date) - const years = differenceInYears(now, date) + const days = differenceInDays(now, date) + if (days < 0) return "Today" + const weeks = differenceInWeeks(now, date) + const months = differenceInMonths(now, date) + const years = differenceInYears(now, date)lib/utils/index.ts (2)
24-24: Use absolute import for top-level modulesReplace relative import with "@/lib/date-utils" per guidelines.
- import { DateFormatter, getRelativeTime } from "../date-utils" + import { DateFormatter, getRelativeTime } from "@/lib/date-utils"[As per coding guidelines]
343-350: Minor: consistent phrasingStrings look good; consider leaving “Created recently” without “at ” if you want less redundancy, but current output is acceptable.
lib/encryption.ts (2)
205-208: Remove console usage in crypto pathsAvoid console in library code. Prefer a logger or omit.
- console.error("AES-GCM fallback also failed:", gcmError) throw cbcError
282-292: Hardcoded seed keys in shared libSEED_ENCRYPTION_CONFIG with zeroed keys is fine for seeds, but keep it out of general-purpose runtime libs to avoid accidental use and static scanner noise. Move to a seed-specific module (e.g., scripts/seed/encryption.ts) and import only in seed code.
components/shared/item-actions-dropdown.tsx (1)
218-233: Route error logs through an error loggerError branches still call
console.log(error). Please switch to an error logger (or at leastconsole.error) so failures surface correctly without violating our “no console logging” rule for success paths.Also applies to: 244-255, 259-269, 304-326, 358-373, 385-395, 399-409, 443-461
| {Array.from({ length: 64 }).map((_, i) => ( | ||
| <div | ||
| key={i} | ||
| className={`size-3 rounded-sm ${ | ||
| Math.random() > 0.5 ? "bg-current" : "bg-transparent" | ||
| }`} | ||
| style={{ color: qrCodeColor }} | ||
| /> | ||
| ))} |
There was a problem hiding this comment.
Math.random() in render causes unnecessary re-renders.
Using Math.random() directly in the render loop generates a new random value on every render, causing the mock QR code pattern to change unexpectedly. Consider using useMemo to stabilize the pattern or replace the mock with the actual QR code implementation.
+import { useMemo } from "react"
+
export function DashboardQrCodeDialog({
open,
onOpenChange,
credentialId,
}: QrCodeDialogProps) {
+ const mockPattern = useMemo(() =>
+ Array.from({ length: 64 }).map(() => Math.random() > 0.5),
+ []
+ )
+
// ... rest of component
<div className="grid grid-cols-8 gap-1">
- {Array.from({ length: 64 }).map((_, i) => (
+ {mockPattern.map((filled, i) => (
<div
key={i}
className={`size-3 rounded-sm ${
- Math.random() > 0.5 ? "bg-current" : "bg-transparent"
+ filled ? "bg-current" : "bg-transparent"
}`}
style={{ color: qrCodeColor }}
/>
))}
</div>🤖 Prompt for AI Agents
In components/app/dashboard-qr-code-dialog.tsx around lines 129 to 137, the
render uses Math.random() inside the JSX map which produces a new random pattern
on every render; replace that with a stable, memoized pattern (e.g. use useMemo
to generate an array of booleans once or when dependencies change) and map over
that memoized array to set the className, or swap this mock with the actual QR
code implementation so the pattern does not change on re-renders.
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
components/app/dashboard-credential-cards-view.tsx (2)
62-66: Make the clickable row accessible (keyboard) or use LinkAdd role, tabIndex, and key handler (or wrap with next/link) so keyboard users can activate the row.
- <div + <div className="dark:hover:bg-secondary/50 hover:border-secondary-foreground/20 border-secondary group flex cursor-pointer items-center gap-4 rounded-lg border-2 p-4 transition-colors duration-200 hover:shadow-sm" - onClick={() => handleCardClick(credential.id)} + onClick={() => handleCardClick(credential.id)} + role="link" + tabIndex={0} + onKeyDown={(e) => { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault() + handleCardClick(credential.id) + } + }} >
91-96: Prevent navigation when clicking copy; also set button typeStops bubbling to the row onClick and ensures button type is explicit.
- <Button + <Button variant="ghost" size="sm" className="hover:text-primary opacity-0 transition-all group-hover/identifier:opacity-100" - onClick={async () => await copy(credential.identifier)} + type="button" + onClick={async (e) => { + e.stopPropagation() + await copy(credential.identifier) + }} >
🧹 Nitpick comments (7)
components/app/dashboard-credential-delete-dialog.tsx (2)
1-35: Consider organizing imports per coding guidelines.The import statements could be better organized to follow the established pattern: React imports, third-party libraries, internal absolute imports, then type-only imports. Currently, entity/utility imports are interspersed with third-party libraries.
Apply this diff to reorganize imports:
"use client" import Image from "next/image" import { useRouter } from "next/navigation" +import { zodResolver } from "@hookform/resolvers/zod" +import { useForm } from "react-hook-form" +import { z } from "zod" + import { PlatformEntity } from "@/entities/utils/platform" +import { useToast } from "@/hooks/use-toast" +import { getLogoDevUrlWithToken, getPlaceholderImage } from "@/lib/utils" import { useDeleteCredential } from "@/orpc/hooks/use-credentials" -import type { CredentialOutput } from "@/schemas/credential/dto" -import type { PlatformSimpleRo } from "@/schemas/utils/platform" -import { zodResolver } from "@hookform/resolvers/zod" -import { useForm } from "react-hook-form" -import { z } from "zod" - -import { getLogoDevUrlWithToken, getPlaceholderImage } from "@/lib/utils" -import { useToast } from "@/hooks/use-toast" import { Icons } from "@/components/shared/icons" import { Button } from "@/components/ui/button" import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form" import { Input } from "@/components/ui/input" import { ResponsiveAlertDialog, ResponsiveAlertDialogCancel, ResponsiveAlertDialogContent, ResponsiveAlertDialogDescription, ResponsiveAlertDialogFooter, ResponsiveAlertDialogHeader, ResponsiveAlertDialogTitle, } from "@/components/ui/responsive-alert-dialog" + +import type { CredentialOutput } from "@/schemas/credential/dto" +import type { PlatformSimpleRo } from "@/schemas/utils/platform"
37-39: Consider adding schema-level validation for the identifier match.The schema currently only validates that the confirmation text is non-empty. The actual match validation against
credential.identifieroccurs in the handler function (Line 82). While this works, incorporating the match validation into the schema using Zod'srefinemethod would provide more robust form-level validation and better error messaging.However, this would require passing
credential.identifierinto the schema definition or using a dynamic schema factory function:const createDeleteCredentialSchema = (expectedIdentifier: string) => z.object({ confirmationText: z .string() .min(1, "Confirmation text is required") .refine((val) => val === expectedIdentifier, { message: "The identifier doesn't match. Please type it exactly as shown.", }), })Then update the form setup to use the dynamic schema:
const form = useForm<DeleteCredentialFormData>({ resolver: zodResolver(createDeleteCredentialSchema(credential.identifier)), defaultValues: { confirmationText: "", }, })This would eliminate the manual validation check at Line 82-88.
components/app/dashboard-credential-grid-view.tsx (3)
72-86: Ensure copy button doesn’t submit forms and is explicitSet type="button" to avoid accidental form submission in composite UIs.
- <Button + <Button variant="ghost" size="sm" className="size-8 p-0" + type="button" onClick={async (e) => { e.stopPropagation() await copy(credential.identifier) }} >
39-42: Optional: pre-index platforms for O(1) lookupsFor large lists, computing a Map once avoids repeated array scans.
- const platform = PlatformEntity.findById( - platforms, - credential.platformId - ) + // Precompute outside map: const platformMap = new Map(platforms.map(p => [p.id, p])) + // Then here: + const platform = + PlatformEntity.getSimpleRo?.(platformMap.get(credential.platformId)!) ?? + PlatformEntity.findById(platforms, credential.platformId)Note: create platformMap before the credentials.map loop.
28-31: Optional: avoid global isCopied state across all cardsCurrent hook instance toggles all copy icons. Track the last copied id to localize UI feedback.
- Keep a lastCopiedId state and show check icon only when lastCopiedId === credential.id.
Also applies to: 81-86
components/app/dashboard-credential-cards-view.tsx (2)
50-53: Optional: pre-index platforms for performanceSame as grid-view: index platforms once to avoid repeated array scans.
- const platform = PlatformEntity.findById( - platforms, - credential.platformId - ) + // Use a precomputed Map outside the loop when rendering many items. + const platform = PlatformEntity.findById(platforms, credential.platformId)Implement platformMap at component start if dataset is large.
115-131: Optional: align date formatting with DateFormatter for consistencyConsider using DateFormatter to standardize date text across views.
- <span>{getRelativeTime(primaryDate)}</span> + <span>{getRelativeTime(primaryDate)}</span> + {/* or standardized: DateFormatter.formatShortDate(primaryDate) */}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
.cursor/rules/hooks-folder.mdc(0 hunks)components/app/dashboard-credential-cards-view.tsx(5 hunks)components/app/dashboard-credential-delete-dialog.tsx(1 hunks)components/app/dashboard-credential-grid-view.tsx(2 hunks)components/app/dashboard-credential-header.tsx(2 hunks)
💤 Files with no reviewable changes (1)
- .cursor/rules/hooks-folder.mdc
🚧 Files skipped from review as they are similar to previous changes (1)
- components/app/dashboard-credential-header.tsx
🧰 Additional context used
📓 Path-based instructions (14)
**/*.{html,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/general.mdc)
**/*.{html,jsx,tsx}: Don't use the accessKey attribute on any HTML element
Don't set aria-hidden="true" on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like or
Only use the scope prop on elements
Don't assign non-interactive ARIA roles to interactive HTML elements
Make sure label elements have text content and are associated with an input
Don't assign interactive ARIA roles to non-interactive HTML elements
Don't assign tabIndex to non-interactive HTML elements
Don't use positive integers for the tabIndex property
Don't include "image", "picture", or "photo" in img alt prop
Don't use an explicit role property that's the same as the implicit/default role
Make static elements with click handlers use a valid role attribute
Give all elements requiring alt text meaningful information for screen readers
Make sure anchors have content that's accessible to screen readers
Assign tabIndex to non-interactive HTML elements with aria-activedescendant
Include all required ARIA attributes for elements with ARIA roles
Make sure ARIA properties are valid for the element's supported roles
Always include a type attribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden)
Always include a title attribute for iframe elements
Include caption tracks for audio and video elements
Make sure all anchors are valid and navigable
Ensure all ARIA properties (aria-*) are valid
Use valid, non-abstract ARIA roles for elements with ARIA roles
Use valid ARIA state and property values
Use valid values for the autocomplete attribute on input elements
Use correct ISO language/country codes for the lang attribute
Don't use variables that haven't been declared in the document
Make sure void (self-closing) elements don't have children
Don't use event handlers on non-interactiv...Files:
components/app/dashboard-credential-cards-view.tsxcomponents/app/dashboard-credential-delete-dialog.tsxcomponents/app/dashboard-credential-grid-view.tsx**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/general.mdc)
**/*.{jsx,tsx}: Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress
Accompany onMouseOver/onMouseOut with onFocus/onBlur
Use semantic elements instead of role attributes in JSX
Don't use unnecessary fragments
Don't pass children as props
Don't use the return value of React.render
Make sure all dependencies are correctly specified in React hooks
Make sure all React hooks are called from the top level of component functions
Don't forget key props in iterators and collection literals
Don't define React components inside other components
Don't assign to React component props
Don't use dangerous JSX props
Don't use both children and dangerouslySetInnerHTML props on the same element
Use <>...</> instead of ...
Don't add extra closing tags for components without children
Don't use Array index in keys
Don't insert comments as text nodes
Don't assign JSX properties multiple times
Watch out for possible "wrong" semicolons inside JSX elements
Don't put multiple components in one file; each file must have one componentFiles:
components/app/dashboard-credential-cards-view.tsxcomponents/app/dashboard-credential-delete-dialog.tsxcomponents/app/dashboard-credential-grid-view.tsx**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/general.mdc)
**/*.{js,jsx,ts,tsx}: Don't use consecutive spaces in regular expression literals
Don't use the arguments object
Don't use the comma operator
Don't write functions that exceed a given Cognitive Complexity score
Don't use unnecessary boolean casts
Don't use unnecessary callbacks with flatMap
Use for...of statements instead of Array.forEach
Don't create classes that only have static members
Don't use this and super in static contexts
Don't use unnecessary catch clauses
Don't use unnecessary constructors
Don't use unnecessary continue statements
Don't export empty modules that don't change anything
Don't use unnecessary escape sequences in regular expression literals
Don't use unnecessary labels
Don't use unnecessary nested block statements
Don't rename imports, exports, and destructured assignments to the same name
Don't use unnecessary string or template literal concatenation
Don't use String.raw in template literals when there are no escape sequences
Don't use useless case statements in switch statements
Don't use ternary operators when simpler alternatives exist
Don't use useless this aliasing
Don't initialize variables to undefined
Don't use void operators
Use arrow functions instead of function expressions
Use Date.now() to get milliseconds since the Unix Epoch
Use .flatMap() instead of map().flat() when possible
Use literal property access instead of computed property access
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work
Use concise optional chaining instead of chained logical expressions
Use regular expression literals instead of the RegExp constructor when possible
Don't use number literal object member names that aren't base 10 or use underscore separators
Remove redundant terms from logical expressions
Use while loops instead of for loops when you don't need initializer and update expressions
Don't reassign const variables
Don't use constant expressions in conditions
Don't use Math.min and Math.max to clamp value...Files:
components/app/dashboard-credential-cards-view.tsxcomponents/app/dashboard-credential-delete-dialog.tsxcomponents/app/dashboard-credential-grid-view.tsx**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/general.mdc)
**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use any or unknown as type constraints
Don't return a value from a function that has a 'void' return type
Don't use the TypeScript directive @ts-ignore
Make sure switch-case statements are exhaustive
Don't use TypeScript enums
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with the ! postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types
Use as const instead of literal types and type annotations
Use either T[] or Array consistently
Use consistent accessibility modifiers on class properties and methods
Put default function parameters and optional function parameters last
Initialize each enum member value explicitly
Use export type for types
Use import type for types
Make sure all enum members are literal values
Use function types instead of object types with call signatures
Don't use void type outside of generic or return types
Don't use TypeScript const enum
Don't declare empty interfaces
Don't let variables evolve into any type through reassignments
Don't use the any type
Don't misuse the non-null assertion operator (!) in TypeScript files
Don't use implicit any type on variable declarations
Don't merge interfaces and classes unsafely
Don't use overload signatures that aren't next to each other
Use the namespace keyword instead of the module keyword to declare TypeScript namespacesUse type-only imports when possible
**/*.{ts,tsx}: Use camelCase for function and method names
Use UPPER_SNAKE_CASE for constants
Use camelCase for variables
Use PascalCase for classes and types
Use absolute imports for top-level modules (e.g., @/components, @/lib, @/entities) instead of long relative paths
Follow import order: React imports, third-party, internal abso...Files:
components/app/dashboard-credential-cards-view.tsxcomponents/app/dashboard-credential-delete-dialog.tsxcomponents/app/dashboard-credential-grid-view.tsx**/*.{html,jsx,tsx,css}
📄 CodeRabbit inference engine (.cursor/rules/general.mdc)
Don't use TailwindCSS class names 'h-NUMBER w-NUMBER'; instead use 'size-NUMBR'
Files:
components/app/dashboard-credential-cards-view.tsxcomponents/app/dashboard-credential-delete-dialog.tsxcomponents/app/dashboard-credential-grid-view.tsxcomponents/app/**
📄 CodeRabbit inference engine (.cursor/rules/components-folder.mdc)
Place application-specific React components under components/app
Files:
components/app/dashboard-credential-cards-view.tsxcomponents/app/dashboard-credential-delete-dialog.tsxcomponents/app/dashboard-credential-grid-view.tsxcomponents/app/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/components-folder.mdc)
Use naming pattern {feature}-{purpose}-{type}.tsx for app components
Files:
components/app/dashboard-credential-cards-view.tsxcomponents/app/dashboard-credential-delete-dialog.tsxcomponents/app/dashboard-credential-grid-view.tsxcomponents/app/dashboard-*.tsx
📄 CodeRabbit inference engine (.cursor/rules/components-folder.mdc)
Dashboard app components in components/app must be prefixed with dashboard-
Place dashboard components under components/app/ with filenames starting dashboard-*.tsx
Files:
components/app/dashboard-credential-cards-view.tsxcomponents/app/dashboard-credential-delete-dialog.tsxcomponents/app/dashboard-credential-grid-view.tsxcomponents/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/components-folder.mdc)
components/**/*.{ts,tsx}: Always define a TypeScript interface for component props
Follow established naming patterns consistently across files
Use strict, explicit TypeScript types for safetyFiles:
components/app/dashboard-credential-cards-view.tsxcomponents/app/dashboard-credential-delete-dialog.tsxcomponents/app/dashboard-credential-grid-view.tsxcomponents/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/components-folder.mdc)
components/**/*.tsx: Add 'use client' directive for components that use client-side hooks/effects
Use cn() for conditional Tailwind classes
Use semantic Tailwind colors (e.g., text-foreground, bg-background) and consistent spacing (space-y-4, gap-4)
Use responsive Tailwind prefixes (md:, lg:, xl:) for adaptive layouts
Define component variants with cva and defaultVariants when variants are needed
Prefer useState for local state; use useReducer for complex state
Use TanStack Query hooks from /orpc/hooks for server state; prefer server-side data fetching when possible
Implement proper ARIA attributes and keyboard navigation for accessibility
Use React.memo for expensive components
Use useMemo and useCallback for expensive computations and stable callbacks
Lazy-load heavy components via dynamic import
Import only needed icons from lucide-react (no wildcard imports)
Wrap components that might fail in Error Boundaries
Provide loading and error states for async UI
Add JSDoc comments for complex components
components/**/*.tsx: Components must be named in kebab-case.tsx (e.g., dashboard-credential-form.tsx)
Use React Hook Form for form management in UI forms
Use React Hook Form error handling, display validation errors, and handle submission errors gracefully in formsFiles:
components/app/dashboard-credential-cards-view.tsxcomponents/app/dashboard-credential-delete-dialog.tsxcomponents/app/dashboard-credential-grid-view.tsxcomponents/**/[a-z0-9-]*.tsx
📄 CodeRabbit inference engine (.cursor/rules/project-overview.mdc)
Name React components in kebab-case with .tsx under the components directory (e.g., dashboard-credential-form.tsx)
Files:
components/app/dashboard-credential-cards-view.tsxcomponents/app/dashboard-credential-delete-dialog.tsxcomponents/app/dashboard-credential-grid-view.tsxcomponents/{app,layout,shared,ui}/**
📄 CodeRabbit inference engine (.cursor/rules/architecture.mdc)
Organize React components under components/app, components/layout, components/shared, and components/ui
Files:
components/app/dashboard-credential-cards-view.tsxcomponents/app/dashboard-credential-delete-dialog.tsxcomponents/app/dashboard-credential-grid-view.tsx**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/zero-locker-patterns.mdc)
**/*.tsx: Use Component Composition Pattern: build complex UI by composing smaller, reusable components (e.g., Form, FormField, FormItem)
Handle form errors with React Hook Form and Zod resolver for client-side validation
Avoid direct database access in React components; use hooks/services instead
**/*.tsx: Use PascalCase for React component names
Use functional components only, typed with explicit props interfaces
Optimize components using React.memo for expensive components, useMemo for heavy computations, and useCallback for stable handlersFiles:
components/app/dashboard-credential-cards-view.tsxcomponents/app/dashboard-credential-delete-dialog.tsxcomponents/app/dashboard-credential-grid-view.tsxcomponents/**/*-dialog.tsx
📄 CodeRabbit inference engine (.cursor/rules/components-folder.mdc)
components/**/*-dialog.tsx: Dialog components should accept open, onOpenChange, and optional data props and render a Dialog/DialogContent structure
Manage focus in dialogs/modals and use focus-visible for keyboard usersFiles:
components/app/dashboard-credential-delete-dialog.tsx🧬 Code graph analysis (3)
components/app/dashboard-credential-cards-view.tsx (3)
hooks/use-copy-to-clipboard.ts (1)
useCopyToClipboard(16-66)entities/utils/platform/entity.ts (1)
PlatformEntity(6-74)components/shared/item-actions-dropdown.tsx (1)
CredentialActionsContextMenu(334-464)components/app/dashboard-credential-delete-dialog.tsx (6)
schemas/credential/dto.ts (1)
CredentialOutput(63-63)schemas/utils/platform.ts (1)
PlatformSimpleRo(34-34)hooks/use-toast.ts (1)
useToast(87-186)orpc/hooks/use-credentials.ts (1)
useDeleteCredential(371-410)entities/utils/platform/entity.ts (1)
PlatformEntity(6-74)lib/utils/index.ts (2)
getPlaceholderImage(204-213)getLogoDevUrlWithToken(219-225)components/app/dashboard-credential-grid-view.tsx (5)
hooks/use-copy-to-clipboard.ts (1)
useCopyToClipboard(16-66)entities/utils/platform/entity.ts (1)
PlatformEntity(6-74)components/shared/item-actions-dropdown.tsx (1)
CredentialActionsDropdown(194-332)lib/date-utils.ts (1)
DateFormatter(11-110)components/shared/status-badge.tsx (1)
StatusBadge(16-74)🔇 Additional comments (4)
components/app/dashboard-credential-delete-dialog.tsx (4)
41-49: LGTM!The type definitions are well-structured, follow TypeScript best practices, and properly define the component's interface. The use of
z.inferfor form data types ensures type safety with the Zod schema.
51-79: LGTM!The component setup follows React and React Hook Form best practices:
- Hooks are called at the top level
- Form configuration with zodResolver is correct
- Derived state is efficiently computed using
watch- The guard clause prevents rendering invalid states
- Platform lookup handles missing platforms gracefully with a fallback
108-113: LGTM!The
handleOpenChangefunction correctly resets the form state when the dialog is closed, preventing stale data from persisting between dialog sessions.
115-211: LGTM!The UI implementation is excellent and follows all best practices:
- Accessibility: Proper ARIA structure via ResponsiveAlertDialog components, semantic labels, keyboard navigation support, and proper disabled states
- Responsive Design: Button order adapts to screen size using
sm:prefixes (lines 193, 201)- Visual Feedback: Loading state with spinner icon (lines 203-205), destructive button variant for danger action
- Form UX: Clear instructions showing the exact text to type, real-time validation feedback, and proper placeholder text
- Credential Preview: Well-structured card layout showing platform logo, identifier, and optional description
- Error Handling: Form-level error messages displayed via FormMessage component
- Styling: Follows Tailwind conventions using semantic color tokens (text-foreground, text-destructive, bg-muted), proper spacing utilities, and approved size-* classes instead of h-/w-* combinations
The component provides a professional, user-friendly deletion confirmation experience.
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
Documentation