om4r932 commited on
Commit
ed24fa8
·
1 Parent(s): 58becbd

Filters update

Browse files
Files changed (3) hide show
  1. index.html +83 -25
  2. static/script.js +167 -27
  3. updates.md +16 -0
index.html CHANGED
@@ -1,11 +1,14 @@
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>Requirements Extractor</title>
 
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  </head>
 
9
  <body class="bg-gray-100 min-h-screen">
10
  <!-- Loading Overlay -->
11
  <div id="loading-overlay" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
@@ -57,23 +60,61 @@
57
  <!-- Filters -->
58
  <div id="filters-container" class="mb-6 hidden">
59
  <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
60
- <div>
61
- <label for="doc-type-filter" class="block text-sm font-medium text-gray-700 mb-2">Type</label>
62
- <select id="doc-type-filter" class="w-full p-2 border border-gray-300 rounded-md">
63
- <option value="">Tous</option>
64
- </select>
 
 
 
 
 
 
 
 
 
65
  </div>
66
- <div>
67
- <label for="doc-status-filter" class="block text-sm font-medium text-gray-700 mb-2">Status</label>
68
- <select id="doc-status-filter" class="w-full p-2 border border-gray-300 rounded-md">
69
- <option value="">Tous</option>
70
- </select>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  </div>
72
- <div>
73
- <label for="agenda-item-filter" class="block text-sm font-medium text-gray-700 mb-2">Agenda Item</label>
74
- <select id="agenda-item-filter" class="w-full p-2 border border-gray-300 rounded-md">
75
- <option value="">Tous</option>
76
- </select>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  </div>
78
  </div>
79
  </div>
@@ -81,21 +122,33 @@
81
  <!-- Action Buttons -->
82
  <div id="action-buttons-container" class="mb-6 hidden">
83
  <div class="flex flex-wrap gap-2">
84
- <button id="download-tdocs-btn" class="px-4 py-2 bg-purple-500 text-white rounded-md hover:bg-purple-600">
 
85
  Download TDocs
86
  </button>
87
- <button id="extract-requirements-btn" class="px-4 py-2 bg-orange-500 text-white rounded-md hover:bg-orange-600">
 
88
  Extract Requirements
89
  </button>
90
- <button id="find-requirements-btn" class="px-4 py-2 bg-red-500 text-white rounded-md hover:bg-red-600 hidden">
 
91
  Find Requirements
92
  </button>
93
- <button id="categorize-requirements-btn" class="px-4 py-2 bg-yellow-500 text-white rounded-md hover:bg-yellow-600 hidden">
 
94
  Categorize Requirements
95
  </button>
96
  </div>
97
  </div>
98
-
 
 
 
 
 
 
 
 
99
  <!-- Data Table -->
100
  <div id="data-table-container" class="mb-6 hidden">
101
  <table id="data-table" class="w-full bg-white rounded-lg shadow overflow-hidden">
@@ -126,8 +179,10 @@
126
  <div id="query-requirements-container" class="mb-6 hidden">
127
  <h2 class="text-2xl font-bold mb-4">Query requirements</h2>
128
  <div class="flex gap-2">
129
- <input type="text" id="query-input" class="flex-1 p-2 border border-gray-300 rounded-md" placeholder="Enter your query...">
130
- <button id="search-requirements-btn" class="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600">
 
 
131
  Search
132
  </button>
133
  </div>
@@ -143,10 +198,12 @@
143
  <!-- Solutions Action Buttons -->
144
  <div id="solutions-action-buttons-container" class="mb-6 hidden">
145
  <div class="flex flex-wrap gap-2 justify-center">
146
- <button id="get-solutions-btn" class="px-4 py-2 bg-indigo-500 text-white rounded-md hover:bg-indigo-600">
 
147
  Get Solutions
148
  </button>
149
- <button id="get-solutions-step-btn" class="px-4 py-2 bg-pink-500 text-white rounded-md hover:bg-pink-600">
 
150
  Get Solutions (Step-by-step)
151
  </button>
152
  </div>
@@ -161,4 +218,5 @@
161
 
162
  <script src="/static/script.js"></script>
163
  </body>
164
- </html>
 
 
1
  <!DOCTYPE html>
