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,147 @@
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);
// Campaigns
router.get('/campaigns', authorize('marketing', 'campaigns', 'read'), async (req, res, next) => {
try {
const where: any = {};
if (req.query.type) where.type = req.query.type;
if (req.query.status) where.status = req.query.status;
if (req.query.ownerId) where.ownerId = req.query.ownerId;
const campaigns = await prisma.campaign.findMany({
where,
include: {
owner: { select: { email: true, username: true } },
},
orderBy: { createdAt: 'desc' },
});
res.json(ResponseFormatter.success(campaigns));
} catch (error) {
next(error);
}
});
router.get('/campaigns/:id', authorize('marketing', 'campaigns', 'read'), async (req, res, next) => {
try {
const campaign = await prisma.campaign.findUnique({
where: { id: req.params.id },
include: {
owner: { select: { email: true, username: true, employee: true } },
activities: { orderBy: { createdAt: 'desc' } },
},
});
res.json(ResponseFormatter.success(campaign));
} catch (error) {
next(error);
}
});
router.post('/campaigns', authorize('marketing', 'campaigns', 'create'), async (req, res, next) => {
try {
const campaignNumber = `CAMP-${new Date().getFullYear()}-${Date.now().toString().slice(-6)}`;
const campaign = await prisma.campaign.create({
data: {
...req.body,
campaignNumber,
ownerId: req.body.ownerId || (req as any).user.id,
},
include: { owner: true },
});
await AuditLogger.log({
entityType: 'CAMPAIGN',
entityId: campaign.id,
action: 'CREATE',
userId: (req as any).user.id,
});
res.status(201).json(ResponseFormatter.success(campaign, 'تم إنشاء الحملة بنجاح - Campaign created'));
} catch (error) {
next(error);
}
});
router.put('/campaigns/:id', authorize('marketing', 'campaigns', 'update'), async (req, res, next) => {
try {
const campaign = await prisma.campaign.update({
where: { id: req.params.id },
data: req.body,
});
res.json(ResponseFormatter.success(campaign, 'تم تحديث الحملة - Campaign updated'));
} catch (error) {
next(error);
}
});
router.post('/campaigns/:id/approve', authorize('marketing', 'campaigns', 'approve'), async (req, res, next) => {
try {
const campaign = await prisma.campaign.update({
where: { id: req.params.id },
data: {
status: 'APPROVED',
approvedBy: (req as any).user.id,
approvedAt: new Date(),
},
});
await AuditLogger.log({
entityType: 'CAMPAIGN',
entityId: campaign.id,
action: 'APPROVE',
userId: (req as any).user.id,
});
res.json(ResponseFormatter.success(campaign, 'تمت الموافقة على الحملة - Campaign approved'));
} catch (error) {
next(error);
}
});
router.post('/campaigns/:id/launch', authorize('marketing', 'campaigns', 'update'), async (req, res, next) => {
try {
const campaign = await prisma.campaign.update({
where: { id: req.params.id },
data: { status: 'RUNNING' },
});
res.json(ResponseFormatter.success(campaign, 'تم إطلاق الحملة - Campaign launched'));
} catch (error) {
next(error);
}
});
// Campaign Statistics
router.get('/campaigns/:id/stats', authorize('marketing', 'campaigns', 'read'), async (req, res, next) => {
try {
const campaign = await prisma.campaign.findUnique({
where: { id: req.params.id },
select: {
id: true,
name: true,
type: true,
budget: true,
actualCost: true,
sentCount: true,
openRate: true,
clickRate: true,
responseRate: true,
leadsGenerated: true,
conversions: true,
expectedROI: true,
actualROI: true,
},
});
res.json(ResponseFormatter.success(campaign));
} catch (error) {
next(error);
}
});
export default router;