updated
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
recticel/
|
recticel/
|
||||||
.venv/
|
.venv/
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
app/__pycache__/
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -939,6 +939,130 @@ def label_templates():
|
|||||||
def create_template():
|
def create_template():
|
||||||
return render_template('create_template.html')
|
return render_template('create_template.html')
|
||||||
|
|
||||||
|
@bp.route('/get_database_tables', methods=['GET'])
|
||||||
|
def get_database_tables():
|
||||||
|
"""Get list of database tables for template creation"""
|
||||||
|
if 'role' not in session or session['role'] not in ['superadmin', 'warehouse_manager', 'etichete']:
|
||||||
|
return jsonify({'error': 'Access denied'}), 403
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Get database connection using the same method as other functions
|
||||||
|
settings_file = current_app.instance_path + '/external_server.conf'
|
||||||
|
settings = {}
|
||||||
|
with open(settings_file, 'r') as f:
|
||||||
|
for line in f:
|
||||||
|
key, value = line.strip().split('=', 1)
|
||||||
|
settings[key] = value
|
||||||
|
|
||||||
|
connection = mariadb.connect(
|
||||||
|
user=settings['username'],
|
||||||
|
password=settings['password'],
|
||||||
|
host=settings['server_domain'],
|
||||||
|
port=int(settings['port']),
|
||||||
|
database=settings['database_name']
|
||||||
|
)
|
||||||
|
|
||||||
|
cursor = connection.cursor()
|
||||||
|
|
||||||
|
# Get all tables in the database
|
||||||
|
cursor.execute("SHOW TABLES")
|
||||||
|
all_tables = [table[0] for table in cursor.fetchall()]
|
||||||
|
|
||||||
|
# Filter to show relevant tables (prioritize order_for_labels)
|
||||||
|
relevant_tables = []
|
||||||
|
if 'order_for_labels' in all_tables:
|
||||||
|
relevant_tables.append('order_for_labels')
|
||||||
|
|
||||||
|
# Add other potentially relevant tables
|
||||||
|
for table in all_tables:
|
||||||
|
if table not in relevant_tables and any(keyword in table.lower() for keyword in ['order', 'label', 'product', 'customer']):
|
||||||
|
relevant_tables.append(table)
|
||||||
|
|
||||||
|
connection.close()
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'success': True,
|
||||||
|
'tables': relevant_tables,
|
||||||
|
'recommended': 'order_for_labels'
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'error': f'Database error: {str(e)}'}), 500
|
||||||
|
|
||||||
|
@bp.route('/get_table_columns/<table_name>', methods=['GET'])
|
||||||
|
def get_table_columns(table_name):
|
||||||
|
"""Get column names and descriptions for a specific table"""
|
||||||
|
if 'role' not in session or session['role'] not in ['superadmin', 'warehouse_manager', 'etichete']:
|
||||||
|
return jsonify({'error': 'Access denied'}), 403
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Get database connection
|
||||||
|
settings_file = current_app.instance_path + '/external_server.conf'
|
||||||
|
settings = {}
|
||||||
|
with open(settings_file, 'r') as f:
|
||||||
|
for line in f:
|
||||||
|
key, value = line.strip().split('=', 1)
|
||||||
|
settings[key] = value
|
||||||
|
|
||||||
|
connection = mariadb.connect(
|
||||||
|
user=settings['username'],
|
||||||
|
password=settings['password'],
|
||||||
|
host=settings['server_domain'],
|
||||||
|
port=int(settings['port']),
|
||||||
|
database=settings['database_name']
|
||||||
|
)
|
||||||
|
|
||||||
|
cursor = connection.cursor()
|
||||||
|
|
||||||
|
# Verify table exists
|
||||||
|
cursor.execute("SHOW TABLES LIKE %s", (table_name,))
|
||||||
|
if not cursor.fetchone():
|
||||||
|
return jsonify({'error': f'Table {table_name} not found'}), 404
|
||||||
|
|
||||||
|
# Get column information
|
||||||
|
cursor.execute(f"DESCRIBE {table_name}")
|
||||||
|
columns_info = cursor.fetchall()
|
||||||
|
|
||||||
|
columns = []
|
||||||
|
for col_info in columns_info:
|
||||||
|
column_name = col_info[0]
|
||||||
|
column_type = col_info[1]
|
||||||
|
is_nullable = col_info[2] == 'YES'
|
||||||
|
column_default = col_info[4]
|
||||||
|
|
||||||
|
# Get column comment if available
|
||||||
|
cursor.execute(f"""
|
||||||
|
SELECT COLUMN_COMMENT
|
||||||
|
FROM INFORMATION_SCHEMA.COLUMNS
|
||||||
|
WHERE TABLE_NAME = %s AND COLUMN_NAME = %s AND TABLE_SCHEMA = DATABASE()
|
||||||
|
""", (table_name, column_name))
|
||||||
|
|
||||||
|
comment_result = cursor.fetchone()
|
||||||
|
column_comment = comment_result[0] if comment_result and comment_result[0] else ''
|
||||||
|
|
||||||
|
# Create user-friendly description
|
||||||
|
description = column_comment or column_name.replace('_', ' ').title()
|
||||||
|
|
||||||
|
columns.append({
|
||||||
|
'name': column_name,
|
||||||
|
'type': column_type,
|
||||||
|
'nullable': is_nullable,
|
||||||
|
'default': column_default,
|
||||||
|
'description': description,
|
||||||
|
'field_id': f"db_{column_name}" # Unique ID for template fields
|
||||||
|
})
|
||||||
|
|
||||||
|
connection.close()
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'success': True,
|
||||||
|
'table': table_name,
|
||||||
|
'columns': columns
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'error': f'Database error: {str(e)}'}), 500
|
||||||
|
|
||||||
@bp.route('/edit_template/<int:template_id>')
|
@bp.route('/edit_template/<int:template_id>')
|
||||||
def edit_template(template_id):
|
def edit_template(template_id):
|
||||||
# Logic for editing a template will go here
|
# Logic for editing a template will go here
|
||||||
|
|||||||
440
py_app/app/templates/create_template copy.html
Normal file
440
py_app/app/templates/create_template copy.html
Normal file
@@ -0,0 +1,440 @@
|
|||||||
|
{% 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 %}
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block title %}Create Template{% 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>
|
|
||||||
<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') }}";
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
{% block title %}Import Warehouse Locations from CSV{% endblock %}
|
|
||||||
{% block content %}
|
|
||||||
<div class="scan-container">
|
|
||||||
<!-- Import Locations from CSV Card (first, fixed position) -->
|
|
||||||
<div class="card scan-form-card" style="margin-bottom: 24px;">
|
|
||||||
<h3>Import Locations from CSV</h3>
|
|
||||||
<form method="POST" enctype="multipart/form-data" class="form-centered" id="csv-upload-form">
|
|
||||||
<label for="csv_file">Choose CSV file:</label>
|
|
||||||
{% if not locations %}
|
|
||||||
<input type="file" name="csv_file" accept=".csv" required><br>
|
|
||||||
<button type="submit" class="btn">Upload & Preview</button>
|
|
||||||
{% else %}
|
|
||||||
<label style="font-weight: bold;">Selected file: {{ session['csv_filename'] if session['csv_filename'] else 'Unknown' }}</label><br>
|
|
||||||
<button type="button" class="btn" onclick="showPopupAndSubmit()">Create Locations</button>
|
|
||||||
{% endif %}
|
|
||||||
<!-- CSV File Format Guidance (moved here) -->
|
|
||||||
<div style="margin-top: 8px;">
|
|
||||||
<h4 style="margin-bottom: 4px;">CSV File Format</h4>
|
|
||||||
<label style="margin-bottom: 8px; display: block;">Fisierul CSV trebuie sa aiba urmatorul format:</label>
|
|
||||||
<table class="scan-table" style="width: 100%;">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Location Code</th>
|
|
||||||
<th>Size</th>
|
|
||||||
<th>Description</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>EX123</td>
|
|
||||||
<td>100</td>
|
|
||||||
<td>Zona depozitare A</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<!-- Popup Modal -->
|
|
||||||
<div id="popup-modal" class="popup" style="display:none; position:fixed; top:0; left:0; width:100vw; height:100vh; background:var(--app-overlay-bg, rgba(30,41,59,0.85)); z-index:9999; align-items:center; justify-content:center;">
|
|
||||||
<div class="popup-content" style="margin:auto; padding:32px; border-radius:8px; box-shadow:0 2px 8px #333; min-width:320px; max-width:400px; text-align:center;">
|
|
||||||
<h3 style="color:var(--app-label-text);">Performing the creation of the warehouse locations</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
function showPopupAndSubmit() {
|
|
||||||
document.getElementById('popup-modal').style.display = 'flex';
|
|
||||||
// Submit the form after showing popup
|
|
||||||
setTimeout(function() {
|
|
||||||
var form = document.getElementById('csv-upload-form');
|
|
||||||
var input = document.createElement('input');
|
|
||||||
input.type = 'hidden';
|
|
||||||
input.name = 'create_locations';
|
|
||||||
input.value = '1';
|
|
||||||
form.appendChild(input);
|
|
||||||
form.submit();
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
window.onload = function() {
|
|
||||||
if (window.location.hash === '#created') {
|
|
||||||
document.getElementById('popup-modal').style.display = 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</div>
|
|
||||||
<!-- Preview Table Card (expandable height, scrollable) -->
|
|
||||||
<div class="card scan-table-card" style="margin-bottom: 24px; max-height: 480px; overflow-y: auto;">
|
|
||||||
<h3>Preview Table</h3>
|
|
||||||
<table class="scan-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Location Code</th>
|
|
||||||
<th>Size</th>
|
|
||||||
<th>Description</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% if locations %}
|
|
||||||
{% for loc in locations %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ loc[0] }}</td>
|
|
||||||
<td>{{ loc[1] }}</td>
|
|
||||||
<td>{{ loc[2] }}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
{% else %}
|
|
||||||
<tr><td colspan="3" style="text-align:center;">No CSV file uploaded yet.</td></tr>
|
|
||||||
{% endif %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{% if report %}
|
|
||||||
<div class="card" style="margin-bottom: 24px;">
|
|
||||||
<h4>Import Report</h4>
|
|
||||||
<p>{{ report }}</p>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block title %}Label Templates{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="card-container">
|
|
||||||
<div class="card">
|
|
||||||
<h3>List of Label Templates</h3>
|
|
||||||
<ul class="user-list">
|
|
||||||
{% for template in templates %}
|
|
||||||
<li data-template-id="{{ template.id }}">
|
|
||||||
<span class="template-name">{{ template.name }}</span>
|
|
||||||
<button class="btn edit-btn">Edit</button>
|
|
||||||
<button class="btn delete-btn">Delete</button>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
<button id="create-template-btn" class="btn create-btn">Create New Template</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -24,11 +24,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Card 3: Manage Label Templates -->
|
<!-- Card 3: Manage Label Templates -->
|
||||||
<div class="dashboard-card">
|
|
||||||
<h3>Manage Label Templates</h3>
|
|
||||||
<p>Manage and configure label templates.</p>
|
|
||||||
<a href="{{ url_for('main.label_templates') }}" class="btn">Manage Templates</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user