2
  <html lang="fr">
3
+
4
  <head>
5
  <meta charset="UTF-8">
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
  <title>Requirements Extractor</title>
8
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/daisyui/5.0.43/daisyui.css" rel="stylesheet" type="text/css" />
9
  <script src="https://cdn.tailwindcss.com"></script>
10
  </head>
11
+
12
  <body class="bg-gray-100 min-h-screen">
13
  <!-- Loading Overlay -->
14
  <div id="loading-overlay" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
 
60
  <!-- Filters -->
61
  <div id="filters-container" class="mb-6 hidden">
62
  <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
63
+ <!-- Type Filter Dropdown -->
64
+ <div class="dropdown dropdown-bottom dropdown-center w-full mb-4">
65
+ <div tabindex="0" role="button" class="btn w-full justify-between" id="doc-type-filter-btn">
66
+ <span id="doc-type-filter-label">Type</span>
67
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 ml-2" fill="none" viewBox="0 0 24 24"
68
+ stroke="currentColor">
69
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
70
+ </svg>
71
+ </div>
72
+ <ul tabindex="0"
73
+ class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-full min-w-[200px] max-h-60 overflow-y-auto"
74
+ id="doc-type-filter-menu">
75
+ <!-- Rempli par JS -->
76
+ </ul>
77
  </div>
78
+
79
+ <!-- Status Filter Dropdown -->
80
+ <div class="dropdown dropdown-bottom dropdown-center w-full mb-4">
81
+ <div tabindex="0" role="button" class="btn w-full justify-between" id="status-dropdown-btn">
82
+ <span id="status-filter-label">Status (Tous)</span>
83
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 ml-2" fill="none" viewBox="0 0 24 24"
84
+ stroke="currentColor">
85
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
86
+ </svg>
87
+ </div>
88
+ <ul tabindex="0"
89
+ class="dropdown-content z-[1] p-2 shadow bg-base-100 rounded-box w-full max-h-60 overflow-y-auto">
90
+ <li class="border-b pb-2 mb-2">
91
+ <label class="flex items-center gap-2 cursor-pointer">
92
+ <input type="checkbox" class="status-checkbox" value="all" checked>
93
+ <span class="font-semibold">Tous</span>
94
+ </label>
95
+ </li>
96
+ <div id="status-options" class="flex flex-col gap-1"></div>
97
+ </ul>
98
  </div>
99
+ <!-- Agenda Item Filter Dropdown -->
100
+ <div class="dropdown dropdown-bottom dropdown-center w-full mb-4">
101
+ <div tabindex="0" role="button" class="btn w-full justify-between" id="agenda-dropdown-btn">
102
+ <span id="agenda-filter-label">Agenda Item (Tous)</span>
103
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 ml-2" fill="none" viewBox="0 0 24 24"
104
+ stroke="currentColor">
105
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
106
+ </svg>
107
+ </div>
108
+ <ul tabindex="0"
109
+ class="dropdown-content z-[1] p-2 shadow bg-base-100 rounded-box w-full max-h-60 overflow-y-auto">
110
+ <li class="border-b pb-2 mb-2">
111
+ <label class="flex items-center gap-2 cursor-pointer">
112
+ <input type="checkbox" class="agenda-checkbox" value="all" checked>
113
+ <span class="font-semibold">Tous</span>
114
+ </label>
115
+ </li>
116
+ <div id="agenda-options" class="flex flex-col gap-1"></div>
117
+ </ul>
118
  </div>
119
  </div>
120
  </div>
 
122
  <!-- Action Buttons -->
123
  <div id="action-buttons-container" class="mb-6 hidden">
124
  <div class="flex flex-wrap gap-2">
125
+ <button id="download-tdocs-btn"
126
+ class="px-4 py-2 bg-purple-500 text-white rounded-md hover:bg-purple-600">
127
  Download TDocs
128
  </button>
129
+ <button id="extract-requirements-btn"
130
+ class="px-4 py-2 bg-orange-500 text-white rounded-md hover:bg-orange-600">
131
  Extract Requirements
132
  </button>
133
+ <button id="find-requirements-btn"
134
+ class="px-4 py-2 bg-red-500 text-white rounded-md hover:bg-red-600 hidden">
135
  Find Requirements
