Skip to content

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

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

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

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, following existing codebase patterns (clients, work entries).

Backend:

  • New projects table in SQLite with name, description, client_id (FK → clients), start_date, status (active/completed/on-hold via CHECK constraint), and user_email scoping
  • Joi validation schemas (projectSchema, updateProjectSchema) with status enum validation
  • Full CRUD routes at /api/projects: list all, get by ID, create, update, delete single, delete all
  • Projects are LEFT JOINed with clients to include client_name in responses
  • 31 new backend tests mirroring the clients test structure

Frontend:

  • Project, CreateProjectRequest, UpdateProjectRequest TypeScript interfaces
  • API client methods for all project CRUD operations
  • ProjectsPage component with table view, create/edit dialog (with client dropdown + status select + date picker), delete with confirmation
  • Status displayed as color-coded chips (green=active, grey=completed, yellow=on-hold)
  • Navigation item added to sidebar

Review & Testing Checklist for Human

  • Verify SQLite FK enforcement is enabled — SQLite does not enforce foreign keys by default (PRAGMA foreign_keys = ON is required). The client_id FK on the projects table and the ON DELETE SET NULL behavior may be silently unenforced. Check database/init.js for whether this pragma is set.
  • No application-level validation that client_id exists — Unlike work entries (which check client existence before insert), the projects route trusts the FK constraint. If FK enforcement is off, users can assign non-existent client IDs to projects. Manually test creating a project with an invalid clientId.
  • Test the full create → edit → delete flow in the UI — Backend tests are all mocked, so SQL query correctness (especially the LEFT JOIN and dynamic UPDATE builder) is only verifiable through manual/integration testing. Start the app, create a project with/without a client, edit its status, then delete it.
  • Verify client deletion cascading behavior — Delete a client that is assigned to a project and confirm the project's client_id is set to null (not orphaned or errored).

Notes

  • All 192 backend tests pass (161 existing + 31 new project tests).
  • Frontend TypeScript compilation passes with no errors.
  • The implementation closely mirrors the existing clients CRUD pattern for consistency.
  • Uses in-memory SQLite, so the new table is created fresh on each server restart (no migration needed).

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


Open with Devin

- Add projects table to database with name, description, client_id, start_date, status fields
- Add Joi validation schemas for project create/update
- Add full CRUD API endpoints at /api/projects
- Add comprehensive backend tests (31 tests covering all endpoints and error cases)
- Add Project TypeScript types and API client methods
- Add ProjectsPage React component with table view, create/edit/delete dialogs
- Add Projects navigation item to sidebar layout
- Status supports active/completed/on-hold with color-coded chips
- Projects can be assigned to existing clients 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 2 potential issues.

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 || '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-user data leakage

The POST / and PUT /:id project routes accept a clientId but never verify that it belongs to the authenticated user. The existing workEntries.js:90-102 route has this check (SELECT id FROM clients WHERE id = ? AND user_email = ?) before inserting, but the new projects routes skip it entirely. A malicious user can create or update a project with any client_id, and the LEFT JOIN clients c ON p.client_id = c.id in the GET queries (projects.js:16-18) would then return another user's client_name, leaking their data.

Prompt for agents
The POST / and PUT /:id handlers in backend/src/routes/projects.js accept a clientId from the request body but never validate that the referenced client belongs to the authenticated user. Compare with the pattern in backend/src/routes/workEntries.js:90-102, which does SELECT id FROM clients WHERE id = ? AND user_email = ? before inserting. The fix should add a similar ownership check in both the create handler (before the INSERT, around line 75) and the update handler (when value.clientId is provided, around line 154). If clientId is provided and non-null, query the clients table to verify the client belongs to req.userEmail. Return a 400 error like 'Client not found or does not belong to user' if the check fails. The clientId is optional/nullable for projects, so only perform the check when a non-null clientId is provided.
Open in Devin Review

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


const projectPayload = {
name: formData.name,
description: formData.description || undefined,
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.

🟡 Description field cannot be cleared when editing a project due to || undefined coercion

In handleSubmit, description uses formData.description || undefined (line 165), which converts an empty string to undefined. Since undefined is stripped during JSON serialization, the backend never receives the field and won't update it. In contrast, clientId and startDate in the same payload use null (lines 166-167), which IS serialized and correctly clears those fields. This inconsistency means a user can clear a project's client or start date, but cannot clear its description once set.

Suggested change
description: formData.description || undefined,
description: formData.description || null,
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