@fayz-ai/plugin-marketing 0.11.0 → 0.11.1
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 +87 -71
- package/dist/index.js.map +1 -1
- package/dist/migrations/index.d.ts +2 -1
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/views/CampaignsView.d.ts.map +1 -1
- package/dist/views/LandingPagesView.d.ts.map +1 -1
- package/dist/views/MarketingPage.d.ts.map +1 -1
- package/package.json +7 -6
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, askAI,
|
|
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';
|
|
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, 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, 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';
|
|
6
|
+
import { History, CheckCircle2, AlertCircle, ChevronRight, Users, Target, Percent, DollarSign, Trash2, WandSparkles, SlidersHorizontal, 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
|
|
@@ -337,15 +337,17 @@ function CampaignsView() {
|
|
|
337
337
|
header: "",
|
|
338
338
|
enableSorting: false,
|
|
339
339
|
cell: ({ row }) => /* @__PURE__ */ jsx(
|
|
340
|
-
|
|
340
|
+
RowActions,
|
|
341
341
|
{
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
342
|
+
actions: [{
|
|
343
|
+
id: "delete",
|
|
344
|
+
label: t("marketing.campaigns.delete"),
|
|
345
|
+
icon: Trash2,
|
|
346
|
+
intent: "danger",
|
|
347
|
+
onSelect: () => {
|
|
348
|
+
void deleteCampaign(row.original.id);
|
|
349
|
+
}
|
|
350
|
+
}]
|
|
349
351
|
}
|
|
350
352
|
)
|
|
351
353
|
}
|
|
@@ -355,9 +357,13 @@ function CampaignsView() {
|
|
|
355
357
|
id: "with-ai",
|
|
356
358
|
label: t("assistant.askAI"),
|
|
357
359
|
description: t("marketing.campaigns.aiHint"),
|
|
358
|
-
icon:
|
|
359
|
-
|
|
360
|
-
|
|
360
|
+
icon: WandSparkles,
|
|
361
|
+
intent: "secondary",
|
|
362
|
+
// Both entries end in a new campaign, so the circle is a "+" even though
|
|
363
|
+
// neither icon is one — a menu of ways to create is still a create.
|
|
364
|
+
creates: true,
|
|
365
|
+
permission: { feature: "marketing", action: "create" },
|
|
366
|
+
onSelect: () => askAI({
|
|
361
367
|
hint: t("marketing.campaigns.askPlaceholder"),
|
|
362
368
|
intro: {
|
|
363
369
|
role: "growth",
|
|
@@ -372,19 +378,15 @@ function CampaignsView() {
|
|
|
372
378
|
id: "by-hand",
|
|
373
379
|
label: t("marketing.campaigns.newByHand"),
|
|
374
380
|
description: t("marketing.campaigns.newByHandHint"),
|
|
375
|
-
icon:
|
|
376
|
-
|
|
381
|
+
icon: SlidersHorizontal,
|
|
382
|
+
intent: "primary",
|
|
383
|
+
creates: true,
|
|
384
|
+
permission: { feature: "marketing", action: "create" },
|
|
385
|
+
onSelect: () => setComposerOpen(true)
|
|
377
386
|
}
|
|
378
387
|
], [t]);
|
|
379
388
|
return /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-6xl space-y-4", children: [
|
|
380
|
-
/* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsx(
|
|
381
|
-
QuickActionsButton,
|
|
382
|
-
{
|
|
383
|
-
label: t("marketing.campaigns.new"),
|
|
384
|
-
variant: "default",
|
|
385
|
-
actions: createActions
|
|
386
|
-
}
|
|
387
|
-
) }) }),
|
|
389
|
+
/* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsx(PageActions, { actions: createActions }) }),
|
|
388
390
|
/* @__PURE__ */ jsx(PageToolbar, { range: /* @__PURE__ */ jsx(RangeTabs, {}) }),
|
|
389
391
|
/* @__PURE__ */ jsx(DataTable, { columns, data: campaigns, emptyMessage: t("marketing.campaigns.empty") }),
|
|
390
392
|
/* @__PURE__ */ jsx(CampaignComposer, { open: composerOpen, onClose: () => setComposerOpen(false) })
|
|
@@ -697,52 +699,42 @@ function LandingPagesView() {
|
|
|
697
699
|
id: "actions",
|
|
698
700
|
header: "",
|
|
699
701
|
enableSorting: false,
|
|
700
|
-
cell: ({ row }) => /* @__PURE__ */
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
}
|
|
724
|
-
),
|
|
725
|
-
/* @__PURE__ */ jsx(
|
|
726
|
-
"button",
|
|
727
|
-
{
|
|
728
|
-
onClick: (e) => {
|
|
729
|
-
e.stopPropagation();
|
|
730
|
-
void deleteLandingPage(row.original.id);
|
|
731
|
-
},
|
|
732
|
-
className: "rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive",
|
|
733
|
-
title: t("marketing.landing.delete"),
|
|
734
|
-
children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" })
|
|
735
|
-
}
|
|
736
|
-
)
|
|
737
|
-
] })
|
|
702
|
+
cell: ({ row }) => /* @__PURE__ */ jsx(
|
|
703
|
+
RowActions,
|
|
704
|
+
{
|
|
705
|
+
actions: [
|
|
706
|
+
...row.original.status === "planned" && row.original.editorUrl ? [{
|
|
707
|
+
id: "build",
|
|
708
|
+
label: t("marketing.landing.buildWithFayz"),
|
|
709
|
+
icon: Sparkles,
|
|
710
|
+
onSelect: () => window.open(row.original.editorUrl, "_blank", "noopener,noreferrer")
|
|
711
|
+
}] : [],
|
|
712
|
+
{ id: "edit", label: t("marketing.landing.edit"), icon: Pencil, onSelect: () => openEdit(row.original) },
|
|
713
|
+
{
|
|
714
|
+
id: "delete",
|
|
715
|
+
label: t("marketing.landing.delete"),
|
|
716
|
+
icon: Trash2,
|
|
717
|
+
intent: "danger",
|
|
718
|
+
onSelect: () => {
|
|
719
|
+
void deleteLandingPage(row.original.id);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
]
|
|
723
|
+
}
|
|
724
|
+
)
|
|
738
725
|
}
|
|
739
726
|
], [t, deleteLandingPage]);
|
|
727
|
+
const createActions = React9.useMemo(() => [{
|
|
728
|
+
id: "new-page",
|
|
729
|
+
label: t("marketing.landing.new"),
|
|
730
|
+
icon: Plus,
|
|
731
|
+
intent: "primary",
|
|
732
|
+
creates: true,
|
|
733
|
+
permission: { feature: "marketing", action: "create" },
|
|
734
|
+
onSelect: openNew
|
|
735
|
+
}], [t]);
|
|
740
736
|
return /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-6xl space-y-4", children: [
|
|
741
|
-
/* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsx(
|
|
742
|
-
/* @__PURE__ */ jsx(Plus, { className: "mr-1.5 h-4 w-4" }),
|
|
743
|
-
" ",
|
|
744
|
-
t("marketing.landing.new")
|
|
745
|
-
] }) }) }),
|
|
737
|
+
/* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsx(PageActions, { actions: createActions }) }),
|
|
746
738
|
/* @__PURE__ */ jsx(PageToolbar, { range: /* @__PURE__ */ jsx(RangeTabs, {}) }),
|
|
747
739
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-4", children: [
|
|
748
740
|
/* @__PURE__ */ jsx(KpiCard, { icon: Layers, label: t("marketing.metric.pages"), value: String(pages.length) }),
|
|
@@ -2842,8 +2834,7 @@ function MarketingPage({ config, provider: provider2, store: store3, contentProv
|
|
|
2842
2834
|
ModuleActionBar,
|
|
2843
2835
|
{
|
|
2844
2836
|
quickActions,
|
|
2845
|
-
settingsPath: "/settings/marketing"
|
|
2846
|
-
settingsLabel: config.labels.pageTitle
|
|
2837
|
+
settingsPath: "/settings/marketing"
|
|
2847
2838
|
}
|
|
2848
2839
|
),
|
|
2849
2840
|
children: renderView(view)
|
|
@@ -4901,7 +4892,7 @@ var MIGRATION_008_LANDING_PAGES = `-- Marketing Plugin: landing pages.
|
|
|
4901
4892
|
CREATE TABLE IF NOT EXISTS public.plg_marketing_landing_pages (
|
|
4902
4893
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
4903
4894
|
tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
4904
|
-
site_id uuid NOT NULL
|
|
4895
|
+
site_id uuid NOT NULL,
|
|
4905
4896
|
name text NOT NULL,
|
|
4906
4897
|
route text NOT NULL,
|
|
4907
4898
|
lead_tags text[] NOT NULL DEFAULT '{}',
|
|
@@ -4912,6 +4903,31 @@ CREATE TABLE IF NOT EXISTS public.plg_marketing_landing_pages (
|
|
|
4912
4903
|
updated_at timestamptz NOT NULL DEFAULT now()
|
|
4913
4904
|
);
|
|
4914
4905
|
|
|
4906
|
+
-- The site table was renamed fayz_projects -> published_sites (db 137), and on a
|
|
4907
|
+
-- pool mid-migration the old name is 141's compat VIEW \u2014 which a FOREIGN KEY
|
|
4908
|
+
-- cannot reference. So the constraint is declared against whichever name is the
|
|
4909
|
+
-- real TABLE here, instead of being frozen into the CREATE above.
|
|
4910
|
+
DO $$
|
|
4911
|
+
DECLARE v_target text;
|
|
4912
|
+
BEGIN
|
|
4913
|
+
SELECT c.relname INTO v_target
|
|
4914
|
+
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
4915
|
+
WHERE n.nspname = 'public' AND c.relkind = 'r'
|
|
4916
|
+
AND c.relname IN ('published_sites', 'fayz_projects')
|
|
4917
|
+
ORDER BY (c.relname = 'published_sites') DESC
|
|
4918
|
+
LIMIT 1;
|
|
4919
|
+
|
|
4920
|
+
IF v_target IS NOT NULL
|
|
4921
|
+
AND NOT EXISTS (SELECT 1 FROM pg_constraint
|
|
4922
|
+
WHERE conrelid = 'public.plg_marketing_landing_pages'::regclass
|
|
4923
|
+
AND conname = 'plg_marketing_landing_pages_site_fk') THEN
|
|
4924
|
+
EXECUTE format(
|
|
4925
|
+
'ALTER TABLE public.plg_marketing_landing_pages
|
|
4926
|
+
ADD CONSTRAINT plg_marketing_landing_pages_site_fk
|
|
4927
|
+
FOREIGN KEY (site_id) REFERENCES public.%I(id) ON DELETE CASCADE', v_target);
|
|
4928
|
+
END IF;
|
|
4929
|
+
END $$;
|
|
4930
|
+
|
|
4915
4931
|
-- Self-healing for pools that ran an earlier cut of this migration (single
|
|
4916
4932
|
-- lead_tag column, no status/prompt). No-op on a fresh table. The views are
|
|
4917
4933
|
-- rebuilt at the bottom; dropping them first is what lets the column go.
|
|
@@ -4974,7 +4990,7 @@ SELECT lp.id, lp.tenant_id, lp.site_id, lp.name, lp.route, lp.lead_tags, lp.stat
|
|
|
4974
4990
|
s.name AS site_name, s.preview_url AS site_url, s.app_id, s.fayz_project_id, s.editor_url,
|
|
4975
4991
|
lp.created_at, lp.updated_at
|
|
4976
4992
|
FROM public.plg_marketing_landing_pages lp
|
|
4977
|
-
JOIN public.fayz_projects s ON s.id = lp.site_id;
|
|
4993
|
+
JOIN public.fayz_projects s ON s.id = lp.site_id; -- alias or table, both read fine
|
|
4978
4994
|
GRANT SELECT ON public.v_marketing_landing_pages TO authenticated;
|
|
4979
4995
|
|
|
4980
4996
|
-- Lead events keyed by tag \u2014 the cross that turns "leads tagged lp-capilar"
|