@fayz-ai/plugin-marketing 0.9.0-next.1 → 0.9.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.
Files changed (88) hide show
  1. package/README.md +3 -3
  2. package/dist/{MarketingContext.d.ts → context.d.ts} +5 -3
  3. package/dist/context.d.ts.map +1 -0
  4. package/dist/data/mock.d.ts +1 -1
  5. package/dist/data/mock.d.ts.map +1 -1
  6. package/dist/{registries.d.ts → data/registries.d.ts} +1 -1
  7. package/dist/data/registries.d.ts.map +1 -0
  8. package/dist/index.d.ts +4 -4
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +17 -22
  11. package/dist/index.js.map +1 -1
  12. package/dist/lib/format.d.ts.map +1 -0
  13. package/dist/{presets.d.ts → lib/presets.d.ts} +1 -1
  14. package/dist/lib/presets.d.ts.map +1 -0
  15. package/dist/{MarketingPage.d.ts → views/MarketingPage.d.ts} +5 -5
  16. package/dist/views/MarketingPage.d.ts.map +1 -0
  17. package/dist/views/blog/BlogAdminView.d.ts.map +1 -1
  18. package/dist/views/content/ContentPlannerContext.d.ts +1 -1
  19. package/dist/views/content/ContentPlannerContext.d.ts.map +1 -1
  20. package/dist/views/dashboardWidgets.d.ts +1 -1
  21. package/dist/views/dashboardWidgets.d.ts.map +1 -1
  22. package/package.json +6 -9
  23. package/dist/MarketingContext.d.ts.map +0 -1
  24. package/dist/MarketingPage.d.ts.map +0 -1
  25. package/dist/format.d.ts.map +0 -1
  26. package/dist/presets.d.ts.map +0 -1
  27. package/dist/registries.d.ts.map +0 -1
  28. package/src/MarketingContext.tsx +0 -42
  29. package/src/MarketingPage.tsx +0 -139
  30. package/src/components/MarketingBits.tsx +0 -131
  31. package/src/components/icons.tsx +0 -36
  32. package/src/data/blogEntities.tsx +0 -91
  33. package/src/data/capability.test.ts +0 -92
  34. package/src/data/contentMedia.ts +0 -25
  35. package/src/data/contentMock.ts +0 -253
  36. package/src/data/contentSupabase.ts +0 -263
  37. package/src/data/contentTypes.ts +0 -138
  38. package/src/data/mock.ts +0 -308
  39. package/src/data/supabase.ts +0 -613
  40. package/src/data/tables.ts +0 -33
  41. package/src/data/types.ts +0 -97
  42. package/src/format.ts +0 -40
  43. package/src/index.ts +0 -457
  44. package/src/integrations/ranklayer/RANKLAYER.md +0 -40
  45. package/src/integrations/ranklayer/connectorDef.tsx +0 -102
  46. package/src/integrations/ranklayer/data/supabase.ts +0 -106
  47. package/src/integrations/ranklayer/types.ts +0 -16
  48. package/src/locales/en.ts +0 -192
  49. package/src/locales/index.ts +0 -7
  50. package/src/locales/pt-BR.ts +0 -192
  51. package/src/migrations/000_plg_rename.sql +0 -15
  52. package/src/migrations/001_content_planner.sql +0 -94
  53. package/src/migrations/002_multi_platform.sql +0 -25
  54. package/src/migrations/003_recording_ops.sql +0 -25
  55. package/src/migrations/004_ranklayer.sql +0 -50
  56. package/src/migrations/005_analytics_base.sql +0 -60
  57. package/src/migrations/006_analytics_views.sql +0 -68
  58. package/src/migrations/007_agent_rpcs.sql +0 -119
  59. package/src/migrations/008_landing_pages.sql +0 -112
  60. package/src/migrations/index.ts +0 -601
  61. package/src/presets.ts +0 -119
  62. package/src/registries.ts +0 -62
  63. package/src/schema/index.ts +0 -106
  64. package/src/store.ts +0 -102
  65. package/src/types.ts +0 -153
  66. package/src/views/CampaignComposer.tsx +0 -89
  67. package/src/views/CampaignsView.tsx +0 -59
  68. package/src/views/ChannelDetailView.tsx +0 -59
  69. package/src/views/ChannelsView.tsx +0 -46
  70. package/src/views/FunnelView.tsx +0 -56
  71. package/src/views/LandingPageComposer.tsx +0 -201
  72. package/src/views/LandingPagesView.tsx +0 -172
  73. package/src/views/OverviewView.tsx +0 -24
  74. package/src/views/SettingsView.tsx +0 -72
  75. package/src/views/blog/BlogAdminView.tsx +0 -99
  76. package/src/views/blog/BlogEntityForm.tsx +0 -101
  77. package/src/views/blog/useEntityCrud.ts +0 -50
  78. package/src/views/content/ContentPlannerContext.tsx +0 -13
  79. package/src/views/content/ContentView.tsx +0 -908
  80. package/src/views/content/PlanBriefView.tsx +0 -268
  81. package/src/views/content/PostPage.tsx +0 -370
  82. package/src/views/content/PostSocialPreview.tsx +0 -212
  83. package/src/views/content/RecordingChecklist.tsx +0 -145
  84. package/src/views/content/contentBits.tsx +0 -114
  85. package/src/views/content/contentStore.ts +0 -160
  86. package/src/views/content/templates.ts +0 -172
  87. package/src/views/dashboardWidgets.tsx +0 -173
  88. /package/dist/{format.d.ts → lib/format.d.ts} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/lib/format.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;CACf;AAED,eAAO,MAAM,gBAAgB,EAAE,iBAAiE,CAAA;AAEhG,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,GAAG,MAAM,CAUjF;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,SAAI,GAAG,MAAM,CAE/D;AAED,kDAAkD;AAClD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED,0DAA0D;AAC1D,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,IAAI,GAAG,MAAM,GAAG,SAAS,CAAA;CAAE,CAI7G"}
