sam7sam commited on
Commit
46b7201
·
verified ·
1 Parent(s): 1f6a4f6

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +1487 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Test Space
3
- emoji: 💻
4
- colorFrom: green
5
- colorTo: green
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: test-space
3
+ emoji: 🐳
4
+ colorFrom: pink
5
+ colorTo: purple
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,1487 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="fr">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Samfact - Gestion Freelance Data Analyse</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
10
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
11
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
12
+ <style>
13
+ .sidebar {
14
+ transition: all 0.3s;
15
+ }
16
+ @media (max-width: 768px) {
17
+ .sidebar {
18
+ transform: translateX(-100%);
19
+ }
20
+ .sidebar.open {
21
+ transform: translateX(0);
22
+ }
23
+ }
24
+ .invoice-preview {
25
+ width: 210mm;
26
+ min-height: 297mm;
27
+ margin: auto;
28
+ padding: 20mm;
29
+ box-shadow: 0 0 10px rgba(0,0,0,0.1);
30
+ background: white;
31
+ }
32
+ </style>
33
+ </head>
34
+ <body class="bg-gray-50 font-sans">
35
+ <!-- Mobile Menu Button -->
36
+ <button id="mobileMenuButton" class="md:hidden fixed top-4 left-4 z-50 bg-blue-600 text-white p-2 rounded-lg">
37
+ <i class="fas fa-bars"></i>
38
+ </button>
39
+
40
+ <!-- Sidebar -->
41
+ <div id="sidebar" class="sidebar fixed top-0 left-0 h-full w-64 bg-blue-800 text-white p-4 shadow-lg z-40">
42
+ <div class="flex items-center mb-8">
43
+ <div class="bg-white text-blue-800 rounded-lg p-2 mr-3">
44
+ <i class="fas fa-chart-line text-2xl"></i>
45
+ </div>
46
+ <h1 class="text-2xl font-bold">Samfact</h1>
47
+ </div>
48
+
49
+ <nav>
50
+ <ul class="space-y-2">
51
+ <li>
52
+ <a href="#" id="dashboardLink" class="flex items-center p-3 rounded-lg hover:bg-blue-700 transition">
53
+ <i class="fas fa-home mr-3"></i> Tableau de bord
54
+ </a>
55
+ </li>
56
+ <li>
57
+ <a href="#" id="projectsLink" class="flex items-center p-3 rounded-lg hover:bg-blue-700 transition">
58
+ <i class="fas fa-project-diagram mr-3"></i> Projets
59
+ </a>
60
+ </li>
61
+ <li>
62
+ <a href="#" id="clientsLink" class="flex items-center p-3 rounded-lg hover:bg-blue-700 transition">
63
+ <i class="fas fa-users mr-3"></i> Clients
64
+ </a>
65
+ </li>
66
+ <li>
67
+ <a href="#" id="invoicesLink" class="flex items-center p-3 rounded-lg hover:bg-blue-700 transition">
68
+ <i class="fas fa-file-invoice-dollar mr-3"></i> Factures & Devis
69
+ </a>
70
+ </li>
71
+ <li>
72
+ <a href="#" id="settingsLink" class="flex items-center p-3 rounded-lg hover:bg-blue-700 transition">
73
+ <i class="fas fa-cog mr-3"></i> Paramètres
74
+ </a>
75
+ </li>
76
+ </ul>
77
+ </nav>
78
+
79
+ <div class="absolute bottom-0 left-0 w-full p-4">
80
+ <div class="bg-blue-700 p-3 rounded-lg">
81
+ <p class="text-sm">Auto-entrepreneur</p>
82
+ <p class="font-semibold">Data Analyse</p>
83
+ </div>
84
+ </div>
85
+ </div>
86
+
87
+ <!-- Main Content -->
88
+ <div class="ml-0 md:ml-64 transition-all duration-300">
89
+ <!-- Header -->
90
+ <header class="bg-white shadow-sm p-4 flex justify-between items-center">
91
+ <h2 id="pageTitle" class="text-xl font-semibold text-gray-800">Tableau de bord</h2>
92
+ <div class="flex items-center space-x-4">
93
+ <div class="relative">
94
+ <button class="bg-blue-600 text-white p-2 rounded-lg hover:bg-blue-700 transition">
95
+ <i class="fas fa-bell"></i>
96
+ </button>
97
+ <span class="absolute top-0 right-0 w-2 h-2 bg-red-500 rounded-full"></span>
98
+ </div>
99
+ <div class="flex items-center">
100
+ <div class="w-8 h-8 rounded-full bg-blue-600 flex items-center justify-center text-white">
101
+ <i class="fas fa-user"></i>
102
+ </div>
103
+ <span class="ml-2 font-medium hidden md:inline">Sam</span>
104
+ </div>
105
+ </div>
106
+ </header>
107
+
108
+ <!-- Dashboard Content -->
109
+ <main class="p-4">
110
+ <!-- Dashboard Section -->
111
+ <section id="dashboardSection">
112
+ <!-- Stats Cards -->
113
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
114
+ <div class="bg-white p-4 rounded-lg shadow">
115
+ <div class="flex items-center">
116
+ <div class="p-3 rounded-lg bg-blue-100 text-blue-600 mr-4">
117
+ <i class="fas fa-project-diagram"></i>
118
+ </div>
119
+ <div>
120
+ <p class="text-gray-500">Projets en cours</p>
121
+ <h3 id="activeProjectsCount" class="text-2xl font-bold">0</h3>
122
+ </div>
123
+ </div>
124
+ </div>
125
+ <div class="bg-white p-4 rounded-lg shadow">
126
+ <div class="flex items-center">
127
+ <div class="p-3 rounded-lg bg-green-100 text-green-600 mr-4">
128
+ <i class="fas fa-calendar-day"></i>
129
+ </div>
130
+ <div>
131
+ <p class="text-gray-500">Jours travaillés</p>
132
+ <h3 id="daysWorkedCount" class="text-2xl font-bold">0</h3>
133
+ </div>
134
+ </div>
135
+ </div>
136
+ <div class="bg-white p-4 rounded-lg shadow">
137
+ <div class="flex items-center">
138
+ <div class="p-3 rounded-lg bg-purple-100 text-purple-600 mr-4">
139
+ <i class="fas fa-euro-sign"></i>
140
+ </div>
141
+ <div>
142
+ <p class="text-gray-500">Revenus HT</p>
143
+ <h3 id="totalRevenue" class="text-2xl font-bold">0€</h3>
144
+ </div>
145
+ </div>
146
+ </div>
147
+ </div>
148
+
149
+ <!-- Chart -->
150
+ <div class="bg-white p-4 rounded-lg shadow mb-6">
151
+ <div class="flex justify-between items-center mb-4">
152
+ <h3 class="font-semibold text-gray-800">Revenus par mois</h3>
153
+ <select id="chartRange" class="border rounded px-3 py-1 text-sm">
154
+ <option value="3">3 mois</option>
155
+ <option value="6">6 mois</option>
156
+ <option value="12">12 mois</option>
157
+ </select>
158
+ </div>
159
+ <canvas id="revenueChart" height="300"></canvas>
160
+ </div>
161
+
162
+ <!-- Projects Table -->
163
+ <div class="bg-white rounded-lg shadow overflow-hidden">
164
+ <div class="flex justify-between items-center p-4 border-b">
165
+ <h3 class="font-semibold text-gray-800">Mes projets</h3>
166
+ <button id="addProjectBtn" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition flex items-center">
167
+ <i class="fas fa-plus mr-2"></i> Nouveau projet
168
+ </button>
169
+ </div>
170
+ <div class="overflow-x-auto">
171
+ <table class="min-w-full divide-y divide-gray-200">
172
+ <thead class="bg-gray-50">
173
+ <tr>
174
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Projet</th>
175
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Client</th>
176
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Jours</th>
177
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Forfait</th>
178
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Total HT</th>
179
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
180
+ </tr>
181
+ </thead>
182
+ <tbody id="projectsTableBody" class="bg-white divide-y divide-gray-200">
183
+ <!-- Projects will be added here dynamically -->
184
+ </tbody>
185
+ </table>
186
+ </div>
187
+ </div>
188
+ </section>
189
+
190
+ <!-- Projects Section -->
191
+ <section id="projectsSection" class="hidden">
192
+ <div class="bg-white rounded-lg shadow overflow-hidden mb-6">
193
+ <div class="flex justify-between items-center p-4 border-b">
194
+ <h3 class="font-semibold text-gray-800">Tous les projets</h3>
195
+ <button id="addProjectBtn2" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition flex items-center">
196
+ <i class="fas fa-plus mr-2"></i> Nouveau projet
197
+ </button>
198
+ </div>
199
+ <div class="overflow-x-auto">
200
+ <table class="min-w-full divide-y divide-gray-200">
201
+ <thead class="bg-gray-50">
202
+ <tr>
203
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Projet</th>
204
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Client</th>
205
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date début</th>
206
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date fin</th>
207
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Jours</th>
208
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Total HT</th>
209
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Statut</th>
210
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
211
+ </tr>
212
+ </thead>
213
+ <tbody id="allProjectsTableBody" class="bg-white divide-y divide-gray-200">
214
+ <!-- All projects will be added here dynamically -->
215
+ </tbody>
216
+ </table>
217
+ </div>
218
+ </div>
219
+ </section>
220
+
221
+ <!-- Clients Section -->
222
+ <section id="clientsSection" class="hidden">
223
+ <div class="bg-white rounded-lg shadow overflow-hidden mb-6">
224
+ <div class="flex justify-between items-center p-4 border-b">
225
+ <h3 class="font-semibold text-gray-800">Mes clients</h3>
226
+ <button id="addClientBtn" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition flex items-center">
227
+ <i class="fas fa-plus mr-2"></i> Nouveau client
228
+ </button>
229
+ </div>
230
+ <div class="overflow-x-auto">
231
+ <table class="min-w-full divide-y divide-gray-200">
232
+ <thead class="bg-gray-50">
233
+ <tr>
234
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Nom</th>
235
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Email</th>
236
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Adresse</th>
237
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">SIRET</th>
238
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
239
+ </tr>
240
+ </thead>
241
+ <tbody id="clientsTableBody" class="bg-white divide-y divide-gray-200">
242
+ <!-- Clients will be added here dynamically -->
243
+ </tbody>
244
+ </table>
245
+ </div>
246
+ </div>
247
+ </section>
248
+
249
+ <!-- Invoices Section -->
250
+ <section id="invoicesSection" class="hidden">
251
+ <div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
252
+ <button id="allInvoicesBtn" class="bg-blue-600 text-white p-4 rounded-lg shadow hover:bg-blue-700 transition">
253
+ <p class="text-sm">Toutes</p>
254
+ <h3 id="allInvoicesCount" class="text-2xl font-bold">0</h3>
255
+ </button>
256
+ <button id="draftInvoicesBtn" class="bg-gray-200 text-gray-800 p-4 rounded-lg shadow hover:bg-gray-300 transition">
257
+ <p class="text-sm">Brouillons</p>
258
+ <h3 id="draftInvoicesCount" class="text-2xl font-bold">0</h3>
259
+ </button>
260
+ <button id="sentInvoicesBtn" class="bg-yellow-100 text-yellow-800 p-4 rounded-lg shadow hover:bg-yellow-200 transition">
261
+ <p class="text-sm">Envoyées</p>
262
+ <h3 id="sentInvoicesCount" class="text-2xl font-bold">0</h3>
263
+ </button>
264
+ <button id="paidInvoicesBtn" class="bg-green-100 text-green-800 p-4 rounded-lg shadow hover:bg-green-200 transition">
265
+ <p class="text-sm">Payées</p>
266
+ <h3 id="paidInvoicesCount" class="text-2xl font-bold">0</h3>
267
+ </button>
268
+ </div>
269
+
270
+ <div class="bg-white rounded-lg shadow overflow-hidden">
271
+ <div class="flex justify-between items-center p-4 border-b">
272
+ <h3 class="font-semibold text-gray-800">Factures & Devis</h3>
273
+ <div class="flex space-x-2">
274
+ <button id="addInvoiceBtn" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition flex items-center">
275
+ <i class="fas fa-file-invoice-dollar mr-2"></i> Nouvelle facture
276
+ </button>
277
+ <button id="addQuoteBtn" class="bg-green-600 text-white px-4 py-2 rounded-lg hover:bg-green-700 transition flex items-center">
278
+ <i class="fas fa-file-signature mr-2"></i> Nouveau devis
279
+ </button>
280
+ </div>
281
+ </div>
282
+ <div class="overflow-x-auto">
283
+ <table class="min-w-full divide-y divide-gray-200">
284
+ <thead class="bg-gray-50">
285
+ <tr>
286
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Numéro</th>
287
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Client</th>
288
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date</th>
289
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Échéance</th>
290
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Total HT</th>
291
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Statut</th>
292
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Type</th>
293
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
294
+ </tr>
295
+ </thead>
296
+ <tbody id="invoicesTableBody" class="bg-white divide-y divide-gray-200">
297
+ <!-- Invoices will be added here dynamically -->
298
+ </tbody>
299
+ </table>
300
+ </div>
301
+ </div>
302
+ </section>
303
+
304
+ <!-- Settings Section -->
305
+ <section id="settingsSection" class="hidden">
306
+ <div class="bg-white rounded-lg shadow overflow-hidden p-6">
307
+ <h3 class="font-semibold text-gray-800 text-lg mb-6">Paramètres</h3>
308
+
309
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
310
+ <!-- Personal Info -->
311
+ <div class="border rounded-lg p-4">
312
+ <h4 class="font-medium text-gray-700 mb-4">Informations personnelles</h4>
313
+ <div class="space-y-4">
314
+ <div>
315
+ <label class="block text-sm text-gray-500 mb-1">Nom complet</label>
316
+ <input type="text" id="userName" class="w-full border rounded px-3 py-2">
317
+ </div>
318
+ <div>
319
+ <label class="block text-sm text-gray-500 mb-1">Email</label>
320
+ <input type="email" id="userEmail" class="w-full border rounded px-3 py-2">
321
+ </div>
322
+ <div>
323
+ <label class="block text-sm text-gray-500 mb-1">Adresse</label>
324
+ <textarea id="userAddress" class="w-full border rounded px-3 py-2"></textarea>
325
+ </div>
326
+ <div>
327
+ <label class="block text-sm text-gray-500 mb-1">SIRET</label>
328
+ <input type="text" id="userSiret" class="w-full border rounded px-3 py-2">
329
+ </div>
330
+ <button id="savePersonalInfoBtn" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition">
331
+ Enregistrer
332
+ </button>
333
+ </div>
334
+ </div>
335
+
336
+ <!-- Business Settings -->
337
+ <div class="border rounded-lg p-4">
338
+ <h4 class="font-medium text-gray-700 mb-4">Paramètres professionnels</h4>
339
+ <div class="space-y-4">
340
+ <div>
341
+ <label class="block text-sm text-gray-500 mb-1">Forfait journalier par défaut (€)</label>
342
+ <input type="number" id="defaultRate" class="w-full border rounded px-3 py-2">
343
+ </div>
344
+ <div>
345
+ <label class="block text-sm text-gray-500 mb-1">Conditions de paiement</label>
346
+ <input type="text" id="paymentTerms" class="w-full border rounded px-3 py-2" placeholder="Ex: 30 jours net">
347
+ </div>
348
+ <div>
349
+ <label class="block text-sm text-gray-500 mb-1">Logo</label>
350
+ <div class="flex items-center space-x-4">
351
+ <div class="w-16 h-16 bg-gray-200 rounded-lg flex items-center justify-center overflow-hidden">
352
+ <img id="logoPreview" src="" alt="Logo" class="hidden max-w-full max-h-full">
353
+ <i class="fas fa-camera text-gray-400"></i>
354
+ </div>
355
+ <div>
356
+ <input type="file" id="logoUpload" class="hidden" accept="image/*">
357
+ <button id="uploadLogoBtn" class="text-sm bg-gray-100 hover:bg-gray-200 px-3 py-1 rounded">
358
+ Choisir un logo
359
+ </button>
360
+ </div>
361
+ </div>
362
+ </div>
363
+ <button id="saveBusinessSettingsBtn" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition">
364
+ Enregistrer
365
+ </button>
366
+ </div>
367
+ </div>
368
+ </div>
369
+ </div>
370
+ </section>
371
+ </main>
372
+ </div>
373
+
374
+ <!-- Modals -->
375
+ <!-- Add Project Modal -->
376
+ <div id="projectModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
377
+ <div class="bg-white rounded-lg shadow-xl w-full max-w-md">
378
+ <div class="flex justify-between items-center p-4 border-b">
379
+ <h3 class="font-semibold text-lg">Nouveau projet</h3>
380
+ <button id="closeProjectModal" class="text-gray-500 hover:text-gray-700">
381
+ <i class="fas fa-times"></i>
382
+ </button>
383
+ </div>
384
+ <div class="p-4">
385
+ <form id="projectForm">
386
+ <div class="space-y-4">
387
+ <div>
388
+ <label class="block text-sm text-gray-500 mb-1">Nom du projet*</label>
389
+ <input type="text" id="projectName" class="w-full border rounded px-3 py-2" required>
390
+ </div>
391
+ <div>
392
+ <label class="block text-sm text-gray-500 mb-1">Client*</label>
393
+ <select id="projectClient" class="w-full border rounded px-3 py-2" required>
394
+ <option value="">Sélectionner un client</option>
395
+ <!-- Clients will be added here dynamically -->
396
+ </select>
397
+ </div>
398
+ <div class="grid grid-cols-2 gap-4">
399
+ <div>
400
+ <label class="block text-sm text-gray-500 mb-1">Date de début*</label>
401
+ <input type="date" id="projectStartDate" class="w-full border rounded px-3 py-2" required>
402
+ </div>
403
+ <div>
404
+ <label class="block text-sm text-gray-500 mb-1">Date de fin</label>
405
+ <input type="date" id="projectEndDate" class="w-full border rounded px-3 py-2">
406
+ </div>
407
+ </div>
408
+ <div class="grid grid-cols-2 gap-4">
409
+ <div>
410
+ <label class="block text-sm text-gray-500 mb-1">Jours travaillés*</label>
411
+ <input type="number" id="projectDays" class="w-full border rounded px-3 py-2" required>
412
+ </div>
413
+ <div>
414
+ <label class="block text-sm text-gray-500 mb-1">Forfait journalier (€)*</label>
415
+ <input type="number" id="projectRate" class="w-full border rounded px-3 py-2" required>
416
+ </div>
417
+ </div>
418
+ <div>
419
+ <label class="block text-sm text-gray-500 mb-1">Description</label>
420
+ <textarea id="projectDescription" class="w-full border rounded px-3 py-2"></textarea>
421
+ </div>
422
+ </div>
423
+ <div class="flex justify-end space-x-2 mt-6">
424
+ <button type="button" id="cancelProjectBtn" class="px-4 py-2 border rounded-lg hover:bg-gray-100">
425
+ Annuler
426
+ </button>
427
+ <button type="submit" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition">
428
+ Enregistrer
429
+ </button>
430
+ </div>
431
+ </form>
432
+ </div>
433
+ </div>
434
+ </div>
435
+
436
+ <!-- Add Client Modal -->
437
+ <div id="clientModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
438
+ <div class="bg-white rounded-lg shadow-xl w-full max-w-md">
439
+ <div class="flex justify-between items-center p-4 border-b">
440
+ <h3 class="font-semibold text-lg">Nouveau client</h3>
441
+ <button id="closeClientModal" class="text-gray-500 hover:text-gray-700">
442
+ <i class="fas fa-times"></i>
443
+ </button>
444
+ </div>
445
+ <div class="p-4">
446
+ <form id="clientForm">
447
+ <div class="space-y-4">
448
+ <div>
449
+ <label class="block text-sm text-gray-500 mb-1">Nom*</label>
450
+ <input type="text" id="clientName" class="w-full border rounded px-3 py-2" required>
451
+ </div>
452
+ <div>
453
+ <label class="block text-sm text-gray-500 mb-1">Email*</label>
454
+ <input type="email" id="clientEmail" class="w-full border rounded px-3 py-2" required>
455
+ </div>
456
+ <div>
457
+ <label class="block text-sm text-gray-500 mb-1">Adresse</label>
458
+ <textarea id="clientAddress" class="w-full border rounded px-3 py-2"></textarea>
459
+ </div>
460
+ <div>
461
+ <label class="block text-sm text-gray-500 mb-1">SIRET</label>
462
+ <input type="text" id="clientSiret" class="w-full border rounded px-3 py-2">
463
+ </div>
464
+ <div>
465
+ <label class="block text-sm text-gray-500 mb-1">Téléphone</label>
466
+ <input type="tel" id="clientPhone" class="w-full border rounded px-3 py-2">
467
+ </div>
468
+ </div>
469
+ <div class="flex justify-end space-x-2 mt-6">
470
+ <button type="button" id="cancelClientBtn" class="px-4 py-2 border rounded-lg hover:bg-gray-100">
471
+ Annuler
472
+ </button>
473
+ <button type="submit" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition">
474
+ Enregistrer
475
+ </button>
476
+ </div>
477
+ </form>
478
+ </div>
479
+ </div>
480
+ </div>
481
+
482
+ <!-- Add Invoice/Quote Modal -->
483
+ <div id="invoiceModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
484
+ <div class="bg-white rounded-lg shadow-xl w-full max-w-4xl">
485
+ <div class="flex justify-between items-center p-4 border-b">
486
+ <h3 id="invoiceModalTitle" class="font-semibold text-lg">Nouvelle facture</h3>
487
+ <button id="closeInvoiceModal" class="text-gray-500 hover:text-gray-700">
488
+ <i class="fas fa-times"></i>
489
+ </button>
490
+ </div>
491
+ <div class="p-4">
492
+ <form id="invoiceForm">
493
+ <input type="hidden" id="invoiceType">
494
+ <input type="hidden" id="invoiceId">
495
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
496
+ <div>
497
+ <div class="space-y-4">
498
+ <div>
499
+ <label class="block text-sm text-gray-500 mb-1">Client*</label>
500
+ <select id="invoiceClient" class="w-full border rounded px-3 py-2" required>
501
+ <option value="">Sélectionner un client</option>
502
+ <!-- Clients will be added here dynamically -->
503
+ </select>
504
+ </div>
505
+ <div class="grid grid-cols-2 gap-4">
506
+ <div>
507
+ <label class="block text-sm text-gray-500 mb-1">Date*</label>
508
+ <input type="date" id="invoiceDate" class="w-full border rounded px-3 py-2" required>
509
+ </div>
510
+ <div>
511
+ <label class="block text-sm text-gray-500 mb-1">Échéance</label>
512
+ <input type="date" id="invoiceDueDate" class="w-full border rounded px-3 py-2">
513
+ </div>
514
+ </div>
515
+ <div>
516
+ <label class="block text-sm text-gray-500 mb-1">Numéro</label>
517
+ <input type="text" id="invoiceNumber" class="w-full border rounded px-3 py-2 bg-gray-100" readonly>
518
+ </div>
519
+ <div>
520
+ <label class="block text-sm text-gray-500 mb-1">Statut</label>
521
+ <select id="invoiceStatus" class="w-full border rounded px-3 py-2">
522
+ <option value="draft">Brouillon</option>
523
+ <option value="sent">Envoyé</option>
524
+ <option value="paid">Payé</option>
525
+ </select>
526
+ </div>
527
+ </div>
528
+ </div>
529
+ <div>
530
+ <div class="space-y-4">
531
+ <div>
532
+ <label class="block text-sm text-gray-500 mb-1">Projet associé</label>
533
+ <select id="invoiceProject" class="w-full border rounded px-3 py-2">
534
+ <option value="">Aucun projet</option>
535
+ <!-- Projects will be added here dynamically -->
536
+ </select>
537
+ </div>
538
+ <div>
539
+ <label class="block text-sm text-gray-500 mb-1">Conditions de paiement</label>
540
+ <input type="text" id="invoicePaymentTerms" class="w-full border rounded px-3 py-2" placeholder="Ex: 30 jours net">
541
+ </div>
542
+ <div>
543
+ <label class="block text-sm text-gray-500 mb-1">Notes</label>
544
+ <textarea id="invoiceNotes" class="w-full border rounded px-3 py-2"></textarea>
545
+ </div>
546
+ </div>
547
+ </div>
548
+ </div>
549
+
550
+ <div class="mt-6">
551
+ <h4 class="font-medium text-gray-700 mb-2">Services</h4>
552
+ <div class="overflow-x-auto">
553
+ <table class="min-w-full divide-y divide-gray-200">
554
+ <thead class="bg-gray-50">
555
+ <tr>
556
+ <th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Description</th>
557
+ <th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Jours</th>
558
+ <th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Forfait</th>
559
+ <th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Total</th>
560
+ <th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"></th>
561
+ </tr>
562
+ </thead>
563
+ <tbody id="invoiceItemsTable" class="bg-white divide-y divide-gray-200">
564
+ <tr>
565
+ <td class="px-4 py-2"><input type="text" class="w-full border rounded px-2 py-1 invoice-item-desc" placeholder="Description"></td>
566
+ <td class="px-4 py-2"><input type="number" class="w-full border rounded px-2 py-1 invoice-item-days" placeholder="Jours"></td>
567
+ <td class="px-4 py-2"><input type="number" class="w-full border rounded px-2 py-1 invoice-item-rate" placeholder="Forfait"></td>
568
+ <td class="px-4 py-2"><input type="text" class="w-full border rounded px-2 py-1 invoice-item-total bg-gray-100" placeholder="0.00" readonly></td>
569
+ <td class="px-4 py-2"><button type="button" class="text-red-500 hover:text-red-700 remove-item-btn"><i class="fas fa-trash"></i></button></td>
570
+ </tr>
571
+ </tbody>
572
+ <tfoot>
573
+ <tr>
574
+ <td colspan="5" class="px-4 py-2">
575
+ <button type="button" id="addInvoiceItemBtn" class="text-blue-600 hover:text-blue-800 text-sm">
576
+ <i class="fas fa-plus mr-1"></i> Ajouter un service
577
+ </button>
578
+ </td>
579
+ </tr>
580
+ <tr>
581
+ <td colspan="3" class="px-4 py-2 text-right font-medium">Total HT</td>
582
+ <td colspan="2" class="px-4 py-2"><input type="text" id="invoiceSubtotal" class="w-full border rounded px-2 py-1 bg-gray-100" value="0.00" readonly></td>
583
+ </tr>
584
+ </tfoot>
585
+ </table>
586
+ </div>
587
+ </div>
588
+
589
+ <div class="flex justify-end space-x-2 mt-6">
590
+ <button type="button" id="cancelInvoiceBtn" class="px-4 py-2 border rounded-lg hover:bg-gray-100">
591
+ Annuler
592
+ </button>
593
+ <button type="submit" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition">
594
+ Enregistrer
595
+ </button>
596
+ </div>
597
+ </form>
598
+ </div>
599
+ </div>
600
+ </div>
601
+
602
+ <!-- Invoice Preview Modal -->
603
+ <div id="invoicePreviewModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden overflow-y-auto">
604
+ <div class="bg-white rounded-lg shadow-xl w-full max-w-4xl m-4">
605
+ <div class="flex justify-between items-center p-4 border-b">
606
+ <h3 id="invoicePreviewTitle" class="font-semibold text-lg">Aperçu</h3>
607
+ <div class="flex space-x-2">
608
+ <button id="printInvoiceBtn" class="bg-gray-200 hover:bg-gray-300 px-3 py-1 rounded flex items-center">
609
+ <i class="fas fa-print mr-2"></i> Imprimer
610
+ </button>
611
+ <button id="downloadPdfBtn" class="bg-blue-600 text-white px-3 py-1 rounded flex items-center hover:bg-blue-700">
612
+ <i class="fas fa-file-pdf mr-2"></i> PDF
613
+ </button>
614
+ <button id="closeInvoicePreview" class="text-gray-500 hover:text-gray-700">
615
+ <i class="fas fa-times"></i>
616
+ </button>
617
+ </div>
618
+ </div>
619
+ <div class="p-4 overflow-auto">
620
+ <div id="invoicePreviewContent" class="invoice-preview">
621
+ <!-- Invoice content will be generated here -->
622
+ </div>
623
+ </div>
624
+ </div>
625
+ </div>
626
+
627
+ <!-- CSV Import Modal -->
628
+ <div id="csvImportModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
629
+ <div class="bg-white rounded-lg shadow-xl w-full max-w-md">
630
+ <div class="flex justify-between items-center p-4 border-b">
631
+ <h3 class="font-semibold text-lg">Importer des données</h3>
632
+ <button id="closeCsvImportModal" class="text-gray-500 hover:text-gray-700">
633
+ <i class="fas fa-times"></i>
634
+ </button>
635
+ </div>
636
+ <div class="p-4">
637
+ <div class="mb-4">
638
+ <p class="text-sm text-gray-600">Sélectionnez un fichier CSV contenant vos projets existants.</p>
639
+ <p class="text-xs text-gray-500 mt-1">Colonnes attendues: Projet, Client, Jours travaillés, Forfait journalier</p>
640
+ </div>
641
+ <div class="border-2 border-dashed border-gray-300 rounded-lg p-6 text-center">
642
+ <input type="file" id="csvFileInput" class="hidden" accept=".csv">
643
+ <label for="csvFileInput" class="cursor-pointer">
644
+ <i class="fas fa-file-csv text-4xl text-gray-400 mb-2"></i>
645
+ <p class="text-sm text-gray-600">Glissez-déposez un fichier ou cliquez pour sélectionner</p>
646
+ <p id="csvFileName" class="text-xs text-gray-500 mt-2 hidden"></p>
647
+ </label>
648
+ </div>
649
+ <div class="flex justify-end space-x-2 mt-6">
650
+ <button type="button" id="cancelCsvImportBtn" class="px-4 py-2 border rounded-lg hover:bg-gray-100">
651
+ Annuler
652
+ </button>
653
+ <button type="button" id="confirmCsvImportBtn" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition disabled:opacity-50" disabled>
654
+ Importer
655
+ </button>
656
+ </div>
657
+ </div>
658
+ </div>
659
+ </div>
660
+
661
+ <script>
662
+ // Initialize the application when DOM is loaded
663
+ document.addEventListener('DOMContentLoaded', function() {
664
+ // Mobile menu toggle
665
+ const mobileMenuButton = document.getElementById('mobileMenuButton');
666
+ const sidebar = document.getElementById('sidebar');
667
+
668
+ mobileMenuButton.addEventListener('click', function() {
669
+ sidebar.classList.toggle('open');
670
+ });
671
+
672
+ // Navigation links
673
+ const navLinks = {
674
+ dashboardLink: document.getElementById('dashboardLink'),
675
+ projectsLink: document.getElementById('projectsLink'),
676
+ clientsLink: document.getElementById('clientsLink'),
677
+ invoicesLink: document.getElementById('invoicesLink'),
678
+ settingsLink: document.getElementById('settingsLink')
679
+ };
680
+
681
+ const sections = {
682
+ dashboardSection: document.getElementById('dashboardSection'),
683
+ projectsSection: document.getElementById('projectsSection'),
684
+ clientsSection: document.getElementById('clientsSection'),
685
+ invoicesSection: document.getElementById('invoicesSection'),
686
+ settingsSection: document.getElementById('settingsSection')
687
+ };
688
+
689
+ const pageTitle = document.getElementById('pageTitle');
690
+
691
+ // Show section and hide others
692
+ function showSection(sectionId, title) {
693
+ Object.values(sections).forEach(section => {
694
+ section.classList.add('hidden');
695
+ });
696
+ document.getElementById(sectionId).classList.remove('hidden');
697
+ pageTitle.textContent = title;
698
+
699
+ // Close sidebar on mobile after navigation
700
+ if (window.innerWidth <= 768) {
701
+ sidebar.classList.remove('open');
702
+ }
703
+ }
704
+
705
+ // Add event listeners to navigation links
706
+ navLinks.dashboardLink.addEventListener('click', function(e) {
707
+ e.preventDefault();
708
+ showSection('dashboardSection', 'Tableau de bord');
709
+ updateDashboardStats();
710
+ });
711
+
712
+ navLinks.projectsLink.addEventListener('click', function(e) {
713
+ e.preventDefault();
714
+ showSection('projectsSection', 'Mes projets');
715
+ renderAllProjectsTable();
716
+ });
717
+
718
+ navLinks.clientsLink.addEventListener('click', function(e) {
719
+ e.preventDefault();
720
+ showSection('clientsSection', 'Mes clients');
721
+ renderClientsTable();
722
+ });
723
+
724
+ navLinks.invoicesLink.addEventListener('click', function(e) {
725
+ e.preventDefault();
726
+ showSection('invoicesSection', 'Factures & Devis');
727
+ renderInvoicesTable();
728
+ });
729
+
730
+ navLinks.settingsLink.addEventListener('click', function(e) {
731
+ e.preventDefault();
732
+ showSection('settingsSection', 'Paramètres');
733
+ loadSettings();
734
+ });
735
+
736
+ // Initialize data storage
737
+ let appData = {
738
+ projects: [],
739
+ clients: [],
740
+ invoices: [],
741
+ settings: {
742
+ userName: "Sam Freelance",
743
+ userEmail: "sam@data-analyse.fr",
744
+ userAddress: "123 Rue des Entrepreneurs, 75000 Paris",
745
+ userSiret: "12345678900012",
746
+ defaultRate: 400,
747
+ paymentTerms: "30 jours net",
748
+ logo: ""
749
+ },
750
+ nextInvoiceNumber: 1,
751
+ nextQuoteNumber: 1
752
+ };
753
+
754
+ // Try to load data from localStorage
755
+ const savedData = localStorage.getItem('samfactData');
756
+ if (savedData) {
757
+ appData = JSON.parse(savedData);
758
+ } else {
759
+ // Add some sample data if no saved data exists
760
+ appData.clients = [
761
+ { id: 1, name: "Entreprise A", email: "contact@entreprise-a.fr", address: "1 Rue des Exemples, Paris", siret: "12345678900001", phone: "0123456789" },
762
+ { id: 2, name: "Startup B", email: "hello@startup-b.com", address: "5 Avenue des Tests, Lyon", siret: "98765432100001", phone: "0987654321" }
763
+ ];
764
+
765
+ appData.projects = [
766
+ { id: 1, name: "Analyse données marketing", clientId: 1, startDate: "2023-05-01", endDate: "2023-05-15", days: 10, rate: 400, description: "Analyse des campagnes marketing", status: "completed" },
767
+ { id: 2, name: "Dashboard analytics", clientId: 2, startDate: "2023-06-01", endDate: "", days: 5, rate: 450, description: "Création de dashboard pour suivi KPI", status: "active" }
768
+ ];
769
+
770
+ appData.invoices = [
771
+ { id: 1, number: "F2023-001", type: "invoice", clientId: 1, date: "2023-05-16", dueDate: "2023-06-16", status: "paid", projectId: 1, paymentTerms: "30 jours net", notes: "", items: [
772
+ { description: "Analyse données marketing", days: 10, rate: 400, total: 4000 }
773
+ ]},
774
+ { id: 2, number: "D2023-001", type: "quote", clientId: 2, date: "2023-05-20", dueDate: "", status: "sent", projectId: 2, paymentTerms: "30 jours net", notes: "Valable 30 jours", items: [
775
+ { description: "Dashboard analytics", days: 5, rate: 450, total: 2250 }
776
+ ]}
777
+ ];
778
+
779
+ appData.nextInvoiceNumber = 3;
780
+ appData.nextQuoteNumber = 2;
781
+
782
+ saveData();
783
+ }
784
+
785
+ // Save data to localStorage
786
+ function saveData() {
787
+ localStorage.setItem('samfactData', JSON.stringify(appData));
788
+ }
789
+
790
+ // Dashboard functions
791
+ function updateDashboardStats() {
792
+ const activeProjects = appData.projects.filter(p => p.status === 'active').length;
793
+ const daysWorked = appData.projects.reduce((sum, project) => sum + (project.days || 0), 0);
794
+ const totalRevenue = appData.projects.reduce((sum, project) => sum + (project.days * project.rate || 0), 0);
795
+
796
+ document.getElementById('activeProjectsCount').textContent = activeProjects;
797
+ document.getElementById('daysWorkedCount').textContent = daysWorked;
798
+ document.getElementById('totalRevenue').textContent = totalRevenue.toLocaleString('fr-FR') + '€';
799
+
800
+ renderProjectsTable();
801
+ updateRevenueChart();
802
+ }
803
+
804
+ function renderProjectsTable() {
805
+ const tableBody = document.getElementById('projectsTableBody');
806
+ tableBody.innerHTML = '';
807
+
808
+ // Show only active projects on dashboard
809
+ const activeProjects = appData.projects.filter(p => p.status === 'active');
810
+
811
+ activeProjects.forEach(project => {
812
+ const client = appData.clients.find(c => c.id === project.clientId) || {};
813
+ const total = project.days * project.rate;
814
+
815
+ const row = document.createElement('tr');
816
+ row.innerHTML = `
817
+ <td class="px-6 py-4 whitespace-nowrap">
818
+ <div class="font-medium text-gray-900">${project.name}</div>
819
+ </td>
820
+ <td class="px-6 py-4 whitespace-nowrap">
821
+ <div class="text-gray-900">${client.name || '-'}</div>
822
+ </td>
823
+ <td class="px-6 py-4 whitespace-nowrap">
824
+ <div class="text-gray-900">${project.days}</div>
825
+ </td>
826
+ <td class="px-6 py-4 whitespace-nowrap">
827
+ <div class="text-gray-900">${project.rate.toLocaleString('fr-FR')}€</div>
828
+ </td>
829
+ <td class="px-6 py-4 whitespace-nowrap">
830
+ <div class="text-gray-900 font-medium">${total.toLocaleString('fr-FR')}€</div>
831
+ </td>
832
+ <td class="px-6 py-4 whitespace-nowrap">
833
+ <button class="text-blue-600 hover:text-blue-900 mr-2 edit-project-btn" data-id="${project.id}">
834
+ <i class="fas fa-edit"></i>
835
+ </button>
836
+ <button class="text-red-600 hover:text-red-900 delete-project-btn" data-id="${project.id}">
837
+ <i class="fas fa-trash"></i>
838
+ </button>
839
+ </td>
840
+ `;
841
+ tableBody.appendChild(row);
842
+ });
843
+
844
+ // Add event listeners to buttons
845
+ document.querySelectorAll('.edit-project-btn').forEach(btn => {
846
+ btn.addEventListener('click', function() {
847
+ openEditProjectModal(parseInt(this.getAttribute('data-id')));
848
+ });
849
+ });
850
+
851
+ document.querySelectorAll('.delete-project-btn').forEach(btn => {
852
+ btn.addEventListener('click', function() {
853
+ if (confirm('Voulez-vous vraiment supprimer ce projet ?')) {
854
+ deleteProject(parseInt(this.getAttribute('data-id')));
855
+ }
856
+ });
857
+ });
858
+ }
859
+
860
+ function updateRevenueChart() {
861
+ const range = parseInt(document.getElementById('chartRange').value);
862
+ const now = new Date();
863
+ const months = [];
864
+
865
+ for (let i = range - 1; i >= 0; i--) {
866
+ const date = new Date(now.getFullYear(), now.getMonth() - i, 1);
867
+ months.push(date.toLocaleDateString('fr-FR', { month: 'short', year: 'numeric' }));
868
+ }
869
+
870
+ const revenueData = new Array(range).fill(0);
871
+
872
+ appData.projects.forEach(project => {
873
+ const projectDate = new Date(project.startDate);
874
+ const monthDiff = (now.getFullYear() - projectDate.getFullYear()) * 12 + now.getMonth() - projectDate.getMonth();
875
+
876
+ if (monthDiff >= 0 && monthDiff < range) {
877
+ revenueData[range - 1 - monthDiff] += project.days * project.rate;
878
+ }
879
+ });
880
+
881
+ const ctx = document.getElementById('revenueChart').getContext('2d');
882
+
883
+ if (window.revenueChart) {
884
+ window.revenueChart.destroy();
885
+ }
886
+
887
+ window.revenueChart = new Chart(ctx, {
888
+ type: 'bar',
889
+ data: {
890
+ labels: months,
891
+ datasets: [{
892
+ label: 'Revenus HT (€)',
893
+ data: revenueData,
894
+ backgroundColor: 'rgba(59, 130, 246, 0.5)',
895
+ borderColor: 'rgba(59, 130, 246, 1)',
896
+ borderWidth: 1
897
+ }]
898
+ },
899
+ options: {
900
+ responsive: true,
901
+ scales: {
902
+ y: {
903
+ beginAtZero: true,
904
+ ticks: {
905
+ callback: function(value) {
906
+ return value.toLocaleString('fr-FR') + '€';
907
+ }
908
+ }
909
+ }
910
+ },
911
+ plugins: {
912
+ tooltip: {
913
+ callbacks: {
914
+ label: function(context) {
915
+ return context.dataset.label + ': ' + context.raw.toLocaleString('fr-FR') + '€';
916
+ }
917
+ }
918
+ }
919
+ }
920
+ }
921
+ });
922
+
923
+ document.getElementById('chartRange').addEventListener('change', updateRevenueChart);
924
+ }
925
+
926
+ // Projects functions
927
+ function renderAllProjectsTable() {
928
+ const tableBody = document.getElementById('allProjectsTableBody');
929
+ tableBody.innerHTML = '';
930
+
931
+ appData.projects.forEach(project => {
932
+ const client = appData.clients.find(c => c.id === project.clientId) || {};
933
+ const total = project.days * project.rate;
934
+ const statusClass = project.status === 'active' ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800';
935
+ const statusText = project.status === 'active' ? 'Actif' : 'Terminé';
936
+
937
+ const row = document.createElement('tr');
938
+ row.innerHTML = `
939
+ <td class="px-6 py-4 whitespace-nowrap">
940
+ <div class="font-medium text-gray-900">${project.name}</div>
941
+ <div class="text-sm text-gray-500">${project.description || ''}</div>
942
+ </td>
943
+ <td class="px-6 py-4 whitespace-nowrap">
944
+ <div class="text-gray-900">${client.name || '-'}</div>
945
+ </td>
946
+ <td class="px-6 py-4 whitespace-nowrap">
947
+ <div class="text-gray-900">${project.startDate ? new Date(project.startDate).toLocaleDateString('fr-FR') : '-'}</div>
948
+ </td>
949
+ <td class="px-6 py-4 whitespace-nowrap">
950
+ <div class="text-gray-900">${project.endDate ? new Date(project.endDate).toLocaleDateString('fr-FR') : '-'}</div>
951
+ </td>
952
+ <td class="px-6 py-4 whitespace-nowrap">
953
+ <div class="text-gray-900">${project.days}</div>
954
+ </td>
955
+ <td class="px-6 py-4 whitespace-nowrap">
956
+ <div class="text-gray-900 font-medium">${total.toLocaleString('fr-FR')}€</div>
957
+ </td>
958
+ <td class="px-6 py-4 whitespace-nowrap">
959
+ <span class="px-2 py-1 text-xs rounded-full ${statusClass}">${statusText}</span>
960
+ </td>
961
+ <td class="px-6 py-4 whitespace-nowrap">
962
+ <button class="text-blue-600 hover:text-blue-900 mr-2 edit-project-btn" data-id="${project.id}">
963
+ <i class="fas fa-edit"></i>
964
+ </button>
965
+ <button class="text-red-600 hover:text-red-900 delete-project-btn" data-id="${project.id}">
966
+ <i class="fas fa-trash"></i>
967
+ </button>
968
+ </td>
969
+ `;
970
+ tableBody.appendChild(row);
971
+ });
972
+
973
+ // Add event listeners to buttons
974
+ document.querySelectorAll('.edit-project-btn').forEach(btn => {
975
+ btn.addEventListener('click', function() {
976
+ openEditProjectModal(parseInt(this.getAttribute('data-id')));
977
+ });
978
+ });
979
+
980
+ document.querySelectorAll('.delete-project-btn').forEach(btn => {
981
+ btn.addEventListener('click', function() {
982
+ if (confirm('Voulez-vous vraiment supprimer ce projet ?')) {
983
+ deleteProject(parseInt(this.getAttribute('data-id')));
984
+ }
985
+ });
986
+ });
987
+ }
988
+
989
+ // Project modal functions
990
+ const projectModal = document.getElementById('projectModal');
991
+ const projectForm = document.getElementById('projectForm');
992
+ const closeProjectModal = document.getElementById('closeProjectModal');
993
+ const cancelProjectBtn = document.getElementById('cancelProjectBtn');
994
+
995
+ // Open modal for new project
996
+ document.getElementById('addProjectBtn').addEventListener('click', openNewProjectModal);
997
+ document.getElementById('addProjectBtn2').addEventListener('click', openNewProjectModal);
998
+
999
+ function openNewProjectModal() {
1000
+ document.getElementById('projectForm').reset();
1001
+ document.getElementById('projectModal').querySelector('h3').textContent = 'Nouveau projet';
1002
+
1003
+ // Populate client dropdown
1004
+ const clientSelect = document.getElementById('projectClient');
1005
+ clientSelect.innerHTML = '<option value="">Sélectionner un client</option>';
1006
+
1007
+ appData.clients.forEach(client => {
1008
+ const option = document.createElement('option');
1009
+ option.value = client.id;
1010
+ option.textContent = client.name;
1011
+ clientSelect.appendChild(option);
1012
+ });
1013
+
1014
+ // Set default rate from settings
1015
+ document.getElementById('projectRate').value = appData.settings.defaultRate || '';
1016
+
1017
+ projectModal.classList.remove('hidden');
1018
+ }
1019
+
1020
+ function openEditProjectModal(projectId) {
1021
+ const project = appData.projects.find(p => p.id === projectId);
1022
+ if (!project) return;
1023
+
1024
+ document.getElementById('projectModal').querySelector('h3').textContent = 'Modifier projet';
1025
+
1026
+ // Populate client dropdown
1027
+ const clientSelect = document.getElementById('projectClient');
1028
+ clientSelect.innerHTML = '<option value="">Sélectionner un client</option>';
1029
+
1030
+ appData.clients.forEach(client => {
1031
+ const option = document.createElement('option');
1032
+ option.value = client.id;
1033
+ option.textContent = client.name;
1034
+ option.selected = client.id === project.clientId;
1035
+ clientSelect.appendChild(option);
1036
+ });
1037
+
1038
+ // Fill form with project data
1039
+ document.getElementById('projectName').value = project.name || '';
1040
+ document.getElementById('projectStartDate').value = project.startDate || '';
1041
+ document.getElementById('projectEndDate').value = project.endDate || '';
1042
+ document.getElementById('projectDays').value = project.days || '';
1043
+ document.getElementById('projectRate').value = project.rate || '';
1044
+ document.getElementById('projectDescription').value = project.description || '';
1045
+
1046
+ // Store project ID in form
1047
+ projectForm.setAttribute('data-project-id', projectId);
1048
+
1049
+ projectModal.classList.remove('hidden');
1050
+ }
1051
+
1052
+ // Close modal
1053
+ closeProjectModal.addEventListener('click', function() {
1054
+ projectModal.classList.add('hidden');
1055
+ });
1056
+
1057
+ cancelProjectBtn.addEventListener('click', function() {
1058
+ projectModal.classList.add('hidden');
1059
+ });
1060
+
1061
+ // Handle form submission
1062
+ projectForm.addEventListener('submit', function(e) {
1063
+ e.preventDefault();
1064
+
1065
+ const projectId = parseInt(this.getAttribute('data-project-id')) || null;
1066
+ const isEdit = !!projectId;
1067
+
1068
+ const projectData = {
1069
+ name: document.getElementById('projectName').value,
1070
+ clientId: parseInt(document.getElementById('projectClient').value),
1071
+ startDate: document.getElementById('projectStartDate').value,
1072
+ endDate: document.getElementById('projectEndDate').value || null,
1073
+ days: parseInt(document.getElementById('projectDays').value),
1074
+ rate: parseFloat(document.getElementById('projectRate').value),
1075
+ description: document.getElementById('projectDescription').value,
1076
+ status: 'active'
1077
+ };
1078
+
1079
+ if (isEdit) {
1080
+ // Update existing project
1081
+ const index = appData.projects.findIndex(p => p.id === projectId);
1082
+ if (index !== -1) {
1083
+ appData.projects[index] = { ...appData.projects[index], ...projectData };
1084
+ }
1085
+ } else {
1086
+ // Add new project
1087
+ const newId = appData.projects.length > 0 ? Math.max(...appData.projects.map(p => p.id)) + 1 : 1;
1088
+ appData.projects.push({ id: newId, ...projectData });
1089
+ }
1090
+
1091
+ saveData();
1092
+ projectModal.classList.add('hidden');
1093
+
1094
+ // Update UI
1095
+ if (isEdit) {
1096
+ renderProjectsTable();
1097
+ renderAllProjectsTable();
1098
+ } else {
1099
+ updateDashboardStats();
1100
+ renderAllProjectsTable();
1101
+ }
1102
+ });
1103
+
1104
+ function deleteProject(projectId) {
1105
+ appData.projects = appData.projects.filter(p => p.id !== projectId);
1106
+ saveData();
1107
+
1108
+ // Update UI
1109
+ updateDashboardStats();
1110
+ renderAllProjectsTable();
1111
+ }
1112
+
1113
+ // Clients functions
1114
+ function renderClientsTable() {
1115
+ const tableBody = document.getElementById('clientsTableBody');
1116
+ tableBody.innerHTML = '';
1117
+
1118
+ appData.clients.forEach(client => {
1119
+ const row = document.createElement('tr');
1120
+ row.innerHTML = `
1121
+ <td class="px-6 py-4 whitespace-nowrap">
1122
+ <div class="font-medium text-gray-900">${client.name}</div>
1123
+ </td>
1124
+ <td class="px-6 py-4 whitespace-nowrap">
1125
+ <div class="text-gray-900">${client.email}</div>
1126
+ </td>
1127
+ <td class="px-6 py-4">
1128
+ <div class="text-gray-900">${client.address || '-'}</div>
1129
+ </td>
1130
+ <td class="px-6 py-4 whitespace-nowrap">
1131
+ <div class="text-gray-900">${client.siret || '-'}</div>
1132
+ </td>
1133
+ <td class="px-6 py-4 whitespace-nowrap">
1134
+ <button class="text-blue-600 hover:text-blue-900 mr-2 edit-client-btn" data-id="${client.id}">
1135
+ <i class="fas fa-edit"></i>
1136
+ </button>
1137
+ <button class="text-red-600 hover:text-red-900 delete-client-btn" data-id="${client.id}">
1138
+ <i class="fas fa-trash"></i>
1139
+ </button>
1140
+ </td>
1141
+ `;
1142
+ tableBody.appendChild(row);
1143
+ });
1144
+
1145
+ // Add event listeners to buttons
1146
+ document.querySelectorAll('.edit-client-btn').forEach(btn => {
1147
+ btn.addEventListener('click', function() {
1148
+ openEditClientModal(parseInt(this.getAttribute('data-id')));
1149
+ });
1150
+ });
1151
+
1152
+ document.querySelectorAll('.delete-client-btn').forEach(btn => {
1153
+ btn.addEventListener('click', function() {
1154
+ if (confirm('Voulez-vous vraiment supprimer ce client ?')) {
1155
+ deleteClient(parseInt(this.getAttribute('data-id')));
1156
+ }
1157
+ });
1158
+ });
1159
+ }
1160
+
1161
+ // Client modal functions
1162
+ const clientModal = document.getElementById('clientModal');
1163
+ const clientForm = document.getElementById('clientForm');
1164
+ const closeClientModal = document.getElementById('closeClientModal');
1165
+ const cancelClientBtn = document.getElementById('cancelClientBtn');
1166
+
1167
+ // Open modal for new client
1168
+ document.getElementById('addClientBtn').addEventListener('click', openNewClientModal);
1169
+
1170
+ function openNewClientModal() {
1171
+ document.getElementById('clientForm').reset();
1172
+ document.getElementById('clientModal').querySelector('h3').textContent = 'Nouveau client';
1173
+ clientForm.removeAttribute('data-client-id');
1174
+ clientModal.classList.remove('hidden');
1175
+ }
1176
+
1177
+ function openEditClientModal(clientId) {
1178
+ const client = appData.clients.find(c => c.id === clientId);
1179
+ if (!client) return;
1180
+
1181
+ document.getElementById('clientModal').querySelector('h3').textContent = 'Modifier client';
1182
+
1183
+ // Fill form with client data
1184
+ document.getElementById('clientName').value = client.name || '';
1185
+ document.getElementById('clientEmail').value = client.email || '';
1186
+ document.getElementById('clientAddress').value = client.address || '';
1187
+ document.getElementById('clientSiret').value = client.siret || '';
1188
+ document.getElementById('clientPhone').value = client.phone || '';
1189
+
1190
+ // Store client ID in form
1191
+ clientForm.setAttribute('data-client-id', clientId);
1192
+
1193
+ clientModal.classList.remove('hidden');
1194
+ }
1195
+
1196
+ // Close modal
1197
+ closeClientModal.addEventListener('click', function() {
1198
+ clientModal.classList.add('hidden');
1199
+ });
1200
+
1201
+ cancelClientBtn.addEventListener('click', function() {
1202
+ clientModal.classList.add('hidden');
1203
+ });
1204
+
1205
+ // Handle form submission
1206
+ clientForm.addEventListener('submit', function(e) {
1207
+ e.preventDefault();
1208
+
1209
+ const clientId = parseInt(this.getAttribute('data-client-id')) || null;
1210
+ const isEdit = !!clientId;
1211
+
1212
+ const clientData = {
1213
+ name: document.getElementById('clientName').value,
1214
+ email: document.getElementById('clientEmail').value,
1215
+ address: document.getElementById('clientAddress').value,
1216
+ siret: document.getElementById('clientSiret').value,
1217
+ phone: document.getElementById('clientPhone').value
1218
+ };
1219
+
1220
+ if (isEdit) {
1221
+ // Update existing client
1222
+ const index = appData.clients.findIndex(c => c.id === clientId);
1223
+ if (index !== -1) {
1224
+ appData.clients[index] = { ...appData.clients[index], ...clientData };
1225
+ }
1226
+ } else {
1227
+ // Add new client
1228
+ const newId = appData.clients.length > 0 ? Math.max(...appData.clients.map(c => c.id)) + 1 : 1;
1229
+ appData.clients.push({ id: newId, ...clientData });
1230
+ }
1231
+
1232
+ saveData();
1233
+ clientModal.classList.add('hidden');
1234
+
1235
+ // Update UI
1236
+ renderClientsTable();
1237
+
1238
+ // Also update client dropdowns in other forms
1239
+ if (!isEdit) {
1240
+ updateDashboardStats();
1241
+ renderAllProjectsTable();
1242
+ }
1243
+ });
1244
+
1245
+ function deleteClient(clientId) {
1246
+ // Check if client is used in any projects or invoices
1247
+ const hasProjects = appData.projects.some(p => p.clientId === clientId);
1248
+ const hasInvoices = appData.invoices.some(i => i.clientId === clientId);
1249
+
1250
+ if (hasProjects || hasInvoices) {
1251
+ alert('Ce client est associé à des projets ou factures et ne peut pas être supprimé.');
1252
+ return;
1253
+ }
1254
+
1255
+ appData.clients = appData.clients.filter(c => c.id !== clientId);
1256
+ saveData();
1257
+
1258
+ // Update UI
1259
+ renderClientsTable();
1260
+ }
1261
+
1262
+ // Invoices functions
1263
+ function renderInvoicesTable(filter = null) {
1264
+ const tableBody = document.getElementById('invoicesTableBody');
1265
+ tableBody.innerHTML = '';
1266
+
1267
+ // Update counters
1268
+ const allInvoices = appData.invoices.length;
1269
+ const draftInvoices = appData.invoices.filter(i => i.status === 'draft').length;
1270
+ const sentInvoices = appData.invoices.filter(i => i.status === 'sent').length;
1271
+ const paidInvoices = appData.invoices.filter(i => i.status === 'paid').length;
1272
+
1273
+ document.getElementById('allInvoicesCount').textContent = allInvoices;
1274
+ document.getElementById('draftInvoicesCount').textContent = draftInvoices;
1275
+ document.getElementById('sentInvoicesCount').textContent = sentInvoices;
1276
+ document.getElementById('paidInvoicesCount').textContent = paidInvoices;
1277
+
1278
+ // Filter invoices if needed
1279
+ let invoicesToShow = appData.invoices;
1280
+ if (filter === 'draft') {
1281
+ invoicesToShow = appData.invoices.filter(i => i.status === 'draft');
1282
+ } else if (filter === 'sent') {
1283
+ invoicesToShow = appData.invoices.filter(i => i.status === 'sent');
1284
+ } else if (filter === 'paid') {
1285
+ invoicesToShow = appData.invoices.filter(i => i.status === 'paid');
1286
+ }
1287
+
1288
+ // Sort by date (newest first)
1289
+ invoicesToShow.sort((a, b) => new Date(b.date) - new Date(a.date));
1290
+
1291
+ invoicesToShow.forEach(invoice => {
1292
+ const client = appData.clients.find(c => c.id === invoice.clientId) || {};
1293
+ const project = invoice.projectId ? appData.projects.find(p => p.id === invoice.projectId) : null;
1294
+ const total = invoice.items.reduce((sum, item) => sum + item.total, 0);
1295
+
1296
+ let statusClass = '';
1297
+ let statusText = '';
1298
+
1299
+ if (invoice.status === 'draft') {
1300
+ statusClass = 'bg-gray-100 text-gray-800';
1301
+ statusText = 'Brouillon';
1302
+ } else if (invoice.status === 'sent') {
1303
+ statusClass = 'bg-yellow-100 text-yellow-800';
1304
+ statusText = 'Envoyé';
1305
+ } else if (invoice.status === 'paid') {
1306
+ statusClass = 'bg-green-100 text-green-800';
1307
+ statusText = 'Payé';
1308
+ }
1309
+
1310
+ const typeText = invoice.type === 'invoice' ? 'Facture' : 'Devis';
1311
+ const typeClass = invoice.type === 'invoice' ? 'bg-blue-100 text-blue-800' : 'bg-green-100 text-green-800';
1312
+
1313
+ const row = document.createElement('tr');
1314
+ row.innerHTML = `
1315
+ <td class="px-6 py-4 whitespace-nowrap">
1316
+ <div class="font-medium text-gray-900">${invoice.number}</div>
1317
+ </td>
1318
+ <td class="px-6 py-4 whitespace-nowrap">
1319
+ <div class="text-gray-900">${client.name || '-'}</div>
1320
+ </td>
1321
+ <td class="px-6 py-4 whitespace-nowrap">
1322
+ <div class="text-gray-900">${new Date(invoice.date).toLocaleDateString('fr-FR')}</div>
1323
+ </td>
1324
+ <td class="px-6 py-4 whitespace-nowrap">
1325
+ <div class="text-gray-900">${invoice.dueDate ? new Date(invoice.dueDate).toLocaleDateString('fr-FR') : '-'}</div>
1326
+ </td>
1327
+ <td class="px-6 py-4 whitespace-nowrap">
1328
+ <div class="text-gray-900 font-medium">${total.toLocaleString('fr-FR')}€</div>
1329
+ </td>
1330
+ <td class="px-6 py-4 whitespace-nowrap">
1331
+ <span class="px-2 py-1 text-xs rounded-full ${statusClass}">${statusText}</span>
1332
+ </td>
1333
+ <td class="px-6 py-4 whitespace-nowrap">
1334
+ <span class="px-2 py-1 text-xs rounded-full ${typeClass}">${typeText}</span>
1335
+ </td>
1336
+ <td class="px-6 py-4 whitespace-nowrap">
1337
+ <button class="text-blue-600 hover:text-blue-900 mr-2 edit-invoice-btn" data-id="${invoice.id}">
1338
+ <i class="fas fa-edit"></i>
1339
+ </button>
1340
+ <button class="text-purple-600 hover:text-purple-900 mr-2 preview-invoice-btn" data-id="${invoice.id}">
1341
+ <i class="fas fa-eye"></i>
1342
+ </button>
1343
+ <button class="text-red-600 hover:text-red-900 delete-invoice-btn" data-id="${invoice.id}">
1344
+ <i class="fas fa-trash"></i>
1345
+ </button>
1346
+ </td>
1347
+ `;
1348
+ tableBody.appendChild(row);
1349
+ });
1350
+
1351
+ // Add event listeners to buttons
1352
+ document.querySelectorAll('.edit-invoice-btn').forEach(btn => {
1353
+ btn.addEventListener('click', function() {
1354
+ openEditInvoiceModal(parseInt(this.getAttribute('data-id')));
1355
+ });
1356
+ });
1357
+
1358
+ document.querySelectorAll('.preview-invoice-btn').forEach(btn => {
1359
+ btn.addEventListener('click', function() {
1360
+ previewInvoice(parseInt(this.getAttribute('data-id')));
1361
+ });
1362
+ });
1363
+
1364
+ document.querySelectorAll('.delete-invoice-btn').forEach(btn => {
1365
+ btn.addEventListener('click', function() {
1366
+ if (confirm('Voulez-vous vraiment supprimer ce document ?')) {
1367
+ deleteInvoice(parseInt(this.getAttribute('data-id')));
1368
+ }
1369
+ });
1370
+ });
1371
+ }
1372
+
1373
+ // Filter buttons
1374
+ document.getElementById('allInvoicesBtn').addEventListener('click', function() {
1375
+ renderInvoicesTable();
1376
+ });
1377
+
1378
+ document.getElementById('draftInvoicesBtn').addEventListener('click', function() {
1379
+ renderInvoicesTable('draft');
1380
+ });
1381
+
1382
+ document.getElementById('sentInvoicesBtn').addEventListener('click', function() {
1383
+ renderInvoicesTable('sent');
1384
+ });
1385
+
1386
+ document.getElementById('paidInvoicesBtn').addEventListener('click', function() {
1387
+ renderInvoicesTable('paid');
1388
+ });
1389
+
1390
+ // Invoice modal functions
1391
+ const invoiceModal = document.getElementById('invoiceModal');
1392
+ const invoiceForm = document.getElementById('invoiceForm');
1393
+ const closeInvoiceModal = document.getElementById('closeInvoiceModal');
1394
+ const cancelInvoiceBtn = document.getElementById('cancelInvoiceBtn');
1395
+
1396
+ // Open modal for new invoice or quote
1397
+ document.getElementById('addInvoiceBtn').addEventListener('click', function() {
1398
+ openNewInvoiceModal('invoice');
1399
+ });
1400
+
1401
+ document.getElementById('addQuoteBtn').addEventListener('click', function() {
1402
+ openNewInvoiceModal('quote');
1403
+ });
1404
+
1405
+ function openNewInvoiceModal(type) {
1406
+ document.getElementById('invoiceForm').reset();
1407
+ document.getElementById('invoiceType').value = type;
1408
+ document.getElementById('invoiceModalTitle').textContent = type === 'invoice' ? 'Nouvelle facture' : 'Nouveau devis';
1409
+ document.getElementById('invoiceId').value = '';
1410
+
1411
+ // Generate invoice/quote number
1412
+ const now = new Date();
1413
+ const year = now.getFullYear();
1414
+
1415
+ if (type === 'invoice') {
1416
+ const nextNum = appData.nextInvoiceNumber.toString().padStart(3, '0');
1417
+ document.getElementById('invoiceNumber').value = `F${year}-${nextNum}`;
1418
+ } else {
1419
+ const nextNum = appData.nextQuoteNumber.toString().padStart(3, '0');
1420
+ document.getElementById('invoiceNumber').value = `D${year}-${nextNum}`;
1421
+ }
1422
+
1423
+ // Set default date to today
1424
+ document.getElementById('invoiceDate').valueAsDate = now;
1425
+
1426
+ // Set default payment terms from settings
1427
+ document.getElementById('invoicePaymentTerms').value = appData.settings.paymentTerms || '';
1428
+
1429
+ // Populate client dropdown
1430
+ const clientSelect = document.getElementById('invoiceClient');
1431
+ clientSelect.innerHTML = '<option value="">Sélectionner un client</option>';
1432
+
1433
+ appData.clients.forEach(client => {
1434
+ const option = document.createElement('option');
1435
+ option.value = client.id;
1436
+ option.textContent = client.name;
1437
+ clientSelect.appendChild(option);
1438
+ });
1439
+
1440
+ // Populate project dropdown
1441
+ const projectSelect = document.getElementById('invoiceProject');
1442
+ projectSelect.innerHTML = '<option value="">Aucun projet</option>';
1443
+
1444
+ appData.projects.forEach(project => {
1445
+ const option = document.createElement('option');
1446
+ option.value = project.id;
1447
+ option.textContent = project.name;
1448
+ projectSelect.appendChild(option);
1449
+ });
1450
+
1451
+ // Clear items table except first row
1452
+ const itemsTable = document.getElementById('invoiceItemsTable');
1453
+ while (itemsTable.rows.length > 1) {
1454
+ itemsTable.deleteRow(1);
1455
+ }
1456
+
1457
+ // Reset first row
1458
+ const firstRow = itemsTable.rows[0];
1459
+ firstRow.cells[0].querySelector('input').value = '';
1460
+ firstRow.cells[1].querySelector('input').value = '';
1461
+ firstRow.cells[2].querySelector('input').value = appData.settings.defaultRate || '';
1462
+ firstRow.cells[3].querySelector('input').value = '0.00';
1463
+
1464
+ invoiceModal.classList.remove('hidden');
1465
+ }
1466
+
1467
+ function openEditInvoiceModal(invoiceId) {
1468
+ const invoice = appData.invoices.find(i => i.id === invoiceId);
1469
+ if (!invoice) return;
1470
+
1471
+ document.getElementById('invoiceModalTitle').textContent = invoice.type === 'invoice' ? 'Modifier facture' : 'Modifier devis';
1472
+ document.getElementById('invoiceType').value = invoice.type;
1473
+ document.getElementById('invoiceId').value = invoice.id;
1474
+
1475
+ // Fill basic info
1476
+ document.getElementById('invoiceNumber').value = invoice.number;
1477
+ document.getElementById('invoiceDate').value = invoice.date;
1478
+ document.getElementById('invoiceDueDate').value = invoice.dueDate || '';
1479
+ document.getElementById('invoiceStatus').value = invoice.status;
1480
+ document.getElementById('invoicePaymentTerms').value = invoice.paymentTerms || '';
1481
+ document.getElementById('invoiceNotes').value = invoice.notes || '';
1482
+
1483
+ // Populate client dropdown
1484
+ const clientSelect = document.getElementById('invoiceClient');
1485
+ clientSelect.innerHTML = '<option value="">Sélectionner un client</option>';
1486
+
1487
+ </html>