136
  </button>
137
+ <button id="categorize-requirements-btn"
138
+ class="px-4 py-2 bg-yellow-500 text-white rounded-md hover:bg-yellow-600 hidden">
139
  Categorize Requirements
140
  </button>
141
  </div>
142
  </div>
143
+ <!-- Data Table Informations -->
144
+ <div class="flex justify-end mb-2 gap-2 items-center">
145
+ <span id="displayed-count" class="text-sm text-gray-700 bg-white rounded px-3 py-1 shadow">
146
+ 0 document affiché
147
+ </span>
148
+ <span id="selected-count" class="text-sm text-blue-700 bg-blue-50 rounded px-3 py-1 shadow">
149
+ 0 document sélectionné
150
+ </span>
151
+ </div>
152
  <!-- Data Table -->
153
  <div id="data-table-container" class="mb-6 hidden">
154
  <table id="data-table" class="w-full bg-white rounded-lg shadow overflow-hidden">
 
179
  <div id="query-requirements-container" class="mb-6 hidden">
180
  <h2 class="text-2xl font-bold mb-4">Query requirements</h2>
181
  <div class="flex gap-2">
182
+ <input type="text" id="query-input" class="flex-1 p-2 border border-gray-300 rounded-md"
183
+ placeholder="Enter your query...">
184
+ <button id="search-requirements-btn"
185
+ class="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600">
186
  Search
187
  </button>
188
  </div>
 
198
  <!-- Solutions Action Buttons -->
199
  <div id="solutions-action-buttons-container" class="mb-6 hidden">
200
  <div class="flex flex-wrap gap-2 justify-center">
201
+ <button id="get-solutions-btn"
202
+ class="px-4 py-2 bg-indigo-500 text-white rounded-md hover:bg-indigo-600">
203
  Get Solutions
204
  </button>
205
+ <button id="get-solutions-step-btn"
206
+ class="px-4 py-2 bg-pink-500 text-white rounded-md hover:bg-pink-600">
207
  Get Solutions (Step-by-step)
208
  </button>
209
  </div>
 
218
 
219
  <script src="/static/script.js"></script>
220
  </body>
221
+
222
+ </html>
static/script.js CHANGED
@@ -1,5 +1,8 @@
1
  // Variables globales
2
  let requirements = [];
 
 
 
3
  let accordionStates = {};
4
  let formattedRequirements = [];
5
  let categorizedRequirements = [];
@@ -81,6 +84,113 @@ function populateSelect(selectId, options, defaultText = 'Sélectionner...') {
81
  }
82
  }
83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  /**
85
  * Extrait les données du tableau selon un mapping
86
  * @param {Object} mapping - Mapping des colonnes {columnName: propertyName}
@@ -218,24 +328,32 @@ function populateDataTable(data) {
218
  });
219
 
220
  setupTableEvents();
 
221
  }
222
 
223
- /**
224
- * Configure les filtres basés sur les données
225
- * @param {Array} data - Données pour extraire les valeurs uniques
226
- */
227
  function setupFilters(data) {
 
228
  const types = [...new Set(data.map(item => item.Type).filter(Boolean))];
229
  const statuses = [...new Set(data.map(item => item['TDoc Status']).filter(Boolean))];
230
  const agendaItems = [...new Set(data.map(item => item['Agenda item description']).filter(Boolean))];
231
 
232
- populateSelect('doc-type-filter', Object.fromEntries(types.map(t => [t, t])), 'Tous');
233
- populateSelect('doc-status-filter', Object.fromEntries(statuses.map(s => [s, s])), 'Tous');
234
- populateSelect('agenda-item-filter', Object.fromEntries(agendaItems.map(a => [a, a])), 'Tous');
 
 
235
 
236
- setupFilterEvents();
237
- }
 
 
 
238
 
 
 
 
 
 
239
  /**
240
  * Configure les événements des filtres
241
  */
@@ -245,44 +363,66 @@ function setupFilterEvents() {
245
  });
246
  }
247
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
  /**
249
  * Applique les filtres au tableau
250
  */
