440 lines
16 KiB
HTML
440 lines
16 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Create Template{% endblock %}
|
|
|
|
{% block head %}
|
|
<style>
|
|
.btn-secondary {
|
|
background: #6c757d !important;
|
|
color: white !important;
|
|
border: 1px solid #6c757d !important;
|
|
padding: 8px 12px;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.btn-outline {
|
|
background: white !important;
|
|
color: #333 !important;
|
|
border: 1px solid #ddd !important;
|
|
}
|
|
|
|
.btn-outline:hover {
|
|
background: #f8f9fa !important;
|
|
border-color: #007bff !important;
|
|
}
|
|
|
|
.template-field {
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
user-select: none;
|
|
}
|
|
|
|
.database-field {
|
|
background: linear-gradient(135deg, #e3f2fd, #bbdefb) !important;
|
|
border: 2px solid #2196f3 !important;
|
|
}
|
|
|
|
.database-field:hover {
|
|
transform: scale(1.02);
|
|
box-shadow: 0 4px 8px rgba(33, 150, 243, 0.3);
|
|
}
|
|
|
|
#label-preview {
|
|
background: #fafafa;
|
|
min-width: 300px;
|
|
min-height: 200px;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.column-btn {
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.table-btn {
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.table-btn:hover {
|
|
transform: translateX(5px);
|
|
}
|
|
|
|
#tables-container h5,
|
|
#columns-container h5 {
|
|
color: #333;
|
|
border-bottom: 2px solid #007bff;
|
|
padding-bottom: 5px;
|
|
margin-bottom: 10px;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="card-container" style="display: flex; gap: 20px;">
|
|
<!-- Left Column -->
|
|
<div class="left-column" style="flex: 1; max-width: 33%;">
|
|
<div class="dashboard-card">
|
|
<h3>Template Settings</h3>
|
|
<p>Set the dimensions and retrieve database headers for the label template:</p>
|
|
|
|
<!-- Dimensions Section -->
|
|
<div>
|
|
<h4>Dimensions</h4>
|
|
<form id="dimensions-form">
|
|
<div style="margin-bottom: 10px;">
|
|
<label for="label-width">Width (mm):</label>
|
|
<input type="number" id="label-width" name="label_width" required>
|
|
</div>
|
|
<div style="margin-bottom: 10px;">
|
|
<label for="label-height">Height (mm):</label>
|
|
<input type="number" id="label-height" name="label_height" required>
|
|
</div>
|
|
<button type="button" id="set-dimensions-btn" class="btn">Set Label Dimensions</button>
|
|
</form>
|
|
</div>
|
|
|
|
<hr style="margin: 20px 0;">
|
|
|
|
<!-- Add Fields Section -->
|
|
<div>
|
|
<h4>Add Fields</h4>
|
|
<button type="button" class="btn add-field-btn" data-type="text-label">Add Text Label</button>
|
|
.big-card {
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.12);
|
|
border-radius: 10px;
|
|
padding: 30px 24px;
|
|
min-height: 600px;
|
|
background: #f5faff;
|
|
}
|
|
.small-card {
|
|
box-shadow: 0 2px 6px rgba(0,0,0,0.10);
|
|
border-radius: 10px;
|
|
padding: 18px 12px;
|
|
min-height: 300px;
|
|
background: #fff;
|
|
}
|
|
<button type="button" class="btn add-field-btn" data-type="text-input">Add Text Input</button>
|
|
<button type="button" class="btn add-field-btn" data-type="barcode">Add Barcode</button>
|
|
<button type="button" class="btn add-field-btn" data-type="qrcode">Add QR Code</button>
|
|
</div>
|
|
<!-- Get Database Headers Section -->
|
|
<div>
|
|
<h4>Get Database Headers</h4>
|
|
<p>Retrieve column names from a selected database table:</p>
|
|
<button type="button" id="get-tables-btn" class="btn">Get Database Tables</button>
|
|
<div id="tables-container" style="margin-top: 10px;">
|
|
<!-- Tables will be dynamically populated here -->
|
|
</div>
|
|
<div id="columns-container" style="margin-top: 10px;">
|
|
<!-- Columns will be dynamically populated here -->
|
|
</div>
|
|
</div>
|
|
|
|
<hr style="margin: 20px 0;">
|
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Right Column -->
|
|
<div class="right-column" style="flex: 2;">
|
|
<div class="dashboard-card">
|
|
<h3>Interactive Label Preview</h3>
|
|
<p>Drag and drop fields to design your label:</p>
|
|
<div class="label-preview-container" style="display: flex; justify-content: center; align-items: center; height: 100%; position: relative;">
|
|
<div id="label-preview" style="border: 1px solid #ddd; padding: 10px; min-height: 400px; position: relative;">
|
|
<!-- Fields will be dynamically added here -->
|
|
</div>
|
|
</div>
|
|
<button type="button" id="preview-btn" class="btn" style="margin-top: 10px;">Preview</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="{{ url_for('static', filename='pdfjs/pdf.worker.js') }}"></script>
|
|
<script>
|
|
// Set the worker source for PDF.js
|
|
pdfjsLib.GlobalWorkerOptions.workerSrc = "{{ url_for('static', filename='pdfjs/pdf.worker.min.js') }}";
|
|
|
|
// Database Tables Functionality
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const getTablesBtn = document.getElementById('get-tables-btn');
|
|
const tablesContainer = document.getElementById('tables-container');
|
|
const columnsContainer = document.getElementById('columns-container');
|
|
|
|
// Handle Get Database Tables button click
|
|
getTablesBtn.addEventListener('click', function() {
|
|
getTablesBtn.textContent = 'Loading...';
|
|
getTablesBtn.disabled = true;
|
|
|
|
fetch('/get_database_tables')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
displayTables(data.tables, data.recommended);
|
|
} else {
|
|
showError('Failed to load tables: ' + data.error);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
showError('Error: ' + error.message);
|
|
})
|
|
.finally(() => {
|
|
getTablesBtn.textContent = 'Get Database Tables';
|
|
getTablesBtn.disabled = false;
|
|
});
|
|
});
|
|
|
|
function displayTables(tables, recommended) {
|
|
if (tables.length === 0) {
|
|
tablesContainer.innerHTML = '<p style="color: #999;">No relevant tables found.</p>';
|
|
return;
|
|
}
|
|
|
|
let html = '<h5>Available Tables:</h5><div style="margin-top: 10px;">';
|
|
|
|
tables.forEach(table => {
|
|
const isRecommended = table === recommended;
|
|
const buttonClass = isRecommended ? 'btn' : 'btn-secondary';
|
|
const recommendedText = isRecommended ? ' ⭐ (Recommended)' : '';
|
|
|
|
html += `
|
|
<button type="button"
|
|
class="table-btn ${buttonClass}"
|
|
data-table="${table}"
|
|
style="display: block; margin: 5px 0; width: 100%; text-align: left;">
|
|
${table}${recommendedText}
|
|
</button>
|
|
`;
|
|
});
|
|
|
|
html += '</div>';
|
|
tablesContainer.innerHTML = html;
|
|
|
|
// Add event listeners to table buttons
|
|
document.querySelectorAll('.table-btn').forEach(btn => {
|
|
btn.addEventListener('click', function() {
|
|
const tableName = this.getAttribute('data-table');
|
|
loadTableColumns(tableName);
|
|
|
|
// Update button states
|
|
document.querySelectorAll('.table-btn').forEach(b => {
|
|
b.classList.remove('btn');
|
|
b.classList.add('btn-secondary');
|
|
});
|
|
this.classList.remove('btn-secondary');
|
|
this.classList.add('btn');
|
|
});
|
|
});
|
|
|
|
// Auto-load recommended table
|
|
if (recommended) {
|
|
setTimeout(() => {
|
|
const recommendedBtn = document.querySelector(`[data-table="${recommended}"]`);
|
|
if (recommendedBtn) {
|
|
recommendedBtn.click();
|
|
}
|
|
}, 100);
|
|
}
|
|
}
|
|
|
|
function loadTableColumns(tableName) {
|
|
columnsContainer.innerHTML = '<p>Loading columns...</p>';
|
|
|
|
fetch(`/get_table_columns/${tableName}`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
displayColumns(data.table, data.columns);
|
|
} else {
|
|
showError('Failed to load columns: ' + data.error);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
showError('Error loading columns: ' + error.message);
|
|
});
|
|
}
|
|
|
|
function displayColumns(tableName, columns) {
|
|
if (columns.length === 0) {
|
|
columnsContainer.innerHTML = '<p style="color: #999;">No columns found.</p>';
|
|
return;
|
|
}
|
|
|
|
let html = `
|
|
<h5>Columns from "${tableName}" table:</h5>
|
|
<p style="font-size: 12px; color: #666; margin: 5px 0;">
|
|
Click on a column to add it as a database field to your label template.
|
|
</p>
|
|
<div style="margin-top: 10px; max-height: 300px; overflow-y: auto;">
|
|
`;
|
|
|
|
columns.forEach(column => {
|
|
// Determine icon based on column type
|
|
let icon = '📝'; // Default text icon
|
|
if (column.name.toLowerCase().includes('id')) icon = '🔢';
|
|
else if (column.name.toLowerCase().includes('date') || column.name.toLowerCase().includes('time')) icon = '📅';
|
|
else if (column.name.toLowerCase().includes('quantity') || column.name.toLowerCase().includes('cantitate')) icon = '🔢';
|
|
else if (column.name.toLowerCase().includes('code') || column.name.toLowerCase().includes('cod')) icon = '🏷️';
|
|
else if (column.name.toLowerCase().includes('customer') || column.name.toLowerCase().includes('client')) icon = '👤';
|
|
else if (column.name.toLowerCase().includes('product') || column.name.toLowerCase().includes('articol')) icon = '📦';
|
|
|
|
html += `
|
|
<button type="button"
|
|
class="column-btn btn-outline"
|
|
data-column="${column.name}"
|
|
data-field-id="${column.field_id}"
|
|
data-description="${column.description}"
|
|
style="display: block; margin: 3px 0; padding: 8px; width: 100%; text-align: left; border: 1px solid #ddd; background: white; border-radius: 4px; cursor: pointer;">
|
|
<div style="display: flex; align-items: center;">
|
|
<span style="margin-right: 8px; font-size: 16px;">${icon}</span>
|
|
<div style="flex: 1;">
|
|
<strong>${column.name}</strong>
|
|
<br>
|
|
<small style="color: #666;">${column.description}</small>
|
|
<br>
|
|
<small style="color: #999; font-style: italic;">${column.type}</small>
|
|
</div>
|
|
</div>
|
|
</button>
|
|
`;
|
|
});
|
|
|
|
html += '</div>';
|
|
columnsContainer.innerHTML = html;
|
|
|
|
// Add event listeners to column buttons
|
|
document.querySelectorAll('.column-btn').forEach(btn => {
|
|
btn.addEventListener('click', function() {
|
|
const columnName = this.getAttribute('data-column');
|
|
const fieldId = this.getAttribute('data-field-id');
|
|
const description = this.getAttribute('data-description');
|
|
|
|
addDatabaseFieldToTemplate(columnName, fieldId, description);
|
|
|
|
// Visual feedback
|
|
this.style.background = '#e7f3ff';
|
|
this.style.borderColor = '#007bff';
|
|
setTimeout(() => {
|
|
this.style.background = 'white';
|
|
this.style.borderColor = '#ddd';
|
|
}, 1000);
|
|
});
|
|
|
|
// Hover effects
|
|
btn.addEventListener('mouseenter', function() {
|
|
this.style.background = '#f8f9fa';
|
|
this.style.borderColor = '#007bff';
|
|
});
|
|
|
|
btn.addEventListener('mouseleave', function() {
|
|
if (this.style.background !== 'rgb(231, 243, 255)') {
|
|
this.style.background = 'white';
|
|
this.style.borderColor = '#ddd';
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function addDatabaseFieldToTemplate(columnName, fieldId, description) {
|
|
const labelPreview = document.getElementById('label-preview');
|
|
|
|
// Create a database field element
|
|
const fieldElement = document.createElement('div');
|
|
fieldElement.className = 'template-field database-field';
|
|
fieldElement.id = fieldId;
|
|
fieldElement.style.cssText = `
|
|
position: absolute;
|
|
left: 20px;
|
|
top: ${20 + (document.querySelectorAll('.template-field').length * 30)}px;
|
|
padding: 5px 10px;
|
|
background: #e3f2fd;
|
|
border: 2px solid #2196f3;
|
|
border-radius: 4px;
|
|
cursor: move;
|
|
font-size: 12px;
|
|
min-width: 120px;
|
|
text-align: center;
|
|
`;
|
|
|
|
fieldElement.innerHTML = `
|
|
<div style="font-weight: bold; color: #1976d2;">${'{{'}${columnName}${'}}'}}</div>
|
|
<div style="font-size: 10px; color: #666;">${description}</div>
|
|
`;
|
|
|
|
// Add data attributes
|
|
fieldElement.setAttribute('data-field-type', 'database');
|
|
fieldElement.setAttribute('data-column-name', columnName);
|
|
fieldElement.setAttribute('data-description', description);
|
|
|
|
labelPreview.appendChild(fieldElement);
|
|
|
|
// Make it draggable (basic implementation)
|
|
makeDraggable(fieldElement);
|
|
|
|
console.log(`Added database field: ${columnName} (${description})`);
|
|
}
|
|
|
|
function makeDraggable(element) {
|
|
let isDragging = false;
|
|
let currentX;
|
|
let currentY;
|
|
let initialX;
|
|
let initialY;
|
|
let xOffset = 0;
|
|
let yOffset = 0;
|
|
|
|
element.addEventListener('mousedown', dragStart);
|
|
document.addEventListener('mousemove', drag);
|
|
document.addEventListener('mouseup', dragEnd);
|
|
|
|
function dragStart(e) {
|
|
initialX = e.clientX - xOffset;
|
|
initialY = e.clientY - yOffset;
|
|
|
|
if (e.target === element) {
|
|
isDragging = true;
|
|
}
|
|
}
|
|
|
|
function drag(e) {
|
|
if (isDragging) {
|
|
e.preventDefault();
|
|
currentX = e.clientX - initialX;
|
|
currentY = e.clientY - initialY;
|
|
xOffset = currentX;
|
|
yOffset = currentY;
|
|
|
|
element.style.left = currentX + 'px';
|
|
element.style.top = currentY + 'px';
|
|
}
|
|
}
|
|
|
|
function dragEnd() {
|
|
initialX = currentX;
|
|
initialY = currentY;
|
|
isDragging = false;
|
|
}
|
|
}
|
|
|
|
function showError(message) {
|
|
const errorDiv = document.createElement('div');
|
|
errorDiv.style.cssText = `
|
|
background: #ffe6e6;
|
|
border: 1px solid #ff9999;
|
|
color: #cc0000;
|
|
padding: 10px;
|
|
border-radius: 4px;
|
|
margin: 10px 0;
|
|
`;
|
|
errorDiv.textContent = message;
|
|
|
|
tablesContainer.appendChild(errorDiv);
|
|
|
|
setTimeout(() => {
|
|
errorDiv.remove();
|
|
}, 5000);
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %} |