@fayz-ai/plugin-marketing 0.10.1 → 0.11.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"MarketingBits.d.ts","sourceRoot":"","sources":["../../src/components/MarketingBits.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,OAAO,KAAK,EAAE,cAAc,EAAgB,MAAM,UAAU,CAAA;AAU5D,wBAAgB,OAAO,CAAC,EACtB,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,GAC9D,EAAE;IACD,IAAI,EAAE,KAAK,CAAC,aAAa,CAAC;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACjD,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,2DAA2D;IAC3D,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,qBA0BA;AAQD,wBAAgB,SAAS,sBAWxB;AAaD,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,cAAc,CAAA;CAAE,qBAOjE;AAMD,wBAAgB,WAAW,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,qBAS/D;AAMD,wBAAgB,aAAa,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;IACjE,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CACxB,qBAaA"}
1
+ {"version":3,"file":"MarketingBits.d.ts","sourceRoot":"","sources":["../../src/components/MarketingBits.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,OAAO,KAAK,EAAE,cAAc,EAAgB,MAAM,UAAU,CAAA;AAU5D,wBAAgB,OAAO,CAAC,EACtB,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,GAC9D,EAAE;IACD,IAAI,EAAE,KAAK,CAAC,aAAa,CAAC;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACjD,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,2DAA2D;IAC3D,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,qBA0BA;AAaD,wBAAgB,SAAS,sBAUxB;AAaD,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,cAAc,CAAA;CAAE,qBAOjE;AAMD,wBAAgB,WAAW,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,qBAS/D;AAMD,wBAAgB,aAAa,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;IACjE,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CACxB,qBAaA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAO/F,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,WAAW,CAAA;AAClB,OAAO,KAAK,EACV,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,cAAc,CAAA;AAIrB,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAOjE,OAAO,EAAoB,KAAK,iBAAiB,EAAE,MAAM,cAAc,CAAA;AACvE,OAAO,EAAqB,KAAK,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,eAAe,CAAA;AACpG,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAclE,MAAM,WAAW,sBAAsB;IACrC,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;IACvC,yEAAyE;IACzE,MAAM,CAAC,EAAE,eAAe,CAAA;IACxB,yEAAyE;IACzE,UAAU,CAAC,EAAE,eAAe,CAAA;IAC5B,uEAAuE;IACvE,QAAQ,CAAC,EAAE,kBAAkB,EAAE,CAAA;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAA;IACzC,QAAQ,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAA;IACrC,MAAM,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAA;IACjC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,CAAA;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,YAAY,CAAC,EAAE,qBAAqB,CAAA;IACpC,6EAA6E;IAC7E,cAAc,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,sBAAsB,CAAA;KAAE,CAAA;IAC1D,iFAAiF;IACjF,kBAAkB,CAAC,EAAE,iBAAiB,EAAE,CAAA;IACxC,4DAA4D;IAC5D,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;IACrC,oEAAoE;IACpE,WAAW,CAAC,EAAE,sBAAsB,CAAA;IACpC;6DACyD;IACzD,QAAQ,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CACxE;AA+CD,wBAAgB,qBAAqB,CAAC,OAAO,CAAC,EAAE,sBAAsB,GAAG,cAAc,CA0TtF;AAED,YAAY,EACV,qBAAqB,EACrB,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,cAAc,CAAA;AACrB,YAAY,EACV,kBAAkB,EAClB,eAAe,EACf,QAAQ,EACR,kBAAkB,EAClB,eAAe,GAChB,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAA;AACvE,OAAO,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAA;AACzD,OAAO,EAAE,+BAA+B,EAAE,MAAM,iBAAiB,CAAA;AACjE,YAAY,EACV,sBAAsB,EACtB,WAAW,EACX,WAAW,EACX,aAAa,EACb,UAAU,EACV,UAAU,EACV,oBAAoB,EACpB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,gCAAgC,EAAE,MAAM,oBAAoB,CAAA;AACrE,OAAO,EAAE,oCAAoC,EAAE,MAAM,wBAAwB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAQ/F,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,WAAW,CAAA;AAClB,OAAO,KAAK,EACV,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,cAAc,CAAA;AAIrB,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAOjE,OAAO,EAAoB,KAAK,iBAAiB,EAAE,MAAM,cAAc,CAAA;AACvE,OAAO,EAAqB,KAAK,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,eAAe,CAAA;AACpG,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAclE,MAAM,WAAW,sBAAsB;IACrC,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;IACvC,yEAAyE;IACzE,MAAM,CAAC,EAAE,eAAe,CAAA;IACxB,yEAAyE;IACzE,UAAU,CAAC,EAAE,eAAe,CAAA;IAC5B,uEAAuE;IACvE,QAAQ,CAAC,EAAE,kBAAkB,EAAE,CAAA;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAA;IACzC,QAAQ,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAA;IACrC,MAAM,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAA;IACjC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,CAAA;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,YAAY,CAAC,EAAE,qBAAqB,CAAA;IACpC,6EAA6E;IAC7E,cAAc,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,sBAAsB,CAAA;KAAE,CAAA;IAC1D,iFAAiF;IACjF,kBAAkB,CAAC,EAAE,iBAAiB,EAAE,CAAA;IACxC,4DAA4D;IAC5D,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;IACrC,oEAAoE;IACpE,WAAW,CAAC,EAAE,sBAAsB,CAAA;IACpC;6DACyD;IACzD,QAAQ,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CACxE;AA+CD,wBAAgB,qBAAqB,CAAC,OAAO,CAAC,EAAE,sBAAsB,GAAG,cAAc,CAoUtF;AAED,YAAY,EACV,qBAAqB,EACrB,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,cAAc,CAAA;AACrB,YAAY,EACV,kBAAkB,EAClB,eAAe,EACf,QAAQ,EACR,kBAAkB,EAClB,eAAe,GAChB,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAA;AACvE,OAAO,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAA;AACzD,OAAO,EAAE,+BAA+B,EAAE,MAAM,iBAAiB,CAAA;AACjE,YAAY,EACV,sBAAsB,EACtB,WAAW,EACX,WAAW,EACX,aAAa,EACb,UAAU,EACV,UAAU,EACV,oBAAoB,EACpB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,gCAAgC,EAAE,MAAM,oBAAoB,CAAA;AACrE,OAAO,EAAE,oCAAoC,EAAE,MAAM,wBAAwB,CAAA"}
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import React9, { useState, useEffect, useMemo, useCallback } from 'react';
2
2
  import { connectionSettings, connectionStatus, settingsStatus, createConnectionStore, getActiveTenantId, registerTranslations, createSafeDataProvider, getSupabaseClientOptional, useTranslation, useDataChanged, resolveDataProvider } from '@fayz-ai/core';
3
- import { createPluginContext, fayzConnectorOAuth, PluginSettingsPanel, useModuleNavigation, createViewRouter, ModuleActionBar, SettingsGroup, SelectRow, ToggleRow, PermissionGate, useLimitGuard, useOrganizationStore, CrudFormPage, invalidateLimit, CrudListView } from '@fayz-ai/admin';
4
- import { defineKpiWidget, defineCustomWidget, defineTableWidget, ModulePage, DataTable, SubpageHeader, Button, Skeleton, ConfirmDialog, ContentSplit, ContentSplitTrigger, Input, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, MarkdownEditor, DashboardCanvas, SegmentedControl, cn, Modal, ModalContent, ModalHeader, ModalTitle, ModalBody, ModalFooter, Badge, SelectSeparator, Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetBody, SheetFooter, Checkbox, toast, KpiCard as KpiCard$1, Card, CardHeader, CardTitle, CardContent, TableWidget } from '@fayz-ai/ui';
3
+ import { createPluginContext, fayzConnectorOAuth, PluginSettingsPanel, useModuleNavigation, createViewRouter, ModuleActionBar, SettingsGroup, SelectRow, ToggleRow, askAI, PermissionGate, QuickActionsButton, useLimitGuard, useOrganizationStore, CrudFormPage, RightRailPage, invalidateLimit, CrudListView } from '@fayz-ai/admin';
4
+ import { defineKpiWidget, defineCustomWidget, defineTableWidget, ModulePage, PageToolbar, DataTable, SubpageHeader, PageHeaderActions, Button, Skeleton, ConfirmDialog, ContentSplit, ContentSplitTrigger, Input, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, MarkdownEditor, DashboardCanvas, RangeControl, cn, Modal, ModalContent, ModalHeader, ModalTitle, ModalBody, ModalFooter, SheetBody, SegmentedControl, Badge, SheetFooter, SelectSeparator, Checkbox, 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, Plus, ExternalLink, Sparkles, Pencil, Layers, MousePointerClick, NotebookPen, ArrowUpRight, ArrowDownRight, Settings2, Columns3, CalendarDays, List, ChevronLeft, Loader2, ImagePlus, X, Bike, Utensils, DoorOpen, MessageCircle, Instagram, Mail, Globe, Megaphone, Search, Link2, AtSign, Pin, Twitter, Linkedin, Facebook, Youtube, Music2, Repeat2, Heart, BarChart2, Share2, ThumbsUp, Send, MoreHorizontal, Bookmark, GripVertical, Image, Trophy } from 'lucide-react';
6
+ import { History, CheckCircle2, AlertCircle, ChevronRight, Users, Target, Percent, DollarSign, Trash2, ExternalLink, Sparkles, Pencil, Plus, Layers, MousePointerClick, NotebookPen, ArrowUpRight, ArrowDownRight, Settings2, Columns3, CalendarDays, List, ChevronLeft, Loader2, ImagePlus, X, Bike, Utensils, DoorOpen, MessageCircle, Instagram, Mail, Globe, Megaphone, Search, Link2, AtSign, Pin, Twitter, Linkedin, Facebook, Youtube, Music2, Repeat2, Heart, BarChart2, Share2, ThumbsUp, Send, MoreHorizontal, Bookmark, GripVertical, Image, Trophy } from 'lucide-react';
7
7
  import { createStore } from 'zustand/vanilla';
8
8
 
9
9
  // src/index.ts
@@ -128,12 +128,11 @@ function RangeTabs() {
128
128
  const range = useMarketingStore((s) => s.range);
129
129
  const setRange = useMarketingStore((s) => s.setRange);
130
130
  return /* @__PURE__ */ jsx(
131
- SegmentedControl,
131
+ RangeControl,
132
132
  {
133
- options: RANGES,
133
+ options: RANGES.map((r) => ({ value: r, label: r })),
134
134
  value: range,
135
- onChange: (r) => void setRange(r),
136
- "aria-label": "Time range"
135
+ onChange: (r) => void setRange(r)
137
136
  }
138
137
  );
139
138
  }
@@ -192,10 +191,7 @@ function ChannelsView({ onOpen }) {
192
191
  { id: "open", header: "", enableSorting: false, cell: () => /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4 text-muted-foreground" }) }
193
192
  ], [t, conversion.labelPlural, conversion.valueLabel, currency]);
194
193
  return /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-6xl space-y-4", children: [
195
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
196
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t("marketing.channels.subtitle") }),
197
- /* @__PURE__ */ jsx(RangeTabs, {})
198
- ] }),
194
+ /* @__PURE__ */ jsx(PageToolbar, { range: /* @__PURE__ */ jsx(RangeTabs, {}) }),
199
195
  /* @__PURE__ */ jsx(
200
196
  DataTable,
201
197
  {
@@ -354,18 +350,42 @@ function CampaignsView() {
354
350
  )
355
351
  }
356
352
  ], [t, conversion.labelPlural, currency, deleteCampaign]);
