@fayz-ai/plugin-courses 0.9.0 → 0.10.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.
Files changed (79) hide show
  1. package/README.md +3 -3
  2. package/dist/components/CommerceUI.d.ts +19 -2
  3. package/dist/components/CommerceUI.d.ts.map +1 -1
  4. package/dist/components/dashboardWidgets.d.ts.map +1 -0
  5. package/dist/index.d.ts +26 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +773 -309
  8. package/dist/index.js.map +1 -1
  9. package/dist/integrations/kiwify/connectorDef.d.ts +34 -0
  10. package/dist/integrations/kiwify/connectorDef.d.ts.map +1 -0
  11. package/dist/integrations/kiwify/data.d.ts +58 -0
  12. package/dist/integrations/kiwify/data.d.ts.map +1 -0
  13. package/dist/lib/nav.d.ts.map +1 -0
  14. package/dist/locales/en.d.ts.map +1 -1
  15. package/dist/locales/pt-BR.d.ts.map +1 -1
  16. package/dist/public/createPublicCheckoutPlugin.d.ts +1 -1
  17. package/dist/public/createPublicCheckoutPlugin.d.ts.map +1 -1
  18. package/dist/public/{data.d.ts → data/index.d.ts} +1 -1
  19. package/dist/public/data/index.d.ts.map +1 -0
  20. package/dist/public/{data.supabase.d.ts → data/supabase.d.ts} +2 -2
  21. package/dist/public/data/supabase.d.ts.map +1 -0
  22. package/dist/public/index.d.ts +4 -4
  23. package/dist/public/index.d.ts.map +1 -1
  24. package/dist/public/index.js +353 -0
  25. package/dist/public/index.js.map +1 -0
  26. package/dist/public/{CheckoutScreen.d.ts → views/CheckoutScreen.d.ts} +1 -1
  27. package/dist/public/views/CheckoutScreen.d.ts.map +1 -0
  28. package/dist/views/CourseEditorPage.d.ts.map +1 -0
  29. package/dist/views/CourseOrderDetailView.d.ts +4 -3
  30. package/dist/views/CourseOrderDetailView.d.ts.map +1 -1
  31. package/dist/{pages → views}/CoursesListPage.d.ts.map +1 -1
  32. package/dist/views/FinancialPage.d.ts.map +1 -0
  33. package/dist/{pages → views}/ReportsPage.d.ts.map +1 -1
  34. package/dist/views/SalesPage.d.ts +19 -0
  35. package/dist/views/SalesPage.d.ts.map +1 -0
  36. package/dist/views/SubscriptionsPage.d.ts +7 -0
  37. package/dist/views/SubscriptionsPage.d.ts.map +1 -0
  38. package/package.json +12 -12
  39. package/dist/connectors/stripe.d.ts +0 -3
  40. package/dist/connectors/stripe.d.ts.map +0 -1
  41. package/dist/dashboardWidgets.d.ts.map +0 -1
  42. package/dist/nav.d.ts.map +0 -1
  43. package/dist/pages/CourseEditorPage.d.ts.map +0 -1
  44. package/dist/pages/FinancialPage.d.ts.map +0 -1
  45. package/dist/pages/SalesPage.d.ts +0 -6
  46. package/dist/pages/SalesPage.d.ts.map +0 -1
  47. package/dist/pages/SubscriptionsPage.d.ts +0 -6
  48. package/dist/pages/SubscriptionsPage.d.ts.map +0 -1
  49. package/dist/public/CheckoutScreen.d.ts.map +0 -1
  50. package/dist/public/data.d.ts.map +0 -1
  51. package/dist/public/data.supabase.d.ts.map +0 -1
  52. package/src/components/CommerceUI.tsx +0 -153
  53. package/src/connectors/stripe.ts +0 -34
  54. package/src/dashboardWidgets.tsx +0 -64
  55. package/src/index.ts +0 -150
  56. package/src/lib/format.ts +0 -22
  57. package/src/locales/en.ts +0 -203
  58. package/src/locales/index.ts +0 -7
  59. package/src/locales/pt-BR.ts +0 -203
  60. package/src/nav.ts +0 -9
  61. package/src/pages/CourseEditorPage.tsx +0 -1122
  62. package/src/pages/CoursesListPage.tsx +0 -137
  63. package/src/pages/FinancialPage.tsx +0 -81
  64. package/src/pages/ReportsPage.tsx +0 -59
  65. package/src/pages/SalesPage.tsx +0 -86
  66. package/src/pages/SubscriptionsPage.tsx +0 -56
  67. package/src/public/CheckoutScreen.tsx +0 -279
  68. package/src/public/createPublicCheckoutPlugin.tsx +0 -88
  69. package/src/public/data.supabase.ts +0 -63
  70. package/src/public/data.ts +0 -89
  71. package/src/public/index.ts +0 -26
  72. package/src/settings/CoursesSettings.tsx +0 -55
  73. package/src/views/CourseOrderDetailView.tsx +0 -132
  74. /package/dist/{dashboardWidgets.d.ts → components/dashboardWidgets.d.ts} +0 -0
  75. /package/dist/{nav.d.ts → lib/nav.d.ts} +0 -0
  76. /package/dist/{pages → views}/CourseEditorPage.d.ts +0 -0
  77. /package/dist/{pages → views}/CoursesListPage.d.ts +0 -0
  78. /package/dist/{pages → views}/FinancialPage.d.ts +0 -0
  79. /package/dist/{pages → views}/ReportsPage.d.ts +0 -0
