<?php $h=static fn($v)=>htmlspecialchars((string)$v,ENT_QUOTES,'UTF-8'); $layout=$dashboard?json_decode((string)($dashboard['layout_json']??'{}'),true):[]; if(!is_array($layout))$layout=[]; ?>
<link rel="stylesheet" href="/public/assets/css/axiom-studio.css">
<main class="axiom-studio studio-designer-page dashboard-designer-page dashboard-v2">
<section class="axiom-studio-hero studio-designer-hero"><div><span class="axiom-studio-kicker">DASHBOARD DESIGNER V2 · 9.4.6</span><h2>Composer, adapter et publier les tableaux de bord</h2><p>Modification des widgets, duplication, paramètres d’affichage, rafraîchissement et prévisualisation responsive.</p></div><div class="axiom-studio-version"><span>Studio</span><strong>Dashboard V2</strong></div></section>
<?php if(!empty($_SESSION['flash_success'])):?><div class="studio-alert success"><?= $h($_SESSION['flash_success']);unset($_SESSION['flash_success']);?></div><?php endif;?>
<?php if(!empty($_SESSION['flash_error'])):?><div class="studio-alert error"><?= $h($_SESSION['flash_error']);unset($_SESSION['flash_error']);?></div><?php endif;?>
<section class="studio-generator-toolbar"><form method="get" action="/public/administration/axiom/dashboard-designer"><label>Module<select name="module_id" onchange="this.form.submit()"><?php foreach($modules as $m):?><option value="<?= (int)$m['id']?>" <?= (int)$m['id']===$moduleId?'selected':''?>><?= $h($m['name'])?></option><?php endforeach;?></select></label><label>Tableau de bord<select name="dashboard_id" onchange="this.form.submit()"><option value="0">Nouveau tableau de bord…</option><?php foreach($dashboards as $d):?><option value="<?= (int)$d['id']?>" <?= $dashboard&&(int)$d['id']===(int)$dashboard['id']?'selected':''?>><?= $h($d['name'])?> · <?= $h($d['status'])?> · <?= (int)$d['widget_count']?> widgets</option><?php endforeach;?></select></label></form><?php if($dashboard):?><div class="dashboard-actions"><form method="post" action="/public/administration/axiom/dashboard-designer/dashboard/dupliquer"><?= \App\Core\Csrf::field()?><input type="hidden" name="module_id" value="<?= $moduleId?>"><input type="hidden" name="dashboard_id" value="<?= (int)$dashboard['id']?>"><button class="studio-secondary">Dupliquer</button></form><form method="post" action="/public/administration/axiom/dashboard-designer/publier"><?= \App\Core\Csrf::field()?><input type="hidden" name="module_id" value="<?= $moduleId?>"><input type="hidden" name="dashboard_id" value="<?= (int)$dashboard['id']?>"><button class="studio-primary">Publier</button></form></div><?php endif;?></section>
<?php if(!$dashboard):?>
<section class="studio-panel workflow-create"><div class="studio-panel-head"><div><span class="axiom-studio-kicker">NOUVEAU TABLEAU</span><h3>Créer un dashboard</h3></div></div><form method="post" action="/public/administration/axiom/dashboard-designer/dashboard" class="studio-form-grid"><?= \App\Core\Csrf::field()?><input type="hidden" name="module_id" value="<?= $moduleId?>"><label>Nom<input name="name" required placeholder="Pilotage du module"></label><label>Code<input name="code" placeholder="pilotage_module"></label><label>Rôle cible<input name="target_role" placeholder="direction"></label><label>Rafraîchissement<select name="refresh_interval"><option value="0">Manuel</option><option value="60">1 minute</option><option value="300">5 minutes</option><option value="900">15 minutes</option></select></label><label class="check-line"><input type="checkbox" name="is_default" value="1"> Tableau par défaut</label><label class="span-2">Description<textarea name="description" rows="3"></textarea></label><div><button class="studio-primary">Créer</button></div></form></section>
<?php else:?>
<section class="studio-panel dashboard-settings"><div class="studio-panel-head"><div><span class="axiom-studio-kicker">PARAMÈTRES</span><h3><?= $h($dashboard['name'])?></h3></div><span class="type-chip"><?= $h($dashboard['status'])?></span></div><form method="post" action="/public/administration/axiom/dashboard-designer/dashboard/modifier" class="studio-form-grid"><?= \App\Core\Csrf::field()?><input type="hidden" name="module_id" value="<?= $moduleId?>"><input type="hidden" name="dashboard_id" value="<?= (int)$dashboard['id']?>"><label>Nom<input name="name" required value="<?= $h($dashboard['name'])?>"></label><label>Rôle cible<input name="target_role" value="<?= $h($dashboard['target_role'])?>"></label><label>Espacement<input type="number" min="0" max="48" name="gap" value="<?= (int)($layout['gap']??16)?>"></label><label>Rafraîchissement<select name="refresh_interval"><?php foreach([0=>'Manuel',60=>'1 minute',300=>'5 minutes',900=>'15 minutes',1800=>'30 minutes'] as $k=>$v):?><option value="<?= $k?>" <?= (int)($layout['refresh_interval']??0)===$k?'selected':''?>><?= $v?></option><?php endforeach;?></select></label><label>Affichage<select name="display_mode"><option value="cards" <?= ($layout['display_mode']??'cards')==='cards'?'selected':''?>>Cartes</option><option value="compact" <?= ($layout['display_mode']??'')==='compact'?'selected':''?>>Compact</option></select></label><label class="check-line"><input type="checkbox" name="is_default" value="1" <?= (int)$dashboard['is_default']===1?'checked':''?>> Tableau par défaut</label><label class="span-2">Description<textarea name="description" rows="2"><?= $h($dashboard['description'])?></textarea></label><div><button class="studio-secondary">Enregistrer les paramètres</button></div></form></section>
<div class="dashboard-designer-layout"><aside class="studio-panel dashboard-toolbox"><div class="studio-panel-head"><div><span class="axiom-studio-kicker">BIBLIOTHÈQUE</span><h3>Ajouter un widget</h3></div></div><form method="post" action="/public/administration/axiom/dashboard-designer/widget" class="workflow-form"><?= \App\Core\Csrf::field()?><input type="hidden" name="module_id" value="<?= $moduleId?>"><input type="hidden" name="dashboard_id" value="<?= (int)$dashboard['id']?>"><label>Titre<input name="title" required></label><label>Sous-titre<input name="subtitle"></label><label>Type<select name="widget_type"><?php foreach($widgetTypes as $k=>$v):?><option value="<?= $h($k)?>"><?= $h($v)?></option><?php endforeach;?></select></label><label>Source<select name="data_source"><?php foreach($dataSources as $k=>$v):?><option value="<?= $h($k)?>"><?= $h($v)?></option><?php endforeach;?></select></label><label>Entité<select name="entity_id"><option value="0">Aucune</option><?php foreach($entities as $e):?><option value="<?= (int)$e['id']?>"><?= $h($e['name'])?></option><?php endforeach;?></select></label><label>Valeur statique<input name="static_value"></label><label>Lien<input name="link_url" placeholder="/administration/..."></label><label>Limite<input type="number" min="1" max="50" name="limit" value="5"></label><div class="dashboard-widths"><label>PC<input type="number" min="1" max="12" name="width_desktop" value="4"></label><label>Tablette<input type="number" min="1" max="12" name="width_tablet" value="6"></label><label>Mobile<input type="number" min="1" max="12" name="width_mobile" value="12"></label></div><button class="studio-secondary wide">Ajouter le widget</button></form></aside>
<section class="studio-panel dashboard-canvas-panel"><div class="studio-panel-head"><div><span class="axiom-studio-kicker">APERÇU RESPONSIVE</span><h3>Grille de production</h3><code><?= $h($dashboard['code'])?></code></div><div class="dashboard-device-switch"><button type="button" data-device="desktop" class="active">PC</button><button type="button" data-device="tablet">Tablette</button><button type="button" data-device="mobile">Mobile</button></div></div><div class="dashboard-canvas mode-<?= $h($layout['display_mode']??'cards')?>" id="dashboard-canvas" style="--dashboard-gap:<?= (int)($layout['gap']??16)?>px"><?php if(!$widgets):?><div class="dashboard-empty">Ajoutez votre premier widget.</div><?php endif;?><?php foreach($widgets as $w):$pv=$w['preview']??[];$cfg=$w['config']??[];?><article class="dashboard-widget type-<?= $h($w['widget_type'])?>" draggable="true" data-id="<?= (int)$w['id']?>" style="--desk:<?= (int)$w['width_desktop']?>;--tab:<?= (int)$w['width_tablet']?>;--mob:<?= (int)$w['width_mobile']?>"><header><div><small><?= $h($widgetTypes[$w['widget_type']]??$w['widget_type'])?></small><strong><?= $h($w['title'])?></strong><?php if(!empty($cfg['subtitle'])):?><em><?= $h($cfg['subtitle'])?></em><?php endif;?></div><div class="widget-head-actions"><button type="button" class="widget-edit-toggle" aria-label="Modifier">✎</button><form method="post" action="/public/administration/axiom/dashboard-designer/archiver"><?= \App\Core\Csrf::field()?><input type="hidden" name="module_id" value="<?= $moduleId?>"><input type="hidden" name="dashboard_id" value="<?= (int)$dashboard['id']?>"><input type="hidden" name="object_type" value="widget"><input type="hidden" name="object_id" value="<?= (int)$w['id']?>"><button title="Archiver">×</button></form></div></header><div class="dashboard-widget-content"><?php if(($pv['kind']??'')==='value'):?><b class="dashboard-kpi"><?= $h($pv['value'])?></b><?php elseif(($pv['kind']??'')==='series'):?><div class="dashboard-bars"><?php foreach(array_slice($pv['rows']??[],0,6) as $r):?><div><span><?= $h($r['label']??'')?></span><i style="--v:<?= max(5,min(100,(int)($r['value']??0)*10))?>%"></i><b><?= (int)($r['value']??0)?></b></div><?php endforeach;?></div><?php elseif(($pv['kind']??'')==='table'):?><div class="dashboard-mini-table"><?php foreach(array_slice($pv['rows']??[],0,4) as $r):?><div><?= $h(implode(' · ',array_slice(array_map('strval',$r),0,3)))?></div><?php endforeach;?></div><?php else:?><p class="studio-muted"><?= $h($pv['value']??($cfg['empty_message']??'Aucune donnée'))?></p><?php endif;?></div><footer><span><?= $h($dataSources[$w['data_source']]??$w['data_source'])?></span><code><?= (int)$w['width_desktop']?>/12</code></footer><form method="post" action="/public/administration/axiom/dashboard-designer/widget/modifier" class="widget-edit-form" hidden><?= \App\Core\Csrf::field()?><input type="hidden" name="module_id" value="<?= $moduleId?>"><input type="hidden" name="dashboard_id" value="<?= (int)$dashboard['id']?>"><input type="hidden" name="widget_id" value="<?= (int)$w['id']?>"><label>Titre<input name="title" value="<?= $h($w['title'])?>" required></label><label>Sous-titre<input name="subtitle" value="<?= $h($cfg['subtitle']??'')?>"></label><label>Type<select name="widget_type"><?php foreach($widgetTypes as $k=>$v):?><option value="<?= $h($k)?>" <?= $w['widget_type']===$k?'selected':''?>><?= $h($v)?></option><?php endforeach;?></select></label><label>Source<select name="data_source"><?php foreach($dataSources as $k=>$v):?><option value="<?= $h($k)?>" <?= $w['data_source']===$k?'selected':''?>><?= $h($v)?></option><?php endforeach;?></select></label><label>Entité<select name="entity_id"><option value="0">Aucune</option><?php foreach($entities as $e):?><option value="<?= (int)$e['id']?>" <?= (int)($cfg['entity_id']??0)===(int)$e['id']?'selected':''?>><?= $h($e['name'])?></option><?php endforeach;?></select></label><label>Valeur<input name="static_value" value="<?= $h($cfg['static_value']??'')?>"></label><label>Lien<input name="link_url" value="<?= $h($cfg['link_url']??'')?>"></label><label>Limite<input type="number" min="1" max="50" name="limit" value="<?= (int)($cfg['limit']??5)?>"></label><div class="dashboard-widths"><label>PC<input type="number" min="1" max="12" name="width_desktop" value="<?= (int)$w['width_desktop']?>"></label><label>Tablette<input type="number" min="1" max="12" name="width_tablet" value="<?= (int)$w['width_tablet']?>"></label><label>Mobile<input type="number" min="1" max="12" name="width_mobile" value="<?= (int)$w['width_mobile']?>"></label></div><button class="studio-primary">Mettre à jour</button></form></article><?php endforeach;?></div><form method="post" action="/public/administration/axiom/dashboard-designer/ordre" id="dashboard-order-form"><?= \App\Core\Csrf::field()?><input type="hidden" name="module_id" value="<?= $moduleId?>"><input type="hidden" name="dashboard_id" value="<?= (int)$dashboard['id']?>"><input type="hidden" name="order_json" id="dashboard-order-json"><button class="studio-secondary" type="submit">Enregistrer l’ordre</button></form></section>
<aside class="studio-panel dashboard-inspector"><div class="studio-panel-head"><div><span class="axiom-studio-kicker">DIFFUSION</span><h3>État du dashboard</h3></div></div><dl class="dashboard-meta"><dt>Rôle cible</dt><dd><?= $h($dashboard['target_role']?:'Tous les rôles')?></dd><dt>Par défaut</dt><dd><?= (int)$dashboard['is_default']===1?'Oui':'Non'?></dd><dt>Publication</dt><dd><?= $h($dashboard['published_at']?:'Brouillon')?></dd><dt>Rafraîchissement</dt><dd><?= (int)($layout['refresh_interval']??0)>0?(int)$layout['refresh_interval'].' s':'Manuel'?></dd><dt>Widgets</dt><dd><?= count($widgets)?></dd></dl></aside></div>
<script>(function(){const c=document.getElementById('dashboard-canvas'),o=document.getElementById('dashboard-order-json');if(!c||!o)return;let drag=null;c.addEventListener('dragstart',e=>{drag=e.target.closest('.dashboard-widget');if(drag)drag.classList.add('dragging')});c.addEventListener('dragend',()=>{if(drag)drag.classList.remove('dragging');drag=null});c.addEventListener('dragover',e=>{e.preventDefault();const after=[...c.querySelectorAll('.dashboard-widget:not(.dragging)')].find(x=>e.clientY<=x.getBoundingClientRect().top+x.offsetHeight/2);if(drag)c.insertBefore(drag,after||null)});document.getElementById('dashboard-order-form').addEventListener('submit',()=>{o.value=JSON.stringify([...c.querySelectorAll('.dashboard-widget')].map(x=>Number(x.dataset.id)))});document.querySelectorAll('.widget-edit-toggle').forEach(b=>b.addEventListener('click',()=>{const f=b.closest('.dashboard-widget').querySelector('.widget-edit-form');f.hidden=!f.hidden}));document.querySelectorAll('.dashboard-device-switch button').forEach(b=>b.addEventListener('click',()=>{document.querySelectorAll('.dashboard-device-switch button').forEach(x=>x.classList.remove('active'));b.classList.add('active');c.dataset.device=b.dataset.device}));})();</script>
<?php endif;?></main>
