starting daily mirror

This commit is contained in:
Quality System Admin
2025-10-25 02:15:54 +03:00
parent 1afd09b88b
commit 87469ecb8e
23 changed files with 5849 additions and 14 deletions

View File

@@ -39,18 +39,15 @@
{% endif %}
</div>
<div class="right-header">
<button id="theme-toggle" class="theme-toggle">Change to dark theme</button>
{% if request.endpoint in ['main.upload_data', 'main.upload_orders', 'main.print_module', 'main.label_templates', 'main.create_template', 'main.print_lost_labels', 'main.view_orders'] %}
<a href="{{ url_for('main.etichete') }}" class="btn go-to-main-etichete-btn">Main Page Etichete</a>
{% endif %}
{% if request.endpoint in ['main.quality', 'main.fg_quality'] %}
<a href="{{ url_for('main.reports') }}" class="btn go-to-main-reports-btn">Main Page Reports</a>
{% endif %}
<a href="{{ url_for('main.dashboard') }}" class="btn go-to-dashboard-btn">Go to Dashboard</a>
{% if 'user' in session %}
<span class="user-info">You are logged in as {{ session['user'] }}</span>
<a href="{{ url_for('main.logout') }}" class="logout-button">Logout</a>
{% endif %}
<button id="theme-toggle" class="theme-toggle">Change to dark theme</button>
{% if request.endpoint.startswith('daily_mirror') %}
<a href="{{ url_for('daily_mirror.daily_mirror_main_route') }}" class="btn btn-info btn-sm ms-2"> <i class="fas fa-home"></i> Daily Mirror Main</a>
{% endif %}
<a href="{{ url_for('main.dashboard') }}" class="btn go-to-dashboard-btn ms-2">Go to Dashboard</a>
{% if 'user' in session %}
<span class="user-info ms-2">You are logged in as {{ session['user'] }}</span>
<a href="{{ url_for('main.logout') }}" class="logout-button ms-2">Logout</a>
{% endif %}
</div>
</div>
</header>

View File