353
+ const createActions = React9.useMemo(() => [
354
+ {
355
+ id: "with-ai",
356
+ label: t("assistant.askAI"),
357
+ description: t("marketing.campaigns.aiHint"),
358
+ icon: "WandSparkles",
359
+ highlight: true,
360
+ action: () => askAI({
361
+ hint: t("marketing.campaigns.askPlaceholder"),
362
+ intro: {
363
+ role: "growth",
364
+ persona: t("marketing.campaigns.askPersona"),
365
+ pitch: t("marketing.campaigns.askPitch"),
366
+ description: t("marketing.campaigns.askDescription"),
367
+ suggestions: [0, 1, 2].map((i) => ({ label: t(`marketing.campaigns.askSuggestion.${i}`) }))
368
+ }
369
+ })
370
+ },
371
+ {
372
+ id: "by-hand",
373
+ label: t("marketing.campaigns.newByHand"),
374
+ description: t("marketing.campaigns.newByHandHint"),
375
+ icon: "SlidersHorizontal",
376
+ action: () => setComposerOpen(true)
377
+ }
378
+ ], [t]);
357
379
  return /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-6xl space-y-4", children: [
358
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
359
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t("marketing.campaigns.subtitle") }),
360
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
361
- /* @__PURE__ */ jsx(RangeTabs, {}),
362
- /* @__PURE__ */ jsx(PermissionGate, { feature: "marketing", action: "create", children: /* @__PURE__ */ jsxs(Button, { onClick: () => setComposerOpen(true), children: [
363
- /* @__PURE__ */ jsx(Plus, { className: "mr-1.5 h-4 w-4" }),
364
- " ",
365
- t("marketing.campaigns.new")
366
- ] }) })
367
- ] })
368
- ] }),
380
+ /* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsx(PermissionGate, { feature: "marketing", action: "create", children: /* @__PURE__ */ jsx(
381
+ QuickActionsButton,
382
+ {
383
+ label: t("marketing.campaigns.new"),
384
+ variant: "default",
385
+ actions: createActions
386
+ }
387
+ ) }) }),
388
+ /* @__PURE__ */ jsx(PageToolbar, { range: /* @__PURE__ */ jsx(RangeTabs, {}) }),
369
389
  /* @__PURE__ */ jsx(DataTable, { columns, data: campaigns, emptyMessage: t("marketing.campaigns.empty") }),
