Skip to content

feat: add Projects management feature with CRUD API, frontend UI, and tests#480

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

feat: add Projects management feature with CRUD API, frontend UI, and tests#480
devin-ai-integration[bot] wants to merge 1 commit intomainfrom
devin/1776237821-add-projects-feature

Conversation

@devin-ai-integration
Copy link
Copy Markdown
Contributor

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

Summary

Adds a full "Projects" management feature to the timesheet app, following existing codebase patterns (clients, work entries).

Backend:

  • New projects table in SQLite with fields: name, description, client_id (FK to clients), start_date, status (active/completed/on-hold), user_email
  • Joi validation schemas (projectSchema, updateProjectSchema) with status enum constraint
  • RESTful CRUD routes at /api/projects (GET list, GET by id, POST, PUT, DELETE by id, DELETE all)
  • Projects JOIN with clients to include client_name in responses
  • 37 unit tests covering all endpoints, validation, and error paths

Frontend:

  • Project / CreateProjectRequest / UpdateProjectRequest TypeScript types
  • API client methods for all project endpoints
  • ProjectsPage component with table view, create/edit dialog (with client dropdown + status selector + date picker), delete with confirmation
  • "Projects" added to sidebar navigation and app routes

Review & Testing Checklist for Human

  • Verify startDate storage format: Joi's Joi.date().iso() converts input strings into JavaScript Date objects. When these are passed to the SQLite driver, they may be stored as Date.toString() (e.g. "Wed Jan 01 2024 00:00:00 GMT+0000") rather than the expected ISO format. Manually create a project with a start date and verify what gets stored/returned. (This same pattern exists in workEntrySchema — may be a pre-existing issue.)
  • Client assignment with non-existent client_id: The API does not validate that clientId references an existing client before inserting. SQLite FK constraints are not enforced (no PRAGMA foreign_keys = ON in init.js). Try creating a project with clientId: 99999 and confirm the behavior is acceptable.
  • End-to-end manual test: Log in, navigate to Projects page, create a project (with and without client assignment), edit it, change status, delete it. Verify the client dropdown populates correctly.
  • Confirm ON DELETE SET NULL behavior: Delete a client that is assigned to a project and verify the project's client_id becomes null (note: this depends on FK enforcement being enabled, which it currently is not).

Notes

  • All backend tests pass (193 total, including 37 new project tests). Frontend TypeScript and ESLint checks pass.
  • The test suite is entirely mocked (no integration tests) — consistent with existing test approach but means SQL correctness is not verified by tests.
  • The bulk DELETE /api/projects endpoint follows the same pattern as DELETE /api/clients — destructive but behind confirmation dialog in the UI.

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


Open with Devin

… tests

- Add projects table to database with name, description, client_id, start_date, status fields
- Add Joi validation schemas for project creation and updates
- Create RESTful API endpoints: GET/POST/PUT/DELETE /api/projects
- Add 37 backend tests covering all CRUD operations and error handling
- Add TypeScript types for Project, CreateProjectRequest, UpdateProjectRequest
- Add API client methods for all project endpoints
- Create ProjectsPage with table view, create/edit dialog, and delete functionality
- Add Projects navigation item to sidebar with FolderOpen icon
- Support project status (active/completed/on-hold) with color-coded chips
- Support optional client assignment via dropdown
@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 1 potential issue.

View 4 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, 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 referencing other users' clients

The project create (POST /) and update (PUT /:id) routes accept a clientId and insert it directly into the database without verifying that the client belongs to the authenticated user. This allows a user to associate their project with another user's client by providing that client's ID via direct API calls.

The existing workEntries.js route validates this correctly — on create (workEntries.js:90-102) it checks SELECT id FROM clients WHERE id = ? AND user_email = ?, and on update (workEntries.js:174-193) it does the same when clientId is being changed. The projects route skips this validation entirely for both operations.

Prompt for agents
In backend/src/routes/projects.js, the POST / handler (create project) at line 75 and the PUT /:id handler (update project) around lines 154-157 both accept a clientId and pass it directly to the database without verifying that the client belongs to the authenticated user (req.userEmail).

The established pattern for this validation can be seen in backend/src/routes/workEntries.js. In the POST handler (lines 90-102), before inserting the work entry, it runs:
  db.get('SELECT id FROM clients WHERE id = ? AND user_email = ?', [clientId, req.userEmail], ...)
and returns a 400 error if the client is not found.

Similarly, in the PUT handler (lines 174-193), it conditionally validates the clientId if it's being updated.

Apply the same pattern to both the POST and PUT handlers in projects.js:
1. In POST /, before the INSERT, check that clientId (when provided and non-null) belongs to the user
2. In PUT /:id, when value.clientId is provided and non-null, verify it belongs to the user before including it in the update query
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