// Carousel preview + edit. Phone frame on left, slide list on right, edit mode.
const { useState: useStateP, useRef: useRefP } = React;
const SLIDE_KINDS = [
{ id: 'cover', name: 'Обложка', icon: 'Cover' },
{ id: 'header', name: 'Заголовок', icon: 'Type' },
{ id: 'number', name: 'Цифра', icon: 'Number' },
{ id: 'quote', name: 'Цитата', icon: 'Quote' },
{ id: 'body', name: 'Текст', icon: 'Stack' },
];
function Preview({ org, slides, setSlides, onBack, onSaved }) {
const { Button, IconButton, Card, Pill, Modal } = window.UI;
const I = window.Icons;
const { SlideFrame } = window.Carousel;
const [active, setActive] = useStateP(0);
const [editing, setEditing] = useStateP(false);
const [tgOpen, setTgOpen] = useStateP(false);
const [downloadOpen, setDownloadOpen] = useStateP(false);
const [savedTip, setSavedTip] = useStateP(false);
const slide = slides[active];
const updateSlide = (patch) => {
setSlides(slides.map((s, i) => i === active ? { ...s, ...patch } : s));
};
const regenerateOne = () => {
// Mock: shuffle small details to feel "regenerated"
setSlides(slides.map((s, i) => i === active ? { ...s, body: s.body ? s.body + ' ' : s.body, label: s.label } : s));
};
return (
{/* Top actions */}
{savedTip && (
Черновик сохранён
)}
{/* Phone preview */}
{slides.map((s, i) => (
))}
{/* nav arrows */}
{/* dots */}
{slides.map((_, i) => (
))}
{String(active + 1).padStart(2, '0')} / {String(slides.length).padStart(2, '0')}
{/* Slide list + editor */}
{/* Slide thumbnails */}
Слайды · {slides.length}
{slides.map((s, i) => (
))}
{/* Edit panel */}
{editing ? (
Редактировать слайд {active + 1}
Тип
{SLIDE_KINDS.map((k) => {
const Icon = I[k.icon];
const active2 = slide.kind === k.id;
return (
);
})}
{(slide.kind === 'cover' || slide.kind === 'header' || slide.kind === 'body') && (
updateSlide({ title: v })}/>
)}
{slide.kind === 'cover' && (
updateSlide({ subtitle: v })} multi/>
)}
{slide.kind === 'number' && (
<>
updateSlide({ number: v })}/>
updateSlide({ body: v })} multi/>
>
)}
{slide.kind === 'quote' && (
<>
updateSlide({ body: v })} multi/>
updateSlide({ author: v })}/>
>
)}
{slide.kind === 'header' && (
updateSlide({ body: v })} multi/>
)}
{slide.kind === 'body' && (
Пункты
{(slide.items || []).map((it, i) => (
updateSlide({ items: slide.items.map((x, idx) => idx === i ? e.target.value : x) })}
className="flex-1 h-9 px-3 rounded-lg border border-ink-200 text-[13px] focus:outline-none focus:border-ink-900"/>
updateSlide({ items: slide.items.filter((_, idx) => idx !== i) })}>
))}
)}
updateSlide({ label: v })}/>
) : (
Сводка
)}
{/* Download modal */}
setDownloadOpen(false)} title="ZIP с PNG-слайдами" sub="Можно загружать прямо в Instagram-карусель.">
{slides[0].title?.toLowerCase().replace(/[^a-zа-я0-9]+/gi, '-') || 'carousel'}.zip
{slides.length} файлов · ~{(slides.length * 0.4).toFixed(1)} МБ
{/* Telegram modal */}
setTgOpen(false)} title="Отправить в Telegram" sub="Бот пришлёт все слайды отдельным альбомом в @username">
@omneee_bot
Подключён к этой организации
);
}
function FieldText({ label, value, onChange, multi = false }) {
return (
);
}
function PhoneFrame({ children }) {
return (
{/* notch */}
{/* status bar */}
9:41
•••
{/* IG-like top */}
{/* content area */}
{children}
);
}
window.Screens = window.Screens || {};
window.Screens.Preview = Preview;