370
390
  /* @__PURE__ */ jsx(CampaignComposer, { open: composerOpen, onClose: () => setComposerOpen(false) })
371
391
  ] });
@@ -376,14 +396,7 @@ function FunnelView() {
376
396
  const funnel = useMarketingStore((s) => s.funnel);
377
397
  const max = Math.max(...funnel.map((s) => s.count), 1);
378
398
  return /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-4xl space-y-4", children: [
379
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
380
- /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
381
- t("marketing.funnel.subtitlePrefix"),
382
- " ",
383
- conversion.label.toLowerCase()
384
- ] }),
385
- /* @__PURE__ */ jsx(RangeTabs, {})
386
- ] }),
399
+ /* @__PURE__ */ jsx(PageToolbar, { range: /* @__PURE__ */ jsx(RangeTabs, {}) }),
387
400
  /* @__PURE__ */ jsxs("div", { className: "rounded-card border border-border bg-card p-5", children: [
388
401
  /* @__PURE__ */ jsx("div", { className: "space-y-3", children: funnel.map((stage, i) => {
389
402
  const pct = max > 0 ? Math.max(stage.count / max * 100, 4) : 0;
@@ -480,113 +493,128 @@ function LandingPageComposer({
480
493
  setSaving(false);
481
494
  }
482
495
  }
483
- return /* @__PURE__ */ jsx(Modal, { open, onOpenChange: (o) => {
484
- if (!o) onClose();
485
- }, children: /* @__PURE__ */ jsxs(ModalContent, { size: "md", children: [
486
- /* @__PURE__ */ jsx(ModalHeader, { children: /* @__PURE__ */ jsx(ModalTitle, { children: page ? t("marketing.landing.edit") : t("marketing.landing.new") }) }),
487
- /* @__PURE__ */ jsxs(ModalBody, { className: "space-y-4", children: [
488
- /* @__PURE__ */ jsxs("label", { className: "block space-y-1.5", children: [
489
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: t("marketing.landing.site") }),
490
- /* @__PURE__ */ jsxs(Select, { value: siteId, onValueChange: setSiteId, children: [
491
- /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: t("marketing.landing.sitePlaceholder") }) }),
492
- /* @__PURE__ */ jsx(SelectContent, { children: sites.map((s) => /* @__PURE__ */ jsx(SelectItem, { value: s.id, children: s.url ? `${s.name} \xB7 ${s.url.replace(/^https?:\/\//, "")}` : s.name }, s.id)) })
493
- ] })
494
- ] }),
495
- /* @__PURE__ */ jsxs("label", { className: "block space-y-1.5", children: [
496
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: t("marketing.landing.name") }),
497
- /* @__PURE__ */ jsx(Input, { value: name, onChange: (e) => setName(e.target.value), placeholder: t("marketing.landing.namePlaceholder") })
498
- ] }),
499
- /* @__PURE__ */ jsxs("label", { className: "block space-y-1.5", children: [
500
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: t("marketing.landing.route") }),
501
- /* @__PURE__ */ jsx(Input, { value: route, onChange: (e) => setRoute(e.target.value), placeholder: "/lp-capilar" }),
502
- site?.url && previewPath ? /* @__PURE__ */ jsxs("span", { className: "block text-xs text-muted-foreground", children: [
503
- site.url.replace(/\/+$/, ""),
504
- previewPath
505
- ] }) : null
506
- ] }),
507
- /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
508
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: t("marketing.landing.status") }),
509
- /* @__PURE__ */ jsx(
510
- SegmentedControl,
511
- {
512
- value: status,
513
- onChange: (v) => setStatus(v),
514
- options: [
515
- { value: "live", label: t("marketing.landing.statusLive") },
516
- { value: "planned", label: t("marketing.landing.statusPlanned") }
517
- ]
518
- }
519
- )
520
- ] }),
521
- status === "planned" ? /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
522
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
523
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: t("marketing.landing.prompt") }),
524
- site?.editorUrl ? /* @__PURE__ */ jsxs(
525
- "button",
526
- {
527
- type: "button",
528
- onClick: () => window.open(site.editorUrl, "_blank", "noopener,noreferrer"),
529
- className: "inline-flex items-center gap-1 text-xs font-medium text-primary hover:underline",
530
- children: [
531
- /* @__PURE__ */ jsx(Sparkles, { className: "h-3.5 w-3.5" }),
532
- " ",
533
- t("marketing.landing.buildWithFayz")
534
- ]
535
- }
536
- ) : null
537
- ] }),
538
- /* @__PURE__ */ jsx(
539
- "textarea",
540
- {
541
- value: prompt,
542
- onChange: (e) => setPrompt(e.target.value),
543
- rows: 3,
544
- placeholder: t("marketing.landing.promptPlaceholder"),
545
- className: "w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground outline-none focus:ring-2 focus:ring-ring"
546
- }
547
- )
548
- ] }) : null,
549
- /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
550
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: t("marketing.landing.leadTag") }),
551
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-1.5 rounded-md border border-border bg-background px-2 py-2 focus-within:ring-2 focus-within:ring-ring", children: [
552
- options.map((opt) => {
553
- const active = tags.includes(opt.slug);
554
- return /* @__PURE__ */ jsx(
555
- Badge,
556
- {
557
- variant: active ? "default" : "outline",
558
- className: "cursor-pointer text-xs",
559
- onClick: () => toggleTag(opt.slug),
560
- children: opt.label
561
- },
562
- opt.slug
563
- );
564
- }),
565
- /* @__PURE__ */ jsx(
566
- "input",
567
- {
568
- value: newTag,
569
- onChange: (e) => setNewTag(e.target.value),
570
- onKeyDown: (e) => {
571
- if (e.key === "Enter") {
572
- e.preventDefault();
573
- addTag();
496
+ return (
497
+ // Beside the list, not over it: the routes already taken are what you check
498
+ // a new one against, and the dialog covered them.
499
+ /* @__PURE__ */ jsxs(
500
+ RightRailPage,
501
+ {
502
+ title: page ? page.name || t("marketing.landing.edit") : t("marketing.landing.new"),
503
+ open,
504
+ onClose,
505
+ width: "max-w-md",
506
+ children: [
507
+ /* @__PURE__ */ jsxs(SheetBody, { className: "space-y-4", children: [
508
+ /* @__PURE__ */ jsxs("label", { className: "block space-y-1.5", children: [
509
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: t("marketing.landing.site") }),
510
+ /* @__PURE__ */ jsxs(Select, { value: siteId, onValueChange: setSiteId, children: [
511
+ /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: t("marketing.landing.sitePlaceholder") }) }),
512
+ /* @__PURE__ */ jsx(SelectContent, { children: sites.map((s) => /* @__PURE__ */ jsx(SelectItem, { value: s.id, children: s.url ? `${s.name} \xB7 ${s.url.replace(/^https?:\/\//, "")}` : s.name }, s.id)) })
513
+ ] })
514
+ ] }),
515
+ /* @__PURE__ */ jsxs("label", { className: "block space-y-1.5", children: [
516
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: t("marketing.landing.name") }),
517
+ /* @__PURE__ */ jsx(Input, { value: name, onChange: (e) => setName(e.target.value), placeholder: t("marketing.landing.namePlaceholder") })
518
+ ] }),
519
+ /* @__PURE__ */ jsxs("label", { className: "block space-y-1.5", children: [
520
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: t("marketing.landing.route") }),
521
+ /* @__PURE__ */ jsx(Input, { value: route, onChange: (e) => setRoute(e.target.value), placeholder: "/lp-capilar" }),
522
+ site?.url && previewPath ? /* @__PURE__ */ jsxs("span", { className: "block text-xs text-muted-foreground", children: [
523
+ site.url.replace(/\/+$/, ""),
524
+ previewPath
525
+ ] }) : null
526
+ ] }),
527
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
528
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: t("marketing.landing.status") }),
529
+ /* @__PURE__ */ jsx(
530
+ SegmentedControl,
531
+ {
532
+ value: status,
533
+ onChange: (v) => setStatus(v),
534
+ options: [
535
+ { value: "live", label: t("marketing.landing.statusLive") },
536
+ { value: "planned", label: t("marketing.landing.statusPlanned") }
537
+ ]
538
+ }
539
+ )
540
+ ] }),
541
+ status === "planned" ? /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
542
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
543
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: t("marketing.landing.prompt") }),
544
+ site?.editorUrl ? /* @__PURE__ */ jsxs(
545
+ "button",
546
+ {
547
+ type: "button",
548
+ onClick: () => window.open(site.editorUrl, "_blank", "noopener,noreferrer"),
549
+ className: "inline-flex items-center gap-1 text-xs font-medium text-primary hover:underline",
550
+ children: [
551
+ /* @__PURE__ */ jsx(Sparkles, { className: "h-3.5 w-3.5" }),
552
+ " ",
553
+ t("marketing.landing.buildWithFayz")
554
+ ]
555
+ }
556
+ ) : null
557
+ ] }),
558
+ /* @__PURE__ */ jsx(
559
+ "textarea",
560
+ {
561
+ value: prompt,
562
+ onChange: (e) => setPrompt(e.target.value),
563
+ rows: 3,
564
+ placeholder: t("marketing.landing.promptPlaceholder"),
565
+ className: "w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground outline-none focus:ring-2 focus:ring-ring"
574
566
  }
575
- },
576
- onBlur: addTag,
577
- placeholder: t("marketing.landing.leadTagPlaceholder"),
578
- className: "min-w-[8rem] flex-1 border-0 bg-transparent px-1 text-xs text-foreground outline-none placeholder:text-muted-foreground"
567
+ )
568
+ ] }) : null,
569
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
570
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: t("marketing.landing.leadTag") }),
571
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-1.5 rounded-md border border-border bg-background px-2 py-2 focus-within:ring-2 focus-within:ring-ring", children: [
572
+ options.map((opt) => {
573
+ const active = tags.includes(opt.slug);
574
+ return /* @__PURE__ */ jsx(
575
+ Badge,
576
+ {
577
+ variant: active ? "default" : "outline",
578
+ className: "cursor-pointer text-xs",
579
+ onClick: () => toggleTag(opt.slug),
580
+ children: opt.label
581
+ },
582
+ opt.slug
583
+ );
584
+ }),
585
+ /* @__PURE__ */ jsx(
586
+ "input",
587
+ {
588
+ value: newTag,
589
+ onChange: (e) => setNewTag(e.target.value),
590
+ onKeyDown: (e) => {
591
+ if (e.key === "Enter") {
592
+ e.preventDefault();
593
+ addTag();
594
+ }
595
+ },
596
+ onBlur: addTag,
597
+ placeholder: t("marketing.landing.leadTagPlaceholder"),
598
+ className: "min-w-[8rem] flex-1 border-0 bg-transparent px-1 text-xs text-foreground outline-none placeholder:text-muted-foreground"
599
+ }
600
+ )
601
+ ] }),
602
+ /* @__PURE__ */ jsx("span", { className: "block text-xs text-muted-foreground", children: t("marketing.landing.leadTagHint") })
603
+ ] })
604
+ ] }),
605
+ /* @__PURE__ */ jsx(SheetFooter, { children: /* @__PURE__ */ jsx(
606
+ Button,
607
+ {
608
+ className: "flex-1",
609
+ onClick: handleSave,
610
+ disabled: saving || !siteId || !name.trim() || !route.trim(),
611
+ children: page ? t("marketing.landing.save") : t("marketing.landing.create")
579
612
  }
580
- )
581
- ] }),
582
- /* @__PURE__ */ jsx("span", { className: "block text-xs text-muted-foreground", children: t("marketing.landing.leadTagHint") })
583
- ] })
584
- ] }),
585
- /* @__PURE__ */ jsxs(ModalFooter, { children: [
586
- /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: onClose, children: t("marketing.composer.cancel") }),
587
- /* @__PURE__ */ jsx(Button, { onClick: handleSave, disabled: saving || !siteId || !name.trim() || !route.trim(), children: page ? t("marketing.landing.save") : t("marketing.landing.create") })
588
- ] })
589
- ] }) });
613
+ ) })
614
+ ]
615
+ }
616
+ )
617
+ );
590
618
  }
