Skip to content

feat: Add Projects management feature with CRUD operations#463

Open
devin-ai-integration[bot] wants to merge 2 commits intomainfrom
devin/1776164746-add-projects-feature
Open

feat: Add Projects management feature with CRUD operations#463
devin-ai-integration[bot] wants to merge 2 commits intomainfrom
devin/1776164746-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 full "Projects" management feature to the timesheet application, covering backend API, frontend UI, and backend tests. Each project has a name, description, optional client assignment, start date, and status (active / completed / on-hold).

Backend:

  • New projects table in SQLite schema with FK to clients (ON DELETE SET NULL) and users (ON DELETE CASCADE), plus a CHECK constraint on status values
  • Joi validation schemas (projectSchema, updateProjectSchema)
  • CRUD routes at /api/projects (GET all, GET by id, POST, PUT, DELETE) — follows the same pattern as the existing /api/clients routes
  • 31 new unit tests mirroring the existing clients.test.js structure

Frontend:

  • Project, CreateProjectRequest, UpdateProjectRequest TypeScript interfaces
  • API client methods for all project CRUD operations
  • ProjectsPage component with table view, colored status chips, create/edit dialog (with client dropdown + date picker + status selector)
  • New "Projects" sidebar nav item and /projects route

Updates since last revision

  • Fixed a bug where clearing a project's description in the edit dialog had no effect. The payload was using || undefined which gets stripped during JSON serialization, so the backend never received the field. Changed to || '', which the backend correctly normalizes to null in the DB.

Review & Testing Checklist for Human

  • No server-side client_id existence check on create/update: When assigning a client to a project, the backend does not verify the client_id actually exists before inserting — it relies on the SQLite FK constraint, which would surface as a generic 500 error rather than a descriptive 400. The existing workEntries routes DO validate client existence; this PR does not. Decide if this gap is acceptable.
  • ON DELETE SET NULL for client FK: When a client is deleted, any projects referencing it will silently have client_id set to NULL. Verify this is the desired behavior vs. blocking deletion or cascading.
  • Manual E2E test: Start both backend and frontend, log in, navigate to Projects, and verify: create a project (with and without client assignment), edit it (change status, reassign client), delete it. Confirm the client dropdown populates correctly. Also verify that adding a description and then clearing it works as expected.
  • Status values and CHECK constraint: Confirm the three allowed status values (active, completed, on-hold) match product requirements. The CHECK constraint is enforced at the DB level in addition to Joi validation.

Notes

  • No frontend tests were added (consistent with the existing codebase which has no frontend test setup).
  • All 192 backend tests pass (161 existing + 31 new).
  • The startDate Joi schema allows both null and empty string '', coercing both to null in the DB. This is intentional but reviewers should be aware of the normalization behavior.

Link to Devin session: https://partner-workshops.devinenterprise.com/sessions/8c62860204b74e93b1dc92edd92438fe


Open with Devin

- Add projects table to database with name, description, client assignment,
  start date, and status (active/completed/on-hold)
- Add Joi validation schemas for project create/update
- Add backend API endpoints: GET/POST/PUT/DELETE /api/projects
- Add comprehensive backend tests (31 tests)
- Add frontend ProjectsPage with table view and create/edit/delete dialogs
- Add Project types and API client methods
- Add Projects navigation item to sidebar layout
@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

devin-ai-integration[bot]

This comment was marked as resolved.

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 1 new potential issue.

View 6 additional findings in Devin Review.

Open in Devin Review

Comment on lines +75 to +77
db.run(
'INSERT INTO projects (name, description, client_id, start_date, status, user_email) VALUES (?, ?, ?, ?, ?, ?)',
[name, description || null, clientId || null, startDate || null, status || 'active', req.userEmail],
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 client ownership validation allows cross-tenant data reference

When creating or updating a project, the clientId is inserted/updated directly without verifying that the client belongs to the authenticated user. This is unlike the work entries route (backend/src/routes/workEntries.js:90-102), which explicitly checks SELECT id FROM clients WHERE id = ? AND user_email = ? before allowing the operation.

A user can associate their project with another user's client by providing that client's ID. The GET queries then expose the other user's client name via the LEFT JOIN clients c ON p.client_id = c.id (e.g., backend/src/routes/projects.js:16-18), leaking cross-tenant data.

Affected locations

POST route (line 77) inserts clientId without ownership check:

[name, description || null, clientId || null, startDate || null, status || 'active', req.userEmail]

PUT route (lines 154-157) updates client_id without ownership check:

if (value.clientId !== undefined) {
  updates.push('client_id = ?');
  values.push(value.clientId || null);
}
Prompt for agents
In backend/src/routes/projects.js, both the POST (line 75-104) and PUT (line 154-157) routes accept a clientId but never verify that the client belongs to the authenticated user. The established pattern from backend/src/routes/workEntries.js (lines 90-102 for create, lines 173-193 for update) is to query SELECT id FROM clients WHERE id = ? AND user_email = ? before proceeding.

For the POST route: before the INSERT INTO projects query, add a conditional check when clientId is provided. Query the clients table to verify ownership (using clientId and req.userEmail). If the client is not found, return 400 with an appropriate error message. Only proceed with the insert if validation passes (or if clientId is null).

For the PUT route: similarly, when value.clientId is defined and not null, verify ownership of the client before building and executing the UPDATE query. This follows the same pattern as workEntries.js lines 173-193.
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