A modern, full-stack ticket management system built with Next.js 15, TypeScript, and MongoDB. Features real-time updates, drag-and-drop Kanban boards, and comprehensive project management capabilities.
- Project Management: Create, edit, and delete projects with detailed descriptions
- Ticket System: Full CRUD operations with drag-and-drop Kanban board
- Real-time Updates: Live notifications and updates via Socket.io
- User Authentication: OTP-based login system with JWT tokens
- Super User Mode: Elevated privileges for administrative tasks
- Modern Design: Clean, responsive interface with Aceternity UI components
- Dark/Light Mode: Toggle between themes with persistent preferences
- Mobile-First: Fully responsive design optimized for all screen sizes
- Interactive Elements: Hover effects, animations, and smooth transitions
- Floating Dock: macOS-style navigation dock
- Interactive Guide: Comprehensive application guide with progress tracking
- Real-time Notifications: Instant updates via Socket.io
- Email Notifications: Rich HTML emails for offline users via SendGrid
- Notification Panel: Dedicated panel with read/unread status
- Browser Notifications: Native browser notifications support
- Hybrid System: Smart delivery based on user online/offline status
- Rich Content: Detailed notifications with context and action details
- OTP Authentication: Secure email-based login system
- JWT Tokens: Stateless authentication with token refresh
- Super User Access: Password-protected administrative mode
- Route Protection: Middleware-based route security
The application follows a layered architecture with clear separation of concerns:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Frontend (Next.js 15) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Components (UI Layer) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ
โ โ Stores (State Management) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ
โ โ API Client (HTTP/Socket) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ REST API / WebSocket
โโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Backend (Node.js + Express) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Routes (API Endpoints) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ
โ โ Middleware (Auth, Validation) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ
โ โ Controllers (Request Handlers) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ
โ โ Services (Business Logic) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ
โ โ Patterns (Strategy, Factory) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ
โ โ Models (Database Queries) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Mongoose ODM
โโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Database (MongoDB) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Location: backend/src/patterns/NotificationStrategy.ts
Purpose: Dynamically switch between different notification delivery methods
Implementation:
interface NotificationStrategy {
send(user: User, activity: Activity): Promise<void>;
}
// Email Strategy
class EmailNotificationStrategy implements NotificationStrategy {
async send(user: User, activity: Activity): Promise<void> {
await sendNotificationEmail(user.email, subject, message);
}
}
// Socket.io Strategy
class SocketNotificationStrategy implements NotificationStrategy {
async send(user: User, activity: Activity): Promise<void> {
this.io.to(`user-${user.id}`).emit('notification', data);
}
}
// Context
class NotificationService {
private strategy: NotificationStrategy;
setStrategy(strategy: NotificationStrategy): void {
this.strategy = strategy;
}
async notify(user: User, activity: Activity): Promise<void> {
await this.strategy.send(user, activity);
}
}Benefits:
- โ Easy to add new notification methods (SMS, Push, etc.)
- โ Runtime strategy switching based on user status
- โ Testable in isolation
- โ Follows Open/Closed Principle
Location: backend/src/patterns/TicketFactory.ts
Purpose: Create tickets with type-specific defaults and validation
Implementation:
class TicketFactory {
static createTicket(type: TicketType, data: Omit<CreateTicketDTO, 'type'>): CreateTicketDTO {
switch (type) {
case TicketType.BUG:
return { ...data, type: TicketType.BUG, priority: data.priority || TicketPriority.HIGH };
case TicketType.FEATURE:
return { ...data, type: TicketType.FEATURE, priority: data.priority || TicketPriority.MEDIUM };
// ... more types
}
}
static isUrgent(type: TicketType): boolean {
return type === TicketType.BUG;
}
static getAssignmentStrategy(type: TicketType): string {
// Returns recommended assignment strategy
}
}Benefits:
- โ Centralized ticket creation logic
- โ Type-specific defaults (bugs โ high priority)
- โ Consistent ticket initialization
- โ Easy to extend with new ticket types
Location: backend/src/middleware/
Purpose: Cross-cutting concerns and request processing pipeline
Implementation:
// Authentication Middleware
export const authenticate = (req: Request, res: Response, next: NextFunction): void => {
const token = extractTokenFromHeader(req.headers.authorization);
const decoded = verifyTokenSafe(token);
req.user = decoded;
next();
};
// Validation Middleware
export const validate = (schema: ZodSchema) => (req: Request, res: Response, next: NextFunction) => {
const result = schema.safeParse(req.body);
if (!result.success) return res.status(400).json({ errors: result.error });
next();
};
// Error Handler Middleware
export const errorHandler = (err: Error, req: Request, res: Response, next: NextFunction) => {
console.error(err.stack);
res.status(500).json({ success: false, message: 'Internal server error' });
};
// User Activity Tracking
export const updateLastSeen = async (req: Request, res: Response, next: NextFunction) => {
if (req.user?.userId) {
await setUserOnline(req.user.userId);
}
next();
};Benefits:
- โ Reusable request processing logic
- โ Clean separation of concerns
- โ Easy to compose and order
- โ Testable in isolation
Location: backend/src/models/queries.ts
Purpose: Abstract database operations and queries
Implementation:
// All database queries centralized in one place
export const findUserByEmail = async (email: string): Promise<User | null> => {
const doc = await UserModel.findOne({ email });
return formatUser(doc);
};
export const createProject = async (name: string, createdBy: string, description?: string): Promise<Project> => {
const doc = await ProjectModel.create({ name, description, created_by: createdBy });
const populated = await doc.populate('created_by', 'email name');
return formatProject(populated)!;
};Benefits:
- โ Database logic separated from business logic
- โ Easy to swap database implementations
- โ Consistent error handling
- โ Query optimization in one place
Location: Socket.io event system
Purpose: Real-time event broadcasting and subscription
Implementation:
// Server broadcasts events
io.to(`project-${projectId}`).emit('ticket-created', ticketData);
io.to(`user-${userId}`).emit('notification', notificationData);
// Clients subscribe to events
socket.on('ticket-updated', (data) => {
updateTicketInStore(data);
});
socket.on('notification', (data) => {
addNotificationToStore(data);
});Benefits:
- โ Decoupled event producers and consumers
- โ Real-time collaboration
- โ Multiple subscribers per event
- โ Room-based event filtering
Location: backend/src/config/database.ts
Purpose: Single Mongoose connection instance shared across the app
Implementation:
// Single mongoose connection โ reused across all imports
const MONGODB_URI = process.env.MONGODB_URL || 'mongodb://localhost:27017';
export const connectDB = async (): Promise<void> => {
await mongoose.connect(MONGODB_URI, { dbName: 'ticket-dashboard' });
};
export default mongoose; // Single instance exportedBenefits:
- โ Single connection pool across app
- โ Resource optimization
- โ Connection pooling
- โ Consistent database access
- Routes: Define API endpoints and HTTP methods
- Controllers: Handle HTTP requests/responses
- Services: Contain business logic
- Models: Database queries and data access
- Middleware: Cross-cutting concerns (auth, validation, logging)
- Services receive dependencies via constructor
- Easier testing with mock dependencies
- Loose coupling between components
- TypeScript throughout (frontend + backend)
- Shared type definitions
- Compile-time error catching
- Better IDE support and autocomplete
- Centralized error middleware
- Consistent error response format
- Validation errors with Zod
- Database error handling
- Node.js with Express.js
- TypeScript for type safety
- MongoDB with Mongoose ODM
- Socket.io for real-time communication
- SendGrid for email services
- JWT for authentication
- Zod for data validation
- Next.js 15 with App Router
- TypeScript for type safety
- Tailwind CSS 4.x for styling
- Zustand for state management
- Framer Motion for animations
- dnd-kit for drag-and-drop
- Aceternity UI components
Ticket-dashboard/
โโโ backend/ # Backend API server
โ โโโ src/
โ โ โโโ controllers/ # Route controllers
โ โ โโโ middleware/ # Express middleware
โ โ โโโ models/ # Mongoose models & queries
โ โ โโโ services/ # Business logic
โ โ โโโ types/ # TypeScript types
โ โ โโโ utils/ # Utility functions
โ โโโ package.json
โโโ frontend/ # Next.js frontend
โ โโโ app/ # App Router pages
โ โโโ components/ # React components
โ โโโ lib/ # API client & utilities
โ โโโ store/ # Zustand stores
โ โโโ types/ # TypeScript interfaces
โโโ README.md
- Node.js 18+
- MongoDB (local via MongoDB Compass or cloud via MongoDB Atlas)
- SendGrid account for email services
git clone <repository-url>
cd Ticket-dashboardcd backend
npm installCreate .env file:
# Server Configuration
PORT=5000
NODE_ENV=development
# Database (optional โ defaults to mongodb://localhost:27017)
# MONGODB_URL=mongodb://localhost:27017
# MONGODB_URL=mongodb+srv://user:password@cluster.mongodb.net โ Atlas
# JWT
JWT_SECRET=your_jwt_secret_min_32_chars
JWT_EXPIRES_IN=7d
# Email Service
SENDGRID_API_KEY=your_sendgrid_api_key
SENDGRID_FROM_EMAIL=your_verified_email
# Super User
SUPER_USER_PASSWORD=your_super_user_password
# CORS
SOCKET_CORS_ORIGIN=http://localhost:3000
CLIENT_URL=http://localhost:3000Start the backend:
npm run devcd frontend
npm installCreate .env.local file:
NEXT_PUBLIC_API_URL=http://localhost:5000Start the frontend:
npm run devLocal (MongoDB Compass)
- Install and open MongoDB Compass
- Connect to
mongodb://localhost:27017 - The
ticket-dashboarddatabase is created automatically on first run
Cloud (MongoDB Atlas)
- Create a free cluster at mongodb.com/atlas
- Copy the connection string and set
MONGODB_URLin backend.env
| Field | Type | Notes |
|---|---|---|
_id |
ObjectId | Auto-generated |
email |
String | Unique, indexed |
name |
String | Optional |
is_online |
Boolean | Default: false |
last_seen |
Date | Updated on each request |
created_at |
Date | Auto (timestamps) |
updated_at |
Date | Auto (timestamps) |
| Field | Type | Notes |
|---|---|---|
_id |
ObjectId | Auto-generated |
name |
String | Required |
description |
String | Optional |
created_by |
ObjectId | Ref: User |
created_at |
Date | Auto (timestamps) |
updated_at |
Date | Auto (timestamps) |
| Field | Type | Notes |
|---|---|---|
_id |
ObjectId | Auto-generated |
project_id |
ObjectId | Ref: Project |
title |
String | Required |
description |
String | Optional |
status |
String | TODO / IN_PROGRESS / DONE |
priority |
String | LOW / MEDIUM / HIGH / URGENT |
type |
String | BUG / FEATURE / TASK / IMPROVEMENT |
assigned_to |
ObjectId | Ref: User (optional) |
created_by |
ObjectId | Ref: User |
order_index |
Number | For drag-and-drop ordering |
| Field | Type | Notes |
|---|---|---|
_id |
ObjectId | Auto-generated |
project_id |
ObjectId | Ref: Project |
ticket_id |
ObjectId | Ref: Ticket (optional) |
user_id |
ObjectId | Ref: User |
type |
String | Action type enum |
description |
String | Human-readable log |
| Field | Type | Notes |
|---|---|---|
_id |
ObjectId | Auto-generated |
email |
String | Target email |
otp |
String | 6-digit code |
expires_at |
Date | TTL index โ auto-deleted by MongoDB |
POST /api/auth/send-otp- Send OTP to emailPOST /api/auth/verify-otp- Verify OTP and loginPOST /api/auth/verify-super-user- Verify super user passwordGET /api/auth/me- Get current user
GET /api/projects- Get all projectsGET /api/projects/:id- Get project by IDPOST /api/projects- Create projectPUT /api/projects/:id- Update projectDELETE /api/projects/:id- Delete project
GET /api/tickets/project/:id- Get project ticketsGET /api/tickets/:id- Get ticket by IDPOST /api/tickets- Create ticketPUT /api/tickets/:id- Update ticketPATCH /api/tickets/:id/move- Move ticket (drag-and-drop)DELETE /api/tickets/:id- Delete ticket
GET /api/users- Get all users
join-project- Join project roomleave-project- Leave project roomticket-updated- Broadcast ticket updateticket-moved- Broadcast ticket moveticket-created- Broadcast ticket creationticket-deleted- Broadcast ticket deletion
project-created- New project createdticket-created- New ticket createdticket-updated- Ticket updatedticket-moved- Ticket movedticket-deleted- Ticket deletednotification- Real-time notification
AppLayout- Main application layoutFloatingDock- macOS-style navigation dockNotificationPanel- Real-time notifications panel
ProjectCard- Individual project cardCreateProjectModal- Project creation modalEditProjectModal- Project editing modal
TicketCard- Individual ticket cardCreateTicketModal- Ticket creation modalEditTicketModal- Ticket editing modalKanbanBoard- Drag-and-drop board
SuperUserToggle- Super user mode toggleOTPForm- OTP verification form
- User authentication state
- JWT token management
- Super user mode
- Hydration handling
- Projects list
- Current project
- CRUD operations
- Loading states
- Tickets list
- Current ticket
- Drag-and-drop state
- Real-time updates
- Notifications list
- Unread count
- Panel visibility
- Real-time updates
- Dark/light mode
- Persistent preferences
- Theme application
- Rich HTML email templates
- Responsive design
- Branded notifications
- Offline user notifications
- OTP verification emails
- Project notifications
- Ticket notifications
- System alerts
Our notification system uses a hybrid approach that intelligently delivers notifications based on user presence:
- For Online Users: Instant delivery via WebSocket connection
- Immediate Updates: Project changes, ticket updates, assignments
- Live Collaboration: See changes as they happen
- For Offline Users: HTML emails with full context
- Offline Threshold: 2 minutes of inactivity
- Detailed Content: Includes ticket details, project info, and action context
// User is considered offline if:
// 1. is_online flag is false, OR
// 2. last_seen is older than 2 minutes
const isOffline = !user.is_online ||
(Date.now() - user.last_seen.getTime()) > 2 * 60 * 1000;
// Send via appropriate channel
if (isOffline) {
await sendEmailNotification(user, notificationData);
} else {
socket.to(user.id).emit('notification', notificationData);
}-
Open Two Browser Windows:
- Window 1: Log in as User A
- Window 2: Log in as User B (different email)
-
Test Real-time Notifications:
- Create/edit a project in Window 1
- Create/edit a ticket in Window 1
- Assign a ticket in Window 1
- Watch notifications appear instantly in Window 2
-
Test Notification Panel:
- Click the bell icon (top-left)
- Mark notifications as read/unread
- Test "Mark all as read" functionality
-
Simulate Offline User:
- Login to the application
- Close the browser/tab
- Wait 2+ minutes
- Perform actions from another account
-
Check Email Inbox:
- Look for emails from your SendGrid verified sender
- Verify HTML formatting
- Check notification content and links
-
Test Different Scenarios:
- Project creation/updates
- Ticket creation/updates/assignments
- Super user actions
- Project Created: "New project 'Project Name' created by user@email.com"
- Ticket Created: "New ticket 'Ticket Title' created in Project Name"
- Ticket Assigned: "Ticket 'Ticket Title' assigned to you by user@email.com"
- Ticket Updated: "Ticket 'Ticket Title' updated in Project Name"
- Ticket Moved: "Ticket 'Ticket Title' moved to In Progress"
- Flexible Schema: Documents map naturally to JSON API responses โ no SQLโobject mapping needed
- Embedded Populate: Mongoose
populate()handles joins efficiently without complex SQL - TTL Indexes: OTP documents are auto-deleted by MongoDB when they expire โ no cron job needed
- Easy Setup: No migration scripts โ collections and indexes are created automatically on first run
- Atlas Ready: Seamlessly scales from local Compass to MongoDB Atlas in production by changing one env variable
// Relationships via ObjectId refs + populate()
const ticket = await TicketModel.findById(id)
.populate('created_by', 'email name')
.populate('assigned_to', 'email name');
// TTL index โ MongoDB auto-deletes expired OTPs
otpSchema.index({ expires_at: 1 }, { expireAfterSeconds: 0 });
// Cascade delete handled in application layer
await TicketModel.deleteMany({ project_id: projectId });
await ActivityModel.deleteMany({ project_id: projectId });Our application includes a comprehensive Interactive Guide that helps users understand all features and functionalities:
- 7 Comprehensive Sections: Covering all aspects of the application
- Progress Tracking: Visual progress bar with completion percentage
- Interactive Navigation: Jump between sections or follow sequentially
- Completion System: Mark sections as complete for progress tracking
- Reset Functionality: Start over anytime
-
๐ Authentication & Login
- OTP-based login system
- Super user mode explanation
- JWT token management
-
๐ Project Management
- Creating and editing projects
- Project permissions and access
- Project organization
-
๐ซ Ticket Management
- Creating and updating tickets
- Drag-and-drop Kanban board
- Ticket assignment and prioritization
-
๐ Notifications System
- Real-time vs email notifications
- Notification panel usage
- User presence tracking
-
๐จ User Interface
- Dark/light mode toggle
- Responsive design features
- Navigation and shortcuts
-
โก Real-time Features
- Socket.io integration
- Live collaboration
- Instant updates
-
โ๏ธ Advanced Features
- Super user capabilities
- System settings
- Power user tips
- Access: Click the help icon (โ) in the floating dock
- Navigate: Use sidebar or Previous/Next buttons
- Complete: Mark sections as complete for progress tracking
- Reset: Clear all progress and start fresh
- Track: Monitor your progress with the visual progress bar
- Onboarding: New users learn the system quickly
- Feature Discovery: Existing users discover advanced features
- Self-Service: Reduce support requests with comprehensive documentation
- Engagement: Gamified progress tracking encourages completion
- Frontend: https://ticket-dashboard-ashen.vercel.app/ (Vercel)
- Backend: https://ticket-dashboard-7ujo.onrender.com (Render)
- Super User Password: AaravShukla@123
- Set up PostgreSQL database
- Configure environment variables
- Deploy to Render
- Set up SendGrid account
- Build the application:
npm run build - Deploy to Vercel
- Configure environment variables
- Update API URLs
# Backend tests
cd backend
npm test
# Frontend tests
cd frontend
npm test- TypeScript for type safety
- ESLint for code linting
- Prettier for code formatting
- Husky for git hooks
PORT=5000
NODE_ENV=development
# Optional โ defaults to mongodb://localhost:27017
MONGODB_URL=mongodb://localhost:27017
JWT_SECRET=your_secret_min_32_chars
JWT_EXPIRES_IN=7d
SENDGRID_API_KEY=your_key
SENDGRID_FROM_EMAIL=your_email
SUPER_USER_PASSWORD=your_password
SOCKET_CORS_ORIGIN=http://localhost:3000
CLIENT_URL=http://localhost:3000NEXT_PUBLIC_API_URL=http://localhost:5000