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