Skip to content

feat: add Projects management feature#470

Open
devin-ai-integration[bot] wants to merge 1 commit intomainfrom
devin/1776165178-add-projects-feature
Open

feat: add Projects management feature#470
devin-ai-integration[bot] wants to merge 1 commit intomainfrom
devin/1776165178-add-projects-feature

Conversation

@devin-ai-integration
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot commented Apr 14, 2026

Summary

Adds a new "Projects" resource to the app with full backend CRUD API, frontend UI page, and backend unit tests. Projects have a name, description, optional client assignment, optional start date, and a status (active / completed / on-hold). Projects are scoped per-user (same pattern as work entries).

Backend:

  • New projects table in init.js with foreign keys to clients (SET NULL on delete) and users (CASCADE on delete), plus a CHECK constraint on status
  • Joi validation schemas (projectSchema, updateProjectSchema) in schemas.js
  • Full CRUD routes in routes/projects.js: list, get by id, create, update, delete one, delete all
  • Client existence is verified when assigning a project to a client (on both create and update)
  • Routes registered at /api/projects in server.js
  • 37 unit tests covering all endpoints, validation, error paths, and client verification

Frontend:

  • ProjectsPage.tsx — table view with create/edit dialog, client dropdown, status dropdown, date picker
  • Project types and API client methods added to types/api.ts and api/client.ts
  • "Projects" nav item added to sidebar (Layout.tsx) and route added to App.tsx

Review & Testing Checklist for Human

  • Client assignment verification does not check user_emailSELECT id FROM clients WHERE id = ? is used when verifying a client exists during project create/update. Since the clients table on main is user-scoped, this means a user could potentially assign a project to another user's client. Verify whether this is acceptable or if AND user_email = ? should be added.
  • End-to-end test: Log in, navigate to /projects, create a project (with and without client assignment), edit the status, delete a project, and verify the "Clear All" button works. Check that the client dropdown populates correctly.
  • Verify the dialog form handles edge cases: empty start date, switching between clients, changing status values, and that the label prop on both <Select> components produces proper notched outlines (no strikethrough).
  • Date handling: Verify that startDate round-trips correctly (frontend sends YYYY-MM-DD string, backend stores it, frontend displays it via toLocaleDateString()). Timezone offsets may cause off-by-one display issues depending on locale.

Notes

  • The CreateProjectRequest and UpdateProjectRequest TypeScript interfaces are defined in types/api.ts but are not imported by the API client or page component (inline types are used instead). Harmless but could be cleaned up.
  • Projects use ON DELETE SET NULL for the client FK — if a client is deleted, the project remains but client_id becomes null. This is intentional.
  • The frontend fetches the clients list (via useQuery(['clients'])) to populate the client dropdown in the project form. This reuses the existing clients API.

Link to Devin session: https://partner-workshops.devinenterprise.com/sessions/21a7ea2e9e9f47f0ac809d285964aadf


Open with Devin

- Add projects table to database schema with name, description, client_id, start_date, status fields
- Add Joi validation schemas for project create/update
- Add full CRUD API routes at /api/projects with client existence verification
- Add 37 backend tests covering all project endpoints and error cases
- Add ProjectsPage frontend component with table view and create/edit dialog
- Add Project types and API client methods
- Add Projects nav item to sidebar layout
- Add /projects route to app router
@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Copy link
Copy Markdown
Contributor Author

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

View 5 additional findings in Devin Review.

Open in Devin Review

};

if (clientId) {
db.get('SELECT id FROM clients WHERE id = ?', [clientId], (err, row) => {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Missing user_email filter in client ownership check allows cross-user data access (POST)

When creating a project with a clientId, the client existence query at backend/src/routes/projects.js:113 is SELECT id FROM clients WHERE id = ?, which only checks if the client exists globally — not that it belongs to the authenticated user. Every other route in the codebase filters by user_email when checking client ownership (e.g., backend/src/routes/clients.js:118, backend/src/routes/workEntries.js:92). This allows User A to associate their project with User B's client ID, and since the GET queries use a LEFT JOIN to fetch client_name, User A would also see User B's client name — a cross-user information leakage.

Suggested change
db.get('SELECT id FROM clients WHERE id = ?', [clientId], (err, row) => {
db.get('SELECT id FROM clients WHERE id = ? AND user_email = ?', [clientId, req.userEmail], (err, row) => {
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


// If clientId is being updated, verify it exists
if (value.clientId) {
db.get('SELECT id FROM clients WHERE id = ?', [value.clientId], (err, clientRow) => {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Missing user_email filter in client ownership check allows cross-user data access (PUT)

Same issue as in the POST route: when updating a project's clientId, the client verification query at backend/src/routes/projects.js:227 is SELECT id FROM clients WHERE id = ? without filtering by the authenticated user's email. This is inconsistent with the established pattern used throughout the codebase (e.g., backend/src/routes/workEntries.js:176, backend/src/routes/clients.js:118) and allows a user to reassign their project to another user's client.

Suggested change
db.get('SELECT id FROM clients WHERE id = ?', [value.clientId], (err, clientRow) => {
db.get('SELECT id FROM clients WHERE id = ? AND user_email = ?', [value.clientId, req.userEmail], (err, clientRow) => {
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants