- Replace old implementation of PROCESSOR_TYPES and CATALOG_TYPES with the new cached approach

- Add an ordered_list dynamic field type (to be refined)
- Add tabulator javascript library to project
This commit is contained in:
Josako
2025-05-29 16:00:25 +02:00
parent 8a29eb0d8f
commit 25e169dbea
23 changed files with 687 additions and 199 deletions

View File

@@ -54,6 +54,7 @@
<hr>
{% include 'footer.html' %}
{% include 'scripts.html' %}
{% include 'ordered_list_configs.html' %}
{% block scripts %}{% endblock %}
</body>
</html>
</html>

View File

@@ -0,0 +1,231 @@
<script type="module">
window.EveAI = window.EveAI || {};
window.EveAI.OrderedListEditors = {
instances: {},
initialize: function(containerId, data, listType, options = {}) {
console.log('Initializing OrderedListEditor for', containerId, 'with data', data, 'and listType', listType);
const container = document.getElementById(containerId);
if (!container || typeof container !== 'object' || !('classList' in container)) {
console.error(`Container with ID ${containerId} not found or not a valid element:`, container);
return null;
}
if (this.instances[containerId]) return this.instances[containerId];
if (typeof window.Tabulator !== 'function') {
console.error('Tabulator not loaded (window.Tabulator missing).');
container.innerHTML = `<div class="alert alert-danger p-3">
<strong>Error:</strong> Tabulator not loaded
</div>`;
return null;
}
// Get the list type configuration
const listTypeConfig = this._getListTypeConfig(listType);
if (!listTypeConfig) {
console.error(`List type configuration for ${listType} not found.`);
container.innerHTML = `<div class="alert alert-danger p-3">
<strong>Error:</strong> List type configuration for ${listType} not found
</div>`;
return null;
}
// Create column definitions from list type
const columns = this._createColumnsFromListType(listTypeConfig);
// Initialize Tabulator
try {
console.log('Creating Tabulator for', containerId);
const table = new Tabulator(container, {
data: data || [],
columns: columns,
layout: "fitColumns",
movableRows: true,
height: "400px",
...options
});
console.log('Tabulator created for', containerId);
container.classList.add('tabulator-initialized');
// Add row button
const addRowBtn = document.createElement('button');
addRowBtn.className = 'btn btn-sm btn-primary mt-2';
addRowBtn.innerHTML = 'Add Row';
addRowBtn.addEventListener('click', () => {
const newRow = {};
// Create empty row with default values
Object.entries(listTypeConfig).forEach(([key, field]) => {
newRow[key] = field.default || '';
});
table.addRow(newRow);
this._updateTextarea(containerId, table);
});
container.parentNode.insertBefore(addRowBtn, container.nextSibling);
// Store instance
this.instances[containerId] = {
table: table,
textarea: document.getElementById(containerId.replace('-editor', ''))
};
// Update textarea on data change
table.on("dataChanged", () => {
this._updateTextarea(containerId, table);
});
return table;
} catch (e) {
console.error(`Error initializing Tabulator for ${containerId}:`, e);
container.innerHTML = `<div class="alert alert-danger p-3">
<strong>Error initializing Tabulator:</strong><br>${e.message}
</div>`;
return null;
}
},
_updateTextarea: function(containerId, table) {
const instance = this.instances[containerId];
if (instance && instance.textarea) {
instance.textarea.value = JSON.stringify(table.getData());
}
},
_getListTypeConfig: function(listType) {
// This would need to be implemented to fetch the list type configuration
// Could be from a global variable set in the template or via an API call
return window.listTypeConfigs && window.listTypeConfigs[listType];
},
_createColumnsFromListType: function(listTypeConfig) {
const columns = [];
// Add drag handle column for row reordering
columns.push({
formatter: "handle",
headerSort: false,
frozen: true,
width: 30,
minWidth: 30
});
// Add columns for each field in the list type
Object.entries(listTypeConfig).forEach(([key, field]) => {
const column = {
title: field.name || key,
field: key,
tooltip: field.description
};
// Set editor based on field type
if (field.type === 'boolean') {
column.formatter = 'tickCross';
column.editor = 'tickCross';
column.hozAlign = 'center';
} else if (field.type === 'enum' && field.allowed_values) {
column.editor = 'select';
column.editorParams = {
values: field.allowed_values
};
} else {
column.editor = 'input';
}
columns.push(column);
});
// Add delete button column
columns.push({
formatter: function(cell, formatterParams, onRendered) {
return "<button class='btn btn-sm btn-danger'><i class='material-icons'>delete</i></button>";
},
width: 40,
hozAlign: "center",
headerSort: false,
cellClick: function(e, cell) {
cell.getRow().delete();
}
});
return columns;
},
get: function(containerId) {
return this.instances[containerId] || null;
},
destroy: function(containerId) {
if (this.instances[containerId]) {
if (this.instances[containerId].table && typeof this.instances[containerId].table.destroy === 'function') {
this.instances[containerId].table.destroy();
}
delete this.instances[containerId];
}
const container = document.getElementById(containerId);
if (container) {
container.classList.remove('tabulator-initialized');
container.innerHTML = '';
}
}
};
document.addEventListener('DOMContentLoaded', function() {
// Initialize list type configurations
window.listTypeConfigs = window.listTypeConfigs || {};
// Initialize ordered list editors
document.querySelectorAll('.ordered-list-field').forEach(function(textarea) {
const containerId = textarea.id + '-editor';
const container = document.getElementById(containerId);
if (!container) return;
try {
const data = textarea.value ? JSON.parse(textarea.value) : [];
const listType = textarea.getAttribute('data-list-type');
// Check if we have the list type configuration
if (listType && !window.listTypeConfigs[listType]) {
console.warn(`List type configuration for ${listType} not found. The editor may not work correctly.`);
}
window.EveAI.OrderedListEditors.initialize(containerId, data, listType);
} catch (e) {
console.error('Error initializing ordered list editor:', e);
container.innerHTML = `<div class="alert alert-danger p-3">
<strong>Error initializing ordered list editor:</strong><br>${e.message}
</div>`;
}
});
});
</script>
<style>
/* Tabulator styling */
.ordered-list-editor {
margin-bottom: 1rem;
}
/* Make sure the Tabulator container has a proper height */
.ordered-list-editor .tabulator {
height: 400px;
width: 100%;
border: 1px solid #dee2e6;
border-radius: 0.25rem;
}
/* Style for the handle column */
.ordered-list-editor .tabulator-row-handle {
cursor: move;
}
/* Style for the delete button */
.ordered-list-editor .tabulator-cell button.btn-danger {
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
}
/* Style for boolean columns */
.ordered-list-editor .tabulator-cell[data-type="boolean"] {
text-align: center;
}
</style>

