feat #58 : wizard multi-étapes création/édition d'article

Remplace le formulaire unique par un wizard 5 étapes (création) et
6 étapes (édition) avec auto-sauvegarde en brouillon, détection de
tags depuis le texte (TagSuggester), aperçu SEO, diff avant validation
et plan Markdown dynamique dans l'éditeur.

Détail des changements :
- ArticleManager : +6 méthodes (updatePartialMeta, saveDraftOverlay,
  getDraftOverlay, hasDraftOverlay, discardDraftOverlay, commitDraftOverlay)
- .htaccess : routes /new/{uuid}/{1-5} et /edit/{uuid}/{1-6}
- index.php : cases create et edit réécrits en switch($step),
  nouveau case autosave_draft et edit_discard_draft
- assets/js/wizard.js : autosave debounce, auto-resize textarea,
  scroll curseur, plan TOC dynamique, toggle pills tags
- templates/wizard/ : nav.php + step1..6.php

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-14 21:46:11 +02:00
parent 24bb244352
commit 6895a3bf65
11 changed files with 1422 additions and 166 deletions
+104
View File
@@ -0,0 +1,104 @@
<?php
// Attendu (edit only) : $uuid, $step, $totalSteps, $mode='edit', $article (original),
// $draftData, $diffLines, $changes, $autoRevisionComment,
// $seoTitle, $seoDescription, $autoSeoDesc, $title (draft), $postSlug,
// $titleChanged, $autoSlug, $published, $published_at, $category
ob_start();
$_CONTEXT = 3;
$_backUrl = '/edit/' . rawurlencode($uuid) . '/5';
$_formAction = '/edit/' . rawurlencode($uuid) . '/6';
$_slugFinal = ($titleChanged && $autoSlug !== $postSlug) ? $autoSlug : $postSlug;
?>
<?php include __DIR__ . '/nav.php'; ?>
<!-- En-tête : titre + boutons à droite ─────────────────────────────────── -->
<form method="POST" action="<?= htmlspecialchars($_formAction) ?>">
<input type="hidden" name="_confirm" value="1">
<input type="hidden" name="slug" value="<?= htmlspecialchars($_slugFinal) ?>">
<div class="d-flex align-items-start justify-content-between gap-3 mb-4 flex-wrap">
<div>
<h1 class="h4 mb-1">Confirmer les modifications</h1>
<?php if (!empty($changes)): ?>
<p class="text-muted small mb-0"><?= htmlspecialchars(ucfirst(implode(' · ', $changes))) ?></p>
<?php else: ?>
<p class="text-muted small mb-0">Aucune modification détectée.</p>
<?php endif; ?>
</div>
<div class="d-flex gap-2 flex-wrap align-items-center">
<a href="<?= htmlspecialchars($_backUrl) ?>" class="btn btn-outline-secondary btn-sm">← Retour</a>
<button type="button" class="btn btn-outline-danger btn-sm"
onclick="if(confirm('Abandonner les modifications et supprimer ce brouillon ?')) window.location='/edit/<?= rawurlencode($uuid) ?>/discard'">
Abandonner
</button>
<button type="submit" class="btn btn-success">✓ Confirmer et enregistrer</button>
</div>
</div>
<!-- Commentaire de révision ────────────────────────────────────────────── -->
<div class="mb-4" style="max-width:520px">
<label for="revision_comment" class="form-label fw-semibold">
Commentaire de révision <small class="text-muted fw-normal">(optionnel)</small>
</label>
<input type="text" class="form-control" id="revision_comment" name="revision_comment"
value="<?= htmlspecialchars($autoRevisionComment) ?>"
placeholder="ex. Correction typos, ajout section X…">
</div>
<!-- Diff contenu ────────────────────────────────────────────────────────── -->
<div class="mb-4">
<h2 class="h6 fw-semibold mb-2">Diff du contenu</h2>
<?php if ($diffLines === []): ?>
<div class="text-muted small">Contenu identique.</div>
<?php else:
$total = count($diffLines);
$show = [];
for ($i = 0; $i < $total; $i++) {
if ($diffLines[$i][0] !== '=') {
for ($c = max(0, $i - $_CONTEXT); $c <= min($total - 1, $i + $_CONTEXT); $c++) {
$show[$c] = true;
}
}
}
?>
<div class="d-flex gap-3 mb-1 small">
<span class="diff-del px-2 py-1 rounded"> Supprimé</span>
<span class="diff-ins px-2 py-1 rounded">+ Ajouté</span>
</div>
<div class="diff-view font-monospace small">
<?php $inEllipsis = false;
for ($i = 0; $i < $total; $i++):
[$op, $line] = $diffLines[$i];
?>
<?php if (!isset($show[$i])): ?>
<?php if (!$inEllipsis): $inEllipsis = true; ?>
<div class="diff-ellipsis text-muted px-2">⋯</div>
<?php endif; continue; ?>
<?php else: $inEllipsis = false; endif; ?>
<?php if ($op === '-'): ?>
<div class="diff-del px-2">&nbsp;<?= htmlspecialchars($line) ?></div>
<?php elseif ($op === '+'): ?>
<div class="diff-ins px-2">+&nbsp;<?= htmlspecialchars($line) ?></div>
<?php elseif ($op === '!'): ?>
<div class="diff-warning text-warning px-2"><?= htmlspecialchars($line) ?></div>
<?php else: ?>
<div class="diff-eq px-2 text-muted">&nbsp;&nbsp;<?= htmlspecialchars($line) ?></div>
<?php endif; ?>
<?php endfor; ?>
</div>
<?php endif; ?>
</div>
</form>
<style>
.diff-view{border:1px solid var(--bs-border-color,#dee2e6);border-radius:6px;overflow-x:auto}
.diff-view > div{padding:1px 8px;white-space:pre;line-height:1.5}
.diff-del{background:#ffeef0;color:#b91c1c}
.diff-ins{background:#e6ffec;color:#15803d}
.diff-ellipsis{background:#f8f9fa;padding:2px 8px;user-select:none}
</style>
<?php
$content = ob_get_clean();
$title = 'Valider les modifications — Étape 6/6';
include BASE_PATH . '/templates/layout.php';