true, 'data' => $data], JSON_UNESCAPED_UNICODE); exit; } function err(string $msg, int $code = 400): never { http_response_code($code); header('Content-Type: application/json'); echo json_encode(['ok' => false, 'error' => $msg]); exit; } function fmtPrice(float $p): string { return number_format($p, 2, ',', ' ') . ' €'; } function imgExts(): array { return ['jpg','jpeg','png','gif','webp','avif']; } function vidExts(): array { return ['mp4','webm','mov']; } function isImage(string $f): bool { return in_array(strtolower(pathinfo($f, PATHINFO_EXTENSION)), imgExts()); } function isVideo(string $f): bool { return in_array(strtolower(pathinfo($f, PATHINFO_EXTENSION)), vidExts()); } function handleUpload(string $field, string $dir): ?array { if (!isset($_FILES[$field]) || $_FILES[$field]['error'] !== UPLOAD_ERR_OK) return null; $orig = $_FILES[$field]['name']; $ext = strtolower(pathinfo($orig, PATHINFO_EXTENSION)); $ok = array_merge(imgExts(), vidExts()); if (!in_array($ext, $ok)) return null; $fname = uniqid('m', true) . '.' . $ext; if (!move_uploaded_file($_FILES[$field]['tmp_name'], $dir . $fname)) return null; return ['filename' => $fname, 'original_name' => $orig, 'size' => filesize($dir . $fname)]; } function statusBadge(string $s): string { $map = [ 'brouillon' => ['label' => 'Brouillon', 'class' => 'badge-muted'], 'prêt' => ['label' => 'Prêt', 'class' => 'badge-volt'], 'publié' => ['label' => 'Publié', 'class' => 'badge-green'], ]; $b = $map[$s] ?? ['label' => $s, 'class' => 'badge-muted']; return '' . h($b['label']) . ''; }