File: /homepages/35/d993672390/htdocs/dermiteseborrheique/app/report.php
<?php
define('DS_APP', true);
require_once dirname(__DIR__) . '/includes/init.php';
$user = auth_require();
// Plan check
if (!user_can('pdf_export', $user)) {
flash('error', 'L\'export PDF est disponible à partir du plan Essentiel.');
redirect('app/dashboard.php');
}
// Période
$months = (int)($_GET['months'] ?? 1);
if (!in_array($months, [1, 3, 6])) $months = 1;
$startDate = date('Y-m-d', strtotime("-{$months} months"));
// Données
$entries = db_fetch_all(
"SELECT je.*, COUNT(ei.id) as item_count
FROM journal_entries je
LEFT JOIN entry_items ei ON je.id = ei.entry_id
WHERE je.user_id = ? AND je.entry_date >= ?
GROUP BY je.id ORDER BY je.entry_date DESC",
[$user['id'], $startDate]
);
$suspects = db_fetch_all(
"SELECT c.*, i.name, i.category, i.brand
FROM correlations c JOIN items i ON c.item_id = i.id
WHERE c.user_id = ? AND c.status IN ('suspect','confirmed')
ORDER BY c.score DESC LIMIT 10",
[$user['id']]
);
$avgScore = $entries ? round(array_sum(array_column($entries, 'skin_score')) / count($entries), 1) : null;
$crisisCount = count(array_filter($entries, fn($e) => $e['is_crisis']));
$catIcons = ['food'=>'🍽','drink'=>'🥤','alcohol'=>'🍷','tobacco'=>'🚬','cosmetic'=>'🧴','haircare'=>'💇','medication'=>'💊','supplement'=>'🌿','other'=>'📦'];
function scoreColor(float $s): string {
return match(true) { $s>=4=>'#00E87A', $s>=3=>'#FFB547', default=>'#FF6B8A' };
}
// Générer le PDF HTML (imprimable)
if ($_GET['action'] ?? '' === 'generate') {
$moisFR = ['','janvier','février','mars','avril','mai','juin','juillet','août','septembre','octobre','novembre','décembre'];
$generatedAt = date('d/m/Y à H:i');
header('Content-Type: text/html; charset=utf-8');
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Rapport DermiteSéborrhéique — <?= e($user['name']) ?></title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Cabinet+Grotesk:wght@400;600;700;900&display=swap');
*{margin:0;padding:0;box-sizing:border-box;}
body{font-family:'Cabinet Grotesk',Arial,sans-serif;font-size:11pt;color:#1A1A2E;background:#fff;padding:20mm;}
@page{margin:15mm;size:A4;}
@media print{body{padding:0;}.no-print{display:none!important;}}
h1{font-size:20pt;font-weight:900;letter-spacing:-0.03em;margin-bottom:4pt;}
h2{font-size:13pt;font-weight:800;letter-spacing:-0.02em;margin:16pt 0 8pt;padding-bottom:4pt;border-bottom:2px solid #00D4C8;}
h3{font-size:10pt;font-weight:700;margin:10pt 0 4pt;}
.header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:20pt;padding-bottom:14pt;border-bottom:3px solid #00D4C8;}
.logo{font-size:14pt;font-weight:900;color:#00D4C8;}
.logo em{font-style:italic;font-weight:400;}
.meta{text-align:right;font-size:9pt;color:#666;}
.disclaimer{background:#FFF9E0;border:1px solid #FFD700;border-radius:6pt;padding:8pt;font-size:8.5pt;color:#7A6A00;margin-bottom:16pt;line-height:1.5;}
.stats-row{display:grid;grid-template-columns:repeat(4,1fr);gap:10pt;margin-bottom:16pt;}
.stat-box{background:#F5F5F5;border-radius:6pt;padding:10pt;text-align:center;}
.stat-val{font-size:18pt;font-weight:900;color:#00D4C8;}
.stat-label{font-size:8pt;color:#666;text-transform:uppercase;letter-spacing:0.05em;}
table{width:100%;border-collapse:collapse;margin-bottom:14pt;}
th{background:#F0FDFC;font-size:8.5pt;font-weight:700;text-transform:uppercase;letter-spacing:0.05em;padding:6pt 8pt;text-align:left;border-bottom:2px solid #00D4C8;color:#007A72;}
td{font-size:9pt;padding:6pt 8pt;border-bottom:1px solid #EEE;vertical-align:middle;}
tr:nth-child(even) td{background:#FAFAFA;}
.score-badge{display:inline-block;padding:2pt 6pt;border-radius:4pt;font-weight:700;font-size:8.5pt;}
.crisis-badge{background:#FFEEF2;color:#CC2255;border:1px solid #FFAABB;padding:2pt 6pt;border-radius:4pt;font-size:8pt;font-weight:700;}
.suspect-bar{height:6pt;background:#EEE;border-radius:3pt;overflow:hidden;min-width:60pt;display:inline-block;}
.suspect-fill{height:100%;border-radius:3pt;}
.footer{margin-top:20pt;padding-top:10pt;border-top:1px solid #DDD;font-size:8pt;color:#999;display:flex;justify-content:space-between;}
.print-btn{position:fixed;top:16px;right:16px;background:#00D4C8;color:#fff;border:none;padding:10px 20px;border-radius:8px;font-size:14px;font-weight:700;cursor:pointer;z-index:999;}
</style>
</head>
<body>
<button class="print-btn no-print" onclick="window.print()">🖨 Imprimer / Enregistrer PDF</button>
<div class="header">
<div>
<div class="logo">dermite<em>séborrhéique</em>.fr</div>
<div style="font-size:9pt;color:#666;margin-top:4pt;">Rapport de suivi de peau</div>
</div>
<div class="meta">
<div><strong><?= e($user['name']) ?></strong></div>
<div><?= e($user['email']) ?></div>
<div>Généré le <?= $generatedAt ?></div>
<div>Période : <?= date('d/m/Y', strtotime($startDate)) ?> → <?= date('d/m/Y') ?></div>
</div>
</div>
<div class="disclaimer">
⚕️ <strong>Important :</strong> Ce rapport est un outil d'aide au suivi personnel et ne constitue pas un avis médical.
Les corrélations identifiées sont des hypothèses statistiques basées sur vos données. Consultez un dermatologue pour tout diagnostic ou traitement.
</div>
<div class="stats-row">
<div class="stat-box"><div class="stat-val"><?= count($entries) ?></div><div class="stat-label">Entrées</div></div>
<div class="stat-box"><div class="stat-val" style="color:<?= scoreColor((float)($avgScore??3)) ?>"><?= $avgScore ?? '—' ?></div><div class="stat-label">Score moyen</div></div>
<div class="stat-box"><div class="stat-val" style="color:#FF6B8A"><?= $crisisCount ?></div><div class="stat-label">Crises</div></div>
<div class="stat-box"><div class="stat-val"><?= count($suspects) ?></div><div class="stat-label">Suspects</div></div>
</div>
<?php if (!empty($suspects)): ?>
<h2>🔍 Suspects identifiés</h2>
<table>
<thead><tr><th>Item</th><th>Catégorie</th><th>Score</th><th>Occurrences</th><th>Avant crises</th><th>Statut</th></tr></thead>
<tbody>
<?php foreach ($suspects as $s):
$color = $s['score'] >= 70 ? '#FF6B8A' : ($s['score'] >= 50 ? '#FFB547' : '#00D4C8');
?>
<tr>
<td><strong><?= e($s['name']) ?></strong><?php if($s['brand']): ?><br><span style="font-size:8pt;color:#999"><?= e($s['brand']) ?></span><?php endif;?></td>
<td><?= $catIcons[$s['category']] ?? '' ?> <?= ucfirst($s['category']) ?></td>
<td>
<div class="suspect-bar"><div class="suspect-fill" style="width:<?= min(100,$s['score']) ?>%;background:<?= $color ?>"></div></div>
<span style="font-weight:700;color:<?= $color ?>;margin-left:4pt"><?= round($s['score']) ?>%</span>
</td>
<td><?= $s['occurrences_total'] ?></td>
<td><?= $s['occurrences_crisis'] ?></td>
<td><span class="score-badge" style="background:<?= $s['status']==='confirmed'?'#E0FFF0':'#FFF9E0' ?>;color:<?= $s['status']==='confirmed'?'#007A40':'#7A6A00' ?>">
<?= match($s['status']){'confirmed'=>'✅ Confirmé','cleared'=>'✓ Éliminé',default=>'? Suspect'} ?>
</span></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<h2>📅 Historique des entrées</h2>
<table>
<thead><tr><th>Date</th><th>Score</th><th>Zones touchées</th><th>Items</th><th>Crise</th><th>Notes</th></tr></thead>
<tbody>
<?php foreach ($entries as $e):
$s = (int)$e['skin_score'];
$color = scoreColor((float)$s);
$zones = json_decode($e['skin_zones'] ?? '{}', true) ?? [];
$touchedZones = array_filter($zones, fn($v) => $v > 0);
$zoneNames = ['scalp'=>'Cuir chevelu','face'=>'Visage','eyebrows'=>'Sourcils','nose'=>'Nez','ears'=>'Oreilles','chest'=>'Poitrine'];
?>
<tr>
<td><?= format_date($e['entry_date'], 'd/m/Y') ?></td>
<td><span class="score-badge" style="background:<?= $color ?>22;color:<?= $color ?>;border:1px solid <?= $color ?>44"><?= $s ?>/5</span></td>
<td style="font-size:8.5pt;"><?= !empty($touchedZones) ? implode(', ', array_map(fn($k)=>$zoneNames[$k]??$k, array_keys($touchedZones))) : '—' ?></td>
<td><?= $e['item_count'] ?></td>
<td><?= $e['is_crisis'] ? '<span class="crisis-badge">⚠ Crise</span>' : '—' ?></td>
<td style="font-size:8pt;color:#666;max-width:120pt;overflow:hidden;"><?= $e['notes'] ? e(mb_substr($e['notes'],0,60)) . (mb_strlen($e['notes'])>60?'…':'') : '—' ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<div class="footer">
<span>dermiteséborrhéique.fr — Rapport confidentiel</span>
<span>Généré le <?= $generatedAt ?></span>
</div>
</body>
</html>
<?php
exit;
}
// ── Page principale ────────────────────────────────────────────────────────────
$pageTitle = 'Rapport PDF';
$activePage = 'report';
ob_start();
?>
<style>
.report-layout{display:grid;grid-template-columns:1fr 280px;gap:20px;align-items:start;}
.rcard{background:var(--surface);border:1px solid var(--border);border-radius:14px;padding:24px;margin-bottom:16px;}
.rcard-title{font-size:1rem;font-weight:800;letter-spacing:-0.01em;margin-bottom:6px;}
.rcard-desc{font-size:0.8rem;color:var(--text-mid);margin-bottom:20px;line-height:1.5;}
.period-btns{display:flex;gap:8px;margin-bottom:20px;}
.period-btn{flex:1;padding:10px;border-radius:8px;border:1px solid var(--border);background:var(--surface2);color:var(--text-mid);font-size:0.82rem;font-weight:600;cursor:pointer;font-family:inherit;transition:all 0.2s;text-align:center;}
.period-btn:hover,.period-btn.active{border-color:var(--cyan);color:var(--cyan);background:var(--cyan-dim);}
.preview-stat{display:flex;justify-content:space-between;padding:10px 0;border-bottom:1px solid var(--border);font-size:0.85rem;}
.preview-stat:last-child{border:none;}
.preview-val{font-weight:700;color:var(--text);}
.generate-btn{width:100%;padding:14px;border-radius:10px;border:none;background:var(--cyan);color:var(--bg);font-size:0.92rem;font-weight:900;font-family:inherit;cursor:pointer;transition:all 0.2s;display:flex;align-items:center;justify-content:center;gap:8px;}
.generate-btn:hover{background:#fff;box-shadow:0 8px 30px rgba(0,212,200,0.3);}
.tip-item{display:flex;gap:10px;padding:8px 0;border-bottom:1px solid var(--border);font-size:0.8rem;}
.tip-item:last-child{border:none;}
.tip-icon{flex-shrink:0;}
.tip-text{color:var(--text-mid);line-height:1.5;}
@media(max-width:900px){.report-layout{grid-template-columns:1fr;}}
</style>
<div class="report-layout">
<div>
<div class="rcard">
<div class="rcard-title">📄 Générer votre rapport</div>
<div class="rcard-desc">Exportez votre historique complet en PDF pour le partager avec votre dermatologue ou le conserver pour votre suivi.</div>
<div style="font-size:0.72rem;font-weight:700;color:var(--text-dim);text-transform:uppercase;letter-spacing:0.08em;margin-bottom:10px;">Période</div>
<div class="period-btns">
<div class="period-btn <?= $months===1?'active':'' ?>" onclick="setPeriod(1)">1 mois</div>
<div class="period-btn <?= $months===3?'active':'' ?>" onclick="setPeriod(3)">3 mois</div>
<div class="period-btn <?= $months===6?'active':'' ?>" onclick="setPeriod(6)">6 mois</div>
</div>
<div style="font-size:0.72rem;font-weight:700;color:var(--text-dim);text-transform:uppercase;letter-spacing:0.08em;margin-bottom:12px;">Aperçu du contenu</div>
<div class="preview-stat"><span style="color:var(--text-mid)">Entrées de journal</span><span class="preview-val"><?= count($entries) ?></span></div>
<div class="preview-stat"><span style="color:var(--text-mid)">Score moyen de peau</span><span class="preview-val" style="color:<?= scoreColor((float)($avgScore??3)) ?>"><?= $avgScore ?? '—' ?>/5</span></div>
<div class="preview-stat"><span style="color:var(--text-mid)">Crises enregistrées</span><span class="preview-val" style="color:var(--rose)"><?= $crisisCount ?></span></div>
<div class="preview-stat"><span style="color:var(--text-mid)">Suspects identifiés</span><span class="preview-val"><?= count($suspects) ?></span></div>
<div class="preview-stat"><span style="color:var(--text-mid)">Période couverte</span><span class="preview-val" style="font-size:0.8rem;"><?= format_date($startDate,'d/m/Y') ?> → <?= date('d/m/Y') ?></span></div>
<div style="height:1px;background:var(--border);margin:20px 0;"></div>
<button class="generate-btn" onclick="generateReport()">
📄 Générer et ouvrir le PDF
</button>
<p style="font-size:0.72rem;color:var(--text-dim);text-align:center;margin-top:10px;">Une nouvelle fenêtre s'ouvrira. Utilisez Imprimer → Enregistrer en PDF.</p>
</div>
<!-- Suspects preview -->
<?php if (!empty($suspects)): ?>
<div class="rcard">
<div class="rcard-title">🔍 Suspects inclus dans le rapport</div>
<?php foreach (array_slice($suspects, 0, 5) as $s):
$color = $s['score'] >= 70 ? 'var(--rose)' : ($s['score'] >= 50 ? 'var(--amber)' : 'var(--cyan)');
?>
<div class="preview-stat">
<span style="color:var(--text)"><?= e($s['name']) ?></span>
<span style="font-weight:800;color:<?= $color ?>"><?= round($s['score']) ?>%</span>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<!-- Sidebar conseils -->
<div>
<div class="rcard" style="position:sticky;top:80px;">
<div style="font-size:0.72rem;font-weight:700;color:var(--text-dim);text-transform:uppercase;letter-spacing:0.08em;margin-bottom:14px;">💡 Conseils pour la consultation</div>
<div class="tip-item"><span class="tip-icon">📅</span><span class="tip-text">Apportez le rapport à votre prochain rendez-vous dermato.</span></div>
<div class="tip-item"><span class="tip-icon">🔍</span><span class="tip-text">Montrez les suspects avec les scores les plus élevés en priorité.</span></div>
<div class="tip-item"><span class="tip-icon">📊</span><span class="tip-text">Le graphique d'évolution montre les périodes de crise et de rémission.</span></div>
<div class="tip-item"><span class="tip-icon">⚠️</span><span class="tip-text">Précisez que ce rapport est indicatif et non un diagnostic médical.</span></div>
<div class="tip-item"><span class="tip-icon">🧪</span><span class="tip-text">Mentionnez les objectifs d'élimination que vous avez testés.</span></div>
</div>
</div>
</div>
<script>
let currentMonths = <?= $months ?>;
function setPeriod(m) {
currentMonths = m;
document.querySelectorAll('.period-btn').forEach((btn, i) => {
btn.classList.toggle('active', [1,3,6][i] === m);
});
}
function generateReport() {
window.open(`?action=generate&months=${currentMonths}`, '_blank');
}
</script>
<?php
$content = ob_get_clean();
require DS_VIEWS . 'layouts/app.php';