@@ -0,0 +1,447 @@
{% extends "base.html" %}
{% block title %}Daily Mirror - Quality Recticel{% endblock %}
{% block content %}
<div class="container-fluid">
<!-- Page Header -->
<div class="row mb-4">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center">
<div>
<h1 class="h3 mb-0">📈 Daily Mirror</h1>
<p class="text-muted">Generate comprehensive daily production reports</p>
</div>
<div>
<a href="{{ url_for('daily_mirror.daily_mirror_history_route') }}" class="btn btn-outline-primary">
<i class="fas fa-history"></i> View History
</a>
<a href="{{ url_for('main.dashboard') }}" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Back to Dashboard
</a>
</div>
</div>
</div>
</div>
<!-- Date Selection Card -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-calendar-alt"></i> Select Report Date
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-4">
<label for="reportDate" class="form-label">Report Date:</label>
<input type="date" class="form-control" id="reportDate" value="{{ today }}">
</div>
<div class="col-md-4 d-flex align-items-end">
<button type="button" class="btn btn-primary" onclick="generateDailyReport()">
<i class="fas fa-chart-line"></i> Generate Report
</button>
</div>
<div class="col-md-4 d-flex align-items-end">
<button type="button" class="btn btn-success" onclick="setTodayDate()">
<i class="fas fa-calendar-day"></i> Today's Report
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Loading Indicator -->
<div id="loadingIndicator" class="row mb-4" style="display: none;">
<div class="col-12">
<div class="card">
<div class="card-body text-center">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-2 mb-0">Generating daily report...</p>
</div>
</div>
</div>
</div>
<!-- Daily Report Results -->
<div id="reportResults" style="display: none;">
<!-- Key Metrics Overview -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-tachometer-alt"></i> Daily Production Overview
<span id="reportDateDisplay" class="badge bg-primary ms-2"></span>
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-3">
<div class="metric-card orders-quantity">
<div class="metric-icon">
<i class="fas fa-clipboard-list"></i>
</div>
<div class="metric-content">
<h3 id="ordersQuantity">-</h3>
<p>Orders Quantity</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="metric-card production-launched">
<div class="metric-icon">
<i class="fas fa-play-circle"></i>
</div>
<div class="metric-content">
<h3 id="productionLaunched">-</h3>
<p>Production Launched</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="metric-card production-finished">
<div class="metric-icon">
<i class="fas fa-check-circle"></i>
</div>
<div class="metric-content">
<h3 id="productionFinished">-</h3>
<p>Production Finished</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="metric-card orders-delivered">
<div class="metric-icon">
<i class="fas fa-truck"></i>
</div>
<div class="metric-content">
<h3 id="ordersDelivered">-</h3>
<p>Orders Delivered</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Quality Control Metrics -->
<div class="row mb-4">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-search"></i> Quality Control Scans
</h5>
</div>
<div class="card-body">
<div class="quality-stats">
<div class="row">
<div class="col-4">
<div class="stat-item">
<h4 id="qualityTotalScans">-</h4>
<p>Total Scans</p>
</div>
</div>
<div class="col-4">
<div class="stat-item approved">
<h4 id="qualityApprovedScans">-</h4>
<p>Approved</p>
</div>
</div>
<div class="col-4">
<div class="stat-item rejected">
<h4 id="qualityRejectedScans">-</h4>
<p>Rejected</p>
</div>
</div>
</div>
<div class="mt-3">
<div class="progress">
<div id="qualityApprovalBar" class="progress-bar bg-success" role="progressbar" style="width: 0%"></div>
</div>
<p class="text-center mt-2 mb-0">
Approval Rate: <span id="qualityApprovalRate" class="fw-bold">0%</span>
</p>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-clipboard-check"></i> Finish Goods Quality
</h5>
</div>
<div class="card-body">
<div class="quality-stats">
<div class="row">
<div class="col-4">
<div class="stat-item">
<h4 id="fgQualityTotalScans">-</h4>
<p>Total Scans</p>
</div>
</div>
<div class="col-4">
<div class="stat-item approved">
<h4 id="fgQualityApprovedScans">-</h4>
<p>Approved</p>
</div>
</div>
<div class="col-4">
<div class="stat-item rejected">
<h4 id="fgQualityRejectedScans">-</h4>
<p>Rejected</p>
</div>
</div>
</div>
<div class="mt-3">
<div class="progress">
<div id="fgQualityApprovalBar" class="progress-bar bg-success" role="progressbar" style="width: 0%"></div>
</div>
<p class="text-center mt-2 mb-0">
Approval Rate: <span id="fgQualityApprovalRate" class="fw-bold">0%</span>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Export and Actions -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-download"></i> Export Options
</h5>
</div>
<div class="card-body">
<div class="btn-group" role="group">
<button type="button" class="btn btn-outline-success" onclick="exportReportPDF()">
<i class="fas fa-file-pdf"></i> Export PDF
</button>
<button type="button" class="btn btn-outline-primary" onclick="exportReportExcel()">
<i class="fas fa-file-excel"></i> Export Excel
</button>
<button type="button" class="btn btn-outline-info" onclick="printReport()">
<i class="fas fa-print"></i> Print Report
</button>
<button type="button" class="btn btn-outline-secondary" onclick="shareReport()">
<i class="fas fa-share"></i> Share Report
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Error Message -->
<div id="errorMessage" class="row mb-4" style="display: none;">
<div class="col-12">
<div class="alert alert-danger" role="alert">
<i class="fas fa-exclamation-triangle"></i>
<strong>Error:</strong> <span id="errorText"></span>
</div>
</div>
</div>
</div>
<style>
.metric-card {
display: flex;
align-items: center;
padding: 1.5rem;
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
border-radius: 10px;
margin-bottom: 1rem;
transition: transform 0.2s ease;
}
.metric-card:hover {
transform: translateY(-2px);
}
.metric-card.orders-quantity {
background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
}
.metric-card.production-launched {
background: linear-gradient(135deg, #f3e5f5 0%, #ce93d8 100%);
}
.metric-card.production-finished {
background: linear-gradient(135deg, #e8f5e8 0%, #a5d6a7 100%);
}
.metric-card.orders-delivered {
background: linear-gradient(135deg, #fff3e0 0%, #ffcc02 100%);
}
.metric-icon {
font-size: 2.5rem;
margin-right: 1rem;
opacity: 0.8;
}
.metric-content h3 {
font-size: 2rem;
font-weight: bold;
margin: 0;
color: #2c3e50;
}
.metric-content p {
margin: 0;
color: #6c757d;
font-weight: 500;
}
.quality-stats .stat-item {
text-align: center;
padding: 1rem 0;
}
.quality-stats .stat-item h4 {
font-size: 1.5rem;
font-weight: bold;
margin: 0;
color: #2c3e50;
}
.quality-stats .stat-item.approved h4 {
color: #28a745;
}
.quality-stats .stat-item.rejected h4 {
color: #dc3545;
}
.quality-stats .stat-item p {
margin: 0;
color: #6c757d;
font-size: 0.9rem;
}
@media (max-width: 768px) {
.metric-card {
flex-direction: column;
text-align: center;
}
.metric-icon {
margin-right: 0;
margin-bottom: 0.5rem;
}
}
</style>
<script>
function setTodayDate() {
const today = new Date().toISOString().split('T')[0];
document.getElementById('reportDate').value = today;
generateDailyReport();
}
function generateDailyReport() {
const reportDate = document.getElementById('reportDate').value;
if (!reportDate) {
showError('Please select a report date');
return;
}
// Show loading indicator
document.getElementById('loadingIndicator').style.display = 'block';
document.getElementById('reportResults').style.display = 'none';
document.getElementById('errorMessage').style.display = 'none';
// Make API call to get daily data
fetch(`/daily_mirror/api/data?date=${reportDate}`)
.then(response => response.json())
.then(data => {
if (data.error) {
showError(data.error);
return;
}
// Update display with data
updateDailyReport(data);
// Hide loading and show results
document.getElementById('loadingIndicator').style.display = 'none';
document.getElementById('reportResults').style.display = 'block';
})
.catch(error => {
console.error('Error generating daily report:', error);
showError('Failed to generate daily report. Please try again.');
});
}
function updateDailyReport(data) {
// Update date display
document.getElementById('reportDateDisplay').textContent = data.date;
// Update key metrics
document.getElementById('ordersQuantity').textContent = data.orders_quantity.toLocaleString();
document.getElementById('productionLaunched').textContent = data.production_launched.toLocaleString();
document.getElementById('productionFinished').textContent = data.production_finished.toLocaleString();
document.getElementById('ordersDelivered').textContent = data.orders_delivered.toLocaleString();
// Update quality control data
document.getElementById('qualityTotalScans').textContent = data.quality_scans.total_scans.toLocaleString();
document.getElementById('qualityApprovedScans').textContent = data.quality_scans.approved_scans.toLocaleString();
document.getElementById('qualityRejectedScans').textContent = data.quality_scans.rejected_scans.toLocaleString();
document.getElementById('qualityApprovalRate').textContent = data.quality_scans.approval_rate + '%';
document.getElementById('qualityApprovalBar').style.width = data.quality_scans.approval_rate + '%';
// Update FG quality data
document.getElementById('fgQualityTotalScans').textContent = data.fg_quality_scans.total_scans.toLocaleString();
document.getElementById('fgQualityApprovedScans').textContent = data.fg_quality_scans.approved_scans.toLocaleString();
document.getElementById('fgQualityRejectedScans').textContent = data.fg_quality_scans.rejected_scans.toLocaleString();
document.getElementById('fgQualityApprovalRate').textContent = data.fg_quality_scans.approval_rate + '%';
document.getElementById('fgQualityApprovalBar').style.width = data.fg_quality_scans.approval_rate + '%';
}
function showError(message) {
document.getElementById('errorText').textContent = message;
document.getElementById('errorMessage').style.display = 'block';
document.getElementById('loadingIndicator').style.display = 'none';
document.getElementById('reportResults').style.display = 'none';
}
function exportReportPDF() {
alert('PDF export functionality will be implemented soon.');
}
function exportReportExcel() {
alert('Excel export functionality will be implemented soon.');
}
function printReport() {
window.print();
}
function shareReport() {
alert('Share functionality will be implemented soon.');
}
// Auto-generate today's report on page load
document.addEventListener('DOMContentLoaded', function() {
generateDailyReport();
});
</script>
{% endblock %}

View File

@@ -0,0 +1,719 @@
{% extends "base.html" %}
{% block title %}Build Database - Daily Mirror{% endblock %}
{% block content %}
<div class="container-fluid">
<!-- Page Header -->
<div class="row mb-4">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center">
<div>
<h1 class="h3 mb-0">🔨 Build Database</h1>
<p class="text-muted">Upload Excel files to populate Daily Mirror database tables</p>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6 mb-4">
<!-- Card 1: Upload Excel File -->
<div class="card h-100">
<div class="card-header bg-primary text-white">
<h5 class="card-title mb-0">
<i class="fas fa-upload"></i> Upload Excel File
</h5>
</div>
<div class="card-body">
<form method="POST" enctype="multipart/form-data" id="uploadForm">
<!-- Table Selection -->
<div class="form-group mb-4">
<label for="target_table" class="form-label">
<strong>Select Target Table:</strong>
</label>
<select class="form-control" name="target_table" id="target_table" required>
<option value="">-- Choose a table --</option>
{% for table in available_tables %}
<option value="{{ table.name }}" data-description="{{ table.description }}">
{{ table.display }}
</option>
{% endfor %}
</select>
<small id="tableDescription" class="form-text text-muted mt-2">
Select a table to see its description.
</small>
</div>
<!-- File Upload -->
<div class="form-group mb-4">
<label for="excel_file" class="form-label">
<strong>Select Excel File:</strong>
</label>
<input type="file" class="form-control" name="excel_file" id="excel_file"
accept=".xlsx,.xls" required>
<small class="form-text text-muted">
Accepted formats: .xlsx, .xls (Maximum file size: 10MB)
</small>
</div>
<!-- Upload Button -->
<div class="text-center">
<button type="button" class="btn btn-primary btn-lg" id="uploadBtn">
<i class="fas fa-cloud-upload-alt"></i> Upload and Process File
</button>
</div>
</form>
</div>
</div>
</div>
<div class="col-lg-6 mb-4">
<!-- Card 2: Excel File Format Instructions -->
<div class="card h-100">
<div class="card-header bg-info text-white">
<h5 class="card-title mb-0">
<i class="fas fa-info-circle"></i> Excel File Format Instructions
</h5>
</div>
<div class="card-body">
<div class="accordion" id="formatAccordion">
<!-- Production Data Format -->
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
data-bs-target="#productionCollapse" aria-expanded="false">
🏭 Production Data Format
</button>
</h2>
<div id="productionCollapse" class="accordion-collapse collapse" data-bs-parent="#formatAccordion">
<div class="accordion-body">
<p><strong>Expected columns for Production Data:</strong></p>
<div class="row">
<div class="col-md-6">
<ul class="list-unstyled mb-0">
<li><code>Production Order ID</code> <span class="text-muted">Unique identifier</span></li>
<li><code>Customer Code</code> <span class="text-muted">Customer code</span></li>
<li><code>Customer Name</code> <span class="text-muted">Customer name</span></li>
<li><code>Article Code</code> <span class="text-muted">Article code</span></li>
</ul>
</div>
<div class="col-md-6">
<ul class="list-unstyled mb-0">
<li><code>Article Description</code> <span class="text-muted">Description</span></li>
<li><code>Quantity</code> <span class="text-muted">To produce</span></li>
<li><code>Production Date</code> <span class="text-muted">Date</span></li>
<li><code>Status</code> <span class="text-muted">Production status</span></li>
</ul>
</div>
</div>
</div>
</div>
</div>
<!-- Orders Data Format -->
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
data-bs-target="#ordersCollapse" aria-expanded="false">
🛒 Orders Data Format
</button>
</h2>
<div id="ordersCollapse" class="accordion-collapse collapse" data-bs-parent="#formatAccordion">
<div class="accordion-body">
<p><strong>Expected columns for Orders Data:</strong></p>
<div class="row">
<div class="col-md-6">
<ul class="list-unstyled mb-0">
<li><code>Order ID</code> <span class="text-muted">Unique identifier</span></li>
<li><code>Customer Code</code> <span class="text-muted">Customer code</span></li>
<li><code>Customer Name</code> <span class="text-muted">Customer name</span></li>
<li><code>Article Code</code> <span class="text-muted">Article code</span></li>
</ul>
</div>
<div class="col-md-6">
<ul class="list-unstyled mb-0">
<li><code>Article Description</code> <span class="text-muted">Description</span></li>
<li><code>Quantity Ordered</code> <span class="text-muted">Ordered</span></li>
<li><code>Order Date</code> <span class="text-muted">Date</span></li>
<li><code>Status</code> <span class="text-muted">Order status</span></li>
</ul>
</div>
</div>
</div>
</div>
</div>
<!-- Delivery Data Format -->
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
data-bs-target="#deliveryCollapse" aria-expanded="false">
🚚 Delivery Data Format (Articole livrate)
</button>
</h2>
<div id="deliveryCollapse" class="accordion-collapse collapse" data-bs-parent="#formatAccordion">
<div class="accordion-body">
<p><strong>Expected columns for Delivery Data:</strong></p>
<div class="row">
<div class="col-md-6">
<ul class="list-unstyled mb-0">
<li><code>Shipment ID</code> <span class="text-muted">Unique shipment identifier</span></li>
<li><code>Order ID</code> <span class="text-muted">Related order</span></li>
<li><code>Customer</code> <span class="text-muted">Customer info</span></li>
<li><code>Article</code> <span class="text-muted">Code/description</span></li>
</ul>
</div>
<div class="col-md-6">
<ul class="list-unstyled mb-0">
<li><code>Quantity Delivered</code> <span class="text-muted">Delivered quantity</span></li>
<li><code>Delivery Date</code> <span class="text-muted">Date</span></li>
<li><code>Status</code> <span class="text-muted">Delivery status</span></li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Upload Result Modal (Better Solution) -->
<div class="modal fade" id="uploadResultModal" tabindex="-1" aria-labelledby="uploadResultModalLabel" aria-hidden="true" data-bs-backdrop="true" data-bs-keyboard="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header" id="modalHeader">
<h5 class="modal-title" id="uploadResultModalLabel">
<i class="fas fa-check-circle"></i> Upload Result
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" id="modalCloseBtn"></button>
</div>
<div class="modal-body">
<div id="uploadResultContent" class="text-center py-3">
<!-- Result content will be inserted here -->
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" id="modalOkBtn">OK</button>
</div>
</div>
</div>
</div>
<style>
.accordion-button:not(.collapsed) {
background-color: #e7f3ff;
color: #0066cc;
}
.form-control:focus {
border-color: #007bff;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
.btn-primary {
transition: all 0.3s ease;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 123, 255, 0.3);
}
/* Result stats styling */
.upload-stats {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
margin-top: 20px;
}
.stat-box {
text-align: center;
padding: 15px;
min-width: 100px;
margin: 5px;
border-radius: 8px;
}
.stat-box.success {
background-color: #d4edda;
border: 1px solid #c3e6cb;
}
.stat-box.warning {
background-color: #fff3cd;
border: 1px solid #ffeeba;
}
.stat-box.error {
background-color: #f8d7da;
border: 1px solid #f5c6cb;
}
.stat-value {
font-size: 2rem;
font-weight: bold;
display: block;
}
.stat-label {
font-size: 0.9rem;
color: #666;
}
/* Reduce font size for Excel Format Instructions card rows */
.col-lg-6:nth-child(2) .card-body {
font-size: 0.95rem;
}
/* Make accordion button labels smaller */
.accordion-button {
font-size: 1rem;
padding-top: 0.4rem;
padding-bottom: 0.4rem;
}
/* Override h2 size in accordion headers */
.accordion-header {
font-size: 1rem;
}
.accordion-header h2 {
font-size: 1rem;
margin: 0;
}
/* Modal summary list styling */
#uploadResultContent ul {
list-style-type: none;
padding-left: 0;
margin: 10px auto;
max-width: 400px;
}
#uploadResultContent ul li {
padding: 5px 0;
font-size: 1rem;
}
#uploadResultContent ul li::before {
content: '✓ ';
color: #28a745;
font-weight: bold;
margin-right: 5px;
}
/* Make "Expected columns" text smaller in accordion bodies */
.accordion-body p {
font-size: 0.9rem;
}
.accordion-body strong {
font-size: 0.9rem;
}
/* Dark mode styles */
body.dark-mode .card {
background-color: #2d3748;
border-color: #4a5568;
color: #e2e8f0;
box-shadow: 0 4px 6px rgba(255, 255, 255, 0.1);
}
body.dark-mode .card-header {
background-color: #4a5568;
color: #e2e8f0;
border-bottom: 1px solid rgba(226, 232, 240, 0.2);
}
body.dark-mode .form-control {
background-color: #4a5568;
border-color: #6b7280;
color: #e2e8f0;
}
body.dark-mode .form-control:focus {
background-color: #4a5568;
border-color: #007bff;
color: #e2e8f0;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
body.dark-mode .accordion-button {
background-color: #374151;
color: #e2e8f0;
border-color: #6b7280;
}
body.dark-mode .accordion-button:not(.collapsed) {
background-color: #1e3a8a;
color: #bfdbfe;
}
body.dark-mode .accordion-body {
background-color: #374151;
color: #e2e8f0;
}
body.dark-mode code {
background-color: #374151;
color: #e2e8f0;
border: 1px solid #6b7280;
}
body.dark-mode .modal-content {
background-color: #2d3748;
color: #e2e8f0;
}
body.dark-mode .modal-header {
border-bottom-color: #4a5568;
}
body.dark-mode .modal-footer {
border-top-color: #4a5568;
}
body.dark-mode .stat-box.success {
background-color: #1e4620;
border-color: #2d5a2e;
color: #a3d9a5;
}
body.dark-mode .stat-box.warning {
background-color: #5a4a1e;
border-color: #6b5a2d;
color: #f4d88f;
}
body.dark-mode .stat-box.error {
background-color: #5a1e1e;
border-color: #6b2d2d;
color: #f8a3a8;
}
body.dark-mode .stat-label {
color: #a0aec0;
}
body.dark-mode .text-muted {
color: #a0aec0 !important;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Initialize theme on page load
const savedTheme = localStorage.getItem('theme');
const body = document.body;
if (savedTheme === 'dark') {
body.classList.add('dark-mode');
} else {
body.classList.remove('dark-mode');
}
const tableSelect = document.getElementById('target_table');
const tableDescription = document.getElementById('tableDescription');
const uploadBtn = document.getElementById('uploadBtn');
const uploadForm = document.getElementById('uploadForm');
const fileInput = document.getElementById('excel_file');
// Update table description when selection changes
tableSelect.addEventListener('change', function() {
const selectedOption = this.options[this.selectedIndex];
if (selectedOption.value) {
const description = selectedOption.getAttribute('data-description');
tableDescription.innerHTML = `<strong>Selected:</strong> ${description}`;
tableDescription.className = 'form-text text-info mt-2';
} else {
tableDescription.innerHTML = 'Select a table to see its description.';
tableDescription.className = 'form-text text-muted mt-2';
}
});
// File input change handler to show file info
fileInput.addEventListener('change', function(e) {
const file = e.target.files[0];
if (file) {
const fileName = file.name;
const fileSize = (file.size / 1024 / 1024).toFixed(2);
console.log(`Selected file: ${fileName} (${fileSize} MB)`);
}
});
// Upload button click handler (AJAX submission)
uploadBtn.addEventListener('click', function(e) {
e.preventDefault();
// Validate file selection
if (!fileInput.files.length) {
alert('Please select an Excel file to upload.');
return;
}
// Validate table selection
if (!tableSelect.value) {
alert('Please select a target table.');
return;
}
// Check file size (10MB limit)
const file = fileInput.files[0];
if (file.size > 10 * 1024 * 1024) {
alert('File size must be less than 10MB.');
return;
}
// Prepare form data
const formData = new FormData(uploadForm);
// Show loading state
uploadBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Processing...';
uploadBtn.disabled = true;
// Submit via AJAX
fetch('/daily_mirror/build_database', {
method: 'POST',
body: formData,
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => {
if (!response.ok) {
return response.json().then(err => Promise.reject(err));
}
return response.json();
})
.then(result => {
// Reset button
uploadBtn.innerHTML = '<i class="fas fa-cloud-upload-alt"></i> Upload and Process File';
uploadBtn.disabled = false;
// Show result in modal
showUploadResult(result);
// Reset form
uploadForm.reset();
tableDescription.innerHTML = 'Select a table to see its description.';
tableDescription.className = 'form-text text-muted mt-2';
})
.catch(error => {
// Reset button
uploadBtn.innerHTML = '<i class="fas fa-cloud-upload-alt"></i> Upload and Process File';
uploadBtn.disabled = false;
// Show error in modal
showUploadError(error);
});
});
function showUploadResult(result) {
const modal = new bootstrap.Modal(document.getElementById('uploadResultModal'));
const modalHeader = document.getElementById('modalHeader');
const modalTitle = document.getElementById('uploadResultModalLabel');
const content = document.getElementById('uploadResultContent');
// Determine overall status
const hasErrors = result.error_count && result.error_count > 0;
const hasSuccess = result.created_rows > 0 || result.updated_rows > 0;
// Update modal header color
if (hasErrors && !hasSuccess) {
modalHeader.className = 'modal-header bg-danger text-white';
modalTitle.innerHTML = '<i class="fas fa-times-circle"></i> Upload Failed';
} else if (hasErrors && hasSuccess) {
modalHeader.className = 'modal-header bg-warning text-dark';
modalTitle.innerHTML = '<i class="fas fa-exclamation-triangle"></i> Upload Completed with Warnings';
} else {
modalHeader.className = 'modal-header bg-success text-white';
modalTitle.innerHTML = '<i class="fas fa-check-circle"></i> Upload Successful';
}
// Build result content with stats
let html = '<div class="upload-stats">';
// Total rows processed from Excel
html += `
<div class="stat-box ${hasErrors ? 'warning' : 'success'}">
<span class="stat-value">${result.total_rows || 0}</span>
<span class="stat-label">Rows Processed</span>
</div>
`;
// Created rows (new in database)
if (result.created_rows > 0) {
html += `
<div class="stat-box success">
<span class="stat-value">${result.created_rows}</span>
<span class="stat-label">New Rows Created</span>
</div>
`;
}
// Updated rows (existing in database)
if (result.updated_rows > 0) {
html += `
<div class="stat-box success">
<span class="stat-value">${result.updated_rows}</span>
<span class="stat-label">Rows Updated</span>
</div>
`;
}
// Errors
if (hasErrors) {
html += `
<div class="stat-box error">
<span class="stat-value">${result.error_count}</span>
<span class="stat-label">Errors</span>
</div>
`;
}
html += '</div>';
// Add detailed summary message
const successCount = (result.created_rows || 0) + (result.updated_rows || 0);
if (successCount > 0) {
let msg = `<p class="mt-3 mb-0"><strong>Successfully processed ${result.total_rows} rows from Excel:</strong></p>`;
msg += '<ul class="text-start">';
if (result.created_rows > 0) {
msg += `<li>${result.created_rows} new ${result.created_rows === 1 ? 'record' : 'records'} created in database</li>`;
}
if (result.updated_rows > 0) {
msg += `<li>${result.updated_rows} existing ${result.updated_rows === 1 ? 'record' : 'records'} updated</li>`;
}
msg += '</ul>';
html += msg;
}
if (hasErrors) {
html += `<p class="text-danger mb-0"><strong>⚠️ ${result.error_count} ${result.error_count === 1 ? 'row' : 'rows'} could not be processed due to errors.</strong></p>`;
}
// Add auto-close countdown for successful uploads without errors
if (!hasErrors && successCount > 0) {
html += `<p class="text-muted mt-2 mb-0" id="autoCloseCountdown"><small>This window will close automatically in <span id="countdown">3</span> seconds...</small></p>`;
}
content.innerHTML = html;
modal.show();
// Get modal element
const modalElement = document.getElementById('uploadResultModal');
// Add explicit close handlers
const okBtn = document.getElementById('modalOkBtn');
const closeBtn = document.getElementById('modalCloseBtn');
if (okBtn) {
okBtn.onclick = function() {
modal.hide();
// Also trigger Bootstrap's native close
modalElement.classList.remove('show');
document.querySelector('.modal-backdrop')?.remove();
document.body.classList.remove('modal-open');
document.body.style.overflow = '';
document.body.style.paddingRight = '';
};
}
if (closeBtn) {
closeBtn.onclick = function() {
modal.hide();
// Also trigger Bootstrap's native close
modalElement.classList.remove('show');
document.querySelector('.modal-backdrop')?.remove();
document.body.classList.remove('modal-open');
document.body.style.overflow = '';
document.body.style.paddingRight = '';
};
}
// Auto-close after 3 seconds for successful uploads without errors
if (!hasErrors && successCount > 0) {
let countdown = 3;
const countdownInterval = setInterval(function() {
countdown--;
const countdownSpan = document.getElementById('countdown');
if (countdownSpan) {
countdownSpan.textContent = countdown;
}
if (countdown <= 0) {
clearInterval(countdownInterval);
}
}, 1000);
setTimeout(function() {
clearInterval(countdownInterval);
modal.hide();
modalElement.classList.remove('show');
document.querySelector('.modal-backdrop')?.remove();
document.body.classList.remove('modal-open');
document.body.style.overflow = '';
document.body.style.paddingRight = '';
}, 3000);
}
}
function showUploadError(error) {
const modal = new bootstrap.Modal(document.getElementById('uploadResultModal'));
const modalHeader = document.getElementById('modalHeader');
const modalTitle = document.getElementById('uploadResultModalLabel');
const content = document.getElementById('uploadResultContent');
// Update modal header
modalHeader.className = 'modal-header bg-danger text-white';
modalTitle.innerHTML = '<i class="fas fa-times-circle"></i> Upload Error';
// Show error message
const errorMsg = error.error || error.message || 'An unexpected error occurred during upload.';
content.innerHTML = `
<div class="alert alert-danger mb-0">
<i class="fas fa-exclamation-triangle"></i>
<strong>Error:</strong> ${errorMsg}
</div>
`;
modal.show();
// Get modal element
const modalElement = document.getElementById('uploadResultModal');
// Add explicit close handlers
const okBtn = document.getElementById('modalOkBtn');
const closeBtn = document.getElementById('modalCloseBtn');
if (okBtn) {
okBtn.onclick = function() {
modal.hide();
// Also trigger Bootstrap's native close
modalElement.classList.remove('show');
document.querySelector('.modal-backdrop')?.remove();
document.body.classList.remove('modal-open');
document.body.style.overflow = '';
document.body.style.paddingRight = '';
};
}
if (closeBtn) {
closeBtn.onclick = function() {
modal.hide();
// Also trigger Bootstrap's native close
modalElement.classList.remove('show');
document.querySelector('.modal-backdrop')?.remove();
document.body.classList.remove('modal-open');
document.body.style.overflow = '';
document.body.style.paddingRight = '';
};
}
}
});
</script>
{% endblock %}

View File

@@ -0,0 +1,449 @@
{% extends "base.html" %}
{% block title %}Daily Mirror History - Quality Recticel{% endblock %}
{% block content %}
<div class="container-fluid">
<!-- Page Header -->
<div class="row mb-4">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center">
<div>
<h1 class="h3 mb-0">📋 Daily Mirror History</h1>
<p class="text-muted">Analyze historical daily production reports and trends</p>
</div>
<div>
<a href="{{ url_for('daily_mirror.daily_mirror_route') }}" class="btn btn-outline-success">
<i class="fas fa-chart-line"></i> Create New Report
</a>
<a href="{{ url_for('main.dashboard') }}" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Back to Dashboard
</a>
</div>
</div>
</div>
</div>
<!-- Date Range Selection -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-calendar-week"></i> Select Date Range
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-3">
<label for="startDate" class="form-label">Start Date:</label>
<input type="date" class="form-control" id="startDate" value="{{ start_date }}">
</div>
<div class="col-md-3">
<label for="endDate" class="form-label">End Date:</label>
<input type="date" class="form-control" id="endDate" value="{{ end_date }}">
</div>
<div class="col-md-3 d-flex align-items-end">
<button type="button" class="btn btn-primary" onclick="loadHistoryData()">
<i class="fas fa-search"></i> Load History
</button>
</div>
<div class="col-md-3 d-flex align-items-end">
<div class="btn-group" role="group">
<button type="button" class="btn btn-outline-secondary" onclick="setDateRange(7)">Last 7 days</button>
<button type="button" class="btn btn-outline-secondary" onclick="setDateRange(30)">Last 30 days</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Loading Indicator -->
<div id="loadingIndicator" class="row mb-4" style="display: none;">
<div class="col-12">
<div class="card">
<div class="card-body text-center">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-2 mb-0">Loading historical data...</p>
</div>
</div>
</div>
</div>
<!-- Summary Statistics -->
<div id="summaryStats" style="display: none;">
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-chart-bar"></i> Period Summary
<span id="periodRange" class="badge bg-secondary ms-2"></span>
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-3">
<div class="summary-metric">
<h4 id="totalOrdersQuantity">-</h4>
<p>Total Orders Quantity</p>
<small id="avgOrdersQuantity" class="text-muted">Avg: -</small>
</div>
</div>
<div class="col-md-3">
<div class="summary-metric">
<h4 id="totalProductionLaunched">-</h4>
<p>Total Production Launched</p>
<small id="avgProductionLaunched" class="text-muted">Avg: -</small>
</div>
</div>
<div class="col-md-3">
<div class="summary-metric">
<h4 id="totalProductionFinished">-</h4>
<p>Total Production Finished</p>
<small id="avgProductionFinished" class="text-muted">Avg: -</small>
</div>
</div>
<div class="col-md-3">
<div class="summary-metric">
<h4 id="totalOrdersDelivered">-</h4>
<p>Total Orders Delivered</p>
<small id="avgOrdersDelivered" class="text-muted">Avg: -</small>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Historical Data Table -->
<div id="historyTable" style="display: none;">
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">
<i class="fas fa-table"></i> Historical Daily Reports
</h5>
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-outline-primary" onclick="exportHistoryCSV()">
<i class="fas fa-file-csv"></i> CSV
</button>
<button type="button" class="btn btn-outline-success" onclick="exportHistoryExcel()">
<i class="fas fa-file-excel"></i> Excel
</button>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-striped table-hover" id="historyDataTable">
<thead class="table-dark">
<tr>
<th>Date</th>
<th>Orders Quantity</th>
<th>Production Launched</th>
<th>Production Finished</th>
<th>Orders Delivered</th>
<th>Quality Approval Rate</th>
<th>FG Quality Approval Rate</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="historyTableBody">
<!-- Data will be populated here -->
</tbody>
</table>
</div>
<!-- Pagination -->
<nav aria-label="History pagination" id="historyPagination" style="display: none;">
<ul class="pagination pagination-sm justify-content-center">
<!-- Pagination will be populated here -->
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
<!-- Chart Visualization -->
<div id="chartVisualization" style="display: none;">
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-chart-line"></i> Trend Analysis
</h5>
</div>
<div class="card-body">
<canvas id="trendChart" height="100"></canvas>
</div>
</div>
</div>
</div>
</div>
<!-- Error Message -->
<div id="errorMessage" class="row mb-4" style="display: none;">
<div class="col-12">
<div class="alert alert-danger" role="alert">
<i class="fas fa-exclamation-triangle"></i>
<strong>Error:</strong> <span id="errorText"></span>
</div>
</div>
</div>
</div>
<style>
.summary-metric {
text-align: center;
padding: 1rem;
background: #f8f9fa;
border-radius: 8px;
margin-bottom: 1rem;
}
.summary-metric h4 {
font-size: 1.8rem;
font-weight: bold;
color: #2c3e50;
margin: 0;
}
.summary-metric p {
margin: 0.5rem 0;
color: #6c757d;
font-weight: 500;
}
.table th {
white-space: nowrap;
}
.approval-rate {
font-weight: bold;
}
.approval-rate.high {
color: #28a745;
}
.approval-rate.medium {
color: #ffc107;
}
.approval-rate.low {
color: #dc3545;
}
@media (max-width: 768px) {
.table-responsive {
font-size: 0.9rem;
}
}
</style>
<script>
let historyData = [];
let currentPage = 1;
const itemsPerPage = 20;
function setDateRange(days) {
const endDate = new Date();
const startDate = new Date();
startDate.setDate(endDate.getDate() - days);
document.getElementById('endDate').value = endDate.toISOString().split('T')[0];
document.getElementById('startDate').value = startDate.toISOString().split('T')[0];
loadHistoryData();
}
function loadHistoryData() {
const startDate = document.getElementById('startDate').value;
const endDate = document.getElementById('endDate').value;
if (!startDate || !endDate) {
showError('Please select both start and end dates');
return;
}
if (new Date(startDate) > new Date(endDate)) {
showError('Start date cannot be after end date');
return;
}
// Show loading indicator
document.getElementById('loadingIndicator').style.display = 'block';
document.getElementById('summaryStats').style.display = 'none';
document.getElementById('historyTable').style.display = 'none';
document.getElementById('chartVisualization').style.display = 'none';
document.getElementById('errorMessage').style.display = 'none';
// Make API call to get historical data
fetch(`/daily_mirror/api/history_data?start_date=${startDate}&end_date=${endDate}`)
.then(response => response.json())
.then(data => {
if (data.error) {
showError(data.error);
return;
}
historyData = data.history;
// Update displays
updateSummaryStats(data);
updateHistoryTable();
updateTrendChart();
// Hide loading and show results
document.getElementById('loadingIndicator').style.display = 'none';
document.getElementById('summaryStats').style.display = 'block';
document.getElementById('historyTable').style.display = 'block';
document.getElementById('chartVisualization').style.display = 'block';
})
.catch(error => {
console.error('Error loading history data:', error);
showError('Failed to load historical data. Please try again.');
});
}
function updateSummaryStats(data) {
const history = data.history;
if (history.length === 0) {
document.getElementById('periodRange').textContent = 'No Data';
return;
}
document.getElementById('periodRange').textContent = `${data.start_date} to ${data.end_date}`;
// Calculate totals and averages
const totals = history.reduce((acc, day) => {
acc.ordersQuantity += day.orders_quantity;
acc.productionLaunched += day.production_launched;
acc.productionFinished += day.production_finished;
acc.ordersDelivered += day.orders_delivered;
return acc;
}, { ordersQuantity: 0, productionLaunched: 0, productionFinished: 0, ordersDelivered: 0 });
const avgDivisor = history.length;
document.getElementById('totalOrdersQuantity').textContent = totals.ordersQuantity.toLocaleString();
document.getElementById('avgOrdersQuantity').textContent = `Avg: ${Math.round(totals.ordersQuantity / avgDivisor).toLocaleString()}`;
document.getElementById('totalProductionLaunched').textContent = totals.productionLaunched.toLocaleString();
document.getElementById('avgProductionLaunched').textContent = `Avg: ${Math.round(totals.productionLaunched / avgDivisor).toLocaleString()}`;
document.getElementById('totalProductionFinished').textContent = totals.productionFinished.toLocaleString();
document.getElementById('avgProductionFinished').textContent = `Avg: ${Math.round(totals.productionFinished / avgDivisor).toLocaleString()}`;
document.getElementById('totalOrdersDelivered').textContent = totals.ordersDelivered.toLocaleString();
document.getElementById('avgOrdersDelivered').textContent = `Avg: ${Math.round(totals.ordersDelivered / avgDivisor).toLocaleString()}`;
}
function updateHistoryTable() {
const tbody = document.getElementById('historyTableBody');
tbody.innerHTML = '';
const startIndex = (currentPage - 1) * itemsPerPage;
const endIndex = startIndex + itemsPerPage;
const pageData = historyData.slice(startIndex, endIndex);
pageData.forEach(day => {
const row = document.createElement('tr');
const qualityRate = day.quality_scans.approval_rate;
const fgQualityRate = day.fg_quality_scans.approval_rate;
row.innerHTML = `
<td><strong>${day.date}</strong></td>
<td>${day.orders_quantity.toLocaleString()}</td>
<td>${day.production_launched.toLocaleString()}</td>
<td>${day.production_finished.toLocaleString()}</td>
<td>${day.orders_delivered.toLocaleString()}</td>
<td><span class="approval-rate ${getApprovalRateClass(qualityRate)}">${qualityRate}%</span></td>
<td><span class="approval-rate ${getApprovalRateClass(fgQualityRate)}">${fgQualityRate}%</span></td>
<td>
<button class="btn btn-sm btn-outline-primary" onclick="viewDayDetails('${day.date}')">
<i class="fas fa-eye"></i> View
</button>
</td>
`;
tbody.appendChild(row);
});
updatePagination();
}
function getApprovalRateClass(rate) {
if (rate >= 95) return 'high';
if (rate >= 85) return 'medium';
return 'low';
}
function updatePagination() {
const totalPages = Math.ceil(historyData.length / itemsPerPage);
if (totalPages <= 1) {
document.getElementById('historyPagination').style.display = 'none';
return;
}
document.getElementById('historyPagination').style.display = 'block';
// Pagination implementation can be added here
}
function updateTrendChart() {
// Chart implementation using Chart.js can be added here
// For now, we'll show a placeholder
const canvas = document.getElementById('trendChart');
const ctx = canvas.getContext('2d');
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw placeholder text
ctx.font = '16px Arial';
ctx.fillStyle = '#6c757d';
ctx.textAlign = 'center';
ctx.fillText('Trend chart visualization will be implemented here', canvas.width / 2, canvas.height / 2);
}
function viewDayDetails(date) {
// Navigate to daily mirror with specific date
window.open(`{{ url_for('daily_mirror.daily_mirror_route') }}?date=${date}`, '_blank');
}
function exportHistoryCSV() {
alert('CSV export functionality will be implemented soon.');
}
function exportHistoryExcel() {
alert('Excel export functionality will be implemented soon.');
}
function showError(message) {
document.getElementById('errorText').textContent = message;
document.getElementById('errorMessage').style.display = 'block';
document.getElementById('loadingIndicator').style.display = 'none';
document.getElementById('summaryStats').style.display = 'none';
document.getElementById('historyTable').style.display = 'none';
document.getElementById('chartVisualization').style.display = 'none';
}
// Auto-load data on page load
document.addEventListener('DOMContentLoaded', function() {
loadHistoryData();
});
</script>
{% endblock %}

View File

@@ -0,0 +1,262 @@
{% extends "base.html" %}
{% block title %}Daily Mirror - Quality Recticel{% endblock %}
{% block content %}
<div class="container-fluid">
<!-- Page Header -->
<div class="row mb-4">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center">
<div>
<h1 class="h3 mb-0">📊 Daily Mirror</h1>
<p class="text-muted">Business Intelligence and Production Reporting</p>
</div>
<div>
<!-- Buttons removed; now present in top header -->
</div>
</div>
</div>
</div>
<!-- Daily Mirror Cards -->
<div class="row">
<!-- Card 1: Build Database -->
<div class="col-lg-6 col-md-6 mb-4">
<div class="card h-100 daily-mirror-card">
<div class="card-body d-flex flex-column">
<div class="text-center mb-3">
<div class="feature-icon bg-primary">
<i class="fas fa-database"></i>
</div>
</div>
<h5 class="card-title text-center">Build Database</h5>
<p class="card-text flex-grow-1 text-center">
Upload Excel files to create and populate tables.
</p>
<div class="mt-auto">
<a href="{{ url_for('daily_mirror.daily_mirror_build_database') }}" class="btn btn-primary btn-block w-100">
<i class="fas fa-hammer"></i> Build Database
</a>
</div>
</div>
</div>
</div>
<!-- Card 2: Tune Database -->
<div class="col-lg-6 col-md-6 mb-4">
<div class="card h-100 daily-mirror-card">
<div class="card-body d-flex flex-column">
<div class="text-center mb-3">
<div class="feature-icon bg-warning">
<i class="fas fa-edit"></i>
</div>
</div>
<h5 class="card-title text-center">Tune Database</h5>
<p class="card-text flex-grow-1 text-center">
Edit and update records after import.
</p>
<div class="mt-auto">
<a href="{{ url_for('daily_mirror.tune_production_data') }}" class="btn btn-warning btn-block w-100 btn-sm mb-2">
<i class="fas fa-industry"></i> Production Orders
</a>
<a href="{{ url_for('daily_mirror.tune_orders_data') }}" class="btn btn-warning btn-block w-100 btn-sm mb-2">
<i class="fas fa-shopping-cart"></i> Customer Orders
</a>
<a href="{{ url_for('daily_mirror.tune_delivery_data') }}" class="btn btn-warning btn-block w-100 btn-sm">
<i class="fas fa-truck"></i> Delivery Records
</a>
</div>
</div>
</div>
</div>
<!-- Card 3: Daily Mirror -->
<div class="col-lg-6 col-md-6 mb-4">
<div class="card h-100 daily-mirror-card">
<div class="card-body d-flex flex-column">
<div class="text-center mb-3">
<div class="feature-icon bg-success">
<i class="fas fa-chart-line"></i>
</div>
</div>
<h5 class="card-title text-center">Daily Mirror</h5>
<p class="card-text flex-grow-1 text-center">
Generate daily production reports.
</p>
<div class="mt-auto">
<a href="{{ url_for('daily_mirror.daily_mirror_route') }}" class="btn btn-success btn-block w-100">
<i class="fas fa-plus-circle"></i> Create Daily Report
</a>
</div>
</div>
</div>
</div>
<!-- Card 4: Daily Mirror History -->
<div class="col-lg-6 col-md-6 mb-4">
<div class="card h-100 daily-mirror-card">
<div class="card-body d-flex flex-column">
<div class="text-center mb-3">
<div class="feature-icon bg-info">
<i class="fas fa-history"></i>
</div>
</div>
<h5 class="card-title text-center">Daily Mirror History</h5>
<p class="card-text flex-grow-1 text-center">
View historical production reports.
</p>
<div class="mt-auto">
<a href="{{ url_for('daily_mirror.daily_mirror_history_route') }}" class="btn btn-info btn-block w-100">
<i class="fas fa-chart-bar"></i> View History
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<style>
.daily-mirror-card {
transition: transform 0.2s ease, box-shadow 0.2s ease;
border: none;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
background-color: var(--card-bg);
color: var(--text-color);
}
.daily-mirror-card:hover {
transform: translateY(-5px);
box-shadow: 0 4px 15px rgba(0,0,0,0.15);
}
.feature-icon {
width: 80px;
height: 80px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto;
color: white;
font-size: 2rem;
}
/* Light mode styles */
body:not(.dark-mode) .daily-mirror-card {
background-color: #ffffff;
color: #333333;
border: 1px solid #e0e0e0;
}
body:not(.dark-mode) .card-text {
color: #666666;
}
body:not(.dark-mode) .text-muted {
color: #6c757d !important;
}
/* Dark mode styles */
body.dark-mode .daily-mirror-card {
background-color: #2d3748;
color: #e2e8f0;
border: 1px solid #4a5568;
box-shadow: 0 2px 4px rgba(255,255,255,0.1);
}
body.dark-mode .daily-mirror-card:hover {
box-shadow: 0 4px 15px rgba(255,255,255,0.15);
}
body.dark-mode .card-text {
color: #cbd5e0;
}
body.dark-mode .text-muted {
color: #a0aec0 !important;
}
body.dark-mode .h3 {
color: #e2e8f0;
}
body.dark-mode .container-fluid {
color: #e2e8f0;
}
/* Ensure buttons maintain their intended colors in both themes */
.btn-primary {
background: linear-gradient(135deg, #007bff 0%, #0056b3 100%);
border: none;
color: white;
}
.btn-warning {
background: linear-gradient(135deg, #ffc107 0%, #e0a800 100%);
border: none;
color: #212529;
}
.btn-success {
background: linear-gradient(135deg, #28a745 0%, #1e7e34 100%);
border: none;
color: white;
}
.btn-info {
background: linear-gradient(135deg, #17a2b8 0%, #138496 100%);
border: none;
color: white;
}
.btn-secondary {
background: linear-gradient(135deg, #6c757d 0%, #545b62 100%);
border: none;
color: white;
}
@media (max-width: 768px) {
.feature-icon {
width: 60px;
height: 60px;
font-size: 1.5rem;
}
}</style>
<script>
// Initialize theme on page load
document.addEventListener('DOMContentLoaded', function() {
// Apply saved theme from localStorage
const savedTheme = localStorage.getItem('theme');
const body = document.body;
if (savedTheme === 'dark') {
body.classList.add('dark-mode');
} else {
body.classList.remove('dark-mode');
}
// Update theme toggle button text if it exists
const themeToggleButton = document.getElementById('theme-toggle');
if (themeToggleButton) {
if (body.classList.contains('dark-mode')) {
themeToggleButton.textContent = 'Change to Light Mode';
} else {
themeToggleButton.textContent = 'Change to Dark Mode';
}
}
});
function showComingSoon(feature) {
alert(`${feature} functionality will be available in a future update!\n\nThis feature is currently under development and will include advanced capabilities for enhanced Daily Mirror operations.`);
}
// Auto-refresh quick stats every 5 minutes
setInterval(function() {
// This could be implemented to refresh the quick stats
console.log('Auto-refresh daily stats (not implemented yet)');
}, 300000); // 5 minutes
</script>
{% endblock %}

View File

@@ -0,0 +1,503 @@
{% extends "base.html" %}
{% block title %}Tune Delivery Data - Daily Mirror{% endblock %}
{% block extra_css %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/daily_mirror_tune.css') }}">
{% endblock %}
{% block content %}
<div class="container-fluid">
<!-- Page Header -->
<div class="row mb-4">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center">
<div>
<h1 class="h3 mb-0">🚚 Tune Delivery Data</h1>
<p class="text-muted">Edit and update delivery records information</p>
</div>
<div>
<!-- Buttons removed; now present in top header -->
</div>
</div>
</div>
</div>
<!-- Filters Section -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-filter"></i> Filters and Search
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-3 mb-3">
<label for="searchInput" class="form-label">Search</label>
<input type="text" class="form-control" id="searchInput"
placeholder="Search by shipment, customer, or article...">
</div>
<div class="col-md-3 mb-3">
<label for="statusFilter" class="form-label">Delivery Status</label>
<select class="form-control" id="statusFilter">
<option value="">All Statuses</option>
<!-- Will be populated dynamically -->
</select>
</div>
<div class="col-md-3 mb-3">
<label for="customerFilter" class="form-label">Customer</label>
<select class="form-control" id="customerFilter">
<option value="">All Customers</option>
<!-- Will be populated dynamically -->
</select>
</div>
<div class="col-md-3 mb-3">
<label for="recordsPerPage" class="form-label">Records per page</label>
<select class="form-control" id="recordsPerPage">
<option value="25">25</option>
<option value="50" selected>50</option>
<option value="100">100</option>
</select>
</div>
</div>
<div class="row">
<div class="col-12">
<button class="btn btn-primary" onclick="loadDeliveryData()">
<i class="fas fa-search"></i> Apply Filters
</button>
<button class="btn btn-secondary" onclick="clearFilters()">
<i class="fas fa-times"></i> Clear
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Data Table Section -->
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">
<i class="fas fa-table"></i> Delivery Records Data
</h5>
<div class="d-flex align-items-center">
<span id="recordsInfo" class="text-muted me-3"></span>
<button class="btn btn-success btn-sm" onclick="saveAllChanges()">
<i class="fas fa-save"></i> Save All Changes
</button>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-striped table-hover" id="deliveryTable">
<thead class="table-dark">
<tr>
<th>Shipment ID</th>
<th>Customer</th>
<th>Order ID</th>
<th>Article Code</th>
<th>Description</th>
<th>Quantity</th>
<th>Shipment Date</th>
<th>Delivery Date</th>
<th>Status</th>
<th>Total Value</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="deliveryTableBody">
<!-- Data will be loaded here -->
</tbody>
</table>
</div>
<!-- Loading indicator -->
<div id="loadingIndicator" class="text-center py-4" style="display: none;">
<i class="fas fa-spinner fa-spin fa-2x"></i>
<p class="mt-2">Loading data...</p>
</div>
<!-- No data message -->
<div id="noDataMessage" class="text-center py-4" style="display: none;">
<i class="fas fa-info-circle fa-2x text-muted"></i>
<p class="mt-2 text-muted">No delivery records found</p>
</div>
</div>
<!-- Pagination -->
<div class="card-footer">
<nav aria-label="Delivery data pagination">
<ul class="pagination pagination-sm justify-content-center mb-0" id="pagination">
<!-- Pagination will be generated here -->
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
<!-- Edit Modal -->
<div class="modal fade" id="editModal" tabindex="-1" aria-labelledby="editModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="editModalLabel">Edit Delivery Record</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="editForm">
<input type="hidden" id="editRecordId">
<div class="row">
<div class="col-md-6 mb-3">
<label for="editShipmentId" class="form-label">Shipment ID</label>
<input type="text" class="form-control" id="editShipmentId" readonly>
</div>
<div class="col-md-6 mb-3">
<label for="editOrderId" class="form-label">Order ID</label>
<input type="text" class="form-control" id="editOrderId">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="editCustomerCode" class="form-label">Customer Code</label>
<input type="text" class="form-control" id="editCustomerCode">
</div>
<div class="col-md-6 mb-3">
<label for="editCustomerName" class="form-label">Customer Name</label>
<input type="text" class="form-control" id="editCustomerName">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="editArticleCode" class="form-label">Article Code</label>
<input type="text" class="form-control" id="editArticleCode">
</div>
<div class="col-md-6 mb-3">
<label for="editQuantity" class="form-label">Quantity Delivered</label>
<input type="number" class="form-control" id="editQuantity">
</div>
</div>
<div class="mb-3">
<label for="editDescription" class="form-label">Article Description</label>
<textarea class="form-control" id="editDescription" rows="2"></textarea>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="editShipmentDate" class="form-label">Shipment Date</label>
<input type="date" class="form-control" id="editShipmentDate">
</div>
<div class="col-md-6 mb-3">
<label for="editDeliveryDate" class="form-label">Delivery Date</label>
<input type="date" class="form-control" id="editDeliveryDate">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="editDeliveryStatus" class="form-label">Delivery Status</label>
<select class="form-control" id="editDeliveryStatus">
<option value="Finalizat">Finalizat</option>
<option value="Proiect">Proiect</option>
<option value="SHIPPED">Shipped</option>
<option value="DELIVERED">Delivered</option>
<option value="RETURNED">Returned</option>
<option value="PARTIAL">Partial</option>
<option value="CANCELLED">Cancelled</option>
</select>
</div>
<div class="col-md-6 mb-3">
<label for="editTotalValue" class="form-label">Total Value (€)</label>
<input type="number" step="0.01" class="form-control" id="editTotalValue">
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
<i class="fas fa-times"></i> Cancel
</button>
<button type="button" class="btn btn-primary" onclick="saveRecord()">
<i class="fas fa-save"></i> Save Changes
</button>
</div>
</div>
</div>
</div>
<script>
let currentPage = 1;
let currentPerPage = 50;
let currentSearch = '';
let currentStatusFilter = '';
let currentCustomerFilter = '';
document.addEventListener('DOMContentLoaded', function() {
// Initialize theme
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'dark') {
document.body.classList.add('dark-mode');
}
// Load initial data
loadDeliveryData();
// Setup search on enter key
document.getElementById('searchInput').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
loadDeliveryData();
}
});
});
function loadDeliveryData(page = 1) {
currentPage = page;
currentPerPage = document.getElementById('recordsPerPage').value;
currentSearch = document.getElementById('searchInput').value;
currentStatusFilter = document.getElementById('statusFilter').value;
currentCustomerFilter = document.getElementById('customerFilter').value;
// Show loading indicator
document.getElementById('loadingIndicator').style.display = 'block';
document.getElementById('deliveryTableBody').style.display = 'none';
document.getElementById('noDataMessage').style.display = 'none';
const params = new URLSearchParams({
page: currentPage,
per_page: currentPerPage,
search: currentSearch,
status: currentStatusFilter,
customer: currentCustomerFilter
});
fetch(`/daily_mirror/api/tune/delivery_data?${params}`)
.then(response => response.json())
.then(data => {
document.getElementById('loadingIndicator').style.display = 'none';
if (data.success) {
if (data.data.length === 0) {
document.getElementById('noDataMessage').style.display = 'block';
} else {
displayDeliveryData(data.data);
updatePagination(data);
updateRecordsInfo(data);
// Populate filter dropdowns on first load
if (currentPage === 1) {
populateCustomerFilter(data.customers);
populateStatusFilter(data.statuses);
}
}
} else {
console.error('Error loading data:', data.error);
alert('Error loading delivery data: ' + data.error);
}
})
.catch(error => {
document.getElementById('loadingIndicator').style.display = 'none';
console.error('Error:', error);
alert('Error loading delivery data: ' + error.message);
});
}
function displayDeliveryData(data) {
const tbody = document.getElementById('deliveryTableBody');
tbody.innerHTML = '';
tbody.style.display = 'table-row-group';
data.forEach(record => {
const row = document.createElement('tr');
row.innerHTML = `
<td><strong>${record.shipment_id}</strong></td>
<td>
<small class="text-muted d-block">${record.customer_code}</small>
${record.customer_name}
</td>
<td>${record.order_id || '-'}</td>
<td><code>${record.article_code}</code></td>
<td><small>${record.article_description || '-'}</small></td>
<td><span class="badge bg-info">${record.quantity_delivered}</span></td>
<td>${record.shipment_date || '-'}</td>
<td>${record.delivery_date || '-'}</td>
<td><span class="badge bg-success">${record.delivery_status}</span></td>
<td><strong>€${parseFloat(record.total_value || 0).toFixed(2)}</strong></td>
<td>
<button class="btn btn-sm btn-primary" onclick="editRecord(${record.id})"
title="Edit Delivery">
<i class="fas fa-edit"></i>
</button>
</td>
`;
tbody.appendChild(row);
});
}
function populateCustomerFilter(customers) {
const filter = document.getElementById('customerFilter');
const currentValue = filter.value;
filter.innerHTML = '<option value="">All Customers</option>';
customers.forEach(customer => {
const option = document.createElement('option');
option.value = customer.code;
option.textContent = `${customer.code} - ${customer.name}`;
filter.appendChild(option);
});
filter.value = currentValue;
}
function populateStatusFilter(statuses) {
const filter = document.getElementById('statusFilter');
const currentValue = filter.value;
filter.innerHTML = '<option value="">All Statuses</option>';
statuses.forEach(status => {
const option = document.createElement('option');
option.value = status;
option.textContent = status;
filter.appendChild(option);
});
filter.value = currentValue;
}
function updatePagination(data) {
const pagination = document.getElementById('pagination');
pagination.innerHTML = '';
if (data.total_pages <= 1) return;
// Previous button
const prevLi = document.createElement('li');
prevLi.className = `page-item ${data.page === 1 ? 'disabled' : ''}`;
prevLi.innerHTML = `<a class="page-link" href="#" onclick="loadDeliveryData(${data.page - 1})">Previous</a>`;
pagination.appendChild(prevLi);
// Page numbers
const startPage = Math.max(1, data.page - 2);
const endPage = Math.min(data.total_pages, data.page + 2);
for (let i = startPage; i <= endPage; i++) {
const li = document.createElement('li');
li.className = `page-item ${i === data.page ? 'active' : ''}`;
li.innerHTML = `<a class="page-link" href="#" onclick="loadDeliveryData(${i})">${i}</a>`;
pagination.appendChild(li);
}
// Next button
const nextLi = document.createElement('li');
nextLi.className = `page-item ${data.page === data.total_pages ? 'disabled' : ''}`;
nextLi.innerHTML = `<a class="page-link" href="#" onclick="loadDeliveryData(${data.page + 1})">Next</a>`;
pagination.appendChild(nextLi);
}
function updateRecordsInfo(data) {
const start = (data.page - 1) * data.per_page + 1;
const end = Math.min(data.page * data.per_page, data.total_records);
document.getElementById('recordsInfo').textContent =
`Showing ${start}-${end} of ${data.total_records} deliveries`;
}
function clearFilters() {
document.getElementById('searchInput').value = '';
document.getElementById('statusFilter').value = '';
document.getElementById('customerFilter').value = '';
loadDeliveryData(1);
}
function editRecord(recordId) {
// Get data via API for editing
fetch(`/daily_mirror/api/tune/delivery_data?page=${currentPage}&per_page=${currentPerPage}&search=${currentSearch}&status=${currentStatusFilter}&customer=${currentCustomerFilter}`)
.then(response => response.json())
.then(data => {
if (data.success) {
const recordData = data.data.find(record => record.id === recordId);
if (recordData) {
populateEditModal(recordData);
const editModal = new bootstrap.Modal(document.getElementById('editModal'));
editModal.show();
}
}
})
.catch(error => {
console.error('Error:', error);
alert('Error loading record data: ' + error.message);
});
}
function populateEditModal(record) {
document.getElementById('editRecordId').value = record.id;
document.getElementById('editShipmentId').value = record.shipment_id;
document.getElementById('editOrderId').value = record.order_id || '';
document.getElementById('editCustomerCode').value = record.customer_code;
document.getElementById('editCustomerName').value = record.customer_name;
document.getElementById('editArticleCode').value = record.article_code;
document.getElementById('editDescription').value = record.article_description || '';
document.getElementById('editQuantity').value = record.quantity_delivered;
document.getElementById('editShipmentDate').value = record.shipment_date;
document.getElementById('editDeliveryDate').value = record.delivery_date;
document.getElementById('editDeliveryStatus').value = record.delivery_status;
document.getElementById('editTotalValue').value = record.total_value;
}
function saveRecord() {
const recordId = document.getElementById('editRecordId').value;
const data = {
customer_code: document.getElementById('editCustomerCode').value,
customer_name: document.getElementById('editCustomerName').value,
order_id: document.getElementById('editOrderId').value,
article_code: document.getElementById('editArticleCode').value,
article_description: document.getElementById('editDescription').value,
quantity_delivered: parseInt(document.getElementById('editQuantity').value) || 0,
shipment_date: document.getElementById('editShipmentDate').value,
delivery_date: document.getElementById('editDeliveryDate').value,
delivery_status: document.getElementById('editDeliveryStatus').value,
total_value: parseFloat(document.getElementById('editTotalValue').value) || 0
};
fetch(`/daily_mirror/api/tune/delivery_data/${recordId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(result => {
if (result.success) {
// Close modal
const editModal = bootstrap.Modal.getInstance(document.getElementById('editModal'));
editModal.hide();
// Reload data
loadDeliveryData(currentPage);
// Show success message
alert('Delivery record updated successfully!');
} else {
alert('Error updating delivery record: ' + result.error);
}
})
.catch(error => {
console.error('Error:', error);
alert('Error updating delivery record: ' + error.message);
});
}
function saveAllChanges() {
alert('Save All Changes functionality will be implemented for bulk operations.');
}
</script>
{% endblock %}

View File

@@ -0,0 +1,522 @@
{% extends "base.html" %}
{% block title %}Tune Orders Data - Daily Mirror{% endblock %}
{% block extra_css %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/daily_mirror_tune.css') }}">
{% endblock %}
{% block content %}
<div class="container-fluid">
<!-- Page Header -->
<div class="row mb-4">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center">
<div>
<h1 class="h3 mb-0">🛒 Tune Orders Data</h1>
<p class="text-muted">Edit and update customer orders information</p>
</div>
<div>
<!-- Buttons removed; now present in top header -->
</div>
</div>
</div>
</div>
<!-- Filters Section -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-filter"></i> Filters and Search
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-3 mb-3">
<label for="searchInput" class="form-label">Search</label>
<input type="text" class="form-control" id="searchInput"
placeholder="Search by order, customer, or article...">
</div>
<div class="col-md-3 mb-3">
<label for="statusFilter" class="form-label">Order Status</label>
<select class="form-control" id="statusFilter">
<option value="">All Statuses</option>
<!-- Will be populated dynamically -->
</select>
</div>
<div class="col-md-3 mb-3">
<label for="customerFilter" class="form-label">Customer</label>
<select class="form-control" id="customerFilter">
<option value="">All Customers</option>
<!-- Will be populated dynamically -->
</select>
</div>
<div class="col-md-3 mb-3">
<label for="recordsPerPage" class="form-label">Records per page</label>
<select class="form-control" id="recordsPerPage">
<option value="25">25</option>
<option value="50" selected>50</option>
<option value="100">100</option>
</select>
</div>
</div>
<div class="row">
<div class="col-12">
<button class="btn btn-primary" onclick="loadOrdersData()">
<i class="fas fa-search"></i> Apply Filters
</button>
<button class="btn btn-secondary" onclick="clearFilters()">
<i class="fas fa-times"></i> Clear
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Data Table Section -->
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">
<i class="fas fa-table"></i> Customer Orders Data
</h5>
<div class="d-flex align-items-center">
<span id="recordsInfo" class="text-muted me-3"></span>
<button class="btn btn-success btn-sm" onclick="saveAllChanges()">
<i class="fas fa-save"></i> Save All Changes
</button>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-striped table-hover" id="ordersTable">
<thead class="table-dark">
<tr>
<th>Order ID</th>
<th>Customer</th>
<th>Client Order</th>
<th>Article Code</th>
<th>Description</th>
<th>Quantity</th>
<th>Delivery Date</th>
<th>Status</th>
<th>Priority</th>
<th>Product Group</th>
<th>Order Date</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="ordersTableBody">
<!-- Data will be loaded here -->
</tbody>
</table>
</div>
<!-- Loading indicator -->
<div id="loadingIndicator" class="text-center py-4" style="display: none;">
<i class="fas fa-spinner fa-spin fa-2x"></i>
<p class="mt-2">Loading data...</p>
</div>
<!-- No data message -->
<div id="noDataMessage" class="text-center py-4" style="display: none;">
<i class="fas fa-info-circle fa-2x text-muted"></i>
<p class="mt-2 text-muted">No orders found</p>
</div>
</div>
<!-- Pagination -->
<div class="card-footer">
<nav aria-label="Orders data pagination">
<ul class="pagination pagination-sm justify-content-center mb-0" id="pagination">
<!-- Pagination will be generated here -->
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
<!-- Edit Modal -->
<div class="modal fade" id="editModal" tabindex="-1" aria-labelledby="editModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="editModalLabel">Edit Customer Order</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="editForm">
<input type="hidden" id="editRecordId">
<div class="row">
<div class="col-md-6 mb-3">
<label for="editOrderId" class="form-label">Order ID</label>
<input type="text" class="form-control" id="editOrderId" readonly>
</div>
<div class="col-md-6 mb-3">
<label for="editCustomerCode" class="form-label">Customer Code</label>
<input type="text" class="form-control" id="editCustomerCode">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="editCustomerName" class="form-label">Customer Name</label>
<input type="text" class="form-control" id="editCustomerName">
</div>
<div class="col-md-6 mb-3">
<label for="editClientOrder" class="form-label">Client Order</label>
<input type="text" class="form-control" id="editClientOrder">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="editArticleCode" class="form-label">Article Code</label>
<input type="text" class="form-control" id="editArticleCode">
</div>
<div class="col-md-6 mb-3">
<label for="editQuantity" class="form-label">Quantity</label>
<input type="number" class="form-control" id="editQuantity">
</div>
</div>
<div class="mb-3">
<label for="editDescription" class="form-label">Article Description</label>
<textarea class="form-control" id="editDescription" rows="2"></textarea>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="editDeliveryDate" class="form-label">Delivery Date</label>
<input type="date" class="form-control" id="editDeliveryDate">
</div>
<div class="col-md-6 mb-3">
<label for="editOrderStatus" class="form-label">Order Status</label>
<select class="form-control" id="editOrderStatus">
<option value="PENDING">Pending</option>
<option value="CONFIRMED">Confirmed</option>
<option value="Confirmat">Confirmat</option>
<option value="IN_PROGRESS">In Progress</option>
<option value="FINISHED">Finished</option>
<option value="DELIVERED">Delivered</option>
<option value="CANCELLED">Cancelled</option>
</select>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="editPriority" class="form-label">Priority</label>
<select class="form-control" id="editPriority">
<option value="LOW">Low</option>
<option value="NORMAL">Normal</option>
<option value="HIGH">High</option>
<option value="URGENT">Urgent</option>
</select>
</div>
<div class="col-md-6 mb-3">
<label for="editProductGroup" class="form-label">Product Group</label>
<input type="text" class="form-control" id="editProductGroup">
</div>
</div>
<div class="mb-3">
<label for="editOrderDate" class="form-label">Order Date</label>
<input type="date" class="form-control" id="editOrderDate">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
<i class="fas fa-times"></i> Cancel
</button>
<button type="button" class="btn btn-primary" onclick="saveRecord()">
<i class="fas fa-save"></i> Save Changes
</button>
</div>
</div>
</div>
</div>
<script>
let currentPage = 1;
let currentPerPage = 50;
let currentSearch = '';
let currentStatusFilter = '';
let currentCustomerFilter = '';
document.addEventListener('DOMContentLoaded', function() {
// Initialize theme
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'dark') {
document.body.classList.add('dark-mode');
}
// Load initial data
loadOrdersData();
// Setup search on enter key
document.getElementById('searchInput').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
loadOrdersData();
}
});
});
function loadOrdersData(page = 1) {
currentPage = page;
currentPerPage = document.getElementById('recordsPerPage').value;
currentSearch = document.getElementById('searchInput').value;
currentStatusFilter = document.getElementById('statusFilter').value;
currentCustomerFilter = document.getElementById('customerFilter').value;
// Show loading indicator
document.getElementById('loadingIndicator').style.display = 'block';
document.getElementById('ordersTableBody').style.display = 'none';
document.getElementById('noDataMessage').style.display = 'none';
const params = new URLSearchParams({
page: currentPage,
per_page: currentPerPage,
search: currentSearch,
status: currentStatusFilter,
customer: currentCustomerFilter
});
fetch(`/daily_mirror/api/tune/orders_data?${params}`)
.then(response => response.json())
.then(data => {
document.getElementById('loadingIndicator').style.display = 'none';
if (data.success) {
if (data.data.length === 0) {
document.getElementById('noDataMessage').style.display = 'block';
} else {
displayOrdersData(data.data);
updatePagination(data);
updateRecordsInfo(data);
// Populate filter dropdowns on first load
if (currentPage === 1) {
populateCustomerFilter(data.customers);
populateStatusFilter(data.statuses);
}
}
} else {
console.error('Error loading data:', data.error);
alert('Error loading orders data: ' + data.error);
}
})
.catch(error => {
document.getElementById('loadingIndicator').style.display = 'none';
console.error('Error:', error);
alert('Error loading orders data: ' + error.message);
});
}
function displayOrdersData(data) {
const tbody = document.getElementById('ordersTableBody');
tbody.innerHTML = '';
tbody.style.display = 'table-row-group';
data.forEach(record => {
const row = document.createElement('tr');
row.innerHTML = `
<td><strong>${record.order_id}</strong></td>
<td>
<small class="text-muted d-block">${record.customer_code}</small>
${record.customer_name}
</td>
<td>${record.client_order || '-'}</td>
<td><code>${record.article_code}</code></td>
<td><small>${record.article_description || '-'}</small></td>
<td><span class="badge bg-info">${record.quantity_requested}</span></td>
<td>${record.delivery_date || '-'}</td>
<td><span class="badge bg-primary">${record.order_status}</span></td>
<td><span class="badge bg-warning">${record.priority || 'NORMAL'}</span></td>
<td><small>${record.product_group || '-'}</small></td>
<td>${record.order_date || '-'}</td>
<td>
<button class="btn btn-sm btn-primary" onclick="editRecord(${record.id})"
title="Edit Order">
<i class="fas fa-edit"></i>
</button>
</td>
`;
tbody.appendChild(row);
});
}
function populateCustomerFilter(customers) {
const filter = document.getElementById('customerFilter');
// Keep the "All Customers" option and add new ones
const currentValue = filter.value;
filter.innerHTML = '<option value="">All Customers</option>';
customers.forEach(customer => {
const option = document.createElement('option');
option.value = customer.code;
option.textContent = `${customer.code} - ${customer.name}`;
filter.appendChild(option);
});
filter.value = currentValue;
}
function populateStatusFilter(statuses) {
const filter = document.getElementById('statusFilter');
const currentValue = filter.value;
filter.innerHTML = '<option value="">All Statuses</option>';
statuses.forEach(status => {
const option = document.createElement('option');
option.value = status;
option.textContent = status;
filter.appendChild(option);
});
filter.value = currentValue;
}
function updatePagination(data) {
const pagination = document.getElementById('pagination');
pagination.innerHTML = '';
if (data.total_pages <= 1) return;
// Previous button
const prevLi = document.createElement('li');
prevLi.className = `page-item ${data.page === 1 ? 'disabled' : ''}`;
prevLi.innerHTML = `<a class="page-link" href="#" onclick="loadOrdersData(${data.page - 1})">Previous</a>`;
pagination.appendChild(prevLi);
// Page numbers
const startPage = Math.max(1, data.page - 2);
const endPage = Math.min(data.total_pages, data.page + 2);
for (let i = startPage; i <= endPage; i++) {
const li = document.createElement('li');
li.className = `page-item ${i === data.page ? 'active' : ''}`;
li.innerHTML = `<a class="page-link" href="#" onclick="loadOrdersData(${i})">${i}</a>`;
pagination.appendChild(li);
}
// Next button
const nextLi = document.createElement('li');
nextLi.className = `page-item ${data.page === data.total_pages ? 'disabled' : ''}`;
nextLi.innerHTML = `<a class="page-link" href="#" onclick="loadOrdersData(${data.page + 1})">Next</a>`;
pagination.appendChild(nextLi);
}
function updateRecordsInfo(data) {
const start = (data.page - 1) * data.per_page + 1;
const end = Math.min(data.page * data.per_page, data.total_records);
document.getElementById('recordsInfo').textContent =
`Showing ${start}-${end} of ${data.total_records} orders`;
}
function clearFilters() {
document.getElementById('searchInput').value = '';
document.getElementById('statusFilter').value = '';
document.getElementById('customerFilter').value = '';
loadOrdersData(1);
}
function editRecord(recordId) {
// Find the record data from the current display
const rows = document.querySelectorAll('#ordersTableBody tr');
let recordData = null;
// Get data via API for editing
fetch(`/daily_mirror/api/tune/orders_data?page=${currentPage}&per_page=${currentPerPage}&search=${currentSearch}&status=${currentStatusFilter}&customer=${currentCustomerFilter}`)
.then(response => response.json())
.then(data => {
if (data.success) {
recordData = data.data.find(record => record.id === recordId);
if (recordData) {
populateEditModal(recordData);
const editModal = new bootstrap.Modal(document.getElementById('editModal'));
editModal.show();
}
}
})
.catch(error => {
console.error('Error:', error);
alert('Error loading record data: ' + error.message);
});
}
function populateEditModal(record) {
document.getElementById('editRecordId').value = record.id;
document.getElementById('editOrderId').value = record.order_id;
document.getElementById('editCustomerCode').value = record.customer_code;
document.getElementById('editCustomerName').value = record.customer_name;
document.getElementById('editClientOrder').value = record.client_order || '';
document.getElementById('editArticleCode').value = record.article_code;
document.getElementById('editDescription').value = record.article_description || '';
document.getElementById('editQuantity').value = record.quantity_requested;
document.getElementById('editDeliveryDate').value = record.delivery_date;
document.getElementById('editOrderStatus').value = record.order_status;
document.getElementById('editPriority').value = record.priority || 'NORMAL';
document.getElementById('editProductGroup').value = record.product_group || '';
document.getElementById('editOrderDate').value = record.order_date;
}
function saveRecord() {
const recordId = document.getElementById('editRecordId').value;
const data = {
customer_code: document.getElementById('editCustomerCode').value,
customer_name: document.getElementById('editCustomerName').value,
client_order: document.getElementById('editClientOrder').value,
article_code: document.getElementById('editArticleCode').value,
article_description: document.getElementById('editDescription').value,
quantity_requested: parseInt(document.getElementById('editQuantity').value) || 0,
delivery_date: document.getElementById('editDeliveryDate').value,
order_status: document.getElementById('editOrderStatus').value,
priority: document.getElementById('editPriority').value,
product_group: document.getElementById('editProductGroup').value,
order_date: document.getElementById('editOrderDate').value
};
fetch(`/daily_mirror/api/tune/orders_data/${recordId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(result => {
if (result.success) {
// Close modal
const editModal = bootstrap.Modal.getInstance(document.getElementById('editModal'));
editModal.hide();
// Reload data
loadOrdersData(currentPage);
// Show success message
alert('Order updated successfully!');
} else {
alert('Error updating order: ' + result.error);
}
})
.catch(error => {
console.error('Error:', error);
alert('Error updating order: ' + error.message);
});
}
function saveAllChanges() {
alert('Save All Changes functionality will be implemented for bulk operations.');
}
</script>
{% endblock %}

View File

@@ -0,0 +1,516 @@
{% extends "base.html" %}
{% block title %}Tune Production Data - Daily Mirror{% endblock %}
{% block extra_css %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/daily_mirror_tune.css') }}">
{% endblock %}
{% block content %}
<div class="container-fluid">
<!-- Page Header -->
<div class="row mb-4">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center">
<div>
<h1 class="h3 mb-0">🏭 Tune Production Data</h1>
<p class="text-muted">Edit and update production orders information</p>
</div>
<div>
<!-- Buttons removed; now present in top header -->
</div>
</div>
</div>
</div>
<!-- Filters Section -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-filter"></i> Filters and Search
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-3 mb-3">
<label for="searchInput" class="form-label">Search</label>
<input type="text" class="form-control" id="searchInput"
placeholder="Search by order, customer, or article...">
</div>
<div class="col-md-3 mb-3">
<label for="statusFilter" class="form-label">Production Status</label>
<select class="form-control" id="statusFilter">
<option value="">All Statuses</option>
<option value="PENDING">Pending</option>
<option value="IN_PROGRESS">In Progress</option>
<option value="FINISHED">Finished</option>
<option value="CANCELLED">Cancelled</option>
</select>
</div>
<div class="col-md-3 mb-3">
<label for="customerFilter" class="form-label">Customer</label>
<select class="form-control" id="customerFilter">
<option value="">All Customers</option>
<!-- Will be populated dynamically -->
</select>
</div>
<div class="col-md-3 mb-3">
<label for="recordsPerPage" class="form-label">Records per page</label>
<select class="form-control" id="recordsPerPage">
<option value="25">25</option>
<option value="50" selected>50</option>
<option value="100">100</option>
</select>
</div>
</div>
<div class="row">
<div class="col-12">
<button class="btn btn-primary" onclick="loadProductionData()">
<i class="fas fa-search"></i> Apply Filters
</button>
<button class="btn btn-secondary" onclick="clearFilters()">
<i class="fas fa-times"></i> Clear
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Data Table Section -->
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">
<i class="fas fa-table"></i> Production Orders Data
</h5>
<div class="d-flex align-items-center">
<span id="recordsInfo" class="text-muted me-3"></span>
<button class="btn btn-success btn-sm" onclick="saveAllChanges()">
<i class="fas fa-save"></i> Save All Changes
</button>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-striped table-hover" id="productionTable">
<thead class="table-dark">
<tr>
<th>Production Order</th>
<th>Customer</th>
<th>Client Order</th>
<th>Article Code</th>
<th>Description</th>
<th>Quantity</th>
<th>Delivery Date</th>
<th>Status</th>
<th>Machine</th>
<th>Planning Date</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="productionTableBody">
<!-- Data will be loaded here -->
</tbody>
</table>
</div>
<!-- Loading indicator -->
<div id="loadingIndicator" class="text-center py-4" style="display: none;">
<i class="fas fa-spinner fa-spin fa-2x"></i>
<p class="mt-2">Loading data...</p>
</div>
<!-- No data message -->
<div id="noDataMessage" class="text-center py-4" style="display: none;">
<i class="fas fa-info-circle fa-2x text-muted"></i>
<p class="mt-2 text-muted">No production orders found</p>
</div>
</div>
<!-- Pagination -->
<div class="card-footer">
<nav aria-label="Production data pagination">
<ul class="pagination pagination-sm justify-content-center mb-0" id="pagination">
<!-- Pagination will be generated here -->
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
<!-- Edit Modal -->
<div class="modal fade" id="editModal" tabindex="-1" aria-labelledby="editModalLabel" aria-hidden="true" data-bs-backdrop="true" data-bs-keyboard="true">
<div class="modal-dialog modal-lg modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="editModalLabel">Edit Production Order</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" id="modalCloseBtn"></button>
</div>
<div class="modal-body">
<form id="editForm">
<input type="hidden" id="editRecordId">
<div class="row">
<div class="col-md-6 mb-3">
<label for="editProductionOrder" class="form-label">Production Order</label>
<input type="text" class="form-control" id="editProductionOrder" readonly>
</div>
<div class="col-md-6 mb-3">
<label for="editCustomerCode" class="form-label">Customer Code</label>
<input type="text" class="form-control" id="editCustomerCode">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="editCustomerName" class="form-label">Customer Name</label>
<input type="text" class="form-control" id="editCustomerName">
</div>
<div class="col-md-6 mb-3">
<label for="editClientOrder" class="form-label">Client Order</label>
<input type="text" class="form-control" id="editClientOrder">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="editArticleCode" class="form-label">Article Code</label>
<input type="text" class="form-control" id="editArticleCode">
</div>
<div class="col-md-6 mb-3">
<label for="editQuantity" class="form-label">Quantity</label>
<input type="number" class="form-control" id="editQuantity">
</div>
</div>
<div class="mb-3">
<label for="editDescription" class="form-label">Article Description</label>
<textarea class="form-control" id="editDescription" rows="2"></textarea>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="editDeliveryDate" class="form-label">Delivery Date</label>
<input type="date" class="form-control" id="editDeliveryDate">
</div>
<div class="col-md-6 mb-3">
<label for="editStatus" class="form-label">Production Status</label>
<select class="form-control" id="editStatus">
<option value="PENDING">Pending</option>
<option value="IN_PROGRESS">In Progress</option>
<option value="FINISHED">Finished</option>
<option value="CANCELLED">Cancelled</option>
</select>
</div>
</div>
<div class="mb-3">
<label for="editMachine" class="form-label">Machine Code</label>
<input type="text" class="form-control" id="editMachine">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" onclick="saveRecord()">
<i class="fas fa-save"></i> Save Changes
</button>
</div>
</div>
</div>
</div>
<script>
let currentPage = 1;
let currentData = [];
let hasChanges = false;
document.addEventListener('DOMContentLoaded', function() {
// Initialize theme
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'dark') {
document.body.classList.add('dark-mode');
}
// Load initial data
loadProductionData();
});
function loadProductionData(page = 1) {
currentPage = page;
// Show loading
document.getElementById('loadingIndicator').style.display = 'block';
document.getElementById('productionTableBody').innerHTML = '';
document.getElementById('noDataMessage').style.display = 'none';
// Get filter values
const search = document.getElementById('searchInput').value;
const status = document.getElementById('statusFilter').value;
const customer = document.getElementById('customerFilter').value;
const perPage = document.getElementById('recordsPerPage').value;
// Build query parameters
const params = new URLSearchParams({
page: page,
per_page: perPage
});
if (search) params.append('search', search);
if (status) params.append('status', status);
if (customer) params.append('customer', customer);
// Fetch data
fetch(`/daily_mirror/api/tune/production_data?${params}`)
.then(response => response.json())
.then(data => {
if (data.error) {
throw new Error(data.error);
}
currentData = data.records;
renderTable(data);
renderPagination(data);
updateRecordsInfo(data);
})
.catch(error => {
console.error('Error loading production data:', error);
alert('Error loading data: ' + error.message);
})
.finally(() => {
document.getElementById('loadingIndicator').style.display = 'none';
});
}
function renderTable(data) {
const tbody = document.getElementById('productionTableBody');
if (data.records.length === 0) {
document.getElementById('noDataMessage').style.display = 'block';
return;
}
tbody.innerHTML = data.records.map((record, index) => `
<tr id="row-${record.id}">
<td><strong>${record.production_order}</strong></td>
<td>${record.customer_code}<br><small class="text-muted">${record.customer_name || ''}</small></td>
<td>${record.client_order || ''}</td>
<td>${record.article_code || ''}</td>
<td><small>${record.article_description || ''}</small></td>
<td>${record.quantity_requested || ''}</td>
<td>${record.delivery_date || ''}</td>
<td><span class="badge bg-${getStatusColor(record.production_status)}">${record.production_status || ''}</span></td>
<td>${record.machine_code || ''}</td>
<td>${record.data_planificare || ''}</td>
<td>
<button class="btn btn-primary btn-action btn-sm" onclick="editRecord(${record.id})" title="Edit">
<i class="fas fa-edit"></i>
</button>
</td>
</tr>
`).join('');
}
function getStatusColor(status) {
switch(status) {
case 'PENDING': return 'warning';
case 'IN_PROGRESS': return 'info';
case 'FINISHED': return 'success';
case 'CANCELLED': return 'danger';
default: return 'secondary';
}
}
function renderPagination(data) {
const pagination = document.getElementById('pagination');
if (data.total_pages <= 1) {
pagination.innerHTML = '';
return;
}
let paginationHTML = '';
// Previous button
if (data.page > 1) {
paginationHTML += `<li class="page-item"><a class="page-link" href="#" onclick="loadProductionData(${data.page - 1})">Previous</a></li>`;
}
// Page numbers
for (let i = Math.max(1, data.page - 2); i <= Math.min(data.total_pages, data.page + 2); i++) {
const active = i === data.page ? 'active' : '';
paginationHTML += `<li class="page-item ${active}"><a class="page-link" href="#" onclick="loadProductionData(${i})">${i}</a></li>`;
}
// Next button
if (data.page < data.total_pages) {
paginationHTML += `<li class="page-item"><a class="page-link" href="#" onclick="loadProductionData(${data.page + 1})">Next</a></li>`;
}
pagination.innerHTML = paginationHTML;
}
function updateRecordsInfo(data) {
const info = document.getElementById('recordsInfo');
const start = (data.page - 1) * data.per_page + 1;
const end = Math.min(data.page * data.per_page, data.total);
info.textContent = `Showing ${start}-${end} of ${data.total} records`;
}
function editRecord(recordId) {
const record = currentData.find(r => r.id === recordId);
if (!record) return;
// Populate the edit form
document.getElementById('editRecordId').value = record.id;
document.getElementById('editProductionOrder').value = record.production_order;
document.getElementById('editCustomerCode').value = record.customer_code || '';
document.getElementById('editCustomerName').value = record.customer_name || '';
document.getElementById('editClientOrder').value = record.client_order || '';
document.getElementById('editArticleCode').value = record.article_code || '';
document.getElementById('editDescription').value = record.article_description || '';
document.getElementById('editQuantity').value = record.quantity_requested || '';
document.getElementById('editDeliveryDate').value = record.delivery_date || '';
document.getElementById('editStatus').value = record.production_status || '';
document.getElementById('editMachine').value = record.machine_code || '';
// Explicitly enable all editable fields
const editableFields = ['editCustomerCode', 'editCustomerName', 'editClientOrder',
'editArticleCode', 'editDescription', 'editQuantity',
'editDeliveryDate', 'editStatus', 'editMachine'];
editableFields.forEach(fieldId => {
const field = document.getElementById(fieldId);
if (field) {
field.disabled = false;
field.removeAttribute('disabled');
field.removeAttribute('readonly');
field.style.backgroundColor = '#ffffff';
field.style.color = '#000000';
field.style.opacity = '1';
field.style.pointerEvents = 'auto';
field.style.cursor = 'text';
field.style.userSelect = 'text';
field.tabIndex = 0;
}
});
// Show the modal with proper configuration
const modalElement = document.getElementById('editModal');
// Remove any existing modal instances to prevent conflicts
const existingModal = bootstrap.Modal.getInstance(modalElement);
if (existingModal) {
existingModal.dispose();
}
const modal = new bootstrap.Modal(modalElement, {
backdrop: true,
keyboard: true,
focus: true
});
modal.show();
// Ensure form inputs are focusable and interactive after modal is shown
modalElement.addEventListener('shown.bs.modal', function () {
// Re-enable all fields after modal animation completes
editableFields.forEach(fieldId => {
const field = document.getElementById(fieldId);
if (field) {
field.disabled = false;
field.removeAttribute('disabled');
field.style.pointerEvents = 'auto';
}
});
// Focus on the first editable field
const firstField = document.getElementById('editCustomerCode');
if (firstField) {
setTimeout(() => {
firstField.focus();
firstField.select();
}, 100);
}
}, { once: true });
}
function saveRecord() {
const recordId = document.getElementById('editRecordId').value;
const formData = {
customer_code: document.getElementById('editCustomerCode').value,
customer_name: document.getElementById('editCustomerName').value,
client_order: document.getElementById('editClientOrder').value,
article_code: document.getElementById('editArticleCode').value,
article_description: document.getElementById('editDescription').value,
quantity_requested: parseInt(document.getElementById('editQuantity').value) || 0,
delivery_date: document.getElementById('editDeliveryDate').value,
production_status: document.getElementById('editStatus').value,
machine_code: document.getElementById('editMachine').value
};
fetch(`/daily_mirror/api/tune/production_data/${recordId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formData)
})
.then(response => response.json())
.then(data => {
if (data.error) {
throw new Error(data.error);
}
// Close modal and reload data
const modal = bootstrap.Modal.getInstance(document.getElementById('editModal'));
modal.hide();
alert('Record updated successfully!');
loadProductionData(currentPage);
})
.catch(error => {
console.error('Error saving record:', error);
alert('Error saving record: ' + error.message);
});
}
function clearFilters() {
document.getElementById('searchInput').value = '';
document.getElementById('statusFilter').value = '';
document.getElementById('customerFilter').value = '';
loadProductionData(1);
}
function saveAllChanges() {
alert('Bulk save functionality will be implemented in a future update!');
}
// Add event listeners for real-time filtering
document.getElementById('searchInput').addEventListener('input', function() {
clearTimeout(this.searchTimeout);
this.searchTimeout = setTimeout(() => {
loadProductionData(1);
}, 500);
});
document.getElementById('statusFilter').addEventListener('change', function() {
loadProductionData(1);
});
document.getElementById('customerFilter').addEventListener('change', function() {
loadProductionData(1);
});
document.getElementById('recordsPerPage').addEventListener('change', function() {
loadProductionData(1);
});
</script>
{% endblock %}

View File

@@ -34,5 +34,17 @@
<a href="{{ url_for('main.settings') }}" class="btn">Access Settings Page</a>
</div>
<div class="dashboard-card">
<h3>📊 Daily Mirror</h3>
<p>Business Intelligence and Production Reporting - Generate comprehensive daily reports including order quantities, production status, and delivery tracking.</p>
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<a href="{{ url_for('daily_mirror.daily_mirror_main_route') }}" class="btn">📊 Daily Mirror Hub</a>
</div>
<div style="margin-top: 8px; font-size: 12px; color: #666;">
<strong>Tracks:</strong> Orders quantity • Production launched • Production finished • Orders delivered
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -408,6 +408,10 @@
<input type="checkbox" id="module_labels" name="modules" value="labels">
<label for="module_labels">Label Management</label>
</div>
<div class="module-checkbox">
<input type="checkbox" id="module_daily_mirror" name="modules" value="daily_mirror">
<label for="module_daily_mirror">Daily Mirror (Business Intelligence)</label>
</div>
</div>
<div id="accessLevelInfo" class="access-level-info" style="display: none;"></div>
</div>
@@ -454,6 +458,10 @@
<input type="checkbox" id="quick_module_labels" name="quick_modules" value="labels">
<label for="quick_module_labels">Label Management</label>
</div>
<div class="module-checkbox">
<input type="checkbox" id="quick_module_daily_mirror" name="quick_modules" value="daily_mirror">
<label for="quick_module_daily_mirror">Daily Mirror</label>
</div>
</div>
</div>
@@ -621,6 +629,10 @@
<input type="checkbox" id="edit_module_labels" name="modules" value="labels">
<label for="edit_module_labels">Label Management</label>
</div>
<div class="module-checkbox">
<input type="checkbox" id="edit_module_daily_mirror" name="modules" value="daily_mirror">
<label for="edit_module_daily_mirror">Daily Mirror</label>
</div>
</div>
<div id="editAccessLevelInfo" class="access-level-info" style="display: none;"></div>
</div>