591
619
  function LandingPagesView() {
592
620
  const t = useTranslation();
@@ -710,14 +738,12 @@ function LandingPagesView() {
710
738
  }
711
739
  ], [t, deleteLandingPage]);
712
740
  return /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-6xl space-y-4", children: [
713
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-2", children: [
714
- /* @__PURE__ */ jsx(RangeTabs, {}),
715
- /* @__PURE__ */ jsx(PermissionGate, { feature: "marketing", action: "create", children: /* @__PURE__ */ jsxs(Button, { onClick: openNew, children: [
716
- /* @__PURE__ */ jsx(Plus, { className: "mr-1.5 h-4 w-4" }),
717
- " ",
718
- t("marketing.landing.new")
719
- ] }) })
720
- ] }),
741
+ /* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsx(PermissionGate, { feature: "marketing", action: "create", children: /* @__PURE__ */ jsxs(Button, { onClick: openNew, children: [
742
+ /* @__PURE__ */ jsx(Plus, { className: "mr-1.5 h-4 w-4" }),
743
+ " ",
744
+ t("marketing.landing.new")
745
+ ] }) }) }),
746
+ /* @__PURE__ */ jsx(PageToolbar, { range: /* @__PURE__ */ jsx(RangeTabs, {}) }),
721
747
  /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-4", children: [
