756 lines
33 KiB
PHP
Executable File
756 lines
33 KiB
PHP
Executable File
<?php
|
|
/**
|
|
* Office 365 Import - WEVAL
|
|
* Import des comptes depuis fichier Excel/CSV
|
|
* CORRIGÉ: Conversion de date DD/MM/YYYY -> YYYY-MM-DD
|
|
*/
|
|
|
|
session_start();
|
|
header('Content-Type: text/html; charset=utf-8');
|
|
|
|
try {
|
|
$pdo = new PDO("pgsql:host=localhost;dbname=adx_system", "admin", "admin123");
|
|
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
} catch (Exception $e) {
|
|
die("Erreur DB: " . $e->getMessage());
|
|
}
|
|
|
|
// Ajouter colonnes si pas présentes
|
|
try {
|
|
$pdo->exec("ALTER TABLE admin.office_accounts ADD COLUMN IF NOT EXISTS source VARCHAR(100)");
|
|
$pdo->exec("ALTER TABLE admin.office_accounts ADD COLUMN IF NOT EXISTS account_type VARCHAR(50)");
|
|
$pdo->exec("ALTER TABLE admin.office_accounts ADD COLUMN IF NOT EXISTS client_id VARCHAR(100)");
|
|
$pdo->exec("ALTER TABLE admin.office_accounts ADD COLUMN IF NOT EXISTS secret_id TEXT");
|
|
$pdo->exec("ALTER TABLE admin.office_accounts ADD COLUMN IF NOT EXISTS comment TEXT");
|
|
$pdo->exec("ALTER TABLE admin.office_accounts ADD COLUMN IF NOT EXISTS import_date DATE");
|
|
} catch (Exception $e) {}
|
|
|
|
/**
|
|
* Convertit une date en format YYYY-MM-DD pour PostgreSQL
|
|
* Supporte: DD/MM/YYYY, MM/DD/YYYY, YYYY-MM-DD, DD-MM-YYYY
|
|
*/
|
|
function parseDate($dateStr) {
|
|
$dateStr = trim($dateStr);
|
|
if (empty($dateStr)) return date('Y-m-d');
|
|
|
|
// Déjà au format YYYY-MM-DD ?
|
|
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $dateStr)) {
|
|
return $dateStr;
|
|
}
|
|
|
|
// Format DD/MM/YYYY ou DD-MM-YYYY (format européen)
|
|
if (preg_match('/^(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})$/', $dateStr, $m)) {
|
|
$day = intval($m[1]);
|
|
$month = intval($m[2]);
|
|
$year = intval($m[3]);
|
|
|
|
// Si le jour > 12, c'est forcément DD/MM/YYYY
|
|
// Si le mois > 12, c'est forcément MM/DD/YYYY (mais invalide)
|
|
// On assume DD/MM/YYYY (format européen) par défaut
|
|
|
|
// Validation basique
|
|
if ($day > 31) {
|
|
// Peut-être MM/DD/YYYY inversé ?
|
|
list($day, $month) = [$month, $day];
|
|
}
|
|
if ($month > 12) {
|
|
// Mois invalide, inverser
|
|
list($day, $month) = [$month, $day];
|
|
}
|
|
|
|
// Vérification finale
|
|
if ($month > 12 || $day > 31) {
|
|
return date('Y-m-d'); // Date invalide, utiliser aujourd'hui
|
|
}
|
|
|
|
return sprintf('%04d-%02d-%02d', $year, $month, $day);
|
|
}
|
|
|
|
// Essayer strtotime en dernier recours
|
|
$ts = strtotime($dateStr);
|
|
if ($ts !== false) {
|
|
return date('Y-m-d', $ts);
|
|
}
|
|
|
|
return date('Y-m-d');
|
|
}
|
|
|
|
$message = '';
|
|
$messageType = '';
|
|
$importResults = [];
|
|
$previewData = [];
|
|
$showPreview = false;
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
|
|
// Action: Aperçu
|
|
if (isset($_POST['action']) && $_POST['action'] === 'preview' && isset($_FILES['excel_file'])) {
|
|
$file = $_FILES['excel_file'];
|
|
if ($file['error'] === UPLOAD_ERR_OK) {
|
|
$content = file_get_contents($file['tmp_name']);
|
|
$lines = explode("\n", $content);
|
|
|
|
$isFirstLine = true;
|
|
$lineNum = 0;
|
|
foreach ($lines as $line) {
|
|
$line = trim($line);
|
|
if (empty($line)) continue;
|
|
$lineNum++;
|
|
|
|
if (strpos($line, "\t") !== false) $parts = explode("\t", $line);
|
|
elseif (strpos($line, ';') !== false) $parts = explode(';', $line);
|
|
else $parts = explode(',', $line);
|
|
|
|
if ($isFirstLine) {
|
|
$isFirstLine = false;
|
|
$h = strtolower(trim($parts[0] ?? ''));
|
|
if (in_array($h, ['source', 'account', 'email', 'account mail'])) continue;
|
|
}
|
|
|
|
$email = trim($parts[1] ?? '');
|
|
if (empty($email) || strpos($email, '@') === false) continue;
|
|
|
|
$check = $pdo->prepare("SELECT id, admin_password FROM admin.office_accounts WHERE admin_email = ?");
|
|
$check->execute([$email]);
|
|
$existing = $check->fetch(PDO::FETCH_ASSOC);
|
|
|
|
$previewData[] = [
|
|
'line' => $lineNum,
|
|
'source' => trim($parts[0] ?? ''),
|
|
'email' => $email,
|
|
'password' => trim($parts[2] ?? ''),
|
|
'type' => trim($parts[3] ?? ''),
|
|
'date_raw' => trim($parts[7] ?? ''),
|
|
'date_parsed' => parseDate(trim($parts[7] ?? '')),
|
|
'exists' => $existing ? true : false,
|
|
'existing_id' => $existing['id'] ?? null,
|
|
'password_changed' => $existing ? ($existing['admin_password'] !== trim($parts[2] ?? '')) : false
|
|
];
|
|
|
|
if (count($previewData) >= 100) break;
|
|
}
|
|
|
|
$showPreview = true;
|
|
$tempFile = '/tmp/office_import_' . session_id() . '.csv';
|
|
move_uploaded_file($file['tmp_name'], $tempFile);
|
|
$_SESSION['import_file'] = $tempFile;
|
|
}
|
|
}
|
|
|
|
// Action: Import
|
|
if (isset($_POST['action']) && $_POST['action'] === 'import') {
|
|
$duplicateAction = $_POST['duplicate_action'] ?? 'update';
|
|
$tempFile = $_SESSION['import_file'] ?? null;
|
|
|
|
if ($tempFile && file_exists($tempFile)) {
|
|
$content = file_get_contents($tempFile);
|
|
$lines = explode("\n", $content);
|
|
|
|
$inserted = 0;
|
|
$updated = 0;
|
|
$skipped = 0;
|
|
$errors = [];
|
|
|
|
$isFirstLine = true;
|
|
foreach ($lines as $line) {
|
|
$line = trim($line);
|
|
if (empty($line)) continue;
|
|
|
|
if (strpos($line, "\t") !== false) $parts = explode("\t", $line);
|
|
elseif (strpos($line, ';') !== false) $parts = explode(';', $line);
|
|
else $parts = explode(',', $line);
|
|
|
|
if ($isFirstLine) {
|
|
$isFirstLine = false;
|
|
$h = strtolower(trim($parts[0] ?? ''));
|
|
if (in_array($h, ['source', 'account', 'email', 'account mail'])) continue;
|
|
}
|
|
|
|
$source = trim($parts[0] ?? '');
|
|
$email = trim($parts[1] ?? '');
|
|
$password = trim($parts[2] ?? '');
|
|
$type = trim($parts[3] ?? '');
|
|
$clientId = trim($parts[4] ?? '');
|
|
$tenantId = trim($parts[5] ?? '');
|
|
$secretId = trim($parts[6] ?? '');
|
|
$importDateRaw = trim($parts[7] ?? '');
|
|
$comment = trim($parts[8] ?? '');
|
|
|
|
if (empty($email) || strpos($email, '@') === false) {
|
|
$skipped++;
|
|
continue;
|
|
}
|
|
|
|
$emailParts = explode('@', $email);
|
|
$name = $emailParts[0] ?? 'Account';
|
|
$tenantDomain = $emailParts[1] ?? '';
|
|
|
|
// CORRECTION: Utiliser la fonction parseDate
|
|
$importDate = parseDate($importDateRaw);
|
|
|
|
try {
|
|
$check = $pdo->prepare("SELECT id FROM admin.office_accounts WHERE admin_email = ?");
|
|
$check->execute([$email]);
|
|
$existing = $check->fetch();
|
|
|
|
if ($existing) {
|
|
if ($duplicateAction === 'skip') {
|
|
$skipped++;
|
|
continue;
|
|
} elseif ($duplicateAction === 'update') {
|
|
$stmt = $pdo->prepare("UPDATE admin.office_accounts SET
|
|
admin_password = COALESCE(NULLIF(?, ''), admin_password),
|
|
source = COALESCE(NULLIF(?, ''), source),
|
|
account_type = COALESCE(NULLIF(?, ''), account_type),
|
|
client_id = COALESCE(NULLIF(?, ''), client_id),
|
|
tenant_id = COALESCE(NULLIF(?, ''), tenant_id),
|
|
secret_id = COALESCE(NULLIF(?, ''), secret_id),
|
|
comment = COALESCE(NULLIF(?, ''), comment),
|
|
last_update = NOW()
|
|
WHERE admin_email = ?");
|
|
$stmt->execute([$password, $source, $type, $clientId, $tenantId, $secretId, $comment, $email]);
|
|
$updated++;
|
|
} elseif ($duplicateAction === 'replace') {
|
|
$stmt = $pdo->prepare("UPDATE admin.office_accounts SET
|
|
admin_password = ?,
|
|
source = ?,
|
|
account_type = ?,
|
|
client_id = ?,
|
|
tenant_id = ?,
|
|
secret_id = ?,
|
|
comment = ?,
|
|
last_update = NOW(),
|
|
password_status = NULL,
|
|
has_mfa = NULL,
|
|
has_license = NULL,
|
|
status = 'Pending'
|
|
WHERE admin_email = ?");
|
|
$stmt->execute([$password, $source ?: null, $type ?: null, $clientId ?: null, $tenantId ?: null, $secretId ?: null, $comment ?: null, $email]);
|
|
$updated++;
|
|
}
|
|
} else {
|
|
$stmt = $pdo->prepare("INSERT INTO admin.office_accounts
|
|
(name, tenant_domain, admin_email, admin_password, source, account_type,
|
|
client_id, tenant_id, secret_id, import_date, status, created_by, comment)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'Pending', 'Import', ?)");
|
|
$stmt->execute([
|
|
$name, $tenantDomain, $email, $password,
|
|
$source ?: null, $type ?: null, $clientId ?: null,
|
|
$tenantId ?: null, $secretId ?: null, $importDate, $comment ?: null
|
|
]);
|
|
$inserted++;
|
|
}
|
|
} catch (Exception $e) {
|
|
$errors[] = "$email: " . $e->getMessage();
|
|
}
|
|
}
|
|
|
|
unlink($tempFile);
|
|
unset($_SESSION['import_file']);
|
|
|
|
$message = "Import terminé : $inserted nouveaux, $updated mis à jour, $skipped ignorés";
|
|
$messageType = "success";
|
|
$importResults = compact('inserted', 'updated', 'skipped', 'errors');
|
|
} else {
|
|
$message = "Fichier non trouvé. Veuillez réuploader.";
|
|
$messageType = "error";
|
|
}
|
|
}
|
|
|
|
// Import direct
|
|
if (isset($_POST['action']) && $_POST['action'] === 'direct_import' && isset($_FILES['excel_file'])) {
|
|
$file = $_FILES['excel_file'];
|
|
$duplicateAction = $_POST['duplicate_action'] ?? 'update';
|
|
|
|
if ($file['error'] === UPLOAD_ERR_OK) {
|
|
$content = file_get_contents($file['tmp_name']);
|
|
$lines = explode("\n", $content);
|
|
|
|
$inserted = 0;
|
|
$updated = 0;
|
|
$skipped = 0;
|
|
$errors = [];
|
|
|
|
$isFirstLine = true;
|
|
foreach ($lines as $line) {
|
|
$line = trim($line);
|
|
if (empty($line)) continue;
|
|
|
|
if (strpos($line, "\t") !== false) $parts = explode("\t", $line);
|
|
elseif (strpos($line, ';') !== false) $parts = explode(';', $line);
|
|
else $parts = explode(',', $line);
|
|
|
|
if ($isFirstLine) {
|
|
$isFirstLine = false;
|
|
$h = strtolower(trim($parts[0] ?? ''));
|
|
if (in_array($h, ['source', 'account', 'email', 'account mail'])) continue;
|
|
}
|
|
|
|
$source = trim($parts[0] ?? '');
|
|
$email = trim($parts[1] ?? '');
|
|
$password = trim($parts[2] ?? '');
|
|
$type = trim($parts[3] ?? '');
|
|
$clientId = trim($parts[4] ?? '');
|
|
$tenantId = trim($parts[5] ?? '');
|
|
$secretId = trim($parts[6] ?? '');
|
|
$importDateRaw = trim($parts[7] ?? '');
|
|
$comment = trim($parts[8] ?? '');
|
|
|
|
if (empty($email) || strpos($email, '@') === false) {
|
|
$skipped++;
|
|
continue;
|
|
}
|
|
|
|
$emailParts = explode('@', $email);
|
|
$name = $emailParts[0] ?? 'Account';
|
|
$tenantDomain = $emailParts[1] ?? '';
|
|
|
|
// CORRECTION: Utiliser la fonction parseDate
|
|
$importDate = parseDate($importDateRaw);
|
|
|
|
try {
|
|
$check = $pdo->prepare("SELECT id FROM admin.office_accounts WHERE admin_email = ?");
|
|
$check->execute([$email]);
|
|
$existing = $check->fetch();
|
|
|
|
if ($existing) {
|
|
if ($duplicateAction === 'skip') {
|
|
$skipped++;
|
|
continue;
|
|
} elseif ($duplicateAction === 'update') {
|
|
$stmt = $pdo->prepare("UPDATE admin.office_accounts SET
|
|
admin_password = COALESCE(NULLIF(?, ''), admin_password),
|
|
source = COALESCE(NULLIF(?, ''), source),
|
|
account_type = COALESCE(NULLIF(?, ''), account_type),
|
|
client_id = COALESCE(NULLIF(?, ''), client_id),
|
|
tenant_id = COALESCE(NULLIF(?, ''), tenant_id),
|
|
secret_id = COALESCE(NULLIF(?, ''), secret_id),
|
|
comment = COALESCE(NULLIF(?, ''), comment),
|
|
last_update = NOW()
|
|
WHERE admin_email = ?");
|
|
$stmt->execute([$password, $source, $type, $clientId, $tenantId, $secretId, $comment, $email]);
|
|
$updated++;
|
|
} elseif ($duplicateAction === 'replace') {
|
|
$stmt = $pdo->prepare("UPDATE admin.office_accounts SET
|
|
admin_password = ?,
|
|
source = ?,
|
|
account_type = ?,
|
|
client_id = ?,
|
|
tenant_id = ?,
|
|
secret_id = ?,
|
|
comment = ?,
|
|
last_update = NOW(),
|
|
password_status = NULL,
|
|
has_mfa = NULL,
|
|
has_license = NULL,
|
|
status = 'Pending'
|
|
WHERE admin_email = ?");
|
|
$stmt->execute([$password, $source ?: null, $type ?: null, $clientId ?: null, $tenantId ?: null, $secretId ?: null, $comment ?: null, $email]);
|
|
$updated++;
|
|
}
|
|
} else {
|
|
$stmt = $pdo->prepare("INSERT INTO admin.office_accounts
|
|
(name, tenant_domain, admin_email, admin_password, source, account_type,
|
|
client_id, tenant_id, secret_id, import_date, status, created_by, comment)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'Pending', 'Import', ?)");
|
|
$stmt->execute([
|
|
$name, $tenantDomain, $email, $password,
|
|
$source ?: null, $type ?: null, $clientId ?: null,
|
|
$tenantId ?: null, $secretId ?: null, $importDate, $comment ?: null
|
|
]);
|
|
$inserted++;
|
|
}
|
|
} catch (Exception $e) {
|
|
$errors[] = "$email: " . $e->getMessage();
|
|
}
|
|
}
|
|
|
|
$message = "Import terminé : $inserted nouveaux, $updated mis à jour, $skipped ignorés";
|
|
$messageType = "success";
|
|
$importResults = compact('inserted', 'updated', 'skipped', 'errors');
|
|
}
|
|
}
|
|
}
|
|
|
|
$stats = $pdo->query("SELECT COUNT(*) as total FROM admin.office_accounts")->fetch();
|
|
$duplicateCount = 0;
|
|
if (!empty($previewData)) {
|
|
$duplicateCount = count(array_filter($previewData, fn($p) => $p['exists']));
|
|
}
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Import Office 365 - WEVAL</title>
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body {
|
|
font-family: 'Segoe UI', system-ui, sans-serif;
|
|
background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 50%, #f8fafc 100%);
|
|
min-height: 100vh;
|
|
color: #1e293b;
|
|
padding: 20px;
|
|
}
|
|
.container { max-width: 1200px; margin: 0 auto; }
|
|
|
|
.header {
|
|
background: #ffffff;
|
|
border-radius: 16px;
|
|
padding: 24px 32px;
|
|
margin-bottom: 24px;
|
|
border: 1px solid #e2e8f0;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
.header h1 {
|
|
font-size: 28px;
|
|
background: linear-gradient(135deg, #0891b2, #0e7490);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
}
|
|
.header p { color: #64748b; margin-top: 4px; }
|
|
|
|
.btn {
|
|
padding: 10px 20px;
|
|
border: none;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
font-weight: 600;
|
|
font-size: 13px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
text-decoration: none;
|
|
transition: all 0.2s;
|
|
}
|
|
.btn-primary { background: linear-gradient(135deg, #0891b2, #06b6d4); color: white; }
|
|
.btn-secondary { background: #f1f5f9; color: #475569; border: 1px solid #e2e8f0; }
|
|
.btn-success { background: linear-gradient(135deg, #059669, #10b981); color: white; }
|
|
.btn-purple { background: linear-gradient(135deg, #7c3aed, #8b5cf6); color: white; }
|
|
|
|
.card {
|
|
background: #ffffff;
|
|
border-radius: 12px;
|
|
border: 1px solid #e2e8f0;
|
|
margin-bottom: 20px;
|
|
}
|
|
.card-header {
|
|
background: #f8fafc;
|
|
padding: 16px 20px;
|
|
border-bottom: 1px solid #e2e8f0;
|
|
border-radius: 12px 12px 0 0;
|
|
}
|
|
.card-header h3 { font-size: 16px; color: #0891b2; }
|
|
.card-body { padding: 24px; }
|
|
|
|
.upload-zone {
|
|
border: 2px dashed #06b6d4;
|
|
border-radius: 12px;
|
|
padding: 40px;
|
|
text-align: center;
|
|
background: #f0f9ff;
|
|
cursor: pointer;
|
|
transition: all 0.3s;
|
|
}
|
|
.upload-zone:hover { background: #e0f2fe; }
|
|
.upload-zone i { font-size: 48px; color: #06b6d4; margin-bottom: 16px; }
|
|
.upload-zone h4 { color: #0891b2; margin-bottom: 8px; }
|
|
|
|
input[type="file"] { display: none; }
|
|
|
|
.format-box {
|
|
background: #f0f9ff;
|
|
border: 1px solid #e0f2fe;
|
|
border-radius: 8px;
|
|
padding: 16px;
|
|
margin-top: 20px;
|
|
}
|
|
.format-box h5 { color: #0891b2; margin-bottom: 12px; }
|
|
.format-box code {
|
|
display: block;
|
|
background: #fff;
|
|
padding: 12px;
|
|
border-radius: 6px;
|
|
font-size: 11px;
|
|
overflow-x: auto;
|
|
}
|
|
|
|
.message {
|
|
padding: 14px 20px;
|
|
border-radius: 10px;
|
|
margin-bottom: 20px;
|
|
}
|
|
.message-success { background: #dcfce7; color: #16a34a; }
|
|
.message-error { background: #fee2e2; color: #dc2626; }
|
|
|
|
.results { background: #f8fafc; border-radius: 8px; padding: 16px; margin-top: 16px; }
|
|
.result-row { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #e2e8f0; }
|
|
.result-row:last-child { border: none; }
|
|
|
|
.stats-row { display: flex; gap: 20px; margin-top: 20px; }
|
|
.stat-box { background: #f0f9ff; padding: 16px 24px; border-radius: 8px; text-align: center; flex: 1; }
|
|
.stat-value { font-size: 28px; font-weight: 700; color: #0891b2; }
|
|
.stat-label { font-size: 12px; color: #64748b; }
|
|
|
|
.options-box {
|
|
background: #fef3c7;
|
|
border: 1px solid #fde68a;
|
|
border-radius: 8px;
|
|
padding: 16px;
|
|
margin: 20px 0;
|
|
}
|
|
.options-box h5 { color: #d97706; margin-bottom: 12px; }
|
|
.radio-group { display: flex; gap: 20px; flex-wrap: wrap; }
|
|
.radio-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 10px 16px;
|
|
background: #fff;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
border: 2px solid transparent;
|
|
}
|
|
.radio-item:has(input:checked) { border-color: #d97706; background: #fffbeb; }
|
|
|
|
.preview-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
font-size: 12px;
|
|
margin-top: 16px;
|
|
}
|
|
.preview-table th, .preview-table td {
|
|
padding: 10px;
|
|
border: 1px solid #e2e8f0;
|
|
text-align: left;
|
|
}
|
|
.preview-table th { background: #f0f9ff; color: #0891b2; }
|
|
.preview-table tr.duplicate { background: #fef3c7; }
|
|
.preview-table tr.new { background: #dcfce7; }
|
|
|
|
.badge {
|
|
padding: 3px 8px;
|
|
border-radius: 4px;
|
|
font-size: 10px;
|
|
font-weight: 600;
|
|
}
|
|
.badge-new { background: #dcfce7; color: #16a34a; }
|
|
.badge-duplicate { background: #fef3c7; color: #d97706; }
|
|
.badge-changed { background: #fee2e2; color: #dc2626; }
|
|
.badge-date { background: #e0f2fe; color: #0891b2; }
|
|
|
|
.preview-summary {
|
|
display: flex;
|
|
gap: 16px;
|
|
margin-bottom: 16px;
|
|
}
|
|
.summary-item {
|
|
padding: 12px 20px;
|
|
border-radius: 8px;
|
|
text-align: center;
|
|
}
|
|
.summary-new { background: #dcfce7; }
|
|
.summary-duplicate { background: #fef3c7; }
|
|
|
|
.errors-box {
|
|
background: #fef2f2;
|
|
border: 1px solid #fecaca;
|
|
border-radius: 8px;
|
|
padding: 16px;
|
|
margin-top: 16px;
|
|
max-height: 200px;
|
|
overflow-y: auto;
|
|
}
|
|
.errors-box h5 { color: #dc2626; margin-bottom: 8px; }
|
|
.error-item { font-size: 11px; color: #dc2626; padding: 4px 0; border-bottom: 1px solid #fecaca; }
|
|
</style>
|
|
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="header">
|
|
<div>
|
|
<h1><i class="fas fa-file-import"></i> Import Office 365</h1>
|
|
<p>Importer des comptes depuis un fichier CSV/TXT</p>
|
|
</div>
|
|
<a href="office-management.php" class="btn btn-secondary"><i class="fas fa-arrow-left"></i> Retour</a>
|
|
</div>
|
|
|
|
<?php if ($message): ?>
|
|
<div class="message message-<?= $messageType ?>"><?= $message ?></div>
|
|
<?php endif; ?>
|
|
|
|
<?php if (!empty($importResults)): ?>
|
|
<div class="card">
|
|
<div class="card-header"><h3><i class="fas fa-chart-bar"></i> Résultats de l'import</h3></div>
|
|
<div class="card-body">
|
|
<div class="results">
|
|
<div class="result-row"><span>✅ Nouveaux comptes créés</span><strong style="color:#10b981"><?= $importResults['inserted'] ?></strong></div>
|
|
<div class="result-row"><span>🔄 Comptes mis à jour</span><strong style="color:#d97706"><?= $importResults['updated'] ?></strong></div>
|
|
<div class="result-row"><span>⏭️ Lignes ignorées</span><strong style="color:#64748b"><?= $importResults['skipped'] ?></strong></div>
|
|
</div>
|
|
<?php if (!empty($importResults['errors'])): ?>
|
|
<div class="errors-box">
|
|
<h5><i class="fas fa-exclamation-triangle"></i> Erreurs (<?= count($importResults['errors']) ?>)</h5>
|
|
<?php foreach (array_slice($importResults['errors'], 0, 50) as $e): ?>
|
|
<div class="error-item"><?= htmlspecialchars($e) ?></div>
|
|
<?php endforeach; ?>
|
|
<?php if (count($importResults['errors']) > 50): ?>
|
|
<div class="error-item">... et <?= count($importResults['errors']) - 50 ?> autres erreurs</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
<div style="margin-top: 16px; text-align: center;">
|
|
<a href="office-management.php" class="btn btn-primary"><i class="fas fa-list"></i> Voir les comptes</a>
|
|
<a href="office-import.php" class="btn btn-secondary"><i class="fas fa-redo"></i> Nouvel import</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($showPreview && !empty($previewData)): ?>
|
|
<div class="card">
|
|
<div class="card-header"><h3><i class="fas fa-eye"></i> Aperçu (<?= count($previewData) ?> lignes)</h3></div>
|
|
<div class="card-body">
|
|
<div class="preview-summary">
|
|
<div class="summary-item summary-new">
|
|
<div style="font-size: 24px; font-weight: 700; color: #16a34a;"><?= count($previewData) - $duplicateCount ?></div>
|
|
<div style="font-size: 12px; color: #16a34a;">Nouveaux</div>
|
|
</div>
|
|
<div class="summary-item summary-duplicate">
|
|
<div style="font-size: 24px; font-weight: 700; color: #d97706;"><?= $duplicateCount ?></div>
|
|
<div style="font-size: 12px; color: #d97706;">Doublons</div>
|
|
</div>
|
|
</div>
|
|
|
|
<form method="POST">
|
|
<input type="hidden" name="action" value="import">
|
|
|
|
<?php if ($duplicateCount > 0): ?>
|
|
<div class="options-box">
|
|
<h5><i class="fas fa-copy"></i> Que faire avec les <?= $duplicateCount ?> doublons ?</h5>
|
|
<div class="radio-group">
|
|
<label class="radio-item"><input type="radio" name="duplicate_action" value="update" checked><span>🔄 Mettre à jour</span></label>
|
|
<label class="radio-item"><input type="radio" name="duplicate_action" value="skip"><span>⏭️ Ignorer</span></label>
|
|
<label class="radio-item"><input type="radio" name="duplicate_action" value="replace"><span>🔁 Remplacer</span></label>
|
|
</div>
|
|
</div>
|
|
<?php else: ?>
|
|
<input type="hidden" name="duplicate_action" value="update">
|
|
<?php endif; ?>
|
|
|
|
<div style="max-height: 400px; overflow-y: auto;">
|
|
<table class="preview-table">
|
|
<thead>
|
|
<tr><th>#</th><th>Email</th><th>Source</th><th>Date brute</th><th>Date convertie</th><th>Status</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($previewData as $p): ?>
|
|
<tr class="<?= $p['exists'] ? 'duplicate' : 'new' ?>">
|
|
<td><?= $p['line'] ?></td>
|
|
<td style="font-size: 11px;"><?= htmlspecialchars($p['email']) ?></td>
|
|
<td><?= htmlspecialchars($p['source']) ?></td>
|
|
<td><code style="font-size: 10px;"><?= htmlspecialchars($p['date_raw']) ?></code></td>
|
|
<td><span class="badge badge-date"><?= $p['date_parsed'] ?></span></td>
|
|
<td>
|
|
<?php if ($p['exists']): ?>
|
|
<span class="badge badge-duplicate">Doublon #<?= $p['existing_id'] ?></span>
|
|
<?php if ($p['password_changed']): ?><span class="badge badge-changed">PWD</span><?php endif; ?>
|
|
<?php else: ?>
|
|
<span class="badge badge-new">Nouveau</span>
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div style="margin-top: 20px; text-align: center; display: flex; gap: 12px; justify-content: center;">
|
|
<a href="office-import.php" class="btn btn-secondary"><i class="fas fa-times"></i> Annuler</a>
|
|
<button type="submit" class="btn btn-success"><i class="fas fa-check"></i> Confirmer l'import</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<?php else: ?>
|
|
<div class="card">
|
|
<div class="card-header"><h3><i class="fas fa-upload"></i> Upload fichier</h3></div>
|
|
<div class="card-body">
|
|
<form method="POST" enctype="multipart/form-data" id="uploadForm">
|
|
<input type="hidden" name="action" value="preview">
|
|
|
|
<label for="fileInput">
|
|
<div class="upload-zone" id="dropZone">
|
|
<i class="fas fa-cloud-upload-alt"></i>
|
|
<h4>Glissez votre fichier ici</h4>
|
|
<p style="color:#64748b">ou cliquez pour sélectionner (CSV, TXT)</p>
|
|
<p id="fileName" style="margin-top:10px; color:#0891b2; font-weight:600;"></p>
|
|
</div>
|
|
</label>
|
|
<input type="file" name="excel_file" id="fileInput" accept=".csv,.txt">
|
|
|
|
<div class="format-box">
|
|
<h5><i class="fas fa-info-circle"></i> Format attendu</h5>
|
|
<code>Source;Account mail;Password;Type;ClientId;TenantId;SecretId;date;Comment</code>
|
|
<p style="margin-top:10px; font-size:12px; color:#64748b;">
|
|
<strong>Date:</strong> Format DD/MM/YYYY (ex: 13/12/2025) - sera converti automatiquement
|
|
</p>
|
|
</div>
|
|
|
|
<div style="margin-top:20px; display: flex; gap: 12px; justify-content: center;">
|
|
<button type="submit" class="btn btn-purple" id="previewBtn" disabled>
|
|
<i class="fas fa-eye"></i> Aperçu avant import
|
|
</button>
|
|
</div>
|
|
</form>
|
|
|
|
<div class="stats-row">
|
|
<div class="stat-box">
|
|
<div class="stat-value"><?= $stats['total'] ?></div>
|
|
<div class="stat-label">Comptes en base</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<script>
|
|
const fileInput = document.getElementById('fileInput');
|
|
const fileName = document.getElementById('fileName');
|
|
const previewBtn = document.getElementById('previewBtn');
|
|
const dropZone = document.getElementById('dropZone');
|
|
|
|
if (dropZone) {
|
|
dropZone.addEventListener('dragover', e => { e.preventDefault(); dropZone.style.background = '#cffafe'; });
|
|
dropZone.addEventListener('dragleave', () => { dropZone.style.background = '#f0f9ff'; });
|
|
dropZone.addEventListener('drop', e => {
|
|
e.preventDefault();
|
|
dropZone.style.background = '#f0f9ff';
|
|
if (e.dataTransfer.files.length) {
|
|
fileInput.files = e.dataTransfer.files;
|
|
updateFile();
|
|
}
|
|
});
|
|
}
|
|
|
|
if (fileInput) fileInput.addEventListener('change', updateFile);
|
|
|
|
function updateFile() {
|
|
if (fileInput.files.length) {
|
|
fileName.textContent = '📄 ' + fileInput.files[0].name;
|
|
if (previewBtn) previewBtn.disabled = false;
|
|
}
|
|
}
|
|
</script>
|
|
<?php include("includes/chatbot-widget.php"); ?>
|
|
|
|
</body>
|
|
</html>
|