feat: drag and drop text layers in badge editor, anchor flyers/recipes above footer, and editable titles in flyers section
This commit is contained in:
1 parent
99dd0f272e
commit
f9aa2297ba
3 files changed
+205
-43
No files matched your search
+56
-29
@@ -27,15 +27,31 @@ export default function Home() {
|
||||
const [isSavedCampaignsOpen, setIsSavedCampaignsOpen] = useState(false);
|
||||
const [targetProductIdForBadge, setTargetProductIdForBadge] = useState<string | null>(null);
|
||||
|
||||
// Pomocné funkcie pre fixné ukotvenie hlavičky a pätičky
|
||||
const isTopModule = (type: string) => type === 'header' || type === 'announcement';
|
||||
const isBottomModule = (type: string) => type === 'footer';
|
||||
const isAnchoredModule = (type: string) => isTopModule(type) || isBottomModule(type);
|
||||
// Pomocné funkcie pre fixné ukotvenie hlavičky, spodných letákov, receptov a pätičky
|
||||
const isTopModule = (m: NewsletterModule) => m.type === 'header' || m.type === 'announcement';
|
||||
|
||||
const isFlyersModule = (m: NewsletterModule) =>
|
||||
m.id === 'mod-flyers-row' ||
|
||||
(m.type === 'articles_row' && m.title?.toLowerCase().includes('leták'));
|
||||
|
||||
const isRecipesModule = (m: NewsletterModule) =>
|
||||
m.id === 'mod-recipes-row' ||
|
||||
(m.type === 'articles_row' &&
|
||||
(m.title?.toLowerCase().includes('recept') || m.title?.toLowerCase().includes('článk')));
|
||||
|
||||
const isFooterModule = (m: NewsletterModule) => m.type === 'footer';
|
||||
|
||||
const isBottomModule = (m: NewsletterModule) =>
|
||||
isFlyersModule(m) || isRecipesModule(m) || isFooterModule(m);
|
||||
|
||||
const isAnchoredModule = (m: NewsletterModule) => isTopModule(m) || isBottomModule(m);
|
||||
|
||||
const enforceAnchors = (mods: NewsletterModule[]): NewsletterModule[] => {
|
||||
const topMods = mods.filter((m) => isTopModule(m.type));
|
||||
const bottomMods = mods.filter((m) => isBottomModule(m.type));
|
||||
const middleMods = mods.filter((m) => !isAnchoredModule(m.type));
|
||||
const topMods = mods.filter((m) => isTopModule(m));
|
||||
const flyerMods = mods.filter((m) => isFlyersModule(m));
|
||||
const recipeMods = mods.filter((m) => isRecipesModule(m));
|
||||
const footerMods = mods.filter((m) => isFooterModule(m));
|
||||
const middleMods = mods.filter((m) => !isAnchoredModule(m));
|
||||
|
||||
// announcement je pred headerom, ak existujú oba
|
||||
topMods.sort((a, b) => {
|
||||
@@ -44,7 +60,7 @@ export default function Home() {
|
||||
return 0;
|
||||
});
|
||||
|
||||
return [...topMods, ...middleMods, ...bottomMods];
|
||||
return [...topMods, ...middleMods, ...flyerMods, ...recipeMods, ...footerMods];
|
||||
};
|
||||
|
||||
// Načítanie existujúceho draftu z databázy pri štarte
|
||||
@@ -135,14 +151,14 @@ export default function Home() {
|
||||
}));
|
||||
};
|
||||
|
||||
// Posun modulu: Hlavička a pätička sú ukotvené a bežné moduly sa nemôžu dostať pred hlavičku ani za pätičku
|
||||
// Posun modulu: Hlavička, letáky, recepty a pätička sú ukotvené a bežné moduly sa nemôžu dostať mimo vyhradený stred
|
||||
const moveModule = (index: number, direction: 'up' | 'down') => {
|
||||
const currentMod = campaign.modules[index];
|
||||
if (!currentMod || isAnchoredModule(currentMod.type)) return;
|
||||
if (!currentMod || isAnchoredModule(currentMod)) return;
|
||||
|
||||
const topCount = campaign.modules.filter((m) => isTopModule(m.type)).length;
|
||||
const footerIndex = campaign.modules.findIndex((m) => isBottomModule(m.type));
|
||||
const maxAllowedIndex = footerIndex !== -1 ? footerIndex - 1 : campaign.modules.length - 1;
|
||||
const topCount = campaign.modules.filter((m) => isTopModule(m)).length;
|
||||
const firstBottomIndex = campaign.modules.findIndex((m) => isBottomModule(m));
|
||||
const maxAllowedIndex = firstBottomIndex !== -1 ? firstBottomIndex - 1 : campaign.modules.length - 1;
|
||||
|
||||
const targetIndex = direction === 'up' ? index - 1 : index + 1;
|
||||
if (targetIndex < topCount || targetIndex > maxAllowedIndex) return;
|
||||
@@ -155,10 +171,10 @@ export default function Home() {
|
||||
setCampaign((prev) => ({ ...prev, modules: enforceAnchors(newModules) }));
|
||||
};
|
||||
|
||||
// Duplikácia: Hlavička a pätička sa nedajú duplikovať
|
||||
// Duplikácia: Ukotvené moduly sa nedajú duplikovať
|
||||
const duplicateModule = (index: number) => {
|
||||
const original = campaign.modules[index];
|
||||
if (isAnchoredModule(original.type)) return;
|
||||
if (isAnchoredModule(original)) return;
|
||||
|
||||
const clone: NewsletterModule = {
|
||||
...original,
|
||||
@@ -173,10 +189,13 @@ export default function Home() {
|
||||
setSelectedModuleId(clone.id);
|
||||
};
|
||||
|
||||
// Zmazanie: Hlavička a pätička sa nedajú vymazať
|
||||
// Zmazanie: Ukotvené moduly (hlavička, letáky, články/recepty, pätička) sa nedajú vymazať
|
||||
const deleteModule = (index: number) => {
|
||||
const mod = campaign.modules[index];
|
||||
if (isAnchoredModule(mod.type)) return;
|
||||
if (isAnchoredModule(mod)) {
|
||||
alert('Hlavičku, letáky, články/recepty ani pätičku nie je možné vymazať.');
|
||||
return;
|
||||
}
|
||||
if (campaign.modules.length <= 1) return;
|
||||
|
||||
const newModules = campaign.modules.filter((_, i) => i !== index);
|
||||
@@ -184,7 +203,7 @@ export default function Home() {
|
||||
setSelectedModuleId(newModules[0]?.id || null);
|
||||
};
|
||||
|
||||
// Pridanie modulu: Vloží sa vždy PRED pätičku (pätička ostáva pevne na spodku)
|
||||
// Pridanie modulu: Vloží sa vždy PRED spodné bloky (letáky, recepty a pätičku)
|
||||
const handleAddModule = (type: any) => {
|
||||
let newMod: NewsletterModule;
|
||||
if (type === 'banner') {
|
||||
@@ -236,10 +255,10 @@ export default function Home() {
|
||||
}
|
||||
|
||||
setCampaign((prev) => {
|
||||
const footerIdx = prev.modules.findIndex((m) => isBottomModule(m.type));
|
||||
const firstBottomIdx = prev.modules.findIndex((m) => isBottomModule(m));
|
||||
const newModules = [...prev.modules];
|
||||
if (footerIdx !== -1) {
|
||||
newModules.splice(footerIdx, 0, newMod);
|
||||
if (firstBottomIdx !== -1) {
|
||||
newModules.splice(firstBottomIdx, 0, newMod);
|
||||
} else {
|
||||
newModules.push(newMod);
|
||||
}
|
||||
@@ -471,8 +490,8 @@ export default function Home() {
|
||||
});
|
||||
};
|
||||
|
||||
const topCount = campaign.modules.filter((m) => isTopModule(m.type)).length;
|
||||
const footerIdx = campaign.modules.findIndex((m) => isBottomModule(m.type));
|
||||
const topCount = campaign.modules.filter((m) => isTopModule(m)).length;
|
||||
const firstBottomIdx = campaign.modules.findIndex((m) => isBottomModule(m));
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-screen overflow-hidden bg-slate-950 font-sans">
|
||||
@@ -507,12 +526,14 @@ export default function Home() {
|
||||
<div className="w-full max-w-[600px] my-4">
|
||||
{campaign.modules.map((mod, idx) => {
|
||||
const isSelected = mod.id === selectedModuleId;
|
||||
const isAnchored = isAnchoredModule(mod.type);
|
||||
const isTop = isTopModule(mod.type);
|
||||
const isBottom = isBottomModule(mod.type);
|
||||
const isAnchored = isAnchoredModule(mod);
|
||||
const isTop = isTopModule(mod);
|
||||
const isFlyers = isFlyersModule(mod);
|
||||
const isRecipes = isRecipesModule(mod);
|
||||
const isFooter = isFooterModule(mod);
|
||||
|
||||
const canMoveUp = !isAnchored && idx > topCount;
|
||||
const canMoveDown = !isAnchored && idx < (footerIdx !== -1 ? footerIdx - 1 : campaign.modules.length - 1);
|
||||
const canMoveDown = !isAnchored && idx < (firstBottomIdx !== -1 ? firstBottomIdx - 1 : campaign.modules.length - 1);
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -531,11 +552,17 @@ export default function Home() {
|
||||
{mod.title}
|
||||
</span>
|
||||
|
||||
{/* Ak je modul ukotvený (Hlavička / Pätička), nezobrazujeme tlačidlá na presun a zmazanie */}
|
||||
{/* Ak je modul ukotvený (Hlavička / Letáky / Recepty / Pätička), nezobrazujeme tlačidlá na presun a zmazanie */}
|
||||
{isAnchored ? (
|
||||
<span className="text-[9px] font-bold bg-white/10 text-amber-300 px-2 py-0.5 rounded-full border border-white/10 uppercase flex items-center gap-1">
|
||||
<Lock className="w-2.5 h-2.5" />
|
||||
{isBottom ? 'Ukotvená pätička' : 'Ukotvená hlavička'}
|
||||
{isFooter
|
||||
? 'Ukotvená pätička'
|
||||
: isFlyers
|
||||
? 'Ukotvené letáky'
|
||||
: isRecipes
|
||||
? 'Ukotvené recepty'
|
||||
: 'Ukotvená hlavička'}
|
||||
</span>
|
||||
) : (
|
||||
<>
|
||||
|
||||
Reference in new issue
Block a user