import JSZip from 'jszip';

export interface OVHFile {
  path: string;
  name: string;
  description: string;
  category: 'api' | 'db' | 'frontend' | 'pwa' | 'doc';
  content: string;
}

export const OVH_FILES: OVHFile[] = [
  {
    path: 'schema.sql',
    name: 'schema.sql',
    description: 'Structure complète des tables MySQL et données initiales (à importer dans phpMyAdmin OVH)',
    category: 'db',
    content: `-- Base de données de coordination des soins (Hébergement OVH Starter)
-- Encodage recommandé : utf8mb4_unicode_ci

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- 1. Table Utilisateurs (fratrie de 7 enfants + admin + compte kiosk)
CREATE TABLE IF NOT EXISTS \`utilisateurs\` (
  \`id\` INT AUTO_INCREMENT PRIMARY KEY,
  \`nom\` VARCHAR(100) NOT NULL,
  \`role\` ENUM('admin', 'membre', 'kiosk') NOT NULL DEFAULT 'membre',
  \`code_pin\` VARCHAR(10) NULL,
  \`actif\` TINYINT(1) NOT NULL DEFAULT 1,
  \`created_at\` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 2. Table Tâches récurrentes
CREATE TABLE IF NOT EXISTS \`taches\` (
  \`id\` INT AUTO_INCREMENT PRIMARY KEY,
  \`nom\` VARCHAR(255) NOT NULL,
  \`frequence\` ENUM('quotidien', 'hebdomadaire', 'mensuel') NOT NULL DEFAULT 'quotidien',
  \`jours_semaine\` VARCHAR(50) NOT NULL DEFAULT '1,2,3,4,5,6,7' COMMENT 'Jours actifs: 1=Lundi .. 7=Dimanche',
  \`categorie\` ENUM('soins', 'logistique', 'maison', 'bienveillance') NOT NULL DEFAULT 'maison',
  \`afficher_alerte\` TINYINT(1) NOT NULL DEFAULT 1,
  \`afficher_carrousel\` TINYINT(1) NOT NULL DEFAULT 0,
  \`actif\` TINYINT(1) NOT NULL DEFAULT 1
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 3. Table Instances de tâches quotidiennes
CREATE TABLE IF NOT EXISTS \`taches_instances\` (
  \`id\` INT AUTO_INCREMENT PRIMARY KEY,
  \`tache_id\` INT NOT NULL,
  \`date\` DATE NOT NULL,
  \`fait\` TINYINT(1) NOT NULL DEFAULT 0,
  \`fait_par\` VARCHAR(100) NULL,
  \`fait_a\` TIME NULL,
  \`created_at\` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  INDEX \`idx_date\` (\`date\`),
  FOREIGN KEY (\`tache_id\`) REFERENCES \`taches\`(\`id\`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 4. Table Médicaments définis
CREATE TABLE IF NOT EXISTS \`medicaments\` (
  \`id\` INT AUTO_INCREMENT PRIMARY KEY,
  \`patient\` ENUM('papa', 'maman') NOT NULL,
  \`nom\` VARCHAR(255) NOT NULL,
  \`dosage\` VARCHAR(100) NOT NULL,
  \`moment\` VARCHAR(50) NOT NULL DEFAULT 'midi',
  \`heure_prevue\` TIME NOT NULL,
  \`actif\` TINYINT(1) NOT NULL DEFAULT 1
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 5. Table Instances de médicaments quotidiennes
CREATE TABLE IF NOT EXISTS \`medicaments_instances\` (
  \`id\` INT AUTO_INCREMENT PRIMARY KEY,
  \`medicament_id\` INT NOT NULL,
  \`patient\` ENUM('papa', 'maman') NOT NULL,
  \`nom\` VARCHAR(255) NOT NULL,
  \`dosage\` VARCHAR(100) NOT NULL,
  \`moment\` VARCHAR(50) NOT NULL,
  \`heure_prevue\` TIME NOT NULL,
  \`date\` DATE NOT NULL,
  \`fait\` TINYINT(1) NOT NULL DEFAULT 0,
  \`fait_par\` VARCHAR(100) NULL,
  \`fait_a\` TIME NULL,
  \`created_at\` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  INDEX \`idx_med_date\` (\`date\`),
  FOREIGN KEY (\`medicament_id\`) REFERENCES \`medicaments\`(\`id\`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 6. Table Calendrier des RDV (sans instances, calculé à la volée)
CREATE TABLE IF NOT EXISTS \`calendrier_rdv\` (
  \`id\` INT AUTO_INCREMENT PRIMARY KEY,
  \`titre\` VARCHAR(255) NOT NULL,
  \`patient\` ENUM('papa', 'maman', 'tous') NOT NULL DEFAULT 'papa',
  \`type\` ENUM('ponctuel', 'recurrent') NOT NULL DEFAULT 'ponctuel',
  \`moment\` VARCHAR(50) NULL COMMENT 'matin / soir si recurrent',
  \`date_debut\` DATETIME NOT NULL,
  \`lieu\` VARCHAR(255) NOT NULL,
  \`notes\` TEXT NULL,
  \`accompagnant_id\` INT NULL,
  \`created_at\` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 7. Table Présences (Planning de la tournante)
CREATE TABLE IF NOT EXISTS \`presences\` (
  \`id\` INT AUTO_INCREMENT PRIMARY KEY,
  \`utilisateur_id\` INT NOT NULL,
  \`date\` DATE NOT NULL,
  \`heure_debut\` TIME NOT NULL DEFAULT '08:00:00',
  \`heure_fin\` TIME NOT NULL DEFAULT '20:00:00',
  INDEX \`idx_pres_date\` (\`date\`),
  FOREIGN KEY (\`utilisateur_id\`) REFERENCES \`utilisateurs\`(\`id\`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 8. Table Messages du Carrousel secondaire
CREATE TABLE IF NOT EXISTS \`messages_carrousel\` (
  \`id\` INT AUTO_INCREMENT PRIMARY KEY,
  \`texte\` TEXT NOT NULL,
  \`date_debut\` DATE NULL,
  \`date_fin\` DATE NULL,
  \`actif\` TINYINT(1) NOT NULL DEFAULT 1
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- Données initiales : Fratrie des 7 enfants
INSERT INTO \`utilisateurs\` (\`id\`, \`nom\`, \`role\`, \`code_pin\`, \`actif\`) VALUES
(1, 'Marie', 'membre', NULL, 1),
(2, 'Thomas', 'membre', NULL, 1),
(3, 'Claire', 'membre', NULL, 1),
(4, 'Julien', 'membre', NULL, 1),
(5, 'Sophie', 'membre', NULL, 1),
(6, 'Nicolas', 'membre', NULL, 1),
(7, 'Camille', 'membre', NULL, 1),
(8, 'Admin', 'admin', '1234', 1),
(9, 'Kiosk Tablette', 'kiosk', NULL, 1)
ON DUPLICATE KEY UPDATE \`nom\`=\`nom\`;

-- Tâches types
INSERT INTO \`taches\` (\`id\`, \`nom\`, \`frequence\`, \`jours_semaine\`, \`categorie\`, \`afficher_alerte\`, \`afficher_carrousel\`) VALUES
(1, 'Sortir la poubelle', 'hebdomadaire', '1,4', 'maison', 1, 0),
(2, 'Arroser les plantes', 'quotidien', '1,2,3,4,5,6,7', 'maison', 0, 1),
(3, 'Aérer la chambre de papa (15 min)', 'quotidien', '1,2,3,4,5,6,7', 'soins', 1, 1),
(4, 'Vérifier réserve compresses & sérum', 'quotidien', '1,2,3,4,5,6,7', 'logistique', 1, 0),
(5, 'Préparer compote fraîche / hydratation', 'quotidien', '1,2,3,4,5,6,7', 'soins', 0, 1)
ON DUPLICATE KEY UPDATE \`nom\`=\`nom\`;

-- Médicaments types
INSERT INTO \`medicaments\` (\`id\`, \`patient\`, \`nom\`, \`dosage\`, \`moment\`, \`heure_prevue\`) VALUES
(1, 'papa', 'Morphine gouttes (0.5ml)', '0.5ml sublingual', 'midi', '12:00:00'),
(2, 'maman', 'Collyre yeux', '2 gouttes chaque œil', 'midi', '14:00:00'),
(3, 'papa', 'Paracétamol sirop', '1 dose-poids', 'soir', '18:30:00'),
(4, 'maman', 'Traitement tension (Kardegic)', '75mg sachet', 'matin', '08:30:00'),
(5, 'papa', 'Spray buccal hydratation confort', 'Pulvérisation', 'soir', '20:00:00')
ON DUPLICATE KEY UPDATE \`nom\`=\`nom\`;

-- Messages carrousel
INSERT INTO \`messages_carrousel\` (\`id\`, \`texte\`, \`actif\`) VALUES
(1, 'Penser à faire boire papa régulièrement (petites gorgées fraîches ou compote)', 1),
(2, 'Les ordonnances renouvelées sont rangées dans le classeur bleu du salon', 1),
(3, 'Maman a besoin de son chapeau pour la terrasse en fin d\\'après-midi', 1)
ON DUPLICATE KEY UPDATE \`texte\`=\`texte\`;

SET FOREIGN_KEY_CHECKS = 1;
`,
  },
  {
    path: 'api/db.php',
    name: 'api/db.php',
    description: 'Connexion PDO MySQL sécurisée avec gestion des entêtes JSON et CORS',
    category: 'api',
    content: `<?php
// Configuration Base de Données OVH Starter
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Authorization');

if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(200);
    exit;
}

// PARAMÈTRES À RENSEIGNER AVEC VOS IDENTIFIANTS OVH
define('DB_HOST', 'localhost'); // ou adresse du serveur MySQL fournie par OVH (ex: xxx.mysql.db)
define('DB_NAME', 'votre_base_soins');
define('DB_USER', 'votre_utilisateur');
define('DB_PASS', 'votre_mot_de_passe');
define('ADMIN_EMAIL', 'admin-famille@example.com'); // pour l'alerte trou de planning

function getDB() {
    static $pdo = null;
    if ($pdo === null) {
        try {
            $dsn = "mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=utf8mb4";
            $pdo = new PDO($dsn, DB_USER, DB_PASS, [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
                PDO::ATTR_EMULATE_PREPARES => false,
            ]);
        } catch (PDOException $e) {
            http_response_code(500);
            echo json_encode(['error' => 'Erreur connexion BDD: ' . $e->getMessage()]);
            exit;
        }
    }
    return $pdo;
}
`,
  },
  {
    path: 'api/taches.php',
    name: 'api/taches.php',
    description: 'API des tâches : récupération du jour et validation "Fait" / "Annuler"',
    category: 'api',
    content: `<?php
require_once __DIR__ . '/db.php';
$pdo = getDB();
$today = date('Y-m-d');
$method = $_SERVER['REQUEST_METHOD'];

if ($method === 'GET') {
    // 1. Génération auto si aucune instance n'existe pour aujourd'hui
    $chk = $pdo->prepare("SELECT COUNT(*) FROM taches_instances WHERE date = ?");
    $chk->execute([$today]);
    if ($chk->fetchColumn() == 0) {
        $dayOfWeek = date('N'); // 1 = Lundi, 7 = Dimanche
        $stmt = $pdo->query("SELECT * FROM taches WHERE actif = 1");
        $taches = $stmt->fetchAll();
        $ins = $pdo->prepare("INSERT INTO taches_instances (tache_id, date, fait) VALUES (?, ?, 0)");
        foreach ($taches as $t) {
            $days = explode(',', $t['jours_semaine']);
            if ($t['frequence'] === 'quotidien' || in_array($dayOfWeek, $days)) {
                $ins->execute([$t['id'], $today]);
            }
        }
    }

    // 2. Récupérer les tâches du jour avec les métadonnées de définition
    $sql = "SELECT ti.id, ti.tache_id, ti.date, ti.fait, ti.fait_par, ti.fait_a,
                   t.nom, t.categorie, t.afficher_alerte, t.afficher_carrousel
            FROM taches_instances ti
            JOIN taches t ON ti.tache_id = t.id
            WHERE ti.date = ?
            ORDER BY ti.fait ASC, t.categorie ASC, t.nom ASC";
    $stmt = $pdo->prepare($sql);
    $stmt->execute([$today]);
    echo json_encode($stmt->fetchAll());
    exit;
}

if ($method === 'POST') {
    $data = json_decode(file_get_contents('php://input'), true);
    $action = $data['action'] ?? '';
    $id = intval($data['id'] ?? 0);
    $userName = trim($data['fait_par'] ?? '');

    if ($action === 'valider') {
        $now = date('H:i:s');
        $upd = $pdo->prepare("UPDATE taches_instances SET fait = 1, fait_par = ?, fait_a = ? WHERE id = ?");
        $upd->execute([$userName, $now, $id]);
        echo json_encode(['success' => true, 'fait' => 1, 'fait_par' => $userName, 'fait_a' => $now]);
        exit;
    }

    if ($action === 'annuler') {
        $upd = $pdo->prepare("UPDATE taches_instances SET fait = 0, fait_par = NULL, fait_a = NULL WHERE id = ?");
        $upd->execute([$id]);
        echo json_encode(['success' => true, 'fait' => 0]);
        exit;
    }

    http_response_code(400);
    echo json_encode(['error' => 'Action inconnue']);
    exit;
}
`,
  },
  {
    path: 'api/medicaments.php',
    name: 'api/medicaments.php',
    description: 'API des médicaments : récupération des prises du jour et validation',
    category: 'api',
    content: `<?php
require_once __DIR__ . '/db.php';
$pdo = getDB();
$today = date('Y-m-d');
$method = $_SERVER['REQUEST_METHOD'];

if ($method === 'GET') {
    // Générer instances du jour si absentes
    $chk = $pdo->prepare("SELECT COUNT(*) FROM medicaments_instances WHERE date = ?");
    $chk->execute([$today]);
    if ($chk->fetchColumn() == 0) {
        $stmt = $pdo->query("SELECT * FROM medicaments WHERE actif = 1");
        $meds = $stmt->fetchAll();
        $ins = $pdo->prepare("INSERT INTO medicaments_instances (medicament_id, patient, nom, dosage, moment, heure_prevue, date, fait)
                              VALUES (?, ?, ?, ?, ?, ?, ?, 0)");
        foreach ($meds as $m) {
            $ins->execute([$m['id'], $m['patient'], $m['nom'], $m['dosage'], $m['moment'], $m['heure_prevue'], $today]);
        }
    }

    $sql = "SELECT * FROM medicaments_instances WHERE date = ? ORDER BY fait ASC, heure_prevue ASC";
    $stmt = $pdo->prepare($sql);
    $stmt->execute([$today]);
    echo json_encode($stmt->fetchAll());
    exit;
}

if ($method === 'POST') {
    $data = json_decode(file_get_contents('php://input'), true);
    $action = $data['action'] ?? '';
    $id = intval($data['id'] ?? 0);
    $userName = trim($data['fait_par'] ?? '');

    if ($action === 'valider') {
        $now = date('H:i:s');
        $upd = $pdo->prepare("UPDATE medicaments_instances SET fait = 1, fait_par = ?, fait_a = ? WHERE id = ?");
        $upd->execute([$userName, $now, $id]);
        echo json_encode(['success' => true, 'fait' => 1, 'fait_par' => $userName, 'fait_a' => $now]);
        exit;
    }

    if ($action === 'annuler') {
        $upd = $pdo->prepare("UPDATE medicaments_instances SET fait = 0, fait_par = NULL, fait_a = NULL WHERE id = ?");
        $upd->execute([$id]);
        echo json_encode(['success' => true, 'fait' => 0]);
        exit;
    }

    http_response_code(400);
    echo json_encode(['error' => 'Action inconnue']);
    exit;
}
`,
  },
  {
    path: 'api/presences.php',
    name: 'api/presences.php',
    description: 'API des présences : tournante de garde, recherche par date et détection trous',
    category: 'api',
    content: `<?php
require_once __DIR__ . '/db.php';
$pdo = getDB();
$method = $_SERVER['REQUEST_METHOD'];

if ($method === 'GET') {
    $date = $_GET['date'] ?? date('Y-m-d');
    
    // Récupérer les présences de la date donnée
    $stmt = $pdo->prepare("SELECT p.*, u.nom as utilisateur_nom 
                           FROM presences p 
                           JOIN utilisateurs u ON p.utilisateur_id = u.id 
                           WHERE p.date = ? 
                           ORDER BY p.heure_debut ASC");
    $stmt->execute([$date]);
    $presences = $stmt->fetchAll();

    // Détection des trous pour J+1 et J+2
    $j1 = date('Y-m-d', strtotime('+1 day'));
    $j2 = date('Y-m-d', strtotime('+2 days'));

    $trous = [];
    $chk1 = $pdo->prepare("SELECT COUNT(*) FROM presences WHERE date = ?");
    $chk1->execute([$j1]);
    if ($chk1->fetchColumn() == 0) $trous[] = $j1;

    $chk2 = $pdo->prepare("SELECT COUNT(*) FROM presences WHERE date = ?");
    $chk2->execute([$j2]);
    if ($chk2->fetchColumn() == 0) $trous[] = $j2;

    echo json_encode([
        'date' => $date,
        'presences' => $presences,
        'trous_j1_j2' => $trous
    ]);
    exit;
}

if ($method === 'POST') {
    $data = json_decode(file_get_contents('php://input'), true);
    $userId = intval($data['utilisateur_id'] ?? 0);
    $date = $data['date'] ?? '';
    $debut = $data['heure_debut'] ?? '08:00:00';
    $fin = $data['heure_fin'] ?? '20:00:00';

    if ($userId && $date) {
        $ins = $pdo->prepare("INSERT INTO presences (utilisateur_id, date, heure_debut, heure_fin) VALUES (?, ?, ?, ?)");
        $ins->execute([$userId, $date, $debut, $fin]);
        echo json_encode(['success' => true]);
        exit;
    }
}
`,
  },
  {
    path: 'api/rdv.php',
    name: 'api/rdv.php',
    description: 'API des rendez-vous médicaux et passages des soignants',
    category: 'api',
    content: `<?php
require_once __DIR__ . '/db.php';
$pdo = getDB();

$today = date('Y-m-d');
$tomorrow = date('Y-m-d', strtotime('+1 day'));

// Liste les RDV récurrents et les ponctuels d'aujourd'hui ou des 7 prochains jours
$sql = "SELECT * FROM calendrier_rdv 
        WHERE type = 'recurrent' 
           OR DATE(date_debut) >= ? 
        ORDER BY date_debut ASC";
$stmt = $pdo->prepare($sql);
$stmt->execute([$today]);
echo json_encode($stmt->fetchAll());
`,
  },
  {
    path: 'cron_minuit.php',
    name: 'cron_minuit.php',
    description: 'Script exécuté chaque nuit à minuit : instanciation quotidienne + email alerte trou de planning',
    category: 'api',
    content: `<?php
// CRON MINUIT — À planifier dans l'espace client OVH (Tâches planifiées)
// Fréquence : 0 0 * * * (Tous les soirs à 00h00)
require_once __DIR__ . '/api/db.php';
$pdo = getDB();

$today = date('Y-m-d');
$j1 = date('Y-m-d', strtotime('+1 day'));
$j2 = date('Y-m-d', strtotime('+2 days'));
$dayOfWeek = date('N');

echo "[CRON " . date('Y-m-d H:i:s') . "] Démarrage routine minuit\\n";

// 1. Génération des instances de tâches pour aujourd'hui
$stmt = $pdo->query("SELECT * FROM taches WHERE actif = 1");
$taches = $stmt->fetchAll();
$insTask = $pdo->prepare("INSERT IGNORE INTO taches_instances (tache_id, date, fait) VALUES (?, ?, 0)");
$taskCount = 0;
foreach ($taches as $t) {
    $days = explode(',', $t['jours_semaine']);
    if ($t['frequence'] === 'quotidien' || in_array($dayOfWeek, $days)) {
        $insTask->execute([$t['id'], $today]);
        $taskCount++;
    }
}
echo "Tâches instanciées pour $today : $taskCount\\n";

// 2. Génération des instances de médicaments pour aujourd'hui
$stmt = $pdo->query("SELECT * FROM medicaments WHERE actif = 1");
$meds = $stmt->fetchAll();
$insMed = $pdo->prepare("INSERT INTO medicaments_instances (medicament_id, patient, nom, dosage, moment, heure_prevue, date, fait)
                        VALUES (?, ?, ?, ?, ?, ?, ?, 0)");
$medCount = 0;
foreach ($meds as $m) {
    $insMed->execute([$m['id'], $m['patient'], $m['nom'], $m['dosage'], $m['moment'], $m['heure_prevue'], $today]);
    $medCount++;
}
echo "Médicaments instanciés pour $today : $medCount\\n";

// 3. Détection des trous de planning pour J+1 et J+2
$trous = [];
$chk1 = $pdo->prepare("SELECT COUNT(*) FROM presences WHERE date = ?");
$chk1->execute([$j1]);
if ($chk1->fetchColumn() == 0) $trous[] = $j1;

$chk2 = $pdo->prepare("SELECT COUNT(*) FROM presences WHERE date = ?");
$chk2->execute([$j2]);
if ($chk2->fetchColumn() == 0) $trous[] = $j2;

if (!empty($trous)) {
    $datesStr = implode(', ', $trous);
    echo "ALERTE : Trou de planning détecté sur : $datesStr\\n";
    
    $to = ADMIN_EMAIL;
    $subject = "⚠️ Alerte Kiosk : Absence de présence assignée pour " . $datesStr;
    $message = "Bonjour,\\n\\n"
             . "Le système de coordination des soins a détecté qu'aucun membre de la fratrie n'est encore assigné de garde pour les dates suivantes :\\n\\n"
             . "  - " . implode("\\n  - ", $trous) . "\\n\\n"
             . "Merci de vous coordonner pour couvrir ce créneau auprès de papa et maman.\\n\\n"
             . "Lien du Kiosk : https://" . $_SERVER['HTTP_HOST'] . "/\\n";
    $headers = "From: kiosk-parents@" . $_SERVER['HTTP_HOST'] . "\\r\\n"
             . "Reply-To: " . ADMIN_EMAIL . "\\r\\n"
             . "Content-Type: text/plain; charset=UTF-8\\r\\n";

    if (mail($to, $subject, $message, $headers)) {
        echo "Email d'alerte envoyé avec succès à $to\\n";
    } else {
        echo "Erreur lors de l'envoi de l'email via mail()\\n";
    }
} else {
    echo "Planning J+1 et J+2 entièrement couvert. Aucun email requis.\\n";
}
`,
  },
  {
    path: 'index.html',
    name: 'index.html',
    description: 'Interface HTML vanilla du Kiosk mural (sans framework, autonome)',
    category: 'frontend',
    content: `<!doctype html>
<html lang="fr">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
  <title>Coordination Soins Parents</title>
  <link rel="manifest" href="manifest.json">
  <meta name="theme-color" content="#121513">
  <link rel="stylesheet" href="style.css">
  <link href="https://fonts.googleapis.com/css2?family=Public+Sans:wght@400;500;600;700&display=swap" rel="stylesheet">
</head>
<body class="theme-dark">
  <div id="app">
    <!-- Vue Dashboard -->
    <div id="view-dashboard" class="view active">
      <div id="garde-banner" class="garde-banner" onclick="ouvrirPresence()">
        <div class="garde-info">
          <p class="label">De garde aujourd'hui</p>
          <h1 class="name" id="garde-nom">Chargement...</h1>
          <p class="range" id="garde-heures">--:-- à --:--</p>
        </div>
        <div class="alert-count" id="badge-urgences">
          <span class="num" id="nb-urgences">0</span>
          <span class="lbl">urgentes</span>
        </div>
      </div>

      <!-- Zone empilée des alertes scrollable -->
      <div class="alert-stack" id="alert-stack">
        <!-- Rempli par JavaScript -->
      </div>

      <!-- Tuiles récapitulatives -->
      <div class="tiles-grid">
        <button class="tile" onclick="naviguer('rdv')">
          <div class="tile-icon">📅</div>
          <div class="tile-title">Rendez-vous</div>
          <p class="tile-sub" id="tile-rdv-sub">Passages & soignants</p>
        </button>
        <button class="tile" onclick="naviguer('taches')">
          <div class="tile-icon">📋</div>
          <div class="tile-title">Tâches du jour</div>
          <p class="tile-sub" id="tile-taches-sub">Soins & logistique</p>
        </button>
      </div>

      <!-- Carrousel secondaire -->
      <div class="carrousel" id="carrousel-bandeau">
        Penser à faire boire régulièrement papa (petites gorgées fraîches)
      </div>
    </div>

    <!-- Vue Tâches -->
    <div id="view-taches" class="view">
      <div class="view-header">
        <button class="btn-back" onclick="naviguer('dashboard')">←</button>
        <h2>Tâches du jour</h2>
      </div>
      <div class="task-list" id="liste-taches"></div>
    </div>

    <!-- Vue Médicaments -->
    <div id="view-medicaments" class="view">
      <div class="view-header">
        <button class="btn-back" onclick="naviguer('dashboard')">←</button>
        <h2>Médicaments du jour</h2>
      </div>
      <div class="task-list" id="liste-meds"></div>
    </div>

    <!-- Vue Présences -->
    <div id="view-presence" class="view">
      <div class="view-header">
        <button class="btn-back" onclick="naviguer('dashboard')">←</button>
        <h2>Planning de présence</h2>
      </div>
      <div class="day-nav">
        <button onclick="changerJour(-1)">‹</button>
        <span class="day-label" id="label-jour">Aujourd'hui</span>
        <button onclick="changerJour(1)">›</button>
      </div>
      <ul class="simple-list" id="liste-presence"></ul>
    </div>

    <!-- Vue RDV -->
    <div id="view-rdv" class="view">
      <div class="view-header">
        <button class="btn-back" onclick="naviguer('dashboard')">←</button>
        <h2>Rendez-vous & Soignants</h2>
      </div>
      <ul class="simple-list" id="liste-rdv"></ul>
    </div>

    <!-- Modale d'Identification -->
    <div id="modal-id" class="modal-overlay hidden">
      <div class="modal">
        <p id="id-question">Bonjour Marie, es-tu de garde ?</p>
        <div class="modal-actions" id="modal-actions-garde">
          <button class="primary" onclick="validerIdentificationGarde(true)">Oui, c'est moi</button>
          <button onclick="ouvrirChoixAutreMembre()">Non, autre personne</button>
        </div>
        <div id="grid-membres" class="id-grid hidden">
          <!-- 6 autres prénoms -->
        </div>
      </div>
    </div>

    <!-- Indicateur de Session Active -->
    <div id="session-pill" class="session-indicator hidden">
      Session active : <span id="session-nom">Marie</span> (<span id="session-timer">60</span>s)
    </div>
  </div>

  <script src="app.js"></script>
</body>
</html>
`,
  },
  {
    path: 'style.css',
    name: 'style.css',
    description: 'Feuille de style CSS avec modes sombre (kiosk) et clair (papier sobre)',
    category: 'frontend',
    content: `@import url('https://fonts.googleapis.com/css2?family=Public+Sans:wght@400;500;600;700&display=swap');

:root {
  --radius: 18px;
  --radius-sm: 10px;
}

/* Thème sombre Kiosk (Mockup tablette murale) */
body.theme-dark {
  --bg: #121513;
  --surface: #1E2320;
  --ink: #F0F4ED;
  --ink-soft: #9BA498;
  --border: #2D352F;
  --accent: #17364D;
  --accent-soft: #244760;
  --danger: #E05353;
  --danger-soft: #421818;
  --warning: #E5983A;
  --warning-soft: #422B11;
  --success: #4EAA6A;
  --success-soft: #183823;
  background: var(--bg);
  color: var(--ink);
}

/* Thème clair (Spécification originale) */
body.theme-light {
  --bg: #F5F4EF;
  --surface: #FFFFFF;
  --ink: #23281F;
  --ink-soft: #5C6355;
  --border: #E1DFD4;
  --accent: #2B4A5E;
  --accent-soft: #DCE6EA;
  --danger: #B33F3F;
  --danger-soft: #F5E1DF;
  --warning: #B4762A;
  --warning-soft: #F3E7D4;
  --success: #3F7D53;
  --success-soft: #DEECE1;
  background: var(--bg);
  color: var(--ink);
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  margin: 0; padding: 0;
  font-family: 'Public Sans', system-ui, sans-serif;
  font-size: 18px; line-height: 1.4;
  user-select: none;
}

#app {
  max-width: 640px;
  margin: 0 auto;
  min-height: 100vh;
  padding: 18px 16px 40px;
}

.view { display: none; }
.view.active { display: block; }

.view-header {
  display: flex; align-items: center; gap: 12px; margin-bottom: 18px;
}
.view-header h2 { margin: 0; font-size: 22px; font-weight: 600; }
.btn-back {
  background: var(--surface); color: inherit;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  width: 46px; height: 46px; font-size: 22px; cursor: pointer;
}

/* Bandeau de garde */
.garde-banner {
  background: var(--accent); color: white;
  border-radius: var(--radius); padding: 18px 20px;
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 12px; cursor: pointer;
}
.garde-banner.vacant { background: var(--danger); }
.garde-info .label { font-size: 13px; opacity: 0.85; margin: 0; }
.garde-info .name { font-size: 24px; font-weight: 700; margin: 2px 0; }
.garde-info .range { font-size: 14px; opacity: 0.9; margin: 0; }
.alert-count {
  background: rgba(224, 83, 83, 0.4); border: 1px solid var(--danger);
  border-radius: var(--radius-sm); padding: 8px 14px; text-align: center;
}
.alert-count .num { font-size: 22px; font-weight: 700; display: block; }
.alert-count .lbl { font-size: 11px; opacity: 0.9; }

/* Alertes empilées */
.alert-stack {
  max-height: 250px; overflow-y: auto; display: flex; flex-direction: column; gap: 8px; margin-bottom: 14px;
}
.alert-row {
  border-radius: var(--radius-sm); padding: 13px 16px;
  display: flex; align-items: center; gap: 12px; font-size: 16px; font-weight: 500;
  cursor: pointer; border: none; text-align: left; width: 100%; font-family: inherit;
}
.alert-row.danger { background: var(--danger-soft); color: var(--danger); }
.alert-row.warning { background: var(--warning-soft); color: var(--warning); }
.alert-row.success { background: var(--success-soft); color: var(--success); }
.alert-row .dot { width: 10px; height: 10px; border-radius: 50%; background: currentColor; flex-shrink: 0; }

/* Tuiles */
.tiles-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 14px; }
.tile {
  background: var(--surface); color: inherit; border: 1px solid var(--border);
  border-radius: var(--radius); padding: 16px; text-align: left; cursor: pointer; font-family: inherit;
}
.tile .tile-title { font-size: 16px; font-weight: 600; margin: 6px 0 2px; }
.tile .tile-sub { font-size: 13px; color: var(--ink-soft); margin: 0; }
.tile .tile-icon { font-size: 22px; }

/* Carrousel */
.carrousel {
  border-top: 1px solid var(--border); padding-top: 12px;
  font-size: 14px; color: var(--ink-soft); min-height: 22px; text-align: center;
}

/* Listes */
.simple-list, .task-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.simple-list li, .task-row {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 14px 16px;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
.btn-fait {
  background: transparent; color: inherit; border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 8px 18px; font-size: 15px; font-weight: 600; cursor: pointer;
}
.task-row.done { background: var(--success-soft); border-color: var(--success); }
.btn-fait.annuler { background: var(--success); color: white; border-color: var(--success); }

/* Modale */
.modal-overlay {
  position: fixed; inset: 0; background: rgba(0,0,0,0.7);
  display: flex; align-items: center; justify-content: center; padding: 20px; z-index: 100;
}
.modal-overlay.hidden { display: none; }
.modal {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 24px 22px; max-width: 440px; width: 100%;
}
.modal p#id-question { font-size: 20px; font-weight: 600; margin: 0 0 18px; }
.modal-actions { display: flex; gap: 10px; }
.modal-actions button {
  flex: 1; padding: 14px; border-radius: var(--radius-sm); border: 1px solid var(--border);
  background: var(--bg); color: inherit; font-size: 16px; cursor: pointer; font-weight: 600;
}
.modal-actions button.primary { background: var(--accent); color: white; border: none; }
.id-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 14px; }
.id-grid.hidden { display: none; }
.id-grid button {
  padding: 14px; border-radius: var(--radius-sm); border: 1px solid var(--border);
  background: var(--bg); color: inherit; font-size: 15px; cursor: pointer; font-weight: 600;
}

/* Indicateur de session */
.session-indicator {
  position: fixed; bottom: 16px; left: 50%; transform: translateX(-50%);
  background: var(--accent); color: white; padding: 8px 20px; border-radius: 999px;
  font-size: 14px; font-weight: 600; box-shadow: 0 4px 12px rgba(0,0,0,0.3);
}
.session-indicator.hidden { display: none; }
`,
  },
  {
    path: 'app.js',
    name: 'app.js',
    description: 'Logique JavaScript Vanilla complète (gestion session 60s, 5s undo, alertes, PWA)',
    category: 'frontend',
    content: `// Application Kiosk Coordination Soins Parents (Vanilla JS)
let activeUser = null;
let sessionTimeout = null;
let sessionSeconds = 60;
let undoTimers = {}; // { key: { timer, seconds } }
let currentPresences = [];
let dutyPerson = null;

// Initialisation au chargement
document.addEventListener('DOMContentLoaded', () => {
  chargerDashboard();
  setInterval(chargerDashboard, 60000); // Rafraîchissement automatique chaque minute
  if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('sw.js').catch(console.error);
  }
});

// Navigation entre vues
function naviguer(nomVue) {
  document.querySelectorAll('.view').forEach(v => v.classList.remove('active'));
  const vue = document.getElementById('view-' + nomVue);
  if (vue) vue.classList.add('active');
  if (nomVue === 'taches') chargerTaches();
  if (nomVue === 'medicaments') chargerMedicaments();
  if (nomVue === 'presence') chargerPresence(0);
  if (nomVue === 'rdv') chargerRdv();
}

// 1. Dashboard
async function chargerDashboard() {
  try {
    const resPres = await fetch('api/presences.php');
    const dataPres = await resPres.json();
    currentPresences = dataPres.presences || [];
    
    // Garde du jour
    if (currentPresences.length > 0) {
      dutyPerson = currentPresences[0].utilisateur_nom;
      document.getElementById('garde-nom').textContent = dutyPerson;
      document.getElementById('garde-heures').textContent = 
        'De ' + currentPresences[0].heure_debut.substring(0,5).replace(':', 'h') + 
        ' à ' + currentPresences[currentPresences.length - 1].heure_fin.substring(0,5).replace(':', 'h');
    } else {
      dutyPerson = null;
      document.getElementById('garde-nom').textContent = 'Trou de planning';
      document.getElementById('garde-heures').textContent = 'Aucun membre assigné';
    }

    // Récupération des tâches et médicaments pour les alertes
    const [resTaches, resMeds, resRdv] = await Promise.all([
      fetch('api/taches.php'),
      fetch('api/medicaments.php'),
      fetch('api/rdv.php')
    ]);
    const taches = await resTaches.json();
    const meds = await resMeds.json();
    const rdvs = await resRdv.json();

    construireAlertes(meds, taches, rdvs);
  } catch (e) {
    console.error('Erreur chargement dashboard', e);
  }
}

function construireAlertes(meds, taches, rdvs) {
  const stack = document.getElementById('alert-stack');
  stack.innerHTML = '';
  let nbUrgences = 0;

  // 1. Médicaments non faits (Rouge)
  meds.filter(m => !m.fait).forEach(m => {
    nbUrgences++;
    const row = document.createElement('button');
    row.className = 'alert-row danger';
    row.innerHTML = '<span class="dot"></span> <span>💊 Médicament ' + m.patient + ' — ' + m.heure_prevue.substring(0,5).replace(':', 'h') + ' (' + m.nom + ')</span>';
    row.onclick = () => naviguer('medicaments');
    stack.appendChild(row);
  });

  // 2. Tâches avec afficher_alerte=1 non faites (Orange)
  taches.filter(t => !t.fait && t.afficher_alerte == 1).forEach(t => {
    nbUrgences++;
    const row = document.createElement('button');
    row.className = 'alert-row warning';
    row.innerHTML = '<span class="dot"></span> <span>🗑️ ' + t.nom + '</span>';
    row.onclick = () => naviguer('taches');
    stack.appendChild(row);
  });

  // 3. RDV / passages du jour (Vert)
  rdvs.forEach(r => {
    const row = document.createElement('button');
    row.className = 'alert-row success';
    row.innerHTML = '<span class="dot"></span> <span>🩺 ' + r.titre + ' — ' + (r.moment || 'ce jour') + '</span>';
    row.onclick = () => naviguer('rdv');
    stack.appendChild(row);
  });

  document.getElementById('nb-urgences').textContent = nbUrgences;
}

// 2. Session et Identification Kiosk
function verifierSession(callbackAction) {
  if (activeUser) {
    renouvelerSession();
    callbackAction();
    return;
  }

  // Ouvrir modale d'identification
  const modal = document.getElementById('modal-id');
  const question = document.getElementById('id-question');
  const actionGarde = document.getElementById('modal-actions-garde');
  const gridMembres = document.getElementById('grid-membres');

  const presumed = dutyPerson || 'Marie';
  question.textContent = 'Bonjour ' + presumed + ', es-tu de garde ?';
  actionGarde.classList.remove('hidden');
  gridMembres.classList.add('hidden');
  modal.classList.remove('hidden');

  window._pendingAction = callbackAction;
}

function validerIdentificationGarde(isGarde) {
  if (isGarde) {
    demarrerSession(dutyPerson || 'Marie');
    fermerModalId();
    if (window._pendingAction) window._pendingAction();
  }
}

function ouvrirChoixAutreMembre() {
  const fratrie = ['Marie', 'Thomas', 'Claire', 'Julien', 'Sophie', 'Nicolas', 'Camille'];
  const grid = document.getElementById('grid-membres');
  grid.innerHTML = '';
  fratrie.forEach(nom => {
    const btn = document.createElement('button');
    btn.textContent = nom;
    btn.onclick = () => {
      demarrerSession(nom);
      fermerModalId();
      if (window._pendingAction) window._pendingAction();
    };
    grid.appendChild(btn);
  });
  document.getElementById('modal-actions-garde').classList.add('hidden');
  grid.classList.remove('hidden');
}

function fermerModalId() {
  document.getElementById('modal-id').classList.add('hidden');
}

function demarrerSession(nom) {
  activeUser = nom;
  sessionSeconds = 60;
  const pill = document.getElementById('session-pill');
  document.getElementById('session-nom').textContent = nom;
  document.getElementById('session-timer').textContent = sessionSeconds;
  pill.classList.remove('hidden');

  clearInterval(sessionTimeout);
  sessionTimeout = setInterval(() => {
    sessionSeconds--;
    document.getElementById('session-timer').textContent = sessionSeconds;
    if (sessionSeconds <= 0) {
      terminerSession();
    }
  }, 1000);
}

function renouvelerSession() {
  sessionSeconds = 60;
}

function terminerSession() {
  activeUser = null;
  clearInterval(sessionTimeout);
  document.getElementById('session-pill').classList.add('hidden');
}

// 3. Action "Fait" avec annulation de 5 secondes
function cliquerFait(type, id, nomItem, elementLigne) {
  verifierSession(async () => {
    // Si déjà en cours d'annulation, annuler
    const key = type + '-' + id;
    if (undoTimers[key]) {
      // Revenir à l'état à faire
      clearTimeout(undoTimers[key].timeout);
      clearInterval(undoTimers[key].interval);
      delete undoTimers[key];
      elementLigne.classList.remove('done');
      const btn = elementLigne.querySelector('.btn-fait');
      btn.textContent = 'Fait';
      btn.classList.remove('annuler');
      return;
    }

    // Passer en mode "Annuler (5s)"
    elementLigne.classList.add('done');
    const btn = elementLigne.querySelector('.btn-fait');
    btn.classList.add('annuler');
    let compte = 5;
    btn.textContent = 'Annuler (' + compte + 's)';

    const interval = setInterval(() => {
      compte--;
      if (compte > 0) {
        btn.textContent = 'Annuler (' + compte + 's)';
      }
    }, 1000);

    const timeout = setTimeout(async () => {
      clearInterval(interval);
      delete undoTimers[key];
      // Appel API définitif
      await fetch('api/' + (type === 'tache' ? 'taches.php' : 'medicaments.php'), {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'valider', id: id, fait_par: activeUser })
      });
      elementLigne.style.transition = 'opacity 0.4s';
      elementLigne.style.opacity = '0';
      setTimeout(() => elementLigne.remove(), 400);
      chargerDashboard();
    }, 5000);

    undoTimers[key] = { timeout, interval };
  });
}

// Chargement des Tâches
async function chargerTaches() {
  const res = await fetch('api/taches.php');
  const taches = await res.json();
  const liste = document.getElementById('liste-taches');
  liste.innerHTML = '';
  taches.filter(t => !t.fait).forEach(t => {
    const li = document.createElement('div');
    li.className = 'task-row';
    li.innerHTML = '<span class="task-name">' + t.nom + '</span>' +
                   '<button class="btn-fait">Fait</button>';
    li.querySelector('.btn-fait').onclick = () => cliquerFait('tache', t.id, t.nom, li);
    liste.appendChild(li);
  });
}

// Chargement des Médicaments
async function chargerMedicaments() {
  const res = await fetch('api/medicaments.php');
  const meds = await res.json();
  const liste = document.getElementById('liste-meds');
  liste.innerHTML = '';
  meds.filter(m => !m.fait).forEach(m => {
    const li = document.createElement('div');
    li.className = 'task-row';
    li.innerHTML = '<span class="task-name">💊 ' + m.patient.toUpperCase() + ' : ' + m.nom + ' (' + m.dosage + ') — ' + m.heure_prevue.substring(0,5).replace(':', 'h') + '</span>' +
                   '<button class="btn-fait">Fait</button>';
    li.querySelector('.btn-fait').onclick = () => cliquerFait('med', m.id, m.nom, li);
    liste.appendChild(li);
  });
}
`,
  },
  {
    path: 'manifest.json',
    name: 'manifest.json',
    description: 'Manifeste PWA pour installation sur écran d’accueil iOS et Android',
    category: 'pwa',
    content: `{
  "id": "/",
  "name": "Coordination Soins Parents",
  "short_name": "Kiosk Soins",
  "description": "Kiosk mural et familial de coordination des soins pour papa et maman",
  "start_url": "/",
  "scope": "/",
  "display": "standalone",
  "background_color": "#121513",
  "theme_color": "#121513",
  "icons": [
    {
      "src": "icon-192.png",
      "sizes": "192x192",
      "type": "image/png",
      "purpose": "any"
    },
    {
      "src": "icon-512.png",
      "sizes": "512x512",
      "type": "image/png",
      "purpose": "any"
    }
  ]
}
`,
  },
  {
    path: 'sw.js',
    name: 'sw.js',
    description: 'Service Worker minimal pour mise en cache hors ligne et rapidité kiosk',
    category: 'pwa',
    content: `const CACHE_NAME = 'kiosk-soins-v1';
const ASSETS = [
  '/',
  '/index.html',
  '/style.css',
  '/app.js',
  '/manifest.json'
];

self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME).then((cache) => cache.addAll(ASSETS))
  );
  self.skipWaiting();
});

self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((keys) =>
      Promise.all(keys.map((k) => k !== CACHE_NAME && caches.delete(k)))
    )
  );
  self.clients.claim();
});

self.addEventListener('fetch', (event) => {
  // Les requêtes API ne sont pas mises en cache de façon agressive
  if (event.request.url.includes('/api/')) {
    event.respondWith(fetch(event.request));
    return;
  }
  event.respondWith(
    caches.match(event.request).then((res) => res || fetch(event.request))
  );
});
`,
  },
  {
    path: 'README_OVH.md',
    name: 'README_OVH.md',
    description: 'Guide pas à pas de déploiement sur hébergement OVH Starter',
    category: 'doc',
    content: `# Guide de déploiement OVH Starter — Kiosk Coordination Soins

## 1. Création de la Base de Données sur OVH
1. Rendez-vous sur votre espace client OVHcloud : **Hébergements** > votre domaine > onglet **Bases de données**.
2. Cliquez sur **Créer une base de données** (MySQL).
3. Notez le nom du serveur (ex: \`monsite.mysql.db\`), le nom de la base, l'utilisateur et le mot de passe créé.

## 2. Import de la structure MySQL (schema.sql)
1. Dans l'espace OVH, cliquez sur les trois points à droite de votre base > **Accéder à phpMyAdmin**.
2. Connectez-vous avec vos identifiants de base de données.
3. Cliquez sur l'onglet **Importer**, sélectionnez le fichier \`schema.sql\` puis cliquez sur **Exécuter**.
4. Toutes les tables (\`utilisateurs\`, \`taches\`, \`medicaments\`, \`presences\`, etc.) et la fratrie des 7 enfants sont créées.

## 3. Configuration des identifiants dans \`api/db.php\`
Ouvrez le fichier \`api/db.php\` et renseignez vos 4 constantes :
\`\`\`php
define('DB_HOST', 'votre_serveur_ovh.mysql.db');
define('DB_NAME', 'votre_nom_de_base');
define('DB_USER', 'votre_utilisateur');
define('DB_PASS', 'votre_mot_de_passe');
define('ADMIN_EMAIL', 'votre-email@gmail.com');
\`\`\`

## 4. Envoi des fichiers par FTP
1. Connectez-vous en FTP via **FileZilla** avec vos accès OVH (onglet **FTP - SSH** de votre espace OVH).
2. Ouvrez le dossier \`www/\` (ou créez un sous-dossier \`soins/\`).
3. Téléversez tous les fichiers du projet dans ce dossier :
   - \`index.html\`
   - \`style.css\`
   - \`app.js\`
   - \`manifest.json\`
   - \`sw.js\`
   - \`cron_minuit.php\`
   - Dossier \`api/\` contenant \`db.php\`, \`taches.php\`, \`medicaments.php\`, \`presences.php\`, \`rdv.php\`

## 5. Mise en place du CRON minuit OVH (Génération auto & Alertes trou de planning)
1. Dans votre hébergement OVH, allez dans **Plus+** > **Tâches planifiées (CRON)**.
2. Cliquez sur **Ajouter une tâche**.
3. Commande : \`cron_minuit.php\` (ou \`www/cron_minuit.php\`).
4. Langage : **PHP 8.2** (ou version par défaut de votre hébergement).
5. Planification : **Tous les jours à 00h00** (\`0 0 * * *\`).
6. Validez. Le script s'exécutera chaque nuit pour régénérer les instances quotidiennes et vérifier si J+1 ou J+2 a un trou de planning pour envoyer l'email d'alerte automatique via \`mail()\`.

## 6. Installation sur tablette murale (PWA)
- Sur iPad : ouvrez l'adresse dans Safari > bouton Partager > **Sur l'écran d'accueil**.
- Sur Android / Fire HD : ouvrez dans Chrome > menu ⋮ > **Installer l'application** ou **Ajouter à l'écran d'accueil**.
- La tablette s'ouvre alors en plein écran sans barre d'adresse navigateur, avec l'affichage kiosk mural !
`,
  },
];

export async function downloadOVHZipArchive(): Promise<void> {
  const zip = new JSZip();

  for (const file of OVH_FILES) {
    zip.file(file.path, file.content);
  }

  const content = await zip.generateAsync({ type: 'blob' });
  const url = URL.createObjectURL(content);
  const a = document.createElement('a');
  a.href = url;
  a.download = 'kiosk-soins-ovh-starter.zip';
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
}
