feat: Complete Z.CRM system with all 6 modules

 Features:
- Complete authentication system with JWT
- Dashboard with all 6 modules visible
- Contact Management module (Salesforce-style)
- CRM & Sales Pipeline module (Pipedrive-style)
- Inventory & Assets module (SAP-style)
- Tasks & Projects module (Jira/Asana-style)
- HR Management module (BambooHR-style)
- Marketing Management module (HubSpot-style)
- Admin Panel with user management and role matrix
- World-class UI/UX with RTL Arabic support
- Cairo font (headings) + Readex Pro font (body)
- Sample data for all modules
- Protected routes and authentication flow
- Backend API with Prisma + PostgreSQL
- Comprehensive documentation

🎨 Design:
- Color-coded modules
- Professional data tables
- Stats cards with metrics
- Progress bars and status badges
- Search and filters
- Responsive layout

📊 Tech Stack:
- Frontend: Next.js 14, TypeScript, Tailwind CSS
- Backend: Node.js, Express, Prisma
- Database: PostgreSQL
- Auth: JWT with bcrypt

🚀 Production-ready frontend with all features accessible
This commit is contained in:
Talal Sharabi
2026-01-06 18:43:43 +04:00
commit 35daa52767
82 changed files with 29445 additions and 0 deletions

View File

@@ -0,0 +1,141 @@
import { Router } from 'express';
import { authenticate, authorize } from '../../shared/middleware/auth';
import prisma from '../../config/database';
import { ResponseFormatter } from '../../shared/utils/responseFormatter';
import { AuditLogger } from '../../shared/utils/auditLogger';
const router = Router();
router.use(authenticate);
// Projects
router.get('/projects', authorize('projects', 'projects', 'read'), async (req, res, next) => {
try {
const projects = await prisma.project.findMany({
include: {
phases: true,
tasks: { take: 10 },
members: { include: { user: true } },
},
orderBy: { createdAt: 'desc' },
});
res.json(ResponseFormatter.success(projects));
} catch (error) {
next(error);
}
});
router.get('/projects/:id', authorize('projects', 'projects', 'read'), async (req, res, next) => {
try {
const project = await prisma.project.findUnique({
where: { id: req.params.id },
include: {
phases: { include: { tasks: true } },
tasks: true,
members: { include: { user: { include: { employee: true } } } },
expenses: true,
attachments: true,
notes: true,
},
});
res.json(ResponseFormatter.success(project));
} catch (error) {
next(error);
}
});
router.post('/projects', authorize('projects', 'projects', 'create'), async (req, res, next) => {
try {
const projectNumber = `PRJ-${new Date().getFullYear()}-${Date.now().toString().slice(-6)}`;
const project = await prisma.project.create({
data: { ...req.body, projectNumber },
});
await AuditLogger.log({
entityType: 'PROJECT',
entityId: project.id,
action: 'CREATE',
userId: (req as any).user.id,
});
res.status(201).json(ResponseFormatter.success(project));
} catch (error) {
next(error);
}
});
router.put('/projects/:id', authorize('projects', 'projects', 'update'), async (req, res, next) => {
try {
const project = await prisma.project.update({
where: { id: req.params.id },
data: req.body,
});
res.json(ResponseFormatter.success(project));
} catch (error) {
next(error);
}
});
// Tasks
router.get('/tasks', authorize('projects', 'tasks', 'read'), async (req, res, next) => {
try {
const where: any = {};
if (req.query.projectId) where.projectId = req.query.projectId;
if (req.query.assignedToId) where.assignedToId = req.query.assignedToId;
if (req.query.status) where.status = req.query.status;
const tasks = await prisma.task.findMany({
where,
include: {
project: true,
assignedTo: { select: { email: true, username: true } },
},
orderBy: { createdAt: 'desc' },
});
res.json(ResponseFormatter.success(tasks));
} catch (error) {
next(error);
}
});
router.post('/tasks', authorize('projects', 'tasks', 'create'), async (req, res, next) => {
try {
const taskNumber = `TSK-${new Date().getFullYear()}-${Date.now().toString().slice(-6)}`;
const task = await prisma.task.create({
data: { ...req.body, taskNumber },
include: { project: true, assignedTo: true },
});
// Create notification for assigned user
if (task.assignedToId) {
await prisma.notification.create({
data: {
userId: task.assignedToId,
type: 'TASK_ASSIGNED',
title: 'مهمة جديدة - New Task Assigned',
message: `تم تعيينك لمهمة: ${task.title}`,
entityType: 'TASK',
entityId: task.id,
},
});
}
res.status(201).json(ResponseFormatter.success(task));
} catch (error) {
next(error);
}
});
router.put('/tasks/:id', authorize('projects', 'tasks', 'update'), async (req, res, next) => {
try {
const task = await prisma.task.update({
where: { id: req.params.id },
data: req.body,
});
res.json(ResponseFormatter.success(task));
} catch (error) {
next(error);
}
});
export default router;