A collaborative project and task management application with real-time updates and kanban boards.
Live demo: https://fahad-projectflow.vercel.app
ProjectFlow is a project management tool designed for teams to organize tasks, track progress across custom kanban columns, and collaborate in real time. It provides visual task boards with drag and drop, inline comments, member role management, in-app notifications, and a project dashboard with delivery metrics.
Built as Task 3 (Project Management Tool) of the CodeAlpha Full Stack Development internship.
The database seed script populates 5 demo accounts. All accounts share the same password: projectflow-demo-1234.
| Name | Project roles | |
|---|---|---|
| Arjun Sharma | arjun.sharma@projectflow.demo | Owner of Clinic Appointment System |
| Priya Mehta | priya.mehta@projectflow.demo | Owner of Brand Refresh; Member of Clinic Appointment System |
| Daniel Okafor | daniel.okafor@projectflow.demo | Owner of Office Move Q1; Member of Clinic Appointment System |
| Sofia Reyes | sofia.reyes@projectflow.demo | Member of Clinic Appointment System and Brand Refresh |
| Lena Fischer | lena.fischer@projectflow.demo | Member of Brand Refresh and Office Move Q1 |
To generate these accounts and sample data:
npm run seedNote: These demo accounts exist in the database only after running the seed command.
- User registration and login with email and password
- Passwords hashed using bcrypt (cost factor 10)
- Stateless authentication using JWT tokens stored in httpOnly, SameSite cookies
- Fixed-window rate limiting on login and registration backed by MongoDB
- Route protection and authentication redirects handled at the proxy layer
- Create, rename, and delete projects
- Role-based project permissions: Owner and Member
- Project owners can add members by email and remove members
- Deleting a project cascades deletion across tasks, comments, and notifications
- Kanban board with columns: Todo, In Progress, and Done
- Drag-and-drop task reordering and column transfers powered by
@dnd-kit - Fractional ordering for constant-time card repositioning with automatic column renumbering
- Task metadata: priority (low, medium, high), colored label tags, due dates, and assignee assignment
- Task detail modal with threaded discussions and comment submission
- Filtering tasks by assignee, priority, and text search query
- WebSocket synchronization powered by Ably
- Live board updates on card creation, moves, edits, and deletions
- Real-time online member presence indicators across active boards
- Event publishing from server routes following database writes
- In-app alerts for task assignment, comments, and project invitations
- Notification bell badge with unread count indicator
- Mark individual notifications as read or mark all as read
- Real-time notification delivery via dedicated user channels
- Project statistics view with key delivery indicators: total tasks, completion percentage, overdue count, and active members
- Tasks by status distribution chart
- Overdue tasks breakdown by priority
- 7-day completion activity chart
- Workload distribution by team member
- Hand-written SVG charts without external charting libraries
- Hand-designed layout using IBM Plex Sans typography and neutral palette
- Accessible semantic landmarks and WCAG AA contrast ratios
- Fully responsive layout supporting viewports down to 360px width
- Theme switch supporting System, Light, and Dark modes (choice stored in a cookie, no flash on load)
- Skip-to-content keyboard navigation link
- Read-only admin area displaying users, projects, and platform activity counts
- Accessible only to emails listed in the ADMIN_EMAILS environment variable (all other users receive a 404 response)
The screenshots show the seeded demo data populated by npm run seed.
| Landing page | Projects | Board |
|---|---|---|
![]() |
![]() |
![]() |
| Task details | Dashboard | Notifications |
![]() |
![]() |
![]() |
| Members | Filters | Admin overview |
![]() |
![]() |
![]() |
| Dark board | Dark dashboard | Phone |
![]() |
![]() |
![]() |
- Framework: Next.js 16.4.0 (App Router)
- Library: React 19.3.0
- Styling: Tailwind CSS 4 built through PostCSS (
@tailwindcss/postcss) with custom CSS variables - Typography: Self-hosted IBM Plex Sans fonts via
next/font/local(WOFF2 font files insrc/fonts/) - Drag and Drop:
@dnd-kit/coreand@dnd-kit/sortable - Icons: Font Awesome (
@fortawesome/react-fontawesome) - Charts: Custom SVG implementations rendered directly in React components without a third-party charting library
- Server Framework: Next.js Route Handlers and Server Components
- Language: TypeScript 5
- Authentication: JWT verification with
joseandbcryptjs - Validation: Zod 4.6.5
- Database: MongoDB with Mongoose 9.11.0 ODM
- Realtime Service: Ably 2.29.0 for WebSocket pub/sub and presence
- Bundler: Webpack (invoked via
next dev --webpackandnext build --webpack) - Linter: ESLint 9 (
eslint-config-next) - Type Checker: TypeScript Compiler (
tsc) - Script Runner:
tsxfor running seed scripts with Node--env-file
Authentication relies on JSON Web Tokens (JWT) signed with HMAC-SHA256. Upon successful login or registration, the token is written to an httpOnly, SameSite=lax cookie named token. In production, the cookie is marked secure. Every incoming request passes through src/proxy.ts, which extracts and verifies the token. If an unauthenticated user attempts to access a protected route, the proxy redirects them to /login?from=<pathname>. Logged-in users attempting to access /, /login, or /register are redirected to /projects.
All route handlers perform server-side verification before executing database queries. The system inspects project membership for every operation. If the requesting user is not a member of the project containing the target entity, the route responds with 404 Not Found rather than 403 Forbidden to prevent project enumeration. Administrative operations (renaming a project, adding members, removing members, or deleting a project) enforce the owner role check.
Cards within a column maintain a floating-point order number. Moving a card calculates the midpoint between the previous and next card orders: (orderAbove + orderBelow) / 2. Placing a card at the top assigns firstOrder - 1, and placing at the bottom assigns lastOrder + 1. If the gap between two cards becomes smaller than 1e-6, the frontend triggers a renumbering sequence that resets card orders in that column to consecutive integers (1, 2, 3...) to prevent precision underflow.
Real-time messaging uses Ably WebSockets. State changes are published from server route handlers only after the database write succeeds. Clients request short-lived (15 minutes) subscribe-only Ably token requests via /api/ably/token. Token capabilities restrict users strictly to subscribing to their accessible project channels (project:<id>) and their private notification channel (user:<userId>). Board channels include presence tracking so members can view who is viewing the board concurrently.
When a task is assigned, a comment is posted, or a user is added to a project, the server creates records in the Notification MongoDB collection and publishes a message to user:<userId> on Ably. The client NotificationsProvider listens on this channel to increment the unread count badge and append alerts in real time.
Because serverless functions run across isolated ephemeral instances without shared memory, rate limiting is backed by MongoDB via the RateLimit collection. Each check performs an atomic findOneAndUpdate with upsert: true on fixed time windows. If a window document exists and has not expired, its hit counter increments; otherwise, a new window document is created. A unique index on the key prevents race conditions, and a MongoDB TTL index on expiresAt automatically cleans up expired counters. If database access fails, the limiter fails open and logs the issue so legitimate users are not locked out.
The root layout reads the theme cookie on the server and applies the data-theme attribute to the html element when the value is explicitly light or dark. When no cookie is present (System), the data-theme attribute remains unset and CSS follows prefers-color-scheme through a media query with no JavaScript required. The client-side theme toggle updates document.cookie (path=/, max-age=31536000, SameSite=Lax for Light or Dark; max-age=0 for System) and immediately synchronizes the data-theme attribute on document.documentElement to prevent any flash on load.
The administration area provides a read-only view of users, projects, and platform activity metrics. Access is restricted using the requireAdmin helper, which executes inside the admin layout and within each admin page component. Admin access is determined strictly by verifying the user email against the comma-separated ADMIN_EMAILS environment variable; no administrator roles or flags are stored in the database. Unauthorized users and unauthenticated requests trigger a 404 Not Found response. The admin queries only retrieve user names, emails, and timestamps, and passwordHash is never selected or queried.
All incoming request payloads are parsed and validated using Zod schemas in src/lib/validators.ts. Fields outside the schema are stripped, preventing unwanted property injection. Route handlers reject malformed payloads with descriptive 400 Bad Request responses before querying database models.
projectflow/
├── public/
│ ├── screens/ # Real application screenshot assets
│ │ ├── board.png
│ │ ├── dashboard.png
│ │ ├── mobile.png
│ │ └── task.png
│ └── favicon.ico
├── scripts/
│ └── seed.ts # Database seeding and cleanup script
├── src/
│ ├── app/
│ │ ├── (admin)/ # Protected read-only admin interface
│ │ │ ├── admin/ # Overview, users, and projects views
│ │ │ └── layout.tsx
│ │ ├── (app)/ # Authenticated app shell layout and routes
│ │ │ ├── layout.tsx
│ │ │ └── projects/ # Projects list and board pages
│ │ ├── (auth)/ # Authentication layout and login/register routes
│ │ │ ├── login/
│ │ │ └── register/
│ │ ├── api/ # API route handlers
│ │ │ ├── ably/token/ # Ephemeral Ably token generation
│ │ │ ├── auth/ # Login, register, logout, and me endpoints
│ │ │ ├── notifications/ # Notification retrieval and mark-read
│ │ │ ├── projects/ # Project CRUD and member management
│ │ │ └── tasks/ # Task update, delete, and comment endpoints
│ │ ├── globals.css # Tailwind tokens, CSS variables, and theme queries
│ │ ├── layout.tsx # Root HTML layout with self-hosted font and cookie theme
│ │ └── page.tsx # Public landing page
│ ├── components/
│ │ ├── dashboard/ # Custom SVG charts and metrics cards
│ │ ├── landing/ # Public landing page components
│ │ ├── ui/ # Reusable UI primitives (Button, Dialog, etc.)
│ │ ├── AppHeader.tsx # Application top navigation
│ │ ├── Avatar.tsx # User avatar initials component
│ │ ├── BoardColumn.tsx # Kanban board column container
│ │ ├── FilterBar.tsx # Search and filter toolbar
│ │ ├── MembersDialog.tsx # Project member management dialog
│ │ ├── NotificationBell.tsx# Notification drop-down panel
│ │ ├── NotificationsProvider.tsx # In-app notification context
│ │ ├── ProjectBoardClient.tsx # Kanban drag-and-drop state manager
│ │ ├── ProjectHeader.tsx # Project title bar and active presence
│ │ ├── ProjectModals.tsx # Modals for new tasks and project settings
│ │ ├── RealtimeProvider.tsx# Ably client connection manager
│ │ ├── TaskCard.tsx # Draggable task card item
│ │ ├── TaskDialog.tsx # Task details view and comment thread
│ │ ├── ThemeToggle.tsx # System, Light, and Dark mode theme toggle
│ │ └── UserContext.tsx # Current user React context
│ ├── fonts/ # Self-hosted IBM Plex Sans WOFF2 font files
│ │ ├── ibm-plex-sans-latin-400-normal.woff2
│ │ ├── ibm-plex-sans-latin-500-normal.woff2
│ │ ├── ibm-plex-sans-latin-600-normal.woff2
│ │ └── LICENSE
│ ├── lib/
│ │ ├── access.ts # Project membership check helpers
│ │ ├── admin.ts # Admin email verification and layout guard
│ │ ├── api.ts # Client-side fetch wrapper
│ │ ├── auth.ts # Auth cookie helpers and token generation
│ │ ├── db.ts # Cached Mongoose connection manager
│ │ ├── jwt.ts # Jose JWT signing and verification
│ │ ├── notifications.ts # Notification insertion and publishing
│ │ ├── rate-limit.ts # MongoDB-backed fixed-window rate limiter
│ │ ├── realtime.ts # Server-side Ably event publisher
│ │ ├── redirect.ts # Navigation redirect helper
│ │ └── validators.ts # Zod validation schemas
│ ├── models/
│ │ ├── Comment.ts # Mongoose Comment model
│ │ ├── Notification.ts # Mongoose Notification model
│ │ ├── Project.ts # Mongoose Project model
│ │ ├── RateLimit.ts # Mongoose RateLimit model with TTL index
│ │ ├── Task.ts # Mongoose Task model
│ │ └── User.ts # Mongoose User model
│ ├── proxy.ts # Next.js proxy middleware for auth and routing
│ └── types/
│ └── index.ts # Shared TypeScript type declarations
├── .env.example # Template for environment variables
├── eslint.config.mjs # ESLint configuration
├── next.config.ts # Next.js configuration
├── package.json # Project dependencies and scripts
├── postcss.config.mjs # PostCSS configuration with Tailwind CSS plugin
└── tsconfig.json # TypeScript configuration
- Node.js version
>=20.9.0 - MongoDB database (local MongoDB instance or MongoDB Atlas cluster)
- Ably account and API key (optional; if omitted, the application operates normally with real-time sync disabled)
git clone https://github.com/fahadmusa725/projectflow.git
cd projectflow
npm installCopy .env.example to create your local .env file:
cp .env.example .envConfigure the following variables in .env:
| Variable | Required | Description |
|---|---|---|
MONGODB_URI |
Yes | MongoDB connection string (e.g. mongodb://localhost:27017/projectflow) |
JWT_SECRET |
Yes | Secret key used to sign and verify JSON Web Tokens |
ABLY_API_KEY |
Optional | Ably API key for real-time WebSockets and presence |
ADMIN_EMAILS |
Optional | Comma-separated list of admin emails (empty means no admin) |
Generate a cryptographically secure value for JWT_SECRET:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"Start the development server:
npm run devOpen http://localhost:3000 in your browser.
Populate the database with demo users, projects, tasks, comments, and notifications:
npm run seedTo remove all demo data:
npm run seed:removeCheck TypeScript types:
npx tsc --noEmitRun linter:
npm run lintBuild production bundle:
npm run buildClick to view API route endpoints
| Method | Path | Access | Purpose |
|---|---|---|---|
POST |
/api/auth/register |
Public | Register a new user account |
POST |
/api/auth/login |
Public | Authenticate user and issue JWT cookie |
POST |
/api/auth/logout |
Authenticated | Clear authentication cookie |
GET |
/api/auth/me |
Authenticated | Retrieve current user profile |
GET |
/api/projects |
Authenticated | List all projects the user is a member of |
POST |
/api/projects |
Authenticated | Create a new project |
GET |
/api/projects/[id] |
Member | Get project details, members, and stats |
PATCH |
/api/projects/[id] |
Owner | Update project name or description |
DELETE |
/api/projects/[id] |
Owner | Delete project and associated records |
POST |
/api/projects/[id]/members |
Owner | Add a registered user as a project member |
DELETE |
/api/projects/[id]/members/[userId] |
Owner | Remove a member from the project |
GET |
/api/projects/[id]/tasks |
Member | List all tasks belonging to a project |
POST |
/api/projects/[id]/tasks |
Member | Create a new task in a project |
PATCH |
/api/tasks/[id] |
Member | Update task title, status, order, assignee, or dates |
DELETE |
/api/tasks/[id] |
Owner or Creator | Delete a task and associated comments |
GET |
/api/tasks/[id]/comments |
Member | List comments for a task |
POST |
/api/tasks/[id]/comments |
Member | Add a comment to a task |
GET |
/api/notifications |
Authenticated | List recent notifications for current user |
PATCH |
/api/notifications/[id] |
Authenticated | Mark a single notification as read |
POST |
/api/notifications/read-all |
Authenticated | Mark all notifications as read |
GET |
/api/ably/token |
Authenticated | Request ephemeral token request for Ably real-time |
- Platform: Vercel
- Environment variables: Set
MONGODB_URI,JWT_SECRET, andABLY_API_KEYin the Vercel Project Settings under Environment Variables. - Database Network Access: In MongoDB Atlas Network Access, ensure that access is permitted from Vercel deployment IP addresses (allow IP
0.0.0.0/0with database credentials configured).
- Revocation delay in real-time channel: A removed project member continues receiving real-time events until their Ably token expires (maximum 15 minutes), after which token renewal is rejected by the server.
- Fixed-window rate limiting: Requests made right before the window reset can allow a burst of traffic at window boundaries.
- No email notifications: Notifications are delivered exclusively in-app via WebSockets and database records; no external email service is integrated.
- No file attachments: Tasks and comments support text only; file upload and attachment storage are not implemented.
- Read-only administration: The admin area is strictly read-only for monitoring platform activity; administrative editing, user bans, or record modifications are not supported.
- Demo accounts are not admins: The seeded demo accounts do not have admin access unless their emails are explicitly added to
ADMIN_EMAILS.
Fahad Musa - https://github.com/fahadmusa725











