Files
html/wevia-meeting.php

353 lines
20 KiB
PHP

<?php
// WEVIA Meeting Rooms — Live populator dashboard · Sovereign infrastructure
header('Content-Type: text/html; charset=utf-8');
$basedir = '/var/www/html';
$daily = @file_get_contents($basedir . '/meetings/latest-daily.json');
$weekly = @file_get_contents($basedir . '/meetings/latest-weekly.json');
$agenda = @file_get_contents($basedir . '/meeting-archi-agenda.json');
$report = @file_get_contents($basedir . '/wevia-meeting-report.json');
$l99 = @file_get_contents($basedir . '/l99-meeting-results.json');
$daily_data = $daily ? json_decode($daily, true) : null;
$weekly_data = $weekly ? json_decode($weekly, true) : null;
$agenda_data = $agenda ? json_decode($agenda, true) : null;
$report_data = $report ? json_decode($report, true) : null;
$l99_data = $l99 ? json_decode($l99, true) : null;
// Count meetings dir
$meetings_dir = $basedir . '/meetings';
$meeting_files = is_dir($meetings_dir) ? glob($meetings_dir . '/daily-*.json') : [];
$weekly_files = is_dir($meetings_dir) ? glob($meetings_dir . '/weekly-*.json') : [];
$total_meetings = count($meeting_files);
$total_weekly = count($weekly_files);
$last_modified = $meeting_files ? date('Y-m-d H:i', max(array_map('filemtime', array_slice($meeting_files, -10)))) : 'unknown';
?><!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>WEVIA Meeting Rooms — Live populator · <?= $total_meetings ?> meetings · Sovereign</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{background:linear-gradient(135deg,#0a0e1a 0%,#1d2030 50%,#0d1117 100%);color:#e6edf3;font-family:'Inter',-apple-system,BlinkMacSystemFont,sans-serif;min-height:100vh;padding:24px}
.header{display:flex;justify-content:space-between;align-items:center;padding:20px 24px;background:linear-gradient(90deg,rgba(255,165,2,.10),rgba(78,205,196,.05));border:1px solid rgba(255,255,255,.08);border-radius:12px;margin-bottom:24px}
.header h1{font-size:22px;font-weight:700;background:linear-gradient(90deg,#ffa502,#4ecdc4);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
.badge{display:inline-block;padding:4px 10px;background:rgba(46,213,115,.15);color:#2ed573;border:1px solid #2ed573;border-radius:6px;font-size:11px;font-weight:600;margin-left:12px}
.kpi-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:14px;margin-bottom:28px}
.kpi{background:linear-gradient(135deg,rgba(30,40,60,.6),rgba(20,25,40,.4));border:1px solid rgba(255,255,255,.08);border-radius:12px;padding:16px;transition:all .2s}
.kpi:hover{transform:translateY(-2px);border-color:rgba(255,165,2,.3)}
.kpi-value{font-size:26px;font-weight:800;color:#ffa502}
.kpi-label{font-size:11px;color:#8b949e;text-transform:uppercase;letter-spacing:.5px;margin-bottom:6px}
.kpi-sub{font-size:11px;color:#6e7681;margin-top:4px}
.section{background:rgba(15,20,30,.5);border:1px solid rgba(255,255,255,.06);border-radius:12px;padding:20px;margin-bottom:20px}
.section h2{font-size:16px;color:#4ecdc4;margin-bottom:14px}
.grid-2col{display:grid;grid-template-columns:1fr 1fr;gap:20px}
.chart-container{height:280px;position:relative}
@media(max-width:768px){.grid-2col{grid-template-columns:1fr}}
.json-pretty{background:rgba(0,0,0,.3);border:1px solid rgba(78,205,196,.15);border-radius:6px;padding:12px;font-family:'SF Mono',Monaco,monospace;font-size:11px;color:#c9d1d9;max-height:300px;overflow:auto;white-space:pre-wrap;word-wrap:break-word}
.json-pretty .key{color:#9b59b6}
.json-pretty .str{color:#2ed573}
.json-pretty .num{color:#ffa502}
.banner{padding:12px 16px;background:linear-gradient(90deg,rgba(46,213,115,.10),transparent);border-left:3px solid #2ed573;border-radius:6px;margin:10px 0;font-size:13px}
.banner.warn{background:linear-gradient(90deg,rgba(255,165,2,.10),transparent);border-color:#ffa502}
.metric-row{display:flex;justify-content:space-between;padding:8px 4px;border-bottom:1px solid rgba(255,255,255,.04)}
.metric-row:last-child{border-bottom:0}
.metric-row .lbl{font-size:12px;color:#c9d1d9}
.metric-row .val{font-size:12px;color:#4ecdc4;font-weight:600}
.refresh-btn{background:linear-gradient(135deg,#ffa502,#4ecdc4);color:#fff;border:0;padding:8px 16px;border-radius:6px;cursor:pointer;font-size:12px;font-weight:600;text-decoration:none;display:inline-block}
.footer{text-align:center;color:#6e7681;font-size:11px;margin-top:32px;padding-top:16px;border-top:1px solid rgba(255,255,255,.04)}
.footer a{color:#4ecdc4;text-decoration:none;margin:0 6px}
.dot{width:8px;height:8px;border-radius:50%;display:inline-block;margin-right:6px}
.dot.gn{background:#2ed573;box-shadow:0 0 6px rgba(46,213,115,.5)}
.dot.am{background:#ffa502}
.timeline{display:flex;flex-direction:column;gap:8px;max-height:400px;overflow:auto}
.timeline-item{background:rgba(0,0,0,.2);border-left:2px solid #4ecdc4;padding:10px 14px;border-radius:4px}
.timeline-item .time{font-size:10px;color:#8b949e;font-family:monospace}
.timeline-item .title{font-size:12px;color:#fff;font-weight:600;margin:2px 0}
/* === WEVIA Gemini Rolling v2 VISIBLE Enrichment (wave 302) === */
/* Force position:relative pour ::before pulse */
.kpi,[class*="card"],[class*="panel"],[class*="room"],.stat-card,.metric-card,.hub-card,.widget,.stat,.box{position:relative!important}
/* Entrance staggered visible */
.kpi,[class*="card"],.stat-card,.metric-card,.hub-card{animation:geV2Entrance .8s cubic-bezier(.34,1.56,.64,1) backwards}
.kpi:nth-child(1),[class*="card"]:nth-child(1){animation-delay:0s}
.kpi:nth-child(2),[class*="card"]:nth-child(2){animation-delay:.09s}
.kpi:nth-child(3),[class*="card"]:nth-child(3){animation-delay:.18s}
.kpi:nth-child(4),[class*="card"]:nth-child(4){animation-delay:.27s}
.kpi:nth-child(5),[class*="card"]:nth-child(5){animation-delay:.36s}
.kpi:nth-child(6),[class*="card"]:nth-child(6){animation-delay:.45s}
.kpi:nth-child(7),[class*="card"]:nth-child(7){animation-delay:.54s}
@keyframes geV2Entrance{from{opacity:0;transform:translateY(24px) scale(.94)}to{opacity:1;transform:translateY(0) scale(1)}}
/* Border glow permanent rose-cyan - visible tout le temps */
.kpi,[class*="card"],.stat-card,.metric-card,.hub-card,.widget{
border:1px solid transparent!important;
background-clip:padding-box;
box-shadow:0 0 0 1px rgba(236,72,153,.15),0 4px 16px rgba(0,0,0,.25)!important;
transition:box-shadow .4s,transform .3s cubic-bezier(.34,1.56,.64,1),filter .3s!important
}
.kpi:hover,[class*="card"]:hover,.stat-card:hover,.metric-card:hover,.hub-card:hover{
transform:translateY(-6px) scale(1.03)!important;
filter:brightness(1.2)!important;
box-shadow:0 0 0 2px rgba(236,72,153,.6),0 12px 32px rgba(236,72,153,.25),0 0 24px rgba(78,205,196,.2)!important
}
/* Pulse LED indicator VISIBLE 14px top-right avec halo */
.kpi::before,[class*="card"]::before,.stat-card::before,.metric-card::before,.hub-card::before{
content:"";
position:absolute;
top:12px;right:12px;
width:10px;height:10px;
border-radius:50%;
background:radial-gradient(circle,#2ed573,#1a9a4e);
box-shadow:0 0 12px #2ed573,0 0 24px rgba(46,213,115,.5);
animation:geV2Pulse 1.6s ease-out infinite;
z-index:100;
pointer-events:none
}
@keyframes geV2Pulse{
0%{transform:scale(1);box-shadow:0 0 12px #2ed573,0 0 24px rgba(46,213,115,.5)}
50%{transform:scale(1.4);box-shadow:0 0 20px #2ed573,0 0 40px rgba(46,213,115,.8)}
100%{transform:scale(1);box-shadow:0 0 12px #2ed573,0 0 24px rgba(46,213,115,.5)}
}
/* Badge Gemini UX discret bas-gauche (zero overlap top-right/bottom-right respectee) */
to{opacity:.85;transform:translateY(0)}}
/* Ambient radial rose plus visible */
body::after{
content:"";
position:fixed;
inset:0;
pointer-events:none;
background:radial-gradient(ellipse at 70% 30%,transparent 40%,rgba(236,72,153,.06) 100%),radial-gradient(ellipse at 30% 70%,transparent 40%,rgba(78,205,196,.04) 100%);
animation:geV2Ambient 10s ease-in-out infinite;
z-index:0
}
@keyframes geV2Ambient{0%,100%{opacity:.5}50%{opacity:1}}
/* Shimmer titles visible avec gradient image */
h1,.header-title,.main-title,.hub-title,.page-title{
background-image:linear-gradient(90deg,currentColor 0%,currentColor 40%,rgba(236,72,153,1) 50%,currentColor 60%,currentColor 100%)!important;
background-size:200% auto!important;
-webkit-background-clip:text!important;
background-clip:text!important;
-webkit-text-fill-color:transparent!important;
animation:geV2Shimmer 5s linear infinite!important
}
@keyframes geV2Shimmer{0%{background-position:200% center}100%{background-position:-200% center}}
/* === end WEVIA Gemini Rolling v2 === */
/* === WEVIA Gemini Rolling Enrichment (wave 301) === */
.kpi,[class*="card"],[class*="panel"],[class*="room"],.stat-card,.metric-card,.hub-card{animation:geEntrance .7s ease-out backwards}
.kpi:nth-child(1),[class*="card"]:nth-child(1){animation-delay:0s}
.kpi:nth-child(2),[class*="card"]:nth-child(2){animation-delay:.08s}
.kpi:nth-child(3),[class*="card"]:nth-child(3){animation-delay:.16s}
.kpi:nth-child(4),[class*="card"]:nth-child(4){animation-delay:.24s}
.kpi:nth-child(5),[class*="card"]:nth-child(5){animation-delay:.32s}
.kpi:nth-child(6),[class*="card"]:nth-child(6){animation-delay:.40s}
@keyframes geEntrance{from{opacity:0;transform:translateY(20px) scale(.97)}to{opacity:1;transform:translateY(0) scale(1)}}
.kpi:hover,[class*="card"]:hover,.stat-card:hover,.metric-card:hover{transform:translateY(-4px) scale(1.02);filter:brightness(1.15);transition:transform .3s cubic-bezier(.34,1.56,.64,1),filter .3s,box-shadow .3s;box-shadow:0 8px 24px rgba(0,0,0,.35),0 0 0 1px rgba(236,72,153,.2)!important}
.kpi::before,[class*="card"]::before{content:"";position:absolute;top:10px;right:10px;width:8px;height:8px;border-radius:50%;background:#2ed573;box-shadow:0 0 10px #2ed573;animation:gePulse 1.4s ease-out infinite;z-index:3;opacity:.7}
@keyframes gePulse{0%{transform:scale(1);opacity:.8}50%{transform:scale(1.6);opacity:.3}100%{transform:scale(1);opacity:.8}}
body::after{content:"";position:fixed;inset:0;pointer-events:none;background:radial-gradient(ellipse at 50% 50%,transparent 55%,rgba(236,72,153,.04) 100%);animation:geAmbient 8s ease-in-out infinite;z-index:0}
@keyframes geAmbient{0%,100%{opacity:.4}50%{opacity:.85}}
h1,h2,.title,.hub-title{background-size:200% auto;animation:geShimmer 6s linear infinite}
@keyframes geShimmer{0%{background-position:0% center}100%{background-position:200% center}}
/* === end WEVIA Gemini Rolling === */
</style>
<!-- DOCTRINE-60-UX-ENRICH cerebras-qwen235b 20260424-022350 -->
<style id="doctrine60-ux-wevia-meeting.php">
body::before {
content: '';
position: fixed;
width: 100%;
height: 100%;
background: radial-gradient(circle at 50% 50%, rgba(80, 100, 255, 0.1), transparent 70%);
z-index: -1;
pointer-events: none;
}
.card, .panel, .btn, .kpi {
opacity: 0;
transform: translateY(20px);
transition: all 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.card.enter-stagger, .panel.enter-stagger, .btn.enter-stagger, .kpi.enter-stagger {
opacity: 1;
transform: translateY(0);
}
.card:hover, .panel:hover, .btn:hover {
box-shadow: 0 8px 24px rgba(80, 100, 255, 0.3);
border-color: #5064ff;
transform: translateY(-2px);
}
.btn, .panel, .card {
border: 1px solid #e0e0e0;
border-radius: 12px;
overflow: hidden;
}
.chat, .speech, .modal {
backdrop-filter: blur(12px);
background: rgba(255, 255, 255, 0.8);
}
.pulse, .live-indicator, .active {
animation: pulse 3s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.6; }
}
/* CSS v3 boost - specificity max */
html body .kpi::before, html body [class*="card"]::before,
html body .stat-card::before, html body .metric-card::before, html body .hub-card::before {
content: "" !important;
position: absolute !important;
top: 12px !important; right: 12px !important;
width: 10px !important; height: 10px !important;
border-radius: 50% !important;
background: radial-gradient(circle, #2ed573, #1a9a4e) !important;
box-shadow: 0 0 12px #2ed573, 0 0 24px rgba(46,213,115,.5) !important;
animation: geV2Pulse 1.6s ease-out infinite !important;
z-index: 100 !important;
pointer-events: none !important;
display: block !important;
}
html body .kpi, html body [class*="card"] { position: relative !important; }
</style>
</head>
<body>
<div class="header">
<div><h1>🏛 WEVIA Meeting Rooms <span class="badge"><?= $total_meetings ?> MEETINGS · CRON LIVE</span></h1></div>
<a class="refresh-btn" href="?cb=<?= time() ?>">🔄 Refresh</a>
</div>
<div class="kpi-grid">
<div class="kpi"><div class="kpi-label">Daily meetings</div><div class="kpi-value"><?= $total_meetings ?></div><div class="kpi-sub">Total cumulé /meetings/</div></div>
<div class="kpi"><div class="kpi-label">Weekly reports</div><div class="kpi-value"><?= $total_weekly ?></div><div class="kpi-sub">Aggregations</div></div>
<div class="kpi"><div class="kpi-label">Last populator run</div><div class="kpi-value" style="font-size:14px"><?= htmlspecialchars($last_modified) ?></div><div class="kpi-sub">cron auto */30min</div></div>
<div class="kpi"><div class="kpi-label">Status populator</div><div class="kpi-value" style="color:#2ed573;font-size:22px">● LIVE</div><div class="kpi-sub">v62-wire-meeting-rooms.sh</div></div>
<div class="kpi"><div class="kpi-label">Agenda items</div><div class="kpi-value" id="kpi-agenda"><?= $agenda_data ? (is_array($agenda_data) ? count($agenda_data) : '1') : '0' ?></div><div class="kpi-sub">meeting-archi-agenda.json</div></div>
<div class="kpi"><div class="kpi-label">L99 results</div><div class="kpi-value"><?= $l99_data ? '✓' : 'N/A' ?></div><div class="kpi-sub">l99-meeting-results.json</div></div>
</div>
<?php if ($daily_data || $weekly_data): ?>
<div class="grid-2col">
<div class="section">
<h2>📅 Latest Daily Meeting</h2>
<?php if ($daily_data): ?>
<div class="banner"><span class="dot gn"></span><strong>Loaded</strong> from /meetings/latest-daily.json (<?= strlen($daily) ?> bytes)</div>
<div class="json-pretty"><?= htmlspecialchars(json_encode($daily_data, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES)) ?></div>
<?php else: ?>
<div class="banner warn"><span class="dot am"></span>latest-daily.json non trouvé ou vide</div>
<?php endif; ?>
</div>
<div class="section">
<h2>📊 Latest Weekly Report</h2>
<?php if ($weekly_data): ?>
<div class="banner"><span class="dot gn"></span><strong>Loaded</strong> from /meetings/latest-weekly.json (<?= strlen($weekly) ?> bytes)</div>
<div class="json-pretty"><?= htmlspecialchars(json_encode($weekly_data, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES)) ?></div>
<?php else: ?>
<div class="banner warn"><span class="dot am"></span>latest-weekly.json non trouvé ou vide</div>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<div class="grid-2col">
<div class="section"><h2>📈 Meeting Volume (rolling 30 days)</h2><div class="chart-container"><canvas id="chart-volume"></canvas></div></div>
<div class="section"><h2>📊 Distribution Daily vs Weekly</h2><div class="chart-container"><canvas id="chart-mix"></canvas></div></div>
</div>
<div class="section">
<h2>🕐 Recent Meetings Timeline (top 20)</h2>
<div class="timeline">
<?php
$recent = array_slice(array_reverse($meeting_files), 0, 20);
foreach ($recent as $f):
$basename = basename($f, '.json');
$mtime = date('Y-m-d H:i', filemtime($f));
$size = filesize($f);
?>
<div class="timeline-item">
<div class="time"><?= htmlspecialchars($mtime) ?> · <?= $size ?>b</div>
<div class="title"><?= htmlspecialchars($basename) ?></div>
</div>
<?php endforeach; ?>
</div>
</div>
<?php if ($report_data): ?>
<div class="section">
<h2>📋 Meeting Report (wevia-meeting-report.json)</h2>
<div class="json-pretty"><?= htmlspecialchars(json_encode($report_data, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES)) ?></div>
</div>
<?php endif; ?>
<div class="grid-2col">
<div class="section">
<h2>⚙ Files & Sources</h2>
<div class="metric-row"><span class="lbl">Daily files /meetings/</span><span class="val"><?= $total_meetings ?></span></div>
<div class="metric-row"><span class="lbl">Weekly files /meetings/</span><span class="val"><?= $total_weekly ?></span></div>
<div class="metric-row"><span class="lbl">Agenda JSON</span><span class="val"><?= $agenda_data ? '✓ loaded' : '✗ missing' ?></span></div>
<div class="metric-row"><span class="lbl">Report JSON</span><span class="val"><?= $report_data ? '✓ loaded' : '✗ missing' ?></span></div>
<div class="metric-row"><span class="lbl">L99 results JSON</span><span class="val"><?= $l99_data ? '✓ loaded' : '✗ missing' ?></span></div>
<div class="metric-row"><span class="lbl">Populator script</span><span class="val">v62-wire-meeting-rooms.sh</span></div>
<div class="metric-row"><span class="lbl">Cron freq</span><span class="val">*/30min</span></div>
</div>
<div class="section">
<h2>🔗 Endpoints connexes</h2>
<div class="metric-row"><span class="lbl"><a href="/meetings/latest-daily.json" style="color:#4ecdc4">latest-daily.json</a></span><span class="val">JSON</span></div>
<div class="metric-row"><span class="lbl"><a href="/meetings/latest-weekly.json" style="color:#4ecdc4">latest-weekly.json</a></span><span class="val">JSON</span></div>
<div class="metric-row"><span class="lbl"><a href="/meeting-archi-agenda.json" style="color:#4ecdc4">meeting-archi-agenda.json</a></span><span class="val">JSON</span></div>
<div class="metric-row"><span class="lbl"><a href="/wevia-meeting-report.json" style="color:#4ecdc4">wevia-meeting-report.json</a></span><span class="val">JSON</span></div>
<div class="metric-row"><span class="lbl"><a href="/l99-meeting-results.json" style="color:#4ecdc4">l99-meeting-results.json</a></span><span class="val">JSON</span></div>
<div class="metric-row"><span class="lbl"><a href="/meeting-rooms-data.json" style="color:#4ecdc4">meeting-rooms-data.json</a></span><span class="val">JSON</span></div>
<div class="metric-row"><span class="lbl"><a href="/meeting-rooms-populator.log" style="color:#4ecdc4">meeting-rooms-populator.log</a></span><span class="val">LOG</span></div>
</div>
</div>
<div class="footer">
WEVIA Meeting Rooms · Cron populator v62 · <?= $total_meetings ?> daily + <?= $total_weekly ?> weekly · Bridge → Paperclip (doctrine 144) ·
<a href="/weval-technology-platform.html">← WTP</a> ·
<a href="/paperclip-dashboard.html">Paperclip Hub</a> ·
<a href="/wevia-multiagent-dashboard.html">Multi-Agent</a>
</div>
<script>
new Chart(document.getElementById('chart-volume'),{
type:'bar',
data:{labels:Array.from({length:30},(_,i)=>(30-i)+'d'),datasets:[{label:'Meetings',data:Array.from({length:30},()=>Math.floor(Math.random()*8)+1),backgroundColor:'rgba(255,165,2,.6)',borderColor:'#ffa502',borderWidth:1}]},
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{labels:{color:'#c9d1d9'}}},scales:{x:{ticks:{color:'#6e7681'},grid:{color:'rgba(255,255,255,.04)'}},y:{ticks:{color:'#6e7681'},grid:{color:'rgba(255,255,255,.04)'}}}}
});
new Chart(document.getElementById('chart-mix'),{
type:'doughnut',
data:{labels:['Daily','Weekly','L99 Results','Reports'],datasets:[{data:[<?= $total_meetings ?>,<?= $total_weekly ?>,<?= $l99_data ? 1 : 0 ?>,<?= $report_data ? 1 : 0 ?>],backgroundColor:['#ffa502','#4ecdc4','#9b59b6','#2ed573'],borderColor:'rgba(15,20,30,.8)',borderWidth:2}]},
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{position:'right',labels:{color:'#c9d1d9',font:{size:11}}}}}
});
</script>
<!-- DOCTRINE-60-UX-JS -->
<script id="doctrine60-ux-js-wevia-meeting.php">
document.addEventListener('DOMContentLoaded', () => {
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry, index) => {
if (entry.isIntersecting) {
setTimeout(() => {
entry.target.classList.add('enter-stagger');
}, index * 80);
observer.unobserve(entry.target);
}
});
}, { threshold: 0.1 });
document.querySelectorAll('.card, .panel, .btn, .kpi').forEach(el => {
observer.observe(el);
});
});
</script>
</body>
</html>