@fayz-ai/plugin-marketing 0.13.17 → 0.13.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1396 -1416
- package/dist/index.js.map +1 -1
- package/dist/views/MarketingPage.d.ts.map +1 -1
- package/dist/views/content/ContentView.d.ts +3 -2
- package/dist/views/content/ContentView.d.ts.map +1 -1
- package/dist/views/content/PostPage.d.ts +3 -3
- package/dist/views/content/PostPage.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { connectionSettings, connectionStatus, settingsStatus, createConnectionStore, getActiveTenantId, registerTranslations, createSafeDataProvider, getSupabaseClientOptional, countByTenant, useTranslation, useDataChanged, resolveDataProvider } from '@fayz-ai/core';
|
|
1
|
+
import React12, { useState, useEffect, useMemo, useCallback } from 'react';
|
|
2
|
+
import { connectionSettings, connectionStatus, settingsStatus, createConnectionStore, getActiveTenantId, registerTranslations, createSafeDataProvider, getSupabaseClientOptional, countByTenant, useTranslation, useDataChanged, getCurrentLocale, resolveDataProvider } from '@fayz-ai/core';
|
|
3
3
|
import { createPluginContext, fayzConnectorOAuth, PluginSettingsPanel, useModuleNavigation, createViewRouter, ModuleActionBar, SettingsGroup, SelectRow, ToggleRow, askAI, PageActions, useLimitGuard, useOrganizationStore, CrudFormPage, RightRailPage, invalidateLimit, CrudListView } from '@fayz-ai/admin';
|
|
4
|
-
import { defineKpiWidget, defineCustomWidget, defineTableWidget, ModulePage, PageToolbar, DataTable, SubpageHeader, RowActions, PageHeaderActions, Button, Skeleton, ConfirmDialog,
|
|
4
|
+
import { defineKpiWidget, defineCustomWidget, defineTableWidget, ModulePage, PageToolbar, DataTable, SubpageHeader, RowActions, PageHeaderActions, Button, Skeleton, ConfirmDialog, ComingSoon, DashboardCanvas, RangeControl, cn, Modal, ModalContent, ModalHeader, ModalTitle, ModalBody, SegmentedControl, Input, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, ModalFooter, SheetBody, Badge, SheetFooter, SelectSeparator, Checkbox, MarkdownEditor, toast, KpiCard as KpiCard$1, Card, CardHeader, CardTitle, CardContent, TableWidget } from '@fayz-ai/ui';
|
|
5
5
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
6
|
-
import { History, CheckCircle2, AlertCircle, ChevronRight, Users, Target, Percent, DollarSign, Trash2, WandSparkles, SlidersHorizontal, Workflow, ExternalLink, Sparkles, Pencil, Plus, Layers, MousePointerClick, NotebookPen, ArrowUpRight, ArrowDownRight, Send, Megaphone, Settings2, Columns3, CalendarDays, List, ChevronLeft,
|
|
6
|
+
import { History, CheckCircle2, AlertCircle, ChevronRight, Users, Target, Percent, DollarSign, Trash2, WandSparkles, SlidersHorizontal, Workflow, ExternalLink, Sparkles, Pencil, Plus, Layers, MousePointerClick, NotebookPen, ArrowUpRight, ArrowDownRight, Send, Megaphone, Settings2, Columns3, CalendarDays, List, ChevronLeft, X, Bike, Utensils, DoorOpen, MessageCircle, Instagram, Mail, Globe, Search, Link2, AtSign, Pin, Twitter, Linkedin, Facebook, Youtube, Music2, GripVertical, Loader2, ImagePlus, Repeat2, Heart, BarChart2, Share2, ThumbsUp, MoreHorizontal, Bookmark, Image, Trophy } from 'lucide-react';
|
|
7
7
|
import { createStore } from 'zustand/vanilla';
|
|
8
8
|
|
|
9
9
|
// src/index.ts
|
|
@@ -14,7 +14,7 @@ var useMarketingProvider = ctx.useProvider;
|
|
|
14
14
|
var useMarketingStore = ctx.useStore;
|
|
15
15
|
function useChannelLookup() {
|
|
16
16
|
const { channels } = useMarketingConfig();
|
|
17
|
-
return
|
|
17
|
+
return React12.useMemo(() => new Map(channels.map((c) => [c.id, c])), [channels]);
|
|
18
18
|
}
|
|
19
19
|
function OverviewView() {
|
|
20
20
|
const setRange = useMarketingStore((s) => s.setRange);
|
|
@@ -270,7 +270,7 @@ function ChannelsView({ onOpen }) {
|
|
|
270
270
|
const { conversion, currency } = useMarketingConfig();
|
|
271
271
|
const channels = useMarketingStore((s) => s.channels);
|
|
272
272
|
const sorted = [...channels].sort((a, b) => b.conversions - a.conversions);
|
|
273
|
-
const columns =
|
|
273
|
+
const columns = React12.useMemo(() => [
|
|
274
274
|
{ accessorKey: "channelId", header: t("marketing.col.channel"), cell: ({ getValue }) => /* @__PURE__ */ jsx(ChannelCell, { channelId: getValue() }) },
|
|
275
275
|
{ accessorKey: "reach", header: t("marketing.col.reach"), cell: ({ getValue }) => /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: formatNumber(getValue()) }) },
|
|
276
276
|
{ accessorKey: "leads", header: t("marketing.col.leads"), cell: ({ getValue }) => /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: formatNumber(getValue()) }) },
|
|
@@ -307,13 +307,13 @@ function ChannelDetailView({ channelId, onBack }) {
|
|
|
307
307
|
const channels = useMarketingStore((s) => s.channels);
|
|
308
308
|
const lookup = useChannelLookup();
|
|
309
309
|
const provider2 = useMarketingProvider();
|
|
310
|
-
const [campaigns, setCampaigns] =
|
|
311
|
-
|
|
310
|
+
const [campaigns, setCampaigns] = React12.useState([]);
|
|
311
|
+
React12.useEffect(() => {
|
|
312
312
|
void provider2.listCampaigns({ range, channelId }).then(setCampaigns);
|
|
313
313
|
}, [provider2, range, channelId]);
|
|
314
314
|
const perf = channels.find((c) => c.channelId === channelId);
|
|
315
315
|
const channel = lookup.get(channelId);
|
|
316
|
-
const columns =
|
|
316
|
+
const columns = React12.useMemo(() => [
|
|
317
317
|
{ accessorKey: "name", header: t("marketing.col.campaign"), cell: ({ getValue }) => /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: getValue() }) },
|
|
318
318
|
{ accessorKey: "conversions", header: conversion.labelPlural, cell: ({ getValue }) => /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: formatNumber(getValue()) }) },
|
|
319
319
|
{ id: "cvr", accessorFn: (c) => c.reach > 0 ? c.conversions / c.reach * 100 : 0, header: t("marketing.col.cvr"), cell: ({ getValue }) => /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: formatPercent(getValue()) }) },
|
|
@@ -352,17 +352,17 @@ function CampaignComposer({ open, onClose, initialSegmentId }) {
|
|
|
352
352
|
const { channels } = useMarketingConfig();
|
|
353
353
|
const provider2 = useMarketingProvider();
|
|
354
354
|
const saveCampaign = useMarketingStore((s) => s.saveCampaign);
|
|
355
|
-
const [kind, setKind] =
|
|
356
|
-
const [name, setName] =
|
|
357
|
-
const [channelId, setChannelId] =
|
|
358
|
-
const [status, setStatus] =
|
|
359
|
-
const [spend, setSpend] =
|
|
360
|
-
const [purpose, setPurpose] =
|
|
361
|
-
const [segmentId, setSegmentId] =
|
|
362
|
-
const [templateKey, setTemplateKey] =
|
|
363
|
-
const [segments, setSegments] =
|
|
364
|
-
const [saving, setSaving] =
|
|
365
|
-
|
|
355
|
+
const [kind, setKind] = React12.useState("ad_spend");
|
|
356
|
+
const [name, setName] = React12.useState("");
|
|
357
|
+
const [channelId, setChannelId] = React12.useState(channels[0]?.id ?? "");
|
|
358
|
+
const [status, setStatus] = React12.useState("draft");
|
|
359
|
+
const [spend, setSpend] = React12.useState("");
|
|
360
|
+
const [purpose, setPurpose] = React12.useState("promotion");
|
|
361
|
+
const [segmentId, setSegmentId] = React12.useState("");
|
|
362
|
+
const [templateKey, setTemplateKey] = React12.useState("");
|
|
363
|
+
const [segments, setSegments] = React12.useState([]);
|
|
364
|
+
const [saving, setSaving] = React12.useState(false);
|
|
365
|
+
React12.useEffect(() => {
|
|
366
366
|
if (!open) return;
|
|
367
367
|
setName("");
|
|
368
368
|
setChannelId(channels[0]?.id ?? "");
|
|
@@ -480,8 +480,8 @@ function CampaignsView() {
|
|
|
480
480
|
const { conversion, currency } = useMarketingConfig();
|
|
481
481
|
const campaigns = useMarketingStore((s) => s.campaigns);
|
|
482
482
|
const deleteCampaign = useMarketingStore((s) => s.deleteCampaign);
|
|
483
|
-
const [composerOpen, setComposerOpen] =
|
|
484
|
-
const columns =
|
|
483
|
+
const [composerOpen, setComposerOpen] = React12.useState(false);
|
|
484
|
+
const columns = React12.useMemo(() => [
|
|
485
485
|
{ accessorKey: "name", header: t("marketing.col.campaign"), cell: ({ getValue }) => /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: getValue() }) },
|
|
486
486
|
{ accessorKey: "channelId", header: t("marketing.col.channel"), cell: ({ getValue }) => /* @__PURE__ */ jsx(ChannelCell, { channelId: getValue() }) },
|
|
487
487
|
{ accessorKey: "reach", header: t("marketing.col.reach"), cell: ({ getValue }) => /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: formatNumber(getValue()) }) },
|
|
@@ -512,7 +512,7 @@ function CampaignsView() {
|
|
|
512
512
|
)
|
|
513
513
|
}
|
|
514
514
|
], [t, conversion.labelPlural, currency, deleteCampaign]);
|
|
515
|
-
const createActions =
|
|
515
|
+
const createActions = React12.useMemo(() => [
|
|
516
516
|
{
|
|
517
517
|
id: "with-ai",
|
|
518
518
|
label: t("assistant.askAI"),
|
|
@@ -618,19 +618,19 @@ function LandingPageComposer({
|
|
|
618
618
|
const sites = useMarketingStore((s) => s.sites);
|
|
619
619
|
const leadTags = useMarketingStore((s) => s.leadTags);
|
|
620
620
|
const saveLandingPage = useMarketingStore((s) => s.saveLandingPage);
|
|
621
|
-
const [siteId, setSiteId] =
|
|
622
|
-
const [name, setName] =
|
|
623
|
-
const [route, setRoute] =
|
|
624
|
-
const [status, setStatus] =
|
|
625
|
-
const [prompt, setPrompt] =
|
|
626
|
-
const [tags, setTags] =
|
|
627
|
-
const [newTag, setNewTag] =
|
|
628
|
-
const [destination, setDestination] =
|
|
629
|
-
const [required, setRequired] =
|
|
630
|
-
const [minH, setMinH] =
|
|
631
|
-
const [maxD, setMaxD] =
|
|
632
|
-
const [saving, setSaving] =
|
|
633
|
-
|
|
621
|
+
const [siteId, setSiteId] = React12.useState("");
|
|
622
|
+
const [name, setName] = React12.useState("");
|
|
623
|
+
const [route, setRoute] = React12.useState("");
|
|
624
|
+
const [status, setStatus] = React12.useState("live");
|
|
625
|
+
const [prompt, setPrompt] = React12.useState("");
|
|
626
|
+
const [tags, setTags] = React12.useState([]);
|
|
627
|
+
const [newTag, setNewTag] = React12.useState("");
|
|
628
|
+
const [destination, setDestination] = React12.useState("lead");
|
|
629
|
+
const [required, setRequired] = React12.useState(["name"]);
|
|
630
|
+
const [minH, setMinH] = React12.useState("");
|
|
631
|
+
const [maxD, setMaxD] = React12.useState("");
|
|
632
|
+
const [saving, setSaving] = React12.useState(false);
|
|
633
|
+
React12.useEffect(() => {
|
|
634
634
|
if (!open) return;
|
|
635
635
|
setSiteId(page?.siteId ?? sites[0]?.id ?? "");
|
|
636
636
|
setName(page?.name ?? "");
|
|
@@ -646,7 +646,7 @@ function LandingPageComposer({
|
|
|
646
646
|
}, [open, page, sites]);
|
|
647
647
|
const site = sites.find((s) => s.id === siteId);
|
|
648
648
|
const previewPath = route.trim() ? route.trim().startsWith("/") ? route.trim() : `/${route.trim()}` : "";
|
|
649
|
-
const options =
|
|
649
|
+
const options = React12.useMemo(() => {
|
|
650
650
|
const byslug = new Map(leadTags.map((tag) => [tag.slug, tag.name]));
|
|
651
651
|
for (const slug of tags) if (!byslug.has(slug)) byslug.set(slug, slug);
|
|
652
652
|
return [...byslug].map(([slug, label]) => ({ slug, label }));
|
|
@@ -856,8 +856,8 @@ function LandingPagesView() {
|
|
|
856
856
|
const t = useTranslation();
|
|
857
857
|
const pages = useMarketingStore((s) => s.landingPages);
|
|
858
858
|
const deleteLandingPage = useMarketingStore((s) => s.deleteLandingPage);
|
|
859
|
-
const [editing, setEditing] =
|
|
860
|
-
const [composerOpen, setComposerOpen] =
|
|
859
|
+
const [editing, setEditing] = React12.useState(null);
|
|
860
|
+
const [composerOpen, setComposerOpen] = React12.useState(false);
|
|
861
861
|
const measured = pages.filter((p) => p.visits !== null);
|
|
862
862
|
const totalVisits = measured.reduce((s, p) => s + (p.visits ?? 0), 0);
|
|
863
863
|
const totalLeads = pages.reduce((s, p) => s + p.conversions, 0);
|
|
@@ -869,7 +869,7 @@ function LandingPagesView() {
|
|
|
869
869
|
setEditing(page);
|
|
870
870
|
setComposerOpen(true);
|
|
871
871
|
}
|
|
872
|
-
const columns =
|
|
872
|
+
const columns = React12.useMemo(() => [
|
|
873
873
|
{
|
|
874
874
|
accessorKey: "name",
|
|
875
875
|
header: t("marketing.col.page"),
|
|
@@ -958,7 +958,7 @@ function LandingPagesView() {
|
|
|
958
958
|
)
|
|
959
959
|
}
|
|
960
960
|
], [t, deleteLandingPage]);
|
|
961
|
-
const createActions =
|
|
961
|
+
const createActions = React12.useMemo(() => [{
|
|
962
962
|
id: "new-page",
|
|
963
963
|
label: t("marketing.landing.new"),
|
|
964
964
|
icon: Plus,
|
|
@@ -1098,9 +1098,9 @@ function Field({ label, children }) {
|
|
|
1098
1098
|
function useBriefDraft() {
|
|
1099
1099
|
const plan = useContentPlannerStore((s) => s.activePlan);
|
|
1100
1100
|
const savePlan = useContentPlannerStore((s) => s.savePlan);
|
|
1101
|
-
const [draft, setDraft] =
|
|
1102
|
-
const [saving, setSaving] =
|
|
1103
|
-
|
|
1101
|
+
const [draft, setDraft] = React12.useState(plan ? toDraft(plan) : null);
|
|
1102
|
+
const [saving, setSaving] = React12.useState(false);
|
|
1103
|
+
React12.useEffect(() => {
|
|
1104
1104
|
if (plan) setDraft(toDraft(plan));
|
|
1105
1105
|
}, [plan?.id]);
|
|
1106
1106
|
const patch = (partial) => setDraft((d) => d ? { ...d, ...partial } : d);
|
|
@@ -1128,7 +1128,7 @@ function useBriefDraft() {
|
|
|
1128
1128
|
}
|
|
1129
1129
|
function BriefFields({ draft, patch }) {
|
|
1130
1130
|
const t = useTranslation();
|
|
1131
|
-
const [pillarInput, setPillarInput] =
|
|
1131
|
+
const [pillarInput, setPillarInput] = React12.useState("");
|
|
1132
1132
|
const addPillar = () => {
|
|
1133
1133
|
const value = pillarInput.trim();
|
|
1134
1134
|
if (!value || draft.pillars.includes(value)) return;
|
|
@@ -1265,1458 +1265,1438 @@ function PlanBriefView({ onBack }) {
|
|
|
1265
1265
|
function PlanBriefSheet({ open, onClose }) {
|
|
1266
1266
|
const t = useTranslation();
|
|
1267
1267
|
const { plan, draft, patch, dirty, saving, save } = useBriefDraft();
|
|
1268
|
-
return /* @__PURE__ */
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
children:
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
]
|
|
1279
|
-
}
|
|
1280
|
-
);
|
|
1268
|
+
return /* @__PURE__ */ jsx(Modal, { open, onOpenChange: (o) => {
|
|
1269
|
+
if (!o) onClose();
|
|
1270
|
+
}, children: /* @__PURE__ */ jsxs(ModalContent, { size: "4xl", children: [
|
|
1271
|
+
/* @__PURE__ */ jsx(ModalHeader, { children: /* @__PURE__ */ jsx(ModalTitle, { children: plan?.name ?? t("marketing.content.brief") }) }),
|
|
1272
|
+
/* @__PURE__ */ jsx(ModalBody, { children: draft ? /* @__PURE__ */ jsx(BriefFields, { draft, patch }) : /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "\u2026" }) }),
|
|
1273
|
+
/* @__PURE__ */ jsxs(ModalFooter, { children: [
|
|
1274
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", onClick: onClose, children: t("common.cancel") }),
|
|
1275
|
+
/* @__PURE__ */ jsx(Button, { onClick: () => void save().then(onClose), disabled: !dirty || saving, children: t("marketing.content.save") })
|
|
1276
|
+
] })
|
|
1277
|
+
] }) });
|
|
1281
1278
|
}
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
var POST_DRAG_MIME = "application/x-mkt-post";
|
|
1285
|
-
function PostCard({ post, onOpen, onDelete }) {
|
|
1286
|
-
const t = useTranslation();
|
|
1287
|
-
const [dragging, setDragging] = React9.useState(false);
|
|
1288
|
-
return /* @__PURE__ */ jsxs(
|
|
1289
|
-
"div",
|
|
1290
|
-
{
|
|
1291
|
-
role: "button",
|
|
1292
|
-
tabIndex: 0,
|
|
1293
|
-
onClick: onOpen,
|
|
1294
|
-
onKeyDown: (e) => {
|
|
1295
|
-
if (e.key === "Enter") onOpen();
|
|
1296
|
-
},
|
|
1297
|
-
draggable: true,
|
|
1298
|
-
onDragStart: (e) => {
|
|
1299
|
-
e.dataTransfer.setData(POST_DRAG_MIME, post.id);
|
|
1300
|
-
e.dataTransfer.effectAllowed = "move";
|
|
1301
|
-
setDragging(true);
|
|
1302
|
-
},
|
|
1303
|
-
onDragEnd: () => setDragging(false),
|
|
1304
|
-
className: cn(
|
|
1305
|
-
"group/post relative w-full cursor-pointer rounded-card border border-border bg-card p-3 pl-7 text-left transition-colors hover:bg-muted/40",
|
|
1306
|
-
dragging && "opacity-50"
|
|
1307
|
-
),
|
|
1308
|
-
children: [
|
|
1309
|
-
/* @__PURE__ */ jsx(
|
|
1310
|
-
GripVertical,
|
|
1311
|
-
{
|
|
1312
|
-
className: "absolute left-1.5 top-3 h-4 w-4 cursor-grab text-muted-foreground/50 opacity-0 transition-opacity group-hover/post:opacity-100",
|
|
1313
|
-
"aria-hidden": true
|
|
1314
|
-
}
|
|
1315
|
-
),
|
|
1316
|
-
onDelete && /* @__PURE__ */ jsx(
|
|
1317
|
-
"button",
|
|
1318
|
-
{
|
|
1319
|
-
type: "button",
|
|
1320
|
-
"aria-label": t("marketing.content.deletePost"),
|
|
1321
|
-
onClick: (e) => {
|
|
1322
|
-
e.stopPropagation();
|
|
1323
|
-
onDelete();
|
|
1324
|
-
},
|
|
1325
|
-
className: "absolute right-1.5 top-2 rounded p-1 text-muted-foreground/60 opacity-0 transition-opacity hover:bg-destructive/10 hover:text-destructive group-hover/post:opacity-100",
|
|
1326
|
-
children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" })
|
|
1327
|
-
}
|
|
1328
|
-
),
|
|
1329
|
-
/* @__PURE__ */ jsx("p", { className: "pr-5 text-sm font-medium text-foreground", children: post.title || t("marketing.content.untitled") }),
|
|
1330
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [
|
|
1331
|
-
/* @__PURE__ */ jsx(FormatBadge, { format: post.format }),
|
|
1332
|
-
/* @__PURE__ */ jsx(PostStatusBadge, { status: post.status }),
|
|
1333
|
-
post.scheduledDate && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: post.scheduledDate }),
|
|
1334
|
-
/* @__PURE__ */ jsx(PlatformBadges, { platforms: post.platforms })
|
|
1335
|
-
] })
|
|
1336
|
-
]
|
|
1337
|
-
}
|
|
1338
|
-
);
|
|
1279
|
+
function canUploadMedia() {
|
|
1280
|
+
return !!getSupabaseClientOptional();
|
|
1339
1281
|
}
|
|
1340
|
-
function
|
|
1282
|
+
async function uploadPostMedia(postId, file) {
|
|
1283
|
+
const supabase = getSupabaseClientOptional();
|
|
1284
|
+
if (!supabase) throw new Error("Supabase not initialized");
|
|
1285
|
+
const tenantId = getActiveTenantId();
|
|
1286
|
+
if (!tenantId) throw new Error("No active tenant");
|
|
1287
|
+
const ext = file.name.split(".").pop()?.toLowerCase() ?? "bin";
|
|
1288
|
+
const path = `content/${tenantId}/${postId}-${Date.now()}.${ext}`;
|
|
1289
|
+
const { error } = await supabase.storage.from("mkt-media").upload(path, file, { upsert: true });
|
|
1290
|
+
if (error) throw error;
|
|
1291
|
+
const { data } = supabase.storage.from("mkt-media").getPublicUrl(path);
|
|
1292
|
+
return data.publicUrl;
|
|
1293
|
+
}
|
|
1294
|
+
function newId() {
|
|
1295
|
+
return typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `ck-${Date.now()}-${Math.floor(Math.random() * 1e6)}`;
|
|
1296
|
+
}
|
|
1297
|
+
function RecordingChecklist({ post }) {
|
|
1341
1298
|
const t = useTranslation();
|
|
1342
|
-
const
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1299
|
+
const savePost = useContentPlannerStore((s) => s.savePost);
|
|
1300
|
+
const [items, setItems] = React12.useState(post.checklist);
|
|
1301
|
+
const [text, setText] = React12.useState("");
|
|
1302
|
+
React12.useEffect(() => {
|
|
1303
|
+
setItems(post.checklist);
|
|
1304
|
+
}, [post.id]);
|
|
1305
|
+
const persist = (next) => {
|
|
1306
|
+
setItems(next);
|
|
1307
|
+
void savePost({ id: post.id, checklist: next });
|
|
1308
|
+
};
|
|
1309
|
+
const toggle = (id) => persist(items.map((i) => i.id === id ? { ...i, done: !i.done } : i));
|
|
1310
|
+
const remove = (id) => persist(items.filter((i) => i.id !== id));
|
|
1311
|
+
const add = () => {
|
|
1312
|
+
const value = text.trim();
|
|
1313
|
+
if (!value) return;
|
|
1314
|
+
persist([...items, { id: newId(), text: value, done: false }]);
|
|
1315
|
+
setText("");
|
|
1316
|
+
};
|
|
1317
|
+
const generateFromScript = () => {
|
|
1318
|
+
const takes = [...post.contentMd.matchAll(/^##\s+(TAKE\s+.+)$/gim)].map(
|
|
1319
|
+
(m) => m[1].trim()
|
|
1320
|
+
);
|
|
1321
|
+
const existing = new Set(items.map((i) => i.text));
|
|
1322
|
+
const fresh = takes.filter((take) => !existing.has(take)).map((take) => ({ id: newId(), text: take, done: false }));
|
|
1323
|
+
if (fresh.length > 0) persist([...items, ...fresh]);
|
|
1324
|
+
};
|
|
1325
|
+
const hasTakes = /^##\s+TAKE\s+/im.test(post.contentMd);
|
|
1326
|
+
const done = items.filter((i) => i.done).length;
|
|
1327
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1328
|
+
items.length > 0 && /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
1329
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-xs text-muted-foreground", children: [
|
|
1330
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
1331
|
+
done,
|
|
1332
|
+
"/",
|
|
1333
|
+
items.length
|
|
1372
1334
|
] }),
|
|
1373
|
-
|
|
1374
|
-
PostCard,
|
|
1375
|
-
{
|
|
1376
|
-
post,
|
|
1377
|
-
onOpen: () => onOpenPost(post.id),
|
|
1378
|
-
onDelete: onDeletePost ? () => onDeletePost(post.id) : void 0
|
|
1379
|
-
},
|
|
1380
|
-
post.id
|
|
1381
|
-
)),
|
|
1382
|
-
/* @__PURE__ */ jsxs(
|
|
1335
|
+
hasTakes && /* @__PURE__ */ jsxs(
|
|
1383
1336
|
"button",
|
|
1384
1337
|
{
|
|
1385
1338
|
type: "button",
|
|
1386
|
-
onClick:
|
|
1387
|
-
|
|
1388
|
-
className: "flex items-center justify-center gap-1 rounded-card border border-dashed border-border p-2 text-xs font-medium text-muted-foreground transition-colors hover:bg-muted/40 hover:text-foreground disabled:pointer-events-none disabled:opacity-50",
|
|
1339
|
+
onClick: generateFromScript,
|
|
1340
|
+
className: "flex items-center gap-1 font-medium text-muted-foreground transition-colors hover:text-foreground",
|
|
1389
1341
|
children: [
|
|
1390
|
-
/* @__PURE__ */ jsx(
|
|
1391
|
-
t("marketing.content.
|
|
1342
|
+
/* @__PURE__ */ jsx(Sparkles, { className: "h-3 w-3" }),
|
|
1343
|
+
t("marketing.content.checklistGenerate")
|
|
1392
1344
|
]
|
|
1393
1345
|
}
|
|
1394
1346
|
)
|
|
1395
|
-
]
|
|
1396
|
-
}
|
|
1397
|
-
);
|
|
1398
|
-
}
|
|
1399
|
-
function AccountModal({ open, account, onClose }) {
|
|
1400
|
-
const t = useTranslation();
|
|
1401
|
-
const saveAccount = useContentPlannerStore((s) => s.saveAccount);
|
|
1402
|
-
const deleteAccount = useContentPlannerStore((s) => s.deleteAccount);
|
|
1403
|
-
const isEdit = !!account;
|
|
1404
|
-
const [name, setName] = React9.useState("");
|
|
1405
|
-
const [handle, setHandle] = React9.useState("");
|
|
1406
|
-
const [platforms, setPlatforms] = React9.useState(["instagram"]);
|
|
1407
|
-
const [saving, setSaving] = React9.useState(false);
|
|
1408
|
-
const [confirmDelete, setConfirmDelete] = React9.useState(false);
|
|
1409
|
-
React9.useEffect(() => {
|
|
1410
|
-
if (open) {
|
|
1411
|
-
setName(account?.name ?? "");
|
|
1412
|
-
setHandle(account?.handle ?? "");
|
|
1413
|
-
setPlatforms(account?.platforms?.length ? account.platforms : ["instagram"]);
|
|
1414
|
-
setSaving(false);
|
|
1415
|
-
setConfirmDelete(false);
|
|
1416
|
-
}
|
|
1417
|
-
}, [open, account]);
|
|
1418
|
-
const create = async () => {
|
|
1419
|
-
if (!name.trim() || platforms.length === 0 || saving) return;
|
|
1420
|
-
setSaving(true);
|
|
1421
|
-
try {
|
|
1422
|
-
await saveAccount({
|
|
1423
|
-
...isEdit ? { id: account.id } : {},
|
|
1424
|
-
name: name.trim(),
|
|
1425
|
-
handle: handle.trim() || void 0,
|
|
1426
|
-
platforms
|
|
1427
|
-
});
|
|
1428
|
-
onClose();
|
|
1429
|
-
} finally {
|
|
1430
|
-
setSaving(false);
|
|
1431
|
-
}
|
|
1432
|
-
};
|
|
1433
|
-
const remove = async () => {
|
|
1434
|
-
if (!account || saving) return;
|
|
1435
|
-
setSaving(true);
|
|
1436
|
-
try {
|
|
1437
|
-
await deleteAccount(account.id);
|
|
1438
|
-
onClose();
|
|
1439
|
-
} finally {
|
|
1440
|
-
setSaving(false);
|
|
1441
|
-
}
|
|
1442
|
-
};
|
|
1443
|
-
return /* @__PURE__ */ jsx(Modal, { open, onOpenChange: (o) => {
|
|
1444
|
-
if (!o) onClose();
|
|
1445
|
-
}, children: /* @__PURE__ */ jsxs(ModalContent, { size: "sm", children: [
|
|
1446
|
-
/* @__PURE__ */ jsx(ModalHeader, { children: /* @__PURE__ */ jsx(ModalTitle, { children: isEdit ? t("marketing.content.editAccountTitle") : t("marketing.content.addAccountTitle") }) }),
|
|
1447
|
-
/* @__PURE__ */ jsxs(ModalBody, { className: "space-y-4", children: [
|
|
1448
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
1449
|
-
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t("marketing.content.nameLabel") }),
|
|
1450
|
-
/* @__PURE__ */ jsx(
|
|
1451
|
-
Input,
|
|
1452
|
-
{
|
|
1453
|
-
autoFocus: true,
|
|
1454
|
-
value: name,
|
|
1455
|
-
onChange: (e) => setName(e.target.value),
|
|
1456
|
-
placeholder: t("marketing.content.accountNamePlaceholder"),
|
|
1457
|
-
onKeyDown: (e) => {
|
|
1458
|
-
if (e.key === "Enter") void create();
|
|
1459
|
-
}
|
|
1460
|
-
}
|
|
1461
|
-
)
|
|
1462
1347
|
] }),
|
|
1463
|
-
/* @__PURE__ */
|
|
1464
|
-
|
|
1348
|
+
/* @__PURE__ */ jsx("div", { className: "h-1 overflow-hidden rounded-full bg-muted", children: /* @__PURE__ */ jsx(
|
|
1349
|
+
"div",
|
|
1350
|
+
{
|
|
1351
|
+
className: "h-full rounded-full bg-primary transition-[width]",
|
|
1352
|
+
style: { width: items.length ? `${done / items.length * 100}%` : 0 }
|
|
1353
|
+
}
|
|
1354
|
+
) })
|
|
1355
|
+
] }),
|
|
1356
|
+
items.length === 0 && /* @__PURE__ */ jsxs("div", { className: "space-y-3 rounded-card border border-dashed border-border p-3 text-center", children: [
|
|
1357
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("marketing.content.checklistEmpty") }),
|
|
1358
|
+
hasTakes && /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: generateFromScript, children: [
|
|
1359
|
+
/* @__PURE__ */ jsx(Sparkles, { className: "mr-1.5 h-3.5 w-3.5" }),
|
|
1360
|
+
t("marketing.content.checklistGenerate")
|
|
1361
|
+
] })
|
|
1362
|
+
] }),
|
|
1363
|
+
/* @__PURE__ */ jsx("ul", { className: "space-y-1", children: items.map((item) => /* @__PURE__ */ jsxs("li", { className: "group/item flex items-start gap-2.5 rounded-button px-1 py-2 transition-colors hover:bg-muted/40", children: [
|
|
1364
|
+
/* @__PURE__ */ jsxs("label", { className: "flex min-w-0 flex-1 cursor-pointer items-start gap-2.5", children: [
|
|
1365
|
+
/* @__PURE__ */ jsx(Checkbox, { checked: item.done, onChange: () => toggle(item.id), className: "mt-0.5" }),
|
|
1465
1366
|
/* @__PURE__ */ jsx(
|
|
1466
|
-
|
|
1367
|
+
"span",
|
|
1467
1368
|
{
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1369
|
+
className: cn(
|
|
1370
|
+
"min-w-0 flex-1 text-sm leading-snug",
|
|
1371
|
+
item.done ? "text-muted-foreground line-through" : "text-foreground"
|
|
1372
|
+
),
|
|
1373
|
+
children: item.text
|
|
1471
1374
|
}
|
|
1472
1375
|
)
|
|
1473
1376
|
] }),
|
|
1474
|
-
/* @__PURE__ */
|
|
1475
|
-
|
|
1476
|
-
/* @__PURE__ */ jsx(PlatformPicker, { value: platforms, onChange: setPlatforms })
|
|
1477
|
-
] })
|
|
1478
|
-
] }),
|
|
1479
|
-
/* @__PURE__ */ jsx(ModalFooter, { children: confirmDelete && isEdit ? /* @__PURE__ */ jsxs("div", { className: "flex w-full items-center gap-3 rounded-card border border-destructive/30 bg-destructive/5 p-3", children: [
|
|
1480
|
-
/* @__PURE__ */ jsx("p", { className: "flex-1 text-xs text-destructive", children: t("marketing.content.deleteAccountConfirm") }),
|
|
1481
|
-
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: () => setConfirmDelete(false), disabled: saving, children: t("marketing.content.cancel") }),
|
|
1482
|
-
/* @__PURE__ */ jsx(Button, { variant: "destructive", size: "sm", onClick: () => void remove(), disabled: saving, children: t("marketing.content.deleteAccount") })
|
|
1483
|
-
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1484
|
-
isEdit && /* @__PURE__ */ jsxs(
|
|
1485
|
-
Button,
|
|
1377
|
+
/* @__PURE__ */ jsx(
|
|
1378
|
+
"button",
|
|
1486
1379
|
{
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
className: "
|
|
1491
|
-
children:
|
|
1492
|
-
/* @__PURE__ */ jsx(Trash2, { className: "mr-1.5 h-4 w-4" }),
|
|
1493
|
-
t("marketing.content.deleteAccount")
|
|
1494
|
-
]
|
|
1380
|
+
type: "button",
|
|
1381
|
+
"aria-label": t("marketing.content.cancel"),
|
|
1382
|
+
onClick: () => remove(item.id),
|
|
1383
|
+
className: "rounded p-0.5 text-muted-foreground/50 opacity-0 transition-opacity hover:text-destructive group-hover/item:opacity-100 focus:opacity-100",
|
|
1384
|
+
children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" })
|
|
1495
1385
|
}
|
|
1496
|
-
)
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
}
|
|
1502
|
-
function AccountSelector() {
|
|
1503
|
-
const t = useTranslation();
|
|
1504
|
-
const loaded = useContentPlannerStore((s) => s.loaded);
|
|
1505
|
-
const accounts = useContentPlannerStore((s) => s.accounts);
|
|
1506
|
-
const activeAccountId = useContentPlannerStore((s) => s.activeAccountId);
|
|
1507
|
-
const setAccount = useContentPlannerStore((s) => s.setAccount);
|
|
1508
|
-
const [open, setOpen] = React9.useState(false);
|
|
1509
|
-
const [modal, setModal] = React9.useState(null);
|
|
1510
|
-
const activeAccount = accounts.find((a) => a.id === activeAccountId) ?? null;
|
|
1511
|
-
if (!loaded) {
|
|
1512
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
1513
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-64" }),
|
|
1514
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-9" })
|
|
1515
|
-
] });
|
|
1516
|
-
}
|
|
1517
|
-
const openSettings = (account) => {
|
|
1518
|
-
setOpen(false);
|
|
1519
|
-
setModal({ mode: "edit", account });
|
|
1520
|
-
};
|
|
1521
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1522
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
1523
|
-
/* @__PURE__ */ jsxs(
|
|
1524
|
-
Select,
|
|
1386
|
+
)
|
|
1387
|
+
] }, item.id)) }),
|
|
1388
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1389
|
+
/* @__PURE__ */ jsx(
|
|
1390
|
+
Input,
|
|
1525
1391
|
{
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
if (
|
|
1531
|
-
setModal({ mode: "create" });
|
|
1532
|
-
return;
|
|
1533
|
-
}
|
|
1534
|
-
void setAccount(id);
|
|
1392
|
+
value: text,
|
|
1393
|
+
onChange: (e) => setText(e.target.value),
|
|
1394
|
+
placeholder: t("marketing.content.checklistAddPlaceholder"),
|
|
1395
|
+
onKeyDown: (e) => {
|
|
1396
|
+
if (e.key === "Enter") add();
|
|
1535
1397
|
},
|
|
1536
|
-
|
|
1537
|
-
/* @__PURE__ */ jsx(SelectTrigger, { className: "w-64", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: t("marketing.content.account") }) }),
|
|
1538
|
-
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
1539
|
-
accounts.length === 0 ? /* @__PURE__ */ jsx(SelectItem, { value: "__none__", disabled: true, children: t("marketing.content.noAccountsAvailable") }) : accounts.map((acc) => /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
1540
|
-
/* @__PURE__ */ jsx(SelectItem, { value: acc.id, className: "pr-9", children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
|
|
1541
|
-
acc.name,
|
|
1542
|
-
/* @__PURE__ */ jsx(PlatformBadges, { platforms: acc.platforms })
|
|
1543
|
-
] }) }),
|
|
1544
|
-
/* @__PURE__ */ jsx(
|
|
1545
|
-
"button",
|
|
1546
|
-
{
|
|
1547
|
-
type: "button",
|
|
1548
|
-
"aria-label": t("marketing.content.accountSettings"),
|
|
1549
|
-
onPointerDown: (e) => {
|
|
1550
|
-
e.stopPropagation();
|
|
1551
|
-
e.preventDefault();
|
|
1552
|
-
},
|
|
1553
|
-
onPointerUp: (e) => {
|
|
1554
|
-
e.stopPropagation();
|
|
1555
|
-
e.preventDefault();
|
|
1556
|
-
},
|
|
1557
|
-
onClick: (e) => {
|
|
1558
|
-
e.stopPropagation();
|
|
1559
|
-
openSettings(acc);
|
|
1560
|
-
},
|
|
1561
|
-
className: "absolute right-2 top-1/2 -translate-y-1/2 rounded p-1 text-muted-foreground/70 transition-colors hover:bg-muted/60 hover:text-foreground",
|
|
1562
|
-
children: /* @__PURE__ */ jsx(Settings2, { className: "h-3.5 w-3.5" })
|
|
1563
|
-
}
|
|
1564
|
-
)
|
|
1565
|
-
] }, acc.id)),
|
|
1566
|
-
/* @__PURE__ */ jsx(SelectSeparator, {}),
|
|
1567
|
-
/* @__PURE__ */ jsx(SelectItem, { value: ADD_ACCOUNT_VALUE, children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
|
|
1568
|
-
/* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5" }),
|
|
1569
|
-
t("marketing.content.addAccount")
|
|
1570
|
-
] }) })
|
|
1571
|
-
] })
|
|
1572
|
-
]
|
|
1398
|
+
className: "h-8 text-sm"
|
|
1573
1399
|
}
|
|
1574
1400
|
),
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
{
|
|
1578
|
-
variant: "ghost",
|
|
1579
|
-
size: "sm",
|
|
1580
|
-
className: "h-9 w-9 p-0 text-muted-foreground",
|
|
1581
|
-
"aria-label": t("marketing.content.accountSettings"),
|
|
1582
|
-
onClick: () => setModal({ mode: "edit", account: activeAccount }),
|
|
1583
|
-
children: /* @__PURE__ */ jsx(Settings2, { className: "h-4 w-4" })
|
|
1584
|
-
}
|
|
1585
|
-
)
|
|
1586
|
-
] }),
|
|
1587
|
-
/* @__PURE__ */ jsx(
|
|
1588
|
-
AccountModal,
|
|
1589
|
-
{
|
|
1590
|
-
open: !!modal,
|
|
1591
|
-
account: modal?.mode === "edit" ? modal.account : null,
|
|
1592
|
-
onClose: () => setModal(null)
|
|
1593
|
-
}
|
|
1594
|
-
)
|
|
1401
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: add, disabled: !text.trim(), "aria-label": t("marketing.content.checklistAdd"), children: /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }) })
|
|
1402
|
+
] })
|
|
1595
1403
|
] });
|
|
1596
1404
|
}
|
|
1597
|
-
function
|
|
1598
|
-
|
|
1599
|
-
return /* @__PURE__ */ jsx(
|
|
1600
|
-
SegmentedControl,
|
|
1601
|
-
{
|
|
1602
|
-
size: "md",
|
|
1603
|
-
"aria-label": t("marketing.content.viewBoard"),
|
|
1604
|
-
value: mode,
|
|
1605
|
-
onChange,
|
|
1606
|
-
options: [
|
|
1607
|
-
{ value: "board", label: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
|
|
1608
|
-
/* @__PURE__ */ jsx(Columns3, { className: "h-3.5 w-3.5" }),
|
|
1609
|
-
t("marketing.content.viewBoard")
|
|
1610
|
-
] }) },
|
|
1611
|
-
{ value: "calendar", label: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
|
|
1612
|
-
/* @__PURE__ */ jsx(CalendarDays, { className: "h-3.5 w-3.5" }),
|
|
1613
|
-
t("marketing.content.viewCalendar")
|
|
1614
|
-
] }) },
|
|
1615
|
-
{ value: "list", label: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
|
|
1616
|
-
/* @__PURE__ */ jsx(List, { className: "h-3.5 w-3.5" }),
|
|
1617
|
-
t("marketing.content.viewList")
|
|
1618
|
-
] }) }
|
|
1619
|
-
]
|
|
1620
|
-
}
|
|
1621
|
-
);
|
|
1622
|
-
}
|
|
1623
|
-
function shortDay(d) {
|
|
1624
|
-
return d.toLocaleDateString(void 0, { day: "numeric", month: "short" });
|
|
1405
|
+
function toCaption(md) {
|
|
1406
|
+
return md.replace(/^#+\s*/gm, "").replace(/\*\*/g, "").replace(/^[-*]\s+/gm, "").trim();
|
|
1625
1407
|
}
|
|
1626
|
-
function
|
|
1627
|
-
return
|
|
1628
|
-
const start = new Date(startDate);
|
|
1629
|
-
start.setDate(start.getDate() + i * 7);
|
|
1630
|
-
const end = new Date(start);
|
|
1631
|
-
end.setDate(start.getDate() + 6);
|
|
1632
|
-
return {
|
|
1633
|
-
week: i + 1,
|
|
1634
|
-
rangeLabel: `${shortDay(start)} \u2013 ${shortDay(end)}`,
|
|
1635
|
-
monthLabel: start.toLocaleDateString(void 0, { month: "long", year: "numeric" })
|
|
1636
|
-
};
|
|
1637
|
-
});
|
|
1408
|
+
function isVideo(url) {
|
|
1409
|
+
return !!url && /\.(mp4|webm|mov)($|\?)/i.test(url);
|
|
1638
1410
|
}
|
|
1639
|
-
function
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
const current = months[months.length - 1];
|
|
1643
|
-
if (current && current.label === bw.monthLabel) current.weeks.push(bw);
|
|
1644
|
-
else months.push({ label: bw.monthLabel, weeks: [bw] });
|
|
1411
|
+
function Media({ mediaUrl, className }) {
|
|
1412
|
+
if (!mediaUrl) {
|
|
1413
|
+
return /* @__PURE__ */ jsx("div", { className: cn("flex aspect-square w-full items-center justify-center bg-muted/40", className), children: /* @__PURE__ */ jsx(Image, { className: "h-8 w-8 text-muted-foreground/40" }) });
|
|
1645
1414
|
}
|
|
1646
|
-
return /* @__PURE__ */ jsx("
|
|
1647
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: label }),
|
|
1648
|
-
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4", children: weeks.map(({ week, rangeLabel }) => /* @__PURE__ */ jsx(
|
|
1649
|
-
WeekColumn,
|
|
1650
|
-
{
|
|
1651
|
-
week,
|
|
1652
|
-
rangeLabel,
|
|
1653
|
-
posts: postsByWeek.get(week) ?? [],
|
|
1654
|
-
onOpenPost,
|
|
1655
|
-
onNewPost,
|
|
1656
|
-
onDeletePost,
|
|
1657
|
-
onDropPost,
|
|
1658
|
-
disabled
|
|
1659
|
-
},
|
|
1660
|
-
week
|
|
1661
|
-
)) })
|
|
1662
|
-
] }, label)) });
|
|
1415
|
+
return isVideo(mediaUrl) ? /* @__PURE__ */ jsx("video", { src: mediaUrl, controls: true, className: cn("w-full bg-black object-contain", className) }) : /* @__PURE__ */ jsx("img", { src: mediaUrl, alt: "", className: cn("w-full object-cover", className) });
|
|
1663
1416
|
}
|
|
1664
|
-
function
|
|
1665
|
-
return
|
|
1417
|
+
function Avatar({ name, ring }) {
|
|
1418
|
+
return /* @__PURE__ */ jsx(
|
|
1419
|
+
"span",
|
|
1420
|
+
{
|
|
1421
|
+
className: cn(
|
|
1422
|
+
"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-muted text-xs font-semibold text-foreground",
|
|
1423
|
+
ring && "ring-2 ring-offset-2 ring-pink-500/60"
|
|
1424
|
+
),
|
|
1425
|
+
children: (name.replace(/^@/, "")[0] ?? "?").toUpperCase()
|
|
1426
|
+
}
|
|
1427
|
+
);
|
|
1666
1428
|
}
|
|
1667
|
-
function
|
|
1668
|
-
const
|
|
1669
|
-
const
|
|
1670
|
-
|
|
1671
|
-
|
|
1429
|
+
function InstagramFrame({ caption, mediaUrl, accountName, accountHandle }) {
|
|
1430
|
+
const t = useTranslation();
|
|
1431
|
+
const handle = (accountHandle || accountName).replace(/^@/, "");
|
|
1432
|
+
return /* @__PURE__ */ jsxs("div", { className: "mx-auto w-full max-w-sm overflow-hidden rounded-card border border-border bg-card", children: [
|
|
1433
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 px-3 py-2.5", children: [
|
|
1434
|
+
/* @__PURE__ */ jsx(Avatar, { name: handle, ring: true }),
|
|
1435
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-sm font-semibold text-foreground", children: handle }),
|
|
1436
|
+
/* @__PURE__ */ jsx(MoreHorizontal, { className: "h-4 w-4 text-muted-foreground" })
|
|
1437
|
+
] }),
|
|
1438
|
+
/* @__PURE__ */ jsx(Media, { mediaUrl }),
|
|
1439
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2 px-3 py-2.5", children: [
|
|
1440
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4 text-foreground", children: [
|
|
1441
|
+
/* @__PURE__ */ jsx(Heart, { className: "h-6 w-6" }),
|
|
1442
|
+
/* @__PURE__ */ jsx(MessageCircle, { className: "h-6 w-6" }),
|
|
1443
|
+
/* @__PURE__ */ jsx(Send, { className: "h-6 w-6" }),
|
|
1444
|
+
/* @__PURE__ */ jsx(Bookmark, { className: "ml-auto h-6 w-6" })
|
|
1445
|
+
] }),
|
|
1446
|
+
/* @__PURE__ */ jsxs("p", { className: "whitespace-pre-wrap text-sm leading-snug text-foreground", children: [
|
|
1447
|
+
/* @__PURE__ */ jsx("span", { className: "mr-1.5 font-semibold", children: handle }),
|
|
1448
|
+
toCaption(caption)
|
|
1449
|
+
] }),
|
|
1450
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] uppercase tracking-wide text-muted-foreground", children: t("marketing.content.previewNow") })
|
|
1451
|
+
] })
|
|
1452
|
+
] });
|
|
1672
1453
|
}
|
|
1673
|
-
function
|
|
1454
|
+
function FacebookFrame({ caption, mediaUrl, accountName }) {
|
|
1674
1455
|
const t = useTranslation();
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
list.push(post);
|
|
1688
|
-
map.set(post.scheduledDate, list);
|
|
1689
|
-
}
|
|
1690
|
-
return map;
|
|
1691
|
-
}, [posts]);
|
|
1692
|
-
const unscheduled = posts.filter((p) => !p.scheduledDate);
|
|
1693
|
-
const days = React9.useMemo(() => {
|
|
1694
|
-
const firstOfMonth = new Date(cursor.getFullYear(), cursor.getMonth(), 1);
|
|
1695
|
-
const start = new Date(firstOfMonth);
|
|
1696
|
-
start.setDate(start.getDate() - (firstOfMonth.getDay() + 6) % 7);
|
|
1697
|
-
const cells = [];
|
|
1698
|
-
for (let i = 0; i < 42; i++) {
|
|
1699
|
-
const d = new Date(start);
|
|
1700
|
-
d.setDate(start.getDate() + i);
|
|
1701
|
-
cells.push(d);
|
|
1702
|
-
}
|
|
1703
|
-
while (cells.length > 7 && cells[cells.length - 7].getMonth() !== cursor.getMonth()) {
|
|
1704
|
-
cells.splice(cells.length - 7, 7);
|
|
1705
|
-
}
|
|
1706
|
-
return cells;
|
|
1707
|
-
}, [cursor]);
|
|
1708
|
-
const monthLabel = cursor.toLocaleDateString(void 0, { month: "long", year: "numeric" });
|
|
1709
|
-
const weekdayLabels = days.slice(0, 7).map(
|
|
1710
|
-
(d) => d.toLocaleDateString(void 0, { weekday: "short" })
|
|
1711
|
-
);
|
|
1712
|
-
const todayISO = toISODate(/* @__PURE__ */ new Date());
|
|
1713
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
1714
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1715
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold capitalize text-foreground", children: monthLabel }),
|
|
1716
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
1717
|
-
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: () => setCursor(new Date(cursor.getFullYear(), cursor.getMonth() - 1, 1)), children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" }) }),
|
|
1718
|
-
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: () => setCursor(new Date(cursor.getFullYear(), cursor.getMonth() + 1, 1)), children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" }) })
|
|
1719
|
-
] })
|
|
1456
|
+
return /* @__PURE__ */ jsxs("div", { className: "mx-auto w-full max-w-sm overflow-hidden rounded-card border border-border bg-card", children: [
|
|
1457
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 px-3 py-2.5", children: [
|
|
1458
|
+
/* @__PURE__ */ jsx(Avatar, { name: accountName }),
|
|
1459
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
1460
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-semibold text-foreground", children: accountName.replace(/^@/, "") }),
|
|
1461
|
+
/* @__PURE__ */ jsxs("p", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [
|
|
1462
|
+
t("marketing.content.previewNow"),
|
|
1463
|
+
" \xB7 ",
|
|
1464
|
+
/* @__PURE__ */ jsx(Globe, { className: "h-3 w-3" })
|
|
1465
|
+
] })
|
|
1466
|
+
] }),
|
|
1467
|
+
/* @__PURE__ */ jsx(MoreHorizontal, { className: "h-4 w-4 text-muted-foreground" })
|
|
1720
1468
|
] }),
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
{
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1469
|
+
/* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap px-3 pb-2.5 text-sm leading-snug text-foreground", children: toCaption(caption) }),
|
|
1470
|
+
/* @__PURE__ */ jsx(Media, { mediaUrl, className: "aspect-auto" }),
|
|
1471
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-around border-t border-border px-3 py-2 text-muted-foreground", children: [
|
|
1472
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5 text-xs font-medium", children: [
|
|
1473
|
+
/* @__PURE__ */ jsx(ThumbsUp, { className: "h-4 w-4" }),
|
|
1474
|
+
" ",
|
|
1475
|
+
t("marketing.content.previewLike")
|
|
1476
|
+
] }),
|
|
1477
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5 text-xs font-medium", children: [
|
|
1478
|
+
/* @__PURE__ */ jsx(MessageCircle, { className: "h-4 w-4" }),
|
|
1479
|
+
" ",
|
|
1480
|
+
t("marketing.content.previewComment")
|
|
1481
|
+
] }),
|
|
1482
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5 text-xs font-medium", children: [
|
|
1483
|
+
/* @__PURE__ */ jsx(Share2, { className: "h-4 w-4" }),
|
|
1484
|
+
" ",
|
|
1485
|
+
t("marketing.content.previewShare")
|
|
1486
|
+
] })
|
|
1487
|
+
] })
|
|
1488
|
+
] });
|
|
1489
|
+
}
|
|
1490
|
+
function LinkedinFrame({ caption, mediaUrl, accountName }) {
|
|
1491
|
+
const t = useTranslation();
|
|
1492
|
+
return /* @__PURE__ */ jsxs("div", { className: "mx-auto w-full max-w-sm overflow-hidden rounded-card border border-border bg-card", children: [
|
|
1493
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 px-3 py-2.5", children: [
|
|
1494
|
+
/* @__PURE__ */ jsx(Avatar, { name: accountName }),
|
|
1495
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
1496
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-semibold text-foreground", children: accountName.replace(/^@/, "") }),
|
|
1497
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("marketing.content.previewNow") })
|
|
1498
|
+
] })
|
|
1736
1499
|
] }),
|
|
1737
|
-
/* @__PURE__ */
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
"div",
|
|
1745
|
-
{
|
|
1746
|
-
className: cn(
|
|
1747
|
-
"group min-h-24 border-b border-r border-border p-1.5 [&:nth-child(7n)]:border-r-0",
|
|
1748
|
-
!inMonth && "bg-muted/20"
|
|
1749
|
-
),
|
|
1750
|
-
children: [
|
|
1751
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1752
|
-
/* @__PURE__ */ jsx(
|
|
1753
|
-
"span",
|
|
1754
|
-
{
|
|
1755
|
-
className: cn(
|
|
1756
|
-
"text-xs",
|
|
1757
|
-
inMonth ? "text-foreground" : "text-muted-foreground/60",
|
|
1758
|
-
iso === todayISO && "flex h-5 w-5 items-center justify-center rounded-full bg-primary font-semibold text-primary-foreground"
|
|
1759
|
-
),
|
|
1760
|
-
children: day.getDate()
|
|
1761
|
-
}
|
|
1762
|
-
),
|
|
1763
|
-
!disabled && /* @__PURE__ */ jsx(
|
|
1764
|
-
"button",
|
|
1765
|
-
{
|
|
1766
|
-
type: "button",
|
|
1767
|
-
onClick: () => onNewPostAt(iso),
|
|
1768
|
-
className: "rounded p-0.5 text-muted-foreground opacity-0 transition-opacity hover:bg-muted/60 hover:text-foreground group-hover:opacity-100",
|
|
1769
|
-
"aria-label": t("marketing.content.newPost"),
|
|
1770
|
-
children: /* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5" })
|
|
1771
|
-
}
|
|
1772
|
-
)
|
|
1773
|
-
] }),
|
|
1774
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-1 space-y-1", children: [
|
|
1775
|
-
(expandedDay === iso ? dayPosts : dayPosts.slice(0, MAX_VISIBLE)).map((post) => /* @__PURE__ */ jsx(
|
|
1776
|
-
"button",
|
|
1777
|
-
{
|
|
1778
|
-
type: "button",
|
|
1779
|
-
onClick: () => onOpenPost(post.id),
|
|
1780
|
-
className: "block w-full truncate rounded border border-border bg-card px-1.5 py-1 text-left text-xs text-foreground transition-colors hover:bg-muted/40",
|
|
1781
|
-
children: post.title || t("marketing.content.untitled")
|
|
1782
|
-
},
|
|
1783
|
-
post.id
|
|
1784
|
-
)),
|
|
1785
|
-
dayPosts.length > MAX_VISIBLE && /* @__PURE__ */ jsx(
|
|
1786
|
-
"button",
|
|
1787
|
-
{
|
|
1788
|
-
type: "button",
|
|
1789
|
-
onClick: () => setExpandedDay(expandedDay === iso ? null : iso),
|
|
1790
|
-
className: "block w-full px-1.5 py-0.5 text-left text-xs font-medium text-muted-foreground transition-colors hover:text-foreground",
|
|
1791
|
-
children: expandedDay === iso ? t("marketing.content.showLess") : `+${dayPosts.length - MAX_VISIBLE}`
|
|
1792
|
-
}
|
|
1793
|
-
)
|
|
1794
|
-
] })
|
|
1795
|
-
]
|
|
1796
|
-
},
|
|
1797
|
-
i
|
|
1798
|
-
);
|
|
1799
|
-
}) })
|
|
1500
|
+
/* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap px-3 pb-2.5 text-sm leading-snug text-foreground", children: toCaption(caption) }),
|
|
1501
|
+
/* @__PURE__ */ jsx(Media, { mediaUrl, className: "aspect-auto" }),
|
|
1502
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-around border-t border-border px-3 py-2 text-muted-foreground", children: [
|
|
1503
|
+
/* @__PURE__ */ jsx(ThumbsUp, { className: "h-4 w-4" }),
|
|
1504
|
+
/* @__PURE__ */ jsx(MessageCircle, { className: "h-4 w-4" }),
|
|
1505
|
+
/* @__PURE__ */ jsx(Repeat2, { className: "h-4 w-4" }),
|
|
1506
|
+
/* @__PURE__ */ jsx(Send, { className: "h-4 w-4" })
|
|
1800
1507
|
] })
|
|
1801
1508
|
] });
|
|
1802
1509
|
}
|
|
1803
|
-
function
|
|
1510
|
+
function XFrame({ caption, mediaUrl, accountName, accountHandle }) {
|
|
1804
1511
|
const t = useTranslation();
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1512
|
+
const handle = (accountHandle || accountName).replace(/^@/, "");
|
|
1513
|
+
return /* @__PURE__ */ jsx("div", { className: "mx-auto w-full max-w-sm rounded-card border border-border bg-card p-3", children: /* @__PURE__ */ jsxs("div", { className: "flex gap-2.5", children: [
|
|
1514
|
+
/* @__PURE__ */ jsx(Avatar, { name: handle }),
|
|
1515
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
1516
|
+
/* @__PURE__ */ jsxs("p", { className: "truncate text-sm text-foreground", children: [
|
|
1517
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold", children: accountName.replace(/^@/, "") }),
|
|
1518
|
+
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
|
|
1519
|
+
" @",
|
|
1520
|
+
handle,
|
|
1521
|
+
" \xB7 ",
|
|
1522
|
+
t("marketing.content.previewNow")
|
|
1523
|
+
] })
|
|
1524
|
+
] }),
|
|
1525
|
+
/* @__PURE__ */ jsx("p", { className: "mt-0.5 whitespace-pre-wrap text-sm leading-snug text-foreground", children: toCaption(caption) }),
|
|
1526
|
+
/* @__PURE__ */ jsx("div", { className: "mt-2 overflow-hidden rounded-xl border border-border", children: /* @__PURE__ */ jsx(Media, { mediaUrl, className: "aspect-auto" }) }),
|
|
1527
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-center justify-between pr-6 text-muted-foreground", children: [
|
|
1528
|
+
/* @__PURE__ */ jsx(MessageCircle, { className: "h-4 w-4" }),
|
|
1529
|
+
/* @__PURE__ */ jsx(Repeat2, { className: "h-4 w-4" }),
|
|
1530
|
+
/* @__PURE__ */ jsx(Heart, { className: "h-4 w-4" }),
|
|
1531
|
+
/* @__PURE__ */ jsx(BarChart2, { className: "h-4 w-4" }),
|
|
1532
|
+
/* @__PURE__ */ jsx(Share2, { className: "h-4 w-4" })
|
|
1533
|
+
] })
|
|
1534
|
+
] })
|
|
1535
|
+
] }) });
|
|
1536
|
+
}
|
|
1537
|
+
var FRAMES = {
|
|
1538
|
+
instagram: InstagramFrame,
|
|
1539
|
+
facebook: FacebookFrame,
|
|
1540
|
+
linkedin: LinkedinFrame,
|
|
1541
|
+
x: XFrame
|
|
1542
|
+
};
|
|
1543
|
+
function PostSocialPreview(props) {
|
|
1544
|
+
const platforms = props.platforms.length > 0 ? props.platforms : ["instagram"];
|
|
1545
|
+
const [selected, setSelected] = React12.useState(platforms[0]);
|
|
1546
|
+
const active = platforms.includes(selected) ? selected : platforms[0];
|
|
1547
|
+
const Frame = FRAMES[active] ?? InstagramFrame;
|
|
1548
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3 rounded-md border border-border bg-muted/20 p-4", children: [
|
|
1549
|
+
platforms.length > 1 && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center gap-1", children: platforms.map((platform) => /* @__PURE__ */ jsxs(
|
|
1550
|
+
"button",
|
|
1818
1551
|
{
|
|
1819
|
-
|
|
1820
|
-
|
|
1552
|
+
type: "button",
|
|
1553
|
+
onClick: () => setSelected(platform),
|
|
1554
|
+
"aria-pressed": active === platform,
|
|
1555
|
+
className: cn(
|
|
1556
|
+
"flex items-center gap-1.5 rounded-button px-2.5 py-1.5 text-xs font-medium capitalize transition-colors",
|
|
1557
|
+
active === platform ? "bg-card text-foreground shadow-button" : "text-muted-foreground hover:text-foreground"
|
|
1558
|
+
),
|
|
1821
1559
|
children: [
|
|
1822
|
-
/* @__PURE__ */ jsx(
|
|
1823
|
-
|
|
1824
|
-
post.title || t("marketing.content.untitled"),
|
|
1825
|
-
/* @__PURE__ */ jsx(PlatformBadges, { platforms: post.platforms })
|
|
1826
|
-
] }) }),
|
|
1827
|
-
/* @__PURE__ */ jsx("td", { className: "px-3 py-2", children: /* @__PURE__ */ jsx(FormatBadge, { format: post.format }) }),
|
|
1828
|
-
/* @__PURE__ */ jsx("td", { className: "px-3 py-2", children: /* @__PURE__ */ jsx(PostStatusBadge, { status: post.status }) }),
|
|
1829
|
-
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-muted-foreground", children: post.scheduledDate ?? "\u2014" })
|
|
1560
|
+
/* @__PURE__ */ jsx(PlatformIcon, { platform, className: "h-3.5 w-3.5" }),
|
|
1561
|
+
platform
|
|
1830
1562
|
]
|
|
1831
1563
|
},
|
|
1832
|
-
|
|
1833
|
-
)) })
|
|
1834
|
-
|
|
1564
|
+
platform
|
|
1565
|
+
)) }),
|
|
1566
|
+
/* @__PURE__ */ jsx(Frame, { ...props })
|
|
1567
|
+
] });
|
|
1835
1568
|
}
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1569
|
+
|
|
1570
|
+
// src/views/content/templates.ts
|
|
1571
|
+
var reel = (title) => `# ${title}
|
|
1572
|
+
|
|
1573
|
+
**TEMA:** ${title.toUpperCase()}
|
|
1574
|
+
**Formato:** Reel \xB7 **Dura\xE7\xE3o:** 45 a 60 segundos
|
|
1575
|
+
|
|
1576
|
+
---
|
|
1577
|
+
|
|
1578
|
+
## TAKE 1 \u2013 GANCHO (0s a 5s)
|
|
1579
|
+
|
|
1580
|
+
*Dire\xE7\xE3o de cena: olhando para a c\xE2mera, energia alta.*
|
|
1581
|
+
|
|
1582
|
+
**Texto na tela:** \u2026
|
|
1583
|
+
|
|
1584
|
+
**Fala:** "\u2026"
|
|
1585
|
+
|
|
1586
|
+
---
|
|
1587
|
+
|
|
1588
|
+
## TAKE 2 \u2013 DESENVOLVIMENTO (5s a 40s)
|
|
1589
|
+
|
|
1590
|
+
*Dire\xE7\xE3o de cena: \u2026*
|
|
1591
|
+
|
|
1592
|
+
**Texto na tela:** \u2026
|
|
1593
|
+
|
|
1594
|
+
**Fala:** "\u2026"
|
|
1595
|
+
|
|
1596
|
+
---
|
|
1597
|
+
|
|
1598
|
+
## TAKE 3 \u2013 CTA (40s a 60s)
|
|
1599
|
+
|
|
1600
|
+
**Fala:** "\u2026"
|
|
1601
|
+
|
|
1602
|
+
**CTA:** \u2026
|
|
1603
|
+
`;
|
|
1604
|
+
var staticPost = (title) => `# ${title}
|
|
1605
|
+
|
|
1606
|
+
**TEMA:** ${title.toUpperCase()}
|
|
1607
|
+
**Formato:** Est\xE1tico
|
|
1608
|
+
|
|
1609
|
+
## Texto da arte
|
|
1610
|
+
|
|
1611
|
+
\u2026
|
|
1612
|
+
|
|
1613
|
+
## Legenda
|
|
1614
|
+
|
|
1615
|
+
\u2026
|
|
1616
|
+
|
|
1617
|
+
**CTA:** \u2026
|
|
1618
|
+
`;
|
|
1619
|
+
var carousel = (title) => `# ${title}
|
|
1620
|
+
|
|
1621
|
+
**TEMA:** ${title.toUpperCase()}
|
|
1622
|
+
**Formato:** Carrossel
|
|
1623
|
+
|
|
1624
|
+
## Slide 1 \u2014 Capa (gancho)
|
|
1625
|
+
|
|
1626
|
+
\u2026
|
|
1627
|
+
|
|
1628
|
+
## Slide 2
|
|
1629
|
+
|
|
1630
|
+
\u2026
|
|
1631
|
+
|
|
1632
|
+
## Slide 3
|
|
1633
|
+
|
|
1634
|
+
\u2026
|
|
1635
|
+
|
|
1636
|
+
## \xDAltimo slide \u2014 CTA
|
|
1637
|
+
|
|
1638
|
+
\u2026
|
|
1639
|
+
`;
|
|
1640
|
+
var story = (title) => `# ${title}
|
|
1641
|
+
|
|
1642
|
+
**TEMA:** ${title.toUpperCase()}
|
|
1643
|
+
**Formato:** Story
|
|
1644
|
+
|
|
1645
|
+
## Frame 1
|
|
1646
|
+
|
|
1647
|
+
\u2026
|
|
1648
|
+
|
|
1649
|
+
## Frame 2
|
|
1650
|
+
|
|
1651
|
+
\u2026
|
|
1652
|
+
|
|
1653
|
+
## Frame final \u2014 CTA
|
|
1654
|
+
|
|
1655
|
+
*Sticker de link / caixa de pergunta / enquete.*
|
|
1656
|
+
|
|
1657
|
+
\u2026
|
|
1658
|
+
`;
|
|
1659
|
+
var video = (title) => `# ${title}
|
|
1660
|
+
|
|
1661
|
+
**TEMA:** ${title.toUpperCase()}
|
|
1662
|
+
**Formato:** V\xEDdeo longo (YouTube) \xB7 **Dura\xE7\xE3o:** 8 a 12 minutos
|
|
1663
|
+
|
|
1664
|
+
## Gancho (0s a 30s)
|
|
1665
|
+
|
|
1666
|
+
\u2026
|
|
1667
|
+
|
|
1668
|
+
## Roteiro por blocos
|
|
1669
|
+
|
|
1670
|
+
### Bloco 1 \u2014 Contexto
|
|
1671
|
+
|
|
1672
|
+
\u2026
|
|
1673
|
+
|
|
1674
|
+
### Bloco 2 \u2014 Desenvolvimento
|
|
1675
|
+
|
|
1676
|
+
\u2026
|
|
1677
|
+
|
|
1678
|
+
### Bloco 3 \u2014 Fechamento
|
|
1679
|
+
|
|
1680
|
+
\u2026
|
|
1681
|
+
|
|
1682
|
+
**CTA:** inscreva-se / comente / link na descri\xE7\xE3o
|
|
1683
|
+
`;
|
|
1684
|
+
var live = (title) => `# ${title}
|
|
1685
|
+
|
|
1686
|
+
**TEMA:** ${title.toUpperCase()}
|
|
1687
|
+
**Formato:** Live
|
|
1688
|
+
|
|
1689
|
+
## Pauta
|
|
1690
|
+
|
|
1691
|
+
1. Abertura e aquecimento (5 min)
|
|
1692
|
+
2. \u2026
|
|
1693
|
+
3. Perguntas e respostas (15 min)
|
|
1694
|
+
|
|
1695
|
+
## Avisos / CTAs durante a live
|
|
1696
|
+
|
|
1697
|
+
\u2026
|
|
1698
|
+
`;
|
|
1699
|
+
var article = (title) => `# ${title}
|
|
1700
|
+
|
|
1701
|
+
**TEMA:** ${title.toUpperCase()}
|
|
1702
|
+
**Formato:** Artigo / Newsletter
|
|
1703
|
+
|
|
1704
|
+
## T\xEDtulo de trabalho
|
|
1705
|
+
|
|
1706
|
+
\u2026
|
|
1707
|
+
|
|
1708
|
+
## Estrutura
|
|
1709
|
+
|
|
1710
|
+
- Abertura (dor / promessa)
|
|
1711
|
+
- Desenvolvimento em 3 pontos
|
|
1712
|
+
- Conclus\xE3o + CTA
|
|
1713
|
+
|
|
1714
|
+
## Rascunho
|
|
1715
|
+
|
|
1716
|
+
\u2026
|
|
1717
|
+
`;
|
|
1718
|
+
var POST_TEMPLATES = {
|
|
1719
|
+
reel,
|
|
1720
|
+
static: staticPost,
|
|
1721
|
+
carousel,
|
|
1722
|
+
story,
|
|
1723
|
+
video,
|
|
1724
|
+
live,
|
|
1725
|
+
article
|
|
1726
|
+
};
|
|
1727
|
+
function MediaAsset({ post, onChanged }) {
|
|
1728
|
+
const t = useTranslation();
|
|
1729
|
+
const inputRef = React12.useRef(null);
|
|
1730
|
+
const [busy, setBusy] = React12.useState(false);
|
|
1731
|
+
const isVideo2 = post.mediaUrl ? /\.(mp4|webm|mov)($|\?)/i.test(post.mediaUrl) : false;
|
|
1732
|
+
const pick = () => inputRef.current?.click();
|
|
1733
|
+
const upload = async (file) => {
|
|
1734
|
+
setBusy(true);
|
|
1735
|
+
try {
|
|
1736
|
+
const url = await uploadPostMedia(post.id, file);
|
|
1737
|
+
onChanged(url);
|
|
1738
|
+
} finally {
|
|
1739
|
+
setBusy(false);
|
|
1910
1740
|
}
|
|
1911
|
-
const post = await savePost({
|
|
1912
|
-
planId: targetPlan.id,
|
|
1913
|
-
weekNumber: week,
|
|
1914
|
-
title: "",
|
|
1915
|
-
format: "reel",
|
|
1916
|
-
status: "idea",
|
|
1917
|
-
scheduledDate: isoDate
|
|
1918
|
-
});
|
|
1919
|
-
invalidateLimit("content_posts_month");
|
|
1920
|
-
onOpenPost(post.id);
|
|
1921
1741
|
};
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
|
|
1929
|
-
/* @__PURE__ */ jsx(AccountSelector, {}),
|
|
1930
|
-
plan && /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: plan.name })
|
|
1931
|
-
] }),
|
|
1932
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1933
|
-
/* @__PURE__ */ jsx(ViewSwitcher, { mode: viewMode, onChange: setViewMode }),
|
|
1934
|
-
plan && /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => setBriefOpen(true), children: [
|
|
1935
|
-
/* @__PURE__ */ jsx(NotebookPen, { className: "mr-1.5 h-4 w-4" }),
|
|
1936
|
-
t("marketing.content.brief")
|
|
1937
|
-
] })
|
|
1938
|
-
] })
|
|
1939
|
-
] }),
|
|
1940
|
-
!busy && !hasAccount && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t("marketing.content.noAccountsHint") }),
|
|
1941
|
-
busy && viewMode === "board" && /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4", children: Array.from({ length: 8 }, (_, i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-44" }, i)) }),
|
|
1942
|
-
busy && viewMode === "calendar" && /* @__PURE__ */ jsx(Skeleton, { className: "h-96 w-full" }),
|
|
1943
|
-
busy && viewMode === "list" && /* @__PURE__ */ jsx("div", { className: "space-y-2", children: Array.from({ length: 6 }, (_, i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-full" }, i)) }),
|
|
1944
|
-
!busy && viewMode === "board" && /* @__PURE__ */ jsx(
|
|
1945
|
-
BoardView,
|
|
1946
|
-
{
|
|
1947
|
-
startDate: boardStart,
|
|
1948
|
-
weeksCount,
|
|
1949
|
-
postsByWeek,
|
|
1950
|
-
onOpenPost,
|
|
1951
|
-
onNewPost: (w) => void createPost(w),
|
|
1952
|
-
onDeletePost: setPendingDeleteId,
|
|
1953
|
-
onDropPost: (id, w) => void movePost(id, w),
|
|
1954
|
-
disabled: !hasAccount
|
|
1955
|
-
}
|
|
1956
|
-
),
|
|
1957
|
-
!busy && viewMode === "calendar" && /* @__PURE__ */ jsx(
|
|
1958
|
-
CalendarView,
|
|
1742
|
+
if (!canUploadMedia()) {
|
|
1743
|
+
return /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("marketing.content.mediaUnavailable") });
|
|
1744
|
+
}
|
|
1745
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1746
|
+
/* @__PURE__ */ jsx(
|
|
1747
|
+
"input",
|
|
1959
1748
|
{
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1749
|
+
ref: inputRef,
|
|
1750
|
+
type: "file",
|
|
1751
|
+
accept: "image/*,video/*",
|
|
1752
|
+
className: "hidden",
|
|
1753
|
+
onChange: (e) => {
|
|
1754
|
+
const file = e.target.files?.[0];
|
|
1755
|
+
if (file) void upload(file);
|
|
1756
|
+
e.target.value = "";
|
|
1757
|
+
}
|
|
1964
1758
|
}
|
|
1965
1759
|
),
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1760
|
+
post.mediaUrl ? /* @__PURE__ */ jsxs("div", { className: "relative overflow-hidden rounded-card border border-border bg-muted/20", children: [
|
|
1761
|
+
isVideo2 ? /* @__PURE__ */ jsx("video", { src: post.mediaUrl, controls: true, className: "max-h-72 w-full object-contain" }) : /* @__PURE__ */ jsx("img", { src: post.mediaUrl, alt: "", className: "max-h-72 w-full object-contain" }),
|
|
1762
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-2 border-t border-border p-2", children: [
|
|
1763
|
+
/* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: pick, disabled: busy, children: [
|
|
1764
|
+
busy ? /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx(ImagePlus, { className: "mr-1.5 h-3.5 w-3.5" }),
|
|
1765
|
+
t("marketing.content.mediaReplace")
|
|
1766
|
+
] }),
|
|
1767
|
+
/* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", onClick: () => onChanged(null), disabled: busy, children: [
|
|
1768
|
+
/* @__PURE__ */ jsx(X, { className: "mr-1 h-3.5 w-3.5" }),
|
|
1769
|
+
t("marketing.content.mediaRemove")
|
|
1770
|
+
] })
|
|
1771
|
+
] })
|
|
1772
|
+
] }) : /* @__PURE__ */ jsxs(
|
|
1773
|
+
"button",
|
|
1970
1774
|
{
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
setPendingDeleteId(null);
|
|
1981
|
-
if (id) void deletePost(id);
|
|
1982
|
-
}
|
|
1775
|
+
type: "button",
|
|
1776
|
+
onClick: pick,
|
|
1777
|
+
disabled: busy,
|
|
1778
|
+
className: "flex w-full flex-col items-center justify-center gap-2 rounded-card border border-dashed border-border p-6 text-muted-foreground transition-colors hover:bg-muted/30 hover:text-foreground",
|
|
1779
|
+
children: [
|
|
1780
|
+
busy ? /* @__PURE__ */ jsx(Loader2, { className: "h-5 w-5 animate-spin" }) : /* @__PURE__ */ jsx(ImagePlus, { className: "h-5 w-5" }),
|
|
1781
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: t("marketing.content.mediaUpload") }),
|
|
1782
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs", children: t("marketing.content.mediaHint") })
|
|
1783
|
+
]
|
|
1983
1784
|
}
|
|
1984
1785
|
)
|
|
1985
1786
|
] });
|
|
1986
1787
|
}
|
|
1987
|
-
function
|
|
1988
|
-
return
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
if (error) throw error;
|
|
1999
|
-
const { data } = supabase.storage.from("mkt-media").getPublicUrl(path);
|
|
2000
|
-
return data.publicUrl;
|
|
1788
|
+
function toDraft2(post) {
|
|
1789
|
+
return {
|
|
1790
|
+
title: post.title,
|
|
1791
|
+
format: post.format,
|
|
1792
|
+
status: post.status,
|
|
1793
|
+
scheduledDate: post.scheduledDate ?? "",
|
|
1794
|
+
platforms: post.platforms,
|
|
1795
|
+
hook: post.hook ?? "",
|
|
1796
|
+
cta: post.cta ?? "",
|
|
1797
|
+
contentMd: post.contentMd
|
|
1798
|
+
};
|
|
2001
1799
|
}
|
|
2002
|
-
function
|
|
2003
|
-
return
|
|
1800
|
+
function PostModal({ postId, onClose }) {
|
|
1801
|
+
return /* @__PURE__ */ jsx(Modal, { open: !!postId, onOpenChange: (o) => {
|
|
1802
|
+
if (!o) onClose();
|
|
1803
|
+
}, children: /* @__PURE__ */ jsx(ModalContent, { size: "5xl", children: postId && /* @__PURE__ */ jsx(PostEditor, { postId, onClose }) }) });
|
|
2004
1804
|
}
|
|
2005
|
-
function
|
|
1805
|
+
function PostEditor({ postId, onClose }) {
|
|
2006
1806
|
const t = useTranslation();
|
|
1807
|
+
const provider2 = useContentPlannerProvider();
|
|
1808
|
+
const storePost = useContentPlannerStore((s) => s.posts.find((p) => p.id === postId));
|
|
2007
1809
|
const savePost = useContentPlannerStore((s) => s.savePost);
|
|
2008
|
-
const
|
|
2009
|
-
const
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
const
|
|
2014
|
-
|
|
2015
|
-
|
|
1810
|
+
const deletePost = useContentPlannerStore((s) => s.deletePost);
|
|
1811
|
+
const accounts = useContentPlannerStore((s) => s.accounts);
|
|
1812
|
+
const activeAccountId = useContentPlannerStore((s) => s.activeAccountId);
|
|
1813
|
+
const [post, setPost] = React12.useState(storePost ?? null);
|
|
1814
|
+
const [draft, setDraft] = React12.useState(storePost ? toDraft2(storePost) : null);
|
|
1815
|
+
const [saving, setSaving] = React12.useState(false);
|
|
1816
|
+
const [confirmDelete, setConfirmDelete] = React12.useState(false);
|
|
1817
|
+
React12.useEffect(() => {
|
|
1818
|
+
if (post?.id === postId) return;
|
|
1819
|
+
if (storePost) {
|
|
1820
|
+
setPost(storePost);
|
|
1821
|
+
setDraft(toDraft2(storePost));
|
|
1822
|
+
return;
|
|
1823
|
+
}
|
|
1824
|
+
void provider2.getPost(postId).then((fetched) => {
|
|
1825
|
+
if (fetched) {
|
|
1826
|
+
setPost(fetched);
|
|
1827
|
+
setDraft(toDraft2(fetched));
|
|
1828
|
+
}
|
|
1829
|
+
});
|
|
1830
|
+
}, [postId, storePost, post?.id, provider2]);
|
|
1831
|
+
if (!post || !draft) {
|
|
1832
|
+
return /* @__PURE__ */ jsxs(ModalBody, { className: "space-y-5", children: [
|
|
1833
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-56" }),
|
|
1834
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-full" }),
|
|
1835
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-3", children: Array.from({ length: 3 }, (_, i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-14" }, i)) }),
|
|
1836
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-64 w-full" })
|
|
1837
|
+
] });
|
|
1838
|
+
}
|
|
1839
|
+
const dirty = draft.title !== post.title || draft.format !== post.format || draft.status !== post.status || draft.scheduledDate !== (post.scheduledDate ?? "") || draft.platforms.join(",") !== post.platforms.join(",") || draft.hook !== (post.hook ?? "") || draft.cta !== (post.cta ?? "") || draft.contentMd !== post.contentMd;
|
|
1840
|
+
const patch = (partial) => setDraft((d) => d ? { ...d, ...partial } : d);
|
|
1841
|
+
const save = async () => {
|
|
1842
|
+
setSaving(true);
|
|
1843
|
+
try {
|
|
1844
|
+
const updated = await savePost({
|
|
1845
|
+
id: post.id,
|
|
1846
|
+
title: draft.title,
|
|
1847
|
+
format: draft.format,
|
|
1848
|
+
status: draft.status,
|
|
1849
|
+
scheduledDate: draft.scheduledDate || null,
|
|
1850
|
+
platforms: draft.platforms,
|
|
1851
|
+
hook: draft.hook,
|
|
1852
|
+
cta: draft.cta,
|
|
1853
|
+
contentMd: draft.contentMd
|
|
1854
|
+
});
|
|
1855
|
+
setPost(updated);
|
|
1856
|
+
setDraft(toDraft2(updated));
|
|
1857
|
+
} finally {
|
|
1858
|
+
setSaving(false);
|
|
1859
|
+
}
|
|
2016
1860
|
};
|
|
2017
|
-
const
|
|
2018
|
-
|
|
2019
|
-
const add = () => {
|
|
2020
|
-
const value = text.trim();
|
|
2021
|
-
if (!value) return;
|
|
2022
|
-
persist([...items, { id: newId(), text: value, done: false }]);
|
|
2023
|
-
setText("");
|
|
1861
|
+
const insertTemplate = () => {
|
|
1862
|
+
patch({ contentMd: POST_TEMPLATES[draft.format](draft.title || t("marketing.content.untitled")) });
|
|
2024
1863
|
};
|
|
2025
|
-
const
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
)
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
1864
|
+
const isStatic = draft.format === "static";
|
|
1865
|
+
const account = accounts.find((a) => a.id === activeAccountId) ?? null;
|
|
1866
|
+
const handleMediaChanged = (mediaUrl) => {
|
|
1867
|
+
void savePost({ id: post.id, mediaUrl }).then((updated) => {
|
|
1868
|
+
setPost(updated);
|
|
1869
|
+
setDraft((d) => d ? { ...d } : d);
|
|
1870
|
+
});
|
|
2032
1871
|
};
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
1872
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1873
|
+
/* @__PURE__ */ jsx(ModalHeader, { children: /* @__PURE__ */ jsx(ModalTitle, { children: draft.title || t("marketing.content.untitled") }) }),
|
|
1874
|
+
/* @__PURE__ */ jsx(ModalBody, { children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-6 lg:grid-cols-[minmax(0,1fr)_18rem]", children: [
|
|
1875
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-5", children: [
|
|
1876
|
+
/* @__PURE__ */ jsx(
|
|
1877
|
+
Input,
|
|
1878
|
+
{
|
|
1879
|
+
value: draft.title,
|
|
1880
|
+
onChange: (e) => patch({ title: e.target.value }),
|
|
1881
|
+
placeholder: t("marketing.content.titlePlaceholder"),
|
|
1882
|
+
className: "text-base font-medium"
|
|
1883
|
+
}
|
|
1884
|
+
),
|
|
1885
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-3", children: [
|
|
1886
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
1887
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t("marketing.content.formatLabel") }),
|
|
1888
|
+
/* @__PURE__ */ jsxs(Select, { value: draft.format, onValueChange: (v) => patch({ format: v }), children: [
|
|
1889
|
+
/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
1890
|
+
/* @__PURE__ */ jsx(SelectContent, { children: POST_FORMATS.map((f) => /* @__PURE__ */ jsx(SelectItem, { value: f, children: t(`marketing.content.format.${f}`) }, f)) })
|
|
1891
|
+
] })
|
|
1892
|
+
] }),
|
|
1893
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
1894
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t("marketing.content.statusLabel") }),
|
|
1895
|
+
/* @__PURE__ */ jsxs(Select, { value: draft.status, onValueChange: (v) => patch({ status: v }), children: [
|
|
1896
|
+
/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
1897
|
+
/* @__PURE__ */ jsx(SelectContent, { children: POST_STATUS_ORDER.map((s) => /* @__PURE__ */ jsx(SelectItem, { value: s, children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
|
|
1898
|
+
/* @__PURE__ */ jsx(StatusDot, { status: s }),
|
|
1899
|
+
t(`marketing.content.status.${s}`)
|
|
1900
|
+
] }) }, s)) })
|
|
1901
|
+
] })
|
|
1902
|
+
] }),
|
|
1903
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
1904
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t("marketing.content.scheduledDate") }),
|
|
1905
|
+
/* @__PURE__ */ jsx(
|
|
1906
|
+
Input,
|
|
1907
|
+
{
|
|
1908
|
+
type: "date",
|
|
1909
|
+
value: draft.scheduledDate,
|
|
1910
|
+
onChange: (e) => patch({ scheduledDate: e.target.value })
|
|
1911
|
+
}
|
|
1912
|
+
)
|
|
1913
|
+
] }),
|
|
1914
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1 sm:col-span-2", children: [
|
|
1915
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t("marketing.content.hook") }),
|
|
1916
|
+
/* @__PURE__ */ jsx(
|
|
1917
|
+
Input,
|
|
1918
|
+
{
|
|
1919
|
+
value: draft.hook,
|
|
1920
|
+
onChange: (e) => patch({ hook: e.target.value }),
|
|
1921
|
+
placeholder: t("marketing.content.hookPlaceholder")
|
|
1922
|
+
}
|
|
1923
|
+
)
|
|
1924
|
+
] }),
|
|
1925
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
1926
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t("marketing.content.cta") }),
|
|
1927
|
+
/* @__PURE__ */ jsx(
|
|
1928
|
+
Input,
|
|
1929
|
+
{
|
|
1930
|
+
value: draft.cta,
|
|
1931
|
+
onChange: (e) => patch({ cta: e.target.value }),
|
|
1932
|
+
placeholder: t("marketing.content.ctaPlaceholder")
|
|
1933
|
+
}
|
|
1934
|
+
)
|
|
1935
|
+
] }),
|
|
1936
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1 sm:col-span-3", children: [
|
|
1937
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t("marketing.content.platformsLabel") }),
|
|
1938
|
+
/* @__PURE__ */ jsx(PlatformPicker, { value: draft.platforms, onChange: (platforms) => patch({ platforms }) })
|
|
1939
|
+
] })
|
|
2042
1940
|
] }),
|
|
2043
|
-
|
|
2044
|
-
"
|
|
2045
|
-
{
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
children:
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
1941
|
+
isStatic && /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
1942
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t("marketing.content.mediaLabel") }),
|
|
1943
|
+
/* @__PURE__ */ jsx(MediaAsset, { post, onChanged: handleMediaChanged })
|
|
1944
|
+
] }),
|
|
1945
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
1946
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1947
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: isStatic ? t("marketing.content.captionLabel") : t("marketing.content.scriptLabel") }),
|
|
1948
|
+
draft.contentMd.trim() === "" && /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: insertTemplate, children: t("marketing.content.insertTemplate") })
|
|
1949
|
+
] }),
|
|
1950
|
+
/* @__PURE__ */ jsx(
|
|
1951
|
+
MarkdownEditor,
|
|
1952
|
+
{
|
|
1953
|
+
value: draft.contentMd,
|
|
1954
|
+
onChange: (contentMd) => patch({ contentMd }),
|
|
1955
|
+
placeholder: isStatic ? t("marketing.content.captionPlaceholder") : t("marketing.content.bodyPlaceholder"),
|
|
1956
|
+
editLabel: t("marketing.content.edit"),
|
|
1957
|
+
previewLabel: t("marketing.content.preview"),
|
|
1958
|
+
renderPreview: isStatic ? (value) => /* @__PURE__ */ jsx(
|
|
1959
|
+
PostSocialPreview,
|
|
1960
|
+
{
|
|
1961
|
+
caption: value,
|
|
1962
|
+
mediaUrl: post.mediaUrl,
|
|
1963
|
+
accountName: account?.name ?? "@conta",
|
|
1964
|
+
accountHandle: account?.handle,
|
|
1965
|
+
platforms: draft.platforms.length > 0 ? draft.platforms : account?.platforms ?? []
|
|
1966
|
+
}
|
|
1967
|
+
) : void 0
|
|
1968
|
+
}
|
|
1969
|
+
)
|
|
1970
|
+
] })
|
|
2055
1971
|
] }),
|
|
2056
|
-
/* @__PURE__ */
|
|
2057
|
-
"
|
|
2058
|
-
{
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
1972
|
+
/* @__PURE__ */ jsxs("aside", { className: "space-y-2 lg:border-l lg:border-border lg:pl-6", children: [
|
|
1973
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-foreground", children: t("marketing.content.checklistTitle") }),
|
|
1974
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("marketing.content.checklistDescription") }),
|
|
1975
|
+
/* @__PURE__ */ jsx(RecordingChecklist, { post })
|
|
1976
|
+
] })
|
|
1977
|
+
] }) }),
|
|
1978
|
+
/* @__PURE__ */ jsxs(ModalFooter, { className: "sm:justify-between", children: [
|
|
1979
|
+
confirmDelete ? /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
1980
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: t("marketing.content.deletePostConfirm") }),
|
|
1981
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: () => setConfirmDelete(false), children: t("marketing.content.cancel") }),
|
|
1982
|
+
/* @__PURE__ */ jsx(Button, { variant: "destructive", size: "sm", onClick: () => void deletePost(post.id).then(onClose), children: t("marketing.content.deletePost") })
|
|
1983
|
+
] }) : /* @__PURE__ */ jsx(Button, { variant: "ghost", className: "text-destructive", onClick: () => setConfirmDelete(true), children: t("marketing.content.deletePost") }),
|
|
1984
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1985
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", onClick: onClose, children: t("marketing.content.cancel") }),
|
|
1986
|
+
/* @__PURE__ */ jsx(Button, { onClick: () => void save().then(onClose), disabled: !dirty || saving, children: t("marketing.content.save") })
|
|
2069
1987
|
] })
|
|
2070
|
-
] }),
|
|
2071
|
-
/* @__PURE__ */ jsx("ul", { className: "space-y-1", children: items.map((item) => /* @__PURE__ */ jsxs("li", { className: "group/item flex items-start gap-2.5 rounded-button px-1 py-2 transition-colors hover:bg-muted/40", children: [
|
|
2072
|
-
/* @__PURE__ */ jsxs("label", { className: "flex min-w-0 flex-1 cursor-pointer items-start gap-2.5", children: [
|
|
2073
|
-
/* @__PURE__ */ jsx(Checkbox, { checked: item.done, onChange: () => toggle(item.id), className: "mt-0.5" }),
|
|
2074
|
-
/* @__PURE__ */ jsx(
|
|
2075
|
-
"span",
|
|
2076
|
-
{
|
|
2077
|
-
className: cn(
|
|
2078
|
-
"min-w-0 flex-1 text-sm leading-snug",
|
|
2079
|
-
item.done ? "text-muted-foreground line-through" : "text-foreground"
|
|
2080
|
-
),
|
|
2081
|
-
children: item.text
|
|
2082
|
-
}
|
|
2083
|
-
)
|
|
2084
|
-
] }),
|
|
2085
|
-
/* @__PURE__ */ jsx(
|
|
2086
|
-
"button",
|
|
2087
|
-
{
|
|
2088
|
-
type: "button",
|
|
2089
|
-
"aria-label": t("marketing.content.cancel"),
|
|
2090
|
-
onClick: () => remove(item.id),
|
|
2091
|
-
className: "rounded p-0.5 text-muted-foreground/50 opacity-0 transition-opacity hover:text-destructive group-hover/item:opacity-100 focus:opacity-100",
|
|
2092
|
-
children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" })
|
|
2093
|
-
}
|
|
2094
|
-
)
|
|
2095
|
-
] }, item.id)) }),
|
|
2096
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2097
|
-
/* @__PURE__ */ jsx(
|
|
2098
|
-
Input,
|
|
2099
|
-
{
|
|
2100
|
-
value: text,
|
|
2101
|
-
onChange: (e) => setText(e.target.value),
|
|
2102
|
-
placeholder: t("marketing.content.checklistAddPlaceholder"),
|
|
2103
|
-
onKeyDown: (e) => {
|
|
2104
|
-
if (e.key === "Enter") add();
|
|
2105
|
-
},
|
|
2106
|
-
className: "h-8 text-sm"
|
|
2107
|
-
}
|
|
2108
|
-
),
|
|
2109
|
-
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: add, disabled: !text.trim(), "aria-label": t("marketing.content.checklistAdd"), children: /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }) })
|
|
2110
1988
|
] })
|
|
2111
1989
|
] });
|
|
2112
1990
|
}
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
function
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
return /* @__PURE__ */ jsx("div", { className: cn("flex aspect-square w-full items-center justify-center bg-muted/40", className), children: /* @__PURE__ */ jsx(Image, { className: "h-8 w-8 text-muted-foreground/40" }) });
|
|
2122
|
-
}
|
|
2123
|
-
return isVideo(mediaUrl) ? /* @__PURE__ */ jsx("video", { src: mediaUrl, controls: true, className: cn("w-full bg-black object-contain", className) }) : /* @__PURE__ */ jsx("img", { src: mediaUrl, alt: "", className: cn("w-full object-cover", className) });
|
|
2124
|
-
}
|
|
2125
|
-
function Avatar({ name, ring }) {
|
|
2126
|
-
return /* @__PURE__ */ jsx(
|
|
2127
|
-
"span",
|
|
1991
|
+
var DEFAULT_WEEKS = 8;
|
|
1992
|
+
var ADD_ACCOUNT_VALUE = "__add-account__";
|
|
1993
|
+
var POST_DRAG_MIME = "application/x-mkt-post";
|
|
1994
|
+
function PostCard({ post, onOpen, onDelete }) {
|
|
1995
|
+
const t = useTranslation();
|
|
1996
|
+
const [dragging, setDragging] = React12.useState(false);
|
|
1997
|
+
return /* @__PURE__ */ jsxs(
|
|
1998
|
+
"div",
|
|
2128
1999
|
{
|
|
2000
|
+
role: "button",
|
|
2001
|
+
tabIndex: 0,
|
|
2002
|
+
onClick: onOpen,
|
|
2003
|
+
onKeyDown: (e) => {
|
|
2004
|
+
if (e.key === "Enter") onOpen();
|
|
2005
|
+
},
|
|
2006
|
+
draggable: true,
|
|
2007
|
+
onDragStart: (e) => {
|
|
2008
|
+
e.dataTransfer.setData(POST_DRAG_MIME, post.id);
|
|
2009
|
+
e.dataTransfer.effectAllowed = "move";
|
|
2010
|
+
setDragging(true);
|
|
2011
|
+
},
|
|
2012
|
+
onDragEnd: () => setDragging(false),
|
|
2129
2013
|
className: cn(
|
|
2130
|
-
"
|
|
2131
|
-
|
|
2014
|
+
"group/post relative w-full cursor-pointer rounded-card border border-border bg-card p-3 pl-7 text-left transition-colors hover:bg-muted/40",
|
|
2015
|
+
dragging && "opacity-50"
|
|
2132
2016
|
),
|
|
2133
|
-
children:
|
|
2017
|
+
children: [
|
|
2018
|
+
/* @__PURE__ */ jsx(
|
|
2019
|
+
GripVertical,
|
|
2020
|
+
{
|
|
2021
|
+
className: "absolute left-1.5 top-3 h-4 w-4 cursor-grab text-muted-foreground/50 opacity-0 transition-opacity group-hover/post:opacity-100",
|
|
2022
|
+
"aria-hidden": true
|
|
2023
|
+
}
|
|
2024
|
+
),
|
|
2025
|
+
onDelete && /* @__PURE__ */ jsx(
|
|
2026
|
+
"button",
|
|
2027
|
+
{
|
|
2028
|
+
type: "button",
|
|
2029
|
+
"aria-label": t("marketing.content.deletePost"),
|
|
2030
|
+
onClick: (e) => {
|
|
2031
|
+
e.stopPropagation();
|
|
2032
|
+
onDelete();
|
|
2033
|
+
},
|
|
2034
|
+
className: "absolute right-1.5 top-2 rounded p-1 text-muted-foreground/60 opacity-0 transition-opacity hover:bg-destructive/10 hover:text-destructive group-hover/post:opacity-100",
|
|
2035
|
+
children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" })
|
|
2036
|
+
}
|
|
2037
|
+
),
|
|
2038
|
+
/* @__PURE__ */ jsx("p", { className: "pr-5 text-sm font-medium text-foreground", children: post.title || t("marketing.content.untitled") }),
|
|
2039
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [
|
|
2040
|
+
/* @__PURE__ */ jsx(FormatBadge, { format: post.format }),
|
|
2041
|
+
/* @__PURE__ */ jsx(PostStatusBadge, { status: post.status }),
|
|
2042
|
+
post.scheduledDate && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: shortDay(/* @__PURE__ */ new Date(`${post.scheduledDate}T00:00:00`)) }),
|
|
2043
|
+
/* @__PURE__ */ jsx(PlatformBadges, { platforms: post.platforms })
|
|
2044
|
+
] })
|
|
2045
|
+
]
|
|
2134
2046
|
}
|
|
2135
2047
|
);
|
|
2136
2048
|
}
|
|
2137
|
-
function
|
|
2049
|
+
function WeekColumn({ week, rangeLabel, posts, onOpenPost, onNewPost, onDeletePost, onDropPost, disabled }) {
|
|
2138
2050
|
const t = useTranslation();
|
|
2139
|
-
const
|
|
2140
|
-
return /* @__PURE__ */ jsxs(
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2051
|
+
const [isDropTarget, setIsDropTarget] = React12.useState(false);
|
|
2052
|
+
return /* @__PURE__ */ jsxs(
|
|
2053
|
+
"div",
|
|
2054
|
+
{
|
|
2055
|
+
className: cn(
|
|
2056
|
+
"flex flex-col gap-2 rounded-card border border-border bg-muted/20 p-2.5 transition-colors",
|
|
2057
|
+
isDropTarget && "border-primary bg-primary/5 ring-1 ring-primary/40"
|
|
2058
|
+
),
|
|
2059
|
+
onDragOver: (e) => {
|
|
2060
|
+
if (!onDropPost || !e.dataTransfer.types.includes(POST_DRAG_MIME)) return;
|
|
2061
|
+
e.preventDefault();
|
|
2062
|
+
e.dataTransfer.dropEffect = "move";
|
|
2063
|
+
setIsDropTarget(true);
|
|
2064
|
+
},
|
|
2065
|
+
onDragLeave: () => setIsDropTarget(false),
|
|
2066
|
+
onDrop: (e) => {
|
|
2067
|
+
if (!onDropPost) return;
|
|
2068
|
+
e.preventDefault();
|
|
2069
|
+
setIsDropTarget(false);
|
|
2070
|
+
const postId = e.dataTransfer.getData(POST_DRAG_MIME);
|
|
2071
|
+
if (postId) onDropPost(postId, week);
|
|
2072
|
+
},
|
|
2073
|
+
children: [
|
|
2074
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline justify-between gap-x-2 gap-y-0.5 px-1", children: [
|
|
2075
|
+
/* @__PURE__ */ jsxs("p", { className: "whitespace-nowrap text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: [
|
|
2076
|
+
t("marketing.content.week"),
|
|
2077
|
+
" ",
|
|
2078
|
+
week
|
|
2079
|
+
] }),
|
|
2080
|
+
/* @__PURE__ */ jsx("p", { className: "whitespace-nowrap text-xs text-muted-foreground/80", children: rangeLabel })
|
|
2081
|
+
] }),
|
|
2082
|
+
posts.map((post) => /* @__PURE__ */ jsx(
|
|
2083
|
+
PostCard,
|
|
2084
|
+
{
|
|
2085
|
+
post,
|
|
2086
|
+
onOpen: () => onOpenPost(post.id),
|
|
2087
|
+
onDelete: onDeletePost ? () => onDeletePost(post.id) : void 0
|
|
2088
|
+
},
|
|
2089
|
+
post.id
|
|
2090
|
+
)),
|
|
2091
|
+
/* @__PURE__ */ jsxs(
|
|
2092
|
+
"button",
|
|
2093
|
+
{
|
|
2094
|
+
type: "button",
|
|
2095
|
+
onClick: () => onNewPost(week),
|
|
2096
|
+
disabled,
|
|
2097
|
+
className: "flex items-center justify-center gap-1 rounded-card border border-dashed border-border p-2 text-xs font-medium text-muted-foreground transition-colors hover:bg-muted/40 hover:text-foreground disabled:pointer-events-none disabled:opacity-50",
|
|
2098
|
+
children: [
|
|
2099
|
+
/* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5" }),
|
|
2100
|
+
t("marketing.content.newPost")
|
|
2101
|
+
]
|
|
2102
|
+
}
|
|
2103
|
+
)
|
|
2104
|
+
]
|
|
2105
|
+
}
|
|
2106
|
+
);
|
|
2161
2107
|
}
|
|
2162
|
-
function
|
|
2108
|
+
function AccountModal({ open, account, onClose }) {
|
|
2163
2109
|
const t = useTranslation();
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2110
|
+
const saveAccount = useContentPlannerStore((s) => s.saveAccount);
|
|
2111
|
+
const deleteAccount = useContentPlannerStore((s) => s.deleteAccount);
|
|
2112
|
+
const isEdit = !!account;
|
|
2113
|
+
const [name, setName] = React12.useState("");
|
|
2114
|
+
const [handle, setHandle] = React12.useState("");
|
|
2115
|
+
const [platforms, setPlatforms] = React12.useState(["instagram"]);
|
|
2116
|
+
const [saving, setSaving] = React12.useState(false);
|
|
2117
|
+
const [confirmDelete, setConfirmDelete] = React12.useState(false);
|
|
2118
|
+
React12.useEffect(() => {
|
|
2119
|
+
if (open) {
|
|
2120
|
+
setName(account?.name ?? "");
|
|
2121
|
+
setHandle(account?.handle ?? "");
|
|
2122
|
+
setPlatforms(account?.platforms?.length ? account.platforms : ["instagram"]);
|
|
2123
|
+
setSaving(false);
|
|
2124
|
+
setConfirmDelete(false);
|
|
2125
|
+
}
|
|
2126
|
+
}, [open, account]);
|
|
2127
|
+
const create = async () => {
|
|
2128
|
+
if (!name.trim() || platforms.length === 0 || saving) return;
|
|
2129
|
+
setSaving(true);
|
|
2130
|
+
try {
|
|
2131
|
+
await saveAccount({
|
|
2132
|
+
...isEdit ? { id: account.id } : {},
|
|
2133
|
+
name: name.trim(),
|
|
2134
|
+
handle: handle.trim() || void 0,
|
|
2135
|
+
platforms
|
|
2136
|
+
});
|
|
2137
|
+
onClose();
|
|
2138
|
+
} finally {
|
|
2139
|
+
setSaving(false);
|
|
2140
|
+
}
|
|
2141
|
+
};
|
|
2142
|
+
const remove = async () => {
|
|
2143
|
+
if (!account || saving) return;
|
|
2144
|
+
setSaving(true);
|
|
2145
|
+
try {
|
|
2146
|
+
await deleteAccount(account.id);
|
|
2147
|
+
onClose();
|
|
2148
|
+
} finally {
|
|
2149
|
+
setSaving(false);
|
|
2150
|
+
}
|
|
2151
|
+
};
|
|
2152
|
+
return /* @__PURE__ */ jsx(Modal, { open, onOpenChange: (o) => {
|
|
2153
|
+
if (!o) onClose();
|
|
2154
|
+
}, children: /* @__PURE__ */ jsxs(ModalContent, { size: "sm", children: [
|
|
2155
|
+
/* @__PURE__ */ jsx(ModalHeader, { children: /* @__PURE__ */ jsx(ModalTitle, { children: isEdit ? t("marketing.content.editAccountTitle") : t("marketing.content.addAccountTitle") }) }),
|
|
2156
|
+
/* @__PURE__ */ jsxs(ModalBody, { className: "space-y-4", children: [
|
|
2157
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
2158
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t("marketing.content.nameLabel") }),
|
|
2159
|
+
/* @__PURE__ */ jsx(
|
|
2160
|
+
Input,
|
|
2161
|
+
{
|
|
2162
|
+
autoFocus: true,
|
|
2163
|
+
value: name,
|
|
2164
|
+
onChange: (e) => setName(e.target.value),
|
|
2165
|
+
placeholder: t("marketing.content.accountNamePlaceholder"),
|
|
2166
|
+
onKeyDown: (e) => {
|
|
2167
|
+
if (e.key === "Enter") void create();
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
)
|
|
2184
2171
|
] }),
|
|
2185
|
-
/* @__PURE__ */ jsxs("
|
|
2186
|
-
/* @__PURE__ */ jsx(
|
|
2187
|
-
|
|
2188
|
-
|
|
2172
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
2173
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t("marketing.content.handleLabel") }),
|
|
2174
|
+
/* @__PURE__ */ jsx(
|
|
2175
|
+
Input,
|
|
2176
|
+
{
|
|
2177
|
+
value: handle,
|
|
2178
|
+
onChange: (e) => setHandle(e.target.value),
|
|
2179
|
+
placeholder: t("marketing.content.handlePlaceholder")
|
|
2180
|
+
}
|
|
2181
|
+
)
|
|
2189
2182
|
] }),
|
|
2190
|
-
/* @__PURE__ */ jsxs("
|
|
2191
|
-
/* @__PURE__ */ jsx(
|
|
2192
|
-
|
|
2193
|
-
t("marketing.content.previewShare")
|
|
2183
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
2184
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t("marketing.content.platformsLabel") }),
|
|
2185
|
+
/* @__PURE__ */ jsx(PlatformPicker, { value: platforms, onChange: setPlatforms })
|
|
2194
2186
|
] })
|
|
2195
|
-
] })
|
|
2196
|
-
|
|
2187
|
+
] }),
|
|
2188
|
+
/* @__PURE__ */ jsx(ModalFooter, { children: confirmDelete && isEdit ? /* @__PURE__ */ jsxs("div", { className: "flex w-full items-center gap-3 rounded-card border border-destructive/30 bg-destructive/5 p-3", children: [
|
|
2189
|
+
/* @__PURE__ */ jsx("p", { className: "flex-1 text-xs text-destructive", children: t("marketing.content.deleteAccountConfirm") }),
|
|
2190
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: () => setConfirmDelete(false), disabled: saving, children: t("marketing.content.cancel") }),
|
|
2191
|
+
/* @__PURE__ */ jsx(Button, { variant: "destructive", size: "sm", onClick: () => void remove(), disabled: saving, children: t("marketing.content.deleteAccount") })
|
|
2192
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2193
|
+
isEdit && /* @__PURE__ */ jsxs(
|
|
2194
|
+
Button,
|
|
2195
|
+
{
|
|
2196
|
+
variant: "outline",
|
|
2197
|
+
onClick: () => setConfirmDelete(true),
|
|
2198
|
+
disabled: saving,
|
|
2199
|
+
className: "mr-auto text-destructive hover:text-destructive",
|
|
2200
|
+
children: [
|
|
2201
|
+
/* @__PURE__ */ jsx(Trash2, { className: "mr-1.5 h-4 w-4" }),
|
|
2202
|
+
t("marketing.content.deleteAccount")
|
|
2203
|
+
]
|
|
2204
|
+
}
|
|
2205
|
+
),
|
|
2206
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", onClick: onClose, disabled: saving, children: t("marketing.content.cancel") }),
|
|
2207
|
+
/* @__PURE__ */ jsx(Button, { onClick: () => void create(), disabled: saving || !name.trim() || platforms.length === 0, children: isEdit ? t("marketing.content.save") : t("marketing.content.createAccount") })
|
|
2208
|
+
] }) })
|
|
2209
|
+
] }) });
|
|
2197
2210
|
}
|
|
2198
|
-
function
|
|
2211
|
+
function AccountSelector() {
|
|
2199
2212
|
const t = useTranslation();
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2213
|
+
const loaded = useContentPlannerStore((s) => s.loaded);
|
|
2214
|
+
const accounts = useContentPlannerStore((s) => s.accounts);
|
|
2215
|
+
const activeAccountId = useContentPlannerStore((s) => s.activeAccountId);
|
|
2216
|
+
const setAccount = useContentPlannerStore((s) => s.setAccount);
|
|
2217
|
+
const [open, setOpen] = React12.useState(false);
|
|
2218
|
+
const [modal, setModal] = React12.useState(null);
|
|
2219
|
+
const activeAccount = accounts.find((a) => a.id === activeAccountId) ?? null;
|
|
2220
|
+
if (!loaded) {
|
|
2221
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
2222
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-64" }),
|
|
2223
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-9" })
|
|
2224
|
+
] });
|
|
2225
|
+
}
|
|
2226
|
+
const openSettings = (account) => {
|
|
2227
|
+
setOpen(false);
|
|
2228
|
+
setModal({ mode: "edit", account });
|
|
2229
|
+
};
|
|
2230
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2231
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
2232
|
+
/* @__PURE__ */ jsxs(
|
|
2233
|
+
Select,
|
|
2234
|
+
{
|
|
2235
|
+
open,
|
|
2236
|
+
onOpenChange: setOpen,
|
|
2237
|
+
value: activeAccountId ?? void 0,
|
|
2238
|
+
onValueChange: (id) => {
|
|
2239
|
+
if (id === ADD_ACCOUNT_VALUE) {
|
|
2240
|
+
setModal({ mode: "create" });
|
|
2241
|
+
return;
|
|
2242
|
+
}
|
|
2243
|
+
void setAccount(id);
|
|
2244
|
+
},
|
|
2245
|
+
children: [
|
|
2246
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "w-64", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: t("marketing.content.account") }) }),
|
|
2247
|
+
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
2248
|
+
accounts.length === 0 ? /* @__PURE__ */ jsx(SelectItem, { value: "__none__", disabled: true, children: t("marketing.content.noAccountsAvailable") }) : accounts.map((acc) => /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
2249
|
+
/* @__PURE__ */ jsx(SelectItem, { value: acc.id, className: "pr-9", children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
|
|
2250
|
+
acc.name,
|
|
2251
|
+
/* @__PURE__ */ jsx(PlatformBadges, { platforms: acc.platforms })
|
|
2252
|
+
] }) }),
|
|
2253
|
+
/* @__PURE__ */ jsx(
|
|
2254
|
+
"button",
|
|
2255
|
+
{
|
|
2256
|
+
type: "button",
|
|
2257
|
+
"aria-label": t("marketing.content.accountSettings"),
|
|
2258
|
+
onPointerDown: (e) => {
|
|
2259
|
+
e.stopPropagation();
|
|
2260
|
+
e.preventDefault();
|
|
2261
|
+
},
|
|
2262
|
+
onPointerUp: (e) => {
|
|
2263
|
+
e.stopPropagation();
|
|
2264
|
+
e.preventDefault();
|
|
2265
|
+
},
|
|
2266
|
+
onClick: (e) => {
|
|
2267
|
+
e.stopPropagation();
|
|
2268
|
+
openSettings(acc);
|
|
2269
|
+
},
|
|
2270
|
+
className: "absolute right-2 top-1/2 -translate-y-1/2 rounded p-1 text-muted-foreground/70 transition-colors hover:bg-muted/60 hover:text-foreground",
|
|
2271
|
+
children: /* @__PURE__ */ jsx(Settings2, { className: "h-3.5 w-3.5" })
|
|
2272
|
+
}
|
|
2273
|
+
)
|
|
2274
|
+
] }, acc.id)),
|
|
2275
|
+
/* @__PURE__ */ jsx(SelectSeparator, {}),
|
|
2276
|
+
/* @__PURE__ */ jsx(SelectItem, { value: ADD_ACCOUNT_VALUE, children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
|
|
2277
|
+
/* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5" }),
|
|
2278
|
+
t("marketing.content.addAccount")
|
|
2279
|
+
] }) })
|
|
2280
|
+
] })
|
|
2281
|
+
]
|
|
2282
|
+
}
|
|
2283
|
+
),
|
|
2284
|
+
activeAccount && /* @__PURE__ */ jsx(
|
|
2285
|
+
Button,
|
|
2286
|
+
{
|
|
2287
|
+
variant: "ghost",
|
|
2288
|
+
size: "sm",
|
|
2289
|
+
className: "h-9 w-9 p-0 text-muted-foreground",
|
|
2290
|
+
"aria-label": t("marketing.content.accountSettings"),
|
|
2291
|
+
onClick: () => setModal({ mode: "edit", account: activeAccount }),
|
|
2292
|
+
children: /* @__PURE__ */ jsx(Settings2, { className: "h-4 w-4" })
|
|
2293
|
+
}
|
|
2294
|
+
)
|
|
2207
2295
|
] }),
|
|
2208
|
-
/* @__PURE__ */ jsx(
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2296
|
+
/* @__PURE__ */ jsx(
|
|
2297
|
+
AccountModal,
|
|
2298
|
+
{
|
|
2299
|
+
open: !!modal,
|
|
2300
|
+
account: modal?.mode === "edit" ? modal.account : null,
|
|
2301
|
+
onClose: () => setModal(null)
|
|
2302
|
+
}
|
|
2303
|
+
)
|
|
2216
2304
|
] });
|
|
2217
2305
|
}
|
|
2218
|
-
function
|
|
2306
|
+
function ViewSwitcher({ mode, onChange }) {
|
|
2219
2307
|
const t = useTranslation();
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
"
|
|
2230
|
-
t("marketing.content.
|
|
2231
|
-
] })
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
/* @__PURE__ */
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
] }) });
|
|
2308
|
+
return /* @__PURE__ */ jsx(
|
|
2309
|
+
SegmentedControl,
|
|
2310
|
+
{
|
|
2311
|
+
size: "md",
|
|
2312
|
+
"aria-label": t("marketing.content.viewBoard"),
|
|
2313
|
+
value: mode,
|
|
2314
|
+
onChange,
|
|
2315
|
+
options: [
|
|
2316
|
+
{ value: "board", label: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
|
|
2317
|
+
/* @__PURE__ */ jsx(Columns3, { className: "h-3.5 w-3.5" }),
|
|
2318
|
+
t("marketing.content.viewBoard")
|
|
2319
|
+
] }) },
|
|
2320
|
+
{ value: "calendar", label: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
|
|
2321
|
+
/* @__PURE__ */ jsx(CalendarDays, { className: "h-3.5 w-3.5" }),
|
|
2322
|
+
t("marketing.content.viewCalendar")
|
|
2323
|
+
] }) },
|
|
2324
|
+
{ value: "list", label: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
|
|
2325
|
+
/* @__PURE__ */ jsx(List, { className: "h-3.5 w-3.5" }),
|
|
2326
|
+
t("marketing.content.viewList")
|
|
2327
|
+
] }) }
|
|
2328
|
+
]
|
|
2329
|
+
}
|
|
2330
|
+
);
|
|
2244
2331
|
}
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2332
|
+
function shortDay(d) {
|
|
2333
|
+
return d.toLocaleDateString(getCurrentLocale(), { day: "numeric", month: "short" });
|
|
2334
|
+
}
|
|
2335
|
+
function buildBoardWeeks(startDate, weeksCount) {
|
|
2336
|
+
return Array.from({ length: weeksCount }, (_, i) => {
|
|
2337
|
+
const start = new Date(startDate);
|
|
2338
|
+
start.setDate(start.getDate() + i * 7);
|
|
2339
|
+
const end = new Date(start);
|
|
2340
|
+
end.setDate(start.getDate() + 6);
|
|
2341
|
+
return {
|
|
2342
|
+
week: i + 1,
|
|
2343
|
+
rangeLabel: `${shortDay(start)} \u2013 ${shortDay(end)}`,
|
|
2344
|
+
monthLabel: start.toLocaleDateString(getCurrentLocale(), { month: "long", year: "numeric" })
|
|
2345
|
+
};
|
|
2346
|
+
});
|
|
2347
|
+
}
|
|
2348
|
+
function BoardView({ startDate, weeksCount, postsByWeek, onOpenPost, onNewPost, onDeletePost, onDropPost, disabled }) {
|
|
2349
|
+
const months = [];
|
|
2350
|
+
for (const bw of buildBoardWeeks(startDate, weeksCount)) {
|
|
2351
|
+
const current = months[months.length - 1];
|
|
2352
|
+
if (current && current.label === bw.monthLabel) current.weeks.push(bw);
|
|
2353
|
+
else months.push({ label: bw.monthLabel, weeks: [bw] });
|
|
2354
|
+
}
|
|
2355
|
+
return /* @__PURE__ */ jsx("div", { className: "space-y-8", children: months.map(({ label, weeks }) => /* @__PURE__ */ jsxs("section", { className: "space-y-3", children: [
|
|
2356
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: label }),
|
|
2357
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4", children: weeks.map(({ week, rangeLabel }) => /* @__PURE__ */ jsx(
|
|
2358
|
+
WeekColumn,
|
|
2259
2359
|
{
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
/* @__PURE__ */ jsx(PlatformIcon, { platform, className: "h-3.5 w-3.5" }),
|
|
2269
|
-
platform
|
|
2270
|
-
]
|
|
2360
|
+
week,
|
|
2361
|
+
rangeLabel,
|
|
2362
|
+
posts: postsByWeek.get(week) ?? [],
|
|
2363
|
+
onOpenPost,
|
|
2364
|
+
onNewPost,
|
|
2365
|
+
onDeletePost,
|
|
2366
|
+
onDropPost,
|
|
2367
|
+
disabled
|
|
2271
2368
|
},
|
|
2272
|
-
|
|
2273
|
-
)) })
|
|
2274
|
-
|
|
2275
|
-
] });
|
|
2369
|
+
week
|
|
2370
|
+
)) })
|
|
2371
|
+
] }, label)) });
|
|
2276
2372
|
}
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
- Abertura (dor / promessa)
|
|
2419
|
-
- Desenvolvimento em 3 pontos
|
|
2420
|
-
- Conclus\xE3o + CTA
|
|
2421
|
-
|
|
2422
|
-
## Rascunho
|
|
2423
|
-
|
|
2424
|
-
\u2026
|
|
2425
|
-
`;
|
|
2426
|
-
var POST_TEMPLATES = {
|
|
2427
|
-
reel,
|
|
2428
|
-
static: staticPost,
|
|
2429
|
-
carousel,
|
|
2430
|
-
story,
|
|
2431
|
-
video,
|
|
2432
|
-
live,
|
|
2433
|
-
article
|
|
2434
|
-
};
|
|
2435
|
-
function MediaAsset({ post, onChanged }) {
|
|
2373
|
+
function toISODate(d) {
|
|
2374
|
+
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
2375
|
+
}
|
|
2376
|
+
function mondayOfCurrentWeek() {
|
|
2377
|
+
const now = /* @__PURE__ */ new Date();
|
|
2378
|
+
const monday = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
2379
|
+
monday.setDate(monday.getDate() - (monday.getDay() + 6) % 7);
|
|
2380
|
+
return monday;
|
|
2381
|
+
}
|
|
2382
|
+
function CalendarView({ posts, onOpenPost, onNewPostAt, disabled }) {
|
|
2383
|
+
const t = useTranslation();
|
|
2384
|
+
const [cursor, setCursor] = React12.useState(() => {
|
|
2385
|
+
const first = posts.find((p) => p.scheduledDate)?.scheduledDate;
|
|
2386
|
+
const base = first ? /* @__PURE__ */ new Date(`${first}T00:00:00`) : /* @__PURE__ */ new Date();
|
|
2387
|
+
return new Date(base.getFullYear(), base.getMonth(), 1);
|
|
2388
|
+
});
|
|
2389
|
+
const [expandedDay, setExpandedDay] = React12.useState(null);
|
|
2390
|
+
const MAX_VISIBLE = 3;
|
|
2391
|
+
const postsByDate = React12.useMemo(() => {
|
|
2392
|
+
const map = /* @__PURE__ */ new Map();
|
|
2393
|
+
for (const post of posts) {
|
|
2394
|
+
if (!post.scheduledDate) continue;
|
|
2395
|
+
const list = map.get(post.scheduledDate) ?? [];
|
|
2396
|
+
list.push(post);
|
|
2397
|
+
map.set(post.scheduledDate, list);
|
|
2398
|
+
}
|
|
2399
|
+
return map;
|
|
2400
|
+
}, [posts]);
|
|
2401
|
+
const unscheduled = posts.filter((p) => !p.scheduledDate);
|
|
2402
|
+
const days = React12.useMemo(() => {
|
|
2403
|
+
const firstOfMonth = new Date(cursor.getFullYear(), cursor.getMonth(), 1);
|
|
2404
|
+
const start = new Date(firstOfMonth);
|
|
2405
|
+
start.setDate(start.getDate() - (firstOfMonth.getDay() + 6) % 7);
|
|
2406
|
+
const cells = [];
|
|
2407
|
+
for (let i = 0; i < 42; i++) {
|
|
2408
|
+
const d = new Date(start);
|
|
2409
|
+
d.setDate(start.getDate() + i);
|
|
2410
|
+
cells.push(d);
|
|
2411
|
+
}
|
|
2412
|
+
while (cells.length > 7 && cells[cells.length - 7].getMonth() !== cursor.getMonth()) {
|
|
2413
|
+
cells.splice(cells.length - 7, 7);
|
|
2414
|
+
}
|
|
2415
|
+
return cells;
|
|
2416
|
+
}, [cursor]);
|
|
2417
|
+
const monthLabel = cursor.toLocaleDateString(getCurrentLocale(), { month: "long", year: "numeric" });
|
|
2418
|
+
const weekdayLabels = days.slice(0, 7).map(
|
|
2419
|
+
(d) => d.toLocaleDateString(getCurrentLocale(), { weekday: "short" })
|
|
2420
|
+
);
|
|
2421
|
+
const todayISO = toISODate(/* @__PURE__ */ new Date());
|
|
2422
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
2423
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
2424
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold capitalize text-foreground", children: monthLabel }),
|
|
2425
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
2426
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: () => setCursor(new Date(cursor.getFullYear(), cursor.getMonth() - 1, 1)), children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" }) }),
|
|
2427
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: () => setCursor(new Date(cursor.getFullYear(), cursor.getMonth() + 1, 1)), children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" }) })
|
|
2428
|
+
] })
|
|
2429
|
+
] }),
|
|
2430
|
+
unscheduled.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 rounded-card border border-dashed border-border bg-muted/20 p-2.5", children: [
|
|
2431
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium uppercase tracking-wide text-muted-foreground", children: t("marketing.content.unscheduled") }),
|
|
2432
|
+
unscheduled.map((post) => /* @__PURE__ */ jsxs(
|
|
2433
|
+
"button",
|
|
2434
|
+
{
|
|
2435
|
+
type: "button",
|
|
2436
|
+
onClick: () => onOpenPost(post.id),
|
|
2437
|
+
className: "inline-flex items-center gap-1.5 rounded-full border border-border bg-card px-2.5 py-1 text-xs font-medium text-foreground transition-colors hover:bg-muted/40",
|
|
2438
|
+
children: [
|
|
2439
|
+
post.title || t("marketing.content.untitled"),
|
|
2440
|
+
/* @__PURE__ */ jsx(FormatBadge, { format: post.format })
|
|
2441
|
+
]
|
|
2442
|
+
},
|
|
2443
|
+
post.id
|
|
2444
|
+
))
|
|
2445
|
+
] }),
|
|
2446
|
+
/* @__PURE__ */ jsxs("div", { className: "overflow-hidden rounded-card border border-border", children: [
|
|
2447
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 border-b border-border bg-muted/30", children: weekdayLabels.map((label, i) => /* @__PURE__ */ jsx("div", { className: "px-2 py-1.5 text-center text-xs font-medium capitalize text-muted-foreground", children: label }, i)) }),
|
|
2448
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-7", children: days.map((day, i) => {
|
|
2449
|
+
const iso = toISODate(day);
|
|
2450
|
+
const inMonth = day.getMonth() === cursor.getMonth();
|
|
2451
|
+
const dayPosts = postsByDate.get(iso) ?? [];
|
|
2452
|
+
return /* @__PURE__ */ jsxs(
|
|
2453
|
+
"div",
|
|
2454
|
+
{
|
|
2455
|
+
className: cn(
|
|
2456
|
+
"group min-h-24 border-b border-r border-border p-1.5 [&:nth-child(7n)]:border-r-0",
|
|
2457
|
+
!inMonth && "bg-muted/20"
|
|
2458
|
+
),
|
|
2459
|
+
children: [
|
|
2460
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
2461
|
+
/* @__PURE__ */ jsx(
|
|
2462
|
+
"span",
|
|
2463
|
+
{
|
|
2464
|
+
className: cn(
|
|
2465
|
+
"text-xs",
|
|
2466
|
+
inMonth ? "text-foreground" : "text-muted-foreground/60",
|
|
2467
|
+
iso === todayISO && "flex h-5 w-5 items-center justify-center rounded-full bg-primary font-semibold text-primary-foreground"
|
|
2468
|
+
),
|
|
2469
|
+
children: day.getDate()
|
|
2470
|
+
}
|
|
2471
|
+
),
|
|
2472
|
+
!disabled && /* @__PURE__ */ jsx(
|
|
2473
|
+
"button",
|
|
2474
|
+
{
|
|
2475
|
+
type: "button",
|
|
2476
|
+
onClick: () => onNewPostAt(iso),
|
|
2477
|
+
className: "rounded p-0.5 text-muted-foreground opacity-0 transition-opacity hover:bg-muted/60 hover:text-foreground group-hover:opacity-100",
|
|
2478
|
+
"aria-label": t("marketing.content.newPost"),
|
|
2479
|
+
children: /* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5" })
|
|
2480
|
+
}
|
|
2481
|
+
)
|
|
2482
|
+
] }),
|
|
2483
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-1 space-y-1", children: [
|
|
2484
|
+
(expandedDay === iso ? dayPosts : dayPosts.slice(0, MAX_VISIBLE)).map((post) => /* @__PURE__ */ jsx(
|
|
2485
|
+
"button",
|
|
2486
|
+
{
|
|
2487
|
+
type: "button",
|
|
2488
|
+
onClick: () => onOpenPost(post.id),
|
|
2489
|
+
className: "block w-full truncate rounded border border-border bg-card px-1.5 py-1 text-left text-xs text-foreground transition-colors hover:bg-muted/40",
|
|
2490
|
+
children: post.title || t("marketing.content.untitled")
|
|
2491
|
+
},
|
|
2492
|
+
post.id
|
|
2493
|
+
)),
|
|
2494
|
+
dayPosts.length > MAX_VISIBLE && /* @__PURE__ */ jsx(
|
|
2495
|
+
"button",
|
|
2496
|
+
{
|
|
2497
|
+
type: "button",
|
|
2498
|
+
onClick: () => setExpandedDay(expandedDay === iso ? null : iso),
|
|
2499
|
+
className: "block w-full px-1.5 py-0.5 text-left text-xs font-medium text-muted-foreground transition-colors hover:text-foreground",
|
|
2500
|
+
children: expandedDay === iso ? t("marketing.content.showLess") : `+${dayPosts.length - MAX_VISIBLE}`
|
|
2501
|
+
}
|
|
2502
|
+
)
|
|
2503
|
+
] })
|
|
2504
|
+
]
|
|
2505
|
+
},
|
|
2506
|
+
i
|
|
2507
|
+
);
|
|
2508
|
+
}) })
|
|
2509
|
+
] })
|
|
2510
|
+
] });
|
|
2511
|
+
}
|
|
2512
|
+
function ListView({ posts, onOpenPost }) {
|
|
2436
2513
|
const t = useTranslation();
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2514
|
+
if (posts.length === 0) {
|
|
2515
|
+
return /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t("marketing.content.noPosts") });
|
|
2516
|
+
}
|
|
2517
|
+
return /* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-card border border-border", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
|
|
2518
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b border-border bg-muted/30 text-left text-xs font-medium uppercase tracking-wide text-muted-foreground", children: [
|
|
2519
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2", children: t("marketing.content.listWeek") }),
|
|
2520
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2", children: t("marketing.content.listTitle") }),
|
|
2521
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2", children: t("marketing.content.listFormat") }),
|
|
2522
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2", children: t("marketing.content.listStatus") }),
|
|
2523
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2", children: t("marketing.content.listDate") })
|
|
2524
|
+
] }) }),
|
|
2525
|
+
/* @__PURE__ */ jsx("tbody", { children: posts.map((post) => /* @__PURE__ */ jsxs(
|
|
2526
|
+
"tr",
|
|
2527
|
+
{
|
|
2528
|
+
onClick: () => onOpenPost(post.id),
|
|
2529
|
+
className: "cursor-pointer border-b border-border last:border-b-0 transition-colors hover:bg-muted/30",
|
|
2530
|
+
children: [
|
|
2531
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-muted-foreground", children: post.weekNumber }),
|
|
2532
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 font-medium text-foreground", children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
|
|
2533
|
+
post.title || t("marketing.content.untitled"),
|
|
2534
|
+
/* @__PURE__ */ jsx(PlatformBadges, { platforms: post.platforms })
|
|
2535
|
+
] }) }),
|
|
2536
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2", children: /* @__PURE__ */ jsx(FormatBadge, { format: post.format }) }),
|
|
2537
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2", children: /* @__PURE__ */ jsx(PostStatusBadge, { status: post.status }) }),
|
|
2538
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-muted-foreground", children: post.scheduledDate ? shortDay(/* @__PURE__ */ new Date(`${post.scheduledDate}T00:00:00`)) : "\u2014" })
|
|
2539
|
+
]
|
|
2540
|
+
},
|
|
2541
|
+
post.id
|
|
2542
|
+
)) })
|
|
2543
|
+
] }) });
|
|
2544
|
+
}
|
|
2545
|
+
function ContentView({ initialPostId = null }) {
|
|
2546
|
+
const t = useTranslation();
|
|
2547
|
+
const [openPostId, setOpenPostId] = React12.useState(initialPostId);
|
|
2548
|
+
const onOpenPost = React12.useCallback((id) => {
|
|
2549
|
+
setOpenPostId(id);
|
|
2550
|
+
window.history.replaceState(window.history.state, "", id ? `#/marketing/content/post/${id}` : "#/marketing/content");
|
|
2551
|
+
}, []);
|
|
2552
|
+
const loaded = useContentPlannerStore((s) => s.loaded);
|
|
2553
|
+
const loading = useContentPlannerStore((s) => s.loading);
|
|
2554
|
+
const accounts = useContentPlannerStore((s) => s.accounts);
|
|
2555
|
+
const activeAccountId = useContentPlannerStore((s) => s.activeAccountId);
|
|
2556
|
+
const plan = useContentPlannerStore((s) => s.activePlan);
|
|
2557
|
+
const posts = useContentPlannerStore((s) => s.posts);
|
|
2558
|
+
const load = useContentPlannerStore((s) => s.load);
|
|
2559
|
+
const savePlan = useContentPlannerStore((s) => s.savePlan);
|
|
2560
|
+
const savePost = useContentPlannerStore((s) => s.savePost);
|
|
2561
|
+
const deletePost = useContentPlannerStore((s) => s.deletePost);
|
|
2562
|
+
const viewMode = useContentPlannerStore((s) => s.viewMode);
|
|
2563
|
+
const setViewMode = useContentPlannerStore((s) => s.setViewMode);
|
|
2564
|
+
const loadedTenantId = useContentPlannerStore((s) => s.loadedTenantId);
|
|
2565
|
+
const guardContentPosts = useLimitGuard("content_posts_month");
|
|
2566
|
+
const [briefOpen, setBriefOpen] = React12.useState(false);
|
|
2567
|
+
const [pendingDeleteId, setPendingDeleteId] = React12.useState(null);
|
|
2568
|
+
const tenantId = useOrganizationStore((state) => state.currentOrg?.id);
|
|
2569
|
+
React12.useEffect(() => {
|
|
2570
|
+
if (!loading && (!loaded || tenantId && tenantId !== loadedTenantId)) void load();
|
|
2571
|
+
}, [tenantId, loadedTenantId, loaded, loading, load]);
|
|
2572
|
+
const postsByWeek = React12.useMemo(() => {
|
|
2573
|
+
const map = /* @__PURE__ */ new Map();
|
|
2574
|
+
for (const post of posts) {
|
|
2575
|
+
const list = map.get(post.weekNumber) ?? [];
|
|
2576
|
+
list.push(post);
|
|
2577
|
+
map.set(post.weekNumber, list);
|
|
2578
|
+
}
|
|
2579
|
+
return map;
|
|
2580
|
+
}, [posts]);
|
|
2581
|
+
const ensurePlan = async () => {
|
|
2582
|
+
if (plan) return plan;
|
|
2583
|
+
if (!activeAccountId) return null;
|
|
2584
|
+
return savePlan({
|
|
2585
|
+
accountId: activeAccountId,
|
|
2586
|
+
name: t("marketing.content.defaultPlanName"),
|
|
2587
|
+
status: "active",
|
|
2588
|
+
weeksCount: DEFAULT_WEEKS,
|
|
2589
|
+
startDate: toISODate(mondayOfCurrentWeek())
|
|
2590
|
+
});
|
|
2591
|
+
};
|
|
2592
|
+
const createPost = async (week) => {
|
|
2593
|
+
if (await guardContentPosts() === "blocked") return;
|
|
2594
|
+
const targetPlan = await ensurePlan();
|
|
2595
|
+
if (!targetPlan) return;
|
|
2596
|
+
const start = targetPlan.startDate ? /* @__PURE__ */ new Date(`${targetPlan.startDate}T00:00:00`) : mondayOfCurrentWeek();
|
|
2597
|
+
start.setDate(start.getDate() + (week - 1) * 7);
|
|
2598
|
+
const post = await savePost({
|
|
2599
|
+
planId: targetPlan.id,
|
|
2600
|
+
weekNumber: week,
|
|
2601
|
+
title: "",
|
|
2602
|
+
format: "reel",
|
|
2603
|
+
status: "idea",
|
|
2604
|
+
scheduledDate: toISODate(start)
|
|
2605
|
+
});
|
|
2606
|
+
invalidateLimit("content_posts_month");
|
|
2607
|
+
onOpenPost(post.id);
|
|
2608
|
+
};
|
|
2609
|
+
const movePost = async (postId, week) => {
|
|
2610
|
+
const post = posts.find((p) => p.id === postId);
|
|
2611
|
+
if (!post || post.weekNumber === week) return;
|
|
2612
|
+
await savePost({ id: postId, weekNumber: week });
|
|
2613
|
+
};
|
|
2614
|
+
const createPostAt = async (isoDate) => {
|
|
2615
|
+
if (await guardContentPosts() === "blocked") return;
|
|
2616
|
+
const targetPlan = await ensurePlan();
|
|
2617
|
+
if (!targetPlan) return;
|
|
2618
|
+
let week = 1;
|
|
2619
|
+
if (targetPlan.startDate) {
|
|
2620
|
+
const start = /* @__PURE__ */ new Date(`${targetPlan.startDate}T00:00:00`);
|
|
2621
|
+
const day = /* @__PURE__ */ new Date(`${isoDate}T00:00:00`);
|
|
2622
|
+
const diff = Math.floor((day.getTime() - start.getTime()) / (7 * 24 * 3600 * 1e3)) + 1;
|
|
2623
|
+
week = Math.min(Math.max(diff, 1), targetPlan.weeksCount);
|
|
2448
2624
|
}
|
|
2625
|
+
const post = await savePost({
|
|
2626
|
+
planId: targetPlan.id,
|
|
2627
|
+
weekNumber: week,
|
|
2628
|
+
title: "",
|
|
2629
|
+
format: "reel",
|
|
2630
|
+
status: "idea",
|
|
2631
|
+
scheduledDate: isoDate
|
|
2632
|
+
});
|
|
2633
|
+
invalidateLimit("content_posts_month");
|
|
2634
|
+
onOpenPost(post.id);
|
|
2449
2635
|
};
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2636
|
+
const busy = !loaded || loading;
|
|
2637
|
+
const hasAccount = accounts.length > 0;
|
|
2638
|
+
const weeksCount = plan?.weeksCount ?? DEFAULT_WEEKS;
|
|
2639
|
+
const boardStart = plan?.startDate ? /* @__PURE__ */ new Date(`${plan.startDate}T00:00:00`) : mondayOfCurrentWeek();
|
|
2640
|
+
return /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-6xl space-y-6", children: [
|
|
2641
|
+
plan && /* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsxs(Button, { onClick: () => setBriefOpen(true), children: [
|
|
2642
|
+
/* @__PURE__ */ jsx(NotebookPen, { className: "mr-1.5 h-4 w-4" }),
|
|
2643
|
+
t("marketing.content.brief")
|
|
2644
|
+
] }) }),
|
|
2645
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [
|
|
2646
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
|
|
2647
|
+
/* @__PURE__ */ jsx(AccountSelector, {}),
|
|
2648
|
+
plan && /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: plan.name })
|
|
2649
|
+
] }),
|
|
2650
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx(ViewSwitcher, { mode: viewMode, onChange: setViewMode }) })
|
|
2651
|
+
] }),
|
|
2652
|
+
!busy && !hasAccount && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t("marketing.content.noAccountsHint") }),
|
|
2653
|
+
busy && viewMode === "board" && /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4", children: Array.from({ length: 8 }, (_, i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-44" }, i)) }),
|
|
2654
|
+
busy && viewMode === "calendar" && /* @__PURE__ */ jsx(Skeleton, { className: "h-96 w-full" }),
|
|
2655
|
+
busy && viewMode === "list" && /* @__PURE__ */ jsx("div", { className: "space-y-2", children: Array.from({ length: 6 }, (_, i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-full" }, i)) }),
|
|
2656
|
+
!busy && viewMode === "board" && /* @__PURE__ */ jsx(
|
|
2657
|
+
BoardView,
|
|
2456
2658
|
{
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
}
|
|
2659
|
+
startDate: boardStart,
|
|
2660
|
+
weeksCount,
|
|
2661
|
+
postsByWeek,
|
|
2662
|
+
onOpenPost,
|
|
2663
|
+
onNewPost: (w) => void createPost(w),
|
|
2664
|
+
onDeletePost: setPendingDeleteId,
|
|
2665
|
+
onDropPost: (id, w) => void movePost(id, w),
|
|
2666
|
+
disabled: !hasAccount
|
|
2466
2667
|
}
|
|
2467
2668
|
),
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-2 border-t border-border p-2", children: [
|
|
2471
|
-
/* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: pick, disabled: busy, children: [
|
|
2472
|
-
busy ? /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx(ImagePlus, { className: "mr-1.5 h-3.5 w-3.5" }),
|
|
2473
|
-
t("marketing.content.mediaReplace")
|
|
2474
|
-
] }),
|
|
2475
|
-
/* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", onClick: () => onChanged(null), disabled: busy, children: [
|
|
2476
|
-
/* @__PURE__ */ jsx(X, { className: "mr-1 h-3.5 w-3.5" }),
|
|
2477
|
-
t("marketing.content.mediaRemove")
|
|
2478
|
-
] })
|
|
2479
|
-
] })
|
|
2480
|
-
] }) : /* @__PURE__ */ jsxs(
|
|
2481
|
-
"button",
|
|
2669
|
+
!busy && viewMode === "calendar" && /* @__PURE__ */ jsx(
|
|
2670
|
+
CalendarView,
|
|
2482
2671
|
{
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2672
|
+
posts,
|
|
2673
|
+
onOpenPost,
|
|
2674
|
+
onNewPostAt: (iso) => void createPostAt(iso),
|
|
2675
|
+
disabled: !hasAccount
|
|
2676
|
+
}
|
|
2677
|
+
),
|
|
2678
|
+
!busy && viewMode === "list" && /* @__PURE__ */ jsx(ListView, { posts, onOpenPost }),
|
|
2679
|
+
/* @__PURE__ */ jsx(PlanBriefSheet, { open: briefOpen, onClose: () => setBriefOpen(false) }),
|
|
2680
|
+
/* @__PURE__ */ jsx(PostModal, { postId: openPostId, onClose: () => onOpenPost(null) }),
|
|
2681
|
+
/* @__PURE__ */ jsx(
|
|
2682
|
+
ConfirmDialog,
|
|
2683
|
+
{
|
|
2684
|
+
open: !!pendingDeleteId,
|
|
2685
|
+
variant: "destructive",
|
|
2686
|
+
title: t("marketing.content.deletePost"),
|
|
2687
|
+
description: t("marketing.content.deletePostConfirm"),
|
|
2688
|
+
confirmLabel: t("marketing.content.deletePost"),
|
|
2689
|
+
cancelLabel: t("marketing.content.cancel"),
|
|
2690
|
+
onCancel: () => setPendingDeleteId(null),
|
|
2691
|
+
onConfirm: () => {
|
|
2692
|
+
const id = pendingDeleteId;
|
|
2693
|
+
setPendingDeleteId(null);
|
|
2694
|
+
if (id) void deletePost(id);
|
|
2695
|
+
}
|
|
2492
2696
|
}
|
|
2493
2697
|
)
|
|
2494
2698
|
] });
|
|
2495
2699
|
}
|
|
2496
|
-
function toDraft2(post) {
|
|
2497
|
-
return {
|
|
2498
|
-
title: post.title,
|
|
2499
|
-
format: post.format,
|
|
2500
|
-
status: post.status,
|
|
2501
|
-
scheduledDate: post.scheduledDate ?? "",
|
|
2502
|
-
platforms: post.platforms,
|
|
2503
|
-
hook: post.hook ?? "",
|
|
2504
|
-
cta: post.cta ?? "",
|
|
2505
|
-
contentMd: post.contentMd
|
|
2506
|
-
};
|
|
2507
|
-
}
|
|
2508
|
-
function PostPage({ postId, onBack }) {
|
|
2509
|
-
const t = useTranslation();
|
|
2510
|
-
const provider2 = useContentPlannerProvider();
|
|
2511
|
-
const storePost = useContentPlannerStore((s) => s.posts.find((p) => p.id === postId));
|
|
2512
|
-
const savePost = useContentPlannerStore((s) => s.savePost);
|
|
2513
|
-
const deletePost = useContentPlannerStore((s) => s.deletePost);
|
|
2514
|
-
const accounts = useContentPlannerStore((s) => s.accounts);
|
|
2515
|
-
const activeAccountId = useContentPlannerStore((s) => s.activeAccountId);
|
|
2516
|
-
const [post, setPost] = React9.useState(storePost ?? null);
|
|
2517
|
-
const [draft, setDraft] = React9.useState(storePost ? toDraft2(storePost) : null);
|
|
2518
|
-
const [saving, setSaving] = React9.useState(false);
|
|
2519
|
-
const [confirmDelete, setConfirmDelete] = React9.useState(false);
|
|
2520
|
-
React9.useEffect(() => {
|
|
2521
|
-
if (post?.id === postId) return;
|
|
2522
|
-
if (storePost) {
|
|
2523
|
-
setPost(storePost);
|
|
2524
|
-
setDraft(toDraft2(storePost));
|
|
2525
|
-
return;
|
|
2526
|
-
}
|
|
2527
|
-
void provider2.getPost(postId).then((fetched) => {
|
|
2528
|
-
if (fetched) {
|
|
2529
|
-
setPost(fetched);
|
|
2530
|
-
setDraft(toDraft2(fetched));
|
|
2531
|
-
}
|
|
2532
|
-
});
|
|
2533
|
-
}, [postId, storePost, post?.id, provider2]);
|
|
2534
|
-
if (!post || !draft) {
|
|
2535
|
-
return /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-3xl space-y-5", children: [
|
|
2536
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
2537
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-56" }),
|
|
2538
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-40" })
|
|
2539
|
-
] }),
|
|
2540
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-full" }),
|
|
2541
|
-
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-3", children: Array.from({ length: 3 }, (_, i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-14" }, i)) }),
|
|
2542
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-64 w-full" })
|
|
2543
|
-
] });
|
|
2544
|
-
}
|
|
2545
|
-
const dirty = draft.title !== post.title || draft.format !== post.format || draft.status !== post.status || draft.scheduledDate !== (post.scheduledDate ?? "") || draft.platforms.join(",") !== post.platforms.join(",") || draft.hook !== (post.hook ?? "") || draft.cta !== (post.cta ?? "") || draft.contentMd !== post.contentMd;
|
|
2546
|
-
const patch = (partial) => setDraft((d) => d ? { ...d, ...partial } : d);
|
|
2547
|
-
const save = async () => {
|
|
2548
|
-
setSaving(true);
|
|
2549
|
-
try {
|
|
2550
|
-
const updated = await savePost({
|
|
2551
|
-
id: post.id,
|
|
2552
|
-
title: draft.title,
|
|
2553
|
-
format: draft.format,
|
|
2554
|
-
status: draft.status,
|
|
2555
|
-
scheduledDate: draft.scheduledDate || null,
|
|
2556
|
-
platforms: draft.platforms,
|
|
2557
|
-
hook: draft.hook,
|
|
2558
|
-
cta: draft.cta,
|
|
2559
|
-
contentMd: draft.contentMd
|
|
2560
|
-
});
|
|
2561
|
-
setPost(updated);
|
|
2562
|
-
setDraft(toDraft2(updated));
|
|
2563
|
-
} finally {
|
|
2564
|
-
setSaving(false);
|
|
2565
|
-
}
|
|
2566
|
-
};
|
|
2567
|
-
const insertTemplate = () => {
|
|
2568
|
-
patch({ contentMd: POST_TEMPLATES[draft.format](draft.title || t("marketing.content.untitled")) });
|
|
2569
|
-
};
|
|
2570
|
-
const isStatic = draft.format === "static";
|
|
2571
|
-
const account = accounts.find((a) => a.id === activeAccountId) ?? null;
|
|
2572
|
-
const now = /* @__PURE__ */ new Date();
|
|
2573
|
-
const todayISO = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
|
|
2574
|
-
const isReleaseDay = !!post.scheduledDate && post.scheduledDate === todayISO;
|
|
2575
|
-
const handleMediaChanged = (mediaUrl) => {
|
|
2576
|
-
void savePost({ id: post.id, mediaUrl }).then((updated) => {
|
|
2577
|
-
setPost(updated);
|
|
2578
|
-
setDraft((d) => d ? { ...d } : d);
|
|
2579
|
-
});
|
|
2580
|
-
};
|
|
2581
|
-
return /* @__PURE__ */ jsx(
|
|
2582
|
-
ContentSplit,
|
|
2583
|
-
{
|
|
2584
|
-
className: "mx-auto max-w-5xl",
|
|
2585
|
-
aside: /* @__PURE__ */ jsx(RecordingChecklist, { post }),
|
|
2586
|
-
asideTitle: t("marketing.content.checklistTitle"),
|
|
2587
|
-
asideDescription: t("marketing.content.checklistDescription"),
|
|
2588
|
-
collapsible: true,
|
|
2589
|
-
defaultCollapsed: !isReleaseDay,
|
|
2590
|
-
children: /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
|
|
2591
|
-
/* @__PURE__ */ jsx(
|
|
2592
|
-
SubpageHeader,
|
|
2593
|
-
{
|
|
2594
|
-
title: draft.title || t("marketing.content.untitled"),
|
|
2595
|
-
parentLabel: t("marketing.content.title"),
|
|
2596
|
-
onBack,
|
|
2597
|
-
actions: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2598
|
-
/* @__PURE__ */ jsx(ContentSplitTrigger, { label: t("marketing.content.checklistOpen") }),
|
|
2599
|
-
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: () => setConfirmDelete(true), children: t("marketing.content.deletePost") }),
|
|
2600
|
-
/* @__PURE__ */ jsx(Button, { size: "sm", onClick: () => void save(), disabled: !dirty || saving, children: t("marketing.content.save") })
|
|
2601
|
-
] })
|
|
2602
|
-
}
|
|
2603
|
-
),
|
|
2604
|
-
/* @__PURE__ */ jsx(
|
|
2605
|
-
Input,
|
|
2606
|
-
{
|
|
2607
|
-
value: draft.title,
|
|
2608
|
-
onChange: (e) => patch({ title: e.target.value }),
|
|
2609
|
-
placeholder: t("marketing.content.titlePlaceholder"),
|
|
2610
|
-
className: "text-base font-medium"
|
|
2611
|
-
}
|
|
2612
|
-
),
|
|
2613
|
-
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-3", children: [
|
|
2614
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
2615
|
-
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t("marketing.content.formatLabel") }),
|
|
2616
|
-
/* @__PURE__ */ jsxs(Select, { value: draft.format, onValueChange: (v) => patch({ format: v }), children: [
|
|
2617
|
-
/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
2618
|
-
/* @__PURE__ */ jsx(SelectContent, { children: POST_FORMATS.map((f) => /* @__PURE__ */ jsx(SelectItem, { value: f, children: t(`marketing.content.format.${f}`) }, f)) })
|
|
2619
|
-
] })
|
|
2620
|
-
] }),
|
|
2621
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
2622
|
-
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t("marketing.content.statusLabel") }),
|
|
2623
|
-
/* @__PURE__ */ jsxs(Select, { value: draft.status, onValueChange: (v) => patch({ status: v }), children: [
|
|
2624
|
-
/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
2625
|
-
/* @__PURE__ */ jsx(SelectContent, { children: POST_STATUS_ORDER.map((s) => /* @__PURE__ */ jsx(SelectItem, { value: s, children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
|
|
2626
|
-
/* @__PURE__ */ jsx(StatusDot, { status: s }),
|
|
2627
|
-
t(`marketing.content.status.${s}`)
|
|
2628
|
-
] }) }, s)) })
|
|
2629
|
-
] })
|
|
2630
|
-
] }),
|
|
2631
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
2632
|
-
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t("marketing.content.scheduledDate") }),
|
|
2633
|
-
/* @__PURE__ */ jsx(
|
|
2634
|
-
Input,
|
|
2635
|
-
{
|
|
2636
|
-
type: "date",
|
|
2637
|
-
value: draft.scheduledDate,
|
|
2638
|
-
onChange: (e) => patch({ scheduledDate: e.target.value })
|
|
2639
|
-
}
|
|
2640
|
-
)
|
|
2641
|
-
] }),
|
|
2642
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-1 sm:col-span-2", children: [
|
|
2643
|
-
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t("marketing.content.hook") }),
|
|
2644
|
-
/* @__PURE__ */ jsx(
|
|
2645
|
-
Input,
|
|
2646
|
-
{
|
|
2647
|
-
value: draft.hook,
|
|
2648
|
-
onChange: (e) => patch({ hook: e.target.value }),
|
|
2649
|
-
placeholder: t("marketing.content.hookPlaceholder")
|
|
2650
|
-
}
|
|
2651
|
-
)
|
|
2652
|
-
] }),
|
|
2653
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
2654
|
-
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t("marketing.content.cta") }),
|
|
2655
|
-
/* @__PURE__ */ jsx(
|
|
2656
|
-
Input,
|
|
2657
|
-
{
|
|
2658
|
-
value: draft.cta,
|
|
2659
|
-
onChange: (e) => patch({ cta: e.target.value }),
|
|
2660
|
-
placeholder: t("marketing.content.ctaPlaceholder")
|
|
2661
|
-
}
|
|
2662
|
-
)
|
|
2663
|
-
] }),
|
|
2664
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-1 sm:col-span-3", children: [
|
|
2665
|
-
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t("marketing.content.platformsLabel") }),
|
|
2666
|
-
/* @__PURE__ */ jsx(PlatformPicker, { value: draft.platforms, onChange: (platforms) => patch({ platforms }) })
|
|
2667
|
-
] })
|
|
2668
|
-
] }),
|
|
2669
|
-
isStatic && /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
2670
|
-
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t("marketing.content.mediaLabel") }),
|
|
2671
|
-
/* @__PURE__ */ jsx(MediaAsset, { post, onChanged: handleMediaChanged })
|
|
2672
|
-
] }),
|
|
2673
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
2674
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
2675
|
-
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: isStatic ? t("marketing.content.captionLabel") : t("marketing.content.scriptLabel") }),
|
|
2676
|
-
draft.contentMd.trim() === "" && /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: insertTemplate, children: t("marketing.content.insertTemplate") })
|
|
2677
|
-
] }),
|
|
2678
|
-
/* @__PURE__ */ jsx(
|
|
2679
|
-
MarkdownEditor,
|
|
2680
|
-
{
|
|
2681
|
-
value: draft.contentMd,
|
|
2682
|
-
onChange: (contentMd) => patch({ contentMd }),
|
|
2683
|
-
placeholder: isStatic ? t("marketing.content.captionPlaceholder") : t("marketing.content.bodyPlaceholder"),
|
|
2684
|
-
editLabel: t("marketing.content.edit"),
|
|
2685
|
-
previewLabel: t("marketing.content.preview"),
|
|
2686
|
-
renderPreview: isStatic ? (value) => /* @__PURE__ */ jsx(
|
|
2687
|
-
PostSocialPreview,
|
|
2688
|
-
{
|
|
2689
|
-
caption: value,
|
|
2690
|
-
mediaUrl: post.mediaUrl,
|
|
2691
|
-
accountName: account?.name ?? "@conta",
|
|
2692
|
-
accountHandle: account?.handle,
|
|
2693
|
-
platforms: draft.platforms.length > 0 ? draft.platforms : account?.platforms ?? []
|
|
2694
|
-
}
|
|
2695
|
-
) : void 0
|
|
2696
|
-
}
|
|
2697
|
-
)
|
|
2698
|
-
] }),
|
|
2699
|
-
/* @__PURE__ */ jsx(
|
|
2700
|
-
ConfirmDialog,
|
|
2701
|
-
{
|
|
2702
|
-
open: confirmDelete,
|
|
2703
|
-
variant: "destructive",
|
|
2704
|
-
title: t("marketing.content.deletePost"),
|
|
2705
|
-
description: t("marketing.content.deletePostConfirm"),
|
|
2706
|
-
confirmLabel: t("marketing.content.deletePost"),
|
|
2707
|
-
cancelLabel: t("marketing.content.cancel"),
|
|
2708
|
-
onCancel: () => setConfirmDelete(false),
|
|
2709
|
-
onConfirm: () => {
|
|
2710
|
-
setConfirmDelete(false);
|
|
2711
|
-
void deletePost(post.id).then(onBack);
|
|
2712
|
-
}
|
|
2713
|
-
}
|
|
2714
|
-
)
|
|
2715
|
-
] })
|
|
2716
|
-
},
|
|
2717
|
-
post.id
|
|
2718
|
-
);
|
|
2719
|
-
}
|
|
2720
2700
|
|
|
2721
2701
|
// src/data/blogEntities.tsx
|
|
2722
2702
|
var BLOG_POSTS_TABLE = "plg_blog_posts";
|
|
@@ -2904,8 +2884,8 @@ function BlogEntityForm({
|
|
|
2904
2884
|
const isPost = entity.data?.table === BLOG_POSTS_TABLE;
|
|
2905
2885
|
const guardBlogPosts = useLimitGuard("blog_posts");
|
|
2906
2886
|
const namePlural = entity.namePlural ?? entity.name + "s";
|
|
2907
|
-
const [initialData, setInitialData] =
|
|
2908
|
-
|
|
2887
|
+
const [initialData, setInitialData] = React12.useState(isEdit ? null : {});
|
|
2888
|
+
React12.useEffect(() => {
|
|
2909
2889
|
if (!editId) return;
|
|
2910
2890
|
let cancelled = false;
|
|
2911
2891
|
(async () => {
|
|
@@ -3063,7 +3043,7 @@ function MarketingPage({ config, provider: provider2, store: store3, contentProv
|
|
|
3063
3043
|
"blog-cat-new": 1,
|
|
3064
3044
|
"blog-cat": 1
|
|
3065
3045
|
}, "overview");
|
|
3066
|
-
|
|
3046
|
+
React12.useEffect(() => {
|
|
3067
3047
|
void store3.getState().load();
|
|
3068
3048
|
}, [store3]);
|
|
3069
3049
|
useDataChanged({ table: "plg_marketing_campaigns" }, () => {
|
|
@@ -3071,9 +3051,9 @@ function MarketingPage({ config, provider: provider2, store: store3, contentProv
|
|
|
3071
3051
|
}, [store3]);
|
|
3072
3052
|
const isOverview = view === "overview";
|
|
3073
3053
|
const nav = buildNav(config, view, navigate, Boolean(cadenceSurface), t);
|
|
3074
|
-
const blogPostEntity =
|
|
3075
|
-
const blogCategoryEntity =
|
|
3076
|
-
const quickActions =
|
|
3054
|
+
const blogPostEntity = React12.useMemo(() => buildBlogPostEntity(), []);
|
|
3055
|
+
const blogCategoryEntity = React12.useMemo(() => buildBlogCategoryEntity(), []);
|
|
3056
|
+
const quickActions = React12.useMemo(() => [
|
|
3077
3057
|
{ id: "new-campaign", label: t("marketing.campaigns.new"), icon: "Megaphone", description: config.labels.campaigns, action: () => navigate("campaigns") }
|
|
3078
3058
|
], [t, config.labels.campaigns]);
|
|
3079
3059
|
const renderView = createViewRouter([
|
|
@@ -3083,8 +3063,8 @@ function MarketingPage({ config, provider: provider2, store: store3, contentProv
|
|
|
3083
3063
|
{ id: "funnel", render: () => /* @__PURE__ */ jsx(FunnelView, {}) },
|
|
3084
3064
|
{ id: "cadences", render: () => /* @__PURE__ */ jsx(CadencesView, { surface: cadenceSurface }) },
|
|
3085
3065
|
{ id: "landing-pages", render: () => /* @__PURE__ */ jsx(LandingPagesView, {}) },
|
|
3086
|
-
{ id: "content", render: () => /* @__PURE__ */ jsx(ContentView, {
|
|
3087
|
-
{ id: "content-post", render: ({ id }) => /* @__PURE__ */ jsx(
|
|
3066
|
+
{ id: "content", render: () => /* @__PURE__ */ jsx(ContentView, {}) },
|
|
3067
|
+
{ id: "content-post", render: ({ id }) => /* @__PURE__ */ jsx(ContentView, { initialPostId: id }) },
|
|
3088
3068
|
{ id: "content-brief", render: () => /* @__PURE__ */ jsx(PlanBriefView, { onBack: () => navigate("content") }) },
|
|
3089
3069
|
{ id: "blog", render: () => /* @__PURE__ */ jsx(BlogAdminView, { navigate }) },
|
|
3090
3070
|
{ id: "blog-new", render: () => /* @__PURE__ */ jsx(BlogEntityForm, { entity: blogPostEntity, parentLabel: config.labels.blog ?? t("marketing.nav.blog"), onDone: () => navigate("blog") }) },
|
|
@@ -3130,11 +3110,11 @@ var SOURCE_OPTIONS = [
|
|
|
3130
3110
|
function SettingsView() {
|
|
3131
3111
|
const t = useTranslation();
|
|
3132
3112
|
const { conversion, channels } = useMarketingConfig();
|
|
3133
|
-
const [source, setSource] =
|
|
3134
|
-
const [trackValue, setTrackValue] =
|
|
3135
|
-
const [autoSync, setAutoSync] =
|
|
3136
|
-
const [assisted, setAssisted] =
|
|
3137
|
-
const [tracked, setTracked] =
|
|
3113
|
+
const [source, setSource] = React12.useState(conversion.source);
|
|
3114
|
+
const [trackValue, setTrackValue] = React12.useState(true);
|
|
3115
|
+
const [autoSync, setAutoSync] = React12.useState(true);
|
|
3116
|
+
const [assisted, setAssisted] = React12.useState(false);
|
|
3117
|
+
const [tracked, setTracked] = React12.useState(
|
|
3138
3118
|
() => Object.fromEntries(channels.map((c) => [c.id, true]))
|
|
3139
3119
|
);
|
|
3140
3120
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
@@ -7385,22 +7365,22 @@ function createMarketingPlugin(options) {
|
|
|
7385
7365
|
() => createMockContentPlannerProvider()
|
|
7386
7366
|
);
|
|
7387
7367
|
const contentStore = createContentPlannerStore(contentProvider);
|
|
7388
|
-
const PageComponent = () =>
|
|
7368
|
+
const PageComponent = () => React12.createElement(
|
|
7389
7369
|
MarketingContextProvider,
|
|
7390
7370
|
{ config, provider: provider2, store: store3 },
|
|
7391
|
-
|
|
7371
|
+
React12.createElement(MarketingPage, { config, provider: provider2, store: store3, contentProvider, contentStore, cadenceSurface: options?.cadenceSurface })
|
|
7392
7372
|
);
|
|
7393
7373
|
PageComponent.displayName = "MarketingPage";
|
|
7394
7374
|
const marketingRegistries = [
|
|
7395
7375
|
...buildMarketingRegistries(config.channels),
|
|
7396
7376
|
...options?.settingsRegistries ?? []
|
|
7397
7377
|
];
|
|
7398
|
-
const SettingsComponent = () =>
|
|
7378
|
+
const SettingsComponent = () => React12.createElement(
|
|
7399
7379
|
MarketingContextProvider,
|
|
7400
7380
|
{ config, provider: provider2, store: store3 },
|
|
7401
|
-
|
|
7381
|
+
React12.createElement(PluginSettingsPanel, {
|
|
7402
7382
|
title: config.labels.settings ?? config.labels.pageTitle,
|
|
7403
|
-
generalSettings:
|
|
7383
|
+
generalSettings: React12.createElement(SettingsView),
|
|
7404
7384
|
registries: marketingRegistries,
|
|
7405
7385
|
// Enables the "Integrações" tab (renders manifest connectors for this host).
|
|
7406
7386
|
hostPluginId: "marketing",
|