fix: remove fallback content, fix video upload and positioning
- Remove hardcoded fallback data — DB is sole content source - Sections render conditionally when data exists - Hero video slots save after each upload (not only when all 3 filled) - Video positions preserved (left/center/right) with empty string slots - Client-side 10MB hard limit on video uploads with clear error - Server-side upload error handling for body size limits - Guard Team section against empty members array - Clean up old uploaded images and videos
This commit is contained in:
+19
-15
@@ -6,7 +6,7 @@ import { InputField } from "../_components/FormField";
|
||||
import { adminFetch } from "@/lib/csrf";
|
||||
import { Upload, X, Loader2, Smartphone, Monitor, Star } from "lucide-react";
|
||||
|
||||
const MAX_VIDEO_SIZE_MB = 8;
|
||||
const MAX_VIDEO_SIZE_MB = 10;
|
||||
const MAX_VIDEO_SIZE_BYTES = MAX_VIDEO_SIZE_MB * 1024 * 1024;
|
||||
|
||||
function formatFileSize(bytes: number): string {
|
||||
@@ -209,10 +209,8 @@ function VideoManager({
|
||||
const syncToParent = useCallback(
|
||||
(updated: (string | null)[]) => {
|
||||
setSlots(updated);
|
||||
// Only propagate when all 3 are filled
|
||||
if (updated.every((s) => s !== null)) {
|
||||
onChange(updated as string[]);
|
||||
}
|
||||
// Save all 3 slots (empty string for unfilled) to preserve positions
|
||||
onChange(updated.map((s) => s || ""));
|
||||
},
|
||||
[onChange]
|
||||
);
|
||||
@@ -246,10 +244,10 @@ function VideoManager({
|
||||
async function handleUpload(idx: number, file: File) {
|
||||
if (file.size > MAX_VIDEO_SIZE_BYTES) {
|
||||
const sizeMb = (file.size / (1024 * 1024)).toFixed(1);
|
||||
setSizeWarning(`Видео ${sizeMb} МБ — рекомендуем до ${MAX_VIDEO_SIZE_MB} МБ для быстрой загрузки`);
|
||||
} else {
|
||||
setSizeWarning(null);
|
||||
alert(`Видео ${sizeMb} МБ — максимум ${MAX_VIDEO_SIZE_MB} МБ. Сожмите видео и попробуйте снова.`);
|
||||
return;
|
||||
}
|
||||
setSizeWarning(null);
|
||||
setUploadingIdx(idx);
|
||||
try {
|
||||
const form = new FormData();
|
||||
@@ -260,14 +258,21 @@ function VideoManager({
|
||||
body: form,
|
||||
});
|
||||
if (!res.ok) {
|
||||
const err = await res.json();
|
||||
alert(err.error || "Ошибка загрузки");
|
||||
const text = await res.text();
|
||||
let msg = "Ошибка загрузки";
|
||||
try {
|
||||
const err = JSON.parse(text);
|
||||
msg = err.error || msg;
|
||||
} catch { /* empty response */ }
|
||||
alert(`${msg} (${res.status})`);
|
||||
return;
|
||||
}
|
||||
const { path } = await res.json();
|
||||
const updated = [...slots];
|
||||
updated[idx] = path;
|
||||
syncToParent(updated);
|
||||
} catch (e) {
|
||||
alert(`Ошибка сети: ${e instanceof Error ? e.message : "попробуйте снова"}`);
|
||||
} finally {
|
||||
setUploadingIdx(null);
|
||||
}
|
||||
@@ -276,8 +281,7 @@ function VideoManager({
|
||||
function handleRemove(idx: number) {
|
||||
const updated = [...slots];
|
||||
updated[idx] = null;
|
||||
setSlots(updated);
|
||||
// Don't propagate incomplete state — keep old saved videos in DB
|
||||
syncToParent(updated);
|
||||
}
|
||||
|
||||
const allFilled = slots.every((s) => s !== null);
|
||||
@@ -351,17 +355,17 @@ export default function HeroEditorPage() {
|
||||
|
||||
<InputField
|
||||
label="Заголовок"
|
||||
value={data.headline}
|
||||
value={data.headline || ""}
|
||||
onChange={(v) => update({ ...data, headline: v })}
|
||||
/>
|
||||
<InputField
|
||||
label="Подзаголовок"
|
||||
value={data.subheadline}
|
||||
value={data.subheadline || ""}
|
||||
onChange={(v) => update({ ...data, subheadline: v })}
|
||||
/>
|
||||
<InputField
|
||||
label="Текст кнопки"
|
||||
value={data.ctaText}
|
||||
value={data.ctaText || ""}
|
||||
onChange={(v) => update({ ...data, ctaText: v })}
|
||||
/>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user