✨ 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
350 lines
9.6 KiB
TypeScript
350 lines
9.6 KiB
TypeScript
import { PrismaClient } from '@prisma/client';
|
|
import bcrypt from 'bcryptjs';
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
async function main() {
|
|
console.log('🌱 Starting database seeding...');
|
|
|
|
// Create Departments
|
|
const salesDept = await prisma.department.create({
|
|
data: {
|
|
name: 'Sales Department',
|
|
nameAr: 'قسم المبيعات',
|
|
code: 'SALES',
|
|
description: 'Sales and Business Development',
|
|
},
|
|
});
|
|
|
|
const itDept = await prisma.department.create({
|
|
data: {
|
|
name: 'IT Department',
|
|
nameAr: 'قسم تقنية المعلومات',
|
|
code: 'IT',
|
|
description: 'Information Technology',
|
|
},
|
|
});
|
|
|
|
const hrDept = await prisma.department.create({
|
|
data: {
|
|
name: 'HR Department',
|
|
nameAr: 'قسم الموارد البشرية',
|
|
code: 'HR',
|
|
description: 'Human Resources',
|
|
},
|
|
});
|
|
|
|
console.log('✅ Created departments');
|
|
|
|
// Create Positions
|
|
const gmPosition = await prisma.position.create({
|
|
data: {
|
|
title: 'General Manager',
|
|
titleAr: 'المدير العام',
|
|
code: 'GM',
|
|
departmentId: salesDept.id,
|
|
level: 1,
|
|
description: 'Chief Executive - Full Access',
|
|
},
|
|
});
|
|
|
|
const salesManagerPosition = await prisma.position.create({
|
|
data: {
|
|
title: 'Sales Manager',
|
|
titleAr: 'مدير المبيعات',
|
|
code: 'SALES_MGR',
|
|
departmentId: salesDept.id,
|
|
level: 2,
|
|
description: 'Sales Department Manager',
|
|
},
|
|
});
|
|
|
|
const salesRepPosition = await prisma.position.create({
|
|
data: {
|
|
title: 'Sales Representative',
|
|
titleAr: 'مندوب مبيعات',
|
|
code: 'SALES_REP',
|
|
departmentId: salesDept.id,
|
|
level: 3,
|
|
description: 'Sales Representative',
|
|
},
|
|
});
|
|
|
|
console.log('✅ Created positions');
|
|
|
|
// Create Permissions for GM (Full Access)
|
|
const modules = ['contacts', 'crm', 'inventory', 'projects', 'hr', 'marketing'];
|
|
const resources = ['*'];
|
|
const actions = ['*'];
|
|
|
|
for (const module of modules) {
|
|
await prisma.positionPermission.create({
|
|
data: {
|
|
positionId: gmPosition.id,
|
|
module,
|
|
resource: resources[0],
|
|
actions,
|
|
},
|
|
});
|
|
}
|
|
|
|
// Create Permissions for Sales Manager
|
|
await prisma.positionPermission.createMany({
|
|
data: [
|
|
{
|
|
positionId: salesManagerPosition.id,
|
|
module: 'contacts',
|
|
resource: 'contacts',
|
|
actions: ['create', 'read', 'update', 'merge'],
|
|
},
|
|
{
|
|
positionId: salesManagerPosition.id,
|
|
module: 'crm',
|
|
resource: 'deals',
|
|
actions: ['create', 'read', 'update', 'approve'],
|
|
},
|
|
{
|
|
positionId: salesManagerPosition.id,
|
|
module: 'crm',
|
|
resource: 'quotes',
|
|
actions: ['create', 'read', 'update', 'approve'],
|
|
},
|
|
],
|
|
});
|
|
|
|
// Create Permissions for Sales Rep
|
|
await prisma.positionPermission.createMany({
|
|
data: [
|
|
{
|
|
positionId: salesRepPosition.id,
|
|
module: 'contacts',
|
|
resource: 'contacts',
|
|
actions: ['create', 'read', 'update'],
|
|
},
|
|
{
|
|
positionId: salesRepPosition.id,
|
|
module: 'crm',
|
|
resource: 'deals',
|
|
actions: ['create', 'read', 'update'],
|
|
},
|
|
{
|
|
positionId: salesRepPosition.id,
|
|
module: 'crm',
|
|
resource: 'quotes',
|
|
actions: ['create', 'read'],
|
|
},
|
|
],
|
|
});
|
|
|
|
console.log('✅ Created permissions');
|
|
|
|
// Create Employees
|
|
const gmEmployee = await prisma.employee.create({
|
|
data: {
|
|
uniqueEmployeeId: 'EMP-2024-0001',
|
|
firstName: 'Ahmed',
|
|
lastName: 'Al-Mutairi',
|
|
firstNameAr: 'أحمد',
|
|
lastNameAr: 'المطيري',
|
|
email: 'gm@atmata.com',
|
|
mobile: '+966500000001',
|
|
dateOfBirth: new Date('1980-01-01'),
|
|
gender: 'MALE',
|
|
nationality: 'Saudi',
|
|
employmentType: 'Full-time',
|
|
contractType: 'Unlimited',
|
|
hireDate: new Date('2020-01-01'),
|
|
departmentId: salesDept.id,
|
|
positionId: gmPosition.id,
|
|
basicSalary: 50000,
|
|
status: 'ACTIVE',
|
|
},
|
|
});
|
|
|
|
const salesManagerEmployee = await prisma.employee.create({
|
|
data: {
|
|
uniqueEmployeeId: 'EMP-2024-0002',
|
|
firstName: 'Fatima',
|
|
lastName: 'Al-Zahrani',
|
|
firstNameAr: 'فاطمة',
|
|
lastNameAr: 'الزهراني',
|
|
email: 'sales.manager@atmata.com',
|
|
mobile: '+966500000002',
|
|
dateOfBirth: new Date('1985-05-15'),
|
|
gender: 'FEMALE',
|
|
nationality: 'Saudi',
|
|
employmentType: 'Full-time',
|
|
contractType: 'Unlimited',
|
|
hireDate: new Date('2021-06-01'),
|
|
departmentId: salesDept.id,
|
|
positionId: salesManagerPosition.id,
|
|
reportingToId: gmEmployee.id,
|
|
basicSalary: 25000,
|
|
status: 'ACTIVE',
|
|
},
|
|
});
|
|
|
|
const salesRepEmployee = await prisma.employee.create({
|
|
data: {
|
|
uniqueEmployeeId: 'EMP-2024-0003',
|
|
firstName: 'Mohammed',
|
|
lastName: 'Al-Qahtani',
|
|
firstNameAr: 'محمد',
|
|
lastNameAr: 'القحطاني',
|
|
email: 'sales.rep@atmata.com',
|
|
mobile: '+966500000003',
|
|
dateOfBirth: new Date('1992-08-20'),
|
|
gender: 'MALE',
|
|
nationality: 'Saudi',
|
|
employmentType: 'Full-time',
|
|
contractType: 'Fixed',
|
|
hireDate: new Date('2023-01-15'),
|
|
departmentId: salesDept.id,
|
|
positionId: salesRepPosition.id,
|
|
reportingToId: salesManagerEmployee.id,
|
|
basicSalary: 12000,
|
|
status: 'ACTIVE',
|
|
},
|
|
});
|
|
|
|
console.log('✅ Created employees');
|
|
|
|
// Create Users
|
|
const hashedPassword = await bcrypt.hash('Admin@123', 10);
|
|
|
|
const gmUser = await prisma.user.create({
|
|
data: {
|
|
email: 'gm@atmata.com',
|
|
username: 'admin',
|
|
password: hashedPassword,
|
|
employeeId: gmEmployee.id,
|
|
isActive: true,
|
|
},
|
|
});
|
|
|
|
const salesManagerUser = await prisma.user.create({
|
|
data: {
|
|
email: 'sales.manager@atmata.com',
|
|
username: 'salesmanager',
|
|
password: hashedPassword,
|
|
employeeId: salesManagerEmployee.id,
|
|
isActive: true,
|
|
},
|
|
});
|
|
|
|
const salesRepUser = await prisma.user.create({
|
|
data: {
|
|
email: 'sales.rep@atmata.com',
|
|
username: 'salesrep',
|
|
password: hashedPassword,
|
|
employeeId: salesRepEmployee.id,
|
|
isActive: true,
|
|
},
|
|
});
|
|
|
|
console.log('✅ Created users');
|
|
|
|
// Create Contact Categories
|
|
await prisma.contactCategory.createMany({
|
|
data: [
|
|
{ name: 'Customer', nameAr: 'عميل', description: 'Paying customers' },
|
|
{ name: 'Supplier', nameAr: 'مورد', description: 'Product/Service suppliers' },
|
|
{ name: 'Partner', nameAr: 'شريك', description: 'Business partners' },
|
|
{ name: 'Lead', nameAr: 'عميل محتمل', description: 'Potential customers' },
|
|
],
|
|
});
|
|
|
|
console.log('✅ Created contact categories');
|
|
|
|
// Create Product Categories
|
|
await prisma.productCategory.createMany({
|
|
data: [
|
|
{ name: 'Electronics', nameAr: 'إلكترونيات', code: 'ELEC' },
|
|
{ name: 'Software', nameAr: 'برمجيات', code: 'SOFT' },
|
|
{ name: 'Services', nameAr: 'خدمات', code: 'SERV' },
|
|
],
|
|
});
|
|
|
|
console.log('✅ Created product categories');
|
|
|
|
// Create Pipelines
|
|
await prisma.pipeline.create({
|
|
data: {
|
|
name: 'B2B Sales Pipeline',
|
|
nameAr: 'مسار مبيعات الشركات',
|
|
structure: 'B2B',
|
|
stages: [
|
|
{ name: 'OPEN', nameAr: 'مفتوحة', order: 1 },
|
|
{ name: 'QUALIFIED', nameAr: 'مؤهلة', order: 2 },
|
|
{ name: 'NEGOTIATION', nameAr: 'تفاوض', order: 3 },
|
|
{ name: 'PROPOSAL', nameAr: 'عرض سعر', order: 4 },
|
|
{ name: 'WON', nameAr: 'فازت', order: 5 },
|
|
{ name: 'LOST', nameAr: 'خسرت', order: 6 },
|
|
],
|
|
isActive: true,
|
|
},
|
|
});
|
|
|
|
await prisma.pipeline.create({
|
|
data: {
|
|
name: 'B2C Sales Pipeline',
|
|
nameAr: 'مسار مبيعات الأفراد',
|
|
structure: 'B2C',
|
|
stages: [
|
|
{ name: 'LEAD', nameAr: 'عميل محتمل', order: 1 },
|
|
{ name: 'CONTACTED', nameAr: 'تم التواصل', order: 2 },
|
|
{ name: 'QUALIFIED', nameAr: 'مؤهل', order: 3 },
|
|
{ name: 'WON', nameAr: 'بيع', order: 4 },
|
|
{ name: 'LOST', nameAr: 'خسارة', order: 5 },
|
|
],
|
|
isActive: true,
|
|
},
|
|
});
|
|
|
|
console.log('✅ Created pipelines');
|
|
|
|
// Create sample warehouse
|
|
await prisma.warehouse.create({
|
|
data: {
|
|
code: 'WH-MAIN',
|
|
name: 'Main Warehouse',
|
|
nameAr: 'المستودع الرئيسي',
|
|
type: 'MAIN',
|
|
city: 'Riyadh',
|
|
country: 'Saudi Arabia',
|
|
isActive: true,
|
|
},
|
|
});
|
|
|
|
console.log('✅ Created warehouse');
|
|
|
|
console.log('\n🎉 Database seeding completed successfully!\n');
|
|
console.log('📋 Default Users Created:');
|
|
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
console.log('1. General Manager');
|
|
console.log(' Email: gm@atmata.com');
|
|
console.log(' Password: Admin@123');
|
|
console.log(' Access: Full System Access');
|
|
console.log('');
|
|
console.log('2. Sales Manager');
|
|
console.log(' Email: sales.manager@atmata.com');
|
|
console.log(' Password: Admin@123');
|
|
console.log(' Access: Contacts, CRM with approvals');
|
|
console.log('');
|
|
console.log('3. Sales Representative');
|
|
console.log(' Email: sales.rep@atmata.com');
|
|
console.log(' Password: Admin@123');
|
|
console.log(' Access: Basic Contacts and CRM');
|
|
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
|
}
|
|
|
|
main()
|
|
.catch((e) => {
|
|
console.error('❌ Error seeding database:', e);
|
|
process.exit(1);
|
|
})
|
|
.finally(async () => {
|
|
await prisma.$disconnect();
|
|
});
|
|
|