251
  function applyFilters() {
252
- const typeFilter = document.getElementById('doc-type-filter').value;
253
- const statusFilter = document.getElementById('doc-status-filter').value;
254
- const agendaFilter = document.getElementById('agenda-item-filter').value;
255
-
256
  const rows = document.querySelectorAll('#data-table tbody tr');
257
-
258
  rows.forEach(row => {
259
- const type = row.getAttribute('data-type');
260
- const status = row.getAttribute('data-status');
261
- const agenda = row.getAttribute('data-agenda');
262
 
263
- const typeMatch = !typeFilter || type === typeFilter;
264
- const statusMatch = !statusFilter || status === statusFilter;
265
- const agendaMatch = !agendaFilter || agenda === agendaFilter;
266
 
267
- if (typeMatch && statusMatch && agendaMatch) {
268
- row.style.display = '';
269
- } else {
270
- row.style.display = 'none';
271
- }
272
  });
 
273
  }
274
 
 
275
  /**
276
  * Configure les événements du tableau
277
  */
278
  function setupTableEvents() {
279
- // Checkbox "Sélectionner tout"
280
  document.getElementById('select-all-checkbox').addEventListener('change', function() {
281
  const checkboxes = document.querySelectorAll('.row-checkbox');
282
  checkboxes.forEach(checkbox => {
283
- if(checkbox.parentElement.parentElement.style.display != "none"){checkbox.checked = this.checked;}
 
 
 
284
  });
 
285
  });
 
 
 
 
 
 
 
286
  }
287
 
