Reqxtract-v2 / static /script.js
om4r932's picture
Change endpoint
58becbd
raw
history blame
35.1 kB
// Variables globales
let requirements = [];
let accordionStates = {};
let formattedRequirements = [];
let categorizedRequirements = [];
let solutionsCriticizedVersions = [];
let isRequirements = false;
// =============================================================================
// FONCTIONS UTILITAIRES POUR LA GESTION DES ÉLÉMENTS
// =============================================================================
/**
* Active/désactive des éléments par leurs IDs
* @param {string[]} elementIds - Liste des IDs des éléments à activer
* @param {boolean} enabled - true pour activer, false pour désactiver
*/
function toggleElementsEnabled(elementIds, enabled = true) {
elementIds.forEach(id => {
const element = document.getElementById(id);
if (element) {
if (enabled) {
element.removeAttribute('disabled');
} else {
element.setAttribute('disabled', 'true');
}
}
});
}
/**
* Affiche/masque des conteneurs par leurs IDs
* @param {string[]} containerIds - Liste des IDs des conteneurs à afficher
* @param {boolean} visible - true pour afficher, false pour masquer
*/
function toggleContainersVisibility(containerIds, visible = true) {
containerIds.forEach(id => {
const container = document.getElementById(id);
if (container) {
if (visible) {
container.classList.remove('hidden');
} else {
container.classList.add('hidden');
}
}
});
}
/**
* Affiche le loading overlay avec un message personnalisé
* @param {string} message - Message à afficher
*/
function showLoadingOverlay(message = 'Chargement en cours...') {
document.getElementById('progress-text').textContent = message;
toggleContainersVisibility(['loading-overlay'], true);
}
/**
* Masque le loading overlay
*/
function hideLoadingOverlay() {
toggleContainersVisibility(['loading-overlay'], false);
}
/**
* Réinitialise un select et ajoute des options
* @param {string} selectId - ID du select
* @param {Object} options - Objet avec les options {value: text}
* @param {string} defaultText - Texte par défaut
*/
function populateSelect(selectId, options, defaultText = 'Sélectionner...') {
const select = document.getElementById(selectId);
if (select) {
select.innerHTML = `<option value="">${defaultText}</option>`;
Object.entries(options).forEach(([text, value]) => {
const option = document.createElement('option');
option.value = value;
option.textContent = text;
select.appendChild(option);
});
}
}
/**
* Extrait les données du tableau selon un mapping
* @param {Object} mapping - Mapping des colonnes {columnName: propertyName}
* @returns {Array} Données extraites
*/
function extractTableData(mapping) {
const tbody = document.querySelector('#data-table tbody');
const rows = tbody.querySelectorAll('tr');
const data = [];
rows.forEach(row => {
const checkboxes = row.querySelectorAll('input[type="checkbox"]:checked');
if (checkboxes.length > 0) {
const rowData = {};
Object.entries(mapping).forEach(([columnName, propertyName]) => {
const cell = row.querySelector(`td[data-column="${columnName}"]`);
if (cell) {
if(columnName == "URL") {
rowData[propertyName] = cell.querySelector('a').getAttribute('href');
} else {
rowData[propertyName] = cell.textContent.trim();
}
}
});
data.push(rowData);
}
});
return data;
}
// =============================================================================
// FONCTIONS MÉTIER
// =============================================================================
/**
* Récupère la liste des meetings pour un working group
*/
async function getMeetings() {
const workingGroup = document.getElementById('working-group-select').value;
if (!workingGroup) return;
showLoadingOverlay('Récupération des meetings...');
toggleElementsEnabled(['get-meetings-btn'], false);
try {
const response = await fetch('/get_meetings', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ working_group: workingGroup })
});
const data = await response.json();
populateSelect('meeting-select', data.meetings, 'Select a meeting');
toggleContainersVisibility(['meeting-container'], true);
} catch (error) {
console.error('Erreur lors de la récupération des meetings:', error);
alert('Erreur lors de la récupération des meetings');
} finally {
hideLoadingOverlay();
toggleElementsEnabled(['get-meetings-btn'], true);
}
}
/**
* Récupère la liste des TDocs pour un meeting
*/
async function getTDocs() {
const workingGroup = document.getElementById('working-group-select').value;
const meeting = document.getElementById('meeting-select').value;
if (!workingGroup || !meeting) return;
showLoadingOverlay('Récupération de la liste des TDocs...');
toggleElementsEnabled(['get-tdocs-btn'], false);
try {
const response = await fetch('/get_dataframe', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ working_group: workingGroup, meeting: meeting })
});
const data = await response.json();
populateDataTable(data.data);
setupFilters(data.data);
toggleContainersVisibility([
'filters-container',
'action-buttons-container',
'data-table-container'
], true);
isRequirements = false;
} catch (error) {
console.error('Erreur lors de la récupération des TDocs:', error);
alert('Erreur lors de la récupération des TDocs');
} finally {
hideLoadingOverlay();
toggleElementsEnabled(['get-tdocs-btn'], true);
}
}
/**
* Remplit le tableau de données
* @param {Array} data - Données à afficher
*/
function populateDataTable(data) {
const tbody = document.querySelector('#data-table tbody');
tbody.innerHTML = '';
data.forEach(row => {
const tr = document.createElement('tr');
tr.setAttribute('data-type', row.Type || '');
tr.setAttribute('data-status', row['TDoc Status'] || '');
tr.setAttribute('data-agenda', row['Agenda item description'] || '');
tr.innerHTML = `
<td class="px-4 py-2">
<input type="checkbox" class="row-checkbox">
</td>
<td class="px-4 py-2" data-column="TDoc">${row.TDoc || ''}</td>
<td class="px-4 py-2" data-column="Title">${row.Title || ''}</td>
<td class="px-4 py-2" data-column="Type">${row.Type || ''}</td>
<td class="px-4 py-2" data-column="Status">${row['TDoc Status'] || ''}</td>
<td class="px-4 py-2" data-column="Agenda">${row['Agenda item description'] || ''}</td>
<td class="px-4 py-2" data-column="URL">
<a href="${row.URL || '#'}" target="_blank" class="text-blue-500 hover:underline">
${row.URL ? 'Lien' : 'N/A'}
</a>
</td>
`;
tbody.appendChild(tr);
});
setupTableEvents();
}
/**
* Configure les filtres basés sur les données
* @param {Array} data - Données pour extraire les valeurs uniques
*/
function setupFilters(data) {
const types = [...new Set(data.map(item => item.Type).filter(Boolean))];
const statuses = [...new Set(data.map(item => item['TDoc Status']).filter(Boolean))];
const agendaItems = [...new Set(data.map(item => item['Agenda item description']).filter(Boolean))];
populateSelect('doc-type-filter', Object.fromEntries(types.map(t => [t, t])), 'Tous');
populateSelect('doc-status-filter', Object.fromEntries(statuses.map(s => [s, s])), 'Tous');
populateSelect('agenda-item-filter', Object.fromEntries(agendaItems.map(a => [a, a])), 'Tous');
setupFilterEvents();
}
/**
* Configure les événements des filtres
*/
function setupFilterEvents() {
['doc-type-filter', 'doc-status-filter', 'agenda-item-filter'].forEach(filterId => {
document.getElementById(filterId).addEventListener('change', applyFilters);
});
}
/**
* Applique les filtres au tableau
*/
function applyFilters() {
const typeFilter = document.getElementById('doc-type-filter').value;
const statusFilter = document.getElementById('doc-status-filter').value;
const agendaFilter = document.getElementById('agenda-item-filter').value;
const rows = document.querySelectorAll('#data-table tbody tr');
rows.forEach(row => {
const type = row.getAttribute('data-type');
const status = row.getAttribute('data-status');
const agenda = row.getAttribute('data-agenda');
const typeMatch = !typeFilter || type === typeFilter;
const statusMatch = !statusFilter || status === statusFilter;
const agendaMatch = !agendaFilter || agenda === agendaFilter;
if (typeMatch && statusMatch && agendaMatch) {
row.style.display = '';
} else {
row.style.display = 'none';
}
});
}
/**
* Configure les événements du tableau
*/
function setupTableEvents() {
// Checkbox "Sélectionner tout"
document.getElementById('select-all-checkbox').addEventListener('change', function() {
const checkboxes = document.querySelectorAll('.row-checkbox');
checkboxes.forEach(checkbox => {
if(checkbox.parentElement.parentElement.style.display != "none"){checkbox.checked = this.checked;}
});
});
}
/**
* Télécharge les TDocs sélectionnés
*/
async function downloadTDocs() {
showLoadingOverlay('Téléchargement des TDocs en cours...');
toggleElementsEnabled(['download-tdocs-btn', 'extract-requirements-btn'], false);
try {
// Extraire les données du tableau avec TDoc et URL
const selectedData = extractTableData({ 'TDoc': 'document', 'URL': 'url' });
if (selectedData.length === 0) {
alert('Veuillez sélectionner au moins un document');
return;
}
// Transformer au format requis: [{tdoc_id: url}, ...]
const documents = selectedData.map(obj => obj.document)
const response = await fetch('/download_tdocs', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ documents: documents })
});
const blob = await response.blob();
downloadBlob(blob, generateDownloadFilename());
} catch (error) {
console.error('Erreur lors du téléchargement:', error);
alert('Erreur lors du téléchargement des TDocs');
} finally {
hideLoadingOverlay();
toggleElementsEnabled(['download-tdocs-btn', 'extract-requirements-btn'], true);
}
}
/**
* Génère un nom de fichier pour le téléchargement
* @returns {string} Nom du fichier
*/
function generateDownloadFilename() {
let filename = document.getElementById('meeting-select').value || 'documents';
const agendaItem = document.getElementById('agenda-item-filter').value;
const docStatus = document.getElementById('doc-status-filter').value;
const docType = document.getElementById('doc-type-filter').value;
if (agendaItem && agendaItem !== 'Tous') {
filename += `_${agendaItem}`;
}
if (docStatus && docStatus !== 'Tous') {
filename += `_${docStatus}`;
}
if (docType && docType !== 'Tous') {
filename = `${docType}_${filename}`;
}
if (isRequirements) {
filename = `requirements_${filename}`;
}
return `${filename}.zip`;
}
/**
* Télécharge un blob
* @param {Blob} blob - Blob à télécharger
* @param {string} filename - Nom du fichier
*/
function downloadBlob(blob, filename) {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
}
/**
* Extrait les requirements des documents sélectionnés
*/
async function extractRequirements() {
const selectedData = extractTableData({ 'TDoc': 'document', 'URL': 'url' });
if (selectedData.length === 0) {
alert('Veuillez sélectionner au moins un document');
return;
}
showLoadingOverlay('Extraction des requirements en cours...');
toggleElementsEnabled(['extract-requirements-btn'], false);
try {
const response = await fetch('/generate_requirements', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ documents: selectedData })
});
const data = await response.json();
requirements = data.requirements;
let req_id = 0;
data.requirements.forEach(obj => {
obj.requirements.forEach(req => {
formattedRequirements.push({
req_id,
"document": obj.document,
"context": obj.context,
"requirement": req
})
req_id++;
})
})
displayRequirements(requirements);
toggleContainersVisibility(['requirements-container', 'query-requirements-container'], true);
toggleContainersVisibility(['find-requirements-btn', 'categorize-requirements-btn', 'get-solutions-btn', 'get-solutions-step-btn'], true);
isRequirements = true;
} catch (error) {
console.error('Erreur lors de l\'extraction des requirements:', error);
alert('Erreur lors de l\'extraction des requirements');
} finally {
hideLoadingOverlay();
toggleElementsEnabled(['extract-requirements-btn'], true);
}
}
/**
* Affiche les requirements
* @param {Array} requirementsData - Données des requirements
*/
function displayRequirements(requirementsData) {
const container = document.getElementById('requirements-list');
container.innerHTML = '';
requirementsData.forEach((docReq, docIndex) => {
const docDiv = document.createElement('div');
docDiv.className = 'mb-6 p-4 border border-gray-200 rounded-lg bg-white';
docDiv.innerHTML = `
<h3 class="text-lg font-semibold mb-2">${docReq.document}</h3>
<p class="text-gray-600 mb-3">${docReq.context}</p>
<ul class="list-disc list-inside space-y-1">
${docReq.requirements.map((req, reqIndex) =>
`<li class="text-sm" data-req-id="${docIndex}-${reqIndex}">${req}</li>`
).join('')}
</ul>
`;
container.appendChild(docDiv);
});
}
/**
* Catégorise les requirements
*/
async function categorizeRequirements(max_categories) {
if (!formattedRequirements || formattedRequirements.length === 0) {
alert('Aucun requirement à catégoriser');
return;
}
showLoadingOverlay('Catégorisation des requirements en cours...');
toggleElementsEnabled(['categorize-requirements-btn'], false);
try {
const response = await fetch('https://organizedprogrammers-reqxtract-api.hf.space/reqs/categorize_requirements', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ requirements: formattedRequirements, "max_n_categories": max_categories })
});
const data = await response.json();
categorizedRequirements = data;
displayCategorizedRequirements(categorizedRequirements.categories);
// Masquer le container de query et afficher les catégories et boutons solutions
toggleContainersVisibility(['query-requirements-container'], false);
toggleContainersVisibility(['categorized-requirements-container', 'solutions-action-buttons-container'], true);
} catch (error) {
console.error('Erreur lors de la catégorisation des requirements:', error);
alert('Erreur lors de la catégorisation des requirements');
} finally {
hideLoadingOverlay();
toggleElementsEnabled(['categorize-requirements-btn'], true);
}
}
/**
* Affiche les requirements catégorisés
* @param {Array} categorizedData - Données des requirements catégorisés
*/
function displayCategorizedRequirements(categorizedData) {
const container = document.getElementById('categorized-requirements-list');
container.innerHTML = '';
categorizedData.forEach((category, categoryIndex) => {
const categoryDiv = document.createElement('div');
categoryDiv.className = 'mb-6 p-4 border border-gray-200 rounded-lg bg-white';
categoryDiv.innerHTML = `
<h3 class="text-lg font-semibold mb-2 text-blue-600">${category.title}</h3>
<div class="space-y-2">
${category.requirements.map((req, reqIndex) =>
`<div class="p-2 bg-gray-50 rounded border-l-4 border-blue-400" data-cat-req-id="${categoryIndex}-${reqIndex}">
<div class="text-sm font-medium text-gray-700">${req.document}</div>
<div class="text-sm text-gray-600">${req.requirement}</div>
</div>`
).join('')}
</div>
`;
container.appendChild(categoryDiv);
});
}
async function searchRequirements() {
const query = document.getElementById('query-input').value.trim();
if (!query) {
alert('Veuillez entrer une requête de recherche');
return;
}
if (!formattedRequirements || formattedRequirements.length === 0) {
alert('Aucun requirement disponible pour la recherche');
return;
}
showLoadingOverlay('Recherche en cours...');
toggleElementsEnabled(['search-requirements-btn'], false);
try {
// Préparer les requirements pour la recherche
const response = await fetch('/get_reqs_from_query', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: query,
requirements: formattedRequirements
})
});
const data = await response.json();
displaySearchResults(data.requirements);
} catch (error) {
console.error('Erreur lors de la recherche:', error);
alert('Erreur lors de la recherche des requirements');
} finally {
hideLoadingOverlay();
toggleElementsEnabled(['search-requirements-btn'], true);
}
}
/**
* Affiche les résultats de recherche
* @param {Array} results - Résultats de la recherche
*/
function displaySearchResults(results) {
const container = document.getElementById('query-results');
container.innerHTML = '';
if (results.length === 0) {
container.innerHTML = '<p class="text-gray-500">Aucun résultat trouvé pour cette requête.</p>';
return;
}
const resultsDiv = document.createElement('div');
resultsDiv.className = 'space-y-3';
results.forEach((result, index) => {
const resultDiv = document.createElement('div');
resultDiv.className = 'p-3 bg-blue-50 border border-blue-200 rounded-lg';
resultDiv.innerHTML = `
<div class="text-sm font-medium text-blue-800">${result.document}</div>
<div class="text-sm text-gray-600 mb-1">${result.context}</div>
<div class="text-sm">${result.requirement}</div>
`;
resultsDiv.appendChild(resultDiv);
});
container.appendChild(resultsDiv);
}
function createSolutionAccordion(solutionCriticizedHistory, containerId, versionIndex = 0, categoryIndex = null) {
const container = document.getElementById(containerId);
if (!container) {
console.error(`Container with ID "${containerId}" not found`);
return;
}
// Si categoryIndex est spécifié, ne mettre à jour que cette catégorie
if (categoryIndex !== null) {
updateSingleAccordion(solutionCriticizedHistory, containerId, versionIndex, categoryIndex);
return;
}
// Vider le container seulement si on recrée tout
container.innerHTML = '';
// Récupérer les données de la version actuelle directement
const currentVersionData = solutionCriticizedHistory[versionIndex];
// Créer l'accordéon principal
const accordion = document.createElement('div');
accordion.className = 'space-y-2';
accordion.id = 'main-accordion';
// Afficher seulement les solutions de la version actuelle
currentVersionData.critiques.forEach((item, index) => {
createSingleAccordionItem(item, index, versionIndex, solutionCriticizedHistory, accordion);
});
// Ajouter l'accordéon au container
container.appendChild(accordion);
}
function createSingleAccordionItem(item, index, versionIndex, solutionCriticizedHistory, accordion) {
const solution = item.solution;
const criticism = item.criticism;
// Récupérer le titre de la catégorie
const categoryTitle = document.querySelector(`#category-${solution['Category_Id']} h2`)?.textContent || `Catégorie ${solution['Category_Id'] + 1}`;
// Container pour chaque solution
const solutionCard = document.createElement('div');
solutionCard.className = 'border border-gray-200 rounded-md shadow-sm';
solutionCard.id = `accordion-item-${index}`;
// En-tête de l'accordéon avec navigation
const header = document.createElement('div');
header.className = 'bg-gray-50 px-4 py-2 cursor-pointer hover:bg-gray-100 transition-colors duration-200';
const currentVersion = versionIndex + 1;
const totalVersions = solutionCriticizedHistory.length;
header.innerHTML = `
<div class="flex justify-between items-center">
<div class="flex items-center space-x-3">
<h3 class="text-sm font-semibold text-gray-800">${categoryTitle}</h3>
<div class="flex items-center space-x-2 bg-white px-3 py-1 rounded-full border">
<button class="version-btn-left w-6 h-6 flex items-center justify-center rounded-full hover:bg-gray-100 transition-colors ${currentVersion === 1 ? 'opacity-50 cursor-not-allowed' : ''}"
data-solution-index="${solution['Category_Id']}"
${currentVersion === 1 ? 'disabled' : ''}>
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
</svg>
</button>
<span class="text-xs font-medium text-gray-600 min-w-[60px] text-center version-indicator">Version ${currentVersion}</span>
<button class="version-btn-right w-6 h-6 flex items-center justify-center rounded-full hover:bg-gray-100 transition-colors ${currentVersion === totalVersions ? 'opacity-50 cursor-not-allowed' : ''}"
data-solution-index="${solution['Category_Id']}"
${currentVersion === totalVersions ? 'disabled' : ''}>
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
</svg>
</button>
</div>
</div>
<svg class="w-4 h-4 transform transition-transform duration-200 accordion-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
</svg>
</div>
`;
// Contenu de l'accordéon
const content = document.createElement('div');
content.className = `accordion-content px-4 py-3 space-y-3`;
content.id = `content-${solution['Category_Id']}`;
// Vérifier l'état d'ouverture précédent
const isOpen = accordionStates[solution['Category_Id']] || false;
if (!isOpen) {
content.classList.add('hidden');
} else {
header.querySelector('.accordion-icon').style.transform = 'rotate(180deg)';
}
// Section Problem Description
const problemSection = document.createElement('div');
problemSection.className = 'bg-red-50 border-l-2 border-red-400 p-3 rounded-r-md';
problemSection.innerHTML = `
<h4 class="text-sm font-semibold text-red-800 mb-2 flex items-center">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"></path>
</svg>
Problem Description
</h4>
<p class="text-xs text-gray-700 leading-relaxed">${solution['Problem Description'] || 'Aucune description du problème disponible.'}</p>
`;
// Section Solution Description
const solutionSection = document.createElement('div');
solutionSection.className = 'bg-green-50 border-l-2 border-green-400 p-3 rounded-r-md';
solutionSection.innerHTML = `
<h4 class="text-sm font-semibold text-green-800 mb-2 flex items-center">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
</svg>
Solution Description
</h4>
<p class="text-xs text-gray-700 leading-relaxed">${solution['Solution Description'] || 'Aucune description de solution disponible.'}</p>
`;
// Section Critique
const critiqueSection = document.createElement('div');
critiqueSection.className = 'bg-yellow-50 border-l-2 border-yellow-400 p-3 rounded-r-md';
let critiqueContent = `
<h4 class="text-sm font-semibold text-yellow-800 mb-2 flex items-center">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path>
</svg>
Critique & Analysis
</h4>
`;
// Sous-sections de critique
if (criticism.technical_challenges && criticism.technical_challenges.length > 0) {
critiqueContent += `
<div class="mb-2">
<h5 class="text-xs font-semibold text-yellow-700 mb-1">Technical Challenges:</h5>
<ul class="list-disc list-inside space-y-0.5 text-xs text-gray-700 ml-3">
${criticism.technical_challenges.map(challenge => `<li>${challenge}</li>`).join('')}
</ul>
</div>
`;
}
if (criticism.weaknesses && criticism.weaknesses.length > 0) {
critiqueContent += `
<div class="mb-2">
<h5 class="text-xs font-semibold text-yellow-700 mb-1">Weaknesses:</h5>
<ul class="list-disc list-inside space-y-0.5 text-xs text-gray-700 ml-3">
${criticism.weaknesses.map(weakness => `<li>${weakness}</li>`).join('')}
</ul>
</div>
`;
}
if (criticism.limitations && criticism.limitations.length > 0) {
critiqueContent += `
<div class="mb-2">
<h5 class="text-xs font-semibent text-yellow-700 mb-1">Limitations:</h5>
<ul class="list-disc list-inside space-y-0.5 text-xs text-gray-700 ml-3">
${criticism.limitations.map(limitation => `<li>${limitation}</li>`).join('')}
</ul>
</div>
`;
}
critiqueSection.innerHTML = critiqueContent;
// Ajouter les sections au contenu
content.appendChild(problemSection);
content.appendChild(solutionSection);
content.appendChild(critiqueSection);
// Événement de clic pour l'accordéon (exclure les boutons de navigation)
header.addEventListener('click', (e) => {
// Ne pas déclencher l'accordéon si on clique sur les boutons de navigation
if (e.target.closest('.version-btn-left') || e.target.closest('.version-btn-right')) {
return;
}
const icon = header.querySelector('.accordion-icon');
const isCurrentlyOpen = !content.classList.contains('hidden');
if (isCurrentlyOpen) {
content.classList.add('hidden');
icon.style.transform = 'rotate(0deg)';
accordionStates[solution['Category_Id']] = false;
} else {
content.classList.remove('hidden');
icon.style.transform = 'rotate(180deg)';
accordionStates[solution['Category_Id']] = true;
}
});
// Événements de navigation pour cette catégorie spécifique
header.querySelector('.version-btn-left')?.addEventListener('click', (e) => {
e.stopPropagation();
updateSingleAccordion(solutionCriticizedHistory, 'accordion-container', versionIndex - 1, index);
});
header.querySelector('.version-btn-right')?.addEventListener('click', (e) => {
e.stopPropagation();
updateSingleAccordion(solutionCriticizedHistory, 'accordion-container', versionIndex + 1, index);
});
// Assembler la carte de solution
solutionCard.appendChild(header);
solutionCard.appendChild(content);
accordion.appendChild(solutionCard);
}
function updateSingleAccordion(solutionCriticizedHistory, containerId, newVersionIndex, categoryIndex) {
// Vérifier les limites de version
if (newVersionIndex < 0 || newVersionIndex >= solutionCriticizedHistory.length) {
return;
}
const accordionItem = document.getElementById(`accordion-item-${categoryIndex}`);
if (!accordionItem) return;
const newData = solutionCriticizedHistory[newVersionIndex];
const newItem = newData.critiques[categoryIndex];
if (!newItem) return;
// Mettre à jour le contenu de cette catégorie spécifique
const tempContainer = document.createElement('div');
createSingleAccordionItem(newItem, categoryIndex, newVersionIndex, solutionCriticizedHistory, tempContainer);
// Remplacer l'ancien item par le nouveau
accordionItem.parentNode.replaceChild(tempContainer.firstChild, accordionItem);
}
// Fonction d'initialisation simplifiée
function initializeSolutionAccordion(solutionCriticizedHistory, containerId, startVersion = 0) {
// Réinitialiser les états d'accordéon
accordionStates = {};
createSolutionAccordion(solutionCriticizedHistory, containerId, startVersion);
document.getElementById(containerId).classList.remove('hidden')
}
async function generateSolutions(){
let response = await fetch('https://organizedprogrammers-reqxtract-api.hf.space/solution/search_solutions_gemini', {method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify(categorizedRequirements)})
let responseObj = await response.json()
return responseObj;
}
async function generateCriticisms(solutions){
let response = await fetch('https://organizedprogrammers-reqxtract-api.hf.space/solution/criticize_solution', {method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify(solutions)})
let responseObj = await response.json()
solutionsCriticizedVersions.push(responseObj)
}
async function refineSolutions(critiques){
let response = await fetch('https://organizedprogrammers-reqxtract-api.hf.space/solution/refine_solution', {method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify(critiques)})
let responseObj = await response.json()
await generateCriticisms(responseObj)
}
async function workflow(steps = 1){
let soluce;
showLoadingOverlay('Génération des solutions & critiques ....');
for(let step = 1; step <= steps; step++){
if(solutionsCriticizedVersions.length == 0){
soluce = await generateSolutions();
await generateCriticisms(soluce)
} else {
let prevSoluce = solutionsCriticizedVersions[solutionsCriticizedVersions.length - 1];
await refineSolutions(prevSoluce)
}
}
hideLoadingOverlay();
initializeSolutionAccordion(solutionsCriticizedVersions, "solutions-list")
}
// =============================================================================
// INITIALISATION DES ÉVÉNEMENTS
// =============================================================================
document.addEventListener('DOMContentLoaded', function() {
// Événements des boutons principaux
document.getElementById('get-meetings-btn').addEventListener('click', getMeetings);
document.getElementById('get-tdocs-btn').addEventListener('click', getTDocs);
document.getElementById('download-tdocs-btn').addEventListener('click', downloadTDocs);
document.getElementById('extract-requirements-btn').addEventListener('click', extractRequirements);
document.getElementById('categorize-requirements-btn').addEventListener('click', ()=>{categorizeRequirements(8)});
// Événement pour le bouton "Find Requirements"
document.getElementById('find-requirements-btn').addEventListener('click', function() {
// Afficher le container de requête
toggleContainersVisibility(['query-requirements-container'], true);
// Scroll vers le container de requête
document.getElementById('query-requirements-container').scrollIntoView({ behavior: 'smooth' });
});
// Événement pour la recherche
document.getElementById('search-requirements-btn').addEventListener('click', searchRequirements);
// Événements pour les boutons de solutions (à implémenter plus tard)
document.getElementById('get-solutions-btn').addEventListener('click', () => {
alert('Fonctionnalité à implémenter');
});
document.getElementById('get-solutions-step-btn').addEventListener('click', () => {
workflow();
});
});