Skip to content

feat: Full-stack task manager with React+Tailwind, Express, MongoDB, JWT auth#6

Open
devin-ai-integration[bot] wants to merge 1 commit intomainfrom
devin/1776088494-fullstack-task-manager
Open

feat: Full-stack task manager with React+Tailwind, Express, MongoDB, JWT auth#6
devin-ai-integration[bot] wants to merge 1 commit intomainfrom
devin/1776088494-fullstack-task-manager

Conversation

@devin-ai-integration
Copy link
Copy Markdown
Contributor

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

Summary

Replaces the in-memory vanilla JS todo prototype with a production-oriented full-stack task manager.

Backend (backend/): Express + MongoDB (Mongoose) + JWT authentication

  • User model with bcrypt-hashed passwords; Task model with title, description, status (todo/in-progress/done), dueDate, and user reference
  • Auth routes: register, login, /me — returns signed JWT on success
  • Task CRUD routes scoped to the authenticated user, with status filtering and sort options
  • Request validation via express-validator; JWT middleware in middleware/auth.js

Frontend (frontend/): React 18 + Vite + Tailwind CSS + React Router v6

  • AuthContext manages JWT in localStorage; Axios interceptors attach token and redirect on 401
  • Login / Register pages, protected Dashboard route
  • Dashboard: task stats, status filter buttons, inline create/edit form, TaskCard with status dropdown, edit/delete actions, overdue highlighting

Other: Updated README with setup instructions, env var tables, API endpoint reference, and Vercel/Render deployment guide.

Review & Testing Checklist for Human

  • Auth security: Verify JWT signing/verification in backend/middleware/auth.js and backend/routes/auth.js — confirm tokens are not leaking sensitive data and password is excluded from responses (select: false on User model)
  • Data isolation: Confirm all task routes in backend/routes/tasks.js filter by user: req.user.id — a user must not be able to read/update/delete another user's tasks
  • 401 redirect loop: The Axios response interceptor (frontend/src/services/api.js) does window.location.href = '/login' on 401. Verify this doesn't cause an infinite reload if /auth/me fires on the login page (the PublicRoute wrapper should prevent this, but worth confirming)
  • End-to-end manual test: Spin up MongoDB, backend, and frontend locally. Register a user → login → create tasks with due dates → edit status → filter → delete. Verify data persists across page refreshes
  • CORS in production: cors({ origin: process.env.CLIENT_URL || '*' }) defaults to open. Confirm CLIENT_URL is set correctly when deploying to Render/Vercel

Notes

  • No automated tests are included in this PR — manual E2E verification is necessary
  • No rate limiting on auth endpoints (login/register) — consider adding before production use
  • Frontend build (npm run build) and lint (npm run lint) pass cleanly (1 non-blocking warning about fast refresh in AuthContext)

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


Open with Devin

…JWT auth

- Backend: Express server with MongoDB (Mongoose), JWT authentication,
  task CRUD API with validation (express-validator)
- Frontend: React 18 + Tailwind CSS + Vite, auth pages (login/register),
  task dashboard with create/edit/delete, status filters, due dates
- Models: User (bcrypt hashed passwords) and Task (title, description,
  status, dueDate, user reference)
- Auth: JWT middleware, register/login/me endpoints
- README: setup instructions, API docs, Vercel + Render deployment guide
@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 5 additional findings in Devin Review.

Open in Devin Review

Comment on lines +17 to +21
(error) => {
if (error.response?.status === 401) {
localStorage.removeItem('token');
window.location.href = '/login';
}
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.

🔴 401 response interceptor hijacks login failures, causing page reload instead of showing error message

The Axios response interceptor at frontend/src/services/api.js:18-21 catches all 401 responses and does window.location.href = '/login', causing a full page reload. However, the login endpoint (backend/routes/auth.js:55,60) returns 401 for invalid credentials. When a user enters a wrong password on the login page, the interceptor fires before the Login component's catch block (frontend/src/pages/Login.jsx:21-22) can display the error message. The result: the page reloads to /login instead of showing "Invalid credentials", making it impossible for users to see why login failed.

Detailed flow
  1. User submits wrong password on /login
  2. POST /api/auth/login returns 401 { message: 'Invalid credentials' }
  3. Axios response interceptor runs: removes token from localStorage, sets window.location.href = '/login'
  4. Promise.reject(error) is returned, but the navigation triggers a full page reload
  5. The Login component's setError(...) either never executes or is immediately lost to the reload
Prompt for agents
The 401 response interceptor in frontend/src/services/api.js should not redirect on 401 responses from authentication endpoints (/auth/login, /auth/register). These endpoints return 401 to mean "invalid credentials", not "expired token".

Approach 1: Check the request URL before redirecting. For example, skip the redirect if the request URL includes '/auth/login' or '/auth/register'.

Approach 2: Only redirect if a token was previously stored in localStorage (i.e., the user was logged in and the token expired), and skip the redirect otherwise.

Either approach would allow the Login and Register components to properly catch and display the error message to the user.
Open in Devin Review

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

Comment on lines +59 to +61
const todoCount = tasks.filter((t) => t.status === 'todo').length;
const inProgressCount = tasks.filter((t) => t.status === 'in-progress').length;
const doneCount = tasks.filter((t) => t.status === 'done').length;
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.

🟡 Dashboard status counts are computed from filtered task list, showing misleading zeros

In Dashboard.jsx, the status counts (todoCount, inProgressCount, doneCount at lines 59-61) are computed from the tasks array, which is fetched with the currently active status filter (line 22-24). When a user filters by e.g. "In Progress", the API only returns in-progress tasks, so todoCount and doneCount will both show 0 in the stats panel — even though the user may have many such tasks. The stats cards give no indication they are filtered, making them misleading.

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