View File

@@ -1,3 +1,12 @@
{# Helper functie om veilig de class van een veld te krijgen #}
{% macro get_field_class(field, default='') %}
{% if field.render_kw is not none and field.render_kw.get('class') is not none %}
{{ field.render_kw.get('class') }}
{% else %}
{{ default }}
{% endif %}
{% endmacro %}
{% macro render_field_content(field, disabled=False, readonly=False, class='') %}
{% if field.type == 'BooleanField' %}
<div class="form-group">
@@ -55,9 +64,13 @@
{% endif %}
{% endif %}
{% set field_class = get_field_class(field) %}
{% if field.type == 'TextAreaField' and 'json-editor' in class %}
<div id="{{ field.id }}-editor" class="json-editor-container"></div>
{{ field(class="form-control d-none " + class, disabled=disabled, readonly=readonly) }}
{% elif field.type == 'OrderedListField' or 'ordered-list-field' in field_class %}
{# Behoud ordered-list-field class en voeg form-control toe #}
{{ field(class="form-control " + field_class|trim, disabled=disabled, readonly=readonly) }}
{% elif field.type == 'SelectField' %}
{{ field(class="form-control form-select " + class, disabled=disabled, readonly=readonly) }}
{% else %}
@@ -76,6 +89,7 @@
{% endmacro %}
{% macro render_field(field, disabled_fields=[], readonly_fields=[], exclude_fields=[], class='') %}
<!-- Debug info -->
<!-- Field name: {{ field.name }}, Field type: {{ field.__class__.__name__ }} -->

View File

@@ -0,0 +1,7 @@
{# Include this template in any page that uses ordered_list fields #}
{# Usage: {% include 'ordered_list_configs.html' %} #}
{# The form must be available in the template context as 'form' #}
{% if form and form.get_list_type_configs_js %}
{{ form.get_list_type_configs_js()|safe }}
{% endif %}

View File

@@ -9,6 +9,7 @@
<script src="{{url_for('static', filename='assets/js/material-kit-pro.min.js')}}"></script>
{% include 'eveai_json_editor.html' %}
{% include 'eveai_ordered_list_editor.html' %}
<script>
document.addEventListener('DOMContentLoaded', function() {