722
748
  /* @__PURE__ */ jsx(KpiCard, { icon: Layers, label: t("marketing.metric.pages"), value: String(pages.length) }),
723
749
  /* @__PURE__ */ jsx(
@@ -1013,19 +1039,19 @@ function PlanBriefView({ onBack }) {
1013
1039
  function PlanBriefSheet({ open, onClose }) {
1014
1040
  const t = useTranslation();
1015
1041
  const { plan, draft, patch, dirty, saving, save } = useBriefDraft();
1016
- return /* @__PURE__ */ jsx(Sheet, { open, onOpenChange: (v) => {
1017
- if (!v) onClose();
1018
- }, children: /* @__PURE__ */ jsxs(SheetContent, { width: "max-w-2xl", children: [
1019
- /* @__PURE__ */ jsxs(SheetHeader, { children: [
1020
- /* @__PURE__ */ jsx(SheetTitle, { children: t("marketing.content.brief") }),
1021
- plan && /* @__PURE__ */ jsx(SheetDescription, { children: plan.name })
1022
- ] }),
1023
- /* @__PURE__ */ jsx(SheetBody, { children: draft ? /* @__PURE__ */ jsx(BriefFields, { draft, patch }) : /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "\u2026" }) }),
1024
- /* @__PURE__ */ jsxs(SheetFooter, { children: [
1025
- /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: onClose, disabled: saving, children: t("marketing.content.cancel") }),
1026
- /* @__PURE__ */ jsx(Button, { onClick: () => void save(), disabled: !dirty || saving, children: t("marketing.content.save") })
1027
- ] })
1028
- ] }) });
1042
+ return /* @__PURE__ */ jsxs(
1043
+ RightRailPage,
1044
+ {
1045
+ title: plan?.name ?? t("marketing.content.brief"),
1046
+ open,
1047
+ onClose,
1048
+ width: "max-w-2xl",
1049
+ children: [
1050
+ /* @__PURE__ */ jsx(SheetBody, { children: draft ? /* @__PURE__ */ jsx(BriefFields, { draft, patch }) : /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "\u2026" }) }),
1051
+ /* @__PURE__ */ jsx(SheetFooter, { children: /* @__PURE__ */ jsx(Button, { className: "flex-1", onClick: () => void save(), disabled: !dirty || saving, children: t("marketing.content.save") }) })
1052
+ ]
1053
+ }
1054
+ );
1029
1055
  }