@@ -1,4 +1,4 @@
1
- import type { AcquisitionChannel, ConversionModel } from './types';
1
+ import type { AcquisitionChannel, ConversionModel } from '../types';
2
2
  export type MarketingDomain = 'agency' | 'beauty' | 'resto';
3
3
  export interface MarketingDomainModules {
4
4
  channels: boolean;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../src/lib/presets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AASnE,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAA;AAE3D,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,OAAO,CAAA;IACjB,SAAS,EAAE,OAAO,CAAA;IAClB,MAAM,EAAE,OAAO,CAAA;IACf,YAAY,EAAE,OAAO,CAAA;IACrB,0EAA0E;IAC1E,cAAc,EAAE,OAAO,CAAA;IACvB,6FAA6F;IAC7F,IAAI,EAAE,OAAO,CAAA;IACb,6EAA6E;IAC7E,UAAU,EAAE,OAAO,CAAA;IACnB,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,eAAe,CAAA;IAC3B,QAAQ,EAAE,kBAAkB,EAAE,CAAA;IAC9B,OAAO,EAAE,sBAAsB,CAAA;CAChC;AA4DD,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,eAAe,EAAE,qBAAqB,CAI5E,CAAA;AAED,mEAAmE;AACnE,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,EAAE,CAsBjE,CAAA"}
@@ -1,10 +1,10 @@
1
1
  import React from 'react';
2
2
  import type { StoreApi } from 'zustand/vanilla';
3
- import { type ResolvedMarketingConfig } from './MarketingContext';
4
- import type { MarketingDataProvider } from './data/types';
5
- import type { ContentPlannerProvider } from './data/contentTypes';
6
- import type { MarketingUIState } from './store';
7
- import type { ContentPlannerUIState } from './views/content/contentStore';
3
+ import { type ResolvedMarketingConfig } from '../context';
4
+ import type { MarketingDataProvider } from '../data/types';
5
+ import type { ContentPlannerProvider } from '../data/contentTypes';
6
+ import type { MarketingUIState } from '../store';
7
+ import type { ContentPlannerUIState } from './content/contentStore';
8
8
  export declare function MarketingPage({ config, provider, store, contentProvider, contentStore }: {
9
9
  config: ResolvedMarketingConfig;
10
10
  provider: MarketingDataProvider;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MarketingPage.d.ts","sourceRoot":"","sources":["../../src/views/MarketingPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAK/C,OAAO,EAA4B,KAAK,uBAAuB,EAAE,MAAM,YAAY,CAAA;AACnF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAC1D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAA;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAQhD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AAkDnE,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,EAAE;IACxF,MAAM,EAAE,uBAAuB,CAAA;IAC/B,QAAQ,EAAE,qBAAqB,CAAA;IAC/B,KAAK,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAA;IACjC,eAAe,EAAE,sBAAsB,CAAA;IACvC,YAAY,EAAE,QAAQ,CAAC,qBAAqB,CAAC,CAAA;CAC9C,qBAkEA"}
@@ -1 +1 @@
1
- {"version":3,"file":"BlogAdminView.d.ts","sourceRoot":"","sources":["../../../src/views/blog/BlogAdminView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuC,MAAM,OAAO,CAAA;AAgF3D,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,qBAkB/E"}
1
+ {"version":3,"file":"BlogAdminView.d.ts","sourceRoot":"","sources":["../../../src/views/blog/BlogAdminView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuC,MAAM,OAAO,CAAA;AAmF3D,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,qBAgB/E"}
@@ -1,4 +1,4 @@
1
- import type { ResolvedMarketingConfig } from '../../MarketingContext';
1
+ import type { ResolvedMarketingConfig } from '../../context';
2
2
  import type { ContentPlannerProvider } from '../../data/contentTypes';
3
3
  import type { ContentPlannerUIState } from './contentStore';
4
4
  export declare const ContentPlannerContextProvider: import("react").FC<{
@@ -1 +1 @@
1
- {"version":3,"file":"ContentPlannerContext.d.ts","sourceRoot":"","sources":["../../../src/views/content/ContentPlannerContext.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAA;AACrE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAA;AACrE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AAM3D,eAAO,MAAM,6BAA6B;;;;;EAAsB,CAAA;AAChE,eAAO,MAAM,uBAAuB,+BAAgB,CAAA;AACpD,eAAO,MAAM,yBAAyB,8BAAkB,CAAA;AACxD,eAAO,MAAM,sBAAsB,yDAAe,CAAA"}
1
+ {"version":3,"file":"ContentPlannerContext.d.ts","sourceRoot":"","sources":["../../../src/views/content/ContentPlannerContext.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AAC5D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAA;AACrE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AAM3D,eAAO,MAAM,6BAA6B;;;;;EAAsB,CAAA;AAChE,eAAO,MAAM,uBAAuB,+BAAgB,CAAA;AACpD,eAAO,MAAM,yBAAyB,8BAAkB,CAAA;AACxD,eAAO,MAAM,sBAAsB,yDAAe,CAAA"}
@@ -1,6 +1,6 @@
1
1
  import type { StoreApi } from 'zustand';
2
2
  import type { DashboardWidgetDef } from '@fayz-ai/core';
3
- import { type ResolvedMarketingConfig } from '../MarketingContext';
3
+ import { type ResolvedMarketingConfig } from '../context';
4
4
  import type { MarketingDataProvider } from '../data/types';
5
5
  import type { MarketingUIState } from '../store';
6
6
  export declare function createMarketingDashboardWidgets(ctx: {
@@ -1 +1 @@
1
- {"version":3,"file":"dashboardWidgets.d.ts","sourceRoot":"","sources":["../../src/views/dashboardWidgets.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AACvC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAMvD,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAkIhD,wBAAgB,+BAA+B,CAAC,GAAG,EAAE;IACnD,MAAM,EAAE,uBAAuB,CAAA;IAC/B,QAAQ,EAAE,qBAAqB,CAAA;IAC/B,KAAK,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAA;CAClC,GAAG,kBAAkB,EAAE,CAuBvB"}
1
+ {"version":3,"file":"dashboardWidgets.d.ts","sourceRoot":"","sources":["../../src/views/dashboardWidgets.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AACvC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAMvD,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,YAAY,CAAA;AACnB,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAkIhD,wBAAgB,+BAA+B,CAAC,GAAG,EAAE;IACnD,MAAM,EAAE,uBAAuB,CAAA;IAC/B,QAAQ,EAAE,qBAAqB,CAAA;IAC/B,KAAK,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAA;CAClC,GAAG,kBAAkB,EAAE,CAuBvB"}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "fayz": {
4
4
  "status": "beta"
5
5
  },
6
- "version": "0.9.0-next.1",
6
+ "version": "0.9.1",
7
7
  "description": "Fayz SDK — marketing campaigns & broadcasts plugin",
8
8
  "type": "module",
9
9
  "sideEffects": false,
@@ -12,20 +12,17 @@
12
12
  "types": "./dist/index.d.ts",
13
13
  "exports": {
14
14
  ".": {
15
- "source": "./src/index.ts",
16
15
  "types": "./dist/index.d.ts",
17
16
  "import": "./dist/index.js"
18
17
  },
19
18
  "./schema": {
20
- "source": "./src/schema/index.ts",
21
19
  "types": "./dist/schema/index.d.ts",
22
20
  "import": "./dist/schema.js",
23
21
  "require": "./dist/schema.cjs"
24
22
  }
25
23
  },
26
24
  "files": [
27
- "dist",
28
- "src"
25
+ "dist"
29
26
  ],
30
27
  "peerDependencies": {
31
28
  "react": "^18.0.0 || ^19.0.0",
@@ -35,10 +32,10 @@
35
32
  },
36
33
  "dependencies": {
37
34
  "@tanstack/react-table": "^8.20.0",
38
- "@fayz-ai/core": "^0.9.0-next.1",
39
- "@fayz-ai/ui": "^0.9.0-next.1",
40
- "@fayz-ai/saas": "^0.9.0-next.1",
41
- "@fayz-ai/db": "^0.9.0-next.1"
35
+ "@fayz-ai/core": "^0.10.0",
36
+ "@fayz-ai/admin": "^0.10.0",
37
+ "@fayz-ai/ui": "^0.10.0",
38
+ "@fayz-ai/db": "^0.10.0"
42
39
  },
43
40
  "devDependencies": {
44
41
  "@types/react": "^18.3.0",
@@ -1 +0,0 @@
1
- {"version":3,"file":"MarketingContext.d.ts","sourceRoot":"","sources":["../src/MarketingContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAClE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AACjD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAA;AACvD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE/C,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,cAAc,EAAE,MAAM,CAAA;IACtB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,eAAe,CAAA;IAC3B,QAAQ,EAAE,kBAAkB,EAAE,CAAA;IAC9B,OAAO,EAAE,sBAAsB,CAAA;IAC/B,QAAQ,EAAE,iBAAiB,CAAA;IAC3B,MAAM,EAAE,eAAe,CAAA;CACxB;AAID,eAAO,MAAM,wBAAwB;;;;;EAAsB,CAAA;AAC3D,eAAO,MAAM,kBAAkB,+BAAgB,CAAA;AAC/C,eAAO,MAAM,oBAAoB,6BAAkB,CAAA;AACnD,eAAO,MAAM,iBAAiB,oDAAe,CAAA;AAE7C,uDAAuD;AACvD,wBAAgB,gBAAgB,IAAI,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAGlE"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"MarketingPage.d.ts","sourceRoot":"","sources":["../src/MarketingPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAK/C,OAAO,EAA4B,KAAK,uBAAuB,EAAE,MAAM,oBAAoB,CAAA;AAC3F,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACzD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAQ/C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAA;AAkDzE,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,EAAE;IACxF,MAAM,EAAE,uBAAuB,CAAA;IAC/B,QAAQ,EAAE,qBAAqB,CAAA;IAC/B,KAAK,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAA;IACjC,eAAe,EAAE,sBAAsB,CAAA;IACvC,YAAY,EAAE,QAAQ,CAAC,qBAAqB,CAAC,CAAA;CAC9C,qBAiEA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;CACf;AAED,eAAO,MAAM,gBAAgB,EAAE,iBAAiE,CAAA;AAEhG,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,GAAG,MAAM,CAUjF;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,SAAI,GAAG,MAAM,CAE/D;AAED,kDAAkD;AAClD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED,0DAA0D;AAC1D,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,IAAI,GAAG,MAAM,GAAG,SAAS,CAAA;CAAE,CAI7G"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../src/presets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AASlE,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAA;AAE3D,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,OAAO,CAAA;IACjB,SAAS,EAAE,OAAO,CAAA;IAClB,MAAM,EAAE,OAAO,CAAA;IACf,YAAY,EAAE,OAAO,CAAA;IACrB,0EAA0E;IAC1E,cAAc,EAAE,OAAO,CAAA;IACvB,6FAA6F;IAC7F,IAAI,EAAE,OAAO,CAAA;IACb,6EAA6E;IAC7E,UAAU,EAAE,OAAO,CAAA;IACnB,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,eAAe,CAAA;IAC3B,QAAQ,EAAE,kBAAkB,EAAE,CAAA;IAC9B,OAAO,EAAE,sBAAsB,CAAA;CAChC;AA4DD,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,eAAe,EAAE,qBAAqB,CAI5E,CAAA;AAED,mEAAmE;AACnE,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,EAAE,CAsBjE,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"registries.d.ts","sourceRoot":"","sources":["../src/registries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAa,MAAM,eAAe,CAAA;AACjE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAwCjD;;;;0EAI0E;AAC1E,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,kBAAkB,EAAE,GAAG,iBAAiB,EAAE,CAe5F"}
@@ -1,42 +0,0 @@
1
- import React from 'react'
2
- import { createPluginContext } from '@fayz-ai/saas'
3
- import type { AcquisitionChannel, ConversionModel } from './types'
4
- import type { MarketingCurrency } from './format'
5
- import type { MarketingDomainModules } from './presets'
6
- import type { MarketingDataProvider } from './data/types'
7
- import type { MarketingUIState } from './store'
8
-
9
- export interface MarketingLabels {
10
- pageTitle: string
11
- pageSubtitle: string
12
- overview: string
13
- channels: string
14
- campaigns: string
15
- funnel: string
16
- landingPages: string
17
- content: string
18
- blog: string
19
- blogCategories: string
20
- settings: string
21
- }
22
-
23
- export interface ResolvedMarketingConfig {
24
- conversion: ConversionModel
25
- channels: AcquisitionChannel[]
26
- modules: MarketingDomainModules
27
- currency: MarketingCurrency
28
- labels: MarketingLabels
29
- }
30
-
31
- const ctx = createPluginContext<ResolvedMarketingConfig, MarketingDataProvider, MarketingUIState>('MarketingPage')
32
-
33
- export const MarketingContextProvider = ctx.ContextProvider
34
- export const useMarketingConfig = ctx.useConfig
35
- export const useMarketingProvider = ctx.useProvider
36
- export const useMarketingStore = ctx.useStore
37
-
38
- /** Convenience: look up a channel definition by id. */
39
- export function useChannelLookup(): Map<string, AcquisitionChannel> {
40
- const { channels } = useMarketingConfig()
41
- return React.useMemo(() => new Map(channels.map((c) => [c.id, c])), [channels])
42
- }
@@ -1,139 +0,0 @@
1
- import React from 'react'
2
- import type { StoreApi } from 'zustand/vanilla'
3
- import { ModulePage, type ModuleNavItem } from '@fayz-ai/ui'
4
- import { useTranslation, useDataChanged } from '@fayz-ai/core'
5
- import type { PluginQuickAction } from '@fayz-ai/core'
6
- import { useModuleNavigation, ModuleActionBar, createViewRouter } from '@fayz-ai/saas'
7
- import { MarketingContextProvider, type ResolvedMarketingConfig } from './MarketingContext'
8
- import type { MarketingDataProvider } from './data/types'
9
- import type { ContentPlannerProvider } from './data/contentTypes'
10
- import type { MarketingUIState } from './store'
11
- import { OverviewView } from './views/OverviewView'
12
- import { ChannelsView } from './views/ChannelsView'
13
- import { ChannelDetailView } from './views/ChannelDetailView'
14
- import { CampaignsView } from './views/CampaignsView'
15
- import { FunnelView } from './views/FunnelView'
16
- import { LandingPagesView } from './views/LandingPagesView'
17
- import { ContentPlannerContextProvider } from './views/content/ContentPlannerContext'
18
- import type { ContentPlannerUIState } from './views/content/contentStore'
19
- import { ContentView } from './views/content/ContentView'
20
- import { PostPage } from './views/content/PostPage'
21
- import { PlanBriefView } from './views/content/PlanBriefView'
22
- import { BlogAdminView } from './views/blog/BlogAdminView'
23
- import { BlogEntityForm } from './views/blog/BlogEntityForm'
24
- import { buildBlogPostEntity, buildBlogCategoryEntity } from './data/blogEntities'
25
-
26
- function buildNav(
27
- config: ResolvedMarketingConfig,
28
- view: string,
29
- navigate: (v: string) => void,
30
- ): ModuleNavItem[] {
31
- const items: ModuleNavItem[] = [
32
- { id: 'overview', label: config.labels.overview, icon: 'BarChart3', active: view === 'overview', onClick: () => navigate('overview') },
33
- ]
34
- if (config.modules.channels) {
35
- items.push({
36
- id: 'channels', label: config.labels.channels, icon: 'Radio',
37
- active: view === 'channels' || view.startsWith('channel-detail:'),
38
- onClick: () => navigate('channels'),
39
- })
40
- }
41
- if (config.modules.campaigns) {
42
- items.push({ id: 'campaigns', label: config.labels.campaigns, icon: 'Megaphone', active: view === 'campaigns', onClick: () => navigate('campaigns') })
43
- }
44
- if (config.modules.funnel) {
45
- items.push({ id: 'funnel', label: config.labels.funnel, icon: 'Filter', active: view === 'funnel', onClick: () => navigate('funnel') })
46
- }
47
- if (config.modules.landingPages) {
48
- items.push({ id: 'landing-pages', label: config.labels.landingPages, icon: 'LayoutTemplate', active: view === 'landing-pages', onClick: () => navigate('landing-pages') })
49
- }
50
- if (config.modules.contentPlanner) {
51
- items.push({
52
- id: 'content', label: config.labels.content, icon: 'Clapperboard',
53
- active: view === 'content' || view === 'content-brief' || view.startsWith('content-post:'),
54
- onClick: () => navigate('content'),
55
- })
56
- }
57
- if (config.modules.blog) {
58
- items.push({
59
- id: 'blog', label: config.labels.blog, icon: 'Newspaper',
60
- active: view === 'blog' || view === 'blog-new' || view.startsWith('blog-post:')
61
- || view === 'blog-cat-new' || view.startsWith('blog-cat:'),
62
- onClick: () => navigate('blog'),
63
- })
64
- }
65
- return items
66
- }
67
-
68
- export function MarketingPage({ config, provider, store, contentProvider, contentStore }: {
69
- config: ResolvedMarketingConfig
70
- provider: MarketingDataProvider
71
- store: StoreApi<MarketingUIState>
72
- contentProvider: ContentPlannerProvider
73
- contentStore: StoreApi<ContentPlannerUIState>
74
- }) {
75
- const t = useTranslation()
76
- const { view, direction, navigate } = useModuleNavigation('/marketing', {
77
- overview: 0, channels: 0, campaigns: 0, funnel: 0, 'landing-pages': 0, content: 0, blog: 0,
78
- 'channel-detail': 1, 'content-post': 1, 'content-brief': 1,
79
- 'blog-new': 1, 'blog-post': 1, 'blog-cat-new': 1, 'blog-cat': 1,
80
- }, 'overview')
81
-
82
- React.useEffect(() => { void store.getState().load() }, [store])
83
-
84
- // Agent RPC writes (createCampaign) land outside the provider — refresh past
85
- // its read cache when any agent write is announced.
86
- useDataChanged({ table: 'plg_marketing_campaigns' }, () => { void store.getState().refresh() }, [store])
87
-
88
- const isOverview = view === 'overview'
89
- const nav = buildNav(config, view, navigate)
90
-
91
- // Blog backoffice entities — one shared EntityDef each drives the list + form.
92
- const blogPostEntity = React.useMemo(() => buildBlogPostEntity(), [])
93
- const blogCategoryEntity = React.useMemo(() => buildBlogCategoryEntity(), [])
94
-
95
- const quickActions = React.useMemo<PluginQuickAction[]>(() => [
96
- { id: 'new-campaign', label: t('marketing.campaigns.new'), icon: 'Megaphone', description: config.labels.campaigns, action: () => navigate('campaigns') },
97
- ], [t, config.labels.campaigns])
98
-
99
- const renderView = createViewRouter([
100
- { id: 'channels', render: () => <ChannelsView onOpen={(id) => navigate(`channel-detail:${id}`)} /> },
101
- { id: 'channel-detail', render: ({ id }) => <ChannelDetailView channelId={id!} onBack={() => navigate('channels')} /> },
102
- { id: 'campaigns', render: () => <CampaignsView /> },
103
- { id: 'funnel', render: () => <FunnelView /> },
104
- { id: 'landing-pages', render: () => <LandingPagesView /> },
105
- { id: 'content', render: () => <ContentView onOpenPost={(id) => navigate(`content-post:${id}`)} /> },
106
- { id: 'content-post', render: ({ id }) => <PostPage postId={id!} onBack={() => navigate('content')} /> },
107
- { id: 'content-brief', render: () => <PlanBriefView onBack={() => navigate('content')} /> },
108
- { id: 'blog', render: () => <BlogAdminView navigate={navigate} /> },
109
- { id: 'blog-new', render: () => <BlogEntityForm entity={blogPostEntity} parentLabel={config.labels.blog} onDone={() => navigate('blog')} /> },
110
- { id: 'blog-post', render: ({ id }) => <BlogEntityForm entity={blogPostEntity} editId={id!} parentLabel={config.labels.blog} onDone={() => navigate('blog')} /> },
111
- { id: 'blog-cat-new', render: () => <BlogEntityForm entity={blogCategoryEntity} parentLabel={config.labels.blogCategories} onDone={() => navigate('blog')} /> },
112
- { id: 'blog-cat', render: ({ id }) => <BlogEntityForm entity={blogCategoryEntity} editId={id!} parentLabel={config.labels.blogCategories} onDone={() => navigate('blog')} /> },
113
- { id: 'overview', render: () => <OverviewView /> },
114
- ], 'overview')
115
-
116
- return (
117
- <MarketingContextProvider config={config} provider={provider} store={store}>
118
- <ContentPlannerContextProvider config={config} provider={contentProvider} store={contentStore}>
119
- <ModulePage
120
- title={config.labels.pageTitle}
121
- subtitle={config.labels.pageSubtitle}
122
- nav={nav}
123
- showHeader={isOverview}
124
- viewKey={view}
125
- direction={direction}
126
- headerAction={
127
- <ModuleActionBar
128
- quickActions={quickActions}
129
- settingsPath="/settings/marketing"
130
- settingsLabel={config.labels.pageTitle}
131
- />
132
- }
133
- >
134
- {renderView(view)}
135
- </ModulePage>
136
- </ContentPlannerContextProvider>
137
- </MarketingContextProvider>
138
- )
139
- }
@@ -1,131 +0,0 @@
1
- import React from 'react'
2
- import { ArrowDownRight, ArrowUpRight } from 'lucide-react'
3
- import { cn, SegmentedControl } from '@fayz-ai/ui'
4
- import { useTranslation } from '@fayz-ai/core'
5
- import type { CampaignStatus, DateRangeKey } from '../types'
6
- import { trendDelta } from '../format'
7
- import { useMarketingStore } from '../MarketingContext'
8
- import { ChannelIcon } from './icons'
9
- import { useChannelLookup } from '../MarketingContext'
10
-
11
- // ---------------------------------------------------------------------------
12
- // KPI card with optional trend chip.
13
- // ---------------------------------------------------------------------------
14
-
15
- export function KpiCard({
16
- icon: Icon, label, value, sub, current, previous, invertTrend,
17
- }: {
18
- icon: React.ComponentType<{ className?: string }>
19
- label: string
20
- value: string
21
- sub?: string
22
- current?: number
23
- previous?: number
24
- /** for cost-style metrics where down is good (e.g. CPA) */
25
- invertTrend?: boolean
26
- }) {
27
- const trend = current != null && previous != null ? trendDelta(current, previous) : null
28
- const good = trend ? (invertTrend ? trend.dir === 'down' : trend.dir === 'up') : false
29
- const bad = trend ? (invertTrend ? trend.dir === 'up' : trend.dir === 'down') : false
30
- return (
31
- <div className="rounded-card border border-border bg-card p-4">
32
- <div className="flex items-center gap-2 text-muted-foreground">
33
- <Icon className="h-4 w-4" />
34
- <span className="text-xs font-medium">{label}</span>
35
- </div>
36
- <div className="mt-2 flex items-end justify-between gap-2">
37
- <p className="text-2xl font-semibold text-foreground">{value}</p>
38
- {trend && trend.dir !== 'neutral' && (
39
- <span className={cn(
40
- 'mb-0.5 inline-flex items-center gap-0.5 text-xs font-medium',
41
- good && 'text-emerald-600', bad && 'text-rose-600',
42
- !good && !bad && 'text-muted-foreground',
43
- )}>
44
- {trend.dir === 'up' ? <ArrowUpRight className="h-3 w-3" /> : <ArrowDownRight className="h-3 w-3" />}
45
- {trend.pct.toFixed(0)}%
46
- </span>
47
- )}
48
- </div>
49
- {sub && <p className="mt-0.5 text-xs text-muted-foreground">{sub}</p>}
50
- </div>
51
- )
52
- }
53
-
54
- // ---------------------------------------------------------------------------
55
- // Date-range selector (segmented). Reads/writes the shared store range.
56
- // ---------------------------------------------------------------------------
57
-
58
- const RANGES: DateRangeKey[] = ['7d', '30d', '90d']
59
-
60
- export function RangeTabs() {
61
- const range = useMarketingStore((s) => s.range)
62
- const setRange = useMarketingStore((s) => s.setRange)
63
- return (
64
- <SegmentedControl
65
- options={RANGES}
66
- value={range}
67
- onChange={(r) => void setRange(r)}
68
- aria-label="Time range"
69
- />
70
- )
71
- }
72
-
73
- // ---------------------------------------------------------------------------
74
- // Status badge for campaigns.
75
- // ---------------------------------------------------------------------------
76
-
77
- const STATUS_STYLE: Record<CampaignStatus, string> = {
78
- active: 'bg-emerald-100 text-emerald-700',
79
- paused: 'bg-amber-100 text-amber-700',
80
- ended: 'bg-muted text-muted-foreground',
81
- draft: 'bg-slate-100 text-slate-600',
82
- }
83
-
84
- export function StatusBadge({ status }: { status: CampaignStatus }) {
85
- const t = useTranslation()
86
- return (
87
- <span className={cn('rounded-full px-2 py-0.5 text-xs font-medium capitalize', STATUS_STYLE[status])}>
88
- {t(`marketing.status.${status}`)}
89
- </span>
90
- )
91
- }
92
-
93
- // ---------------------------------------------------------------------------
94
- // Channel cell (icon + label) used in tables.
95
- // ---------------------------------------------------------------------------
96
-
97
- export function ChannelCell({ channelId }: { channelId: string }) {
98
- const lookup = useChannelLookup()
99
- const ch = lookup.get(channelId)
100
- return (
101
- <span className="inline-flex items-center gap-1.5 text-foreground">
102
- <ChannelIcon name={ch?.icon ?? 'Link2'} className="h-3.5 w-3.5 text-muted-foreground" />
103
- {ch?.label ?? channelId}
104
- </span>
105
- )
106
- }
107
-
108
- // ---------------------------------------------------------------------------
109
- // Horizontal proportion bar (funnel / channel-mix), no charting dep.
110
- // ---------------------------------------------------------------------------
111
-
112
- export function ProportionBar({ value, max, color, label, right }: {
113
- value: number
114
- max: number
115
- color: string
116
- label: React.ReactNode
117
- right?: React.ReactNode
118
- }) {
119
- const pct = max > 0 ? Math.max((value / max) * 100, 2) : 0
120
- return (
121
- <div className="flex items-center gap-3">
122
- <div className="w-32 shrink-0 text-sm text-muted-foreground">{label}</div>
123
- <div className="h-7 flex-1 overflow-hidden rounded-md bg-muted/40">
124
- <div className="flex h-full items-center rounded-md px-2 text-xs font-semibold"
125
- style={{ width: `${pct}%`, backgroundColor: color + '26', color }}>
126
- {right}
127
- </div>
128
- </div>
129
- </div>
130
- )
131
- }
@@ -1,36 +0,0 @@
1
- import React from 'react'
2
- import {
3
- Search, Megaphone, Globe, Mail, Users, MousePointerClick,
4
- Instagram, MessageCircle, DoorOpen, Utensils, Bike, Link2,
5
- Youtube, Facebook, Linkedin, Twitter, Music2, Pin, AtSign,
6
- } from 'lucide-react'
7
-
8
- // Channel icon names referenced by presets → lucide components. Falls back to
9
- // a generic link icon for anything unmapped.
10
- const ICONS: Record<string, React.ComponentType<{ className?: string }>> = {
11
- Search, Megaphone, Globe, Mail, Users, MousePointerClick,
12
- Instagram, MessageCircle, DoorOpen, Utensils, Bike,
13
- }
14
-
15
- export function ChannelIcon({ name, className }: { name: string; className?: string }) {
16
- const Icon = ICONS[name] ?? Link2
17
- return <Icon className={className} />
18
- }
19
-
20
- // Platform icons for the content planner. Lucide has no TikTok / Pinterest /
21
- // Threads glyphs — Music2 / Pin / AtSign are the conventional stand-ins.
22
- const PLATFORM_ICONS: Record<string, React.ComponentType<{ className?: string }>> = {
23
- instagram: Instagram,
24
- tiktok: Music2,
25
- youtube: Youtube,
26
- facebook: Facebook,
27
- linkedin: Linkedin,
28
- x: Twitter,
29
- pinterest: Pin,
30
- threads: AtSign,
31
- }
32
-
33
- export function PlatformIcon({ platform, className }: { platform: string; className?: string }) {
34
- const Icon = PLATFORM_ICONS[platform] ?? Link2
35
- return <Icon className={className} />
36
- }
@@ -1,91 +0,0 @@
1
- import type { EntityDef, FieldDef } from '@fayz-ai/core'
2
-
3
- // ---------------------------------------------------------------------------
4
- // Blog backoffice entities — the single source of truth that drives BOTH the
5
- // generic list (CrudListView columns + facets) and the create/edit form
6
- // (CrudFormPage fields, incl. the markdown body + category relation).
7
- //
8
- // Persistence is the generic Supabase provider resolved from `data.table`
9
- // (plg_blog_posts / plg_blog_categories, owned by @fayz-ai/plugin-blog's
10
- // migrations). camelCase field keys map to snake_case columns automatically.
11
- // The website reads the same rows through the anon v_public_blog_posts view.
12
- // ---------------------------------------------------------------------------
13
-
14
- export const BLOG_POSTS_TABLE = 'plg_blog_posts'
15
- export const BLOG_CATEGORIES_TABLE = 'plg_blog_categories'
16
-
17
- export interface BlogPostRow {
18
- id: string
19
- title: string
20
- slug: string
21
- excerpt: string
22
- body: string
23
- categoryId?: string
24
- coverImage?: string
25
- authorName?: string
26
- authorRole?: string
27
- authorAvatarUrl?: string
28
- authorBio?: string
29
- status: 'draft' | 'published'
30
- readTime?: string
31
- publishedAt?: string
32
- }
33
-
34
- export interface BlogCategoryRow {
35
- id: string
36
- name: string
37
- slug: string
38
- description?: string
39
- }
40
-
41
- export function buildBlogPostEntity(): EntityDef<BlogPostRow> {
42
- const fields: FieldDef[] = [
43
- { key: 'title', label: 'Title', type: 'text', required: true, searchable: true, showInTable: true, sortable: true },
44
- { key: 'slug', label: 'Slug', type: 'text', required: true, searchable: true, showInTable: false, hint: 'URL identifier, e.g. "cannabis-and-oral-health"' },
45
- { key: 'status', label: 'Status', type: 'select', options: ['draft', 'published'], showInTable: true, defaultValue: 'draft' },
46
- { key: 'categoryId', label: 'Category', type: 'relation', relation: { table: BLOG_CATEGORIES_TABLE, valueField: 'id', labelField: 'name', tenantScoped: true }, showInTable: false },
47
- { key: 'excerpt', label: 'Excerpt', type: 'textarea', span: 2, showInTable: false, placeholder: 'Short summary shown on cards' },
48
- { key: 'body', label: 'Body', type: 'markdown', span: 2, showInTable: false },
49
- { key: 'coverImage', label: 'Cover image', type: 'image', showInTable: false, placeholder: 'https://…' },
50
- { key: 'readTime', label: 'Read time', type: 'text', showInTable: false, placeholder: '5 min' },
51
- { key: 'publishedAt', label: 'Published at', type: 'date', showInTable: true, sortable: true },
52
- { key: 'authorName', label: 'Author name', type: 'text', showInTable: false },
53
- { key: 'authorRole', label: 'Author role', type: 'text', showInTable: false, placeholder: 'e.g. Cirurgião-Dentista' },
54
- { key: 'authorAvatarUrl', label: 'Author avatar', type: 'image', showInTable: false, placeholder: 'https://…' },
55
- { key: 'authorBio', label: 'Author bio', type: 'textarea', span: 2, showInTable: false },
56
- ]
57
- return {
58
- name: 'Post',
59
- namePlural: 'Posts',
60
- icon: 'Newspaper',
61
- fields,
62
- displayField: 'title',
63
- subtitleField: 'slug',
64
- facets: [{ field: 'status', allLabel: 'All' }],
65
- data: {
66
- table: BLOG_POSTS_TABLE,
67
- tenantScoped: true,
68
- searchColumns: ['title', 'slug', 'excerpt'],
69
- },
70
- }
71
- }
72
-
73
- export function buildBlogCategoryEntity(): EntityDef<BlogCategoryRow> {
74
- const fields: FieldDef[] = [
75
- { key: 'name', label: 'Name', type: 'text', required: true, searchable: true, showInTable: true, sortable: true },
76
- { key: 'slug', label: 'Slug', type: 'text', required: true, searchable: true, showInTable: true, hint: 'URL identifier' },
77
- { key: 'description', label: 'Description', type: 'textarea', span: 2, showInTable: false },
78
- ]
79
- return {
80
- name: 'Category',
81
- namePlural: 'Categories',
82
- icon: 'Tags',
83
- fields,
84
- displayField: 'name',
85
- data: {
86
- table: BLOG_CATEGORIES_TABLE,
87
- tenantScoped: true,
88
- searchColumns: ['name', 'slug'],
89
- },
90
- }
91
- }