gateway/frontend/script.js
2025-03-14 00:29:45 +01:00

654 lines
No EOL
24 KiB
JavaScript

document.addEventListener('DOMContentLoaded', function() {
// Backend API Basis-URL
const API_BASE_URL = 'http://localhost:8000';
// Status-Management
let state = {
workspaces: [],
files: [],
currentWorkspace: null,
selectedFiles: [],
selectedAgents: [],
prompt: "",
logs: [],
results: [],
isRunning: false,
currentWorkflowId: null
};
let activeView = 'workflow'; // Standard-Ansicht beim Start
// DOM-Elemente für Menüpunkte
const workflowLink = document.querySelector('a[href="#workflow"]').parentElement;
const dataLink = document.querySelector('a[href="#data"]').parentElement;
const promptsLink = document.querySelector('a[href="#prompts"]').parentElement;
const agentsLink = document.querySelector('a[href="#agents"]').parentElement;
// DOM-Elemente für die Ansichten
const workflowView = document.querySelector('.workflow-container');
const dataView = document.getElementById('data-view');
const promptsView = document.getElementById('prompts-view');
const agentsView = document.getElementById('agents-view');
// DOM-Elemente
const workspaceList = document.getElementById('workspace-list');
const availableFiles = document.getElementById('available-files');
const selectedFiles = document.getElementById('selected-files');
const emptyFilesState = document.getElementById('empty-files-state');
const fileInput = document.getElementById('file-input');
const uploadBtn = document.getElementById('upload-btn');
const promptInput = document.getElementById('prompt-input');
const agentList = document.getElementById('agent-list');
const resetBtn = document.getElementById('reset-btn');
const startWorkflowBtn = document.getElementById('start-workflow-btn');
const executionLog = document.getElementById('execution-log');
const resultsContainer = document.getElementById('results-container');
const emptyResultsState = document.getElementById('empty-results-state');
const resultsList = document.getElementById('results-list');
// API-Funktionen
async function fetchWorkspaces() {
try {
const response = await fetch(`${API_BASE_URL}/workspaces`);
if (!response.ok) throw new Error('Fehler beim Abrufen der Workspaces');
const workspaces = await response.json();
state.workspaces = workspaces;
if (workspaces.length > 0) {
state.currentWorkspace = workspaces[0];
}
renderWorkspaces();
return workspaces;
} catch (error) {
console.error('Fehler beim Abrufen der Workspaces:', error);
return [];
}
}
async function fetchFiles() {
try {
const response = await fetch(`${API_BASE_URL}/files`);
if (!response.ok) throw new Error('Fehler beim Abrufen der Dateien');
const files = await response.json();
state.files = files;
renderFiles();
return files;
} catch (error) {
console.error('Fehler beim Abrufen der Dateien:', error);
return [];
}
}
async function uploadFile(file) {
try {
const formData = new FormData();
formData.append('file', file);
const response = await fetch(`${API_BASE_URL}/files/upload`, {
method: 'POST',
body: formData
});
if (!response.ok) throw new Error('Fehler beim Hochladen der Datei');
const newFile = await response.json();
return newFile;
} catch (error) {
console.error('Fehler beim Hochladen der Datei:', error);
return null;
}
}
async function runWorkflowAPI(workflowData) {
try {
const response = await fetch(`${API_BASE_URL}/workflow/run`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(workflowData)
});
if (!response.ok) throw new Error('Fehler beim Starten des Workflows');
const result = await response.json();
return result;
} catch (error) {
console.error('Fehler beim Starten des Workflows:', error);
return null;
}
}
async function fetchWorkflowStatus(workflowId) {
try {
const response = await fetch(`${API_BASE_URL}/workflow/${workflowId}/status`);
if (!response.ok) throw new Error('Fehler beim Abrufen des Workflow-Status');
const status = await response.json();
return status;
} catch (error) {
console.error('Fehler beim Abrufen des Workflow-Status:', error);
return null;
}
}
async function fetchWorkflowLogs(workflowId) {
try {
const response = await fetch(`${API_BASE_URL}/workflow/${workflowId}/logs`);
if (!response.ok) throw new Error('Fehler beim Abrufen der Workflow-Logs');
const logs = await response.json();
return logs;
} catch (error) {
console.error('Fehler beim Abrufen der Workflow-Logs:', error);
return [];
}
}
async function fetchWorkflowResults(workflowId) {
try {
const response = await fetch(`${API_BASE_URL}/workflow/${workflowId}/results`);
if (!response.ok) throw new Error('Fehler beim Abrufen der Workflow-Ergebnisse');
const results = await response.json();
return results;
} catch (error) {
console.error('Fehler beim Abrufen der Workflow-Ergebnisse:', error);
return [];
}
}
// Funktion zum Wechseln der Ansicht
function setActiveView(view) {
// Aktive Klasse von allen Menüpunkten entfernen
workflowLink.classList.remove('active');
dataLink.classList.remove('active');
promptsLink.classList.remove('active');
agentsLink.classList.remove('active');
// Alle Ansichten ausblenden
workflowView.style.display = 'none';
dataView.style.display = 'none';
promptsView.style.display = 'none';
agentsView.style.display = 'none';
// Aktive Ansicht und Menüpunkt setzen
activeView = view;
switch (view) {
case 'workflow':
workflowLink.classList.add('active');
workflowView.style.display = 'flex';
break;
case 'data':
dataLink.classList.add('active');
dataView.style.display = 'block';
// Aktualisiere Daten bei Ansichtswechsel
fetchFiles();
break;
case 'prompts':
promptsLink.classList.add('active');
promptsView.style.display = 'block';
// Prompts laden wenn wir die Ansicht wechseln
if (state.currentWorkspace) {
renderPrompts();
}
break;
case 'agents':
agentsLink.classList.add('active');
agentsView.style.display = 'block';
// Agenten laden wenn wir die Ansicht wechseln
if (state.currentWorkspace) {
renderAgentsList();
}
break;
}
}
// Workspaces rendern
function renderWorkspaces() {
workspaceList.innerHTML = '';
state.workspaces.forEach(workspace => {
const li = document.createElement('li');
li.className = `workspace-item ${workspace.id === state.currentWorkspace?.id ? 'active' : ''}`;
li.innerHTML = `
<i class="fas fa-folder"></i>
<span>${workspace.name}</span>
`;
li.addEventListener('click', async () => {
state.currentWorkspace = workspace;
state.selectedFiles = [];
state.selectedAgents = [];
state.prompt = "";
renderWorkspaces();
renderFiles();
renderSelectedFiles();
renderAgents();
});
workspaceList.appendChild(li);
});
}
// Verfügbare Dateien rendern
function renderFiles() {
availableFiles.innerHTML = '';
state.files.forEach(file => {
const li = document.createElement('li');
li.className = 'file-item';
const isSelected = state.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) {
state.selectedFiles.push(file);
} else {
state.selectedFiles = state.selectedFiles.filter(f => f.id !== file.id);
}
renderSelectedFiles();
});
availableFiles.appendChild(li);
});
}
// Ausgewählte Dateien rendern
function renderSelectedFiles() {
if (state.selectedFiles.length === 0) {
emptyFilesState.style.display = 'flex';
selectedFiles.innerHTML = '';
} else {
emptyFilesState.style.display = 'none';
selectedFiles.innerHTML = '';
state.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');
state.selectedFiles = state.selectedFiles.filter(f => f.id !== fileId);
renderSelectedFiles();
renderFiles();
});
});
}
}
// Prompts rendern
function renderPrompts() {
const promptsContainer = document.getElementById('prompts-list');
if (!promptsContainer) return;
promptsContainer.innerHTML = '';
if (!state.currentWorkspace || !state.currentWorkspace.prompts || state.currentWorkspace.prompts.length === 0) {
promptsContainer.innerHTML = '<div class="empty-state">Keine Prompts im aktuellen Workspace verfügbar.</div>';
return;
}
state.currentWorkspace.prompts.forEach(prompt => {
const promptItem = document.createElement('div');
promptItem.className = 'prompt-item';
promptItem.innerHTML = `
<div class="prompt-content">${prompt.content}</div>
<div class="prompt-meta">Erstellt am: ${new Date(prompt.created_at).toLocaleString()}</div>
<div class="prompt-actions">
<button class="use-prompt-btn" data-id="${prompt.id}">
<i class="fas fa-play"></i> Verwenden
</button>
</div>
`;
// Event-Listener für "Verwenden"-Button
const useBtn = promptItem.querySelector('.use-prompt-btn');
useBtn.addEventListener('click', () => {
// Prompt zum aktuellen Workflow hinzufügen
promptInput.value = prompt.content;
state.prompt = prompt.content;
setActiveView('workflow');
});
promptsContainer.appendChild(promptItem);
});
}
// Agentenliste rendern
function renderAgentsList() {
const agentsContainer = document.getElementById('agents-list-container');
if (!agentsContainer) return;
agentsContainer.innerHTML = '';
if (!state.currentWorkspace || !state.currentWorkspace.agents || state.currentWorkspace.agents.length === 0) {
agentsContainer.innerHTML = '<div class="empty-state">Keine Agenten im aktuellen Workspace verfügbar.</div>';
return;
}
state.currentWorkspace.agents.forEach(agent => {
const agentItem = document.createElement('div');
agentItem.className = 'agent-list-item';
agentItem.innerHTML = `
<div class="agent-header">
<h4>${agent.name}</h4>
<span class="agent-type">${agent.type}</span>
</div>
<div class="agent-description">${agent.description || ''}</div>
<div class="agent-capabilities">
${agent.capabilities ? agent.capabilities.map(cap => `<span class="capability-tag">${cap}</span>`).join('') : ''}
</div>
`;
agentsContainer.appendChild(agentItem);
});
}
// Agenten für Workflow rendern
function renderAgents() {
agentList.innerHTML = '';
if (!state.currentWorkspace || !state.currentWorkspace.agents || state.currentWorkspace.agents.length === 0) {
agentList.innerHTML = '<div class="empty-state">Keine Agenten im ausgewählten Workspace verfügbar.</div>';
return;
}
state.currentWorkspace.agents.forEach(agent => {
const li = document.createElement('li');
li.className = 'agent-item';
const isSelected = state.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) {
state.selectedAgents.push(agent);
} else {
state.selectedAgents = state.selectedAgents.filter(a => a.id !== agent.id);
}
});
agentList.appendChild(li);
});
}
// Ausführungsprotokoll aktualisieren und rendern
function updateLogs(logs) {
state.logs = logs;
renderLogs();
}
// Protokoll rendern
function renderLogs() {
if (state.logs.length === 0) {
executionLog.innerHTML = '<div class="log-empty-state">Workflow noch nicht gestartet. Protokoll wird hier angezeigt.</div>';
return;
}
executionLog.innerHTML = '';
state.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;
}
// Ergebnisse aktualisieren und rendern
function updateResults(results) {
state.results = results;
renderResults();
}
// Ergebnisse rendern
function renderResults() {
if (state.results.length === 0) {
emptyResultsState.style.display = 'flex';
resultsList.style.display = 'none';
return;
}
emptyResultsState.style.display = 'none';
resultsList.style.display = 'block';
resultsList.innerHTML = '';
state.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() {
if (state.selectedFiles.length === 0 || state.selectedAgents.length === 0 || !promptInput.value.trim()) {
alert("Bitte wählen Sie Dateien und Agenten aus und geben Sie einen Prompt ein.");
return;
}
state.isRunning = true;
state.prompt = promptInput.value;
state.logs = [];
state.results = [];
startWorkflowBtn.textContent = 'Wird ausgeführt...';
startWorkflowBtn.classList.add('running');
startWorkflowBtn.disabled = true;
renderLogs();
renderResults();
// API-Aufruf an das Backend
const workflowData = {
workspace_id: state.currentWorkspace.id,
files: state.selectedFiles.map(file => file.id),
agents: state.selectedAgents.map(agent => agent.id),
prompt: state.prompt,
workflow_name: "Workflow " + new Date().toLocaleString()
};
try {
// Workflow starten
const response = await runWorkflowAPI(workflowData);
if (response && response.workflow_id) {
state.currentWorkflowId = response.workflow_id;
// Polling für Status, Logs und Ergebnisse
pollWorkflowStatus();
} else {
throw new Error("Workflow konnte nicht gestartet werden");
}
} catch (error) {
console.error("Fehler beim Starten des Workflows:", error);
state.isRunning = false;
startWorkflowBtn.textContent = 'Workflow starten';
startWorkflowBtn.classList.remove('running');
startWorkflowBtn.disabled = false;
alert("Fehler beim Starten des Workflows: " + error.message);
}
}
// Polling für Workflow-Status, Logs und Ergebnisse
async function pollWorkflowStatus() {
if (!state.currentWorkflowId || !state.isRunning) return;
try {
// Status abrufen
const status = await fetchWorkflowStatus(state.currentWorkflowId);
// Logs abrufen und anzeigen
const logs = await fetchWorkflowLogs(state.currentWorkflowId);
updateLogs(logs);
// Wenn der Workflow abgeschlossen ist, Ergebnisse abrufen
if (status && (status.status === 'completed' || status.status === 'failed')) {
const results = await fetchWorkflowResults(state.currentWorkflowId);
updateResults(results);
state.isRunning = false;
startWorkflowBtn.textContent = 'Workflow starten';
startWorkflowBtn.classList.remove('running');
startWorkflowBtn.disabled = false;
return; // Polling beenden
}
// Weiteres Polling nach kurzer Verzögerung
setTimeout(pollWorkflowStatus, 2000);
} catch (error) {
console.error("Fehler beim Abrufen des Workflow-Status:", error);
state.isRunning = false;
startWorkflowBtn.textContent = 'Workflow starten';
startWorkflowBtn.classList.remove('running');
startWorkflowBtn.disabled = false;
}
}
// Zurücksetzen
function resetWorkflow() {
state.selectedFiles = [];
state.selectedAgents = [];
promptInput.value = "";
state.prompt = "";
renderFiles();
renderSelectedFiles();
renderAgents();
}
// Event-Listener für Menüpunkte
workflowLink.addEventListener('click', function(e) {
e.preventDefault();
setActiveView('workflow');
});
dataLink.addEventListener('click', function(e) {
e.preventDefault();
setActiveView('data');
});
promptsLink.addEventListener('click', function(e) {
e.preventDefault();
setActiveView('prompts');
});
agentsLink.addEventListener('click', function(e) {
e.preventDefault();
setActiveView('agents');
});
// Event-Listener
uploadBtn.addEventListener('click', () => fileInput.click());
fileInput.addEventListener('change', async (e) => {
if (e.target.files.length > 0) {
// Dateien hochladen
for (const file of e.target.files) {
try {
const newFile = await uploadFile(file);
if (newFile) {
state.files.push(newFile);
state.selectedFiles.push(newFile);
}
} catch (error) {
console.error("Fehler beim Hochladen der Datei:", error);
}
}
renderFiles();
renderSelectedFiles();
fileInput.value = "";
}
});
promptInput.addEventListener('input', (e) => {
state.prompt = e.target.value;
});
resetBtn.addEventListener('click', resetWorkflow);
startWorkflowBtn.addEventListener('click', runWorkflow);
// Initialisierung
async function init() {
// Daten vom Backend laden
await fetchWorkspaces();
await fetchFiles();
// UI initialisieren
renderFiles();
renderSelectedFiles();
renderAgents();
renderLogs();
renderResults();
// Initial die Standard-Ansicht setzen
setActiveView(activeView);
}
// Anwendung initialisieren
init();
});