Skip to content

feat: Add Projects management feature with CRUD API and frontend UI#460

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

feat: Add Projects management feature with CRUD API and frontend UI#460
devin-ai-integration[bot] wants to merge 1 commit intomainfrom
devin/1776164000-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, covering backend API and frontend UI. 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 foreign keys to clients and users, plus indexes on user_email, client_id, and status
  • Joi validation schemas (projectSchema, updateProjectSchema) with status enum constraint
  • CRUD routes at /api/projects (list, get, create, update, delete, delete-all) following the existing clients pattern
  • Client ownership verification when assigning a client_id to a project
  • 34 unit tests mirroring the existing clients.test.js structure

Frontend:

  • Project, CreateProjectRequest, UpdateProjectRequest TypeScript interfaces
  • API client methods for all project endpoints
  • ProjectsPage component with MUI table, create/edit dialog (client dropdown, status select, date picker), color-coded status chips, and bulk delete
  • Route at /projects and sidebar nav entry with AccountTree icon

Review & Testing Checklist for Human

  • Verify SQL JOIN queries work correctly against real SQLite — tests mock the DB entirely, so the LEFT JOIN clients queries in projects.js (GET list, GET by id, POST retrieve, PUT retrieve) have not been validated against a real database. Spin up the backend and create/list projects to confirm the join works and client_name populates correctly.
  • Test client assignment edge cases — assign a client to a project, then delete that client. Verify the project's client_id is set to NULL (via ON DELETE SET NULL) and the UI handles the now-missing client gracefully.
  • Test start_date round-trip — create a project with a start date, edit it, clear the date, and save. Confirm the empty-string-to-null conversion works correctly through Joi validation and the DB layer.
  • Verify the frontend form dialog — open create/edit dialogs and confirm the client dropdown loads clients correctly, status dropdown works, and date field renders with the shrink label behavior.
  • Check CreateProjectRequest / UpdateProjectRequest types — these are defined in types/api.ts but the API client methods use inline types instead; consider whether to wire them up or remove the unused interfaces.

Notes

  • The project follows existing patterns closely (compare projects.js to clients.js, ProjectsPage.tsx to ClientsPage.tsx).
  • Database is in-memory SQLite, so no migration is needed — the table is created on startup.
  • The helper functions insertProject and performUpdate in projects.js are module-level functions (not on the router) to keep the route handlers readable; this diverges slightly from the clients pattern which inlines everything.

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


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 updates
- Add CRUD API endpoints at /api/projects with client ownership verification
- Add comprehensive backend tests (34 test cases)
- Add ProjectsPage React component with MUI table, create/edit dialog, status chips
- Add project types and API client methods in frontend
- Add Projects navigation item in sidebar layout
- Support active/completed/on-hold status with color-coded chips
- Support optional client assignment with dropdown selector
@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 5 additional findings in Devin Review.

Open in Devin Review


const payload = {
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.

🟡 Users cannot clear project description when editing due to || undefined coercion

In handleSubmit, the payload is constructed with description: formData.description || undefined at line 161. When a user edits a project and clears the description field, formData.description becomes '', which is falsy, so it gets coerced to undefined. Since JSON.stringify strips undefined values, the description key is omitted from the request body entirely. The backend's updateProjectSchema then doesn't see a description field, so performUpdate (backend/src/routes/projects.js:200-203) skips updating it, leaving the old description in the database. In contrast, client_id and start_date use || null which correctly serializes and allows clearing those fields. The fix is to use description: formData.description || null (and add .allow(null) to the Joi schema) or simply send description: formData.description since the backend already handles empty strings via value.description || null in performUpdate, and the Joi schema already has .allow('').

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