Prototype CRM
Prototype CRM
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://kit.fontawesome.com/3b5b23580e.js"
crossorigin="anonymous"></script>
<style>
/* Custom styles */
:root {
--primary-color: #5D5CDE;
--dark-color: #181818;
--light-color: #FFFFFF;
.bg-app-primary {
background-color: var(--dark-color);
color: var(--light-color);
.bg-app-secondary {
background-color: #222222;
color: var(--light-color);
.card-bg {
background-color: #2d2d2d;
color: var(--light-color);
.text-app-primary {
color: var(--light-color);
.border-app {
border-color: #333333;
.bg-app-primary {
background-color: var(--light-color);
color: var(--dark-color);
.bg-app-secondary {
background-color: #f8f9fa;
color: var(--dark-color);
}
.card-bg {
background-color: white;
color: var(--dark-color);
.text-app-primary {
color: var(--dark-color);
.border-app {
border-color: #e5e7eb;
.btn-primary {
background-color: var(--secondary-color);
color: white;
.btn-primary:hover {
opacity: 0.9;
.btn-secondary {
background-color: #333;
color: white;
.btn-secondary:hover {
opacity: 0.9;
}
.notification-badge {
position: absolute;
top: -5px;
right: -5px;
background-color: red;
color: white;
border-radius: 50%;
width: 20px;
height: 20px;
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
@keyframes spin {
0% { transform: rotate(0deg); }
.loading-spinner {
border-left-color: var(--secondary-color);
border-radius: 50%;
width: 24px;
height: 24px;
margin: 0 auto;
/* Table styles */
.data-table {
width: 100%;
border-collapse: collapse;
.data-table th {
background-color: var(--secondary-color);
color: white;
padding: 10px;
text-align: left;
position: sticky;
top: 0;
z-index: 10;
.data-table td {
padding: 10px;
.data-table tr:hover {
}
/* Modal styles */
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0,0,0,0.5);
.modal-content {
background-color: var(--light-color);
color: var(--dark-color);
padding: 20px;
border-radius: 8px;
width: 80%;
max-width: 700px;
position: relative;
.modal-content {
background-color: #222;
color: white;
}
.close-modal {
position: absolute;
right: 15px;
top: 10px;
font-size: 24px;
font-weight: bold;
cursor: pointer;
.client-card {
.client-card:hover {
transform: translateY(-2px);
/* Status badges */
.status-badge {
border-radius: 4px;
font-size: 12px;
font-weight: bold;
text-transform: uppercase;
}
.status-pending {
background-color: #FFC107;
color: #333;
.status-confirmed {
background-color: #4CAF50;
color: white;
.status-rejected {
background-color: #F44336;
color: white;
.call-badge {
display: inline-block;
margin-right: 5px;
width: 24px;
height: 24px;
border-radius: 50%;
text-align: center;
line-height: 24px;
font-size: 12px;
.call-pending {
background-color: #e0e0e0;
color: #333;
.call-scheduled {
background-color: #FFC107;
color: #333;
.call-completed {
background-color: #4CAF50;
color: white;
.call-missed {
background-color: #F44336;
color: white;
/* Logo styling */
.logo {
height: 40px;
/* Custom scrollbar */
::-webkit-scrollbar {
width: 8px;
::-webkit-scrollbar-track {
background: #f1f1f1;
}
::-webkit-scrollbar-thumb {
background: var(--secondary-color);
border-radius: 4px;
::-webkit-scrollbar-thumb:hover {
background: #388e3c;
</style>
</head>
<div class="text-center">
</div>
</div>
</svg>
</div>
</div>
</button>
<span>Admin</span>
</button>
</div>
</div>
</div>
</nav>
<ul class="space-y-2">
<li>
<span>Tableau de bord</span>
</a>
</li>
<li>
<span>Employé titulaire</span>
</a>
</li>
<li>
</a>
</li>
<li>
</a>
</li>
<li>
</a>
</li>
<li>
<span>Chomeur</span>
</a>
</li>
<li>
<span>Résident à l'étranger</span>
</a>
</li>
<li>
<span>Déclarations de problèmes</span>
</a>
</li>
</ul>
</div>
<div class="space-y-3">
<div>
</div>
<div>
</div>
<div>
<p class="text-sm text-gray-500 dark:text-gray-400">En
attente</p>
</div>
<div>
</div>
</div>
</div>
</div>
<div id="content-area">
<div>
<p class="text-sm text-gray-500 dark:text-gray-
400">Soumissions Quotidiennes</p>
</div>
</div>
</div>
</p>
</div>
<div>
</div>
</div>
</div>
</p>
</div>
<div>
</div>
</div>
</div>
</p>
</div>
<!-- Monthly Confirmed Card -->
<div>
</div>
</div>
</div>
</p>
</div>
</div>
</div>
</div>
<div class="h-64">
<canvas id="submissions-chart"></canvas>
</div>
</div>
<div class="h-64">
<canvas id="client-types-chart"></canvas>
</div>
</div>
</div>
</div>
</div>
<div class="h-64">
<canvas id="confirmed-chart"></canvas>
</div>
</div>
<div class="h-64">
<canvas id="client-status-chart"></canvas>
</div>
</div>
</div>
<table class="data-table">
<thead>
<tr>
<th>Date</th>
<th>Nom</th>
<th>Téléphone</th>
<th>Type</th>
<th>Statut</th>
<th>Action</th>
</tr>
</thead>
<tbody id="recent-activity">
<tr>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<div class="relative">
</div>
<option value="confirme">Confirmé</option>
<option value="rejete">Rejeté</option>
</select>
</div>
</div>
<span>En attente</span>
</div>
<div class="flex items-center">
<span>Programmé</span>
</div>
<span>Complété</span>
</div>
<span>Manqué</span>
</div>
</div>
</p>
</div>
</div>
</div>
</section>
<div class="relative">
</div>
<option value="confirme">Confirmé</option>
<option value="rejete">Rejeté</option>
</select>
</div>
</div>
</div>
</div>
</section>
<div class="relative">
</div>
<select id="filter-profession-lib-fisc" class="px-4 py-2
rounded-lg border shadow-sm focus:outline-none focus:ring-2 focus:ring-
green-500 text-base">
<option value="confirme">Confirmé</option>
<option value="rejete">Rejeté</option>
</select>
</div>
</div>
</div>
</div>
</section>
</div>
<option value="confirme">Confirmé</option>
<option value="rejete">Rejeté</option>
</select>
</div>
</div>
</div>
</div>
</section>
<!-- Chomeur Section -->
<div class="relative">
</div>
<option value="confirme">Confirmé</option>
<option value="rejete">Rejeté</option>
</select>
</div>
</div>
</div>
</div>
</section>
<div class="relative">
</div>
<option value="confirme">Confirmé</option>
<option value="rejete">Rejeté</option>
</select>
</div>
</div>
<!-- Client Cards -->
</div>
</div>
</section>
<div>
</div>
<div>
<label for="problem-description" class="block mb-1
font-medium">Description</label>
</div>
<div>
<option value="low">Basse</option>
<option value="medium"
selected>Moyenne</option>
<option value="high">Haute</option>
<option value="critical">Critique</option>
</select>
</div>
</form>
</div>
<div class="overflow-x-auto">
<table class="data-table">
<thead>
<tr>
<th>Date</th>
<th>Titre</th>
<th>Priorité</th>
<th>Statut</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="problems-list">
<tr id="no-problems-message">
</tr>
</tbody>
</table>
</div>
</div>
</section>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-content">
</div>
</div>
</div>
</div>
<!-- Call Tracking Modal -->
<div class="modal-content">
<div id="call-tracking-content">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-content">
<div class="space-y-4">
<span>En attente</span>
</label>
<span>Confirmé</span>
</label>
<span>Rejeté</span>
</label>
</div>
<div>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// ------------------------
// Global variables
// ------------------------
// ------------------------
// ------------------------
function generateMockData() {
const clientTypes = [
];
];
const client = {
id: i,
timestamp: submissionDate.toISOString(),
profession: clientType,
existingLoanType: '',
existingLoanAmount: 0,
status: status,
statusNotes: '',
calls: {
first: {
date: submissionDate,
status: weightedRandomStatus(callStatuses,
callStatusWeights),
notes: ''
},
second: {
date: new Date(submissionDate.getTime() + 2 * 24 * 60
* 60 * 1000),
status: weightedRandomStatus(callStatuses,
callStatusWeights),
notes: ''
},
third: {
status: weightedRandomStatus(callStatuses,
callStatusWeights),
notes: ''
};
if (client.hasExistingLoan) {
client.existingLoanAmount = randomLine[28] ?
randomLine[28] : Math.floor(Math.random() * 30000) + 1000;
if (randomLine.length > 0) {
}
csvRows.push(client);
return csvRows;
function weightedRandomIndex(weights) {
random -= weights[i];
if (random < 0) {
return i;
return weights.length - 1;
return statuses[index];
function parseCsvData() {
// ------------------------
// Utility functions
// ------------------------
function formatDate(dateString) {
return date.toLocaleDateString('fr-FR', {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
function showSection(sectionId) {
section.classList.add('hidden');
});
if (sectionToShow) {
sectionToShow.classList.remove('hidden');
document.querySelectorAll('.nav-link').forEach(link => {
link.classList.remove('bg-green-500', 'text-white');
link.classList.add('hover:bg-gray-100', 'dark:hover:bg-gray-
700');
});
if (activeLink) {
activeLink.classList.add('bg-green-500', 'text-white');
activeLink.classList.remove('hover:bg-gray-100',
'dark:hover:bg-gray-700');
if (modal) {
modal.style.display = 'block';
function closeModal(modalId) {
if (modal) {
modal.style.display = 'none';
client.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
client.phone.toLowerCase().includes(searchTerm.toLowerCase());
// Status filtering
});
function generateClientCard(client) {
switch(client.status) {
case 'en-attente':
statusBadgeClass = 'status-pending';
break;
case 'confirme':
statusBadgeClass = 'status-confirmed';
statusLabel = 'Confirmé';
break;
case 'rejete':
statusBadgeClass = 'status-rejected';
statusLabel = 'Rejeté';
break;
return `
<h3 class="font-bold">${client.name}</h3>
</div>
${client.phone}
</p>
${formatDate(client.timestamp)}
</p>
${client.loanType}
</p>
</div>
${callBadges}
</div>
<div class="space-x-1">
</button>
</button>
</div>
</button>
</div>
</div>
`;
function generateCallBadges(client) {
const callStatuses = [
];
callStatuses.forEach(call => {
switch(call.status) {
case 'pending':
badgeClass = 'call-pending';
break;
case 'scheduled':
badgeClass = 'call-scheduled';
break;
case 'completed':
badgeClass = 'call-completed';
break;
case 'missed':
badgeClass = 'call-missed';
break;
});
return badges;
}
// Display client details in the modal
function displayClientInfo(clientId) {
if (!client) {
return;
let infoHtml = `
<div class="space-y-3">
</div>
<div>
<p>${client.phone}</p>
</div>
<div>
<p>${formatDate(client.timestamp)}</p>
</div>
<div>
<p class="text-sm text-gray-500">Profession</p>
<p>${client.profession}</p>
</div>
<div>
<p>${client.salary} DT</p>
</div>
<div>
<p>${client.loanType}</p>
</div>
<div>
<p>${client.loanAmount} DT</p>
</div>
</div>
</div>
<p class="font-medium">${
}</p>
</div>
${client.hasExistingLoan ? `
<div>
<p>${client.existingLoanType}</p>
</div>
<div>
<p>${client.existingLoanAmount} DT</p>
</div>
</div>` : ''}
</div>
${client.resident_etranger ? `
<div class="mt-2">
</div>` : ''}
</div>
</div>
`;
document.getElementById('client-info-content').innerHTML =
infoHtml;
// ------------------------
// Dashboard functionality
// ------------------------
// Initialize charts
function initializeCharts() {
type: 'line',
data: {
labels: ['Jan', 'Fév', 'Mar', 'Avr', 'Mai', 'Juin', 'Juil', 'Aoû', 'Sep',
'Oct', 'Nov', 'Déc'],
datasets: [{
label: 'Soumissions',
data: [12, 19, 30, 45, 56, 68, 71, 84, 90, 102, 110, 125],
borderWidth: 2,
tension: 0.3
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
precision: 0
},
plugins: {
legend: {
display: false
});
type: 'doughnut',
data: {
labels: [
'Employé titulaire',
'Chomeur',
'Résident à l\'étranger'
],
datasets: [{
backgroundColor: [
],
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'right',
labels: {
padding: 15,
usePointStyle: true,
pointStyle: 'circle'
});
type: 'bar',
data: {
labels: ['Jan', 'Fév', 'Mar', 'Avr', 'Mai', 'Juin', 'Juil', 'Aoû', 'Sep',
'Oct', 'Nov', 'Déc'],
datasets: [{
data: [5, 8, 12, 18, 23, 28, 33, 38, 42, 46, 50, 55],
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
precision: 0
},
plugins: {
legend: {
display: false
});
type: 'pie',
data: {
datasets: [{
data: [65, 25, 10],
backgroundColor: [
],
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'right',
labels: {
padding: 15,
usePointStyle: true,
pointStyle: 'circle'
});
this.classList.add('bg-green-500', 'text-white');
this.classList.remove('bg-gray-300', 'text-gray-700');
document.getElementById('weekly-chart-btn').classList.add('bg-
gray-300', 'text-gray-700');
document.getElementById('weekly-chart-
btn').classList.remove('bg-green-500', 'text-white');
submissionsChart.update();
});
document.getElementById('weekly-chart-
btn').addEventListener('click', function() {
this.classList.add('bg-green-500', 'text-white');
this.classList.remove('bg-gray-300', 'text-gray-700');
document.getElementById('monthly-chart-
btn').classList.add('bg-gray-300', 'text-gray-700');
document.getElementById('monthly-chart-
btn').classList.remove('bg-green-500', 'text-white');
});
document.getElementById('monthly-confirmed-
btn').addEventListener('click', function() {
this.classList.add('bg-green-500', 'text-white');
this.classList.remove('bg-gray-300', 'text-gray-700');
document.getElementById('daily-confirmed-
btn').classList.add('bg-gray-300', 'text-gray-700');
document.getElementById('daily-confirmed-
btn').classList.remove('bg-green-500', 'text-white');
confirmedChart.update();
});
document.getElementById('daily-confirmed-
btn').addEventListener('click', function() {
this.classList.add('bg-green-500', 'text-white');
this.classList.remove('bg-gray-300', 'text-gray-700');
document.getElementById('monthly-confirmed-
btn').classList.add('bg-gray-300', 'text-gray-700');
document.getElementById('monthly-confirmed-
btn').classList.remove('bg-green-500', 'text-white');
// Update chart data for daily view (example)
confirmedChart.update();
});
function updateDashboardMetrics() {
today.setHours(0, 0, 0, 0);
yesterday.setDate(yesterday.getDate() - 1);
submissionDate.setHours(0, 0, 0, 0);
}).length;
submissionDate.setHours(0, 0, 0, 0);
}).length;
}).length;
}).length;
submissionDate.setHours(0, 0, 0, 0);
}).length;
}).length;
}).length;
// Update UI elements
document.getElementById('daily-submissions').textContent =
todaysSubmissions;
document.getElementById('monthly-submissions').textContent =
thisMonthsSubmissions;
document.getElementById('daily-confirmed').textContent =
todaysConfirmed;
document.getElementById('monthly-confirmed').textContent =
thisMonthsConfirmed;
updatePercentageChange('daily-submissions-change',
dailySubmissionsChange);
updatePercentageChange('monthly-submissions-change',
monthlySubmissionsChange);
updatePercentageChange('daily-confirmed-change',
dailyConfirmedChange);
updatePercentageChange('monthly-confirmed-change',
monthlyConfirmedChange);
document.getElementById('total-clients').textContent =
allClients.length;
document.getElementById('confirmed-clients').textContent =
allClients.filter(client => client.status === 'confirme').length;
document.getElementById('pending-clients').textContent =
allClients.filter(client => client.status === 'en-attente').length;
document.getElementById('rejected-clients').textContent =
allClients.filter(client => client.status === 'rejete').length;
updateRecentActivity();
if (changePercentage > 0) {
element.textContent = `+${formattedPercentage}%`;
element.classList.remove('text-red-500');
element.classList.add('text-green-500');
} else {
element.textContent = `${formattedPercentage}%`;
element.classList.remove('text-green-500');
element.classList.add('text-red-500');
if (recentClients.length === 0) {
} else {
recentClients.forEach(client => {
switch(client.status) {
case 'en-attente':
break;
case 'confirme':
break;
case 'rejete':
break;
}
recentActivityHtml += `
<tr>
<td>${formatDate(client.timestamp)}</td>
<td>${client.name}</td>
<td>${client.phone}</td>
<td>${getProfessionType(client.profession)}</td>
<td>${statusBadge}</td>
<td>
</button>
</td>
</tr>
`;
});
document.getElementById('recent-activity').innerHTML =
recentActivityHtml;
document.querySelectorAll('#recent-activity .client-info-
btn').forEach(button => {
button.addEventListener('click', function() {
currentClientId = clientId;
displayClientInfo(clientId);
openModal('client-info-modal');
});
});
function getProfessionType(profession) {
if (profession.includes('Employé titulaire')) {
} else if (profession.includes('Chomeur')) {
return 'Chomeur';
} else {
return profession;
// ------------------------
// ------------------------
// Load clients for a specific category
switch(category) {
case 'employe-titulaire':
break;
case 'employe-non-titulaire':
break;
case 'profession-lib-fisc':
break;
case 'profession-lib-non-fisc':
break;
case 'chomeur':
break;
case 'resident-etranger':
break;
}
renderClientList(filteredClients, elementId);
if (!container) {
return;
if (clients.length === 0) {
container.innerHTML = `
</div>
`;
return;
clients.forEach(client => {
clientCardsHtml += generateClientCard(client);
});
container.innerHTML = clientCardsHtml;
container.querySelectorAll('.client-info-btn').forEach(button => {
button.addEventListener('click', function() {
currentClientId = clientId;
displayClientInfo(clientId);
openModal('client-info-modal');
});
});
container.querySelectorAll('.track-calls-btn').forEach(button => {
button.addEventListener('click', function() {
currentClientId = clientId;
openCallTrackingModal(clientId);
});
});
container.querySelectorAll('.change-status-btn').forEach(button =>
{
button.addEventListener('click', function() {
currentClientId = clientId;
openStatusChangeModal(clientId);
});
});
function setupSearchAndFilter(category) {
if (!searchInput || !filterSelect) {
return;
switch(category) {
case 'employe-titulaire':
break;
case 'employe-non-titulaire':
break;
case 'profession-lib-fisc':
break;
case 'profession-lib-non-fisc':
break;
case 'chomeur':
break;
case 'resident-etranger':
break;
renderClientList(filteredClients, `${category}-list`);
};
searchInput.addEventListener('input', applyFilters);
filterSelect.addEventListener('change', applyFilters);
// ------------------------
// ------------------------
function openCallTrackingModal(clientId) {
if (!client) {
return;
document.getElementById('call-1-date').textContent =
formatDate(client.calls.first.date);
document.getElementById('call-2-date').textContent =
formatDate(client.calls.second.date);
document.getElementById('call-3-date').textContent =
formatDate(client.calls.third.date);
updateCallStatusUI(client);
document.getElementById('call-notes').value = '';
// Open the modal
openModal('call-tracking-modal');
function updateCallStatusUI(client) {
const calls = [
];
calls.forEach(call => {
call.element.classList.remove('border-green-500', 'border-yellow-
500', 'border-red-500', 'border-gray-500');
switch(call.status) {
case 'pending':
call.element.classList.add('border-gray-500');
call.element.querySelector('h3').textContent = `Appel $
{call.number} (En attente)`;
break;
case 'scheduled':
call.element.classList.add('border-yellow-500');
call.element.querySelector('h3').textContent = `Appel $
{call.number} (Programmé)`;
break;
case 'completed':
call.element.classList.add('border-green-500');
call.element.querySelector('h3').textContent = `Appel $
{call.number} (Complété)`;
break;
case 'missed':
call.element.classList.add('border-red-500');
call.element.querySelector('h3').textContent = `Appel $
{call.number} (Manqué)`;
break;
});
function openStatusChangeModal(clientId) {
if (!client) {
return;
document.querySelector(`input[name="client-status"][value="$
{client.status}"]`).checked = true;
document.getElementById('status-notes').value =
client.statusNotes || '';
openModal('client-status-modal');
// ------------------------
// ------------------------
// Add a problem
function addProblem(problem) {
problems.push({
id: problems.length + 1,
title: problem.title,
description: problem.description,
priority: problem.priority,
status: 'new'
});
localStorage.setItem('problems', JSON.stringify(problems));
// Add a notification
addNotification({
type: 'problem',
message: problem.description,
priority: problem.priority
});
updateProblemsList();
function updateProblemsList() {
if (problems.length === 0) {
problemsList.innerHTML = `
<tr id="no-problems-message">
</tr>
`;
return;
problems.forEach(problem => {
// Priority badge
switch(problem.priority) {
case 'low':
break;
case 'medium':
break;
case 'high':
break;
case 'critical':
break;
}
// Status badge
switch(problem.status) {
case 'new':
break;
case 'in-progress':
break;
case 'resolved':
break;
case 'closed':
break;
problemsHtml += `
<tr>
<td>${formatDate(problem.date)}</td>
<td>
<div class="font-medium">${problem.title}</div>
<div class="text-sm text-gray-500 truncate max-w-xs">$
{problem.description}</div>
</td>
<td>${priorityBadge}</td>
<td>${statusBadge}</td>
<td>
</button>
</button>
</td>
</tr>
`;
});
problemsList.innerHTML = problemsHtml;
document.querySelectorAll('.view-problem-btn').forEach(button =>
{
button.addEventListener('click', function() {
});
document.querySelectorAll('.delete-problem-btn').forEach(button
=> {
button.addEventListener('click', function() {
// Remove problem
localStorage.setItem('problems', JSON.stringify(problems));
updateProblemsList();
});
});
// ------------------------
// Notifications functionality
// ------------------------
// Add a notification
function addNotification(notification) {
notifications.push({
id: notifications.length + 1,
read: false,
...notification
});
localStorage.setItem('notifications', JSON.stringify(notifications));
updateNotificationCount();
updateNotificationsList();
function updateNotificationCount() {
if (unreadCount > 0) {
notificationBadge.textContent = unreadCount;
notificationBadge.classList.remove('hidden');
} else {
notificationBadge.classList.add('hidden');
if (notifications.length === 0) {
notificationsList.innerHTML = `
</div>
`;
return;
sortedNotifications.forEach(notification => {
if (notification.priority) {
switch(notification.priority) {
case 'low':
priorityClass = 'border-blue-500';
break;
case 'medium':
priorityClass = 'border-yellow-500';
break;
case 'high':
priorityClass = 'border-orange-500';
break;
case 'critical':
priorityClass = 'border-red-500';
break;
notificationsHtml += `
<div>
<h3 class="font-medium">${notification.title}</h3>
<p class="text-sm">${notification.message}</p>
</div>
${!notification.read ? `
</button>
` : ''}
</div>
</div>
`;
});
notificationsList.innerHTML = notificationsHtml;
document.querySelectorAll('.mark-read-btn').forEach(button => {
button.addEventListener('click', function(e) {
e.stopPropagation();
markNotificationAsRead(notificationId);
});
});
document.querySelectorAll('.notification-item').forEach(item => {
item.addEventListener('click', function() {
// Mark as read
markNotificationAsRead(notificationId);
// Handle notification action if needed
showSection('problemes');
closeModal('notifications-modal');
});
});
function markNotificationAsRead(notificationId) {
return n;
});
localStorage.setItem('notifications', JSON.stringify(notifications));
// Update UI
updateNotificationCount();
updateNotificationsList();
}
// ------------------------
// Event listeners
// ------------------------
// Navigation links
document.querySelectorAll('.nav-link').forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
showSection(targetSection);
});
});
// Notifications button
document.getElementById('notifications-
btn').addEventListener('click', function() {
openModal('notifications-modal');
updateNotificationsList();
});
document.getElementById('close-
notifications').addEventListener('click', function() {
closeModal('notifications-modal');
});
// Close client info modal
document.getElementById('close-client-
info').addEventListener('click', function() {
closeModal('client-info-modal');
});
document.getElementById('close-call-
tracking').addEventListener('click', function() {
closeModal('call-tracking-modal');
});
document.getElementById('close-client-
status').addEventListener('click', function() {
closeModal('client-status-modal');
});
document.getElementById('problem-
form').addEventListener('submit', function(e) {
e.preventDefault();
if (!title) {
return;
if (!description) {
return;
addProblem({
title,
description,
priority
});
// Reset form
document.getElementById('problem-title').value = '';
document.getElementById('problem-description').value = '';
document.getElementById('problem-priority').value = 'medium';
});
// Call status buttons
document.getElementById('complete-call-1').addEventListener('click',
updateCallStatus.bind(null, 1, 'completed'));
document.getElementById('miss-call-1').addEventListener('click',
updateCallStatus.bind(null, 1, 'missed'));
document.getElementById('complete-call-2').addEventListener('click',
updateCallStatus.bind(null, 2, 'completed'));
document.getElementById('miss-call-2').addEventListener('click',
updateCallStatus.bind(null, 2, 'missed'));
document.getElementById('complete-call-3').addEventListener('click',
updateCallStatus.bind(null, 3, 'completed'));
document.getElementById('miss-call-3').addEventListener('click',
updateCallStatus.bind(null, 3, 'missed'));
if (!currentClientId) return;
switch(callNumber) {
case 1:
client.calls.first.status = status;
break;
case 2:
client.calls.second.status = status;
break;
case 3:
client.calls.third.status = status;
break;
// Update UI
updateCallStatusUI(client);
if (notes) {
switch(callNumber) {
case 1:
client.calls.first.notes = notes;
break;
case 2:
client.calls.second.notes = notes;
break;
case 3:
client.calls.third.notes = notes;
break;
}
}
allClients[clientIndex] = client;
if (clientType) {
loadClientsByCategory(clientType, `${clientType}-list`);
document.getElementById('save-call-notes').addEventListener('click',
function() {
if (!currentClientId) return;
// For simplicity, we'll just add the notes to the most recent call
client.calls.third.notes = notes;
client.calls.second.notes = notes;
} else {
client.calls.first.notes = notes;
allClients[clientIndex] = client;
});
document.getElementById('save-status-
change').addEventListener('click', function() {
if (!currentClientId) return;
allClients[clientIndex].status = status;
allClients[clientIndex].statusNotes = notes;
// Close modal
closeModal('client-status-modal');
// Refresh metrics
updateDashboardMetrics();
if (clientType) {
loadClientsByCategory(clientType, `${clientType}-list`);
});
document.getElementById('cancel-status-
change').addEventListener('click', function() {
closeModal('client-status-modal');
});
if (client.profession.includes('Employé titulaire')) {
return 'employe-titulaire';
return 'employe-non-titulaire';
return 'profession-lib-fisc';
return 'profession-lib-non-fisc';
} else if (client.profession.includes('Chomeur')) {
return 'chomeur';
} else if (client.resident_etranger) {
return 'resident-etranger';
return null;
// ------------------------
// Initialization
// ------------------------
function init() {
setTimeout(() => {
allClients = generateMockData();
// Initialize charts
initializeCharts();
updateDashboardMetrics();
loadClientsByCategory('employe-titulaire', 'employe-titulaire-
list');
loadClientsByCategory('employe-non-titulaire', 'employe-non-
titulaire-list');
loadClientsByCategory('profession-lib-fisc', 'profession-lib-fisc-
list');
loadClientsByCategory('profession-lib-non-fisc', 'profession-lib-
non-fisc-list');
loadClientsByCategory('chomeur', 'chomeur-list');
loadClientsByCategory('resident-etranger', 'resident-etranger-
list');
setupSearchAndFilter('employe-titulaire');
setupSearchAndFilter('employe-non-titulaire');
setupSearchAndFilter('profession-lib-fisc');
setupSearchAndFilter('profession-lib-non-fisc');
setupSearchAndFilter('chomeur');
setupSearchAndFilter('resident-etranger');
updateProblemsList();
updateNotificationCount();
showSection('dashboard');
document.getElementById('loading-screen').style.display =
'none';
}, 2000);
init();
window.addEventListener('click', function(event) {
modal.style.display = 'none';
});
});
});
</script>
</body>
</html>