Import
This commit is contained in:
1 parent
51c49327d4
commit
bb2686ef62
4 files changed
+918
No files matched your search
@@ -0,0 +1,226 @@
|
||||
<?php
|
||||
/**
|
||||
* admin/import_history.php
|
||||
*
|
||||
* Historique des imports .ntm et de leurs erreurs. Lecture seule sauf
|
||||
* pour la purge optionnelle des lignes d'erreur d'un import, action
|
||||
* protegee par CSRF + confirmation explicite.
|
||||
*
|
||||
* CORRIGE (20/09/2026, suite a erreur 500 en usage reel) :
|
||||
* - require_once _nav.php manquait, provoquant une erreur fatale
|
||||
* "Call to undefined function render_admin_nav()".
|
||||
* - Les tables import_batches / import_errors necessaires a cette
|
||||
* page n'existaient pas : voir
|
||||
* admin/sql/migration_add_missing_tables.sql.
|
||||
*
|
||||
* IMPORTANT : import_batches/import_errors sont DISTINCTES de la table
|
||||
* `imports` deja existante et alimentee par lib/ntm_import_to_staging.php
|
||||
* (utilisee par admin/import_ntm.php). Tant qu'aucune page n'insere
|
||||
* explicitement dans import_batches/import_errors, cette page
|
||||
* s'affichera correctement mais restera VIDE ("Aucun import pour le
|
||||
* moment"), meme si vous avez deja importe des fichiers .ntm via
|
||||
* import_ntm.php. Une evolution future pourrait brancher cette page
|
||||
* directement sur la table `imports` existante plutot que de dupliquer
|
||||
* un systeme de suivi parallele jamais alimente.
|
||||
*
|
||||
* Corrige suite a l'audit du 20/09/2026 : le detail technique des
|
||||
* exceptions n'est plus affiche a l'ecran ; il est journalise via
|
||||
* error_log().
|
||||
*
|
||||
* Hypothese de schema :
|
||||
* import_batches(id, filename, imported_at, imported_by, status,
|
||||
* rows_total, rows_imported, rows_rejected, country_id)
|
||||
* import_errors(id, batch_id, line_number, message, raw_line)
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/admin_auth.php';
|
||||
require_once __DIR__ . '/../lib/db.php';
|
||||
require_once __DIR__ . '/_nav.php';
|
||||
|
||||
require_admin_auth();
|
||||
|
||||
$db = pdo();
|
||||
$feedback = null;
|
||||
$feedbackType = 'success';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
require_csrf_token();
|
||||
|
||||
$action = $_POST['action'] ?? '';
|
||||
$confirmed = isset($_POST['confirm']) && $_POST['confirm'] === '1';
|
||||
$batchId = filter_input(INPUT_POST, 'batch_id', FILTER_VALIDATE_INT);
|
||||
|
||||
if ($action === 'purge_errors') {
|
||||
if (!$confirmed || !$batchId) {
|
||||
$feedback = 'Purge refusee : confirmation ou identifiant manquant.';
|
||||
$feedbackType = 'error';
|
||||
} else {
|
||||
try {
|
||||
$stmt = $db->prepare('DELETE FROM import_errors WHERE batch_id = ?');
|
||||
$stmt->execute([$batchId]);
|
||||
$feedback = sprintf('%d ligne(s) d\'erreur purgee(s) pour l\'import #%d.', $stmt->rowCount(), $batchId);
|
||||
} catch (Throwable $e) {
|
||||
error_log('[import_history] purge_errors: ' . $e->getMessage());
|
||||
$feedback = 'Une erreur est survenue lors de la purge.';
|
||||
$feedbackType = 'error';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$page = max(1, filter_input(INPUT_GET, 'page', FILTER_VALIDATE_INT) ?: 1);
|
||||
$perPage = 25;
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$totalBatches = 0;
|
||||
$batches = [];
|
||||
try {
|
||||
$totalBatches = (int)$db->query('SELECT COUNT(*) FROM import_batches')->fetchColumn();
|
||||
|
||||
$stmt = $db->prepare(
|
||||
'SELECT id, filename, imported_at, imported_by, status, rows_total, rows_imported, rows_rejected
|
||||
FROM import_batches
|
||||
ORDER BY imported_at DESC
|
||||
LIMIT :limit OFFSET :offset'
|
||||
);
|
||||
$stmt->bindValue(':limit', $perPage, PDO::PARAM_INT);
|
||||
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
$batches = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (Throwable $e) {
|
||||
error_log('[import_history] liste batches: ' . $e->getMessage());
|
||||
$feedback = 'Impossible de charger l\'historique des imports. Consultez les journaux du serveur.';
|
||||
$feedbackType = 'error';
|
||||
}
|
||||
|
||||
$errorsByBatch = [];
|
||||
$detailBatchId = filter_input(INPUT_GET, 'detail', FILTER_VALIDATE_INT);
|
||||
if ($detailBatchId) {
|
||||
try {
|
||||
$stmt = $db->prepare(
|
||||
'SELECT line_number, message, raw_line
|
||||
FROM import_errors
|
||||
WHERE batch_id = ?
|
||||
ORDER BY line_number ASC
|
||||
LIMIT 500'
|
||||
);
|
||||
$stmt->execute([$detailBatchId]);
|
||||
$errorsByBatch = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (Throwable $e) {
|
||||
error_log('[import_history] detail erreurs: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$totalPages = max(1, (int)ceil($totalBatches / $perPage));
|
||||
$user = current_admin_username();
|
||||
|
||||
function status_badge(string $status): string
|
||||
{
|
||||
$map = [
|
||||
'success' => 'badge-success',
|
||||
'partial' => 'badge-warning',
|
||||
'failed' => 'badge-error',
|
||||
];
|
||||
$class = $map[$status] ?? 'badge';
|
||||
return '<span class="badge ' . $class . '">' . htmlspecialchars($status, ENT_QUOTES, 'UTF-8') . '</span>';
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Historique des imports — NetMonster Admin</title>
|
||||
<link rel="stylesheet" href="assets/theme.css">
|
||||
</head>
|
||||
<body>
|
||||
<?php render_admin_nav('import_history.php', $user); ?>
|
||||
|
||||
<main class="page-wrap">
|
||||
<h1>Historique des imports .ntm</h1>
|
||||
<p class="page-subtitle">Suivi en lecture seule des imports realises. La purge des erreurs d'un import est possible mais n'affecte jamais les donnees deja validees.</p>
|
||||
|
||||
<?php if ($feedback !== null): ?>
|
||||
<p class="<?= $feedbackType === 'error' ? 'msg-err' : 'msg-ok' ?>">
|
||||
<?= htmlspecialchars($feedback, ENT_QUOTES, 'UTF-8') ?>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
|
||||
<section class="card">
|
||||
<h2>Imports (<?= $totalBatches ?>)</h2>
|
||||
<table class="data-table">
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Fichier</th>
|
||||
<th>Date</th>
|
||||
<th>Utilisateur</th>
|
||||
<th>Statut</th>
|
||||
<th>Lignes totales</th>
|
||||
<th>Importees</th>
|
||||
<th>Rejetees</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
<?php foreach ($batches as $batch): ?>
|
||||
<tr>
|
||||
<td><?= (int)$batch['id'] ?></td>
|
||||
<td><?= htmlspecialchars((string)$batch['filename'], ENT_QUOTES, 'UTF-8') ?></td>
|
||||
<td><?= htmlspecialchars((string)$batch['imported_at'], ENT_QUOTES, 'UTF-8') ?></td>
|
||||
<td><?= htmlspecialchars((string)$batch['imported_by'], ENT_QUOTES, 'UTF-8') ?></td>
|
||||
<td><?= status_badge((string)$batch['status']) ?></td>
|
||||
<td><?= (int)$batch['rows_total'] ?></td>
|
||||
<td><?= (int)$batch['rows_imported'] ?></td>
|
||||
<td><?= (int)$batch['rows_rejected'] ?></td>
|
||||
<td>
|
||||
<a href="import_history.php?detail=<?= (int)$batch['id'] ?>">Voir erreurs</a>
|
||||
<?php if ((int)$batch['rows_rejected'] > 0): ?>
|
||||
<form method="post" style="display:inline" onsubmit="return confirm('Confirmer la purge des erreurs de cet import ?');">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="action" value="purge_errors">
|
||||
<input type="hidden" name="confirm" value="1">
|
||||
<input type="hidden" name="batch_id" value="<?= (int)$batch['id'] ?>">
|
||||
<button type="submit" class="reject-small">Purger erreurs</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php if (empty($batches)): ?>
|
||||
<tr><td colspan="9" class="no-cadastre">Aucun import pour le moment.</td></tr>
|
||||
<?php endif; ?>
|
||||
</table>
|
||||
|
||||
<?php if ($totalPages > 1): ?>
|
||||
<nav class="pagination">
|
||||
<?php for ($p = 1; $p <= $totalPages; $p++): ?>
|
||||
<a href="import_history.php?page=<?= $p ?>" class="<?= $p === $page ? 'active' : '' ?>"><?= $p ?></a>
|
||||
<?php endfor; ?>
|
||||
</nav>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<?php if ($detailBatchId): ?>
|
||||
<section class="card">
|
||||
<h2>Erreurs de l'import #<?= (int)$detailBatchId ?></h2>
|
||||
<?php if (empty($errorsByBatch)): ?>
|
||||
<p>Aucune erreur enregistree pour cet import.</p>
|
||||
<?php else: ?>
|
||||
<table class="data-table">
|
||||
<tr>
|
||||
<th>Ligne</th>
|
||||
<th>Message</th>
|
||||
<th>Contenu brut</th>
|
||||
</tr>
|
||||
<?php foreach ($errorsByBatch as $err): ?>
|
||||
<tr>
|
||||
<td><?= (int)$err['line_number'] ?></td>
|
||||
<td><?= htmlspecialchars((string)$err['message'], ENT_QUOTES, 'UTF-8') ?></td>
|
||||
<td><code><?= htmlspecialchars((string)$err['raw_line'], ENT_QUOTES, 'UTF-8') ?></code></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user