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:
141
backend/src/modules/projects/projects.routes.ts
Normal file
141
backend/src/modules/projects/projects.routes.ts
Normal 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;
|
||||
|
||||
Reference in New Issue
Block a user