429 lines
20 KiB
Plaintext
429 lines
20 KiB
Plaintext
@* FruitBank operational dashboard — loaded via AJAX, never blocks page render *@
|
||
|
||
<div id="fb-dashboard-loading" class="text-center py-3">
|
||
<i class="fas fa-spinner fa-spin fa-lg text-muted"></i>
|
||
<span class="text-muted ml-2">Operatív adatok betöltése...</span>
|
||
</div>
|
||
|
||
<div id="fb-dashboard-content" style="display:none;">
|
||
|
||
<div class="row" id="fb-pipeline-stats">
|
||
<div class="col-6 col-md col-sm-4">
|
||
<div class="info-box mb-3">
|
||
<span class="info-box-icon bg-info elevation-1"><i class="fas fa-shopping-basket"></i></span>
|
||
<div class="info-box-content">
|
||
<span class="info-box-text">Mai rendelés</span>
|
||
<span class="info-box-number" id="fb-stat-total">–</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-6 col-md col-sm-4">
|
||
<div class="info-box mb-3">
|
||
<span class="info-box-icon bg-warning elevation-1"><i class="fas fa-weight-hanging"></i></span>
|
||
<div class="info-box-content">
|
||
<span class="info-box-text">Mérés alatt</span>
|
||
<span class="info-box-number" id="fb-stat-measuring">–</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-6 col-md col-sm-4">
|
||
<div class="info-box mb-3">
|
||
<span class="info-box-icon bg-success elevation-1"><i class="fas fa-check-circle"></i></span>
|
||
<div class="info-box-content">
|
||
<span class="info-box-text">Auditálva</span>
|
||
<span class="info-box-number" id="fb-stat-audited">–</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-6 col-md col-sm-4">
|
||
<div class="info-box mb-3">
|
||
<span class="info-box-icon bg-danger elevation-1"><i class="fas fa-file-invoice-dollar"></i></span>
|
||
<div class="info-box-content">
|
||
<span class="info-box-text">InnVoice hiányzik</span>
|
||
<span class="info-box-number" id="fb-stat-innvoice">–</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-6 col-md col-sm-4">
|
||
<div class="info-box mb-3">
|
||
<span class="info-box-icon bg-secondary elevation-1"><i class="fas fa-flag-checkered"></i></span>
|
||
<div class="info-box-content">
|
||
<span class="info-box-text">Befejezve</span>
|
||
<span class="info-box-number" id="fb-stat-completed">–</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card card-primary" id="fb-orders-card">
|
||
<div class="card-header">
|
||
<h3 class="card-title">
|
||
<i class="fas fa-list mr-1"></i>
|
||
Mai rendelések
|
||
</h3>
|
||
<div class="card-tools">
|
||
<button type="button" class="btn btn-tool" data-card-widget="collapse">
|
||
<i class="fas fa-minus"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="card-body p-0">
|
||
<table class="table table-sm table-striped table-hover mb-0" id="fb-orders-table">
|
||
<thead class="thead-light">
|
||
<tr>
|
||
<th>Rendelés</th>
|
||
<th>Partner</th>
|
||
<th class="text-right">Összeg</th>
|
||
<th class="text-center">Mérés</th>
|
||
<th class="text-center">InnVoice</th>
|
||
<th class="text-center">Státusz</th>
|
||
<th>Átvétel</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="fb-orders-body">
|
||
<tr><td colspan="7" class="text-center text-muted py-3">Betöltés...</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="card-footer text-muted small" id="fb-orders-footer" style="display:none;">
|
||
<a href="/Admin/CustomOrder/List">Összes rendelés →</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card" id="fb-alerts-card" style="display:none;">
|
||
<div class="card-header bg-danger">
|
||
<h3 class="card-title text-white">
|
||
<i class="fas fa-exclamation-triangle mr-1"></i>
|
||
Figyelmet igénylő tételek
|
||
<span class="badge badge-light ml-2" id="fb-alerts-count">0</span>
|
||
</h3>
|
||
<div class="card-tools">
|
||
<button type="button" class="btn btn-tool text-white" data-card-widget="collapse">
|
||
<i class="fas fa-minus"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="card-body p-0">
|
||
<table class="table table-sm table-striped mb-0">
|
||
<thead class="thead-light">
|
||
<tr>
|
||
<th>Típus</th>
|
||
<th>Partner</th>
|
||
<th>Részlet</th>
|
||
<th>Link</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="fb-alerts-body"></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card card-warning collapsed-card" id="fb-credits-card" style="display:none;">
|
||
<div class="card-header">
|
||
<h3 class="card-title">
|
||
<i class="fas fa-credit-card mr-1"></i>
|
||
Hitelkeret státusz
|
||
<span class="badge badge-secondary ml-2" id="fb-credits-count">0</span>
|
||
</h3>
|
||
<div class="card-tools">
|
||
<button type="button" class="btn btn-tool" data-card-widget="collapse">
|
||
<i class="fas fa-plus"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="card-body p-0">
|
||
<table class="table table-sm table-striped mb-0">
|
||
<thead class="thead-light">
|
||
<tr>
|
||
<th>Partner</th>
|
||
<th class="text-right">Hitelkeret</th>
|
||
<th class="text-right">Kint lévő</th>
|
||
<th class="text-right">Szabad</th>
|
||
<th class="text-center">Státusz</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="fb-credits-body"></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card card-info collapsed-card" id="fb-preorders-card" style="display:none;">
|
||
<div class="card-header">
|
||
<h3 class="card-title">
|
||
<i class="fas fa-clock mr-1"></i>
|
||
Nyitott előrendelések
|
||
<span class="badge badge-secondary ml-2" id="fb-preorders-count">0</span>
|
||
</h3>
|
||
<div class="card-tools">
|
||
<button type="button" class="btn btn-tool" data-card-widget="collapse">
|
||
<i class="fas fa-plus"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="card-body p-0">
|
||
<table class="table table-sm table-striped mb-0">
|
||
<thead class="thead-light">
|
||
<tr>
|
||
<th>Partner</th>
|
||
<th>Létrehozva</th>
|
||
<th class="text-center">Tételek</th>
|
||
<th class="text-center">Teljesített</th>
|
||
<th class="text-center">Státusz</th>
|
||
<th></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="fb-preorders-body"></tbody>
|
||
</table>
|
||
</div>
|
||
<div class="card-footer text-muted small">
|
||
<a href="/Admin/Preorders">Összes előrendelés →</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card card-secondary collapsed-card" id="fb-docs-card" style="display:none;">
|
||
<div class="card-header">
|
||
<h3 class="card-title">
|
||
<i class="fas fa-truck-loading mr-1"></i>
|
||
Feldolgozatlan szállítmányok
|
||
<span class="badge badge-secondary ml-2" id="fb-docs-count">0</span>
|
||
</h3>
|
||
<div class="card-tools">
|
||
<button type="button" class="btn btn-tool" data-card-widget="collapse">
|
||
<i class="fas fa-plus"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="card-body p-0">
|
||
<table class="table table-sm table-striped mb-0">
|
||
<thead class="thead-light">
|
||
<tr>
|
||
<th>Partner</th>
|
||
<th>Szállítás dátuma</th>
|
||
<th class="text-center">Tételek</th>
|
||
<th class="text-center">Feldolgozva</th>
|
||
<th></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="fb-docs-body"></tbody>
|
||
</table>
|
||
</div>
|
||
<div class="card-footer text-muted small">
|
||
<a href="/Admin/Shipping">Összes szállítmány →</a>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<script>
|
||
(function () {
|
||
|
||
// ── Helpers ──────────────────────────────────────────────────────────────
|
||
|
||
function fmt(num) {
|
||
return new Intl.NumberFormat('hu-HU', { style: 'currency', currency: 'HUF', maximumFractionDigits: 0 }).format(num);
|
||
}
|
||
|
||
function measuringBadge(status) {
|
||
var map = {
|
||
'NotStarted': '<span class="badge badge-secondary">Nem kezdett</span>',
|
||
'Started': '<span class="badge badge-warning">Mérés alatt</span>',
|
||
'Audited': '<span class="badge badge-success">Auditálva</span>'
|
||
};
|
||
return map[status] || '<span class="badge badge-light">' + status + '</span>';
|
||
}
|
||
|
||
function orderStatusBadge(status) {
|
||
var map = {
|
||
'Pending': '<span class="badge badge-secondary">Függőben</span>',
|
||
'Processing': '<span class="badge badge-info">Folyamatban</span>',
|
||
'Complete': '<span class="badge badge-success">Befejezve</span>',
|
||
'Cancelled': '<span class="badge badge-danger">Törölve</span>'
|
||
};
|
||
return map[status] || '<span class="badge badge-light">' + status + '</span>';
|
||
}
|
||
|
||
function creditBadge(status) {
|
||
var map = {
|
||
'ok': '<span class="badge badge-success">OK</span>',
|
||
'warning': '<span class="badge badge-warning">Közel a limithez</span>',
|
||
'exceeded': '<span class="badge badge-danger">Túllépve</span>'
|
||
};
|
||
return map[status] || status;
|
||
}
|
||
|
||
function preorderStatusBadge(status) {
|
||
var map = {
|
||
'Pending': '<span class="badge badge-warning">Várakozik</span>',
|
||
'PartiallyFulfilled': '<span class="badge badge-info">Részben teljesítve</span>',
|
||
'Confirmed': '<span class="badge badge-success">Megerősítve</span>',
|
||
'Cancelled': '<span class="badge badge-secondary">Törölve</span>'
|
||
};
|
||
return map[status] || status;
|
||
}
|
||
|
||
function alertTypeBadge(type) {
|
||
var map = {
|
||
'missing_innvoice': '<span class="badge badge-danger">InnVoice</span>',
|
||
'stale_measuring': '<span class="badge badge-warning">Régi mérés</span>',
|
||
'credit_exceeded': '<span class="badge badge-danger">Hitelkeret</span>',
|
||
'old_preorder': '<span class="badge badge-warning">Előrendelés</span>'
|
||
};
|
||
return map[type] || '<span class="badge badge-secondary">' + type + '</span>';
|
||
}
|
||
|
||
function alertLink(alert) {
|
||
if (alert.type === 'missing_innvoice' || alert.type === 'stale_measuring')
|
||
return '<a href="/Admin/Order/Edit/' + alert.orderId + '" class="btn btn-xs btn-outline-primary">Rendelés</a>';
|
||
if (alert.type === 'credit_exceeded')
|
||
return '<a href="/Admin/CustomerCredit/Details/' + alert.customerId + '" class="btn btn-xs btn-outline-warning">Hitelkeret</a>';
|
||
if (alert.type === 'old_preorder')
|
||
return '<a href="/Admin/Preorders" class="btn btn-xs btn-outline-info">Előrendelések</a>';
|
||
return '';
|
||
}
|
||
|
||
function alertDetail(alert) {
|
||
if (alert.type === 'missing_innvoice' || alert.type === 'stale_measuring')
|
||
return '#' + (alert.orderNumber || alert.orderId) + (alert.dateOfReceipt ? ' <small class="text-muted">' + alert.dateOfReceipt + '</small>' : '');
|
||
if (alert.type === 'credit_exceeded')
|
||
return fmt(alert.outstanding) + ' / ' + fmt(alert.creditLimit);
|
||
if (alert.type === 'old_preorder')
|
||
return alert.createdAt;
|
||
return alert.message || '';
|
||
}
|
||
|
||
// ── Main load ────────────────────────────────────────────────────────────
|
||
|
||
$.ajax({
|
||
url: '/Admin/CustomDashboard/GetDashboardData',
|
||
type: 'GET',
|
||
timeout: 60000,
|
||
success: function (data) {
|
||
|
||
// ── Pipeline stat boxes ──────────────────────────────────────────
|
||
var p = data.pipeline;
|
||
$('#fb-stat-total').text(p.total);
|
||
$('#fb-stat-measuring').text(p.measuring);
|
||
$('#fb-stat-audited').text(p.audited);
|
||
$('#fb-stat-innvoice').text(p.missingInnVoice);
|
||
$('#fb-stat-completed').text(p.completed);
|
||
|
||
// Highlight the InnVoice box red if there are missing syncs
|
||
if (p.missingInnVoice > 0)
|
||
$('#fb-stat-innvoice').closest('.info-box').addClass('bg-danger text-white').find('.info-box-text').addClass('text-white');
|
||
|
||
// ── Today's order rows ───────────────────────────────────────────
|
||
var $ob = $('#fb-orders-body').empty();
|
||
if (p.rows && p.rows.length > 0) {
|
||
p.rows.forEach(function (o) {
|
||
var innVoiceIcon = o.hasInnVoice
|
||
? '<i class="fas fa-check text-success"></i>'
|
||
: '<i class="fas fa-times text-danger"></i>';
|
||
$ob.append(
|
||
'<tr>' +
|
||
'<td><a href="/Admin/Order/Edit/' + o.id + '">#' + (o.orderNumber || o.id) + '</a></td>' +
|
||
'<td>' + (o.company || '–') + '</td>' +
|
||
'<td class="text-right text-nowrap">' + fmt(o.total) + '</td>' +
|
||
'<td class="text-center">' + measuringBadge(o.measuringStatus) + '</td>' +
|
||
'<td class="text-center">' + innVoiceIcon + '</td>' +
|
||
'<td class="text-center">' + orderStatusBadge(o.orderStatus) + '</td>' +
|
||
'<td class="text-nowrap small text-muted">' + (o.dateOfReceipt || '') + '</td>' +
|
||
'</tr>'
|
||
);
|
||
});
|
||
if (p.total > 20) {
|
||
$('#fb-orders-footer').show();
|
||
}
|
||
} else {
|
||
$ob.append('<tr><td colspan="7" class="text-center text-muted py-3">Nincs mai rendelés</td></tr>');
|
||
}
|
||
|
||
// ── Alerts ───────────────────────────────────────────────────────
|
||
if (data.alerts && data.alerts.length > 0) {
|
||
$('#fb-alerts-count').text(data.alerts.length);
|
||
var $ab = $('#fb-alerts-body').empty();
|
||
data.alerts.forEach(function (a) {
|
||
$ab.append(
|
||
'<tr>' +
|
||
'<td>' + alertTypeBadge(a.type) + '</td>' +
|
||
'<td>' + (a.company || '–') + '</td>' +
|
||
'<td class="small">' + alertDetail(a) + '</td>' +
|
||
'<td>' + alertLink(a) + '</td>' +
|
||
'</tr>'
|
||
);
|
||
});
|
||
$('#fb-alerts-card').show();
|
||
}
|
||
|
||
// ── Credit status ────────────────────────────────────────────────
|
||
if (data.creditStatus && data.creditStatus.length > 0) {
|
||
$('#fb-credits-count').text(data.creditStatus.length);
|
||
var $cb = $('#fb-credits-body').empty();
|
||
data.creditStatus.forEach(function (c) {
|
||
var rowClass = c.status === 'exceeded' ? 'table-danger' : c.status === 'warning' ? 'table-warning' : '';
|
||
$cb.append(
|
||
'<tr class="' + rowClass + '">' +
|
||
'<td><a href="/Admin/CustomerCredit/Details/' + c.customerId + '">' + c.company + '</a></td>' +
|
||
'<td class="text-right text-nowrap">' + fmt(c.creditLimit) + '</td>' +
|
||
'<td class="text-right text-nowrap">' + fmt(c.outstanding) + '</td>' +
|
||
'<td class="text-right text-nowrap">' + fmt(c.remaining) + '</td>' +
|
||
'<td class="text-center">' + creditBadge(c.status) + '</td>' +
|
||
'</tr>'
|
||
);
|
||
});
|
||
$('#fb-credits-card').show();
|
||
}
|
||
|
||
// ── Pending preorders ────────────────────────────────────────────
|
||
if (data.pendingPreorders && data.pendingPreorders.length > 0) {
|
||
$('#fb-preorders-count').text(data.pendingPreorders.length);
|
||
var $pb = $('#fb-preorders-body').empty();
|
||
data.pendingPreorders.forEach(function (p) {
|
||
$pb.append(
|
||
'<tr>' +
|
||
'<td>' + p.company + '</td>' +
|
||
'<td class="small text-muted">' + p.createdAt + '</td>' +
|
||
'<td class="text-center">' + p.itemCount + '</td>' +
|
||
'<td class="text-center">' + p.fulfilledCount + ' / ' + p.itemCount + '</td>' +
|
||
'<td class="text-center">' + preorderStatusBadge(p.status) + '</td>' +
|
||
'<td><a href="/Admin/Preorders/Details/' + p.id + '" class="btn btn-xs btn-outline-secondary">Részletek</a></td>' +
|
||
'</tr>'
|
||
);
|
||
});
|
||
$('#fb-preorders-card').show();
|
||
}
|
||
|
||
// ── Unprocessed shipping docs ────────────────────────────────────
|
||
if (data.unprocessedDocs && data.unprocessedDocs.length > 0) {
|
||
$('#fb-docs-count').text(data.unprocessedDocs.length);
|
||
var $db = $('#fb-docs-body').empty();
|
||
data.unprocessedDocs.forEach(function (d) {
|
||
var progress = d.totalItems > 0
|
||
? d.measuredItems + ' / ' + d.totalItems
|
||
: '–';
|
||
$db.append(
|
||
'<tr>' +
|
||
'<td>' + (d.partnerName || '–') + '</td>' +
|
||
'<td class="small text-muted">' + (d.shippingDate || '–') + '</td>' +
|
||
'<td class="text-center">' + d.totalItems + '</td>' +
|
||
'<td class="text-center">' + progress + '</td>' +
|
||
'<td><a href="/Admin/Shipping/ShippingDocument/' + d.id + '" class="btn btn-xs btn-outline-secondary">Megnyitás</a></td>' +
|
||
'</tr>'
|
||
);
|
||
});
|
||
$('#fb-docs-card').show();
|
||
}
|
||
|
||
// ── Show everything, hide spinner ────────────────────────────────
|
||
$('#fb-dashboard-loading').hide();
|
||
$('#fb-dashboard-content').show();
|
||
},
|
||
error: function (xhr, status, error) {
|
||
$('#fb-dashboard-loading').html(
|
||
'<p class="text-muted"><i class="fas fa-exclamation-circle mr-1"></i>Nem sikerült betölteni az operatív adatokat. (' + (xhr.status || status) + ')</p>'
|
||
);
|
||
}
|
||
});
|
||
|
||
}());
|
||
</script>
|