98 fix deleting projects for members#106
Conversation
|
Caution Review failedThe pull request is closed. WalkthroughAdds server- and client-side handling for project removal/deletion: Durable Object disconnect endpoints and forced WebSocket closes, client access-denied detection that triggers cleanup and redirects, IndexedDB/local-data cleanup APIs, notification dispatch for removed/deleted events, plus PDF duplicate guards and minor UI/toast/style updates. Changes
Sequence Diagram(s)sequenceDiagram
participant Admin as Admin (actor)
participant API as API Server (members/projects routes)
participant ProjectDO as ProjectDoc (Durable Object)
participant Notify as Internal Notify API
participant Client as Client App (browser)
participant Store as useProject/store (IndexedDB/Yjs)
Admin->>API: DELETE /projects/:id or DELETE /members/:userId
API->>ProjectDO: POST /internal/disconnect-all or remove-member (force disconnect)
activate ProjectDO
ProjectDO->>ProjectDO: disconnectAll / disconnectUser (close ws with reason)
ProjectDO->>Client: Close WebSocket for affected sessions (reason: project-deleted / membership-revoked)
deactivate ProjectDO
API->>Notify: POST internal notify for affected users (type: removed-from-project / project-deleted)
Notify->>Client: Deliver notification payloads
Client->>Client: on WebSocket close -> match CLOSE_REASONS / ACCESS_DENIED_ERRORS
Client->>Store: call cleanupProjectLocalData(projectId)
activate Store
Store->>Store: destroy Yjs connection, unregister providers
Store->>Store: delete IndexedDB (prefixed DB) and clear caches
Store-->>Client: cleanup complete
deactivate Store
Client->>Client: show toast ("Removed from Project" / "Project Deleted"), navigate to /dashboard (replace)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
📜 Recent review detailsConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (4)
Comment |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
corates | 4eed74e | Commit Preview URL | Dec 19 2025, 02:51 PM |
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
packages/workers/src/routes/members.js (1)
363-391: VerifyauthUserproperty availability from Better-Auth.Similar to the project deletion flow, this notification uses
authUser.name || authUser.emailat line 383. Ensure that the user object returned by the authentication middleware consistently provides these properties.Note: This is the same concern raised in
packages/workers/src/routes/projects.jsline 291. A single verification script there will cover both cases.
🧹 Nitpick comments (7)
packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx (1)
37-51: Consolidate duplicatedACCESS_DENIED_ERRORSconstant.The
ACCESS_DENIED_ERRORSarray is duplicated verbatim in three files:
ChecklistYjsWrapper.jsx(lines 38-43)ReconciliationWrapper.jsx(lines 40-45)ProjectView.jsx(lines 62-67)Extract this to a shared constants file or utility module to maintain consistency and simplify future updates.
Proposed refactor
Create a shared constants file:
// packages/web/src/constants/errors.js export const ACCESS_DENIED_ERRORS = [ 'This project has been deleted', 'You have been removed from this project', 'You are not a member of this project', 'Unable to connect to project. It may have been deleted or you may not have access.', ];Then import in each component:
+import { ACCESS_DENIED_ERRORS } from '@/constants/errors.js'; - const ACCESS_DENIED_ERRORS = [ - 'This project has been deleted', - 'You have been removed from this project', - 'You are not a member of this project', - 'Unable to connect to project. It may have been deleted or you may not have access.', - ];packages/web/src/components/project-ui/ProjectDashboard.jsx (1)
47-63: Async notification handling looks correct.The handler appropriately awaits
cleanupProjectLocalDatabefore showing toasts, ensuring proper sequencing.Consider adding defensive handling for
notification.projectNamein case it's undefined:Optional defensive check
} else if (notification.type === 'removed-from-project') { await cleanupProjectLocalData(notification.projectId); showToast.info( 'Removed from Project', - `You were removed from "${notification.projectName}"`, + `You were removed from "${notification.projectName || 'a project'}"`, ); } else if (notification.type === 'project-deleted') { await cleanupProjectLocalData(notification.projectId); - showToast.info('Project Deleted', `"${notification.projectName}" was deleted`); + showToast.info('Project Deleted', `"${notification.projectName || 'A project'}" was deleted`); }packages/web/src/components/NotificationToast.jsx (1)
20-35: New notification types are correctly structured.The content shapes for
removed-from-projectandproject-deletedare appropriate - no action buttons since the project is no longer accessible.Consider defensive handling for potentially undefined fields:
Optional defensive handling
if (notification.type === 'removed-from-project') { return { title: 'Removed from Project', - message: `You've been removed from "${notification.projectName}"`, + message: `You've been removed from "${notification.projectName || 'a project'}"`, action: null, actionLabel: null, }; } if (notification.type === 'project-deleted') { return { title: 'Project Deleted', - message: `"${notification.projectName}" has been deleted by ${notification.deletedBy}`, + message: notification.deletedBy + ? `"${notification.projectName || 'A project'}" has been deleted by ${notification.deletedBy}` + : `"${notification.projectName || 'A project'}" has been deleted`, action: null, actionLabel: null, }; }packages/web/src/primitives/useProject/index.js (1)
26-30: Good practice centralizing the IndexedDB prefix.The
INDEXEDDB_PREFIXconstant is correctly defined and matches the usage pattern at line 239. Consider using the constant in theIndexeddbPersistenceconstructor for consistency:Suggested consistency fix
// Set up IndexedDB persistence for offline support connectionEntry.indexeddbProvider = new IndexeddbPersistence( - `corates-project-${projectId}`, + `${INDEXEDDB_PREFIX}${projectId}`, ydoc, );packages/web/src/primitives/useProject/connection.js (2)
142-160: Thereason.includes('member')check may be too permissive.The condition
reason.includes('member')at line 145 could match unintended strings (e.g., "remember", "membership renewal"). Consider checking for exact matches or more specific substrings:Suggested refinement
// Handle membership rejection (1008 = Policy Violation) or generic "not a member" if ( event.code === 1008 || - reason.includes('member') || + reason.includes('not a member') || + reason.includes('not-a-member') || reason === CLOSE_REASONS.NOT_A_MEMBER ) {
183-198: Consider adding 'connection-failed' to CLOSE_REASONS for consistency.The
onAccessDeniedcallback at line 195 uses the string'connection-failed', which is not defined inCLOSE_REASONS. For consistency and type safety, consider adding it to the constant:Suggested addition
export const CLOSE_REASONS = { PROJECT_DELETED: 'project-deleted', MEMBERSHIP_REVOKED: 'membership-revoked', NOT_A_MEMBER: 'not-a-member', + CONNECTION_FAILED: 'connection-failed', };Then update line 195:
if (onAccessDenied) { - onAccessDenied({ reason: 'connection-failed' }); + onAccessDenied({ reason: CLOSE_REASONS.CONNECTION_FAILED }); }packages/workers/src/durable-objects/ProjectDoc.js (1)
579-590: Consider using a more semantically appropriate close code.The implementation uses close code 1000 (Normal Closure) for project deletion, which typically indicates a graceful, expected closure. For a server-initiated forced disconnect due to project deletion, consider:
- 1001 (Going Away) - Server is going away or resource is being terminated
- 1008 (Policy Violation) - Consistent with the
disconnectUserimplementationClose code 1000 may be intentional to avoid client reconnection attempts, but a more specific code would better communicate the nature of the disconnection.
Proposed change to use close code 1001
disconnectAll(reason = 'project-deleted') { - const closeCode = 1000; // Normal closure + const closeCode = 1001; // Going Away this.sessions.forEach((sessionData, ws) => { if (ws.readyState === 1) { ws.close(closeCode, reason); } }); }
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
.github/copilot-instructions.md(1 hunks)packages/ui/src/zag/Toast.jsx(1 hunks)packages/web/src/components/NotificationToast.jsx(1 hunks)packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx(1 hunks)packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx(2 hunks)packages/web/src/components/project-ui/ProjectDashboard.jsx(2 hunks)packages/web/src/components/project-ui/ProjectView.jsx(2 hunks)packages/web/src/primitives/__tests__/useProject.test.js(1 hunks)packages/web/src/primitives/useProject/connection.js(5 hunks)packages/web/src/primitives/useProject/index.js(4 hunks)packages/web/src/stores/projectStore.js(3 hunks)packages/workers/src/durable-objects/ProjectDoc.js(4 hunks)packages/workers/src/routes/members.js(1 hunks)packages/workers/src/routes/projects.js(1 hunks)
🧰 Additional context used
📓 Path-based instructions (14)
**/*
📄 CodeRabbit inference engine (.cursorrules)
Do not use emojis in code, comments, documentation, or commit messages
Files:
packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsxpackages/workers/src/routes/projects.jspackages/web/src/components/NotificationToast.jsxpackages/workers/src/routes/members.jspackages/web/src/primitives/useProject/connection.jspackages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsxpackages/web/src/primitives/__tests__/useProject.test.jspackages/ui/src/zag/Toast.jsxpackages/workers/src/durable-objects/ProjectDoc.jspackages/web/src/primitives/useProject/index.jspackages/web/src/stores/projectStore.jspackages/web/src/components/project-ui/ProjectDashboard.jsxpackages/web/src/components/project-ui/ProjectView.jsx
packages/web/src/**/*.{jsx,tsx,js,ts}
📄 CodeRabbit inference engine (.cursorrules)
packages/web/src/**/*.{jsx,tsx,js,ts}: For UI icons, use thesolid-iconslibrary or SVGs only. Do not use emojis
Import stores directly where needed instead of passing values through multiple components
When you need to compute a value based on props or state in SolidJS, usecreateMemoto ensure it updates reactively
For complex state or state objects in SolidJS, use Solid'screateStorefor better performance and reactivity
You may create reusable logic in 'primitives' (hooks) that can be shared across components to keep components clean and focused on rendering
Files:
packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsxpackages/web/src/components/NotificationToast.jsxpackages/web/src/primitives/useProject/connection.jspackages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsxpackages/web/src/primitives/__tests__/useProject.test.jspackages/web/src/primitives/useProject/index.jspackages/web/src/stores/projectStore.jspackages/web/src/components/project-ui/ProjectDashboard.jsxpackages/web/src/components/project-ui/ProjectView.jsx
packages/**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
packages/**/*.{js,jsx,ts,tsx}: Prefer modern ES6+ syntax and features
Use aliases for imports when appropriate to improve readability
Prefer using config files rather than hardcoding values
Keep files small, focused, and modular. If a file exceeds a high number of lines, consider refactoring by extracting sub-modules into a folder with index.jsx and helper components, moving complex logic into separate utility files or primitives, or splitting large forms into section components
Each file should handle one coherent responsibility
Use Zod for schema and input validation
Files:
packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsxpackages/workers/src/routes/projects.jspackages/web/src/components/NotificationToast.jsxpackages/workers/src/routes/members.jspackages/web/src/primitives/useProject/connection.jspackages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsxpackages/web/src/primitives/__tests__/useProject.test.jspackages/ui/src/zag/Toast.jsxpackages/workers/src/durable-objects/ProjectDoc.jspackages/web/src/primitives/useProject/index.jspackages/web/src/stores/projectStore.jspackages/web/src/components/project-ui/ProjectDashboard.jsxpackages/web/src/components/project-ui/ProjectView.jsx
packages/web/src/components/**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursorrules)
packages/web/src/components/**/*.{jsx,tsx}: Use responsive design principles for UI components
Use Zag.js for UI components and design system
Zag components exist inpackages/web/src/components/zag/*and should be reused; check the README.md in that folder for a list of existing components before adding new components and when debugging
Components should receive at most 1–5 props, and only for local configuration, not shared state
If a component would need more than 5 props, move the shared data into an external store, a primitive, or Solid context
Components should be lean and focused and should not implement business logic; move business logic into stores, utilities, or primitives
Never have a component act as a 'God component' coordinating multiple large concerns
Files:
packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsxpackages/web/src/components/NotificationToast.jsxpackages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsxpackages/web/src/components/project-ui/ProjectDashboard.jsxpackages/web/src/components/project-ui/ProjectView.jsx
packages/web/src/**/*.{jsx,tsx,js,ts,css}
📄 CodeRabbit inference engine (.cursorrules)
Ensure browser compatibility for all frontend code (Safari is usually problematic)
Files:
packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsxpackages/web/src/components/NotificationToast.jsxpackages/web/src/primitives/useProject/connection.jspackages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsxpackages/web/src/primitives/__tests__/useProject.test.jspackages/web/src/primitives/useProject/index.jspackages/web/src/stores/projectStore.jspackages/web/src/components/project-ui/ProjectDashboard.jsxpackages/web/src/components/project-ui/ProjectView.jsx
packages/web/src/components/**/*.{jsx,tsx,js}
📄 CodeRabbit inference engine (.cursorrules)
Group related components in subdirectories with an
index.jsbarrel export
Files:
packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsxpackages/web/src/components/NotificationToast.jsxpackages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsxpackages/web/src/components/project-ui/ProjectDashboard.jsxpackages/web/src/components/project-ui/ProjectView.jsx
packages/web/src/**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursorrules)
packages/web/src/**/*.{jsx,tsx}: Do NOT prop-drill application state. Shared or cross-feature state must live in external stores under packages/web/src/stores/ or relative to the component file
Do not destructure props in SolidJS components as it breaks reactivity. Instead, access props directly from the props object or wrap them in a function to ensure they are always up-to-date
Files:
packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsxpackages/web/src/components/NotificationToast.jsxpackages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsxpackages/web/src/components/project-ui/ProjectDashboard.jsxpackages/web/src/components/project-ui/ProjectView.jsx
**/*.{js,jsx,ts,tsx,json,md,css,html}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Do not use emojis in code, comments, documentation, or commit messages
Files:
packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsxpackages/workers/src/routes/projects.jspackages/web/src/components/NotificationToast.jsxpackages/workers/src/routes/members.jspackages/web/src/primitives/useProject/connection.jspackages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsxpackages/web/src/primitives/__tests__/useProject.test.jspackages/ui/src/zag/Toast.jsxpackages/workers/src/durable-objects/ProjectDoc.jspackages/web/src/primitives/useProject/index.jspackages/web/src/stores/projectStore.jspackages/web/src/components/project-ui/ProjectDashboard.jsxpackages/web/src/components/project-ui/ProjectView.jsx
packages/web/src/**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
packages/web/src/**/*.{js,jsx,ts,tsx}: For UI icons, use thesolid-iconslibrary or SVGs only. Do not use emojis
Ensure browser compatibility for all frontend code (Safari is usually problematic)
Do NOT prop-drill application state. Shared or cross-feature state must live in external stores under packages/web/src/stores/ or relative to the component file
UsecreateMemoto compute values based on props or state to ensure reactive updates
Create reusable logic in primitives (hooks) that can be shared across components to keep components clean and focused on rendering
Files:
packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsxpackages/web/src/components/NotificationToast.jsxpackages/web/src/primitives/useProject/connection.jspackages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsxpackages/web/src/primitives/__tests__/useProject.test.jspackages/web/src/primitives/useProject/index.jspackages/web/src/stores/projectStore.jspackages/web/src/components/project-ui/ProjectDashboard.jsxpackages/web/src/components/project-ui/ProjectView.jsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{js,jsx,ts,tsx}: Follow standard JavaScript/SolidJS/Cloudflare best practices
Prefer modern ES6+ syntax and features
Use aliases for imports when appropriate to improve readability
Prefer using config files rather than hardcoding values
Keep files small, focused, and modular. Extract sub-modules into a folder with index.jsx and helper components, move complex logic into separate utility files or primitives, and split large forms into section components
Each file should handle one coherent responsibility
Files:
packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsxpackages/workers/src/routes/projects.jspackages/web/src/components/NotificationToast.jsxpackages/workers/src/routes/members.jspackages/web/src/primitives/useProject/connection.jspackages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsxpackages/web/src/primitives/__tests__/useProject.test.jspackages/ui/src/zag/Toast.jsxpackages/workers/src/durable-objects/ProjectDoc.jspackages/web/src/primitives/useProject/index.jspackages/web/src/stores/projectStore.jspackages/web/src/components/project-ui/ProjectDashboard.jsxpackages/web/src/components/project-ui/ProjectView.jsx
packages/web/src/components/**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
packages/web/src/components/**/*.{js,jsx,ts,tsx}: Use responsive design principles for UI components
Use Zag.js for UI components and design system
Reuse Zag components frompackages/web/src/components/zag/*and check the README.md in that folder for existing components before adding new components
Components should receive at most 1–5 props, and only for local configuration, not shared state. If a component would need more than 5 props, move the shared data into an external store, a primitive, or Solid context
Do not destructure props in SolidJS components. Instead, access props directly from the props object or wrap them in a function to ensure they are always up-to-date
Components should be lean and focused on rendering. Do not implement business logic; move that into stores, utilities, or primitives
Never have a component act as a 'God component' coordinating multiple large concerns
Files:
packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsxpackages/web/src/components/NotificationToast.jsxpackages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsxpackages/web/src/components/project-ui/ProjectDashboard.jsxpackages/web/src/components/project-ui/ProjectView.jsx
packages/web/src/components/**
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Group related components in subdirectories with an
index.jsbarrel export
Files:
packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsxpackages/web/src/components/NotificationToast.jsxpackages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsxpackages/web/src/components/project-ui/ProjectDashboard.jsxpackages/web/src/components/project-ui/ProjectView.jsx
packages/workers/**/*.{js,ts}
📄 CodeRabbit inference engine (.cursorrules)
packages/workers/**/*.{js,ts}: Use Drizzle ORM for database interactions and migrations
Use Better-Auth for authentication and user management
packages/workers/**/*.{js,ts}: Use Zod for schema and input validation
Use Drizzle ORM for database interactions and migrations
Use Better-Auth for authentication and user management
Files:
packages/workers/src/routes/projects.jspackages/workers/src/routes/members.jspackages/workers/src/durable-objects/ProjectDoc.js
packages/web/src/stores/**/*.{js,ts}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use Solid's
createStorefor complex state or state objects to ensure better performance and reactivity
Files:
packages/web/src/stores/projectStore.js
🧠 Learnings (9)
📚 Learning: 2025-12-19T12:38:38.880Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-19T12:38:38.880Z
Learning: Use the Corates MCP tools to explore local documentation sources for Better-Auth, Drizzle, Icons, and Zag documentation
Applied to files:
.github/copilot-instructions.md
📚 Learning: 2025-12-19T12:38:18.491Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursorrules:0-0
Timestamp: 2025-12-19T12:38:18.491Z
Learning: Use the Corates MCP for documentation about Better-Auth, Drizzle, Icons, and Zag
Applied to files:
.github/copilot-instructions.md
📚 Learning: 2025-12-19T12:38:38.880Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-19T12:38:38.880Z
Learning: Applies to packages/web/src/components/**/*.{js,jsx,ts,tsx} : Use Zag.js for UI components and design system
Applied to files:
.github/copilot-instructions.mdpackages/ui/src/zag/Toast.jsx
📚 Learning: 2025-12-19T12:38:18.491Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursorrules:0-0
Timestamp: 2025-12-19T12:38:18.491Z
Learning: Applies to packages/web/src/components/**/*.{jsx,tsx} : Use Zag.js for UI components and design system
Applied to files:
.github/copilot-instructions.mdpackages/ui/src/zag/Toast.jsx
📚 Learning: 2025-12-19T12:38:18.491Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursorrules:0-0
Timestamp: 2025-12-19T12:38:18.491Z
Learning: Applies to packages/web/src/components/**/*.{jsx,tsx} : Zag components exist in `packages/web/src/components/zag/*` and should be reused; check the README.md in that folder for a list of existing components before adding new components and when debugging
Applied to files:
.github/copilot-instructions.md
📚 Learning: 2025-12-19T12:38:38.880Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-19T12:38:38.880Z
Learning: Applies to packages/web/src/components/**/*.{js,jsx,ts,tsx} : Reuse Zag components from `packages/web/src/components/zag/*` and check the README.md in that folder for existing components before adding new components
Applied to files:
.github/copilot-instructions.md
📚 Learning: 2025-12-19T12:38:38.880Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-19T12:38:38.880Z
Learning: Applies to packages/web/src/stores/**/*.{js,ts} : Use Solid's `createStore` for complex state or state objects to ensure better performance and reactivity
Applied to files:
packages/web/src/primitives/useProject/index.jspackages/web/src/components/project-ui/ProjectDashboard.jsx
📚 Learning: 2025-12-19T12:38:18.491Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursorrules:0-0
Timestamp: 2025-12-19T12:38:18.491Z
Learning: Applies to packages/web/src/**/*.{jsx,tsx,js,ts} : For complex state or state objects in SolidJS, use Solid's `createStore` for better performance and reactivity
Applied to files:
packages/web/src/primitives/useProject/index.jspackages/web/src/components/project-ui/ProjectDashboard.jsx
📚 Learning: 2025-12-19T12:38:18.491Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursorrules:0-0
Timestamp: 2025-12-19T12:38:18.491Z
Learning: Applies to packages/web/src/**/*.{jsx,tsx,js,ts} : When you need to compute a value based on props or state in SolidJS, use `createMemo` to ensure it updates reactively
Applied to files:
packages/web/src/primitives/useProject/index.js
🧬 Code graph analysis (6)
packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx (15)
packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx (3)
ACCESS_DENIED_ERRORS(38-43)connectionState(35-35)navigate(16-16)packages/web/src/components/project-ui/ProjectView.jsx (3)
ACCESS_DENIED_ERRORS(62-67)connectionState(44-44)navigate(33-33)packages/web/src/primitives/useProject/index.js (1)
connectionState(158-158)packages/web/src/primitives/useProjectData.js (1)
connectionState(30-30)packages/web/src/components/project-ui/ProjectDashboard.jsx (1)
navigate(14-14)packages/web/src/components/project-ui/reconcile-tab/ReconcileTab.jsx (1)
navigate(16-16)packages/web/src/components/NotFoundPage.jsx (1)
navigate(6-6)packages/web/src/components/Navbar.jsx (1)
navigate(11-11)packages/web/src/components/auth-ui/CompleteProfile.jsx (1)
navigate(25-25)packages/web/src/components/auth-ui/TwoFactorVerify.jsx (1)
navigate(13-13)packages/web/src/components/checklist-ui/ChecklistsDashboard.jsx (1)
navigate(10-10)packages/web/src/components/checklist-ui/CreateLocalChecklist.jsx (1)
navigate(15-15)packages/web/src/components/checklist-ui/LocalChecklistView.jsx (1)
navigate(20-20)packages/web/src/components/sidebar/ProjectTreeItem.jsx (1)
navigate(13-13)packages/web/src/components/sidebar/Sidebar.jsx (1)
navigate(18-18)
packages/workers/src/routes/members.js (1)
packages/workers/src/routes/projects.js (17)
c(85-85)c(177-177)projectId(48-48)projectId(87-87)projectId(175-175)projectId(226-226)project(245-249)db(49-49)db(84-84)db(176-176)db(227-227)userSessionId(281-281)userSession(282-282)authUser(47-47)authUser(83-83)authUser(174-174)authUser(225-225)
packages/web/src/primitives/useProject/connection.js (1)
packages/web/src/stores/projectStore.js (1)
projectStore(538-538)
packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx (8)
packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx (3)
ACCESS_DENIED_ERRORS(40-45)connectionState(37-37)navigate(17-17)packages/web/src/components/project-ui/ProjectView.jsx (3)
ACCESS_DENIED_ERRORS(62-67)connectionState(44-44)navigate(33-33)packages/web/src/primitives/useProject/index.js (1)
connectionState(158-158)packages/web/src/primitives/useProjectData.js (1)
connectionState(30-30)packages/ui/src/zag/Toast.jsx (2)
showToast(131-149)showToast(131-149)packages/web/src/components/Navbar.jsx (1)
navigate(11-11)packages/web/src/components/auth-ui/CheckEmail.jsx (1)
navigate(19-19)packages/web/src/components/checklist-ui/ChecklistsDashboard.jsx (1)
navigate(10-10)
packages/web/src/stores/projectStore.js (1)
packages/web/src/components/project-ui/ProjectDashboard.jsx (1)
projects(27-27)
packages/web/src/components/project-ui/ProjectDashboard.jsx (2)
packages/web/src/primitives/useProject/index.js (1)
cleanupProjectLocalData(97-136)packages/ui/src/zag/Toast.jsx (2)
showToast(131-149)showToast(131-149)
⏰ 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). (2)
- GitHub Check: Workers Builds: corates
- GitHub Check: Workers Builds: corates-workers-prod
🔇 Additional comments (21)
.github/copilot-instructions.md (1)
43-43: Documentation guidance addition is clear and consistent.The appended linting guidance aligns well with the existing MCP-centric approach encouraged throughout the document. This reinforces the unified tooling recommendation and provides helpful direction to Copilot.
packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx (1)
39-53: Access-denied redirect logic is correctly implemented.The pattern matches the implementation in other views. The duplication of
ACCESS_DENIED_ERRORSwas noted in the previous file review - once consolidated, this file should import from the shared location.packages/web/src/components/project-ui/ProjectView.jsx (1)
60-77: Access-denied handling is well-documented and correctly implemented.The comments clearly explain when these errors occur. The implementation is consistent with the other views. Once the
ACCESS_DENIED_ERRORSconstant is consolidated (as noted earlier), this file should import from the shared location.packages/web/src/stores/projectStore.js (2)
412-426: Stale project reconciliation logic is well-designed.The approach correctly:
- Compares server project list against local cache
- Triggers async cleanup without blocking the fetch
- Handles cleanup errors gracefully with logging
The fire-and-forget pattern (
onStaleProjectCleanup(cachedId).catch(...)) is appropriate here since cleanup failure shouldn't prevent the user from seeing their project list.
540-547: Callback registration pattern avoids circular dependencies.The
registerStaleProjectCleanupexport is a clean solution for injecting the cleanup function fromuseProjectwithout creating circular imports. The callback is registered at module load time whenuseProjectis first imported, ensuring early initialization and preventing circular dependency issues.packages/ui/src/zag/Toast.jsx (1)
63-72: LGTM - Animation styling with CSS variables.The CSS variable bindings for toast animations are well-implemented. The
will-changeproperty is appropriately used to hint browser optimization, and the cubic-bezier timing function provides smooth easing. The individualtranslateCSS property is widely supported as of August 2022, so fallbacks are only necessary if the project specifically targets Safari versions earlier than 14.1.packages/web/src/primitives/__tests__/useProject.test.js (1)
41-45: LGTM - Mock additions align with new cleanup API.The added mocks for
clearProject,removeProjectFromList, andregisterStaleProjectCleanupcorrectly mirror the new projectStore API surface needed for the stale project cleanup workflow.packages/web/src/primitives/useProject/index.js (3)
117-131: IndexedDB deletion handles edge cases appropriately.The
onblockedhandler correctly logs a warning and continues, which is the right approach since other browser tabs may have open connections. The cleanup flow remains resilient even if IndexedDB deletion fails.
138-140: Module-level registration is acceptable but couples import order.The registration at module load time works because
projectStoreis imported before this code executes. This coupling is acceptable given the explicit import, but be aware that refactoring imports could break this.
265-278: LGTM - onAccessDenied handler properly integrates with cleanup workflow.The async handler correctly awaits
cleanupProjectLocalDatato ensure all local data is purged when access is denied. This handles removal, deletion, and unauthorized access scenarios consistently.packages/web/src/primitives/useProject/connection.js (3)
10-18: LGTM - Well-defined close reason constants.The
CLOSE_REASONSexport provides type-safe constants that match backend close reasons, enabling consistent handling across the frontend.
86-91: LGTM - Error count reset on successful connection.Resetting
consecutiveErrorswhenstatus === 'connected'ensures transient network issues don't accumulate towards the MAX_CONSECUTIVE_ERRORS threshold.
30-38: LGTM - Error tracking initialization and threshold are reasonable.The
consecutiveErrorscounter andMAX_CONSECUTIVE_ERRORS = 5threshold provide a good balance between resilience (allowing temporary network issues) and user experience (not endlessly retrying when access is permanently denied).packages/workers/src/routes/projects.js (3)
244-255: LGTM! Pre-deletion data fetch is correctly placed.Fetching the project name and member list before deletion ensures the data is available for notifications after the cascade delete removes them from the database.
257-272: LGTM! Proper error isolation for DO disconnection.The try/catch ensures that if the Durable Object disconnection fails, the project deletion still proceeds. This is the correct approach since the WebSocket connections will eventually time out anyway.
277-300: No action required. Better-Auth's user schema requires thenamefield alongsideemailVerified, soauthUser.nameis guaranteed to exist. TheauthUser.name || authUser.emailfallback is defensive coding that adds no risk.packages/workers/src/routes/members.js (1)
358-358: LGTM! Comment accurately reflects the enhanced behavior.The updated comment now indicates that the sync operation also forces disconnection, which aligns with the
disconnectUsercall added inProjectDoc.jsline 198.packages/workers/src/durable-objects/ProjectDoc.js (4)
64-66: LGTM! Internal endpoint registration follows existing patterns.The disconnect-all endpoint is correctly placed within the internal request handling block and follows the same pattern as other internal endpoints.
197-198: LGTM! Disconnect call properly integrated into member removal flow.The forced disconnection happens after the membership is removed from the Y.Doc, ensuring the data state is consistent before closing the WebSocket connection.
215-223: LGTM! Clean handler implementation.The handler properly delegates to the
disconnectAllhelper and returns an appropriate success response.
565-577: LGTM! Proper WebSocket disconnection with appropriate close code.The use of close code 1008 (Policy Violation) is appropriate for membership revocation, and the implementation correctly filters sessions by user ID and connection state.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
packages/web/src/primitives/useProject/index.js (1)
97-116: Previous concern addressed: connection state is now reset.The force-release cleanup path now properly resets the connection state via
projectStore.setConnectionState(projectId, { connected: false, synced: false })on line 115, matching the behavior of the regularreleaseConnectionfunction on line 87.
🧹 Nitpick comments (5)
packages/workers/src/routes/pdfs.js (1)
205-207: Minor: Relocate comment for clarity.The comment "// Store in R2" on line 205 is separated from the actual PUT operation on line 207 by a blank line. Consider placing it directly above line 207 for better readability.
🔎 Suggested adjustment
- // Store in R2 - + // Store in R2 await c.env.PDF_BUCKET.put(key, pdfData, {packages/workers/src/routes/projects.js (2)
305-328: Consider parallelizing notifications for better performance.Notifications are sent sequentially in a loop. Since each notification is independent and errors are already caught individually, sending them in parallel would reduce latency.
Suggested parallel notification approach
// Send notifications to all members (except the one who deleted) + const notificationPromises = members + .filter(member => member.userId !== authUser.id) + .map(async member => { + try { + const userSessionId = c.env.USER_SESSION.idFromName(member.userId); + const userSession = c.env.USER_SESSION.get(userSessionId); + await userSession.fetch( + new Request('https://internal/notify', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + type: 'project-deleted', + projectId, + projectName: project?.name || 'Unknown Project', + deletedBy: authUser.name || authUser.email, + timestamp: Date.now(), + }), + }), + ); + } catch (err) { + console.error('Failed to send deletion notification to user:', member.userId, err); + } + }); + + await Promise.allSettled(notificationPromises); - for (const member of members) { - if (member.userId !== authUser.id) { - try { - const userSessionId = c.env.USER_SESSION.idFromName(member.userId); - const userSession = c.env.USER_SESSION.get(userSessionId); - await userSession.fetch( - new Request('https://internal/notify', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - type: 'project-deleted', - projectId, - projectName: project?.name || 'Unknown Project', - deletedBy: authUser.name || authUser.email, - timestamp: Date.now(), - }), - }), - ); - } catch (err) { - console.error('Failed to send deletion notification to user:', member.userId, err); - } - } - }
280-292: Use batch delete to improve performance.R2 supports deleting up to 1000 keys in a single call. The current implementation deletes objects individually via
Promise.all, which is less efficient than using batch delete.Replace the individual delete calls with a single batch delete:
do { const listed = await c.env.PDF_BUCKET.list({ prefix, cursor }); if (listed.objects.length > 0) { // Delete objects in batches const keysToDelete = listed.objects.map(obj => obj.key); - await Promise.all(keysToDelete.map(key => c.env.PDF_BUCKET.delete(key))); + await c.env.PDF_BUCKET.delete(keysToDelete); deletedCount += keysToDelete.length; } cursor = listed.truncated ? listed.cursor : undefined; } while (cursor);packages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsx (2)
39-56: Consider adding client-side file size validation.While the function correctly validates the file type and handles errors, adding a file size check before upload would improve UX by preventing users from attempting to upload excessively large files.
Optional: Add file size validation
const handleFileSelect = async e => { const file = e.target.files?.[0]; if (!file || file.type !== 'application/pdf') { showToast.error('Invalid File', 'Please select a PDF file'); return; } + + // Optional: Add file size limit (e.g., 50MB) + const MAX_FILE_SIZE = 50 * 1024 * 1024; // 50MB in bytes + if (file.size > MAX_FILE_SIZE) { + showToast.error('File Too Large', 'Please select a PDF smaller than 50MB'); + return; + } setUploading(true);
78-81: TODO: Metadata editing not yet implemented.The metadata editing functionality is marked as TODO and currently only logs to the console.
Would you like me to help implement the metadata edit modal or open a new issue to track this feature?
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx(2 hunks)packages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsx(1 hunks)packages/web/src/primitives/useProject/index.js(4 hunks)packages/web/src/stores/projectActionsStore/pdfs.js(1 hunks)packages/workers/src/config/constants.js(1 hunks)packages/workers/src/routes/pdfs.js(1 hunks)packages/workers/src/routes/projects.js(1 hunks)
🧰 Additional context used
📓 Path-based instructions (14)
**/*
📄 CodeRabbit inference engine (.cursorrules)
Do not use emojis in code, comments, documentation, or commit messages
Files:
packages/workers/src/routes/pdfs.jspackages/workers/src/routes/projects.jspackages/workers/src/config/constants.jspackages/web/src/primitives/useProject/index.jspackages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsxpackages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsxpackages/web/src/stores/projectActionsStore/pdfs.js
packages/**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
packages/**/*.{js,jsx,ts,tsx}: Prefer modern ES6+ syntax and features
Use aliases for imports when appropriate to improve readability
Prefer using config files rather than hardcoding values
Keep files small, focused, and modular. If a file exceeds a high number of lines, consider refactoring by extracting sub-modules into a folder with index.jsx and helper components, moving complex logic into separate utility files or primitives, or splitting large forms into section components
Each file should handle one coherent responsibility
Use Zod for schema and input validation
Files:
packages/workers/src/routes/pdfs.jspackages/workers/src/routes/projects.jspackages/workers/src/config/constants.jspackages/web/src/primitives/useProject/index.jspackages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsxpackages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsxpackages/web/src/stores/projectActionsStore/pdfs.js
packages/workers/**/*.{js,ts}
📄 CodeRabbit inference engine (.cursorrules)
packages/workers/**/*.{js,ts}: Use Drizzle ORM for database interactions and migrations
Use Better-Auth for authentication and user management
packages/workers/**/*.{js,ts}: Use Zod for schema and input validation
Use Drizzle ORM for database interactions and migrations
Use Better-Auth for authentication and user management
Files:
packages/workers/src/routes/pdfs.jspackages/workers/src/routes/projects.jspackages/workers/src/config/constants.js
**/*.{js,jsx,ts,tsx,json,md,css,html}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Do not use emojis in code, comments, documentation, or commit messages
Files:
packages/workers/src/routes/pdfs.jspackages/workers/src/routes/projects.jspackages/workers/src/config/constants.jspackages/web/src/primitives/useProject/index.jspackages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsxpackages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsxpackages/web/src/stores/projectActionsStore/pdfs.js
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{js,jsx,ts,tsx}: Follow standard JavaScript/SolidJS/Cloudflare best practices
Prefer modern ES6+ syntax and features
Use aliases for imports when appropriate to improve readability
Prefer using config files rather than hardcoding values
Keep files small, focused, and modular. Extract sub-modules into a folder with index.jsx and helper components, move complex logic into separate utility files or primitives, and split large forms into section components
Each file should handle one coherent responsibility
Files:
packages/workers/src/routes/pdfs.jspackages/workers/src/routes/projects.jspackages/workers/src/config/constants.jspackages/web/src/primitives/useProject/index.jspackages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsxpackages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsxpackages/web/src/stores/projectActionsStore/pdfs.js
packages/web/src/**/*.{jsx,tsx,js,ts}
📄 CodeRabbit inference engine (.cursorrules)
packages/web/src/**/*.{jsx,tsx,js,ts}: For UI icons, use thesolid-iconslibrary or SVGs only. Do not use emojis
Import stores directly where needed instead of passing values through multiple components
When you need to compute a value based on props or state in SolidJS, usecreateMemoto ensure it updates reactively
For complex state or state objects in SolidJS, use Solid'screateStorefor better performance and reactivity
You may create reusable logic in 'primitives' (hooks) that can be shared across components to keep components clean and focused on rendering
Files:
packages/web/src/primitives/useProject/index.jspackages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsxpackages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsxpackages/web/src/stores/projectActionsStore/pdfs.js
packages/web/src/**/*.{jsx,tsx,js,ts,css}
📄 CodeRabbit inference engine (.cursorrules)
Ensure browser compatibility for all frontend code (Safari is usually problematic)
Files:
packages/web/src/primitives/useProject/index.jspackages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsxpackages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsxpackages/web/src/stores/projectActionsStore/pdfs.js
packages/web/src/**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
packages/web/src/**/*.{js,jsx,ts,tsx}: For UI icons, use thesolid-iconslibrary or SVGs only. Do not use emojis
Ensure browser compatibility for all frontend code (Safari is usually problematic)
Do NOT prop-drill application state. Shared or cross-feature state must live in external stores under packages/web/src/stores/ or relative to the component file
UsecreateMemoto compute values based on props or state to ensure reactive updates
Create reusable logic in primitives (hooks) that can be shared across components to keep components clean and focused on rendering
Files:
packages/web/src/primitives/useProject/index.jspackages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsxpackages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsxpackages/web/src/stores/projectActionsStore/pdfs.js
packages/web/src/components/**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursorrules)
packages/web/src/components/**/*.{jsx,tsx}: Use responsive design principles for UI components
Use Zag.js for UI components and design system
Zag components exist inpackages/web/src/components/zag/*and should be reused; check the README.md in that folder for a list of existing components before adding new components and when debugging
Components should receive at most 1–5 props, and only for local configuration, not shared state
If a component would need more than 5 props, move the shared data into an external store, a primitive, or Solid context
Components should be lean and focused and should not implement business logic; move business logic into stores, utilities, or primitives
Never have a component act as a 'God component' coordinating multiple large concerns
Files:
packages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsxpackages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
packages/web/src/components/**/*.{jsx,tsx,js}
📄 CodeRabbit inference engine (.cursorrules)
Group related components in subdirectories with an
index.jsbarrel export
Files:
packages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsxpackages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
packages/web/src/**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursorrules)
packages/web/src/**/*.{jsx,tsx}: Do NOT prop-drill application state. Shared or cross-feature state must live in external stores under packages/web/src/stores/ or relative to the component file
Do not destructure props in SolidJS components as it breaks reactivity. Instead, access props directly from the props object or wrap them in a function to ensure they are always up-to-date
Files:
packages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsxpackages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
packages/web/src/components/**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
packages/web/src/components/**/*.{js,jsx,ts,tsx}: Use responsive design principles for UI components
Use Zag.js for UI components and design system
Reuse Zag components frompackages/web/src/components/zag/*and check the README.md in that folder for existing components before adding new components
Components should receive at most 1–5 props, and only for local configuration, not shared state. If a component would need more than 5 props, move the shared data into an external store, a primitive, or Solid context
Do not destructure props in SolidJS components. Instead, access props directly from the props object or wrap them in a function to ensure they are always up-to-date
Components should be lean and focused on rendering. Do not implement business logic; move that into stores, utilities, or primitives
Never have a component act as a 'God component' coordinating multiple large concerns
Files:
packages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsxpackages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
packages/web/src/components/**
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Group related components in subdirectories with an
index.jsbarrel export
Files:
packages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsxpackages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
packages/web/src/stores/**/*.{js,ts}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use Solid's
createStorefor complex state or state objects to ensure better performance and reactivity
Files:
packages/web/src/stores/projectActionsStore/pdfs.js
🧠 Learnings (3)
📚 Learning: 2025-12-19T12:38:38.880Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-19T12:38:38.880Z
Learning: Applies to packages/web/src/stores/**/*.{js,ts} : Use Solid's `createStore` for complex state or state objects to ensure better performance and reactivity
Applied to files:
packages/web/src/primitives/useProject/index.js
📚 Learning: 2025-12-19T12:38:18.491Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursorrules:0-0
Timestamp: 2025-12-19T12:38:18.491Z
Learning: Applies to packages/web/src/**/*.{jsx,tsx,js,ts} : For complex state or state objects in SolidJS, use Solid's `createStore` for better performance and reactivity
Applied to files:
packages/web/src/primitives/useProject/index.js
📚 Learning: 2025-12-19T12:38:18.491Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursorrules:0-0
Timestamp: 2025-12-19T12:38:18.491Z
Learning: Applies to packages/web/src/**/*.{jsx,tsx,js,ts} : When you need to compute a value based on props or state in SolidJS, use `createMemo` to ensure it updates reactively
Applied to files:
packages/web/src/primitives/useProject/index.js
🧬 Code graph analysis (2)
packages/web/src/primitives/useProject/index.js (2)
packages/web/src/stores/projectStore.js (2)
projectStore(538-538)registerStaleProjectCleanup(545-547)packages/web/src/primitives/useProject/connection.js (1)
createConnectionManager(30-270)
packages/web/src/stores/projectActionsStore/pdfs.js (2)
packages/web/src/stores/projectActionsStore/index.js (2)
study(112-112)packages/workers/src/routes/pdfs.js (6)
projectId(25-25)projectId(68-68)projectId(96-96)projectId(237-237)projectId(285-285)file(126-126)
⏰ 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). (2)
- GitHub Check: Workers Builds: corates
- GitHub Check: Workers Builds: corates-workers-prod
🔇 Additional comments (12)
packages/web/src/stores/projectActionsStore/pdfs.js (1)
87-92: Client-side duplicate check improves UX.The pre-upload duplicate check provides immediate user feedback before initiating the upload. The error message is clear and actionable.
Note: A race condition exists between this check and the actual upload (another user could upload a file with the same name in between), but this is acceptable since the server-side check at the workers endpoint is authoritative and will catch any duplicates.
packages/workers/src/routes/pdfs.js (1)
192-203: Server-side duplicate check prevents file overwrites.The HEAD request before storage ensures existing files are not silently overwritten. The error response is consistent with the client-side check and provides clear guidance.
The additional R2 HEAD operation adds minimal latency and is necessary for data integrity.
packages/workers/src/config/constants.js (1)
102-102: LGTM!The new
FILE_ALREADY_EXISTSerror code follows the established taxonomy (5xxx for file errors) and provides a clear, consistent error message for duplicate file scenarios.packages/workers/src/routes/projects.js (2)
244-255: LGTM! Fetching project metadata and members before deletion.The approach of retrieving project name and member list before deletion is correct for supporting notifications. The queries are efficient and use proper Drizzle ORM patterns.
257-272: LGTM! Graceful DO disconnect with proper error handling.The try-catch ensures that DO disconnect failures don't block the deletion flow. This is the correct approach since the project will be deleted anyway.
packages/web/src/primitives/useProject/index.js (5)
26-30: LGTM! IndexedDB prefix is consistent.The
INDEXEDDB_PREFIXconstant (corates-project-) correctly matches the prefix used when creatingIndexeddbPersistenceon line 240 (corates-project-${projectId}). This ensures the cleanup function deletes the correct database.Also applies to: 239-242
118-132: LGTM! Robust IndexedDB deletion with proper blockers handling.The
onblockedhandler correctly logs a warning and continues withresolve()rather than blocking indefinitely. This is important because blocked deletions can occur when other tabs have open connections, and the cleanup should proceed gracefully.
139-141: LGTM! Registration at module load time.Registering the cleanup callback at module load avoids circular dependency issues while ensuring the projectStore can trigger cleanup when detecting stale projects. The comment clearly explains the rationale.
274-278: LGTM! onAccessDenied triggers complete cleanup.The async callback properly awaits
cleanupProjectLocalDatato ensure all local data is removed when access is denied. This handles project deletion, membership revocation, and unauthorized access scenarios comprehensively.
134-137: BothclearProjectandremoveProjectFromListmethods are defined in projectStore and properly exported. No issues found.packages/web/src/components/project-ui/all-studies-tab/study-card/StudyPdfSection.jsx (1)
51-51: The error handling in this code is appropriate and follows standard patterns. TheuploadPdfAPI function inpdf-api.jsalready sanitizes errors by attempting to parse the response JSON and falling back to a generic message if parsing fails. The backend is responsible for providing user-friendly error messages in theerror.errorfield. The change on line 51 to surfaceerr.messagewith a fallback is good UX practice—it displays specific backend-provided errors while falling back to a generic message if none is available. No sensitive information is exposed in the current implementation.packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx (1)
191-191: Improved error messaging.Using
err.messageprovides users with specific error details instead of a generic message, making it easier to diagnose upload issues. The fallback ensures a message is always shown.
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.