Files
trf_certest/public/userarea/errors.php
T
2026-10-06 11:59:02 +02:00

402 lines
17 KiB
PHP

<?php
include('include/headscript.php');
$template_id = intval($_GET['id'] ?? 0);
if (!$template_id) {
header("Location: xlstemplates_grid.php?status=error&message=" . urlencode("Template ID mancante"));
exit;
}
$user_id = $iduserlogin ?? 1;
$show_all_users = isset($_GET['all_users']) && $_GET['all_users'] == '1';
$allowedLimits = [20, 40, 60, 100];
$rawLimit = (int)($_GET['limit'] ?? 20);
$perPage = in_array($rawLimit, $allowedLimits) ? $rawLimit : 20;
$page = max(1, (int)($_GET['page'] ?? 1));
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$logDir = __DIR__ . '/logs/api/';
// Estrae un riassunto breve dell'errore dal file di log della commessa
function extractErrorSummary(string $logDir, ?string $logFile): string
{
if (empty($logFile)) return '';
$path = $logDir . basename($logFile);
if (!is_file($path)) return '';
$content = @file_get_contents($path);
if ($content === false || $content === '') return '';
// 1) prova col "detail" del JSON del 500
if (preg_match('/"detail"\s*:\s*"([^"]+)"/', $content, $m)) {
return trim(stripcslashes($m[1]));
}
// 2) fallback: ultima riga MESSAGE:
if (preg_match_all('/^MESSAGE:\s*(.+)$/m', $content, $m) && !empty($m[1])) {
return trim(end($m[1]));
}
return '';
}
// Query: solo le righe in errore per questo template
$userFilter = $show_all_users ? '' : 'AND d.user_id = ?';
$baseWhere = "WHERE d.templateid = ? AND d.status = 'x' {$userFilter}";
$baseParams = [$template_id];
if (!$show_all_users) $baseParams[] = $user_id;
$countStmt = $pdo->prepare("SELECT COUNT(*) FROM datadb d {$baseWhere}");
$countStmt->execute($baseParams);
$totalRows = (int)$countStmt->fetchColumn();
$totalPages = max(1, (int)ceil($totalRows / $perPage));
if ($page > $totalPages) $page = $totalPages;
$stmt = $pdo->prepare("
SELECT d.*, CONCAT(u.first_name, ' ', u.last_name) AS user_name
FROM datadb d
LEFT JOIN auth_users u ON d.user_id = u.id
{$baseWhere}
ORDER BY d.export_date DESC, d.iddatadb DESC
LIMIT {$perPage} OFFSET " . (($page - 1) * $perPage) . "
");
$stmt->execute($baseParams);
$rowsData = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<!doctype html>
<html lang="it">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
<?php include('cssinclude.php'); ?>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" crossorigin="anonymous" referrerpolicy="no-referrer" />
<style>
.err-table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
.err-table th,
.err-table td {
padding: 8px 10px;
border-bottom: 1px solid #dee2e6;
vertical-align: top;
}
.err-table th {
background: #f8f9fa;
text-align: left;
font-weight: 600;
color: #495057;
}
.err-table tr:hover {
background: #fff8f8;
}
.status-badge {
display: inline-block;
padding: 2px 8px;
font-size: 12px;
font-weight: 500;
border-radius: 12px;
}
.status-x {
background: #dc3545;
color: #fff;
}
.err-msg {
color: #b02a37;
max-width: 420px;
white-space: normal;
word-break: break-word;
}
.commessa-code {
font-size: 0.8em;
color: #555;
display: block;
margin-top: 2px;
}
.action-btn {
padding: 5px 8px;
margin-right: 4px;
border: none;
border-radius: 5px;
cursor: pointer;
color: #fff;
text-decoration: none;
display: inline-flex;
align-items: center;
}
.btn-resend {
background: #28a745;
}
.btn-log {
background: #17a2b8;
}
.btn-revert {
background: #fd7e14;
}
.btn-delete {
background: #dc3545;
}
.pager-bar {
display: flex;
align-items: center;
justify-content: space-between;
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 6px;
padding: 6px 14px;
font-size: 13px;
margin-bottom: 10px;
}
.pager-num,
.pager-btn {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 28px;
height: 28px;
padding: 0 6px;
border: 1px solid #ced4da;
border-radius: 4px;
text-decoration: none;
color: #495057;
margin-left: 4px;
}
.pager-num.active {
background: #0d6efd;
color: #fff;
border-color: #0d6efd;
}
</style>
<title>Export Errors - <?= htmlspecialchars($titlewebsite ?? '', ENT_QUOTES, 'UTF-8'); ?></title>
</head>
<body>
<div class="wrapper">
<?php include('include/navbar.php'); ?>
<?php include('include/topbar.php'); ?>
<div class="page-wrapper">
<div class="page-content">
<div class="mb-3 d-flex align-items-center gap-2">
<a href="imported.php?id=<?= $template_id ?>" class="btn btn-warning me-2">Imported (i)</a>
<a href="tolims.php?id=<?= $template_id ?>" class="btn btn-success me-2">To LIMS (l)</a>
<a href="errors.php?id=<?= $template_id ?>" class="btn btn-danger">Errors (x)</a>
<span class="ms-3">
<label class="form-check-label" style="font-size:13px; cursor:pointer;">
<?php $tp = $_GET;
unset($tp['all_users']);
$toggleBase = 'errors.php?' . http_build_query($tp); ?>
<input type="checkbox" class="form-check-input" <?= $show_all_users ? 'checked' : '' ?>
onchange="window.location.href='<?= htmlspecialchars($toggleBase) ?>' + (this.checked ? '&all_users=1' : '')">
Show all users
</label>
</span>
<span class="text-muted" style="font-size:12px;">(<?= count($rowsData) ?> of <?= $totalRows ?> errori<?= !$show_all_users ? ' — my records' : '' ?>)</span>
</div>
<?php if ($totalPages > 1):
$pageQuery = $_GET;
unset($pageQuery['page']);
$pageBase = 'errors.php?' . http_build_query($pageQuery); ?>
<div class="pager-bar">
<span>Pagina <?= $page ?> di <?= $totalPages ?> — <?= $totalRows ?> righe</span>
<span>
<?php for ($p = 1; $p <= $totalPages; $p++): ?>
<a href="<?= htmlspecialchars($pageBase . '&page=' . $p) ?>" class="pager-num <?= $p === $page ? 'active' : '' ?>"><?= $p ?></a>
<?php endfor; ?>
</span>
</div>
<?php endif; ?>
<div class="card radius-10">
<div class="card-header">
<span style="font-weight:600;"><i class="fas fa-triangle-exclamation" style="color:#dc3545;"></i> Export falliti verso il LIMS</span>
</div>
<div class="card-body">
<?php if (empty($rowsData)): ?>
<p class="text-muted mb-0">Nessun errore di export. 🎉</p>
<?php else: ?>
<div style="overflow-x:auto;">
<table class="err-table">
<thead>
<tr>
<th style="width:210px;">Azioni</th>
<th>Status</th>
<th>Client</th>
<th>Errore</th>
<th style="width:150px;">Data export</th>
<th>File import</th>
</tr>
</thead>
<tbody>
<?php foreach ($rowsData as $row):
$errMsg = extractErrorSummary($logDir, $row['log_file'] ?? null); ?>
<tr data-id="<?= (int)$row['iddatadb'] ?>">
<td>
<button type="button" class="action-btn btn-resend resend-btn" data-iddatadb="<?= (int)$row['iddatadb'] ?>" title="Reinvia al LIMS"><i class="fas fa-paper-plane"></i></button>
<?php if (!empty($row['log_file'])): ?>
<a href="download_logs.php?iddatadb=<?= (int)$row['iddatadb'] ?>" class="action-btn btn-log" title="Scarica log"><i class="fas fa-file-download"></i></a>
<?php endif; ?>
<button type="button" class="action-btn btn-revert revert-btn" data-iddatadb="<?= (int)$row['iddatadb'] ?>" title="Riporta a Imported"><i class="fas fa-rotate-left"></i></button>
<button type="button" class="action-btn btn-delete delete-btn" data-iddatadb="<?= (int)$row['iddatadb'] ?>" title="Elimina record"><i class="fas fa-trash"></i></button>
</td>
<td>
<span class="status-badge status-x">Error</span>
<?php if (!empty($row['commessaweb'])): ?>
<span class="commessa-code"><?= htmlspecialchars($row['commessaweb']) ?></span>
<?php endif; ?>
</td>
<td><span class="client-text" data-client-id="<?= htmlspecialchars((string)($row['idclient'] ?? '')) ?>"><?= htmlspecialchars((string)($row['idclient'] ?? '')) ?></span></td>
<td class="err-msg"><?= htmlspecialchars($errMsg ?: '—') ?></td>
<td><?= htmlspecialchars((string)($row['export_date'] ?? '')) ?></td>
<td><?php if (!empty($row['filename_import'])): ?><a href="imported_trf/<?= htmlspecialchars($row['filename_import']) ?>" target="_blank">File</a><?php endif; ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<div class="overlay toggle-icon"></div>
<?php include('include/footer.php'); ?>
</div>
<?php include('jsinclude.php'); ?>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(function() {
// Risoluzione nomi cliente (come in tolims.php)
(function() {
const ids = new Set();
document.querySelectorAll('.client-text').forEach(el => {
const id = el.dataset.clientId;
if (id) ids.add(id);
});
ids.forEach(id => {
fetch('search_clienti.php?id=' + encodeURIComponent(id))
.then(r => r.json())
.then(j => {
const it = (j.results || [])[0];
if (it) document.querySelectorAll('.client-text[data-client-id="' + id + '"]').forEach(el => el.textContent = it.text);
})
.catch(() => {});
});
})();
// Reinvia: richiama export_to_lims.php. Se la commessa era già stata creata,
// il CHECK PRELIMINARE la verifica nel LIMS e non ne crea una seconda.
$(document).on('click', '.resend-btn', function() {
const $btn = $(this);
const iddatadb = $btn.data('iddatadb');
if (!confirm('Reinviare questa commessa al LIMS? Se risulta già nel LIMS verrà solo riallineata, altrimenti ne verrà creata una nuova.')) return;
const original = $btn.html();
$btn.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i>');
$.ajax({
url: 'export_to_lims.php',
method: 'POST',
dataType: 'json',
data: {
iddatadb: iddatadb
},
success: function(res) {
if (res && res.success) {
const $tr = $('tr[data-id="' + iddatadb + '"]');
$tr.css('background', '#d4edda');
setTimeout(() => $tr.fadeOut(300, () => $tr.remove()), 500);
} else {
alert('Ancora errore: ' + ((res && res.message) ? res.message : 'invio non riuscito'));
$btn.prop('disabled', false).html(original);
}
},
error: function() {
alert('Errore di rete durante il reinvio.');
$btn.prop('disabled', false).html(original);
}
});
});
// Elimina record
$(document).on('click', '.delete-btn', function() {
const $btn = $(this);
const iddatadb = $btn.data('iddatadb');
if (!confirm('Eliminare definitivamente questo record? L\'operazione non è reversibile.')) return;
const original = $btn.html();
$btn.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i>');
$.ajax({
url: 'revert_status.php',
method: 'POST',
dataType: 'json',
data: {
iddatadb: iddatadb,
action: 'delete'
},
success: function(res) {
if (res && res.success) {
const $tr = $('tr[data-id="' + iddatadb + '"]');
$tr.fadeOut(300, () => $tr.remove());
} else {
alert('Errore: ' + ((res && res.message) ? res.message : 'cancellazione non riuscita'));
$btn.prop('disabled', false).html(original);
}
},
error: function() {
alert('Errore di rete.');
$btn.prop('disabled', false).html(original);
}
});
});
// Revert a Imported (i)
$(document).on('click', '.revert-btn', function() {
const $btn = $(this);
const iddatadb = $btn.data('iddatadb');
if (!confirm('Riportare questo record a Imported (i)?')) return;
$.ajax({
url: 'revert_status.php',
method: 'POST',
dataType: 'json',
data: {
iddatadb: iddatadb,
status: 'i'
},
success: function(res) {
if (res && res.success) {
const $tr = $('tr[data-id="' + iddatadb + '"]');
$tr.fadeOut(300, () => $tr.remove());
} else {
alert('Errore: ' + ((res && res.message) ? res.message : 'operazione non riuscita'));
}
},
error: function() {
alert('Errore di rete.');
}
});
});
});
</script>
</body>
</html>