gateway/backend/webparts/part-workflow.js
2025-03-15 19:53:40 +01:00

408 lines
16 KiB
JavaScript

// workflow-module.js
(function() {
// Workflow-Modul-Zustand
let workflowState = {
selectedFiles: [],
selectedAgents: [],
prompt: "",
logs: [],
results: [],
isRunning: false
};
// DOM-Elemente
let availableFiles, selectedFiles, emptyFilesState,
fileInput, uploadBtn, promptInput, promptSelect,
agentList, resetBtn, startWorkflowBtn,
executionLog, resultsContainer,
emptyResultsState, resultsList;
// Initialisierungsfunktion für das Workflow-Modul
function initWorkflowModule(globalState) {
// DOM-Elemente referenzieren
availableFiles = document.getElementById('available-files');
selectedFiles = document.getElementById('selected-files');
emptyFilesState = document.getElementById('empty-files-state');
fileInput = document.getElementById('file-input');
uploadBtn = document.getElementById('upload-btn');
promptInput = document.getElementById('prompt-input');
promptSelect = document.getElementById('prompt-select');
agentList = document.getElementById('agent-list');
resetBtn = document.getElementById('reset-btn');
startWorkflowBtn = document.getElementById('start-workflow-btn');
executionLog = document.getElementById('execution-log');
resultsContainer = document.getElementById('results-container');
emptyResultsState = document.getElementById('empty-results-state');
resultsList = document.getElementById('results-list');
// Event-Listener hinzufügen
setupEventListeners(globalState);
// Initialdaten rendern
renderFiles(globalState);
renderPromptSelector(globalState);
renderAgents(globalState);
}
// Event-Listener einrichten
function setupEventListeners(globalState) {
// Datei-Upload
uploadBtn.addEventListener('click', () => fileInput.click());
fileInput.addEventListener('change',
window.globalUtils.handleFileUpload(fileInput, (newFile) => {
// Nach erfolgreichem Upload
workflowState.selectedFiles.push(newFile);
renderSelectedFiles();
renderFiles(globalState);
})
);
// Prompt-Eingabe
promptInput.addEventListener('input', (e) => {
workflowState.prompt = e.target.value;
});
// Prompt-Auswahl
promptSelect.addEventListener('change', () => {
const selectedPromptId = promptSelect.value;
if (selectedPromptId) {
const selectedPrompt = globalState.availablePrompts.find(p => p.id === selectedPromptId);
if (selectedPrompt) {
promptInput.value = selectedPrompt.content;
workflowState.prompt = selectedPrompt.content;
}
}
});
// Zurücksetzen
resetBtn.addEventListener('click', resetWorkflow);
// Workflow starten
startWorkflowBtn.addEventListener('click', () => runWorkflow(globalState));
}
// Verfügbare Dateien rendern
function renderFiles(globalState) {
availableFiles.innerHTML = '';
globalState.availableFiles.forEach(file => {
const li = document.createElement('li');
li.className = 'file-item';
const isSelected = workflowState.selectedFiles.some(f => f.id === file.id);
li.innerHTML = `
<div class="file-item-name">
<input type="checkbox" id="file-${file.id}" ${isSelected ? 'checked' : ''}>
<label for="file-${file.id}">
<i class="fas ${file.type === 'document' ? 'fa-file-alt' : 'fa-file-image'}"></i>
${file.name}
</label>
</div>
<span class="file-item-info">${file.size || ''}</span>
`;
const checkbox = li.querySelector(`#file-${file.id}`);
checkbox.addEventListener('change', () => {
if (checkbox.checked) {
workflowState.selectedFiles.push(file);
} else {
workflowState.selectedFiles = workflowState.selectedFiles.filter(f => f.id !== file.id);
}
renderSelectedFiles();
});
availableFiles.appendChild(li);
});
}
// Ausgewählte Dateien rendern
function renderSelectedFiles() {
if (workflowState.selectedFiles.length === 0) {
emptyFilesState.style.display = 'flex';
selectedFiles.innerHTML = '';
} else {
emptyFilesState.style.display = 'none';
selectedFiles.innerHTML = '';
workflowState.selectedFiles.forEach(file => {
const li = document.createElement('li');
li.className = 'selected-file-item';
li.innerHTML = `
<div class="file-item-name">
<i class="fas ${file.type === 'document' ? 'fa-file-alt' : 'fa-file-image'}"></i>
${file.name}
</div>
<button class="remove-file-btn" data-id="${file.id}">
<i class="fas fa-times"></i>
</button>
`;
selectedFiles.appendChild(li);
});
// Event-Listener für Entfernen-Buttons
document.querySelectorAll('.remove-file-btn').forEach(btn => {
btn.addEventListener('click', () => {
const fileId = btn.getAttribute('data-id');
workflowState.selectedFiles = workflowState.selectedFiles.filter(f => f.id !== fileId);
renderSelectedFiles();
renderFiles(window.globalState);
});
});
}
}
// Prompt-Auswahlbox rendern
function renderPromptSelector(globalState) {
// Existierende Optionen entfernen (außer der ersten)
while (promptSelect.options.length > 1) {
promptSelect.remove(1);
}
// Prompts aus dem globalen State hinzufügen
globalState.availablePrompts.forEach(prompt => {
const option = document.createElement('option');
option.value = prompt.id;
option.textContent = prompt.content.substring(0, 50) + (prompt.content.length > 50 ? '...' : '');
promptSelect.appendChild(option);
});
}
// Agenten rendern
function renderAgents(globalState) {
agentList.innerHTML = '';
// Agenten für den aktuellen Workspace filtern
const workspaceAgents = globalState.availableAgents.filter(
agent => agent.workspace_id === globalState.currentWorkspace?.id
);
if (workspaceAgents.length === 0) {
agentList.innerHTML = '<div class="empty-state">Keine Agenten im ausgewählten Workspace verfügbar.</div>';
return;
}
workspaceAgents.forEach(agent => {
const li = document.createElement('li');
li.className = 'agent-item';
const isSelected = workflowState.selectedAgents.some(a => a.id === agent.id);
li.innerHTML = `
<div class="agent-header">
<input type="checkbox" id="agent-${agent.id}" class="agent-checkbox" ${isSelected ? 'checked' : ''}>
<label for="agent-${agent.id}" class="agent-name">${agent.name}</label>
</div>
<div class="agent-description">${agent.description || ''}</div>
`;
const checkbox = li.querySelector(`#agent-${agent.id}`);
checkbox.addEventListener('change', () => {
if (checkbox.checked) {
workflowState.selectedAgents.push(agent);
} else {
workflowState.selectedAgents = workflowState.selectedAgents.filter(a => a.id !== agent.id);
}
});
agentList.appendChild(li);
});
}
// Ausführungsprotokoll aktualisieren
function addLogEntry(message, type = 'info') {
const log = {
id: `log_${Date.now()}`,
message,
type,
timestamp: new Date().toISOString()
};
workflowState.logs.push(log);
renderLogs();
return log;
}
// Protokoll rendern
function renderLogs() {
if (workflowState.logs.length === 0) {
executionLog.innerHTML = '<div class="log-empty-state">Workflow noch nicht gestartet. Protokoll wird hier angezeigt.</div>';
return;
}
executionLog.innerHTML = '';
workflowState.logs.forEach(log => {
const logEntry = document.createElement('div');
logEntry.className = 'log-entry';
logEntry.innerHTML = `
<span class="log-time">[${new Date(log.timestamp).toLocaleTimeString()}]</span>
<span class="log-message log-${log.type}">${log.message}</span>
`;
executionLog.appendChild(logEntry);
});
// Zum Ende scrollen
executionLog.scrollTop = executionLog.scrollHeight;
}
// Ergebnis hinzufügen
function addResult(result) {
workflowState.results.push(result);
renderResults();
}
// Ergebnisse rendern
function renderResults() {
if (workflowState.results.length === 0) {
emptyResultsState.style.display = 'flex';
resultsList.style.display = 'none';
return;
}
emptyResultsState.style.display = 'none';
resultsList.style.display = 'block';
resultsList.innerHTML = '';
workflowState.results.forEach(result => {
const resultItem = document.createElement('div');
resultItem.className = 'result-item';
if (result.type === 'text') {
resultItem.innerHTML = `
<div class="result-header">
<div>
<div class="result-title">${result.title}</div>
<div class="result-agent">Erstellt von: ${result.agent_name}</div>
</div>
<button class="save-btn">
<i class="fas fa-save"></i> Speichern
</button>
</div>
<div class="result-content">${result.content}</div>
`;
} else if (result.type === 'chart') {
resultItem.innerHTML = `
<div class="result-header">
<div>
<div class="result-title">${result.title}</div>
<div class="result-agent">Erstellt von: ${result.agent_name}</div>
</div>
<button class="save-btn">
<i class="fas fa-save"></i> Speichern
</button>
</div>
<div class="chart-container">
<div class="chart-title">Umsatzentwicklung nach Quartal</div>
<div>${result.content}</div>
</div>
`;
}
resultsList.appendChild(resultItem);
});
}
// Workflow-Ausführung
async function runWorkflow(globalState) {
if (workflowState.selectedFiles.length === 0 ||
workflowState.selectedAgents.length === 0 ||
!workflowState.prompt.trim()) {
alert("Bitte wählen Sie Dateien und Agenten aus und geben Sie einen Prompt ein.");
return;
}
workflowState.isRunning = true;
workflowState.logs = [];
workflowState.results = [];
startWorkflowBtn.textContent = 'Wird ausgeführt...';
startWorkflowBtn.classList.add('running');
startWorkflowBtn.disabled = true;
renderLogs();
renderResults();
try {
// API-Aufruf an das Backend für Workflow-Ausführung
const workflowData = {
workspace_id: globalState.currentWorkspace.id,
files: workflowState.selectedFiles.map(file => file.id),
agents: workflowState.selectedAgents.map(agent => agent.id),
prompt: workflowState.prompt,
workflow_name: "Workflow " + new Date().toLocaleString()
};
const response = await window.globalUtils.postData('/api/workflow/run', workflowData);
if (response && response.workflow_id) {
const workflowId = response.workflow_id;
addLogEntry("Workflow gestartet", "info");
addLogEntry(`Workflow-ID: ${workflowId}`, "info");
// Polling für Workflow-Status und -Ergebnisse starten
pollWorkflowStatus(workflowId);
} else {
throw new Error("Workflow konnte nicht gestartet werden");
}
} catch (error) {
console.error("Fehler beim Starten des Workflows:", error);
workflowState.isRunning = false;
startWorkflowBtn.textContent = 'Workflow starten';
startWorkflowBtn.classList.remove('running');
startWorkflowBtn.disabled = false;
addLogEntry(`Fehler beim Starten des Workflows: ${error.message}`, "error");
}
}
// Workflow-Status abfragen
async function pollWorkflowStatus(workflowId) {
try {
// Status abrufen
const status = await window.globalUtils.fetchData(`/api/workflow/${workflowId}/status`);
// Logs abrufen und anzeigen
const logs = await window.globalUtils.fetchData(`/api/workflow/${workflowId}/logs`);
workflowState.logs = logs;
renderLogs();
// Status aktualisieren
if (status) {
// Fortschritt anzeigen
addLogEntry(`Fortschritt: ${Math.round(status.progress * 100)}%`, "info");
// Wenn der Workflow abgeschlossen ist, Ergebnisse abrufen
if (status.status === 'completed' || status.status === 'failed') {
const results = await window.globalUtils.fetchData(`/api/workflow/${workflowId}/results`);
workflowState.results = results;
renderResults();
addLogEntry(`Workflow ${status.status === 'completed' ? 'erfolgreich beendet' : 'fehlgeschlagen'}`,
status.status === 'completed' ? "success" : "error");
workflowState.isRunning = false;
startWorkflowBtn.textContent = 'Workflow starten';
startWorkflowBtn.classList.remove('running');
startWorkflowBtn.disabled = false;
return;
}
}
// Weiteres Polling nach einer kurzen Verzögerung
setTimeout(() => pollWorkflowStatus(workflowId), 2000);
} catch (error) {
console.error("Fehler beim Abrufen des Workflow-Status:", error);
addLogEntry(`Fehler beim Abrufen des Workflow-Status: ${error.message}`, "error");
workflowState.isRunning = false;
startWorkflowBtn.textContent = 'Workflow starten';
startWorkflowBtn.classList.remove('running');
startWorkflowBtn.disabled = false;
}
}
// Workflow zurücksetzen
function resetWorkflow() {
workflowState.selectedFiles = [];
workflowState.selectedAgents = [];
promptInput.value = "";
promptSelect.selectedIndex = 0;
workflowState.prompt = "";
renderFiles(window.globalState);
renderSelectedFiles();
renderAgents(window.globalState);
}
// Modul-Initialisierung exportieren
window.initWorkflowModule = initWorkflowModule;
})();