288
  /**
 
1
  // Variables globales
2
  let requirements = [];
3
+ let selectedType = ""; // "" = Tous
4
+ let selectedStatus = new Set(); // valeurs cochées (hors "Tous")
5
+ let selectedAgenda = new Set();
6
  let accordionStates = {};
7
  let formattedRequirements = [];
8
  let categorizedRequirements = [];
 
84
  }
85
  }
86
 
87
+ function populateCheckboxDropdown(optionsContainerId, options, filterType, labelId, selectionSet) {
88
+ const container = document.getElementById(optionsContainerId);
89
+ container.innerHTML = '';
90
+ selectionSet.clear(); // reset all
91
+
92
+ // Ajoute chaque option
93
+ options.forEach(option => {
94
+ const safeId = `${filterType}-${encodeURIComponent(option).replace(/[%\s]/g, '_')}`;
95
+ const label = document.createElement('label');
96
+ label.className = "flex items-center gap-2 cursor-pointer py-1";
97
+ label.innerHTML = `
98
+ <input type="checkbox" class="${filterType}-checkbox option-checkbox" id="${safeId}" value="${option}">
99
+ <span>${option}</span>
100
+ `;
101
+ label.querySelector('input').addEventListener('change', function() {
102
+ if (this.checked) {
103
+ selectionSet.add(this.value);
104
+ } else {
105
+ selectionSet.delete(this.value);
106
+ }
107
+ // Gestion du label "Tous"
108
+ updateCheckboxDropdownLabel(filterType, labelId, selectionSet, options.length);
109
+ // Gestion du "Tous" global
110
+ const allBox = document.querySelector(`.${filterType}-checkbox[value="all"]`);
111
+ if (allBox && allBox.checked) allBox.checked = false;
112
+ // Si plus rien n'est coché, recoche "Tous"
113
+ if (selectionSet.size === 0 && allBox) allBox.checked = true;
114
+ applyFilters();
115
+ });
116
+ container.appendChild(label);
117
+ });
118
+
119
+ // Réinitialise le label
120
+ updateCheckboxDropdownLabel(filterType, labelId, selectionSet, options.length);
121
+
122
+ // Gestion de "Tous"
123
+ const allBox = document.querySelector(`.${filterType}-checkbox[value="all"]`);
124
+ if (allBox) {
125
+ allBox.addEventListener('change', function() {
126
+ if (this.checked) {
127
+ // Décoche tout le reste
128
+ selectionSet.clear();
129
+ container.querySelectorAll('input[type="checkbox"]').forEach(cb => cb.checked = false);
130
+ this.checked = true; // reste coché
131
+ updateCheckboxDropdownLabel(filterType, labelId, selectionSet, options.length);
132
+ applyFilters();
133
+ }
134
+ });
135
+ }
136
+ }
137
+
138
+ function updateCheckboxDropdownLabel(type, labelId, set, totalCount) {
139
+ const label = document.getElementById(labelId);
140
+ if (!set.size) {
141
+ label.textContent = type.charAt(0).toUpperCase() + type.slice(1) + " (Tous)";
142
+ } else if (set.size === 1) {
143
+ label.textContent = [...set][0];
144
+ } else {
145
+ label.textContent = `${type.charAt(0).toUpperCase() + type.slice(1)} (${set.size}/${totalCount})`;
146
+ }
147
+ }
148
+
149
+ function updateSelectedFilters(filterType, value, isChecked) {
150
+ if (isChecked) {
151
+ selectedFilters[filterType].add(value);
152
+ } else {
153
+ selectedFilters[filterType].delete(value);
154
+ }
155
+ }
156
+
157
+ function populateDaisyDropdown(menuId, options, labelId, onSelect) {
158
+ const menu = document.getElementById(menuId);
159
+ menu.innerHTML = '';
160
+ // Option "Tous"
161
+ const liAll = document.createElement('li');
162
+ liAll.innerHTML = `<a data-value="">Tous</a>`;
163
+ liAll.querySelector('a').onclick = e => {
164
+ e.preventDefault();
165
+ document.getElementById(labelId).textContent = "Type";
166
+ onSelect("");
167
+ };
168
+ menu.appendChild(liAll);
169
+
170
+ // Ajoute chaque option
171
+ options.forEach(opt => {
172
+ const li = document.createElement('li');
173
+ li.innerHTML = `<a data-value="${opt}">${opt}</a>`;
174
+ li.querySelector('a').onclick = e => {
175
+ e.preventDefault();
176
+ document.getElementById(labelId).textContent = opt;
177
+ onSelect(opt);
178
+ };
179
+ menu.appendChild(li);
180
+ });
181
+ }
182
+
183
+ function updateFilterLabel(filterType) {
184
+ const selectedCount = selectedFilters[filterType].size;
185
+ const labelElement = document.getElementById(`${filterType}-filter-label`);
186
+
187
+ if (selectedCount === 0) {
188
+ labelElement.textContent = `${filterType} (Tous)`;
189
+ } else {
190
+ labelElement.textContent = `${filterType} (${selectedCount} sélectionné${selectedCount > 1 ? 's' : ''})`;
191
+ }
192
+ }
193
+
194
  /**
195
  * Extrait les données du tableau selon un mapping
196
  * @param {Object} mapping - Mapping des colonnes {columnName: propertyName}
 
328
  });
329
 
330
  setupTableEvents();
331
+ updateSelectedAndDisplayedCount();
332
  }
333
 
 
 
 
 
334
  function setupFilters(data) {
335
+ // Extrait les valeurs uniques
336
  const types = [...new Set(data.map(item => item.Type).filter(Boolean))];
337
  const statuses = [...new Set(data.map(item => item['TDoc Status']).filter(Boolean))];
338
  const agendaItems = [...new Set(data.map(item => item['Agenda item description']).filter(Boolean))];
339
 
340
+ // Type (sélection unique DaisyUI)
341
+ populateDaisyDropdown('doc-type-filter-menu', types, 'doc-type-filter-label', type => {
342
+ selectedType = type;
343
+ applyFilters();
344
+ });
345
 
346
+ // Status (checkbox multiselect)
347
+ populateCheckboxDropdown('status-options', statuses, 'status', 'status-filter-label', selectedStatus);
348
+
349
+ // Agenda (checkbox multiselect)
350
+ populateCheckboxDropdown('agenda-options', agendaItems, 'agenda', 'agenda-filter-label', selectedAgenda);
351
 
352
+ // Initialisation des labels (optionnel)
353
+ document.getElementById('doc-type-filter-label').textContent = 'Type';
354
+ document.getElementById('status-filter-label').textContent = 'Status (Tous)';
355
+ document.getElementById('agenda-filter-label').textContent = 'Agenda Item (Tous)';
356
+ }
357
  /**
358
  * Configure les événements des filtres
359
  */
 
363
  });
364
  }
365
 
