Initial commit — Serveur Lucas SmartEye
API réception alertes chute (SmartEye/YOLO), analyse IA (Gemini 2.5 Flash), gestion alertes avec escalade (watchdog), notifications Firebase, dashboard web, documentation MkDocs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
482
admin.php
Executable file
482
admin.php
Executable file
@@ -0,0 +1,482 @@
|
||||
<?php
|
||||
// --- SMARTEYE ADMIN DASHBOARD v3.0 (Lucas Edition) ---
|
||||
session_start();
|
||||
$ADMIN_PASS = "smart123"; // ⚠️ Mot de passe Admin
|
||||
$DB_FILE = "database.json";
|
||||
|
||||
// --- AUTHENTIFICATION ---
|
||||
if (isset($_POST['login'])) {
|
||||
if ($_POST['pass'] === $ADMIN_PASS) { $_SESSION['admin'] = true; }
|
||||
else { $error = "Mot de passe incorrect"; }
|
||||
}
|
||||
if (isset($_GET['logout'])) { session_destroy(); header("Location: admin.php"); exit; }
|
||||
|
||||
if (!isset($_SESSION['admin'])) {
|
||||
// Page de Login
|
||||
echo '<!DOCTYPE html><html lang="fr"><head><title>Admin Login</title><script src="https://cdn.tailwindcss.com"></script></head>
|
||||
<body class="bg-slate-900 flex items-center justify-center h-screen">
|
||||
<div class="bg-slate-800 p-8 rounded-xl shadow-2xl border border-slate-700 w-96 text-center">
|
||||
<div class="w-12 h-12 bg-emerald-500 rounded-full mx-auto mb-4 animate-pulse"></div>
|
||||
<h1 class="text-2xl font-bold text-white mb-6 tracking-wider">SMARTEYE <span class="text-emerald-400">LUCAS</span></h1>
|
||||
<form method="post" class="space-y-4">
|
||||
<input type="password" name="pass" placeholder="Code d\'accès" class="w-full bg-slate-900 border border-slate-700 text-white px-4 py-3 rounded focus:outline-none focus:border-emerald-500 text-center text-lg">
|
||||
<button name="login" class="w-full bg-emerald-600 hover:bg-emerald-500 text-white font-bold py-3 rounded transition">Connexion</button>
|
||||
</form>
|
||||
'.(isset($error)?'<p class="text-red-500 mt-4 text-sm">'.$error.'</p>':'').'
|
||||
</div></body></html>';
|
||||
exit;
|
||||
}
|
||||
|
||||
// --- CHARGEMENT DES DONNÉES ---
|
||||
$data = file_exists($DB_FILE) ? json_decode(file_get_contents($DB_FILE), true) : ['clients' => []];
|
||||
// Sécurité : structure minimale si fichier vide ou corrompu
|
||||
if (!isset($data['clients'])) $data['clients'] = [];
|
||||
|
||||
// --- FONCTION TEST ALERTE (SIMULATION SMS) ---
|
||||
if (isset($_GET['test_alert'])) {
|
||||
$idTest = $_GET['test_alert'];
|
||||
if (isset($data['clients'][$idTest])) {
|
||||
$client = $data['clients'][$idTest];
|
||||
$contacts = $client['contacts'] ?? [];
|
||||
|
||||
$count = 0;
|
||||
$mobiles = [];
|
||||
|
||||
// ICI : Simulation de l'appel API SMS
|
||||
// Dans le futur api.php, on utilisera ces numéros
|
||||
foreach($contacts as $c) {
|
||||
if(!empty($c['phone'])) {
|
||||
$mobiles[] = $c['phone'];
|
||||
$count++;
|
||||
// LOG l'envoi pour preuve
|
||||
$log_msg = date("Y-m-d H:i:s") . " [TEST ADMIN] SMS vers {$c['phone']} pour {$client['name']}\n";
|
||||
file_put_contents("sms_history.log", $log_msg, FILE_APPEND);
|
||||
}
|
||||
}
|
||||
|
||||
$successMsg = "✅ Test d'alerte simulé envoyé à $count contacts !<br><span class='text-xs opacity-70'>(" . implode(', ', $mobiles) . ")</span>";
|
||||
}
|
||||
}
|
||||
|
||||
// --- SAUVEGARDE CLIENT (AJOUT / EDIT) ---
|
||||
if (isset($_POST['save_client'])) {
|
||||
// Nettoyage de l'ID pour éviter les caractères spéciaux
|
||||
$id = preg_replace('/[^a-z0-9_]/', '', strtolower($_POST['client_id']));
|
||||
|
||||
// Initialisation si nouveau
|
||||
if (!isset($data['clients'][$id])) {
|
||||
$data['clients'][$id]['created_at'] = date('Y-m-d H:i:s');
|
||||
$data['clients'][$id]['alerte'] = false;
|
||||
// Création dossier images
|
||||
$client_dir = "clients/" . $_POST['name'] . "/";
|
||||
if (!is_dir($client_dir)) { mkdir($client_dir, 0775, true); }
|
||||
}
|
||||
|
||||
// Token : saisi manuellement, ou auto-généré si vide
|
||||
$token_input = trim($_POST['token'] ?? '');
|
||||
if (!empty($token_input)) {
|
||||
$data['clients'][$id]['token'] = $token_input;
|
||||
} elseif (!isset($data['clients'][$id]['token'])) {
|
||||
$data['clients'][$id]['token'] = bin2hex(random_bytes(4));
|
||||
}
|
||||
|
||||
// Mot de passe LucasApp (bcrypt) — ignorer si valeur inchangée (bullets)
|
||||
$new_password = trim($_POST['client_password'] ?? '');
|
||||
if (!empty($new_password) && $new_password !== str_repeat("\xE2\x80\xA2", 8) && $new_password !== '••••••••') {
|
||||
$data['clients'][$id]['password_hash'] = password_hash($new_password, PASSWORD_BCRYPT);
|
||||
}
|
||||
|
||||
// Mise à jour des champs
|
||||
$data['clients'][$id]['name'] = $_POST['name'];
|
||||
$data['clients'][$id]['address'] = $_POST['address'];
|
||||
$data['clients'][$id]['city'] = $_POST['city'];
|
||||
$data['clients'][$id]['phone_mobile'] = $_POST['phone_mobile'];
|
||||
$data['clients'][$id]['phone_fixed'] = $_POST['phone_fixed'];
|
||||
$data['clients'][$id]['cp'] = $_POST['cp'] ?? '';
|
||||
$data['clients'][$id]['date_naissance'] = $_POST['date_naissance'] ?? '';
|
||||
$data['clients'][$id]['sex'] = $_POST['sex'];
|
||||
|
||||
// Champs bénéficiaire
|
||||
$data['clients'][$id]['senior_name'] = $_POST['senior_name'] ?? '';
|
||||
$data['clients'][$id]['senior_nickname'] = $_POST['senior_nickname'] ?? '';
|
||||
$data['clients'][$id]['senior_photo'] = $_POST['senior_photo'] ?? '';
|
||||
if (!isset($data['clients'][$id]['fcm_tokens'])) {
|
||||
$data['clients'][$id]['fcm_tokens'] = [];
|
||||
}
|
||||
|
||||
// Champs compte / technique
|
||||
$data['clients'][$id]['jetson_id'] = $_POST['jetson_id'] ?? '';
|
||||
$data['clients'][$id]['jetson_ip'] = $_POST['jetson_ip'] ?? '';
|
||||
$data['clients'][$id]['jetson_modele'] = $_POST['jetson_modele'] ?? '';
|
||||
$data['clients'][$id]['jetson_ram'] = $_POST['jetson_ram'] ?? '';
|
||||
$data['clients'][$id]['jetson_ssd'] = $_POST['jetson_ssd'] ?? '';
|
||||
|
||||
// Caméras (5 max)
|
||||
$cameras = [];
|
||||
if (isset($_POST['cam_marque'])) {
|
||||
for ($i = 0; $i < count($_POST['cam_marque']); $i++) {
|
||||
if (!empty($_POST['cam_marque'][$i]) || !empty($_POST['cam_ip'][$i])) {
|
||||
$cameras[] = [
|
||||
'marque' => $_POST['cam_marque'][$i] ?? '',
|
||||
'modele' => $_POST['cam_modele'][$i] ?? '',
|
||||
'serie' => $_POST['cam_serie'][$i] ?? '',
|
||||
'ip' => $_POST['cam_ip'][$i] ?? '',
|
||||
'port_rtsp' => $_POST['cam_port_rtsp'][$i] ?? '',
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
$data['clients'][$id]['cameras'] = $cameras;
|
||||
|
||||
// Gestion des contacts multiples
|
||||
$contacts = [];
|
||||
if (isset($_POST['contact_name'])) {
|
||||
for ($i = 0; $i < count($_POST['contact_name']); $i++) {
|
||||
if (!empty($_POST['contact_name'][$i])) {
|
||||
$contacts[] = [
|
||||
'name' => $_POST['contact_name'][$i],
|
||||
'role' => $_POST['contact_role'][$i],
|
||||
'phone' => $_POST['contact_phone'][$i],
|
||||
'email' => $_POST['contact_email'][$i],
|
||||
'os' => $_POST['contact_os'][$i] ?? ''
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
$data['clients'][$id]['contacts'] = $contacts;
|
||||
|
||||
// Écriture DB
|
||||
file_put_contents($DB_FILE, json_encode($data, JSON_PRETTY_PRINT));
|
||||
$tab = $_POST['current_tab'] ?? 'identite';
|
||||
header("Location: admin.php?edit=$id&success=1&tab=$tab"); exit;
|
||||
}
|
||||
|
||||
// --- ARRÊT ALERTE ---
|
||||
if (isset($_GET['stop_alert'])) {
|
||||
$stopId = $_GET['stop_alert'];
|
||||
if (isset($data['clients'][$stopId])) {
|
||||
$data['clients'][$stopId]['alerte'] = false;
|
||||
unset($data['clients'][$stopId]['handled_by']);
|
||||
unset($data['clients'][$stopId]['handled_at']);
|
||||
unset($data['clients'][$stopId]['message']);
|
||||
unset($data['clients'][$stopId]['last_update']);
|
||||
file_put_contents($DB_FILE, json_encode($data, JSON_PRETTY_PRINT));
|
||||
// Reload data after save
|
||||
$data = json_decode(file_get_contents($DB_FILE), true);
|
||||
}
|
||||
header("Location: admin.php?alert_stopped=1"); exit;
|
||||
}
|
||||
|
||||
// --- SUPPRESSION ---
|
||||
if (isset($_GET['del'])) {
|
||||
unset($data['clients'][$_GET['del']]);
|
||||
file_put_contents($DB_FILE, json_encode($data, JSON_PRETTY_PRINT));
|
||||
header("Location: admin.php"); exit;
|
||||
}
|
||||
|
||||
// --- PRÉPARATION VUE ---
|
||||
$editClient = null; $editId = "";
|
||||
if (isset($_GET['edit'])) {
|
||||
$editId = $_GET['edit'];
|
||||
$editClient = $data['clients'][$editId] ?? null;
|
||||
} elseif (isset($_GET['new'])) {
|
||||
// Mode nouveau client
|
||||
$editClient = null;
|
||||
}
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>SmartEye Admin</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script>
|
||||
function addContact() {
|
||||
const tpl = document.getElementById('contact-template').innerHTML;
|
||||
document.getElementById('contacts-container').insertAdjacentHTML('beforeend', tpl);
|
||||
}
|
||||
function removeContact(btn) { btn.closest('.contact-row').remove(); }
|
||||
function switchTab(name) {
|
||||
document.querySelectorAll('.tab-panel').forEach(p => p.classList.add('hidden'));
|
||||
document.querySelectorAll('.tab-btn').forEach(b => {
|
||||
b.classList.remove('border-violet-500','border-emerald-500','border-blue-500','text-violet-400','text-emerald-400','text-blue-400','bg-slate-800/50');
|
||||
b.classList.add('text-slate-500','border-transparent');
|
||||
});
|
||||
document.getElementById('panel-'+name).classList.remove('hidden');
|
||||
const btn = document.getElementById('tab-'+name);
|
||||
btn.classList.remove('text-slate-500','border-transparent');
|
||||
const colors = {identite:['violet'],compte:['emerald'],contacts:['blue']};
|
||||
const c = (colors[name]||['violet'])[0];
|
||||
btn.classList.add('border-'+c+'-500','text-'+c+'-400','bg-slate-800/50');
|
||||
const h = document.getElementById('current_tab');
|
||||
if (h) h.value = name;
|
||||
}
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const tab = params.get('tab');
|
||||
if (tab && document.getElementById('panel-'+tab)) switchTab(tab);
|
||||
});
|
||||
</script>
|
||||
<style>body { background-color: #0f172a; color: #e2e8f0; }</style>
|
||||
</head>
|
||||
<body class="min-h-screen flex flex-col">
|
||||
|
||||
<nav class="bg-slate-900 border-b border-slate-800 px-6 py-4 flex justify-between items-center sticky top-0 z-50 shadow-md">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-3 h-3 rounded-full bg-emerald-500"></div>
|
||||
<span class="font-bold text-xl tracking-wider text-white">SMARTEYE <span class="text-slate-500 text-sm font-normal">MANAGER</span></span>
|
||||
</div>
|
||||
<?php if($editClient): ?>
|
||||
<span class="text-slate-400 font-mono text-sm bg-slate-800 px-4 py-2 rounded border border-slate-700"><?php echo $editId; ?></span>
|
||||
<?php endif; ?>
|
||||
<div class="flex gap-4">
|
||||
<?php if($editClient || isset($_GET['new'])): ?>
|
||||
<a href="admin.php" class="bg-slate-700 hover:bg-slate-600 text-white px-4 py-2 rounded text-sm flex items-center gap-2 transition"><span>↩</span> Retour</a>
|
||||
<?php endif; ?>
|
||||
<a href="?logout" class="text-red-400 hover:text-white text-sm border border-red-900/50 px-3 py-2 rounded transition">Déconnexion</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="flex-1 p-8 max-w-7xl mx-auto w-full">
|
||||
|
||||
<?php if(isset($_GET['success'])): ?>
|
||||
<div class="bg-emerald-900/50 border border-emerald-500/50 text-emerald-200 px-4 py-3 rounded mb-6 text-center animate-bounce">✅ Modifications enregistrées avec succès !</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if(isset($successMsg)): ?>
|
||||
<div class="bg-blue-900/50 border border-blue-500/50 text-blue-200 px-4 py-3 rounded mb-6 text-center"><?php echo $successMsg; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if(isset($_GET['alert_stopped'])): ?>
|
||||
<div class="bg-emerald-900/50 border border-emerald-500/50 text-emerald-200 px-4 py-3 rounded mb-6 text-center">✅ Alerte arrêtée avec succès — Le client est revenu en surveillance active.</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if($editClient || isset($_GET['new'])): ?>
|
||||
<form method="post" class="max-w-4xl mx-auto">
|
||||
|
||||
<div class="bg-slate-800 rounded-xl border border-slate-700 shadow-lg overflow-hidden">
|
||||
|
||||
<?php if($editClient): ?>
|
||||
<input type="hidden" name="client_id" value="<?php echo $editId; ?>">
|
||||
<?php endif; ?>
|
||||
<input type="hidden" name="current_tab" id="current_tab" value="<?php echo htmlspecialchars($_GET['tab'] ?? 'identite'); ?>">
|
||||
|
||||
<!-- ONGLETS -->
|
||||
<div class="flex border-b border-slate-700 bg-slate-900">
|
||||
<button type="button" onclick="switchTab('identite')" id="tab-identite" class="tab-btn flex-1 px-4 py-3 text-sm font-bold text-violet-400 border-b-2 border-violet-500 bg-slate-800/50 transition">👤 Identité</button>
|
||||
<button type="button" onclick="switchTab('compte')" id="tab-compte" class="tab-btn flex-1 px-4 py-3 text-sm font-bold text-slate-500 border-b-2 border-transparent hover:text-emerald-400 transition">📱 Infos Compte</button>
|
||||
<button type="button" onclick="switchTab('contacts')" id="tab-contacts" class="tab-btn flex-1 px-4 py-3 text-sm font-bold text-slate-500 border-b-2 border-transparent hover:text-blue-400 transition">📞 Réseau de Contact</button>
|
||||
</div>
|
||||
|
||||
<div class="p-6">
|
||||
|
||||
<!-- TAB IDENTITÉ -->
|
||||
<div id="panel-identite" class="tab-panel space-y-4">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="text-xs text-violet-300 uppercase font-bold">Nom complet</label>
|
||||
<input name="senior_name" value="<?php echo $editClient['senior_name'] ?? ''; ?>" placeholder="Marie Dupont" class="w-full bg-slate-900 border border-violet-500/50 rounded px-3 py-2 text-white text-lg font-bold">
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-xs text-violet-300 uppercase font-bold">Surnom</label>
|
||||
<input name="senior_nickname" value="<?php echo $editClient['senior_nickname'] ?? ''; ?>" placeholder="Mamie" class="w-full bg-slate-900 border border-violet-500/50 rounded px-3 py-2 text-white">
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 gap-3">
|
||||
<div><label class="text-xs text-slate-500 uppercase font-bold">Date de naissance</label><input name="date_naissance" type="date" value="<?php echo $editClient['date_naissance'] ?? ''; ?>" class="w-full bg-slate-900 border border-slate-600 rounded px-3 py-2 text-white"></div>
|
||||
<div><label class="text-xs text-slate-500 uppercase font-bold">Sexe</label><select name="sex" class="w-full bg-slate-900 border border-slate-600 rounded px-3 py-2 text-white"><option value="F" <?php echo ($editClient['sex']??'')=='F'?'selected':''; ?>>Femme</option><option value="H" <?php echo ($editClient['sex']??'')=='H'?'selected':''; ?>>Homme</option></select></div>
|
||||
<div><label class="text-xs text-slate-500 uppercase font-bold">Photo</label><input name="senior_photo" value="<?php echo $editClient['senior_photo'] ?? ''; ?>" placeholder="photos/mamie.jpg" class="w-full bg-slate-900 border border-slate-600 rounded px-3 py-2 text-white text-sm"></div>
|
||||
</div>
|
||||
<div><label class="text-xs text-slate-500 uppercase font-bold">Adresse</label><input name="address" value="<?php echo $editClient['address'] ?? ''; ?>" class="w-full bg-slate-900 border border-slate-600 rounded px-3 py-2 text-white"></div>
|
||||
<div class="grid grid-cols-3 gap-3">
|
||||
<div><label class="text-xs text-slate-500 uppercase font-bold">Code postal</label><input name="cp" value="<?php echo $editClient['cp'] ?? ''; ?>" placeholder="75001" class="w-full bg-slate-900 border border-slate-600 rounded px-3 py-2 text-white font-mono"></div>
|
||||
<div class="col-span-2"><label class="text-xs text-slate-500 uppercase font-bold">Ville</label><input name="city" value="<?php echo $editClient['city'] ?? ''; ?>" placeholder="Paris" class="w-full bg-slate-900 border border-slate-600 rounded px-3 py-2 text-white"></div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div><label class="text-xs text-slate-500 uppercase font-bold">Mobile</label><input name="phone_mobile" value="<?php echo $editClient['phone_mobile'] ?? ''; ?>" required class="w-full bg-slate-900 border border-slate-600 rounded px-3 py-2 text-white"></div>
|
||||
<div><label class="text-xs text-slate-500 uppercase font-bold">Fixe</label><input name="phone_fixed" value="<?php echo $editClient['phone_fixed'] ?? ''; ?>" class="w-full bg-slate-900 border border-slate-600 rounded px-3 py-2 text-white"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TAB INFOS COMPTE -->
|
||||
<div id="panel-compte" class="tab-panel space-y-4 hidden">
|
||||
<?php if(!$editClient): ?>
|
||||
<div>
|
||||
<label class="text-xs text-emerald-400 uppercase font-bold">ID Système (clé unique)</label>
|
||||
<input name="client_id" required placeholder="ex: pizzulo" class="w-full bg-slate-900 border border-emerald-500/50 rounded px-3 py-2 text-white font-mono text-sm">
|
||||
<p class="text-xs text-slate-600 mt-1">Identifiant technique interne, non modifiable</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="text-xs text-emerald-400 uppercase font-bold">Identifiant App *</label>
|
||||
<input name="name" value="<?php echo $editClient['name'] ?? ''; ?>" required class="w-full bg-slate-900 border border-emerald-500/50 rounded px-3 py-2 text-white font-bold">
|
||||
<p class="text-xs text-slate-600 mt-1">Saisi par le client dans LucasApp</p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-xs text-emerald-400 uppercase font-bold">Mot de passe App *</label>
|
||||
<input name="client_password" type="password" value="<?php echo !empty($editClient['password_hash']) ? '••••••••' : ''; ?>" <?php echo empty($editClient['password_hash']) ? 'required' : ''; ?> placeholder="<?php echo empty($editClient['password_hash']) ? 'non renseigné' : ''; ?>" class="w-full bg-slate-900 border <?php echo empty($editClient['password_hash']) ? 'border-red-500/50' : 'border-emerald-500/50'; ?> rounded px-3 py-2 text-white">
|
||||
<p class="text-xs <?php echo !empty($editClient['password_hash']) ? 'text-slate-500' : 'text-red-400'; ?> mt-1"><?php echo !empty($editClient['password_hash']) ? 'Laisser tel quel pour ne pas changer' : 'Obligatoire — l\'app ne peut pas se connecter'; ?></p>
|
||||
</div>
|
||||
</div>
|
||||
<?php if(!empty($editClient['fcm_tokens'])): ?>
|
||||
<div class="bg-emerald-900/20 border border-emerald-500/30 rounded px-3 py-2 text-xs text-emerald-300"><?php echo count($editClient['fcm_tokens']); ?> appareil(s) connecté(s)</div>
|
||||
<?php endif; ?>
|
||||
<div>
|
||||
<label class="text-xs text-amber-400 uppercase font-bold">Token SmartEye *</label>
|
||||
<div class="flex gap-2">
|
||||
<input name="token" value="<?php echo $editClient['token'] ?? ''; ?>" <?php echo $editClient ? '' : 'placeholder="Auto-généré si vide"'; ?> class="flex-1 bg-slate-900 border border-amber-500/50 rounded px-3 py-2 text-amber-200 font-mono text-sm">
|
||||
<?php if($editClient): ?>
|
||||
<button type="button" onclick="if(confirm('Régénérer le token ?')){document.querySelector('input[name=token]').value='<?php echo bin2hex(random_bytes(4)); ?>';}" class="bg-amber-600/20 text-amber-400 hover:bg-amber-600 hover:text-white px-3 rounded text-xs border border-amber-900 transition">Régénérer</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="text-xs text-slate-500 uppercase font-bold">Date de création</label>
|
||||
<input value="<?php echo $editClient['created_at'] ?? date('Y-m-d H:i:s'); ?>" disabled class="w-full bg-slate-950 border border-slate-700 rounded px-3 py-2 text-slate-400 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-xs text-emerald-400 uppercase font-bold">Identifiant Jetson</label>
|
||||
<input name="jetson_id" value="<?php echo $editClient['jetson_id'] ?? ''; ?>" placeholder="ex: jetson-nano-01" class="w-full bg-slate-900 border border-emerald-500/50 rounded px-3 py-2 text-white font-mono text-sm">
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-4 gap-3">
|
||||
<div><label class="text-xs text-slate-500 uppercase font-bold">IP Jetson</label><input name="jetson_ip" value="<?php echo $editClient['jetson_ip'] ?? ''; ?>" placeholder="192.168.1.x" class="w-full bg-slate-900 border border-slate-600 rounded px-2 py-1.5 text-white text-sm font-mono"></div>
|
||||
<div><label class="text-xs text-slate-500 uppercase font-bold">Modèle</label><input name="jetson_modele" value="<?php echo $editClient['jetson_modele'] ?? ''; ?>" placeholder="Nano / Orin" class="w-full bg-slate-900 border border-slate-600 rounded px-2 py-1.5 text-white text-sm"></div>
|
||||
<div><label class="text-xs text-slate-500 uppercase font-bold">RAM (Go)</label><input name="jetson_ram" value="<?php echo $editClient['jetson_ram'] ?? ''; ?>" placeholder="4" type="number" class="w-full bg-slate-900 border border-slate-600 rounded px-2 py-1.5 text-white text-sm font-mono"></div>
|
||||
<div><label class="text-xs text-slate-500 uppercase font-bold">SSD (Go)</label><input name="jetson_ssd" value="<?php echo $editClient['jetson_ssd'] ?? ''; ?>" placeholder="128" type="number" class="w-full bg-slate-900 border border-slate-600 rounded px-2 py-1.5 text-white text-sm font-mono"></div>
|
||||
</div>
|
||||
|
||||
<!-- CAMÉRAS -->
|
||||
<div class="mt-2">
|
||||
<label class="text-xs text-amber-400 uppercase font-bold tracking-wider">Caméras</label>
|
||||
</div>
|
||||
<?php for ($cam = 1; $cam <= 5; $cam++): $camData = $editClient['cameras'][$cam - 1] ?? []; ?>
|
||||
<div class="bg-slate-900/50 border border-slate-700 rounded p-3 <?php echo empty($camData['marque']) && $cam > 1 ? 'opacity-50' : ''; ?>">
|
||||
<div class="text-xs text-slate-500 font-bold mb-2">CAM <?php echo $cam; ?></div>
|
||||
<div class="grid grid-cols-5 gap-2">
|
||||
<div><input name="cam_marque[]" value="<?php echo $camData['marque'] ?? ''; ?>" placeholder="Marque" class="w-full bg-slate-900 border border-slate-600 rounded px-2 py-1.5 text-white text-sm"></div>
|
||||
<div><input name="cam_modele[]" value="<?php echo $camData['modele'] ?? ''; ?>" placeholder="Modèle" class="w-full bg-slate-900 border border-slate-600 rounded px-2 py-1.5 text-white text-sm"></div>
|
||||
<div><input name="cam_serie[]" value="<?php echo $camData['serie'] ?? ''; ?>" placeholder="N° série" class="w-full bg-slate-900 border border-slate-600 rounded px-2 py-1.5 text-white text-sm font-mono"></div>
|
||||
<div><input name="cam_ip[]" value="<?php echo $camData['ip'] ?? ''; ?>" placeholder="192.168.1.x" class="w-full bg-slate-900 border border-slate-600 rounded px-2 py-1.5 text-white text-sm font-mono"></div>
|
||||
<div><input name="cam_port_rtsp[]" value="<?php echo $camData['port_rtsp'] ?? ''; ?>" placeholder="Port tunnel" type="number" class="w-full bg-slate-900 border border-amber-500/50 rounded px-2 py-1.5 text-amber-200 text-sm font-mono"></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endfor; ?>
|
||||
</div>
|
||||
|
||||
<!-- TAB RÉSEAU DE CONTACT -->
|
||||
<div id="panel-contacts" class="tab-panel space-y-4 hidden">
|
||||
<div class="flex justify-between items-center">
|
||||
<p class="text-xs text-slate-500">Famille, voisins, aidants — prévenus en cas d'alerte</p>
|
||||
<div class="flex gap-2">
|
||||
<?php if($editClient): ?>
|
||||
<a href="?test_alert=<?php echo $editId; ?>" onclick="return confirm('Envoyer un SMS de test simulé ?')" class="bg-orange-600 hover:bg-orange-500 text-white px-3 py-1 rounded text-xs font-bold transition">Simulation Alerte</a>
|
||||
<?php endif; ?>
|
||||
<button type="button" onclick="addContact()" class="text-xs bg-blue-600 hover:bg-blue-500 px-3 py-1 rounded text-white transition">+ Ajouter</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="contacts-container" class="space-y-3 max-h-[400px] overflow-y-auto">
|
||||
<?php if(!empty($editClient['contacts'])): foreach($editClient['contacts'] as $c): ?>
|
||||
<div class="contact-row grid grid-cols-12 gap-2 items-center bg-slate-900/50 p-3 rounded border border-slate-700 group hover:border-slate-500 transition">
|
||||
<div class="col-span-2"><input name="contact_name[]" value="<?php echo $c['name']; ?>" placeholder="Nom" class="w-full bg-transparent border-b border-slate-600 text-sm px-2 py-1 focus:border-blue-500 outline-none text-white"></div>
|
||||
<div class="col-span-2"><input name="contact_role[]" value="<?php echo $c['role']; ?>" placeholder="Rôle" class="w-full bg-transparent border-b border-slate-600 text-sm px-2 py-1 focus:border-blue-500 outline-none text-white"></div>
|
||||
<div class="col-span-3"><input name="contact_phone[]" value="<?php echo $c['phone']; ?>" placeholder="Tél" class="w-full bg-transparent border-b border-slate-600 text-sm px-2 py-1 focus:border-blue-500 outline-none text-white"></div>
|
||||
<div class="col-span-2"><input name="contact_email[]" value="<?php echo $c['email']; ?>" placeholder="Email" class="w-full bg-transparent border-b border-slate-600 text-sm px-2 py-1 focus:border-blue-500 outline-none text-white"></div>
|
||||
<div class="col-span-2"><select name="contact_os[]" class="w-full bg-transparent border-b border-slate-600 text-sm px-1 py-1 outline-none text-white"><option value="" <?php echo empty($c['os'])?'selected':''; ?>>OS</option><option value="Android" <?php echo ($c['os']??'')==='Android'?'selected':''; ?>>Android</option><option value="iOS" <?php echo ($c['os']??'')==='iOS'?'selected':''; ?>>iOS</option></select></div>
|
||||
<div class="col-span-1 text-right"><button type="button" onclick="removeContact(this)" class="text-red-500 hover:text-red-300 p-1 font-bold">×</button></div>
|
||||
</div>
|
||||
<?php endforeach; endif; ?>
|
||||
</div>
|
||||
|
||||
<script type="text/template" id="contact-template">
|
||||
<div class="contact-row grid grid-cols-12 gap-2 items-center bg-slate-900/50 p-3 rounded border border-slate-700">
|
||||
<div class="col-span-2"><input name="contact_name[]" placeholder="Nom" class="w-full bg-transparent border-b border-slate-600 text-sm px-2 py-1 outline-none text-white"></div>
|
||||
<div class="col-span-2"><input name="contact_role[]" placeholder="Rôle" class="w-full bg-transparent border-b border-slate-600 text-sm px-2 py-1 outline-none text-white"></div>
|
||||
<div class="col-span-3"><input name="contact_phone[]" placeholder="Tél" class="w-full bg-transparent border-b border-slate-600 text-sm px-2 py-1 outline-none text-white"></div>
|
||||
<div class="col-span-2"><input name="contact_email[]" placeholder="Email" class="w-full bg-transparent border-b border-slate-600 text-sm px-2 py-1 outline-none text-white"></div>
|
||||
<div class="col-span-2"><select name="contact_os[]" class="w-full bg-transparent border-b border-slate-600 text-sm px-1 py-1 outline-none text-white"><option value="">OS</option><option value="Android">Android</option><option value="iOS">iOS</option></select></div>
|
||||
<div class="col-span-1 text-right"><button type="button" onclick="removeContact(this)" class="text-red-500 hover:text-red-300 p-1 font-bold">×</button></div>
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<!-- BOUTON SAVE (toujours visible) -->
|
||||
<div class="mt-6 pt-4 border-t border-slate-700 flex justify-between items-center">
|
||||
<a href="admin.php" class="text-slate-400 hover:text-white px-4 py-2 rounded border border-slate-600 transition">Annuler</a>
|
||||
<button name="save_client" class="bg-emerald-600 hover:bg-emerald-500 text-white font-bold py-3 px-8 rounded shadow-lg transition">Enregistrer</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<?php else: ?>
|
||||
<div class="bg-slate-800 rounded-xl border border-slate-700 shadow-lg overflow-hidden">
|
||||
<div class="p-6 border-b border-slate-700 flex justify-between items-center bg-slate-800/80 backdrop-blur">
|
||||
<h2 class="text-slate-200 font-bold text-lg flex items-center gap-2"><span class="bg-emerald-500 w-2 h-6 rounded-full"></span> Parc Clients Surveillés</h2>
|
||||
<a href="?new=1" class="bg-emerald-600 hover:bg-emerald-500 text-white px-6 py-2 rounded font-bold transition shadow-lg flex items-center gap-2"><span>+</span> Nouveau Dossier</a>
|
||||
</div>
|
||||
|
||||
<?php if(empty($data['clients'])): ?>
|
||||
<div class="p-12 text-center text-slate-500">
|
||||
<p class="text-4xl mb-4">📭</p>
|
||||
<p>Aucun client enregistré.</p>
|
||||
<p class="text-sm">Cliquez sur "Nouveau Dossier" pour commencer.</p>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<table class="w-full text-left text-sm">
|
||||
<thead class="bg-slate-900 text-slate-400 text-xs uppercase"><tr><th class="p-4">Bénéficiaire</th><th class="p-4">Localisation</th><th class="p-4">Contacts</th><th class="p-4">Statut</th><th class="p-4 text-right">Actions</th></tr></thead>
|
||||
<tbody class="divide-y divide-slate-700">
|
||||
<?php foreach ($data['clients'] as $id => $client): ?>
|
||||
<tr class="hover:bg-slate-700/30 transition group">
|
||||
<td class="p-4">
|
||||
<div class="font-bold text-white text-base"><?php echo $client['name']; ?></div>
|
||||
<div class="text-xs text-slate-500 mt-1">ID: <?php echo $id; ?></div>
|
||||
</td>
|
||||
<td class="p-4 text-slate-300">
|
||||
<?php echo $client['city'] ?? '-'; ?><br>
|
||||
<span class="text-xs text-slate-500 truncate max-w-[150px] block"><?php echo $client['address'] ?? ''; ?></span>
|
||||
</td>
|
||||
<td class="p-4">
|
||||
<div class="flex -space-x-2">
|
||||
<?php if(!empty($client['contacts'])): foreach(array_slice($client['contacts'],0,3) as $c): ?>
|
||||
<div class="w-8 h-8 rounded-full bg-blue-900 border-2 border-slate-800 flex items-center justify-center text-xs font-bold text-blue-200" title="<?php echo $c['name']; ?>"><?php echo strtoupper(substr($c['name'],0,1)); ?></div>
|
||||
<?php endforeach; endif; ?>
|
||||
<div class="w-8 h-8 rounded-full bg-slate-700 border-2 border-slate-800 flex items-center justify-center text-xs text-slate-400"><?php echo count($client['contacts'] ?? []); ?></div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="p-4">
|
||||
<?php if($client['alerte'] ?? false): ?>
|
||||
<span class="bg-red-900 text-red-200 px-2 py-1 rounded text-xs font-bold animate-pulse">ALERTE EN COURS</span>
|
||||
<?php else: ?>
|
||||
<span class="bg-emerald-900/30 text-emerald-400 px-2 py-1 rounded text-xs">Surveillance Active</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="p-4 text-right">
|
||||
<div class="flex justify-end gap-2 opacity-80 group-hover:opacity-100 transition">
|
||||
<?php if($client['alerte'] ?? false): ?>
|
||||
<a href="?stop_alert=<?php echo $id; ?>" onclick="return confirm('Confirmer l\'arrêt de l\'alerte pour <?php echo addslashes($client['name']); ?> ?\nLe statut repassera en Surveillance Active.')" class="bg-emerald-600/20 text-emerald-400 hover:bg-emerald-600 hover:text-white px-2 py-1 rounded text-xs border border-emerald-900 transition animate-pulse" title="Arrêter l'alerte (traitée)">✅</a>
|
||||
<?php endif; ?>
|
||||
<a href="sms_dashboard.php" class="bg-purple-600/20 text-purple-400 hover:bg-purple-600 hover:text-white px-2 py-1 rounded text-xs border border-purple-900 transition" title="Historique SMS">📱</a>
|
||||
<a href="?test_alert=<?php echo $id; ?>" onclick="return confirm('Lancer une simulation ?')" class="bg-orange-600/20 text-orange-400 hover:bg-orange-600 hover:text-white px-2 py-1 rounded text-xs border border-orange-900 transition" title="Test Alerte">🔔</a>
|
||||
<a href="?edit=<?php echo $id; ?>" class="bg-blue-600/20 text-blue-400 hover:bg-blue-600 hover:text-white px-3 py-1 rounded text-xs border border-blue-900 transition">Éditer</a>
|
||||
<a href="index.php?id=<?php echo $id; ?>" target="_blank" class="bg-slate-700 text-slate-300 hover:bg-slate-600 hover:text-white px-3 py-1 rounded text-xs border border-slate-600 transition">Voir</a>
|
||||
<a href="?del=<?php echo $id; ?>" onclick="return confirm('Supprimer ce dossier définitivement ?')" class="text-red-500 hover:text-red-400 px-2 py-1">×</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user