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