@@ -0,0 +1,34 @@
1
+ import { type ConnectorDefinition, type ConnectorField, type ConnectorSetting } from '@fayz-ai/core';
2
+ export declare const KIWIFY_CONNECTOR_ID = "kiwify";
3
+ /**
4
+ * What authorizes the connection — all three come from the same Kiwify screen
5
+ * ("Apps → API → Criar API Key"), and none can be inferred from the others.
6
+ *
7
+ * Two of them are one credential. `client_id` and `client_secret` are issued
8
+ * together and the id is useless to anyone without the secret, so the id
9
+ * carries `secret: true`: it goes to the vault WITH its other half instead of
10
+ * falling through to this file, which is how half a credential ends up in a
11
+ * pool table. Only the secret is masked — a merchant has to be able to check
12
+ * they pasted the right id.
13
+ *
14
+ * The account id is genuinely not a credential. It is the
15
+ * `x-kiwify-account-id` header that says WHICH account the token is spent on,
16
+ * so it stays on the connection row where an operator can read it back.
17
+ *
18
+ * No `providerInstall`: Kiwify's docs name the path ("Apps → API") and publish
19
+ * no URL for it, and a door that opens somewhere wrong is worse than a
20
+ * placeholder that says where to look.
21
+ */
22
+ export declare const KIWIFY_FIELDS: ConnectorField[];
23
+ /** Kiwify's own 90-day ceiling on a sales query, in one place. */
24
+ export declare const KIWIFY_MAX_WINDOW_DAYS = 90;
25
+ /**
26
+ * How the live feed BEHAVES — chosen after the account is linked, changed
27
+ * whenever the tenant likes.
28
+ *
29
+ * None of it authorizes anything, which is why none of it is a field: a school
30
+ * that decides to stop auto-enrolling has not re-authorized Kiwify.
31
+ */
32
+ export declare const KIWIFY_SETTINGS: ConnectorSetting[];
33
+ export declare const kiwifyConnectorDef: ConnectorDefinition;
34
+ //# sourceMappingURL=connectorDef.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connectorDef.d.ts","sourceRoot":"","sources":["../../../src/integrations/kiwify/connectorDef.tsx"],"names":[],"mappings":"AA2BA,OAAO,EAKL,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EAEtB,MAAM,eAAe,CAAA;AAWtB,eAAO,MAAM,mBAAmB,WAAW,CAAA;AAE3C;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,aAAa,EAAE,cAAc,EAIzC,CAAA;AAED,kEAAkE;AAClE,eAAO,MAAM,sBAAsB,KAAK,CAAA;AAExC;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,EAAE,gBAAgB,EAwC7C,CAAA;AAuND,eAAO,MAAM,kBAAkB,EAAE,mBAkGhC,CAAA"}
@@ -0,0 +1,58 @@
1
+ /** The edge function that holds every Kiwify call this connector makes. */
2
+ export declare const KIWIFY_FUNCTION = "kiwify-courses-sync";
3
+ /** `plg_courses_orders.external_source` for everything this connector writes. */
4
+ export declare const KIWIFY_SOURCE = "kiwify";
5
+ export interface KiwifyProduct {
6
+ id: string;
7
+ name: string;
8
+ }
9
+ export interface KiwifyImportResult {
10
+ fetched: number;
11
+ imported: number;
12
+ updated: number;
13
+ duplicates: number;
14
+ unmapped: number;
15
+ enrolled: number;
16
+ }
17
+ /** A course here, and the Kiwify product it is — the tenant's own de-para. */
18
+ export interface CourseMapping {
19
+ courseId: string;
20
+ title: string;
21
+ externalId: string | null;
22
+ }
23
+ /**
24
+ * Ask the provider who we are.
25
+ *
26
+ * Not `testConnection`, and not on the ConnectorDefinition: that verb runs
27
+ * BEFORE saving, with the form's values, and this connector's secret never
28
+ * reaches this code — the hub correctly hides the button for exactly that
29
+ * reason. This runs AFTER the credential is placed, which is the only moment
30
+ * there is anything to test.
31
+ *
32
+ * What comes back is the account's name, which is the only answer a person can
33
+ * verify: "sim, é a minha conta" is a different statement from "a chamada não
34
+ * deu erro".
35
+ */
36
+ export declare function checkAccount(): Promise<{
37
+ ok: boolean;
38
+ message?: string;
39
+ }>;
40
+ /** The provider's catalogue, for the mapping panel. Never stored — read each time it is shown. */
41
+ export declare function listProducts(): Promise<KiwifyProduct[]>;
42
+ /** Pull a period of sales. The window is capped at Kiwify's own 90 days server-side. */
43
+ export declare function importSales(input: {
44
+ from: string;
45
+ to: string;
46
+ }): Promise<KiwifyImportResult>;
47
+ /**
48
+ * The de-para, read straight off the courses table.
49
+ *
50
+ * `external_id` is not on the `Course` domain type and is not going on it: it is
51
+ * a fact about an INTEGRATION, meaningless to a school that never sold on
52
+ * Kiwify, and putting it in the provider contract would make every consumer of
53
+ * `Course` carry a field only this panel reads.
54
+ */
55
+ export declare function listMappings(): Promise<CourseMapping[]>;
56
+ /** Point a course at a Kiwify product, or clear it. */
57
+ export declare function saveMapping(courseId: string, externalId: string | null): Promise<void>;
58
+ //# sourceMappingURL=data.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../../src/integrations/kiwify/data.ts"],"names":[],"mappings":"AAYA,2EAA2E;AAC3E,eAAO,MAAM,eAAe,wBAAwB,CAAA;AAEpD,iFAAiF;AACjF,eAAO,MAAM,aAAa,WAAW,CAAA;AAErC,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,8EAA8E;AAC9E,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAuCD;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAEzE;AAED,kGAAkG;AAClG,wBAAgB,YAAY,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC,CAEvD;AAED,wFAAwF;AACxF,wBAAgB,WAAW,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAE5F;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC,CAkB7D;AAED,uDAAuD;AACvD,wBAAsB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAY5F"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nav.d.ts","sourceRoot":"","sources":["../../src/lib/nav.ts"],"names":[],"mappings":"AAMA,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE3C"}
@@ -1 +1 @@
1
- {"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../src/locales/en.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CA0MrC,CAAA"}
1
+ {"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../src/locales/en.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAuMrC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"pt-BR.d.ts","sourceRoot":"","sources":["../../src/locales/pt-BR.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CA0MvC,CAAA"}
1
+ {"version":3,"file":"pt-BR.d.ts","sourceRoot":"","sources":["../../src/locales/pt-BR.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAuMvC,CAAA"}
@@ -1,7 +1,7 @@
1
1
  import { type FC } from 'react';
2
2
  import type { PluginManifest, PluginScope, VerticalId } from '@fayz-ai/core';
3
3
  import { type PublicCheckoutDataProvider } from './data';
4
- import { type CheckoutScreenProps } from './CheckoutScreen';
4
+ import { type CheckoutScreenProps } from './views/CheckoutScreen';
5
5
  export interface PublicCheckoutOptions {
6
6
  /** Mount path for the public checkout route. Default '/checkout'. Slug is appended as ':slug'. */
7
7
  basePath?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"createPublicCheckoutPlugin.d.ts","sourceRoot":"","sources":["../../src/public/createPublicCheckoutPlugin.tsx"],"names":[],"mappings":"AAUA,OAAO,EAAiB,KAAK,EAAE,EAAE,MAAM,OAAO,CAAA;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC5E,OAAO,EAA8B,KAAK,0BAA0B,EAAE,MAAM,QAAQ,CAAA;AAEpF,OAAO,EAAkB,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAE3E,MAAM,WAAW,qBAAqB;IACpC,kGAAkG;IAClG,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,uEAAuE;IACvE,YAAY,CAAC,EAAE,0BAA0B,CAAA;IACzC,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,UAAU,CAAC,EAAE,UAAU,CAAA;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,cAAc,CAAA;IACxB,YAAY,EAAE,0BAA0B,CAAA;IACxC,0FAA0F;IAC1F,cAAc,EAAE,EAAE,CAAC,IAAI,CAAC,mBAAmB,EAAE,UAAU,CAAC,CAAC,CAAA;CAC1D;AAED,wBAAgB,0BAA0B,CAAC,OAAO,GAAE,qBAA0B,GAAG,oBAAoB,CAgDpG"}
1
+ {"version":3,"file":"createPublicCheckoutPlugin.d.ts","sourceRoot":"","sources":["../../src/public/createPublicCheckoutPlugin.tsx"],"names":[],"mappings":"AAUA,OAAO,EAAiB,KAAK,EAAE,EAAE,MAAM,OAAO,CAAA;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC5E,OAAO,EAA8B,KAAK,0BAA0B,EAAE,MAAM,QAAQ,CAAA;AAEpF,OAAO,EAAkB,KAAK,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAEjF,MAAM,WAAW,qBAAqB;IACpC,kGAAkG;IAClG,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,uEAAuE;IACvE,YAAY,CAAC,EAAE,0BAA0B,CAAA;IACzC,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,UAAU,CAAC,EAAE,UAAU,CAAA;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,cAAc,CAAA;IACxB,YAAY,EAAE,0BAA0B,CAAA;IACxC,0FAA0F;IAC1F,cAAc,EAAE,EAAE,CAAC,IAAI,CAAC,mBAAmB,EAAE,UAAU,CAAC,CAAC,CAAA;CAC1D;AAED,wBAAgB,0BAA0B,CAAC,OAAO,GAAE,qBAA0B,GAAG,oBAAoB,CAgDpG"}
@@ -25,4 +25,4 @@ export interface MockCheckoutOptions {
25
25
  * single course + offer and a fake order id so the flow renders end-to-end.
26
26
  */
27
27
  export declare function createMockCheckoutProvider(opts?: MockCheckoutOptions): PublicCheckoutDataProvider;
28
- //# sourceMappingURL=data.d.ts.map
28
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/public/data/index.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AAErD,MAAM,MAAM,qBAAqB,GAAG,KAAK,GAAG,MAAM,CAAA;AAElD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,aAAa,CAAC,EAAE,qBAAqB,CAAA;CACtC;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,0BAA0B;IACzC,6EAA6E;IAC7E,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IACrD,0EAA0E;IAC1E,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAA;IACxD,4EAA4E;IAC5E,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAA;CAC/E;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;IACxB,KAAK,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;CACvB;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,GAAE,mBAAwB,GAAG,0BAA0B,CA8CrG"}
@@ -1,7 +1,7 @@
1
- import type { PublicCheckoutDataProvider } from './data';
1
+ import type { PublicCheckoutDataProvider } from './index';
2
2
  export interface SupabaseCheckoutOptions {
3
3
  /** Tenant this checkout sells for (RPC arg — required). */
4
4
  tenantId: string;
5
5
  }
6
6
  export declare function createSupabaseCheckoutProvider(options: SupabaseCheckoutOptions): PublicCheckoutDataProvider;
7
- //# sourceMappingURL=data.supabase.d.ts.map
7
+ //# sourceMappingURL=supabase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"supabase.d.ts","sourceRoot":"","sources":["../../../src/public/data/supabase.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,0BAA0B,EAAmB,MAAM,SAAS,CAAA;AAE1E,MAAM,WAAW,uBAAuB;IACtC,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAA;CACjB;AAYD,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,uBAAuB,GAAG,0BAA0B,CA8B3G"}
@@ -1,9 +1,9 @@
1
1
  export { createPublicCheckoutPlugin } from './createPublicCheckoutPlugin';
2
2
  export type { PublicCheckoutOptions, PublicCheckoutPlugin } from './createPublicCheckoutPlugin';
3
- export { CheckoutScreen } from './CheckoutScreen';
4
- export type { CheckoutScreenProps } from './CheckoutScreen';
3
+ export { CheckoutScreen } from './views/CheckoutScreen';
4
+ export type { CheckoutScreenProps } from './views/CheckoutScreen';
5
5
  export { createMockCheckoutProvider } from './data';
6
- export { createSupabaseCheckoutProvider } from './data.supabase';
7
- export type { SupabaseCheckoutOptions } from './data.supabase';
6
+ export { createSupabaseCheckoutProvider } from './data/supabase';
7
+ export type { SupabaseCheckoutOptions } from './data/supabase';
8
8
  export type { PublicCheckoutDataProvider, PlaceOrderInput, PlaceOrderResult, CheckoutPaymentMethod, MockCheckoutOptions, } from './data';
9
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/public/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAA;AACzE,YAAY,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAA;AAE/F,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACjD,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAG3D,OAAO,EAAE,0BAA0B,EAAE,MAAM,QAAQ,CAAA;AACnD,OAAO,EAAE,8BAA8B,EAAE,MAAM,iBAAiB,CAAA;AAChE,YAAY,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAA;AAC9D,YAAY,EACV,0BAA0B,EAC1B,eAAe,EACf,gBAAgB,EAChB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,QAAQ,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/public/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAA;AACzE,YAAY,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAA;AAE/F,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AACvD,YAAY,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAGjE,OAAO,EAAE,0BAA0B,EAAE,MAAM,QAAQ,CAAA;AACnD,OAAO,EAAE,8BAA8B,EAAE,MAAM,iBAAiB,CAAA;AAChE,YAAY,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAA;AAC9D,YAAY,EACV,0BAA0B,EAC1B,eAAe,EACf,gBAAgB,EAChB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,QAAQ,CAAA"}
@@ -0,0 +1,353 @@
1
+ import { useState, useEffect, useMemo, createElement } from 'react';
2
+ import { getSupabaseClientOptional } from '@fayz-ai/core';
3
+ import { createSupabaseCoursesProvider } from '@fayz-ai/courses';
4
+ import { Loader2, CheckCircle2, ShieldCheck, QrCode, CreditCard, Lock } from 'lucide-react';
5
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
+
7
+ // src/public/createPublicCheckoutPlugin.tsx
8
+
9
+ // src/public/data/index.ts
10
+ function createMockCheckoutProvider(opts = {}) {
11
+ const course = {
12
+ id: "mock-course",
13
+ slug: "curso-demo",
14
+ title: "Curso Demo",
15
+ subtitle: "Um curso de demonstra\xE7\xE3o",
16
+ description: "Conte\xFAdo de exemplo para validar o fluxo de checkout.",
17
+ thumbnailUrl: null,
18
+ price: 197,
19
+ currency: "BRL",
20
+ status: "published",
21
+ sortOrder: 0,
22
+ createdAt: "2026-01-01T00:00:00.000Z",
23
+ updatedAt: "2026-01-01T00:00:00.000Z",
24
+ ...opts.course
25
+ };
26
+ const offer = {
27
+ id: "mock-offer",
28
+ courseId: course.id,
29
+ name: course.title,
30
+ price: course.price,
31
+ currency: course.currency,
32
+ kind: "one_time",
33
+ recurringInterval: null,
34
+ isDefault: true,
35
+ isOrderBump: false,
36
+ sortOrder: 0,
37
+ ...opts.offer
38
+ };
39
+ let seq = 0;
40
+ return {
41
+ async getCourseBySlug(slug) {
42
+ return { ...course, slug: slug || course.slug };
43
+ },
44
+ async getDefaultOffer() {
45
+ return offer;
46
+ },
47
+ async placeOrder() {
48
+ seq += 1;
49
+ if (typeof console !== "undefined") {
50
+ console.warn("[plugin-courses/public] MOCK placeOrder \u2014 no sale was recorded. Pass a tenantId for the real DB.");
51
+ }
52
+ return { orderId: `mock-order-${seq}` };
53
+ }
54
+ };
55
+ }
56
+ function getClient() {
57
+ const client = getSupabaseClientOptional();
58
+ if (!client) throw new Error("No Supabase client registered \u2014 call setGlobalSupabaseClient() at app startup.");
59
+ return client;
60
+ }
61
+ function createSupabaseCheckoutProvider(options) {
62
+ const { tenantId } = options;
63
+ const courses = createSupabaseCoursesProvider();
64
+ return {
65
+ async getCourseBySlug(slug) {
66
+ return courses.getCourse(slug);
67
+ },
68
+ async getDefaultOffer(courseId) {
69
+ const offers = await courses.listOffers(courseId);
70
+ return offers.find((o) => o.isDefault) ?? offers[0] ?? null;
71
+ },
72
+ async placeOrder(offerId, input) {
73
+ const email = input.email.trim();
74
+ const { data, error } = await getClient().rpc("course_place_order_simulated", {
75
+ p_tenant_id: tenantId,
76
+ p_offer_id: offerId,
77
+ p_customer_email: email,
78
+ p_customer_name: input.name?.trim() || null,
79
+ p_payment_method: input.paymentMethod ?? "pix"
80
+ });
81
+ if (error) throw new Error(`[plugin-courses/public] placeOrder: ${error.message}`);
82
+ const orderId = Array.isArray(data) ? data[0] : data;
83
+ return { orderId: String(orderId) };
84
+ }
85
+ };
86
+ }
87
+ function formatMoney(value, currency = "BRL", locale = "pt-BR") {
88
+ try {
89
+ return new Intl.NumberFormat(locale, { style: "currency", currency }).format(value);
90
+ } catch {
91
+ return `R$ ${value.toFixed(2)}`;
92
+ }
93
+ }
94
+ var CheckoutScreen = ({
95
+ slug,
96
+ provider,
97
+ accent = "#10b981",
98
+ memberAreaHref = "#/",
99
+ onSuccess
100
+ }) => {
101
+ const [course, setCourse] = useState(null);
102
+ const [offer, setOffer] = useState(null);
103
+ const [loading, setLoading] = useState(true);
104
+ const [loadError, setLoadError] = useState(null);
105
+ const [name, setName] = useState("");
106
+ const [email, setEmail] = useState("");
107
+ const [method, setMethod] = useState("pix");
108
+ const [submitting, setSubmitting] = useState(false);
109
+ const [submitError, setSubmitError] = useState(null);
110
+ const [orderId, setOrderId] = useState(null);
111
+ useEffect(() => {
112
+ let cancelled = false;
113
+ setLoading(true);
114
+ setLoadError(null);
115
+ (async () => {
116
+ try {
117
+ const c = await provider.getCourseBySlug(slug);
118
+ if (!c) throw new Error("Curso n\xE3o encontrado.");
119
+ const o = await provider.getDefaultOffer(c.id);
120
+ if (cancelled) return;
121
+ setCourse(c);
122
+ setOffer(o);
123
+ } catch (err) {
124
+ if (!cancelled) setLoadError(err instanceof Error ? err.message : "Falha ao carregar o curso.");
125
+ } finally {
126
+ if (!cancelled) setLoading(false);
127
+ }
128
+ })();
129
+ return () => {
130
+ cancelled = true;
131
+ };
132
+ }, [slug, provider]);
133
+ const price = offer?.price ?? course?.price ?? 0;
134
+ const currency = offer?.currency ?? course?.currency ?? "BRL";
135
+ const isSubscription = offer?.kind === "subscription";
136
+ const intervalLabel = offer?.recurringInterval === "year" ? "/ano" : "/m\xEAs";
137
+ const canSubmit = useMemo(
138
+ () => !!offer && !!email.trim() && /.+@.+\..+/.test(email.trim()) && !submitting,
139
+ [offer, email, submitting]
140
+ );
141
+ async function handleSubmit(e) {
142
+ e.preventDefault();
143
+ if (!offer || !canSubmit) return;
144
+ setSubmitting(true);
145
+ setSubmitError(null);
146
+ try {
147
+ const { orderId: id } = await provider.placeOrder(offer.id, {
148
+ email: email.trim(),
149
+ name: name.trim() || void 0,
150
+ paymentMethod: method
151
+ });
152
+ setOrderId(id);
153
+ onSuccess?.({ orderId: id, email: email.trim() });
154
+ } catch (err) {
155
+ setSubmitError(err instanceof Error ? err.message : "N\xE3o foi poss\xEDvel concluir a compra. Tente novamente.");
156
+ } finally {
157
+ setSubmitting(false);
158
+ }
159
+ }
160
+ if (loading) {
161
+ return /* @__PURE__ */ jsx("div", { className: "min-h-screen flex items-center justify-center bg-gray-50", children: /* @__PURE__ */ jsx(Loader2, { className: "w-6 h-6 animate-spin text-gray-400" }) });
162
+ }
163
+ if (loadError || !course) {
164
+ return /* @__PURE__ */ jsx("div", { className: "min-h-screen flex items-center justify-center bg-gray-50 p-6", children: /* @__PURE__ */ jsxs("div", { className: "max-w-md text-center", children: [
165
+ /* @__PURE__ */ jsx("p", { className: "text-lg font-semibold text-gray-900", children: "Ops!" }),
166
+ /* @__PURE__ */ jsx("p", { className: "mt-2 text-gray-600", children: loadError ?? "Curso indispon\xEDvel." }),
167
+ /* @__PURE__ */ jsx("a", { href: "#/", className: "mt-4 inline-block text-sm font-medium underline", style: { color: accent }, children: "Voltar" })
168
+ ] }) });
169
+ }
170
+ if (orderId) {
171
+ return /* @__PURE__ */ jsx("div", { className: "min-h-screen flex items-center justify-center bg-gray-50 p-6", children: /* @__PURE__ */ jsxs("div", { className: "max-w-md w-full bg-white rounded-2xl shadow-sm border border-gray-100 p-8 text-center", children: [
172
+ /* @__PURE__ */ jsx(
173
+ "div",
174
+ {
175
+ className: "mx-auto w-14 h-14 rounded-full flex items-center justify-center",
176
+ style: { backgroundColor: `${accent}1a` },
177
+ children: /* @__PURE__ */ jsx(CheckCircle2, { className: "w-8 h-8", style: { color: accent } })
178
+ }
179
+ ),
180
+ /* @__PURE__ */ jsx("h1", { className: "mt-4 text-xl font-bold text-gray-900", children: "Compra confirmada!" }),
181
+ /* @__PURE__ */ jsxs("p", { className: "mt-2 text-gray-600", children: [
182
+ "Seu acesso a ",
183
+ /* @__PURE__ */ jsx("strong", { children: course.title }),
184
+ " j\xE1 est\xE1 liberado. Enviamos os detalhes para",
185
+ " ",
186
+ /* @__PURE__ */ jsx("strong", { children: email.trim() }),
187
+ "."
188
+ ] }),
189
+ /* @__PURE__ */ jsxs("p", { className: "mt-1 text-xs text-gray-400", children: [
190
+ "Pedido ",
191
+ orderId
192
+ ] }),
193
+ /* @__PURE__ */ jsx(
194
+ "a",
195
+ {
196
+ href: memberAreaHref,
197
+ className: "mt-6 inline-flex w-full items-center justify-center rounded-xl px-4 py-3 font-semibold text-white",
198
+ style: { backgroundColor: accent },
199
+ children: "Acessar \xE1rea de membros"
200
+ }
201
+ )
202
+ ] }) });
203
+ }
204
+ return /* @__PURE__ */ jsx("div", { className: "min-h-screen bg-gray-50 py-10 px-4", children: /* @__PURE__ */ jsxs("div", { className: "max-w-3xl mx-auto grid gap-6 md:grid-cols-5", children: [
205
+ /* @__PURE__ */ jsxs("aside", { className: "md:col-span-2 bg-white rounded-2xl border border-gray-100 shadow-sm p-6 h-fit", children: [
206
+ course.thumbnailUrl && /* @__PURE__ */ jsx(
207
+ "img",
208
+ {
209
+ src: course.thumbnailUrl,
210
+ alt: course.title,
211
+ className: "w-full aspect-video object-cover rounded-xl mb-4"
212
+ }
213
+ ),
214
+ /* @__PURE__ */ jsx("h2", { className: "text-lg font-bold text-gray-900", children: course.title }),
215
+ course.subtitle && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-gray-500", children: course.subtitle }),
216
+ /* @__PURE__ */ jsx("div", { className: "mt-4 border-t border-gray-100 pt-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between", children: [
217
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-gray-500", children: offer?.name ?? "Acesso ao curso" }),
218
+ /* @__PURE__ */ jsxs("span", { className: "text-xl font-bold text-gray-900", children: [
219
+ formatMoney(price, currency),
220
+ isSubscription && /* @__PURE__ */ jsxs("span", { className: "text-sm font-normal text-gray-400", children: [
221
+ " ",
222
+ intervalLabel
223
+ ] })
224
+ ] })
225
+ ] }) }),
226
+ /* @__PURE__ */ jsxs("div", { className: "mt-4 flex items-center gap-2 text-xs text-gray-400", children: [
227
+ /* @__PURE__ */ jsx(ShieldCheck, { className: "w-4 h-4" }),
228
+ "Pagamento seguro \xB7 acesso imediato"
229
+ ] })
230
+ ] }),
231
+ /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "md:col-span-3 bg-white rounded-2xl border border-gray-100 shadow-sm p-6", children: [
232
+ /* @__PURE__ */ jsx("h1", { className: "text-xl font-bold text-gray-900", children: "Finalizar compra" }),
233
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Preencha seus dados para liberar o acesso." }),
234
+ /* @__PURE__ */ jsxs("label", { className: "block mt-6 text-sm font-medium text-gray-700", children: [
235
+ "Nome completo",
236
+ /* @__PURE__ */ jsx(
237
+ "input",
238
+ {
239
+ type: "text",
240
+ value: name,
241
+ onChange: (e) => setName(e.target.value),
242
+ placeholder: "Seu nome",
243
+ className: "mt-1 w-full rounded-xl border border-gray-200 px-3 py-2.5 focus:outline-none focus:ring-2",
244
+ style: { ["--tw-ring-color"]: accent }
245
+ }
246
+ )
247
+ ] }),
248
+ /* @__PURE__ */ jsxs("label", { className: "block mt-4 text-sm font-medium text-gray-700", children: [
249
+ "E-mail *",
250
+ /* @__PURE__ */ jsx(
251
+ "input",
252
+ {
253
+ type: "email",
254
+ required: true,
255
+ value: email,
256
+ onChange: (e) => setEmail(e.target.value),
257
+ placeholder: "voce@email.com",
258
+ className: "mt-1 w-full rounded-xl border border-gray-200 px-3 py-2.5 focus:outline-none focus:ring-2",
259
+ style: { ["--tw-ring-color"]: accent }
260
+ }
261
+ )
262
+ ] }),
263
+ /* @__PURE__ */ jsxs("fieldset", { className: "mt-4", children: [
264
+ /* @__PURE__ */ jsx("legend", { className: "text-sm font-medium text-gray-700", children: "Forma de pagamento" }),
265
+ /* @__PURE__ */ jsx("div", { className: "mt-2 grid grid-cols-2 gap-3", children: [
266
+ { id: "pix", label: "Pix", icon: QrCode },
267
+ { id: "card", label: "Cart\xE3o", icon: CreditCard }
268
+ ].map(({ id, label, icon: Icon }) => {
269
+ const active = method === id;
270
+ return /* @__PURE__ */ jsxs(
271
+ "button",
272
+ {
273
+ type: "button",
274
+ onClick: () => setMethod(id),
275
+ className: "flex items-center gap-2 rounded-xl border px-3 py-2.5 text-sm font-medium transition",
276
+ style: active ? { borderColor: accent, backgroundColor: `${accent}0f`, color: accent } : { borderColor: "#e5e7eb", color: "#374151" },
277
+ children: [
278
+ /* @__PURE__ */ jsx(Icon, { className: "w-4 h-4" }),
279
+ label
280
+ ]
281
+ },
282
+ id
283
+ );
284
+ }) })
285
+ ] }),
286
+ submitError && /* @__PURE__ */ jsx("p", { className: "mt-4 text-sm text-red-600 bg-red-50 border border-red-100 rounded-xl px-3 py-2", children: submitError }),
287
+ /* @__PURE__ */ jsx(
288
+ "button",
289
+ {
290
+ type: "submit",
291
+ disabled: !canSubmit,
292
+ className: "mt-6 inline-flex w-full items-center justify-center gap-2 rounded-xl px-4 py-3 font-semibold text-white disabled:opacity-50",
293
+ style: { backgroundColor: accent },
294
+ children: submitting ? /* @__PURE__ */ jsxs(Fragment, { children: [
295
+ /* @__PURE__ */ jsx(Loader2, { className: "w-4 h-4 animate-spin" }),
296
+ " Processando\u2026"
297
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
298
+ /* @__PURE__ */ jsx(Lock, { className: "w-4 h-4" }),
299
+ " Pagar ",
300
+ formatMoney(price, currency),
301
+ isSubscription && ` ${intervalLabel}`
302
+ ] })
303
+ }
304
+ ),
305
+ /* @__PURE__ */ jsx("p", { className: "mt-3 text-center text-xs text-gray-400", children: "Ambiente de demonstra\xE7\xE3o \u2014 pagamento simulado, sem cobran\xE7a real." })
306
+ ] })
307
+ ] }) });
308
+ };
309
+ CheckoutScreen.displayName = "CheckoutScreen";
310
+
311
+ // src/public/createPublicCheckoutPlugin.tsx
312
+ function createPublicCheckoutPlugin(options = {}) {
313
+ const basePath = options.basePath ?? "/checkout";
314
+ let provider;
315
+ if (options.dataProvider) {
316
+ provider = options.dataProvider;
317
+ } else if (options.tenantId) {
318
+ provider = createSupabaseCheckoutProvider({ tenantId: options.tenantId });
319
+ } else {
320
+ if (typeof console !== "undefined") {
321
+ console.warn(
322
+ "[plugin-courses/public] no `tenantId` and no `dataProvider` \u2014 falling back to the MOCK checkout provider. Pass `tenantId` to sell against the real DB, or inject a dataProvider."
323
+ );
324
+ }
325
+ provider = createMockCheckoutProvider();
326
+ }
327
+ const BoundCheckoutScreen = (props) => createElement(CheckoutScreen, { ...props, provider, accent: props.accent ?? options.accent });
328
+ BoundCheckoutScreen.displayName = "BoundCheckoutScreen";
329
+ const RouteScreen = (props) => {
330
+ const slug = props.slug ?? "";
331
+ return createElement(BoundCheckoutScreen, { slug, accent: options.accent });
332
+ };
333
+ RouteScreen.displayName = "CourseCheckoutScreen";
334
+ const manifest = {
335
+ id: "course-checkout",
336
+ name: "Checkout de curso",
337
+ icon: "ShoppingCart",
338
+ version: "0.1.0",
339
+ scope: options.scope ?? "universal",
340
+ verticalId: options.verticalId,
341
+ scaffolds: ["member", "website", "landing_page"],
342
+ defaultEnabled: true,
343
+ dependencies: [],
344
+ navigation: [],
345
+ routes: [{ path: `${basePath}/:slug`, component: RouteScreen, guard: "public" }],
346
+ widgets: []
347
+ };
348
+ return { manifest, dataProvider: provider, CheckoutScreen: BoundCheckoutScreen };
349
+ }
350
+
351
+ export { CheckoutScreen, createMockCheckoutProvider, createPublicCheckoutPlugin, createSupabaseCheckoutProvider };
352
+ //# sourceMappingURL=index.js.map
353
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/public/data/index.ts","../../src/public/data/supabase.ts","../../src/public/views/CheckoutScreen.tsx","../../src/public/createPublicCheckoutPlugin.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// ---------------------------------------------------------------------------\n// Data seam for the public course checkout surface.\n//\n// Mirrors plugins/plugin-agenda/src/public/data.ts: a small provider interface\n// with a mock default (dev without a backend) and a Supabase implementation\n// (data.supabase.ts) that reads the anon-visible catalog and calls the\n// course_place_order_simulated RPC. Kept dependency-light on purpose — the\n// public surface must NOT drag in @fayz-ai/admin or the courses admin graph.\n// ---------------------------------------------------------------------------\n\nimport type { Course, Offer } from '@fayz-ai/courses'\n\nexport type CheckoutPaymentMethod = 'pix' | 'card'\n\nexport interface PlaceOrderInput {\n email: string\n name?: string\n paymentMethod?: CheckoutPaymentMethod\n}\n\nexport interface PlaceOrderResult {\n orderId: string\n}\n\nexport interface PublicCheckoutDataProvider {\n /** Published course by slug (or id). Null when not found / not published. */\n getCourseBySlug(slug: string): Promise<Course | null>\n /** The offer pre-selected on the checkout (isDefault, else the first). */\n getDefaultOffer(courseId: string): Promise<Offer | null>\n /** Records the (simulated-paid) sale + enrollment. Returns the order id. */\n placeOrder(offerId: string, input: PlaceOrderInput): Promise<PlaceOrderResult>\n}\n\nexport interface MockCheckoutOptions {\n course?: Partial<Course>\n offer?: Partial<Offer>\n}\n\n/**\n * Loud mock: usable for a UI-only dev run with no backend. It fabricates a\n * single course + offer and a fake order id so the flow renders end-to-end.\n */\nexport function createMockCheckoutProvider(opts: MockCheckoutOptions = {}): PublicCheckoutDataProvider {\n const course: Course = {\n id: 'mock-course',\n slug: 'curso-demo',\n title: 'Curso Demo',\n subtitle: 'Um curso de demonstração',\n description: 'Conteúdo de exemplo para validar o fluxo de checkout.',\n thumbnailUrl: null,\n price: 197,\n currency: 'BRL',\n status: 'published',\n sortOrder: 0,\n createdAt: '2026-01-01T00:00:00.000Z',\n updatedAt: '2026-01-01T00:00:00.000Z',\n ...(opts.course as Partial<Course>),\n } as Course\n const offer: Offer = {\n id: 'mock-offer',\n courseId: course.id,\n name: course.title,\n price: course.price,\n currency: course.currency,\n kind: 'one_time',\n recurringInterval: null,\n isDefault: true,\n isOrderBump: false,\n sortOrder: 0,\n ...(opts.offer as Partial<Offer>),\n } as Offer\n\n let seq = 0\n return {\n async getCourseBySlug(slug: string) {\n return { ...course, slug: slug || course.slug }\n },\n async getDefaultOffer() {\n return offer\n },\n async placeOrder() {\n seq += 1\n if (typeof console !== 'undefined') {\n console.warn('[plugin-courses/public] MOCK placeOrder — no sale was recorded. Pass a tenantId for the real DB.')\n }\n return { orderId: `mock-order-${seq}` }\n },\n }\n}\n","// ---------------------------------------------------------------------------\n// Supabase implementation of PublicCheckoutDataProvider — anon-safe.\n//\n// Public checkout runs with the publishable (anon) key and (usually) no user\n// session, so it only touches objects designed for anon access:\n// - plg_courses_courses / plg_courses_offers (published catalog is anon-\n// readable via the storefront RLS — read here through the shared\n// @fayz-ai/courses Supabase provider)\n// - course_place_order_simulated (SECURITY DEFINER RPC: places the order and\n// simulates a confirmed payment — paid order + enrollment [+ subscription])\n// Shipped by packages/courses/migrations/0009_course_place_order_simulated.sql.\n// ---------------------------------------------------------------------------\n\nimport { getSupabaseClientOptional } from '@fayz-ai/core'\nimport { createSupabaseCoursesProvider } from '@fayz-ai/courses'\nimport type { PublicCheckoutDataProvider, PlaceOrderInput } from './index'\n\nexport interface SupabaseCheckoutOptions {\n /** Tenant this checkout sells for (RPC arg — required). */\n tenantId: string\n}\n\ninterface SupabaseLikeClient {\n rpc(fn: string, args: Record<string, unknown>): PromiseLike<{ data: unknown; error: { message: string } | null }>\n}\n\nfunction getClient(): SupabaseLikeClient {\n const client = getSupabaseClientOptional()\n if (!client) throw new Error('No Supabase client registered — call setGlobalSupabaseClient() at app startup.')\n return client as SupabaseLikeClient\n}\n\nexport function createSupabaseCheckoutProvider(options: SupabaseCheckoutOptions): PublicCheckoutDataProvider {\n const { tenantId } = options\n // Catalog reads reuse the courses provider (tenant scoping comes from the\n // host's setCoursesTenantResolver + storefront RLS on published rows).\n const courses = createSupabaseCoursesProvider()\n\n return {\n async getCourseBySlug(slug: string) {\n return courses.getCourse(slug)\n },\n\n async getDefaultOffer(courseId: string) {\n const offers = await courses.listOffers(courseId)\n return offers.find((o) => o.isDefault) ?? offers[0] ?? null\n },\n\n async placeOrder(offerId: string, input: PlaceOrderInput) {\n const email = input.email.trim()\n const { data, error } = await getClient().rpc('course_place_order_simulated', {\n p_tenant_id: tenantId,\n p_offer_id: offerId,\n p_customer_email: email,\n p_customer_name: input.name?.trim() || null,\n p_payment_method: input.paymentMethod ?? 'pix',\n })\n if (error) throw new Error(`[plugin-courses/public] placeOrder: ${error.message}`)\n const orderId = Array.isArray(data) ? (data[0] as string) : (data as string)\n return { orderId: String(orderId) }\n },\n }\n}\n","// ---------------------------------------------------------------------------\n// CheckoutScreen — the public \"comprar curso\" surface.\n//\n// Loads a published course + its default offer by slug, collects name/email/\n// payment method, and records a (simulated-paid) sale via the data provider.\n// Dependency-light: React + lucide + Tailwind only — no @fayz-ai/admin.\n// ---------------------------------------------------------------------------\n\nimport { useEffect, useMemo, useState, type FC, type FormEvent } from 'react'\nimport { CheckCircle2, CreditCard, Loader2, Lock, QrCode, ShieldCheck } from 'lucide-react'\nimport type { Course, Offer } from '@fayz-ai/courses'\nimport type { CheckoutPaymentMethod, PublicCheckoutDataProvider } from '../data'\n\nexport interface CheckoutScreenProps {\n slug: string\n provider: PublicCheckoutDataProvider\n /** Accent color (CSS) for buttons/highlights. Default emerald. */\n accent?: string\n /** Where \"acessar área de membros\" points after success. Default '#/'. */\n memberAreaHref?: string\n /** Fired after a successful order with the resulting order id + email. */\n onSuccess?: (info: { orderId: string; email: string }) => void\n}\n\nfunction formatMoney(value: number, currency = 'BRL', locale = 'pt-BR'): string {\n try {\n return new Intl.NumberFormat(locale, { style: 'currency', currency }).format(value)\n } catch {\n return `R$ ${value.toFixed(2)}`\n }\n}\n\nexport const CheckoutScreen: FC<CheckoutScreenProps> = ({\n slug,\n provider,\n accent = '#10b981',\n memberAreaHref = '#/',\n onSuccess,\n}) => {\n const [course, setCourse] = useState<Course | null>(null)\n const [offer, setOffer] = useState<Offer | null>(null)\n const [loading, setLoading] = useState(true)\n const [loadError, setLoadError] = useState<string | null>(null)\n\n const [name, setName] = useState('')\n const [email, setEmail] = useState('')\n const [method, setMethod] = useState<CheckoutPaymentMethod>('pix')\n\n const [submitting, setSubmitting] = useState(false)\n const [submitError, setSubmitError] = useState<string | null>(null)\n const [orderId, setOrderId] = useState<string | null>(null)\n\n useEffect(() => {\n let cancelled = false\n setLoading(true)\n setLoadError(null)\n ;(async () => {\n try {\n const c = await provider.getCourseBySlug(slug)\n if (!c) throw new Error('Curso não encontrado.')\n const o = await provider.getDefaultOffer(c.id)\n if (cancelled) return\n setCourse(c)\n setOffer(o)\n } catch (err) {\n if (!cancelled) setLoadError(err instanceof Error ? err.message : 'Falha ao carregar o curso.')\n } finally {\n if (!cancelled) setLoading(false)\n }\n })()\n return () => {\n cancelled = true\n }\n }, [slug, provider])\n\n const price = offer?.price ?? course?.price ?? 0\n const currency = offer?.currency ?? course?.currency ?? 'BRL'\n const isSubscription = offer?.kind === 'subscription'\n const intervalLabel = offer?.recurringInterval === 'year' ? '/ano' : '/mês'\n\n const canSubmit = useMemo(\n () => !!offer && !!email.trim() && /.+@.+\\..+/.test(email.trim()) && !submitting,\n [offer, email, submitting],\n )\n\n async function handleSubmit(e: FormEvent) {\n e.preventDefault()\n if (!offer || !canSubmit) return\n setSubmitting(true)\n setSubmitError(null)\n try {\n const { orderId: id } = await provider.placeOrder(offer.id, {\n email: email.trim(),\n name: name.trim() || undefined,\n paymentMethod: method,\n })\n setOrderId(id)\n onSuccess?.({ orderId: id, email: email.trim() })\n } catch (err) {\n setSubmitError(err instanceof Error ? err.message : 'Não foi possível concluir a compra. Tente novamente.')\n } finally {\n setSubmitting(false)\n }\n }\n\n if (loading) {\n return (\n <div className=\"min-h-screen flex items-center justify-center bg-gray-50\">\n <Loader2 className=\"w-6 h-6 animate-spin text-gray-400\" />\n </div>\n )\n }\n\n if (loadError || !course) {\n return (\n <div className=\"min-h-screen flex items-center justify-center bg-gray-50 p-6\">\n <div className=\"max-w-md text-center\">\n <p className=\"text-lg font-semibold text-gray-900\">Ops!</p>\n <p className=\"mt-2 text-gray-600\">{loadError ?? 'Curso indisponível.'}</p>\n <a href=\"#/\" className=\"mt-4 inline-block text-sm font-medium underline\" style={{ color: accent }}>\n Voltar\n </a>\n </div>\n </div>\n )\n }\n\n // Success state ----------------------------------------------------------\n if (orderId) {\n return (\n <div className=\"min-h-screen flex items-center justify-center bg-gray-50 p-6\">\n <div className=\"max-w-md w-full bg-white rounded-2xl shadow-sm border border-gray-100 p-8 text-center\">\n <div\n className=\"mx-auto w-14 h-14 rounded-full flex items-center justify-center\"\n style={{ backgroundColor: `${accent}1a` }}\n >\n <CheckCircle2 className=\"w-8 h-8\" style={{ color: accent }} />\n </div>\n <h1 className=\"mt-4 text-xl font-bold text-gray-900\">Compra confirmada!</h1>\n <p className=\"mt-2 text-gray-600\">\n Seu acesso a <strong>{course.title}</strong> já está liberado. Enviamos os detalhes para{' '}\n <strong>{email.trim()}</strong>.\n </p>\n <p className=\"mt-1 text-xs text-gray-400\">Pedido {orderId}</p>\n <a\n href={memberAreaHref}\n className=\"mt-6 inline-flex w-full items-center justify-center rounded-xl px-4 py-3 font-semibold text-white\"\n style={{ backgroundColor: accent }}\n >\n Acessar área de membros\n </a>\n </div>\n </div>\n )\n }\n\n // Checkout form ----------------------------------------------------------\n return (\n <div className=\"min-h-screen bg-gray-50 py-10 px-4\">\n <div className=\"max-w-3xl mx-auto grid gap-6 md:grid-cols-5\">\n {/* Order summary */}\n <aside className=\"md:col-span-2 bg-white rounded-2xl border border-gray-100 shadow-sm p-6 h-fit\">\n {course.thumbnailUrl && (\n <img\n src={course.thumbnailUrl}\n alt={course.title}\n className=\"w-full aspect-video object-cover rounded-xl mb-4\"\n />\n )}\n <h2 className=\"text-lg font-bold text-gray-900\">{course.title}</h2>\n {course.subtitle && <p className=\"mt-1 text-sm text-gray-500\">{course.subtitle}</p>}\n <div className=\"mt-4 border-t border-gray-100 pt-4\">\n <div className=\"flex items-baseline justify-between\">\n <span className=\"text-sm text-gray-500\">{offer?.name ?? 'Acesso ao curso'}</span>\n <span className=\"text-xl font-bold text-gray-900\">\n {formatMoney(price, currency)}\n {isSubscription && <span className=\"text-sm font-normal text-gray-400\"> {intervalLabel}</span>}\n </span>\n </div>\n </div>\n <div className=\"mt-4 flex items-center gap-2 text-xs text-gray-400\">\n <ShieldCheck className=\"w-4 h-4\" />\n Pagamento seguro · acesso imediato\n </div>\n </aside>\n\n {/* Payment form */}\n <form onSubmit={handleSubmit} className=\"md:col-span-3 bg-white rounded-2xl border border-gray-100 shadow-sm p-6\">\n <h1 className=\"text-xl font-bold text-gray-900\">Finalizar compra</h1>\n <p className=\"mt-1 text-sm text-gray-500\">Preencha seus dados para liberar o acesso.</p>\n\n <label className=\"block mt-6 text-sm font-medium text-gray-700\">\n Nome completo\n <input\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"Seu nome\"\n className=\"mt-1 w-full rounded-xl border border-gray-200 px-3 py-2.5 focus:outline-none focus:ring-2\"\n style={{ ['--tw-ring-color' as string]: accent }}\n />\n </label>\n\n <label className=\"block mt-4 text-sm font-medium text-gray-700\">\n E-mail *\n <input\n type=\"email\"\n required\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n placeholder=\"voce@email.com\"\n className=\"mt-1 w-full rounded-xl border border-gray-200 px-3 py-2.5 focus:outline-none focus:ring-2\"\n style={{ ['--tw-ring-color' as string]: accent }}\n />\n </label>\n\n <fieldset className=\"mt-4\">\n <legend className=\"text-sm font-medium text-gray-700\">Forma de pagamento</legend>\n <div className=\"mt-2 grid grid-cols-2 gap-3\">\n {(\n [\n { id: 'pix' as const, label: 'Pix', icon: QrCode },\n { id: 'card' as const, label: 'Cartão', icon: CreditCard },\n ]\n ).map(({ id, label, icon: Icon }) => {\n const active = method === id\n return (\n <button\n key={id}\n type=\"button\"\n onClick={() => setMethod(id)}\n className=\"flex items-center gap-2 rounded-xl border px-3 py-2.5 text-sm font-medium transition\"\n style={\n active\n ? { borderColor: accent, backgroundColor: `${accent}0f`, color: accent }\n : { borderColor: '#e5e7eb', color: '#374151' }\n }\n >\n <Icon className=\"w-4 h-4\" />\n {label}\n </button>\n )\n })}\n </div>\n </fieldset>\n\n {submitError && (\n <p className=\"mt-4 text-sm text-red-600 bg-red-50 border border-red-100 rounded-xl px-3 py-2\">\n {submitError}\n </p>\n )}\n\n <button\n type=\"submit\"\n disabled={!canSubmit}\n className=\"mt-6 inline-flex w-full items-center justify-center gap-2 rounded-xl px-4 py-3 font-semibold text-white disabled:opacity-50\"\n style={{ backgroundColor: accent }}\n >\n {submitting ? (\n <>\n <Loader2 className=\"w-4 h-4 animate-spin\" /> Processando…\n </>\n ) : (\n <>\n <Lock className=\"w-4 h-4\" /> Pagar {formatMoney(price, currency)}\n {isSubscription && ` ${intervalLabel}`}\n </>\n )}\n </button>\n <p className=\"mt-3 text-center text-xs text-gray-400\">\n Ambiente de demonstração — pagamento simulado, sem cobrança real.\n </p>\n </form>\n </div>\n </div>\n )\n}\n\nCheckoutScreen.displayName = 'CheckoutScreen'\n","// ---------------------------------------------------------------------------\n// createPublicCheckoutPlugin — the public \"landing → checkout\" seam for courses.\n//\n// Mirrors plugin-agenda/src/public/createPublicBookingPlugin: resolve a data\n// provider (injected → Supabase when a tenantId is set → loud mock), expose a\n// ready-to-mount CheckoutScreen + a PluginManifest with a public route. A host\n// app (e.g. course-members) either mounts the manifest route or renders\n// CheckoutScreen directly in its own router.\n// ---------------------------------------------------------------------------\n\nimport { createElement, type FC } from 'react'\nimport type { PluginManifest, PluginScope, VerticalId } from '@fayz-ai/core'\nimport { createMockCheckoutProvider, type PublicCheckoutDataProvider } from './data'\nimport { createSupabaseCheckoutProvider } from './data/supabase'\nimport { CheckoutScreen, type CheckoutScreenProps } from './views/CheckoutScreen'\n\nexport interface PublicCheckoutOptions {\n /** Mount path for the public checkout route. Default '/checkout'. Slug is appended as ':slug'. */\n basePath?: string\n /**\n * Tenant this checkout sells for. REQUIRED when a Supabase client is\n * configured (setGlobalSupabaseClient) — it scopes the catalog + the RPC.\n */\n tenantId?: string\n /** Accent color forwarded to CheckoutScreen. */\n accent?: string\n /** Inject a custom provider (overrides the mock/Supabase resolver). */\n dataProvider?: PublicCheckoutDataProvider\n scope?: PluginScope\n verticalId?: VerticalId\n}\n\nexport interface PublicCheckoutPlugin {\n manifest: PluginManifest\n dataProvider: PublicCheckoutDataProvider\n /** CheckoutScreen bound to the resolved provider — pass `slug` (+ optional onSuccess). */\n CheckoutScreen: FC<Omit<CheckoutScreenProps, 'provider'>>\n}\n\nexport function createPublicCheckoutPlugin(options: PublicCheckoutOptions = {}): PublicCheckoutPlugin {\n const basePath = options.basePath ?? '/checkout'\n\n // Resolution: explicit dataProvider → Supabase whenever a tenantId is declared\n // (app's global client if registered, else Fayz Cloud) → seeded mock (LOUD).\n let provider: PublicCheckoutDataProvider\n if (options.dataProvider) {\n provider = options.dataProvider\n } else if (options.tenantId) {\n provider = createSupabaseCheckoutProvider({ tenantId: options.tenantId })\n } else {\n if (typeof console !== 'undefined') {\n console.warn(\n '[plugin-courses/public] no `tenantId` and no `dataProvider` — falling back to the MOCK checkout provider. ' +\n 'Pass `tenantId` to sell against the real DB, or inject a dataProvider.',\n )\n }\n provider = createMockCheckoutProvider()\n }\n\n const BoundCheckoutScreen: FC<Omit<CheckoutScreenProps, 'provider'>> = (props) =>\n createElement(CheckoutScreen, { ...props, provider, accent: props.accent ?? options.accent })\n BoundCheckoutScreen.displayName = 'BoundCheckoutScreen'\n\n // Route component for manifest-driven hosts (slug injected via route params).\n // course-members renders BoundCheckoutScreen directly instead.\n const RouteScreen: FC<unknown> = (props) => {\n const slug = (props as { slug?: string }).slug ?? ''\n return createElement(BoundCheckoutScreen, { slug, accent: options.accent })\n }\n RouteScreen.displayName = 'CourseCheckoutScreen'\n\n const manifest: PluginManifest = {\n id: 'course-checkout',\n name: 'Checkout de curso',\n icon: 'ShoppingCart',\n version: '0.1.0',\n scope: options.scope ?? 'universal',\n verticalId: options.verticalId,\n scaffolds: ['member', 'website', 'landing_page'],\n defaultEnabled: true,\n dependencies: [],\n navigation: [],\n routes: [{ path: `${basePath}/:slug`, component: RouteScreen, guard: 'public' }],\n widgets: [],\n }\n\n return { manifest, dataProvider: provider, CheckoutScreen: BoundCheckoutScreen }\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { type FC } from 'react';
2
- import type { PublicCheckoutDataProvider } from './data';
2
+ import type { PublicCheckoutDataProvider } from '../data';
3
3
  export interface CheckoutScreenProps {
4
4
  slug: string;
5
5
  provider: PublicCheckoutDataProvider;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CheckoutScreen.d.ts","sourceRoot":"","sources":["../../../src/public/views/CheckoutScreen.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAgC,KAAK,EAAE,EAAkB,MAAM,OAAO,CAAA;AAG7E,OAAO,KAAK,EAAyB,0BAA0B,EAAE,MAAM,SAAS,CAAA;AAEhF,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,0BAA0B,CAAA;IACpC,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0EAA0E;IAC1E,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;CAC/D;AAUD,eAAO,MAAM,cAAc,EAAE,EAAE,CAAC,mBAAmB,CAoPlD,CAAA"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CourseEditorPage.d.ts","sourceRoot":"","sources":["../../src/views/CourseEditorPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAsF9B,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,EAAE,EAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,qBA0kBvD;yBA1kBe,gBAAgB"}