Lucas ARRIESSE
commited on
Commit
·
e4ce2a0
1
Parent(s):
9829679
Rework JS + add settings btn
Browse files- static/index.html +74 -39
- static/js/{script.js → app.js} +21 -7
- static/js/sse.js +1 -3
- static/js/ui-utils.js +39 -15
static/index.html
CHANGED
@@ -22,10 +22,16 @@
|
|
22 |
</div>
|
23 |
|
24 |
<div class="container mx-auto p-6">
|
25 |
-
<
|
|
|
|
|
|
|
|
|
|
|
26 |
<div id="selection-container" class="mb-6">
|
27 |
-
<div class="grid grid-cols-1 md:grid-cols-2 gap-4
|
28 |
-
|
|
|
29 |
<div>
|
30 |
<label for="working-group-select" class="block text-sm font-medium text-gray-700 mb-2">Working
|
31 |
Group</label>
|
@@ -48,41 +54,36 @@
|
|
48 |
</select>
|
49 |
</div>
|
50 |
|
51 |
-
<!-- Meeting -->
|
52 |
<div>
|
53 |
<label for="meeting-select" class="block text-sm font-medium text-gray-700 mb-2">Meeting</label>
|
54 |
-
<
|
55 |
-
<
|
56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
57 |
</div>
|
58 |
</div>
|
59 |
-
|
60 |
-
<!-- Buttons -->
|
61 |
-
<div class="flex flex-col sm:flex-row gap-2">
|
62 |
-
<!-- <button id="get-meetings-btn" class="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600">
|
63 |
-
Get Meetings
|
64 |
-
</button> -->
|
65 |
-
<button id="get-tdocs-btn" class="px-4 py-2 bg-green-500 text-white rounded-md hover:bg-green-600">
|
66 |
-
Get TDocs
|
67 |
-
</button>
|
68 |
-
</div>
|
69 |
</div>
|
70 |
|
71 |
-
<!-- Add an horizontal separation -->
|
72 |
<hr>
|
73 |
<!-- Tab list for subsections -->
|
74 |
<div role="tablist" class="tabs tabs-border tabs-xl" id="tab-container">
|
75 |
-
<a role="tab" class="tab tab-active" id="doc-table-tab"
|
76 |
Documents</a>
|
77 |
-
<a role="tab" class="tab tab-disabled" id="requirements-tab"
|
78 |
<div class="flex items-center gap-1">
|
79 |
<div class="badge badge-neutral badge-outline badge-xs" id="requirements-tab-badge">0</div>
|
80 |
<span>Requirements</span>
|
81 |
</div>
|
82 |
</a>
|
83 |
-
<a role="tab" class="tab tab-disabled" id="solutions-tab"
|
84 |
Solve</a>
|
85 |
-
<a role="tab" class="tab tab-disabled" id="query-tab"
|
86 |
requirements</a>
|
87 |
</div>
|
88 |
|
@@ -155,7 +156,6 @@
|
|
155 |
|
156 |
<!-- Data Table Informations -->
|
157 |
<div class="flex justify-between items-center mb-2 pt-5" id="data-table-info-container">
|
158 |
-
<!-- Left side: buttons -->
|
159 |
<div class="flex gap-2 items-center">
|
160 |
<div class="tooltip" data-tip="Extract requirements from selected documents">
|
161 |
<button id="extract-requirements-btn"
|
@@ -174,7 +174,7 @@
|
|
174 |
</button>
|
175 |
</div>
|
176 |
|
177 |
-
<!--
|
178 |
<div class="flex gap-2 items-center">
|
179 |
<span id="displayed-count" class="text-sm text-gray-700 bg-white rounded px-3 py-1 shadow">
|
180 |
0 total documents
|
@@ -186,7 +186,7 @@
|
|
186 |
</div>
|
187 |
|
188 |
|
189 |
-
<!--
|
190 |
<div id="data-table-container" class="mb-6">
|
191 |
<table id="data-table" class="w-full bg-white rounded-lg shadow overflow-hidden">
|
192 |
<thead class="bg-gray-50">
|
@@ -207,13 +207,12 @@
|
|
207 |
</div>
|
208 |
</div>
|
209 |
|
|
|
210 |
<div id="requirements-tab-contents" class="hidden pt-10">
|
211 |
-
<!-- Requirement list container -->
|
212 |
<div id="requirements-container" class="mb-6">
|
213 |
<div class="flex">
|
214 |
<h2 class="text-2xl font-bold mb-4">Extracted requirement list</h2>
|
215 |
<div class="justify-end pl-5">
|
216 |
-
<!--Copy ALL reqs button-->
|
217 |
<div class="tooltip" data-tip="Copy ALL requirements to clipboard">
|
218 |
<button class="btn btn-square" id="copy-all-reqs-btn" aria-label="Copy">
|
219 |
📋
|
@@ -270,15 +269,6 @@
|
|
270 |
Categorize
|
271 |
</button>
|
272 |
</div>
|
273 |
-
<!-- <div class="flex flex-wrap justify-end items-center gap-6">
|
274 |
-
<div class="tooltip" data-tip="Use Insight Finder's API to generate solutions">
|
275 |
-
<label class="label">
|
276 |
-
<span class="label-text text-base-content">Use insight finder solver</span>
|
277 |
-
</label>
|
278 |
-
<input type="checkbox" class="toggle toggle-primary" id="use-insight-finder-solver"
|
279 |
-
checked="false" />
|
280 |
-
</div>
|
281 |
-
</div> -->
|
282 |
</div>
|
283 |
<div id="categorized-requirements-container pt-10" class="mb-6">
|
284 |
<div class="flex mb-4 pt-10">
|
@@ -333,9 +323,54 @@
|
|
333 |
</div>
|
334 |
</div>
|
335 |
|
336 |
-
|
337 |
-
|
338 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
339 |
</body>
|
340 |
|
341 |
</html>
|
|
|
22 |
</div>
|
23 |
|
24 |
<div class="container mx-auto p-6">
|
25 |
+
<div class="relative flex justify-center items-center p-4">
|
26 |
+
<h1 class="text-3xl font-bold">Requirements Extractor</h1>
|
27 |
+
<button class="absolute right-4 btn btn-sm btn-circle btn-outline" id="settings-btn"
|
28 |
+
onclick="settings_modal.showModal()" title="Settings">🔧</button>
|
29 |
+
</div>
|
30 |
+
|
31 |
<div id="selection-container" class="mb-6">
|
32 |
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
33 |
+
|
34 |
+
<!-- WG selection-->
|
35 |
<div>
|
36 |
<label for="working-group-select" class="block text-sm font-medium text-gray-700 mb-2">Working
|
37 |
Group</label>
|
|
|
54 |
</select>
|
55 |
</div>
|
56 |
|
57 |
+
<!-- Meeting & Action Button Group -->
|
58 |
<div>
|
59 |
<label for="meeting-select" class="block text-sm font-medium text-gray-700 mb-2">Meeting</label>
|
60 |
+
<div class="flex items-end gap-2">
|
61 |
+
<select id="meeting-select" class="w-full p-2 border border-gray-300 rounded-md">
|
62 |
+
<option value="">Select a meeting</option>
|
63 |
+
</select>
|
64 |
+
<button id="get-tdocs-btn"
|
65 |
+
class="px-4 py-2 bg-green-500 text-white rounded-md hover:bg-green-600 flex-shrink-0">
|
66 |
+
Get TDocs
|
67 |
+
</button>
|
68 |
+
</div>
|
69 |
</div>
|
70 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
71 |
</div>
|
72 |
|
|
|
73 |
<hr>
|
74 |
<!-- Tab list for subsections -->
|
75 |
<div role="tablist" class="tabs tabs-border tabs-xl" id="tab-container">
|
76 |
+
<a role="tab" class="tab tab-active" id="doc-table-tab">📝
|
77 |
Documents</a>
|
78 |
+
<a role="tab" class="tab tab-disabled" id="requirements-tab">
|
79 |
<div class="flex items-center gap-1">
|
80 |
<div class="badge badge-neutral badge-outline badge-xs" id="requirements-tab-badge">0</div>
|
81 |
<span>Requirements</span>
|
82 |
</div>
|
83 |
</a>
|
84 |
+
<a role="tab" class="tab tab-disabled" id="solutions-tab">Group and
|
85 |
Solve</a>
|
86 |
+
<a role="tab" class="tab tab-disabled" id="query-tab">🔎 Find relevant
|
87 |
requirements</a>
|
88 |
</div>
|
89 |
|
|
|
156 |
|
157 |
<!-- Data Table Informations -->
|
158 |
<div class="flex justify-between items-center mb-2 pt-5" id="data-table-info-container">
|
|
|
159 |
<div class="flex gap-2 items-center">
|
160 |
<div class="tooltip" data-tip="Extract requirements from selected documents">
|
161 |
<button id="extract-requirements-btn"
|
|
|
174 |
</button>
|
175 |
</div>
|
176 |
|
177 |
+
<!-- document counts -->
|
178 |
<div class="flex gap-2 items-center">
|
179 |
<span id="displayed-count" class="text-sm text-gray-700 bg-white rounded px-3 py-1 shadow">
|
180 |
0 total documents
|
|
|
186 |
</div>
|
187 |
|
188 |
|
189 |
+
<!-- TDoc table -->
|
190 |
<div id="data-table-container" class="mb-6">
|
191 |
<table id="data-table" class="w-full bg-white rounded-lg shadow overflow-hidden">
|
192 |
<thead class="bg-gray-50">
|
|
|
207 |
</div>
|
208 |
</div>
|
209 |
|
210 |
+
<!-- Requirement list tab-->
|
211 |
<div id="requirements-tab-contents" class="hidden pt-10">
|
|
|
212 |
<div id="requirements-container" class="mb-6">
|
213 |
<div class="flex">
|
214 |
<h2 class="text-2xl font-bold mb-4">Extracted requirement list</h2>
|
215 |
<div class="justify-end pl-5">
|
|
|
216 |
<div class="tooltip" data-tip="Copy ALL requirements to clipboard">
|
217 |
<button class="btn btn-square" id="copy-all-reqs-btn" aria-label="Copy">
|
218 |
📋
|
|
|
269 |
Categorize
|
270 |
</button>
|
271 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
272 |
</div>
|
273 |
<div id="categorized-requirements-container pt-10" class="mb-6">
|
274 |
<div class="flex mb-4 pt-10">
|
|
|
323 |
</div>
|
324 |
</div>
|
325 |
|
326 |
+
|
327 |
+
<!--App settings modal container-->
|
328 |
+
<dialog id="settings_modal" class="modal">
|
329 |
+
<div class="modal-box w-11/12 max-w-5xl">
|
330 |
+
<h3 class="text-lg font-bold">Reqxtract settings</h3>
|
331 |
+
<p class="py-4">Enter your LLM provider URL and key to enable using private solution assessment and
|
332 |
+
refining</p>
|
333 |
+
<div class="modal-action">
|
334 |
+
<form method="dialog">
|
335 |
+
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>
|
336 |
+
</form>
|
337 |
+
</div>
|
338 |
+
<h2 class="text-lg font-bold">Private generation settings</h2>
|
339 |
+
<form class="space-y-4">
|
340 |
+
<div>
|
341 |
+
<label for="provider-url" class="block mb-2 text-sm font-medium">LLM provider URL</label>
|
342 |
+
<input id="settings-provider-url" name="provider-url" class="input input-bordered w-full">
|
343 |
+
</div>
|
344 |
+
|
345 |
+
<div>
|
346 |
+
<label for="provider-token" class="block mb-2 text-sm font-medium">LLM provider token</label>
|
347 |
+
<input id="settings-provider-token" name="provider-token" class="input input-bordered w-full"
|
348 |
+
type="password">
|
349 |
+
</div>
|
350 |
+
|
351 |
+
<div>
|
352 |
+
<label for="assessment-rules" class="block mb-2 text-sm font-medium">Assessment rules</label>
|
353 |
+
<textarea id="settings-assessment-rules" name="assessment-rules"
|
354 |
+
class="textarea textarea-bordered w-full h-48"
|
355 |
+
placeholder="Enter your rules here..."></textarea>
|
356 |
+
</div>
|
357 |
+
|
358 |
+
<div>
|
359 |
+
<label for="portfolio-info" class="block mb-2 text-sm font-medium">Portfolio information</label>
|
360 |
+
<textarea id="settings-portfolio" name="portfolio-info"
|
361 |
+
class="textarea textarea-bordered w-full h-48"
|
362 |
+
placeholder="Enter your portfolio info here..."></textarea>
|
363 |
+
</div>
|
364 |
+
</form>
|
365 |
+
<div class="flex">
|
366 |
+
<button class="btn btn-success" id="test-btn">Save config</button>
|
367 |
+
</div>
|
368 |
+
</div>
|
369 |
+
</dialog>
|
370 |
+
|
371 |
+
<script type="module" src="js/sse.js"></script>
|
372 |
+
<script type="module" src="js/ui-utils.js"></script>
|
373 |
+
<script type="module" src="js/app.js"></script>
|
374 |
</body>
|
375 |
|
376 |
</html>
|
static/js/{script.js → app.js}
RENAMED
@@ -1,4 +1,12 @@
|
|
1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
// ==================================== Variables globales ========================================
|
3 |
let requirements = [];
|
4 |
|
@@ -79,6 +87,7 @@ async function getTDocs() {
|
|
79 |
'doc-table-tab-contents',
|
80 |
], true);
|
81 |
|
|
|
82 |
hasRequirementsExtracted = false;
|
83 |
} catch (error) {
|
84 |
console.error('Error while getting TDocs:', error);
|
@@ -139,10 +148,10 @@ function setupFilters(data) {
|
|
139 |
});
|
140 |
|
141 |
// Status (checkbox multiselect)
|
142 |
-
populateCheckboxDropdown('status-options', statuses, 'status', 'status-filter-label', selectedStatus);
|
143 |
|
144 |
// Agenda (checkbox multiselect)
|
145 |
-
populateCheckboxDropdown('agenda-options', agendaItems, 'agenda', 'agenda-filter-label', selectedAgenda);
|
146 |
|
147 |
// Initialisation des labels (optionnel)
|
148 |
document.getElementById('doc-type-filter-label').textContent = 'Type';
|
@@ -180,7 +189,7 @@ function updateSelectedAndDisplayedCount() {
|
|
180 |
/**
|
181 |
* Applique les filtres au tableau
|
182 |
*/
|
183 |
-
function applyFilters() {
|
184 |
const rows = document.querySelectorAll('#data-table tbody tr');
|
185 |
rows.forEach(row => {
|
186 |
const typeVal = row.getAttribute('data-type');
|
@@ -352,7 +361,7 @@ async function extractRequirements() {
|
|
352 |
|
353 |
displayRequirements(requirements);
|
354 |
|
355 |
-
toggleContainersVisibility(['requirements-container', 'query-requirements-container'], true);
|
356 |
toggleContainersVisibility(['categorize-requirements-btn'], true);
|
357 |
|
358 |
// we got some requirements to the other tabs can be enabled
|
@@ -911,7 +920,7 @@ function createSingleAccordionItem(item, index, versionIndex, solutionCriticized
|
|
911 |
|
912 |
// ===================================== Section sources ================================
|
913 |
|
914 |
-
createEl = (tag, properties) => {
|
915 |
const element = document.createElement(tag);
|
916 |
Object.assign(element, properties);
|
917 |
return element;
|
@@ -1099,6 +1108,7 @@ async function workflow(steps = 1) {
|
|
1099 |
// =============================================================================
|
1100 |
|
1101 |
document.addEventListener('DOMContentLoaded', function () {
|
|
|
1102 |
// Événements des boutons principaux
|
1103 |
// document.getElementById('get-meetings-btn').addEventListener('click', getMeetings);
|
1104 |
document.getElementById('working-group-select').addEventListener('change', (ev) => {
|
@@ -1121,7 +1131,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
1121 |
const n_steps = document.getElementById('solution-gen-nsteps').value;
|
1122 |
workflow(n_steps);
|
1123 |
});
|
1124 |
-
|
1125 |
workflow(1);
|
1126 |
});
|
1127 |
});
|
@@ -1139,4 +1149,8 @@ document.getElementById('copy-reqs-btn').addEventListener('click', (ev) => {
|
|
1139 |
copySelectedRequirementsAsMarkdown();
|
1140 |
});
|
1141 |
|
1142 |
-
document.getElementById('copy-all-reqs-btn').addEventListener('click', copyAllRequirementsAsMarkdown);
|
|
|
|
|
|
|
|
|
|
1 |
|
2 |
+
import {
|
3 |
+
toggleElementsEnabled, toggleContainersVisibility, showLoadingOverlay, hideLoadingOverlay, populateSelect,
|
4 |
+
populateCheckboxDropdown, updateCheckboxDropdownLabel, updateSelectedFilters, populateDaisyDropdown, updateFilterLabel,
|
5 |
+
extractTableData, switchTab, enableTabSwitching, debounceAutoCategoryCount,
|
6 |
+
bindTabs, checkCanUsePrivateGen
|
7 |
+
} from "./ui-utils.js";
|
8 |
+
import { postWithSSE } from "./sse.js";
|
9 |
+
|
10 |
// ==================================== Variables globales ========================================
|
11 |
let requirements = [];
|
12 |
|
|
|
87 |
'doc-table-tab-contents',
|
88 |
], true);
|
89 |
|
90 |
+
switchTab('doc-table-tab');
|
91 |
hasRequirementsExtracted = false;
|
92 |
} catch (error) {
|
93 |
console.error('Error while getting TDocs:', error);
|
|
|
148 |
});
|
149 |
|
150 |
// Status (checkbox multiselect)
|
151 |
+
populateCheckboxDropdown('status-options', statuses, 'status', 'status-filter-label', selectedStatus, applyFilters);
|
152 |
|
153 |
// Agenda (checkbox multiselect)
|
154 |
+
populateCheckboxDropdown('agenda-options', agendaItems, 'agenda', 'agenda-filter-label', selectedAgenda, applyFilters);
|
155 |
|
156 |
// Initialisation des labels (optionnel)
|
157 |
document.getElementById('doc-type-filter-label').textContent = 'Type';
|
|
|
189 |
/**
|
190 |
* Applique les filtres au tableau
|
191 |
*/
|
192 |
+
export function applyFilters() {
|
193 |
const rows = document.querySelectorAll('#data-table tbody tr');
|
194 |
rows.forEach(row => {
|
195 |
const typeVal = row.getAttribute('data-type');
|
|
|
361 |
|
362 |
displayRequirements(requirements);
|
363 |
|
364 |
+
// toggleContainersVisibility(['requirements-container', 'query-requirements-container'], true);
|
365 |
toggleContainersVisibility(['categorize-requirements-btn'], true);
|
366 |
|
367 |
// we got some requirements to the other tabs can be enabled
|
|
|
920 |
|
921 |
// ===================================== Section sources ================================
|
922 |
|
923 |
+
const createEl = (tag, properties) => {
|
924 |
const element = document.createElement(tag);
|
925 |
Object.assign(element, properties);
|
926 |
return element;
|
|
|
1108 |
// =============================================================================
|
1109 |
|
1110 |
document.addEventListener('DOMContentLoaded', function () {
|
1111 |
+
bindTabs();
|
1112 |
// Événements des boutons principaux
|
1113 |
// document.getElementById('get-meetings-btn').addEventListener('click', getMeetings);
|
1114 |
document.getElementById('working-group-select').addEventListener('change', (ev) => {
|
|
|
1131 |
const n_steps = document.getElementById('solution-gen-nsteps').value;
|
1132 |
workflow(n_steps);
|
1133 |
});
|
1134 |
+
document.getElementById('get-solutions-step-btn').addEventListener('click', () => {
|
1135 |
workflow(1);
|
1136 |
});
|
1137 |
});
|
|
|
1149 |
copySelectedRequirementsAsMarkdown();
|
1150 |
});
|
1151 |
|
1152 |
+
document.getElementById('copy-all-reqs-btn').addEventListener('click', copyAllRequirementsAsMarkdown);
|
1153 |
+
|
1154 |
+
document.getElementById('test-btn').addEventListener('click', _ => {
|
1155 |
+
console.log(checkCanUsePrivateGen());
|
1156 |
+
});
|
static/js/sse.js
CHANGED
@@ -1,5 +1,3 @@
|
|
1 |
-
// sse-fetch.js
|
2 |
-
|
3 |
/**
|
4 |
* Performs a POST request and handles the response as a Server-Sent Events (SSE) stream.
|
5 |
* The standard EventSource API does not support POST requests, so we use fetch.
|
@@ -10,7 +8,7 @@
|
|
10 |
* @param {(data: object) => void} callbacks.onMessage A function called for each message received.
|
11 |
* @param {(error: Error) => void} callbacks.onError A function called if an error occurs.
|
12 |
*/
|
13 |
-
async function postWithSSE(url, body, callbacks) {
|
14 |
const { onMessage, onError } = callbacks;
|
15 |
|
16 |
try {
|
|
|
|
|
|
|
1 |
/**
|
2 |
* Performs a POST request and handles the response as a Server-Sent Events (SSE) stream.
|
3 |
* The standard EventSource API does not support POST requests, so we use fetch.
|
|
|
8 |
* @param {(data: object) => void} callbacks.onMessage A function called for each message received.
|
9 |
* @param {(error: Error) => void} callbacks.onError A function called if an error occurs.
|
10 |
*/
|
11 |
+
export async function postWithSSE(url, body, callbacks) {
|
12 |
const { onMessage, onError } = callbacks;
|
13 |
|
14 |
try {
|
static/js/ui-utils.js
CHANGED
@@ -7,7 +7,7 @@
|
|
7 |
* @param {string[]} elementIds - Liste des IDs des éléments à activer
|
8 |
* @param {boolean} enabled - true pour activer, false pour désactiver
|
9 |
*/
|
10 |
-
function toggleElementsEnabled(elementIds, enabled = true) {
|
11 |
elementIds.forEach(id => {
|
12 |
const element = document.getElementById(id);
|
13 |
if (element) {
|
@@ -25,7 +25,7 @@ function toggleElementsEnabled(elementIds, enabled = true) {
|
|
25 |
* @param {string[]} containerIds - Liste des IDs des conteneurs à afficher
|
26 |
* @param {boolean} visible - true pour afficher, false pour masquer
|
27 |
*/
|
28 |
-
function toggleContainersVisibility(containerIds, visible = true) {
|
29 |
containerIds.forEach(id => {
|
30 |
const container = document.getElementById(id);
|
31 |
if (container) {
|
@@ -42,7 +42,7 @@ function toggleContainersVisibility(containerIds, visible = true) {
|
|
42 |
* Affiche le loading overlay avec un message personnalisé
|
43 |
* @param {string} message - Message à afficher
|
44 |
*/
|
45 |
-
function showLoadingOverlay(message = 'Chargement en cours...') {
|
46 |
document.getElementById('progress-text').textContent = message;
|
47 |
toggleContainersVisibility(['loading-overlay'], true);
|
48 |
}
|
@@ -50,7 +50,7 @@ function showLoadingOverlay(message = 'Chargement en cours...') {
|
|
50 |
/**
|
51 |
* Masque le loading overlay
|
52 |
*/
|
53 |
-
function hideLoadingOverlay() {
|
54 |
toggleContainersVisibility(['loading-overlay'], false);
|
55 |
}
|
56 |
|
@@ -60,7 +60,7 @@ function hideLoadingOverlay() {
|
|
60 |
* @param {Object} options - Objet avec les options {value: text}
|
61 |
* @param {string} defaultText - Texte par défaut
|
62 |
*/
|
63 |
-
function populateSelect(selectId, options, defaultText = 'Sélectionner...') {
|
64 |
const select = document.getElementById(selectId);
|
65 |
if (select) {
|
66 |
select.innerHTML = `<option value="">${defaultText}</option>`;
|
@@ -73,7 +73,7 @@ function populateSelect(selectId, options, defaultText = 'Sélectionner...') {
|
|
73 |
}
|
74 |
}
|
75 |
|
76 |
-
function populateCheckboxDropdown(optionsContainerId, options, filterType, labelId, selectionSet) {
|
77 |
const container = document.getElementById(optionsContainerId);
|
78 |
container.innerHTML = '';
|
79 |
selectionSet.clear(); // reset all
|
@@ -93,6 +93,7 @@ function populateCheckboxDropdown(optionsContainerId, options, filterType, label
|
|
93 |
} else {
|
94 |
selectionSet.delete(this.value);
|
95 |
}
|
|
|
96 |
// Gestion du label "Tous"
|
97 |
updateCheckboxDropdownLabel(filterType, labelId, selectionSet, options.length);
|
98 |
// Gestion du "Tous" global
|
@@ -100,7 +101,7 @@ function populateCheckboxDropdown(optionsContainerId, options, filterType, label
|
|
100 |
if (allBox && allBox.checked) allBox.checked = false;
|
101 |
// Si plus rien n'est coché, recoche "Tous"
|
102 |
if (selectionSet.size === 0 && allBox) allBox.checked = true;
|
103 |
-
|
104 |
});
|
105 |
container.appendChild(label);
|
106 |
});
|
@@ -124,7 +125,7 @@ function populateCheckboxDropdown(optionsContainerId, options, filterType, label
|
|
124 |
}
|
125 |
}
|
126 |
|
127 |
-
function updateCheckboxDropdownLabel(type, labelId, set, totalCount) {
|
128 |
const label = document.getElementById(labelId);
|
129 |
if (!set.size) {
|
130 |
label.textContent = type.charAt(0).toUpperCase() + type.slice(1) + " (Tous)";
|
@@ -135,7 +136,7 @@ function updateCheckboxDropdownLabel(type, labelId, set, totalCount) {
|
|
135 |
}
|
136 |
}
|
137 |
|
138 |
-
function updateSelectedFilters(filterType, value, isChecked) {
|
139 |
if (isChecked) {
|
140 |
selectedFilters[filterType].add(value);
|
141 |
} else {
|
@@ -143,7 +144,7 @@ function updateSelectedFilters(filterType, value, isChecked) {
|
|
143 |
}
|
144 |
}
|
145 |
|
146 |
-
function populateDaisyDropdown(menuId, options, labelId, onSelect) {
|
147 |
const menu = document.getElementById(menuId);
|
148 |
menu.innerHTML = '';
|
149 |
// Option "Tous"
|
@@ -169,7 +170,7 @@ function populateDaisyDropdown(menuId, options, labelId, onSelect) {
|
|
169 |
});
|
170 |
}
|
171 |
|
172 |
-
function updateFilterLabel(filterType) {
|
173 |
const selectedCount = selectedFilters[filterType].size;
|
174 |
const labelElement = document.getElementById(`${filterType}-filter-label`);
|
175 |
|
@@ -185,7 +186,7 @@ function updateFilterLabel(filterType) {
|
|
185 |
* @param {Object} mapping - Mapping des colonnes {columnName: propertyName}
|
186 |
* @returns {Array} Données extraites
|
187 |
*/
|
188 |
-
function extractTableData(mapping) {
|
189 |
const tbody = document.querySelector('#data-table tbody');
|
190 |
const rows = tbody.querySelectorAll('tr');
|
191 |
const data = [];
|
@@ -222,7 +223,7 @@ const TABS = {
|
|
222 |
* Bascule l'affichage sur le nouveau tab
|
223 |
* @param {*} newTab
|
224 |
*/
|
225 |
-
function switchTab(newTab) {
|
226 |
// Remove active tab style from all tabs
|
227 |
Object.keys(TABS).forEach(tabId => {
|
228 |
const tabElement = document.getElementById(tabId);
|
@@ -249,10 +250,20 @@ function switchTab(newTab) {
|
|
249 |
}
|
250 |
}
|
251 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
252 |
/**
|
253 |
* Bascule l'affichage vers la tab uniquement si les requirements sont
|
254 |
*/
|
255 |
-
function enableTabSwitching() {
|
256 |
Object.keys(TABS).forEach(tabId => {
|
257 |
const tab = document.getElementById(tabId);
|
258 |
if (tab)
|
@@ -264,7 +275,20 @@ function enableTabSwitching() {
|
|
264 |
/**
|
265 |
* Change l'état d'activation du number box de choix de nb de catégories.
|
266 |
*/
|
267 |
-
function debounceAutoCategoryCount(state) {
|
268 |
document.getElementById('category-count').disabled = state;
|
269 |
}
|
270 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
* @param {string[]} elementIds - Liste des IDs des éléments à activer
|
8 |
* @param {boolean} enabled - true pour activer, false pour désactiver
|
9 |
*/
|
10 |
+
export function toggleElementsEnabled(elementIds, enabled = true) {
|
11 |
elementIds.forEach(id => {
|
12 |
const element = document.getElementById(id);
|
13 |
if (element) {
|
|
|
25 |
* @param {string[]} containerIds - Liste des IDs des conteneurs à afficher
|
26 |
* @param {boolean} visible - true pour afficher, false pour masquer
|
27 |
*/
|
28 |
+
export function toggleContainersVisibility(containerIds, visible = true) {
|
29 |
containerIds.forEach(id => {
|
30 |
const container = document.getElementById(id);
|
31 |
if (container) {
|
|
|
42 |
* Affiche le loading overlay avec un message personnalisé
|
43 |
* @param {string} message - Message à afficher
|
44 |
*/
|
45 |
+
export function showLoadingOverlay(message = 'Chargement en cours...') {
|
46 |
document.getElementById('progress-text').textContent = message;
|
47 |
toggleContainersVisibility(['loading-overlay'], true);
|
48 |
}
|
|
|
50 |
/**
|
51 |
* Masque le loading overlay
|
52 |
*/
|
53 |
+
export function hideLoadingOverlay() {
|
54 |
toggleContainersVisibility(['loading-overlay'], false);
|
55 |
}
|
56 |
|
|
|
60 |
* @param {Object} options - Objet avec les options {value: text}
|
61 |
* @param {string} defaultText - Texte par défaut
|
62 |
*/
|
63 |
+
export function populateSelect(selectId, options, defaultText = 'Sélectionner...') {
|
64 |
const select = document.getElementById(selectId);
|
65 |
if (select) {
|
66 |
select.innerHTML = `<option value="">${defaultText}</option>`;
|
|
|
73 |
}
|
74 |
}
|
75 |
|
76 |
+
export function populateCheckboxDropdown(optionsContainerId, options, filterType, labelId, selectionSet, onSelect) {
|
77 |
const container = document.getElementById(optionsContainerId);
|
78 |
container.innerHTML = '';
|
79 |
selectionSet.clear(); // reset all
|
|
|
93 |
} else {
|
94 |
selectionSet.delete(this.value);
|
95 |
}
|
96 |
+
|
97 |
// Gestion du label "Tous"
|
98 |
updateCheckboxDropdownLabel(filterType, labelId, selectionSet, options.length);
|
99 |
// Gestion du "Tous" global
|
|
|
101 |
if (allBox && allBox.checked) allBox.checked = false;
|
102 |
// Si plus rien n'est coché, recoche "Tous"
|
103 |
if (selectionSet.size === 0 && allBox) allBox.checked = true;
|
104 |
+
onSelect?.();
|
105 |
});
|
106 |
container.appendChild(label);
|
107 |
});
|
|
|
125 |
}
|
126 |
}
|
127 |
|
128 |
+
export function updateCheckboxDropdownLabel(type, labelId, set, totalCount) {
|
129 |
const label = document.getElementById(labelId);
|
130 |
if (!set.size) {
|
131 |
label.textContent = type.charAt(0).toUpperCase() + type.slice(1) + " (Tous)";
|
|
|
136 |
}
|
137 |
}
|
138 |
|
139 |
+
export function updateSelectedFilters(filterType, value, isChecked) {
|
140 |
if (isChecked) {
|
141 |
selectedFilters[filterType].add(value);
|
142 |
} else {
|
|
|
144 |
}
|
145 |
}
|
146 |
|
147 |
+
export function populateDaisyDropdown(menuId, options, labelId, onSelect) {
|
148 |
const menu = document.getElementById(menuId);
|
149 |
menu.innerHTML = '';
|
150 |
// Option "Tous"
|
|
|
170 |
});
|
171 |
}
|
172 |
|
173 |
+
export function updateFilterLabel(filterType) {
|
174 |
const selectedCount = selectedFilters[filterType].size;
|
175 |
const labelElement = document.getElementById(`${filterType}-filter-label`);
|
176 |
|
|
|
186 |
* @param {Object} mapping - Mapping des colonnes {columnName: propertyName}
|
187 |
* @returns {Array} Données extraites
|
188 |
*/
|
189 |
+
export function extractTableData(mapping) {
|
190 |
const tbody = document.querySelector('#data-table tbody');
|
191 |
const rows = tbody.querySelectorAll('tr');
|
192 |
const data = [];
|
|
|
223 |
* Bascule l'affichage sur le nouveau tab
|
224 |
* @param {*} newTab
|
225 |
*/
|
226 |
+
export function switchTab(newTab) {
|
227 |
// Remove active tab style from all tabs
|
228 |
Object.keys(TABS).forEach(tabId => {
|
229 |
const tabElement = document.getElementById(tabId);
|
|
|
250 |
}
|
251 |
}
|
252 |
|
253 |
+
/**
|
254 |
+
* Setup les boutons pour basculer vers un autre tab
|
255 |
+
*/
|
256 |
+
export function bindTabs() {
|
257 |
+
Object.keys(TABS).forEach(tabId => {
|
258 |
+
const tabElement = document.getElementById(tabId);
|
259 |
+
tabElement.addEventListener('click', _ => switchTab(tabId));
|
260 |
+
});
|
261 |
+
}
|
262 |
+
|
263 |
/**
|
264 |
* Bascule l'affichage vers la tab uniquement si les requirements sont
|
265 |
*/
|
266 |
+
export function enableTabSwitching() {
|
267 |
Object.keys(TABS).forEach(tabId => {
|
268 |
const tab = document.getElementById(tabId);
|
269 |
if (tab)
|
|
|
275 |
/**
|
276 |
* Change l'état d'activation du number box de choix de nb de catégories.
|
277 |
*/
|
278 |
+
export function debounceAutoCategoryCount(state) {
|
279 |
document.getElementById('category-count').disabled = state;
|
280 |
}
|
281 |
|
282 |
+
|
283 |
+
/**
|
284 |
+
* Vérifie si les paramètres sont bien renseignés pour utiliser la génération privée.
|
285 |
+
*/
|
286 |
+
export function checkCanUsePrivateGen() {
|
287 |
+
const provider_url = document.getElementById('settings-provider-url').value;
|
288 |
+
const provider_token = document.getElementById('settings-provider-token').value;
|
289 |
+
const assessment_rules = document.getElementById('settings-assessment-rules').value;
|
290 |
+
const portfolio_info = document.getElementById('settings-portfolio').value;
|
291 |
+
|
292 |
+
const isEmpty = (str) => (!str?.length);
|
293 |
+
return !isEmpty(provider_url) && !isEmpty(provider_token) && !isEmpty(assessment_rules) && !isEmpty(portfolio_info);
|
294 |
+
}
|