366
+ function updateSelectedAndDisplayedCount() {
367
+ // Lignes visibles (après filtrage)
368
+ const rows = document.querySelectorAll('#data-table tbody tr');
369
+ let displayed = 0, selected = 0;
370
+ rows.forEach(row => {
371
+ // display: none signifie caché par le filtre
372
+ if (row.style.display === '' || row.style.display === undefined) {
373
+ displayed++;
374
+ const cb = row.querySelector('.row-checkbox');
375
+ if (cb && cb.checked) selected++;
376
+ }
377
+ });
378
+
379
+ document.getElementById('displayed-count').textContent =
380
+ `${displayed} document${displayed !== 1 ? 's' : ''} affiché${displayed > 1 ? 's' : ''}`;
381
+ document.getElementById('selected-count').textContent =
382
+ `${selected} document${selected !== 1 ? 's' : ''} sélectionné${selected > 1 ? 's' : ''}`;
383
+ }
384
+
385
  /**
386
  * Applique les filtres au tableau
387
  */
388
  function applyFilters() {
 
 
 
 
389
  const rows = document.querySelectorAll('#data-table tbody tr');
 
390
  rows.forEach(row => {
391
+ const typeVal = row.getAttribute('data-type');
392
+ const statusVal = row.getAttribute('data-status');
393
+ const agendaVal = row.getAttribute('data-agenda');
394
 
395
+ const typeMatch = !selectedType || typeVal === selectedType;
396
+ const statusMatch = !selectedStatus.size || selectedStatus.has(statusVal);
397
+ const agendaMatch = !selectedAgenda.size || selectedAgenda.has(agendaVal);
398
 
399
+ row.style.display = (typeMatch && statusMatch && agendaMatch) ? '' : 'none';
 
 
 
 
400
  });
401
+ updateSelectedAndDisplayedCount?.();
402
  }
403
 
404
+
405
  /**
406
  * Configure les événements du tableau
407
  */
408
  function setupTableEvents() {
 
409
  document.getElementById('select-all-checkbox').addEventListener('change', function() {
410
  const checkboxes = document.querySelectorAll('.row-checkbox');
411
  checkboxes.forEach(checkbox => {
412
+ // Ne coche que les visibles
413
+ if (checkbox.closest('tr').style.display === '' || checkbox.closest('tr').style.display === undefined) {
414
+ checkbox.checked = this.checked;
415
+ }
416
  });
417
+ updateSelectedAndDisplayedCount();
418
  });
419
+
420
+ // Listener sur chaque ligne
421
+ const rowCheckboxes = document.querySelectorAll('.row-checkbox');
422
+ rowCheckboxes.forEach(cb => cb.addEventListener('change', updateSelectedAndDisplayedCount));
423
+
424
+ // Compteur initial
425
+ updateSelectedAndDisplayedCount();
426
  }
427
 
428
  /**
updates.md ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Mises à jour à réaliser (Fixes/UI change/API change)
2
+ #### Remarques : Modifs appliqué à la V2 (refonte de l'UI)
3
+ - [x] Permettre la séléction de plusieurs agendas & de status
4
+ - [x] Déployer sous OrganizedProgrammers
5
+ - [x] Afficher nombre de documents qui vont être traités, en bas du dataframe
6
+ - [ ] Afficher une barre de chargement pour savoir ou en est le chargement
7
+ - [ ] Accordéon par catégorie
8
+ - [ ] Permettre de limiter le nombre de catégories
9
+ - [ ] Regénérer les catégories
10
+ - [ ] Bouton pour copier les tous les requirements catégorisés.
11
+ - [ ] Optionnellement, séléctionner avec une checkbox ceux qu'on veut récup
12
+ - [ ] Permettre de séléctionner catégories >> requirements utiliser pour les solutions à générer (ne plus tout utiliser pour l'instant)
13
+ - [ ] Afficher en markdown la description en makrdown
14
+ - [ ] Permettre de manager les solutions générées (supprimer)
15
+ - [ ] BOUTON EXPORT OMARRRRRRRRRRRRRRRRRRRRRRRRRR
16
+ - [ ] Catch quand on a un erreur dans le processing, pour indiquer sur les projects qui utilisent l'API de regarder manuellement