1030
1056
  var DEFAULT_WEEKS = 8;
1031
1057
  var ADD_ACCOUNT_VALUE = "__add-account__";
@@ -2580,6 +2606,21 @@ function PostsList({ navigate, navTabs }) {
2580
2606
  navTabs,
2581
2607
  activeNavTab: "posts",
2582
2608
  onNew: () => navigate("blog-new"),
2609
+ aiCreate: {
2610
+ hint: "Descreva o post\u2026",
2611
+ description: "Descreva o assunto e eu escrevo o rascunho.",
2612
+ intro: {
2613
+ role: "growth",
2614
+ persona: "Redator do blog",
2615
+ pitch: "Diga sobre o que \xE9 o post.",
2616
+ description: "Escrevo t\xEDtulo, resumo e corpo em markdown, com slug e tempo de leitura. Nasce como rascunho \u2014 voc\xEA l\xEA, ajusta e publica quando quiser.",
2617
+ suggestions: [
2618
+ { label: "Um post sobre cuidados p\xF3s-procedimento" },
2619
+ { label: "Cinco d\xFAvidas frequentes dos clientes, respondidas" },
2620
+ { label: "Um post curto para divulgar a promo\xE7\xE3o do m\xEAs" }
2621
+ ]
2622
+ }
2623
+ },
2583
2624
  addLabel: "New post",
2584
2625
  onRowClick: (row) => navigate(`blog-post:${row.id}`)
2585
2626
  }
