Sage100/src/data/mockData.js
2026-01-20 11:06:52 +03:00

373 lines
17 KiB
JavaScript

// Comprehensive Mock Data Expansion
export const CompanyInfo = {
name: "Sage",
email: "sage@gmail.com",
adresse: "12 Avenue de l'Innovation",
adresse2: "75011 Paris, France",
code_postal: "75008",
ville: "Paris",
siret: "123 456 789 00012",
tva_intra: "FR12345678901",
telephone: "01 23 45 67 89",
}
export const currentUser = {
id: 1,
name: 'Jean Dupont',
email: 'jean.dupont@bijou.com',
role: 'Administrateur',
department: 'Direction',
phone: '+33 6 12 34 56 78',
avatar: 'JD',
preferences: {
language: 'fr',
currency: 'EUR',
theme: 'light',
notifications: { email: true, sms: false, inApp: true }
}
};
export const mockUsers = [
{ id: 1, name: 'Jean Dupont', email: 'jean.dupont@bijou.com', role: 'Administrateur', department: 'Direction', avatar: 'JD', status: 'active' },
{ id: 2, name: 'Marie Dubois', email: 'marie.dubois@bijou.com', role: 'Commercial', department: 'Ventes', avatar: 'MD', status: 'active' },
{ id: 3, name: 'Thomas Robert', email: 'thomas.robert@bijou.com', role: 'Commercial', department: 'Ventes', avatar: 'TR', status: 'active' },
{ id: 4, name: 'Sophie Martin', email: 'sophie.martin@bijou.com', role: 'Support', department: 'SAV', avatar: 'SM', status: 'active' },
{ id: 5, name: 'Pierre Durand', email: 'pierre.durand@bijou.com', role: 'Comptabilité', department: 'Finance', avatar: 'PD', status: 'active' },
{ id: 6, name: 'Julie Moreau', email: 'julie.moreau@bijou.com', role: 'Logistique', department: 'Opérations', avatar: 'JM', status: 'active' },
];
export const mockProductFamilies = [
{ id: 1, code: 'FAM-001', name: 'Ordinateurs Portables', color: 'bg-blue-100 text-blue-800', articleCount: 12, category: 'Matériel', status: 'active' },
{ id: 2, code: 'FAM-002', name: 'Écrans & Moniteurs', color: 'bg-purple-100 text-purple-800', articleCount: 8, category: 'Matériel', status: 'active' },
{ id: 3, code: 'FAM-003', name: 'Logiciels & Licences', color: 'bg-green-100 text-green-800', articleCount: 25, category: 'Software', status: 'active' },
{ id: 4, code: 'FAM-004', name: 'Services & Maintenance', color: 'bg-orange-100 text-orange-800', articleCount: 5, category: 'Service', status: 'active' },
{ id: 5, code: 'FAM-005', name: 'Accessoires', color: 'bg-gray-100 text-gray-800', articleCount: 45, category: 'Accessoire', status: 'inactive' },
];
export const mockArticles = Array.from({ length: 50 }, (_, i) => {
const family = mockProductFamilies[i % mockProductFamilies.length];
const costPrice = Math.floor(Math.random() * 1000) + 50;
const margin = 1.3 + (Math.random() * 0.4); // 30% to 70% margin
return {
id: i + 1,
reference: `ART-${(i + 1000).toString()}`,
name: `${family.name.split(' ')[0]} Pro ${2025 + (i % 3)} - Modèle ${String.fromCharCode(65 + (i % 26))}`,
familyId: family.id,
familyName: family.name,
familyColor: family.color,
costPrice: costPrice,
sellingPrice: Math.floor(costPrice * margin),
vatRate: 20,
unit: 'Pcs',
stock: Math.floor(Math.random() * 100),
minStock: 10,
status: i % 10 === 0 ? 'inactive' : 'active',
description: "Description détaillée de l'article pour les devis et factures.",
createdAt: new Date(Date.now() - Math.floor(Math.random() * 10000000000)).toISOString().split('T')[0],
};
});
export const mockClients = Array.from({ length: 25 }, (_, i) => ({
id: i + 1,
name: [`Sophie Martin`, `Pierre Durand`, `Marie Lambert`, `Lucas Bernard`, `Emma Petit`][i % 5],
company: [`ACME Corp`, `TechStart SAS`, `Digital Plus`, `InnovCo`, `WebAgency`, `LogiTrans`, `BuildIt`, `GreenEnergy`, `FoodProc`, `HealthCare`][i % 10],
email: `contact${i}@company.com`,
phone: `+33 1 ${Math.floor(Math.random() * 90 + 10)} ${Math.floor(Math.random() * 90 + 10)} ${Math.floor(Math.random() * 90 + 10)} ${Math.floor(Math.random() * 90 + 10)}`,
status: ['active', 'inactive', 'prospect', 'vip'][Math.floor(Math.random() * 4)],
owner: mockUsers[Math.floor(Math.random() * 3)].name,
industry: ['Technology', 'Finance', 'Retail', 'Manufacturing'][Math.floor(Math.random() * 4)],
city: ['Paris', 'Lyon', 'Marseille', 'Bordeaux', 'Lille'][Math.floor(Math.random() * 5)],
address: `${Math.floor(Math.random() * 100) + 1} Rue de l'Innovation, 75001 Paris`,
createdAt: new Date(Date.now() - Math.floor(Math.random() * 10000000000)).toISOString().split('T')[0],
lastActivity: new Date(Date.now() - Math.floor(Math.random() * 1000000000)).toISOString().split('T')[0]
}));
export const mockContacts = Array.from({ length: 30 }, (_, i) => ({
id: i + 1,
firstName: ['Jean', 'Marie', 'Paul', 'Sophie', 'Luc'][i % 5],
lastName: ['Dupont', 'Durand', 'Martin', 'Bernard', 'Petit'][i % 5],
email: `contact${i}@example.com`,
phone: `+33 6 ${Math.floor(Math.random() * 90 + 10)} ${Math.floor(Math.random() * 90 + 10)} ${Math.floor(Math.random() * 90 + 10)} ${Math.floor(Math.random() * 90 + 10)}`,
role: ['CEO', 'CTO', 'CFO', 'Manager', 'Buyer'][i % 5],
clientId: (i % mockClients.length) + 1
}));
export const mockSuppliers = Array.from({ length: 15 }, (_, i) => ({
id: i + 1,
name: [`Office Depot`, `Dell`, `Microsoft`, `Amazon AWS`, `Google`, `Lyreco`, `Metro`, `Rexel`, `Manutan`, `Orange`][i % 10],
contact: [`Jean Supply`, `Marie Vente`, `Paul Achat`][i % 3],
email: `supplier${i}@partner.com`,
phone: `+33 1 99 88 77 ${i.toString().padStart(2, '0')}`,
status: ['active', 'inactive', 'pending'][Math.floor(Math.random() * 3)],
category: ['Hardware', 'Software', 'Office', 'Services'][Math.floor(Math.random() * 4)],
paymentTerms: '30 jours fin de mois'
}));
const stages = ['new', 'qualification', 'discovery', 'proposal', 'negotiation', 'won', 'lost'];
export const mockOpportunities = Array.from({ length: 25 }, (_, i) => ({
id: i + 1,
name: `Projet ${['Digital', 'Cloud', 'Migration', 'Audit', 'Formation', 'Maintenance'][i % 6]} ${2025 + i}`,
client: mockClients[i % mockClients.length].company,
clientId: (i % mockClients.length) + 1,
contactId: (i % mockContacts.length) + 1,
amount: Math.floor(Math.random() * 50000) + 5000,
probability: Math.floor(Math.random() * 10) * 10,
stage: stages[Math.floor(Math.random() * 7)],
priority: ['low', 'medium', 'high'][Math.floor(Math.random() * 3)],
owner: mockUsers[Math.floor(Math.random() * 3)].name,
createdAt: new Date(Date.now() - Math.floor(Math.random() * 8000000000)).toISOString().split('T')[0],
closeDate: new Date(Date.now() + Math.floor(Math.random() * 8000000000)).toISOString().split('T')[0],
nextAction: 'Relance client',
description: 'Projet stratégique pour le développement de la nouvelle infrastructure cloud du client.',
quotes: []
}));
export const mockQuotes = Array.from({ length: 15 }, (_, i) => ({
id: i + 1,
number: `DEV-2025-${(i + 1).toString().padStart(3, '0')}`,
client: mockClients[i % mockClients.length].company,
clientId: (i % mockClients.length) + 1,
opportunityId: (i % mockOpportunities.length) + 1,
date: new Date(Date.now() - Math.floor(Math.random() * 5000000000)).toISOString().split('T')[0],
amountHT: Math.floor(Math.random() * 20000) + 1000,
status: ['draft', 'sent', 'accepted', 'refused', 'expired'][Math.floor(Math.random() * 5)],
owner: mockUsers[Math.floor(Math.random() * 3)].name,
items: [
{ description: "Consulting Senior", quantity: 2, unitPrice: 1200, vat: 20 },
{ description: "Licence Logiciel", quantity: 1, unitPrice: 5000, vat: 20 }
]
})).map(q => ({ ...q, vat: q.amountHT * 0.2, amountTTC: q.amountHT * 1.2 }));
export const mockOrders = mockQuotes.filter(q => q.status === 'accepted').map((q, i) => ({
id: i + 1,
number: `CMD-2025-${(i + 1).toString().padStart(3, '0')}`,
sourceQuote: q.number,
client: q.client,
clientId: q.clientId,
date: new Date(new Date(q.date).getTime() + 86400000 * 2).toISOString().split('T')[0],
deliveryDate: new Date(new Date(q.date).getTime() + 86400000 * 15).toISOString().split('T')[0],
amountHT: q.amountHT,
amountTTC: q.amountTTC,
status: ['pending', 'validated', 'shipped', 'delivered'][Math.floor(Math.random() * 4)],
items: q.items
}));
// ENRICHED INVOICES MOCK DATA
const invoiceStatuses = ['draft', 'sent', 'paid', 'partial', 'overdue', 'cancelled'];
export const mockInvoices = Array.from({ length: 25 }, (_, i) => {
const client = mockClients[i % mockClients.length];
const status = invoiceStatuses[Math.floor(Math.random() * invoiceStatuses.length)];
const amountHT = Math.floor(Math.random() * 15000) + 500;
const vat = amountHT * 0.2;
const amountTTC = amountHT + vat;
const date = new Date(Date.now() - Math.floor(Math.random() * 4000000000)).toISOString().split('T')[0];
const dueDate = new Date(new Date(date).getTime() + 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0];
let balanceDue = amountTTC;
let payments = [];
if (status === 'paid') {
balanceDue = 0;
payments.push({ id: 1, date: dueDate, amount: amountTTC, method: 'Virement', reference: 'VIR-12345' });
} else if (status === 'partial') {
const partialAmount = Math.floor(amountTTC * 0.4);
balanceDue = amountTTC - partialAmount;
payments.push({ id: 1, date: new Date(date).toISOString().split('T')[0], amount: partialAmount, method: 'Virement', reference: 'ACCOMPTE' });
}
return {
id: i + 1,
number: `FAC-2025-${(i + 100).toString().padStart(3, '0')}`,
client: client.company,
clientId: client.id,
clientAvatar: client.name.charAt(0),
sourceDocument: i % 3 === 0 ? `CMD-2025-00${(i % 5) + 1}` : null,
date: date,
dueDate: dueDate,
amountHT: amountHT,
vat: vat,
amountTTC: amountTTC,
balanceDue: balanceDue,
status: status,
items: [
{ id: 1, description: "Prestation de service informatique", quantity: 1, unitPrice: amountHT * 0.6, vat: 20, total: amountHT * 0.6 },
{ id: 2, description: "Matériel divers", quantity: 2, unitPrice: amountHT * 0.2, vat: 20, total: amountHT * 0.4 }
],
payments: payments,
timeline: [
{ date: date, title: 'Facture créée', type: 'creation', user: 'Jean Dupont' },
...(status !== 'draft' ? [{ date: date, title: 'Facture envoyée', type: 'email', user: 'Jean Dupont' }] : []),
...(status === 'paid' ? [{ date: dueDate, title: 'Paiement reçu', type: 'payment', user: 'Système' }] : [])
]
};
});
export const mockPurchaseOrders = Array.from({ length: 12 }, (_, i) => ({
id: i + 1,
number: `BC-2025-${(i + 100).toString()}`,
supplier: mockSuppliers[i % mockSuppliers.length].name,
supplierId: (i % mockSuppliers.length) + 1,
date: new Date(Date.now() - Math.floor(Math.random() * 5000000000)).toISOString().split('T')[0],
deliveryDate: new Date(Date.now() + Math.floor(Math.random() * 2000000000)).toISOString().split('T')[0],
amountHT: Math.floor(Math.random() * 10000) + 500,
status: ['draft', 'sent', 'received', 'invoiced'][Math.floor(Math.random() * 4)],
items: [
{ description: 'MacBook Pro 16"', quantity: 2, unitPrice: 2400 },
{ description: 'Dell Monitor 27"', quantity: 5, unitPrice: 350 }
]
})).map(o => ({ ...o, amountTTC: o.amountHT * 1.2 }));
export const mockReceptionNotes = mockPurchaseOrders.filter(p => p.status === 'received' || p.status === 'invoiced').map((p, i) => ({
id: i + 1,
number: `BR-2025-${(i + 100).toString()}`,
purchaseOrder: p.number,
supplier: p.supplier,
date: new Date(new Date(p.date).getTime() + 86400000 * 5).toISOString().split('T')[0],
status: 'validated',
items: p.items
}));
export const mockPurchaseInvoices = mockReceptionNotes.slice(0, 8).map((br, i) => ({
id: i + 1,
number: `FA-2025-${(i + 500).toString()}`,
supplier: br.supplier,
receptionNote: br.number,
date: new Date(new Date(br.date).getTime() + 86400000 * 2).toISOString().split('T')[0],
dueDate: new Date(new Date(br.date).getTime() + 86400000 * 32).toISOString().split('T')[0],
amountTTC: Math.floor(Math.random() * 10000) + 500,
status: ['pending', 'paid', 'overdue'][Math.floor(Math.random() * 3)]
}));
export const mockTickets = Array.from({ length: 15 }, (_, i) => ({
id: i + 1,
number: `TKT-${(1000 + i)}`,
client: mockClients[i % mockClients.length].company,
clientId: (i % mockClients.length) + 1,
subject: ['Erreur connexion', 'Demande évolution', 'Question facture', 'Bug critique', 'Formation', 'Accès perdu'][i % 6],
priority: ['low', 'normal', 'high', 'critical'][Math.floor(Math.random() * 4)],
status: ['open', 'in-progress', 'pending', 'resolved', 'closed'][Math.floor(Math.random() * 5)],
assignedTo: mockUsers[3 + Math.floor(Math.random() * 3)].name,
openDate: new Date(Date.now() - Math.floor(Math.random() * 1000000000)).toISOString().split('T')[0],
category: ['Technique', 'Commercial', 'Administratif'][Math.floor(Math.random() * 3)],
description: "Le client rencontre des difficultés pour accéder à la plateforme depuis la mise à jour de lundi.",
comments: [
{ user: 'Jean Dupont', date: '2025-12-01', text: 'J\'ai pris en compte la demande.' },
{ user: 'Sophie Martin', date: '2025-12-02', text: 'Le problème semble venir du cache navigateur.' }
]
}));
export const mockTimeline = Array.from({ length: 20 }, (_, i) => ({
id: i,
type: ['email', 'call', 'meeting', 'quote', 'task'][Math.floor(Math.random() * 5)],
title: ['Email envoyé', 'Appel client', 'Réunion projet', 'Devis créé', 'Tâche terminée'][Math.floor(Math.random() * 5)],
description: 'Détails de l\'activité simulée pour la démonstration.',
date: new Date(Date.now() - Math.floor(Math.random() * 1000000000)).toISOString().split('T')[0],
user: mockUsers[Math.floor(Math.random() * mockUsers.length)].name
}));
export const mockStats = {
revenue: [
{ name: 'Jan', value: 145000 }, { name: 'Fév', value: 168000 }, { name: 'Mar', value: 156000 },
{ name: 'Avr', value: 178000 }, { name: 'Mai', value: 192000 }, { name: 'Juin', value: 215000 },
{ name: 'Juil', value: 198000 }, { name: 'Août', value: 185000 }, { name: 'Sep', value: 225000 },
{ name: 'Oct', value: 238000 }, { name: 'Nov', value: 245000 }, { name: 'Déc', value: 260000 }
],
expenses: [
{ name: 'Jan', value: 110000 }, { name: 'Fév', value: 115000 }, { name: 'Mar', value: 112000 },
{ name: 'Avr', value: 125000 }, { name: 'Mai', value: 128000 }, { name: 'Juin', value: 135000 },
{ name: 'Juil', value: 130000 }, { name: 'Août', value: 128000 }, { name: 'Sep', value: 140000 },
{ name: 'Oct', value: 145000 }, { name: 'Nov', value: 150000 }, { name: 'Déc', value: 155000 }
],
ticketPriority: [
{ name: 'Basse', value: 25, fill: '#3b82f6' },
{ name: 'Normale', value: 45, fill: '#9ca3af' },
{ name: 'Haute', value: 20, fill: '#f97316' },
{ name: 'Critique', value: 10, fill: '#ef4444' }
],
ticketStatus: [
{ name: 'Ouvert', value: 15, fill: '#ef4444' },
{ name: 'En cours', value: 25, fill: '#f97316' },
{ name: 'Attente', value: 10, fill: '#eab308' },
{ name: 'Résolu', value: 40, fill: '#22c55e' },
{ name: 'Fermé', value: 10, fill: '#9ca3af' }
],
quoteStatus: [
{ name: 'Brouillon', value: 15, fill: '#9ca3af' },
{ name: 'Envoyé', value: 30, fill: '#3b82f6' },
{ name: 'Accepté', value: 45, fill: '#22c55e' },
{ name: 'Refusé', value: 10, fill: '#ef4444' }
],
salesByRep: [
{ name: 'Jean', value: 450000 },
{ name: 'Marie', value: 380000 },
{ name: 'Thomas', value: 320000 }
]
};
// --- KPI HELPERS ---
export const filterDataByPeriod = (data, dateField, period) => {
const now = new Date();
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
// Handle Custom Range Object
if (typeof period === 'object' && period?.id === 'custom') {
if (!period.start || !period.end) return data;
const startDate = new Date(period.start);
const endDate = new Date(period.end);
// Set end date to end of day for inclusive comparison
endDate.setHours(23, 59, 59, 999);
return data.filter(item => {
const itemDate = new Date(item[dateField]);
if (isNaN(itemDate)) return false;
return itemDate >= startDate && itemDate <= endDate;
});
}
// Handle string identifiers
return data.filter(item => {
const itemDate = new Date(item[dateField]);
if (isNaN(itemDate)) return false;
switch (period) {
case 'today':
return itemDate >= today;
case 'week':
const startOfWeek = new Date(today);
startOfWeek.setDate(today.getDate() - today.getDay()); // Sunday
return itemDate >= startOfWeek;
case 'month':
return itemDate.getMonth() === today.getMonth() && itemDate.getFullYear() === today.getFullYear();
case 'quarter':
const currentQuarter = Math.floor(today.getMonth() / 3);
const itemQuarter = Math.floor(itemDate.getMonth() / 3);
return itemQuarter === currentQuarter && itemDate.getFullYear() === today.getFullYear();
case 'year':
return itemDate.getFullYear() === today.getFullYear();
case '30days':
const thirtyDaysAgo = new Date(today);
thirtyDaysAgo.setDate(today.getDate() - 30);
return itemDate >= thirtyDaysAgo;
default: // 'all'
return true;
}
});
};
export const calculateKPIs = (data, period, config) => {
const filtered = filterDataByPeriod(data, config.dateField || 'date', period);
// Determine trend seed
const seed = typeof period === 'object' ? (period.start?.length || 0) : period.length;
return {
filteredCount: filtered.length,
totalAmount: filtered.reduce((acc, item) => acc + (item[config.amountField] || 0), 0),
trend: seed % 2 === 0 ? 'up' : 'down',
items: filtered
};
};