@fayz-ai/plugin-sites 0.2.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Faya Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # @fayz-ai/plugin-sites
2
+
3
+ > Websites, funnels, and landing pages — built into the same app as your CRM.
4
+
5
+ [![npm](https://img.shields.io/npm/v/@fayz-ai/plugin-sites.svg)](https://www.npmjs.com/package/@fayz-ai/plugin-sites)
6
+ [![license](https://img.shields.io/npm/l/@fayz-ai/plugin-sites.svg)](https://github.com/FayaLabs/fayz-sdk/blob/main/LICENSE)
7
+
8
+ Most teams duct-tape a page builder onto a separate CRM and pray the leads sync. plugin-sites is the GoHighLevel "Sites" surface, native to your Fayz app: funnels, landing pages, and full websites that capture leads straight into the same tenant your pipeline lives in. No webhooks, no Zapier, no copy-paste.
9
+
10
+ This is an early surface and we're honest about it. M1 ships a polished `/sites` home — a real, navigable view of funnels and landing pages with visits and conversion stats — to lock the UX and the data shape. The drag-and-drop block builder (from architecture-v2) and public-facing rendered pages land in a later milestone.
11
+
12
+ ## What's inside
13
+ - **`/sites` route + nav entry** (icon `LayoutTemplate`), permission-gated on `sites:read`
14
+ - **Sites home view** — funnels, websites, and landing pages with status, visits, and conversion stats
15
+ - **Universal scope** — works in any vertical (beauty, food, health, services, retail)
16
+ - **Configurable nav** — `navPosition`, `navSection`, `navLabel`
17
+ - **Declared feature** — `sites` ("Sites & Funnels", in the "Convert" group) for permissions and feature gating
18
+
19
+ > Status: M1 mock home. The page builder and published-page surfaces are on the roadmap.
20
+
21
+ ## Install
22
+ ```bash
23
+ npm install @fayz-ai/plugin-sites
24
+ ```
25
+ Peer deps: `react`, `react-dom`. Runtime deps: `@fayz-ai/core`, `@fayz-ai/ui`.
26
+
27
+ ## Usage
28
+ ```tsx
29
+ import { defineSaas } from '@fayz-ai/saas'
30
+ import { createSitesPlugin } from '@fayz-ai/plugin-sites'
31
+
32
+ export const app = defineSaas({
33
+ // ...
34
+ plugins: [
35
+ createSitesPlugin({
36
+ navLabel: 'Sites',
37
+ navPosition: 7,
38
+ }),
39
+ ],
40
+ })
41
+ ```
42
+
43
+ ## Part of the Fayz SDK
44
+ One of the composable plugins for `@fayz-ai/saas` — this one owns the convert surface: funnels, landing pages, and websites.
45
+
46
+ ## Roadmap & contributing
47
+ Built and evolving in the open. See the [Fayz SDK roadmap](../../docs/ROADMAP.md#plugin-sites) for current gaps, missing features, and good first issues.
package/dist/index.cjs ADDED
@@ -0,0 +1,110 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var lucideReact = require('lucide-react');
5
+ var ui = require('@fayz-ai/ui');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
10
+ var React__default = /*#__PURE__*/_interopDefault(React);
11
+
12
+ // src/index.ts
13
+ var SITES = [
14
+ { id: "1", name: "Free Consultation Funnel", type: "Funnel", status: "Published", visits: 3820, conversion: 12.4, accent: "#6366f1" },
15
+ { id: "2", name: "Summer Promo Landing", type: "Landing page", status: "Published", visits: 1560, conversion: 8.1, accent: "#ec4899" },
16
+ { id: "3", name: "Agency Website", type: "Website", status: "Published", visits: 9240, conversion: 3.2, accent: "#0ea5e9" },
17
+ { id: "4", name: "Webinar Registration", type: "Funnel", status: "Draft", visits: 0, conversion: 0, accent: "#f59e0b" },
18
+ { id: "5", name: "Lead Magnet \u2014 Ebook", type: "Landing page", status: "Draft", visits: 0, conversion: 0, accent: "#14b8a6" }
19
+ ];
20
+ var TYPE_ICON = { Funnel: lucideReact.Layers, Website: lucideReact.Globe, "Landing page": lucideReact.FileText };
21
+ function Stat({ icon: Icon, label, value }) {
22
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-card border border-border bg-card p-4", children: [
23
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-muted-foreground", children: [
24
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "h-4 w-4" }),
25
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium", children: label })
26
+ ] }),
27
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-2xl font-semibold text-foreground", children: value })
28
+ ] });
29
+ }
30
+ function SitesHome() {
31
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto max-w-6xl p-6", children: [
32
+ /* @__PURE__ */ jsxRuntime.jsx(ui.PageHeaderActions, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { children: [
33
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "mr-1.5 h-4 w-4" }),
34
+ " New funnel"
35
+ ] }) }),
36
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 grid grid-cols-2 gap-4 lg:grid-cols-4", children: [
37
+ /* @__PURE__ */ jsxRuntime.jsx(Stat, { icon: lucideReact.Eye, label: "Total visits (30d)", value: "14,620" }),
38
+ /* @__PURE__ */ jsxRuntime.jsx(Stat, { icon: lucideReact.MousePointerClick, label: "Avg. conversion", value: "7.1%" }),
39
+ /* @__PURE__ */ jsxRuntime.jsx(Stat, { icon: lucideReact.Layers, label: "Published funnels", value: "3" }),
40
+ /* @__PURE__ */ jsxRuntime.jsx(Stat, { icon: lucideReact.FileText, label: "Leads captured", value: "1,038" })
41
+ ] }),
42
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3", children: SITES.map((s) => {
43
+ const Icon = TYPE_ICON[s.type];
44
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "group overflow-hidden rounded-card border border-border bg-card", children: [
45
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-28 items-center justify-center", style: { backgroundColor: `${s.accent}1a` }, children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "h-8 w-8", style: { color: s.accent } }) }),
46
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4", children: [
47
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
48
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-muted-foreground", children: s.type }),
49
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `rounded-full px-2 py-0.5 text-xs font-medium ${s.status === "Published" ? "bg-emerald-100 text-emerald-700" : "bg-muted text-muted-foreground"}`, children: s.status })
50
+ ] }),
51
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm font-semibold text-foreground", children: s.name }),
52
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 flex items-center justify-between text-xs text-muted-foreground", children: [
53
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
54
+ s.visits.toLocaleString(),
55
+ " visits"
56
+ ] }),
57
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
58
+ s.conversion,
59
+ "% conv."
60
+ ] })
61
+ ] }),
62
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { variant: "outline", size: "sm", className: "mt-3 w-full", children: [
63
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { className: "mr-1.5 h-3.5 w-3.5" }),
64
+ " Open editor"
65
+ ] })
66
+ ] })
67
+ ] }, s.id);
68
+ }) }),
69
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-4 text-center text-xs text-muted-foreground", children: "Mock preview \xB7 the drag-and-drop page builder (block system + public surfaces) ships in a later milestone." })
70
+ ] });
71
+ }
72
+
73
+ // src/index.ts
74
+ function createSitesPlugin(options) {
75
+ const PageComponent = () => React__default.default.createElement(SitesHome);
76
+ PageComponent.displayName = "SitesHome";
77
+ return {
78
+ id: "sites",
79
+ name: options?.navLabel ?? "Sites",
80
+ icon: "LayoutTemplate",
81
+ version: "1.0.0",
82
+ scope: options?.scope ?? "universal",
83
+ verticalId: options?.verticalId,
84
+ defaultEnabled: true,
85
+ dependencies: [],
86
+ declaredFeatures: [{ id: "sites", label: "Sites & Funnels", group: "Convert" }],
87
+ navigation: [
88
+ {
89
+ section: options?.navSection ?? "main",
90
+ position: options?.navPosition ?? 7,
91
+ label: options?.navLabel ?? "Sites",
92
+ route: "/sites",
93
+ icon: "LayoutTemplate",
94
+ permission: { feature: "sites", action: "read" }
95
+ }
96
+ ],
97
+ routes: [
98
+ {
99
+ path: "/sites",
100
+ component: PageComponent,
101
+ permission: { feature: "sites", action: "read" }
102
+ }
103
+ ],
104
+ widgets: []
105
+ };
106
+ }
107
+
108
+ exports.createSitesPlugin = createSitesPlugin;
109
+ //# sourceMappingURL=index.cjs.map
110
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/views/SitesHome.tsx","../src/index.ts"],"names":["Layers","Globe","FileText","jsxs","jsx","PageHeaderActions","Button","Plus","Eye","MousePointerClick","ExternalLink","React"],"mappings":"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","file":"index.cjs","sourcesContent":["import React from 'react'\nimport { Plus, ExternalLink, MousePointerClick, Eye, Globe, Layers, FileText } from 'lucide-react'\nimport { Button, PageHeaderActions } from '@fayz-ai/ui'\n\ninterface Site {\n id: string\n name: string\n type: 'Funnel' | 'Website' | 'Landing page'\n status: 'Published' | 'Draft'\n visits: number\n conversion: number\n accent: string\n}\n\nconst SITES: Site[] = [\n { id: '1', name: 'Free Consultation Funnel', type: 'Funnel', status: 'Published', visits: 3820, conversion: 12.4, accent: '#6366f1' },\n { id: '2', name: 'Summer Promo Landing', type: 'Landing page', status: 'Published', visits: 1560, conversion: 8.1, accent: '#ec4899' },\n { id: '3', name: 'Agency Website', type: 'Website', status: 'Published', visits: 9240, conversion: 3.2, accent: '#0ea5e9' },\n { id: '4', name: 'Webinar Registration', type: 'Funnel', status: 'Draft', visits: 0, conversion: 0, accent: '#f59e0b' },\n { id: '5', name: 'Lead Magnet — Ebook', type: 'Landing page', status: 'Draft', visits: 0, conversion: 0, accent: '#14b8a6' },\n]\n\nconst TYPE_ICON = { Funnel: Layers, Website: Globe, 'Landing page': FileText } as const\n\nfunction Stat({ icon: Icon, label, value }: { icon: React.ComponentType<{ className?: string }>; label: string; value: string }) {\n return (\n <div className=\"rounded-card border border-border bg-card p-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground\">\n <Icon className=\"h-4 w-4\" /><span className=\"text-xs font-medium\">{label}</span>\n </div>\n <p className=\"mt-2 text-2xl font-semibold text-foreground\">{value}</p>\n </div>\n )\n}\n\nexport function SitesHome() {\n return (\n <div className=\"mx-auto max-w-6xl p-6\">\n <PageHeaderActions>\n <Button><Plus className=\"mr-1.5 h-4 w-4\" /> New funnel</Button>\n </PageHeaderActions>\n\n <div className=\"mt-6 grid grid-cols-2 gap-4 lg:grid-cols-4\">\n <Stat icon={Eye} label=\"Total visits (30d)\" value=\"14,620\" />\n <Stat icon={MousePointerClick} label=\"Avg. conversion\" value=\"7.1%\" />\n <Stat icon={Layers} label=\"Published funnels\" value=\"3\" />\n <Stat icon={FileText} label=\"Leads captured\" value=\"1,038\" />\n </div>\n\n <div className=\"mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {SITES.map((s) => {\n const Icon = TYPE_ICON[s.type]\n return (\n <div key={s.id} className=\"group overflow-hidden rounded-card border border-border bg-card\">\n <div className=\"flex h-28 items-center justify-center\" style={{ backgroundColor: `${s.accent}1a` }}>\n <Icon className=\"h-8 w-8\" style={{ color: s.accent }} />\n </div>\n <div className=\"p-4\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-xs font-medium text-muted-foreground\">{s.type}</span>\n <span className={`rounded-full px-2 py-0.5 text-xs font-medium ${s.status === 'Published' ? 'bg-emerald-100 text-emerald-700' : 'bg-muted text-muted-foreground'}`}>{s.status}</span>\n </div>\n <p className=\"mt-1 text-sm font-semibold text-foreground\">{s.name}</p>\n <div className=\"mt-3 flex items-center justify-between text-xs text-muted-foreground\">\n <span>{s.visits.toLocaleString()} visits</span>\n <span>{s.conversion}% conv.</span>\n </div>\n <Button variant=\"outline\" size=\"sm\" className=\"mt-3 w-full\">\n <ExternalLink className=\"mr-1.5 h-3.5 w-3.5\" /> Open editor\n </Button>\n </div>\n </div>\n )\n })}\n </div>\n\n <p className=\"mt-4 text-center text-xs text-muted-foreground\">\n Mock preview · the drag-and-drop page builder (block system + public surfaces) ships in a later milestone.\n </p>\n </div>\n )\n}\n","import React from 'react'\nimport type { PluginManifest, PluginScope, VerticalId } from '@fayz-ai/core'\nimport { SitesHome } from './views/SitesHome'\n\n// ---------------------------------------------------------------------------\n// @fayz-ai/plugin-sites — websites, funnels & landing pages (GoHighLevel \"Sites\").\n// Universal plugin. M1 ships a rich mock home; the drag-and-drop page builder\n// (block system from architecture-v2 + public surfaces) ships in a later milestone.\n// ---------------------------------------------------------------------------\n\nexport interface SitesPluginOptions {\n navPosition?: number\n navSection?: 'main' | 'secondary' | 'settings'\n navLabel?: string\n scope?: PluginScope\n verticalId?: VerticalId\n}\n\nexport function createSitesPlugin(options?: SitesPluginOptions): PluginManifest {\n const PageComponent: React.ComponentType<unknown> = () => React.createElement(SitesHome)\n PageComponent.displayName = 'SitesHome'\n\n return {\n id: 'sites',\n name: options?.navLabel ?? 'Sites',\n icon: 'LayoutTemplate',\n version: '1.0.0',\n scope: options?.scope ?? 'universal',\n verticalId: options?.verticalId,\n defaultEnabled: true,\n dependencies: [],\n declaredFeatures: [{ id: 'sites', label: 'Sites & Funnels', group: 'Convert' }],\n navigation: [\n {\n section: options?.navSection ?? 'main',\n position: options?.navPosition ?? 7,\n label: options?.navLabel ?? 'Sites',\n route: '/sites',\n icon: 'LayoutTemplate',\n permission: { feature: 'sites', action: 'read' as const },\n },\n ],\n routes: [\n {\n path: '/sites',\n component: PageComponent,\n permission: { feature: 'sites', action: 'read' as const },\n },\n ],\n widgets: [],\n }\n}\n"]}
@@ -0,0 +1,10 @@
1
+ import type { PluginManifest, PluginScope, VerticalId } from '@fayz-ai/core';
2
+ export interface SitesPluginOptions {
3
+ navPosition?: number;
4
+ navSection?: 'main' | 'secondary' | 'settings';
5
+ navLabel?: string;
6
+ scope?: PluginScope;
7
+ verticalId?: VerticalId;
8
+ }
9
+ export declare function createSitesPlugin(options?: SitesPluginOptions): PluginManifest;
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAS5E,MAAM,WAAW,kBAAkB;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;CACxB;AAED,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,cAAc,CAiC9E"}
package/dist/index.js ADDED
@@ -0,0 +1,104 @@
1
+ import React from 'react';
2
+ import { Plus, Eye, MousePointerClick, Layers, FileText, Globe, ExternalLink } from 'lucide-react';
3
+ import { PageHeaderActions, Button } from '@fayz-ai/ui';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
5
+
6
+ // src/index.ts
7
+ var SITES = [
8
+ { id: "1", name: "Free Consultation Funnel", type: "Funnel", status: "Published", visits: 3820, conversion: 12.4, accent: "#6366f1" },
9
+ { id: "2", name: "Summer Promo Landing", type: "Landing page", status: "Published", visits: 1560, conversion: 8.1, accent: "#ec4899" },
10
+ { id: "3", name: "Agency Website", type: "Website", status: "Published", visits: 9240, conversion: 3.2, accent: "#0ea5e9" },
11
+ { id: "4", name: "Webinar Registration", type: "Funnel", status: "Draft", visits: 0, conversion: 0, accent: "#f59e0b" },
12
+ { id: "5", name: "Lead Magnet \u2014 Ebook", type: "Landing page", status: "Draft", visits: 0, conversion: 0, accent: "#14b8a6" }
13
+ ];
14
+ var TYPE_ICON = { Funnel: Layers, Website: Globe, "Landing page": FileText };
15
+ function Stat({ icon: Icon, label, value }) {
16
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-card border border-border bg-card p-4", children: [
17
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-muted-foreground", children: [
18
+ /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }),
19
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium", children: label })
20
+ ] }),
21
+ /* @__PURE__ */ jsx("p", { className: "mt-2 text-2xl font-semibold text-foreground", children: value })
22
+ ] });
23
+ }
24
+ function SitesHome() {
25
+ return /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-6xl p-6", children: [
26
+ /* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsxs(Button, { children: [
27
+ /* @__PURE__ */ jsx(Plus, { className: "mr-1.5 h-4 w-4" }),
28
+ " New funnel"
29
+ ] }) }),
30
+ /* @__PURE__ */ jsxs("div", { className: "mt-6 grid grid-cols-2 gap-4 lg:grid-cols-4", children: [
31
+ /* @__PURE__ */ jsx(Stat, { icon: Eye, label: "Total visits (30d)", value: "14,620" }),
32
+ /* @__PURE__ */ jsx(Stat, { icon: MousePointerClick, label: "Avg. conversion", value: "7.1%" }),
33
+ /* @__PURE__ */ jsx(Stat, { icon: Layers, label: "Published funnels", value: "3" }),
34
+ /* @__PURE__ */ jsx(Stat, { icon: FileText, label: "Leads captured", value: "1,038" })
35
+ ] }),
36
+ /* @__PURE__ */ jsx("div", { className: "mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3", children: SITES.map((s) => {
37
+ const Icon = TYPE_ICON[s.type];
38
+ return /* @__PURE__ */ jsxs("div", { className: "group overflow-hidden rounded-card border border-border bg-card", children: [
39
+ /* @__PURE__ */ jsx("div", { className: "flex h-28 items-center justify-center", style: { backgroundColor: `${s.accent}1a` }, children: /* @__PURE__ */ jsx(Icon, { className: "h-8 w-8", style: { color: s.accent } }) }),
40
+ /* @__PURE__ */ jsxs("div", { className: "p-4", children: [
41
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
42
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: s.type }),
43
+ /* @__PURE__ */ jsx("span", { className: `rounded-full px-2 py-0.5 text-xs font-medium ${s.status === "Published" ? "bg-emerald-100 text-emerald-700" : "bg-muted text-muted-foreground"}`, children: s.status })
44
+ ] }),
45
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm font-semibold text-foreground", children: s.name }),
46
+ /* @__PURE__ */ jsxs("div", { className: "mt-3 flex items-center justify-between text-xs text-muted-foreground", children: [
47
+ /* @__PURE__ */ jsxs("span", { children: [
48
+ s.visits.toLocaleString(),
49
+ " visits"
50
+ ] }),
51
+ /* @__PURE__ */ jsxs("span", { children: [
52
+ s.conversion,
53
+ "% conv."
54
+ ] })
55
+ ] }),
56
+ /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", className: "mt-3 w-full", children: [
57
+ /* @__PURE__ */ jsx(ExternalLink, { className: "mr-1.5 h-3.5 w-3.5" }),
58
+ " Open editor"
59
+ ] })
60
+ ] })
61
+ ] }, s.id);
62
+ }) }),
63
+ /* @__PURE__ */ jsx("p", { className: "mt-4 text-center text-xs text-muted-foreground", children: "Mock preview \xB7 the drag-and-drop page builder (block system + public surfaces) ships in a later milestone." })
64
+ ] });
65
+ }
66
+
67
+ // src/index.ts
68
+ function createSitesPlugin(options) {
69
+ const PageComponent = () => React.createElement(SitesHome);
70
+ PageComponent.displayName = "SitesHome";
71
+ return {
72
+ id: "sites",
73
+ name: options?.navLabel ?? "Sites",
74
+ icon: "LayoutTemplate",
75
+ version: "1.0.0",
76
+ scope: options?.scope ?? "universal",
77
+ verticalId: options?.verticalId,
78
+ defaultEnabled: true,
79
+ dependencies: [],
80
+ declaredFeatures: [{ id: "sites", label: "Sites & Funnels", group: "Convert" }],
81
+ navigation: [
82
+ {
83
+ section: options?.navSection ?? "main",
84
+ position: options?.navPosition ?? 7,
85
+ label: options?.navLabel ?? "Sites",
86
+ route: "/sites",
87
+ icon: "LayoutTemplate",
88
+ permission: { feature: "sites", action: "read" }
89
+ }
90
+ ],
91
+ routes: [
92
+ {
93
+ path: "/sites",
94
+ component: PageComponent,
95
+ permission: { feature: "sites", action: "read" }
96
+ }
97
+ ],
98
+ widgets: []
99
+ };
100
+ }
101
+
102
+ export { createSitesPlugin };
103
+ //# sourceMappingURL=index.js.map
104
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/views/SitesHome.tsx","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import React from 'react'\nimport { Plus, ExternalLink, MousePointerClick, Eye, Globe, Layers, FileText } from 'lucide-react'\nimport { Button, PageHeaderActions } from '@fayz-ai/ui'\n\ninterface Site {\n id: string\n name: string\n type: 'Funnel' | 'Website' | 'Landing page'\n status: 'Published' | 'Draft'\n visits: number\n conversion: number\n accent: string\n}\n\nconst SITES: Site[] = [\n { id: '1', name: 'Free Consultation Funnel', type: 'Funnel', status: 'Published', visits: 3820, conversion: 12.4, accent: '#6366f1' },\n { id: '2', name: 'Summer Promo Landing', type: 'Landing page', status: 'Published', visits: 1560, conversion: 8.1, accent: '#ec4899' },\n { id: '3', name: 'Agency Website', type: 'Website', status: 'Published', visits: 9240, conversion: 3.2, accent: '#0ea5e9' },\n { id: '4', name: 'Webinar Registration', type: 'Funnel', status: 'Draft', visits: 0, conversion: 0, accent: '#f59e0b' },\n { id: '5', name: 'Lead Magnet — Ebook', type: 'Landing page', status: 'Draft', visits: 0, conversion: 0, accent: '#14b8a6' },\n]\n\nconst TYPE_ICON = { Funnel: Layers, Website: Globe, 'Landing page': FileText } as const\n\nfunction Stat({ icon: Icon, label, value }: { icon: React.ComponentType<{ className?: string }>; label: string; value: string }) {\n return (\n <div className=\"rounded-card border border-border bg-card p-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground\">\n <Icon className=\"h-4 w-4\" /><span className=\"text-xs font-medium\">{label}</span>\n </div>\n <p className=\"mt-2 text-2xl font-semibold text-foreground\">{value}</p>\n </div>\n )\n}\n\nexport function SitesHome() {\n return (\n <div className=\"mx-auto max-w-6xl p-6\">\n <PageHeaderActions>\n <Button><Plus className=\"mr-1.5 h-4 w-4\" /> New funnel</Button>\n </PageHeaderActions>\n\n <div className=\"mt-6 grid grid-cols-2 gap-4 lg:grid-cols-4\">\n <Stat icon={Eye} label=\"Total visits (30d)\" value=\"14,620\" />\n <Stat icon={MousePointerClick} label=\"Avg. conversion\" value=\"7.1%\" />\n <Stat icon={Layers} label=\"Published funnels\" value=\"3\" />\n <Stat icon={FileText} label=\"Leads captured\" value=\"1,038\" />\n </div>\n\n <div className=\"mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {SITES.map((s) => {\n const Icon = TYPE_ICON[s.type]\n return (\n <div key={s.id} className=\"group overflow-hidden rounded-card border border-border bg-card\">\n <div className=\"flex h-28 items-center justify-center\" style={{ backgroundColor: `${s.accent}1a` }}>\n <Icon className=\"h-8 w-8\" style={{ color: s.accent }} />\n </div>\n <div className=\"p-4\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-xs font-medium text-muted-foreground\">{s.type}</span>\n <span className={`rounded-full px-2 py-0.5 text-xs font-medium ${s.status === 'Published' ? 'bg-emerald-100 text-emerald-700' : 'bg-muted text-muted-foreground'}`}>{s.status}</span>\n </div>\n <p className=\"mt-1 text-sm font-semibold text-foreground\">{s.name}</p>\n <div className=\"mt-3 flex items-center justify-between text-xs text-muted-foreground\">\n <span>{s.visits.toLocaleString()} visits</span>\n <span>{s.conversion}% conv.</span>\n </div>\n <Button variant=\"outline\" size=\"sm\" className=\"mt-3 w-full\">\n <ExternalLink className=\"mr-1.5 h-3.5 w-3.5\" /> Open editor\n </Button>\n </div>\n </div>\n )\n })}\n </div>\n\n <p className=\"mt-4 text-center text-xs text-muted-foreground\">\n Mock preview · the drag-and-drop page builder (block system + public surfaces) ships in a later milestone.\n </p>\n </div>\n )\n}\n","import React from 'react'\nimport type { PluginManifest, PluginScope, VerticalId } from '@fayz-ai/core'\nimport { SitesHome } from './views/SitesHome'\n\n// ---------------------------------------------------------------------------\n// @fayz-ai/plugin-sites — websites, funnels & landing pages (GoHighLevel \"Sites\").\n// Universal plugin. M1 ships a rich mock home; the drag-and-drop page builder\n// (block system from architecture-v2 + public surfaces) ships in a later milestone.\n// ---------------------------------------------------------------------------\n\nexport interface SitesPluginOptions {\n navPosition?: number\n navSection?: 'main' | 'secondary' | 'settings'\n navLabel?: string\n scope?: PluginScope\n verticalId?: VerticalId\n}\n\nexport function createSitesPlugin(options?: SitesPluginOptions): PluginManifest {\n const PageComponent: React.ComponentType<unknown> = () => React.createElement(SitesHome)\n PageComponent.displayName = 'SitesHome'\n\n return {\n id: 'sites',\n name: options?.navLabel ?? 'Sites',\n icon: 'LayoutTemplate',\n version: '1.0.0',\n scope: options?.scope ?? 'universal',\n verticalId: options?.verticalId,\n defaultEnabled: true,\n dependencies: [],\n declaredFeatures: [{ id: 'sites', label: 'Sites & Funnels', group: 'Convert' }],\n navigation: [\n {\n section: options?.navSection ?? 'main',\n position: options?.navPosition ?? 7,\n label: options?.navLabel ?? 'Sites',\n route: '/sites',\n icon: 'LayoutTemplate',\n permission: { feature: 'sites', action: 'read' as const },\n },\n ],\n routes: [\n {\n path: '/sites',\n component: PageComponent,\n permission: { feature: 'sites', action: 'read' as const },\n },\n ],\n widgets: [],\n }\n}\n"]}
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ export declare function SitesHome(): React.JSX.Element;
3
+ //# sourceMappingURL=SitesHome.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SitesHome.d.ts","sourceRoot":"","sources":["../../src/views/SitesHome.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAmCzB,wBAAgB,SAAS,sBA8CxB"}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@fayz-ai/plugin-sites",
3
+ "version": "0.2.0",
4
+ "description": "Fayz SDK — sites, funnels & landing pages plugin",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "source": "./src/index.ts",
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "src"
20
+ ],
21
+ "peerDependencies": {
22
+ "react": "^18.0.0 || ^19.0.0",
23
+ "react-dom": "^18.0.0 || ^19.0.0"
24
+ },
25
+ "dependencies": {
26
+ "lucide-react": "^0.400.0",
27
+ "@fayz-ai/core": "^0.2.0",
28
+ "@fayz-ai/ui": "^0.2.0"
29
+ },
30
+ "devDependencies": {
31
+ "@types/react": "^18.3.0",
32
+ "react": "^18.3.0",
33
+ "tsup": "^8.2.0",
34
+ "typescript": "^5.5.0",
35
+ "@types/react-dom": "^18.3.0"
36
+ },
37
+ "license": "MIT",
38
+ "keywords": [
39
+ "fayz",
40
+ "fayz-plugin",
41
+ "fayz-sdk"
42
+ ],
43
+ "publishConfig": {
44
+ "access": "public"
45
+ },
46
+ "scripts": {
47
+ "build": "tsup && tsc --emitDeclarationOnly --declaration --declarationMap --noEmit false",
48
+ "dev": "tsup --watch",
49
+ "typecheck": "tsc --noEmit",
50
+ "clean": "rm -rf dist"
51
+ }
52
+ }
package/src/index.ts ADDED
@@ -0,0 +1,52 @@
1
+ import React from 'react'
2
+ import type { PluginManifest, PluginScope, VerticalId } from '@fayz-ai/core'
3
+ import { SitesHome } from './views/SitesHome'
4
+
5
+ // ---------------------------------------------------------------------------
6
+ // @fayz-ai/plugin-sites — websites, funnels & landing pages (GoHighLevel "Sites").
7
+ // Universal plugin. M1 ships a rich mock home; the drag-and-drop page builder
8
+ // (block system from architecture-v2 + public surfaces) ships in a later milestone.
9
+ // ---------------------------------------------------------------------------
10
+
11
+ export interface SitesPluginOptions {
12
+ navPosition?: number
13
+ navSection?: 'main' | 'secondary' | 'settings'
14
+ navLabel?: string
15
+ scope?: PluginScope
16
+ verticalId?: VerticalId
17
+ }
18
+
19
+ export function createSitesPlugin(options?: SitesPluginOptions): PluginManifest {
20
+ const PageComponent: React.ComponentType<unknown> = () => React.createElement(SitesHome)
21
+ PageComponent.displayName = 'SitesHome'
22
+
23
+ return {
24
+ id: 'sites',
25
+ name: options?.navLabel ?? 'Sites',
26
+ icon: 'LayoutTemplate',
27
+ version: '1.0.0',
28
+ scope: options?.scope ?? 'universal',
29
+ verticalId: options?.verticalId,
30
+ defaultEnabled: true,
31
+ dependencies: [],
32
+ declaredFeatures: [{ id: 'sites', label: 'Sites & Funnels', group: 'Convert' }],
33
+ navigation: [
34
+ {
35
+ section: options?.navSection ?? 'main',
36
+ position: options?.navPosition ?? 7,
37
+ label: options?.navLabel ?? 'Sites',
38
+ route: '/sites',
39
+ icon: 'LayoutTemplate',
40
+ permission: { feature: 'sites', action: 'read' as const },
41
+ },
42
+ ],
43
+ routes: [
44
+ {
45
+ path: '/sites',
46
+ component: PageComponent,
47
+ permission: { feature: 'sites', action: 'read' as const },
48
+ },
49
+ ],
50
+ widgets: [],
51
+ }
52
+ }
@@ -0,0 +1,82 @@
1
+ import React from 'react'
2
+ import { Plus, ExternalLink, MousePointerClick, Eye, Globe, Layers, FileText } from 'lucide-react'
3
+ import { Button, PageHeaderActions } from '@fayz-ai/ui'
4
+
5
+ interface Site {
6
+ id: string
7
+ name: string
8
+ type: 'Funnel' | 'Website' | 'Landing page'
9
+ status: 'Published' | 'Draft'
10
+ visits: number
11
+ conversion: number
12
+ accent: string
13
+ }
14
+
15
+ const SITES: Site[] = [
16
+ { id: '1', name: 'Free Consultation Funnel', type: 'Funnel', status: 'Published', visits: 3820, conversion: 12.4, accent: '#6366f1' },
17
+ { id: '2', name: 'Summer Promo Landing', type: 'Landing page', status: 'Published', visits: 1560, conversion: 8.1, accent: '#ec4899' },
18
+ { id: '3', name: 'Agency Website', type: 'Website', status: 'Published', visits: 9240, conversion: 3.2, accent: '#0ea5e9' },
19
+ { id: '4', name: 'Webinar Registration', type: 'Funnel', status: 'Draft', visits: 0, conversion: 0, accent: '#f59e0b' },
20
+ { id: '5', name: 'Lead Magnet — Ebook', type: 'Landing page', status: 'Draft', visits: 0, conversion: 0, accent: '#14b8a6' },
21
+ ]
22
+
23
+ const TYPE_ICON = { Funnel: Layers, Website: Globe, 'Landing page': FileText } as const
24
+
25
+ function Stat({ icon: Icon, label, value }: { icon: React.ComponentType<{ className?: string }>; label: string; value: string }) {
26
+ return (
27
+ <div className="rounded-card border border-border bg-card p-4">
28
+ <div className="flex items-center gap-2 text-muted-foreground">
29
+ <Icon className="h-4 w-4" /><span className="text-xs font-medium">{label}</span>
30
+ </div>
31
+ <p className="mt-2 text-2xl font-semibold text-foreground">{value}</p>
32
+ </div>
33
+ )
34
+ }
35
+
36
+ export function SitesHome() {
37
+ return (
38
+ <div className="mx-auto max-w-6xl p-6">
39
+ <PageHeaderActions>
40
+ <Button><Plus className="mr-1.5 h-4 w-4" /> New funnel</Button>
41
+ </PageHeaderActions>
42
+
43
+ <div className="mt-6 grid grid-cols-2 gap-4 lg:grid-cols-4">
44
+ <Stat icon={Eye} label="Total visits (30d)" value="14,620" />
45
+ <Stat icon={MousePointerClick} label="Avg. conversion" value="7.1%" />
46
+ <Stat icon={Layers} label="Published funnels" value="3" />
47
+ <Stat icon={FileText} label="Leads captured" value="1,038" />
48
+ </div>
49
+
50
+ <div className="mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
51
+ {SITES.map((s) => {
52
+ const Icon = TYPE_ICON[s.type]
53
+ return (
54
+ <div key={s.id} className="group overflow-hidden rounded-card border border-border bg-card">
55
+ <div className="flex h-28 items-center justify-center" style={{ backgroundColor: `${s.accent}1a` }}>
56
+ <Icon className="h-8 w-8" style={{ color: s.accent }} />
57
+ </div>
58
+ <div className="p-4">
59
+ <div className="flex items-center justify-between">
60
+ <span className="text-xs font-medium text-muted-foreground">{s.type}</span>
61
+ <span className={`rounded-full px-2 py-0.5 text-xs font-medium ${s.status === 'Published' ? 'bg-emerald-100 text-emerald-700' : 'bg-muted text-muted-foreground'}`}>{s.status}</span>
62
+ </div>
63
+ <p className="mt-1 text-sm font-semibold text-foreground">{s.name}</p>
64
+ <div className="mt-3 flex items-center justify-between text-xs text-muted-foreground">
65
+ <span>{s.visits.toLocaleString()} visits</span>
66
+ <span>{s.conversion}% conv.</span>
67
+ </div>
68
+ <Button variant="outline" size="sm" className="mt-3 w-full">
69
+ <ExternalLink className="mr-1.5 h-3.5 w-3.5" /> Open editor
70
+ </Button>
71
+ </div>
72
+ </div>
73
+ )
74
+ })}
75
+ </div>
76
+
77
+ <p className="mt-4 text-center text-xs text-muted-foreground">
78
+ Mock preview · the drag-and-drop page builder (block system + public surfaces) ships in a later milestone.
79
+ </p>
80
+ </div>
81
+ )
82
+ }