- Added backend routes for DEV Portal dashboard and workflow editor - Created frontend templates for portal and editor using Jinja2 - Integrated draw.io for workflow diagram editing and saving - Developed API endpoints for features, ideas, and workflows management - Established database schema for features, ideas, and workflows - Documented DEV Portal functionality, API endpoints, and database structure
215 lines
6.5 KiB
HTML
215 lines
6.5 KiB
HTML
{% extends "shared/frontend/base.html" %}
|
|
|
|
{% block title %}Workflow Editor - DEV Portal{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
#diagramContainer {
|
|
width: 100%;
|
|
height: calc(100vh - 200px);
|
|
border: 1px solid #ddd;
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.editor-toolbar {
|
|
background: var(--bg-card);
|
|
padding: 1rem;
|
|
border-radius: 8px;
|
|
margin-bottom: 1rem;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="mb-4">
|
|
<a href="/devportal" class="btn btn-light">
|
|
<i class="bi bi-arrow-left me-2"></i>Tilbage til DEV Portal
|
|
</a>
|
|
</div>
|
|
|
|
<div class="editor-toolbar">
|
|
<div class="row align-items-center">
|
|
<div class="col-md-4">
|
|
<input type="text" class="form-control" id="workflowTitle" placeholder="Workflow titel...">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<input type="text" class="form-control" id="workflowDescription" placeholder="Beskrivelse...">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<select class="form-select" id="workflowCategory">
|
|
<option value="flowchart">Flowchart</option>
|
|
<option value="process">Proces</option>
|
|
<option value="system_diagram">System Diagram</option>
|
|
<option value="other">Andet</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3 text-end">
|
|
<button class="btn btn-success" onclick="saveWorkflow()">
|
|
<i class="bi bi-save me-2"></i>Gem Workflow
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="diagramContainer">
|
|
<iframe id="diagramFrame" frameborder="0" style="width: 100%; height: 100%;"></iframe>
|
|
</div>
|
|
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
let currentWorkflowId = {{ workflow_id if workflow_id else 'null' }};
|
|
let diagramXml = null;
|
|
|
|
function initEditor() {
|
|
const iframe = document.getElementById('diagramFrame');
|
|
|
|
// Build draw.io embed URL with parameters
|
|
const params = new URLSearchParams({
|
|
embed: '1',
|
|
ui: 'kennedy',
|
|
spin: '1',
|
|
proto: 'json',
|
|
configure: '1',
|
|
noSaveBtn: '1',
|
|
noExitBtn: '1',
|
|
libraries: '1',
|
|
saveAndExit: '0'
|
|
});
|
|
|
|
iframe.src = `https://embed.diagrams.net/?${params.toString()}`;
|
|
|
|
// Listen for messages from draw.io
|
|
window.addEventListener('message', function(evt) {
|
|
if (evt.data.length > 0) {
|
|
try {
|
|
const msg = JSON.parse(evt.data);
|
|
|
|
// When editor is ready
|
|
if (msg.event === 'init') {
|
|
iframe.contentWindow.postMessage(JSON.stringify({
|
|
action: 'load',
|
|
autosave: 1,
|
|
xml: diagramXml || '' // Load existing diagram if editing
|
|
}), '*');
|
|
}
|
|
|
|
// When diagram is exported
|
|
if (msg.event === 'export') {
|
|
diagramXml = msg.xml;
|
|
}
|
|
|
|
// Auto-save on every change
|
|
if (msg.event === 'autosave') {
|
|
diagramXml = msg.xml;
|
|
}
|
|
|
|
// Request export when saving
|
|
if (msg.event === 'save') {
|
|
iframe.contentWindow.postMessage(JSON.stringify({
|
|
action: 'export',
|
|
format: 'xml'
|
|
}), '*');
|
|
}
|
|
} catch (e) {
|
|
// Ignore non-JSON messages
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
async function loadWorkflow() {
|
|
if (!currentWorkflowId) return;
|
|
|
|
try {
|
|
const response = await fetch(`/api/v1/devportal/workflows/${currentWorkflowId}`);
|
|
const workflow = await response.json();
|
|
|
|
document.getElementById('workflowTitle').value = workflow.title;
|
|
document.getElementById('workflowDescription').value = workflow.description || '';
|
|
document.getElementById('workflowCategory').value = workflow.category || 'flowchart';
|
|
diagramXml = workflow.diagram_xml;
|
|
|
|
// Reinitialize editor with loaded data
|
|
initEditor();
|
|
} catch (error) {
|
|
console.error('Error loading workflow:', error);
|
|
}
|
|
}
|
|
|
|
async function saveWorkflow() {
|
|
const title = document.getElementById('workflowTitle').value;
|
|
const description = document.getElementById('workflowDescription').value;
|
|
const category = document.getElementById('workflowCategory').value;
|
|
|
|
if (!title) {
|
|
alert('Indtast venligst en titel');
|
|
return;
|
|
}
|
|
|
|
// Request export from draw.io
|
|
const iframe = document.getElementById('diagramFrame');
|
|
iframe.contentWindow.postMessage(JSON.stringify({
|
|
action: 'export',
|
|
format: 'xml'
|
|
}), '*');
|
|
|
|
// Wait a bit for export to complete
|
|
setTimeout(async () => {
|
|
if (!diagramXml) {
|
|
alert('Kunne ikke eksportere diagram. Prøv igen.');
|
|
return;
|
|
}
|
|
|
|
const workflow = {
|
|
title,
|
|
description,
|
|
category,
|
|
diagram_xml: diagramXml
|
|
};
|
|
|
|
try {
|
|
let response;
|
|
if (currentWorkflowId) {
|
|
// Update existing
|
|
response = await fetch(`/api/v1/devportal/workflows/${currentWorkflowId}`, {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(workflow)
|
|
});
|
|
} else {
|
|
// Create new
|
|
response = await fetch('/api/v1/devportal/workflows', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(workflow)
|
|
});
|
|
}
|
|
|
|
if (response.ok) {
|
|
const result = await response.json();
|
|
alert('Workflow gemt!');
|
|
window.location.href = '/devportal';
|
|
} else {
|
|
alert('Fejl ved gemning af workflow');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error saving workflow:', error);
|
|
alert('Fejl ved gemning af workflow');
|
|
}
|
|
}, 500);
|
|
}
|
|
|
|
// Initialize on load
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
if (currentWorkflowId) {
|
|
loadWorkflow();
|
|
} else {
|
|
initEditor();
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %}
|