feat: lista platnosti akcie nad produktmi, platnost na kartach predvolene skryta
- novy blok Lista platnosti akcie (vzhlad ako oznamovacia lista, bez badge a CTA): text + globalna platnost, da sa skryt a posuvat, nie zmazat; do existujucich kampani sa vlozi automaticky nad prvu produktovu sekciu - platnost na kartach produktov sa da zobrazit prepinacom (predvolene skryta), produkty s vlastnou platnostou ju zobrazia vzdy Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5f6a39debe
commit
98fdad535d
5 files changed
+213
-44
No files matched your search
+77
-29
@@ -2,8 +2,8 @@
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { NewsletterCampaign, NewsletterModule, ProductItem, BadgeTemplate } from '@/types/newsletter';
|
||||
import { DEFAULT_CAMPAIGN, SUPABASE_ASSETS_BASE } from '@/lib/defaultCampaign';
|
||||
import { renderEmailHtml, renderSingleModuleHtml } from '@/lib/emailRenderer';
|
||||
import { DEFAULT_CAMPAIGN, SUPABASE_ASSETS_BASE, ensureValidityBar } from '@/lib/defaultCampaign';
|
||||
import { renderEmailHtml, renderSingleModuleHtml, getCampaignValidity } from '@/lib/emailRenderer';
|
||||
import { supabase } from '@/lib/supabase';
|
||||
import { DEFAULT_BADGE_TEMPLATES } from '@/lib/badgeUtils';
|
||||
import { getBrevoCampaignUrl } from '@/lib/brevo';
|
||||
@@ -142,7 +142,7 @@ export default function Home() {
|
||||
preheader: dbItem.data.preheader || DEFAULT_CAMPAIGN.preheader,
|
||||
status: dbItem.status,
|
||||
brevoCampaignId: dbItem.brevo_campaign_id,
|
||||
modules: enforceAnchors(dbItem.data.modules)
|
||||
modules: enforceAnchors(ensureValidityBar(dbItem.data.modules))
|
||||
});
|
||||
if (dbItem.brevo_campaign_id) {
|
||||
setBrevoUrl(getBrevoCampaignUrl(dbItem.brevo_campaign_id));
|
||||
@@ -234,7 +234,7 @@ export default function Home() {
|
||||
// Duplikácia: Ukotvené moduly sa nedajú duplikovať
|
||||
const duplicateModule = (index: number) => {
|
||||
const original = campaign.modules[index];
|
||||
if (isAnchoredModule(original)) return;
|
||||
if (isAnchoredModule(original) || original.type === 'validity_bar') return;
|
||||
|
||||
const clone: NewsletterModule = {
|
||||
...original,
|
||||
@@ -256,6 +256,10 @@ export default function Home() {
|
||||
alert('Hlavičku, letáky, články/recepty ani pätičku nie je možné vymazať.');
|
||||
return;
|
||||
}
|
||||
if (mod.type === 'validity_bar') {
|
||||
alert('Lištu platnosti akcie nie je možné vymazať, dá sa len skryť.');
|
||||
return;
|
||||
}
|
||||
if (campaign.modules.length <= 1) return;
|
||||
|
||||
const newModules = campaign.modules.filter((_, i) => i !== index);
|
||||
@@ -591,14 +595,15 @@ export default function Home() {
|
||||
}));
|
||||
};
|
||||
|
||||
// Globálna platnosť akcie: prepíše sa do všetkých produktových sekcií aj vernostných zliav
|
||||
// Spoločné nastavenia (globálna platnosť akcie, zobrazenie platnosti na kartách) sa prepíšu
|
||||
// do všetkých produktových sekcií aj vernostných zliav
|
||||
// (produkty s vlastnou platnosťou validityOverride si ju ponechajú)
|
||||
const handleSetGlobalValidity = (validity: string) => {
|
||||
const handleUpdateAllProductSections = (patch: Record<string, unknown>) => {
|
||||
setCampaign((prev) => ({
|
||||
...prev,
|
||||
modules: prev.modules.map((m) =>
|
||||
m.type === 'products_grid' || m.type === 'loyalty_club'
|
||||
? { ...m, data: { ...m.data, globalValidity: validity } }
|
||||
? { ...m, data: { ...m.data, ...patch } }
|
||||
: m
|
||||
)
|
||||
}));
|
||||
@@ -704,6 +709,26 @@ export default function Home() {
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{mod.type === 'validity_bar' && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleUpdateModule({
|
||||
...mod,
|
||||
data: { ...mod.data, enabled: mod.data.enabled === false }
|
||||
});
|
||||
}}
|
||||
className={`text-[9px] font-bold px-2 py-0.5 rounded-full border transition-all mr-1 ${
|
||||
mod.data.enabled === false
|
||||
? 'bg-red-500/20 text-red-300 border-red-400/30 hover:bg-red-500/30'
|
||||
: 'bg-emerald-500/20 text-emerald-300 border-emerald-400/30 hover:bg-emerald-500/30'
|
||||
}`}
|
||||
title={mod.data.enabled === false ? 'Zobraziť lištu v e-maile' : 'Skryť lištu v e-maile'}
|
||||
>
|
||||
{mod.data.enabled === false ? 'Skrytá (zapnúť)' : 'Zobrazená'}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
@@ -726,26 +751,30 @@ export default function Home() {
|
||||
>
|
||||
<ChevronDown className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
duplicateModule(idx);
|
||||
}}
|
||||
className="hover:text-blue-400 p-1 ml-1"
|
||||
title="Duplikovať sekciu"
|
||||
>
|
||||
<Copy className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
deleteModule(idx);
|
||||
}}
|
||||
className="hover:text-red-400 p-1 ml-1"
|
||||
title="Zmazať sekciu"
|
||||
>
|
||||
<Trash2 className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
{mod.type !== 'validity_bar' && (
|
||||
<>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
duplicateModule(idx);
|
||||
}}
|
||||
className="hover:text-blue-400 p-1 ml-1"
|
||||
title="Duplikovať sekciu"
|
||||
>
|
||||
<Copy className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
deleteModule(idx);
|
||||
}}
|
||||
className="hover:text-red-400 p-1 ml-1"
|
||||
title="Zmazať sekciu"
|
||||
>
|
||||
<Trash2 className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
@@ -770,6 +799,24 @@ export default function Home() {
|
||||
Aktivovať vernostné zľavy
|
||||
</button>
|
||||
</div>
|
||||
) : mod.type === 'validity_bar' && mod.data.enabled === false ? (
|
||||
<div className="p-4 my-2 border-2 border-dashed border-red-500/40 bg-red-950/30 rounded-xl text-center">
|
||||
<p className="text-xs font-bold text-red-300 uppercase tracking-wider">
|
||||
Lišta platnosti akcie (Skrytá)
|
||||
</p>
|
||||
<p className="text-[11px] text-gray-300 mt-0.5">
|
||||
Táto lišta je v e-maile skrytá. Kliknutím sem ju môžete aktivovať.
|
||||
</p>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleUpdateModule({ ...mod, data: { ...mod.data, enabled: true } });
|
||||
}}
|
||||
className="mt-2 px-3 py-1 bg-red-600 hover:bg-red-700 text-white text-xs font-bold rounded-lg transition-colors shadow-sm"
|
||||
>
|
||||
Aktivovať lištu platnosti
|
||||
</button>
|
||||
</div>
|
||||
) : mod.type === 'announcement' && mod.data.enabled === false ? (
|
||||
<div className="p-4 my-2 border-2 border-dashed border-red-500/40 bg-red-950/30 rounded-xl text-center">
|
||||
<p className="text-xs font-bold text-red-300 uppercase tracking-wider">
|
||||
@@ -808,7 +855,8 @@ export default function Home() {
|
||||
module={selectedModule}
|
||||
onUpdateModule={handleUpdateModule}
|
||||
onApplyBadgeToAll={handleApplyBadgeToAll}
|
||||
onSetGlobalValidity={handleSetGlobalValidity}
|
||||
onUpdateAllProductSections={handleUpdateAllProductSections}
|
||||
campaignValidity={getCampaignValidity(campaign)}
|
||||
availableBadges={availableBadges}
|
||||
onOpenBadgeManager={(pId) => {
|
||||
setTargetProductIdForBadge(pId);
|
||||
@@ -846,7 +894,7 @@ export default function Home() {
|
||||
onClose={() => setIsSavedCampaignsOpen(false)}
|
||||
currentCampaign={campaign}
|
||||
onLoadCampaign={(selected: NewsletterCampaign) => {
|
||||
setCampaign({ ...selected, modules: enforceAnchors(selected.modules) });
|
||||
setCampaign({ ...selected, modules: enforceAnchors(ensureValidityBar(selected.modules)) });
|
||||
setSelectedModuleId(selected.modules[1]?.id || selected.modules[0]?.id || null);
|
||||
if (selected.brevoCampaignId) {
|
||||
setBrevoUrl(getBrevoCampaignUrl(selected.brevoCampaignId));
|
||||
|
||||
Reference in new issue
Block a user