654 lines
No EOL
24 KiB
JavaScript
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();
|
|
}); |