Skip to content

feat: Add Projects management feature with CRUD API, tests, and UI#471

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

feat: Add Projects management feature with CRUD API, tests, and UI#471
devin-ai-integration[bot] wants to merge 3 commits intomainfrom
devin/1776165387-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 app. Each project has a name, description, optional client assignment, start date, and status (active / completed / on-hold).

Backend:

  • New projects table in SQLite with foreign keys to clients (ON DELETE SET NULL) and users (ON DELETE CASCADE), plus a CHECK constraint on status values
  • Joi validation schemas (projectSchema, updateProjectSchema)
  • CRUD endpoints at /api/projects — GET all, GET by id, POST, PUT, DELETE — following the same patterns as the existing /api/clients routes
  • Queries use LEFT JOIN to include client_name in responses
  • Client ownership validation on POST and PUT: when a clientId is provided, the route verifies the client belongs to the authenticated user before proceeding (matching the pattern in workEntries.js)

Frontend:

  • ProjectsPage with MUI table, color-coded status chips, and a create/edit dialog with client dropdown + date picker
  • API client methods and TypeScript interfaces for Project types
  • Route (/projects) and sidebar nav item added

Tests:

  • 34 unit tests covering all endpoints, validation errors, DB errors, client ownership validation, and edge cases (all passing)

Updates since last revision

  • Security fix (20c0b44): Added client ownership validation to POST and PUT routes. Previously, a user could assign their project to another user's client and leak the client name via the LEFT JOIN. Now both routes check SELECT id FROM clients WHERE id = ? AND user_email = ? before inserting/updating, returning 400 if the client doesn't belong to the user. Added 5 new tests for these paths.
  • Bug fix (12cdb12): Changed description: formData.description || undefined to || null in handleSubmit so that clearing a project's description in the edit dialog actually persists the change. The previous || undefined coercion caused JSON.stringify to strip the field entirely, so the backend's dynamic query builder would skip it. Updated API client and mutation type signatures to accept string | null for description.

Review & Testing Checklist for Human

  • Verify client ownership validation logic: The fix uses nested callback helpers (insertProject/performUpdate) — trace the control flow in projects.js POST and PUT handlers to confirm all code paths correctly gate on the ownership check and that no early-return is missed
  • Test cross-user isolation end-to-end: Create two users, create a client under user A, then attempt to create/update a project under user B with user A's client ID — confirm it returns 400. The unit tests mock the DB so this hasn't been verified against real SQLite
  • Test clearing optional fields in the edit dialog: Open a project with a description, clear the text, save — verify the description is actually removed. Repeat for client assignment and start date to confirm all nullable fields can be cleared
  • Verify the SQL schema: The projects table uses ON DELETE SET NULL for client_id — confirm this is the desired behavior when a client is deleted (projects remain but lose their client assignment)
  • Test the full flow end-to-end: Create a client, create a project assigned to that client, edit the project's status, delete the project

Notes

  • The CreateProjectRequest / UpdateProjectRequest TypeScript interfaces are defined in types/api.ts but the API client uses inline type annotations instead — these could be consolidated in a follow-up
  • Unlike the clients feature, there is no "delete all projects" bulk endpoint — this was an intentional simplification
  • The database is in-memory so no migration is needed; the table is created on startup alongside existing tables

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


Open with Devin

- Add projects table to database with name, description, client_id, start_date, status fields
- Add Joi validation schemas for project creation and update
- Add CRUD API endpoints at /api/projects (GET, GET/:id, POST, PUT/:id, DELETE/:id)
- Add 29 backend tests covering all endpoints and error cases
- Add Project types and API client methods in frontend
- Add ProjectsPage with table view and create/edit/delete dialog
- Add Projects nav item to sidebar layout
- Add /projects route to App.tsx
@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.

- POST /api/projects now validates clientId belongs to authenticated user
- PUT /api/projects/:id now validates clientId belongs to authenticated user
- Added 5 new tests covering ownership validation and error cases
- Follows existing pattern from workEntries routes
devin-ai-integration[bot]

This comment was marked as resolved.

…field

Changes description coercion from || undefined to || null so users
can clear a project's description when editing. Updated API client
and mutation type signatures to accept string | null for description.
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