@@ -5247,6 +5288,16 @@ var en = {
5247
5288
  "marketing.channels.noCampaigns": "No campaigns yet on this channel.",
5248
5289
  // campaigns
5249
5290
  "marketing.campaigns.subtitle": "All acquisition campaigns and their conversion performance",
5291
+ "marketing.campaigns.aiHint": "Describe the campaign and I set it up.",
5292
+ "marketing.campaigns.newByHand": "Create by hand",
5293
+ "marketing.campaigns.newByHandHint": "Fill in channel, budget and period.",
5294
+ "marketing.campaigns.askPlaceholder": "Describe the campaign\u2026",
5295
+ "marketing.campaigns.askPersona": "Campaign planner",
5296
+ "marketing.campaigns.askPitch": "Tell me the campaign you want to run.",
5297
+ "marketing.campaigns.askDescription": "Goal, channel, how much you mean to spend and for how long \u2014 tell me what you have and I fill the rest sensibly against your history. You confirm before it is created.",
5298
+ "marketing.campaigns.askSuggestion.0": "Instagram campaign for the month, $800",
5299
+ "marketing.campaigns.askSuggestion.1": "Win back clients who have not returned in 90 days",
5300
+ "marketing.campaigns.askSuggestion.2": "Weekend promo on Google",
5250
5301
  "marketing.campaigns.new": "New campaign",
5251
5302
  "marketing.campaigns.empty": "No campaigns yet \u2014 create your first one.",
5252
5303
  "marketing.campaigns.delete": "Delete campaign",
@@ -5441,6 +5492,16 @@ var ptBR = {
5441
5492
  "marketing.channels.noCampaigns": "Nenhuma campanha ainda neste canal.",
5442
5493
  // campaigns
5443
5494
  "marketing.campaigns.subtitle": "Todas as campanhas de aquisi\xE7\xE3o e seu desempenho de convers\xE3o",
5495
+ "marketing.campaigns.aiHint": "Descreva a campanha e eu monto.",
5496
+ "marketing.campaigns.newByHand": "Criar manualmente",
5497
+ "marketing.campaigns.newByHandHint": "Preencher canal, or\xE7amento e per\xEDodo.",
5498
+ "marketing.campaigns.askPlaceholder": "Descreva a campanha\u2026",
5499
+ "marketing.campaigns.askPersona": "Planejador de campanhas",
5500
+ "marketing.campaigns.askPitch": "Diga a campanha que voc\xEA quer rodar.",
5501
+ "marketing.campaigns.askDescription": "Objetivo, canal, quanto pretende gastar e por quanto tempo \u2014 diga o que tiver e eu completo o resto com o que faz sentido para o seu hist\xF3rico. Voc\xEA confirma antes de criar.",
5502
+ "marketing.campaigns.askSuggestion.0": "Campanha de Instagram para o m\xEAs, R$ 800",
5503
+ "marketing.campaigns.askSuggestion.1": "Reativar clientes que n\xE3o voltam h\xE1 90 dias",
5504
+ "marketing.campaigns.askSuggestion.2": "Promo\xE7\xE3o de fim de semana no Google",
5444
5505
  "marketing.campaigns.new": "Nova campanha",
5445
5506
  "marketing.campaigns.empty": "Nenhuma campanha ainda \u2014 crie a primeira.",
5446
5507
  "marketing.campaigns.delete": "Excluir campanha",
@@ -5669,7 +5730,6 @@ function createMarketingPlugin(options) {
5669
5730
  { config, provider: provider2, store: store3 },
5670
5731
  React9.createElement(PluginSettingsPanel, {
5671
5732
  title: config.labels.settings ?? config.labels.pageTitle,
5672
- subtitle: config.labels.pageSubtitle,
5673
5733
  generalSettings: React9.createElement(SettingsView),
5674
5734
  registries: marketingRegistries,
5675
5735
  // Enables the "Integrações" tab (renders manifest connectors for this host).
@@ -5680,6 +5740,7 @@ function createMarketingPlugin(options) {
5680
5740
  SettingsComponent.displayName = "MarketingSettings";
5681
5741
  return {
5682
5742
  id: "marketing",
5743
+ defaultAgentRole: "growth",
5683
5744
  name: options?.navLabel ?? config.labels.pageTitle,
5684
5745
  icon: "Megaphone",
5685
5746
  version: "1.0.0",
@@ -5752,6 +5813,16 @@ function createMarketingPlugin(options) {
5752
5813
  }
5753
5814
  }
5754
5815
  }] : [],
5816
+ // O post do blog é a única entidade daqui que o agente pode ESCREVER: é
5817
+ // um rascunho de texto, o formulário é o mesmo que a pessoa usaria, e a
5818
+ // permissão de create continua sendo cobrada pelas primitivas. Campanha e
5819
+ // post de conteúdo seguem só-leitura — campanha tem RPC própria e o
5820
+ // planner escreve por outro caminho.
5821
+ ...config.modules.blog ? [{
5822
+ key: "marketing:blogPosts",
5823
+ writable: true,
5824
+ entity: buildBlogPostEntity()
5825
+ }] : [],
5755
5826
  ...config.modules.contentPlanner ? [{
5756
5827
  key: "marketing:contentPosts",
5757
5828
  writable: false,