@fayz-ai/plugin-courses 0.8.1 → 0.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/CommerceUI.d.ts +31 -1
- package/dist/components/CommerceUI.d.ts.map +1 -1
- package/dist/index.cjs +1237 -301
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1240 -304
- package/dist/index.js.map +1 -1
- package/dist/locales/en.d.ts.map +1 -1
- package/dist/locales/pt-BR.d.ts.map +1 -1
- package/dist/pages/CourseEditorPage.d.ts.map +1 -1
- package/dist/pages/CoursesListPage.d.ts.map +1 -1
- package/dist/pages/SalesPage.d.ts.map +1 -1
- package/dist/public/CheckoutScreen.d.ts +17 -0
- package/dist/public/CheckoutScreen.d.ts.map +1 -0
- package/dist/public/createPublicCheckoutPlugin.d.ts +27 -0
- package/dist/public/createPublicCheckoutPlugin.d.ts.map +1 -0
- package/dist/public/data.d.ts +28 -0
- package/dist/public/data.d.ts.map +1 -0
- package/dist/public/data.supabase.d.ts +7 -0
- package/dist/public/data.supabase.d.ts.map +1 -0
- package/dist/public/index.d.ts +9 -0
- package/dist/public/index.d.ts.map +1 -0
- package/dist/views/CourseOrderDetailView.d.ts +10 -0
- package/dist/views/CourseOrderDetailView.d.ts.map +1 -0
- package/package.json +14 -5
- package/src/components/CommerceUI.tsx +78 -2
- package/src/index.ts +6 -3
- package/src/locales/en.ts +73 -12
- package/src/locales/pt-BR.ts +73 -12
- package/src/pages/CourseEditorPage.tsx +990 -177
- package/src/pages/CoursesListPage.tsx +20 -1
- package/src/pages/SalesPage.tsx +49 -20
- package/src/public/CheckoutScreen.tsx +279 -0
- package/src/public/createPublicCheckoutPlugin.tsx +88 -0
- package/src/public/data.supabase.ts +63 -0
- package/src/public/data.ts +89 -0
- package/src/public/index.ts +26 -0
- package/src/views/CourseOrderDetailView.tsx +132 -0
- package/dist/pages/MembersAreaPage.d.ts +0 -6
- package/dist/pages/MembersAreaPage.d.ts.map +0 -1
- package/src/pages/MembersAreaPage.tsx +0 -60
package/dist/locales/en.d.ts.map
CHANGED
|
@@ -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,
|
|
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 +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,
|
|
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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CourseEditorPage.d.ts","sourceRoot":"","sources":["../../src/pages/CourseEditorPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"CourseEditorPage.d.ts","sourceRoot":"","sources":["../../src/pages/CourseEditorPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAqF9B,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,EAAE,EAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,qBAyiBvD;yBAziBe,gBAAgB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CoursesListPage.d.ts","sourceRoot":"","sources":["../../src/pages/CoursesListPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAa9B,wBAAgB,eAAe,
|
|
1
|
+
{"version":3,"file":"CoursesListPage.d.ts","sourceRoot":"","sources":["../../src/pages/CoursesListPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAa9B,wBAAgB,eAAe,sBAyG9B;yBAzGe,eAAe"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SalesPage.d.ts","sourceRoot":"","sources":["../../src/pages/SalesPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"SalesPage.d.ts","sourceRoot":"","sources":["../../src/pages/SalesPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAmB9B,wBAAgB,SAAS,sBAiExB;yBAjEe,SAAS"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type FC } from 'react';
|
|
2
|
+
import type { PublicCheckoutDataProvider } from './data';
|
|
3
|
+
export interface CheckoutScreenProps {
|
|
4
|
+
slug: string;
|
|
5
|
+
provider: PublicCheckoutDataProvider;
|
|
6
|
+
/** Accent color (CSS) for buttons/highlights. Default emerald. */
|
|
7
|
+
accent?: string;
|
|
8
|
+
/** Where "acessar área de membros" points after success. Default '#/'. */
|
|
9
|
+
memberAreaHref?: string;
|
|
10
|
+
/** Fired after a successful order with the resulting order id + email. */
|
|
11
|
+
onSuccess?: (info: {
|
|
12
|
+
orderId: string;
|
|
13
|
+
email: string;
|
|
14
|
+
}) => void;
|
|
15
|
+
}
|
|
16
|
+
export declare const CheckoutScreen: FC<CheckoutScreenProps>;
|
|
17
|
+
//# sourceMappingURL=CheckoutScreen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CheckoutScreen.d.ts","sourceRoot":"","sources":["../../src/public/CheckoutScreen.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAgC,KAAK,EAAE,EAAkB,MAAM,OAAO,CAAA;AAG7E,OAAO,KAAK,EAAyB,0BAA0B,EAAE,MAAM,QAAQ,CAAA;AAE/E,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,27 @@
|
|
|
1
|
+
import { type FC } from 'react';
|
|
2
|
+
import type { PluginManifest, PluginScope, VerticalId } from '@fayz-ai/core';
|
|
3
|
+
import { type PublicCheckoutDataProvider } from './data';
|
|
4
|
+
import { type CheckoutScreenProps } from './CheckoutScreen';
|
|
5
|
+
export interface PublicCheckoutOptions {
|
|
6
|
+
/** Mount path for the public checkout route. Default '/checkout'. Slug is appended as ':slug'. */
|
|
7
|
+
basePath?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Tenant this checkout sells for. REQUIRED when a Supabase client is
|
|
10
|
+
* configured (setGlobalSupabaseClient) — it scopes the catalog + the RPC.
|
|
11
|
+
*/
|
|
12
|
+
tenantId?: string;
|
|
13
|
+
/** Accent color forwarded to CheckoutScreen. */
|
|
14
|
+
accent?: string;
|
|
15
|
+
/** Inject a custom provider (overrides the mock/Supabase resolver). */
|
|
16
|
+
dataProvider?: PublicCheckoutDataProvider;
|
|
17
|
+
scope?: PluginScope;
|
|
18
|
+
verticalId?: VerticalId;
|
|
19
|
+
}
|
|
20
|
+
export interface PublicCheckoutPlugin {
|
|
21
|
+
manifest: PluginManifest;
|
|
22
|
+
dataProvider: PublicCheckoutDataProvider;
|
|
23
|
+
/** CheckoutScreen bound to the resolved provider — pass `slug` (+ optional onSuccess). */
|
|
24
|
+
CheckoutScreen: FC<Omit<CheckoutScreenProps, 'provider'>>;
|
|
25
|
+
}
|
|
26
|
+
export declare function createPublicCheckoutPlugin(options?: PublicCheckoutOptions): PublicCheckoutPlugin;
|
|
27
|
+
//# sourceMappingURL=createPublicCheckoutPlugin.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Course, Offer } from '@fayz-ai/courses';
|
|
2
|
+
export type CheckoutPaymentMethod = 'pix' | 'card';
|
|
3
|
+
export interface PlaceOrderInput {
|
|
4
|
+
email: string;
|
|
5
|
+
name?: string;
|
|
6
|
+
paymentMethod?: CheckoutPaymentMethod;
|
|
7
|
+
}
|
|
8
|
+
export interface PlaceOrderResult {
|
|
9
|
+
orderId: string;
|
|
10
|
+
}
|
|
11
|
+
export interface PublicCheckoutDataProvider {
|
|
12
|
+
/** Published course by slug (or id). Null when not found / not published. */
|
|
13
|
+
getCourseBySlug(slug: string): Promise<Course | null>;
|
|
14
|
+
/** The offer pre-selected on the checkout (isDefault, else the first). */
|
|
15
|
+
getDefaultOffer(courseId: string): Promise<Offer | null>;
|
|
16
|
+
/** Records the (simulated-paid) sale + enrollment. Returns the order id. */
|
|
17
|
+
placeOrder(offerId: string, input: PlaceOrderInput): Promise<PlaceOrderResult>;
|
|
18
|
+
}
|
|
19
|
+
export interface MockCheckoutOptions {
|
|
20
|
+
course?: Partial<Course>;
|
|
21
|
+
offer?: Partial<Offer>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Loud mock: usable for a UI-only dev run with no backend. It fabricates a
|
|
25
|
+
* single course + offer and a fake order id so the flow renders end-to-end.
|
|
26
|
+
*/
|
|
27
|
+
export declare function createMockCheckoutProvider(opts?: MockCheckoutOptions): PublicCheckoutDataProvider;
|
|
28
|
+
//# sourceMappingURL=data.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../src/public/data.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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { PublicCheckoutDataProvider } from './data';
|
|
2
|
+
export interface SupabaseCheckoutOptions {
|
|
3
|
+
/** Tenant this checkout sells for (RPC arg — required). */
|
|
4
|
+
tenantId: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function createSupabaseCheckoutProvider(options: SupabaseCheckoutOptions): PublicCheckoutDataProvider;
|
|
7
|
+
//# sourceMappingURL=data.supabase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data.supabase.d.ts","sourceRoot":"","sources":["../../src/public/data.supabase.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,0BAA0B,EAAmB,MAAM,QAAQ,CAAA;AAEzE,MAAM,WAAW,uBAAuB;IACtC,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAA;CACjB;AAYD,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,uBAAuB,GAAG,0BAA0B,CA8B3G"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { createPublicCheckoutPlugin } from './createPublicCheckoutPlugin';
|
|
2
|
+
export type { PublicCheckoutOptions, PublicCheckoutPlugin } from './createPublicCheckoutPlugin';
|
|
3
|
+
export { CheckoutScreen } from './CheckoutScreen';
|
|
4
|
+
export type { CheckoutScreenProps } from './CheckoutScreen';
|
|
5
|
+
export { createMockCheckoutProvider } from './data';
|
|
6
|
+
export { createSupabaseCheckoutProvider } from './data.supabase';
|
|
7
|
+
export type { SupabaseCheckoutOptions } from './data.supabase';
|
|
8
|
+
export type { PublicCheckoutDataProvider, PlaceOrderInput, PlaceOrderResult, CheckoutPaymentMethod, MockCheckoutOptions, } from './data';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
/** Order detail (`/courses/sales/:id`). Loads the canonical order and renders the
|
|
3
|
+
* SHARED archetype OrderDetailView (@fayz-ai/saas) — same component the shop uses. */
|
|
4
|
+
export declare function CourseOrderDetailView({ id }: {
|
|
5
|
+
id?: string;
|
|
6
|
+
}): React.JSX.Element;
|
|
7
|
+
export declare namespace CourseOrderDetailView {
|
|
8
|
+
var displayName: string;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=CourseOrderDetailView.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CourseOrderDetailView.d.ts","sourceRoot":"","sources":["../../src/views/CourseOrderDetailView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AA+B9B;uFACuF;AACvF,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,EAAE,EAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,qBAiG5D;yBAjGe,qBAAqB"}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"fayz": {
|
|
4
4
|
"status": "preview"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.8.
|
|
6
|
+
"version": "0.8.4",
|
|
7
7
|
"description": "[experimental] Fayz SDK — courses admin management plugin",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"main": "./dist/index.cjs",
|
|
@@ -15,6 +15,12 @@
|
|
|
15
15
|
"types": "./dist/index.d.ts",
|
|
16
16
|
"import": "./dist/index.js",
|
|
17
17
|
"require": "./dist/index.cjs"
|
|
18
|
+
},
|
|
19
|
+
"./public": {
|
|
20
|
+
"source": "./src/public/index.ts",
|
|
21
|
+
"types": "./dist/public/index.d.ts",
|
|
22
|
+
"import": "./dist/public/index.js",
|
|
23
|
+
"require": "./dist/public/index.cjs"
|
|
18
24
|
}
|
|
19
25
|
},
|
|
20
26
|
"files": [
|
|
@@ -28,10 +34,13 @@
|
|
|
28
34
|
"dependencies": {
|
|
29
35
|
"lucide-react": "^0.400.0",
|
|
30
36
|
"@tanstack/react-table": "^8.20.0",
|
|
31
|
-
"@
|
|
32
|
-
"@
|
|
33
|
-
"@
|
|
34
|
-
"@fayz-ai/courses": "^0.8.
|
|
37
|
+
"@dnd-kit/core": "^6.3.1",
|
|
38
|
+
"@dnd-kit/sortable": "^10.0.0",
|
|
39
|
+
"@dnd-kit/utilities": "^3.2.2",
|
|
40
|
+
"@fayz-ai/courses": "^0.8.1",
|
|
41
|
+
"@fayz-ai/saas": "^0.8.4",
|
|
42
|
+
"@fayz-ai/core": "^0.8.4",
|
|
43
|
+
"@fayz-ai/ui": "^0.8.4"
|
|
35
44
|
},
|
|
36
45
|
"devDependencies": {
|
|
37
46
|
"@types/react": "^18.3.0",
|
|
@@ -47,6 +47,73 @@ export function TableEmpty({ children }: { children: React.ReactNode }) {
|
|
|
47
47
|
)
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Standard empty state — an icon chip, a title, an optional description and a
|
|
52
|
+
* call-to-action. Use anywhere a list/section can be legitimately empty so the
|
|
53
|
+
* screen never shows a bare dashed box.
|
|
54
|
+
*/
|
|
55
|
+
export function EmptyState({ icon, title, description, action }: {
|
|
56
|
+
icon?: React.ReactNode
|
|
57
|
+
title: string
|
|
58
|
+
description?: string
|
|
59
|
+
action?: React.ReactNode
|
|
60
|
+
}) {
|
|
61
|
+
return (
|
|
62
|
+
<div className="flex flex-col items-center justify-center rounded-xl border border-dashed border-border bg-card/40 px-6 py-12 text-center">
|
|
63
|
+
{icon && (
|
|
64
|
+
<div className="mb-3 flex h-11 w-11 items-center justify-center rounded-full bg-muted text-muted-foreground">
|
|
65
|
+
{icon}
|
|
66
|
+
</div>
|
|
67
|
+
)}
|
|
68
|
+
<p className="text-sm font-medium text-foreground">{title}</p>
|
|
69
|
+
{description && <p className="mt-1 max-w-sm text-sm text-muted-foreground">{description}</p>}
|
|
70
|
+
{action && <div className="mt-4">{action}</div>}
|
|
71
|
+
</div>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* A muted, low-emphasis informational callout. Use to set honest expectations
|
|
77
|
+
* (e.g. "this applies to a feature that isn't shipped yet") without dressing it
|
|
78
|
+
* up as an interactive control. Wrap key phrases in <strong> for emphasis.
|
|
79
|
+
*/
|
|
80
|
+
export function InfoNote({ icon, children, className }: {
|
|
81
|
+
icon?: React.ReactNode
|
|
82
|
+
children: React.ReactNode
|
|
83
|
+
className?: string
|
|
84
|
+
}) {
|
|
85
|
+
return (
|
|
86
|
+
<div className={'flex items-start gap-2.5 rounded-lg border border-border bg-muted/40 px-3.5 py-3 text-sm text-muted-foreground ' + (className ?? '')}>
|
|
87
|
+
{icon && <span className="mt-0.5 shrink-0 text-muted-foreground/80">{icon}</span>}
|
|
88
|
+
<div className="min-w-0 [&_strong]:font-medium [&_strong]:text-foreground">{children}</div>
|
|
89
|
+
</div>
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** A titled card section — the house pattern for grouping form fields. */
|
|
94
|
+
export function SectionCard({ title, description, actions, children, className }: {
|
|
95
|
+
title?: string
|
|
96
|
+
description?: string
|
|
97
|
+
actions?: React.ReactNode
|
|
98
|
+
children: React.ReactNode
|
|
99
|
+
className?: string
|
|
100
|
+
}) {
|
|
101
|
+
return (
|
|
102
|
+
<Card className={'p-5 md:p-6 ' + (className ?? '')}>
|
|
103
|
+
{(title || actions) && (
|
|
104
|
+
<div className="mb-4 flex items-start justify-between gap-4">
|
|
105
|
+
<div>
|
|
106
|
+
{title && <h2 className="text-sm font-semibold text-foreground">{title}</h2>}
|
|
107
|
+
{description && <p className="mt-0.5 text-sm text-muted-foreground">{description}</p>}
|
|
108
|
+
</div>
|
|
109
|
+
{actions}
|
|
110
|
+
</div>
|
|
111
|
+
)}
|
|
112
|
+
{children}
|
|
113
|
+
</Card>
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
|
|
50
117
|
export interface SimpleColumn<T> {
|
|
51
118
|
key: string
|
|
52
119
|
header: string
|
|
@@ -59,11 +126,12 @@ export interface SimpleColumn<T> {
|
|
|
59
126
|
* pages describe columns declaratively; this maps them to tanstack ColumnDef so
|
|
60
127
|
* we reuse DataTable's sorting/skeleton/empty chrome instead of a bespoke table.
|
|
61
128
|
*/
|
|
62
|
-
export function SimpleTable<T>({ columns, rows, empty, loading }: {
|
|
129
|
+
export function SimpleTable<T>({ columns, rows, empty, loading, onRowClick }: {
|
|
63
130
|
columns: SimpleColumn<T>[]
|
|
64
131
|
rows: T[]
|
|
65
132
|
empty: string
|
|
66
133
|
loading?: boolean
|
|
134
|
+
onRowClick?: (row: T) => void
|
|
67
135
|
}) {
|
|
68
136
|
const cols = React.useMemo<ColumnDef<T>[]>(
|
|
69
137
|
() => columns.map((c) => ({
|
|
@@ -73,5 +141,13 @@ export function SimpleTable<T>({ columns, rows, empty, loading }: {
|
|
|
73
141
|
})),
|
|
74
142
|
[columns],
|
|
75
143
|
)
|
|
76
|
-
return
|
|
144
|
+
return (
|
|
145
|
+
<DataTable
|
|
146
|
+
columns={cols}
|
|
147
|
+
data={rows}
|
|
148
|
+
emptyMessage={empty}
|
|
149
|
+
loading={loading}
|
|
150
|
+
onRowClick={onRowClick ? (row: T) => onRowClick(row) : undefined}
|
|
151
|
+
/>
|
|
152
|
+
)
|
|
77
153
|
}
|
package/src/index.ts
CHANGED
|
@@ -12,10 +12,10 @@ import { coursesLocales } from './locales'
|
|
|
12
12
|
import { CoursesListPage } from './pages/CoursesListPage'
|
|
13
13
|
import { CourseEditorPage } from './pages/CourseEditorPage'
|
|
14
14
|
import { SalesPage } from './pages/SalesPage'
|
|
15
|
+
import { CourseOrderDetailView } from './views/CourseOrderDetailView'
|
|
15
16
|
import { SubscriptionsPage } from './pages/SubscriptionsPage'
|
|
16
17
|
import { FinancialPage } from './pages/FinancialPage'
|
|
17
18
|
import { ReportsPage } from './pages/ReportsPage'
|
|
18
|
-
import { MembersAreaPage } from './pages/MembersAreaPage'
|
|
19
19
|
import { CoursesSettingsTab } from './settings/CoursesSettings'
|
|
20
20
|
import { createCoursesDashboardWidgets } from './dashboardWidgets'
|
|
21
21
|
import { createStripeConnector } from './connectors/stripe'
|
|
@@ -85,7 +85,9 @@ export function createCoursesPlugin(options?: CoursesPluginOptions): PluginManif
|
|
|
85
85
|
const labels = options?.labels ?? {}
|
|
86
86
|
const navigation = [
|
|
87
87
|
{ section, position: base, label: options?.navLabel ?? 'Courses', route: '/courses', icon: 'BookOpen', permission: read('courses') },
|
|
88
|
-
|
|
88
|
+
// The members area is no longer a standalone nav entry — it lives inside each
|
|
89
|
+
// course's record (CourseEditorPage → "Alunos" tab). modules.membersArea is
|
|
90
|
+
// kept accepted-but-inert for backward compat with existing manifests.
|
|
89
91
|
...(modules.sales ? [{ section, position: base + 2, label: labels.sales ?? 'Sales', route: '/courses/sales', icon: 'TrendingUp', permission: read('courses.sales') }] : []),
|
|
90
92
|
...(modules.subscriptions ? [{ section, position: base + 3, label: labels.subscriptions ?? 'Subscriptions', route: '/courses/subscriptions', icon: 'Repeat', permission: read('courses.sales') }] : []),
|
|
91
93
|
...(modules.financial ? [{ section, position: base + 4, label: labels.financial ?? 'Financial', route: '/courses/financial', icon: 'CreditCard', permission: read('courses.finance') }] : []),
|
|
@@ -96,8 +98,9 @@ export function createCoursesPlugin(options?: CoursesPluginOptions): PluginManif
|
|
|
96
98
|
// Static segments outscore the `:id` param in the saas router, so these
|
|
97
99
|
// resolve before the editor route (AdminShell sorts by routeScore).
|
|
98
100
|
{ path: '/courses', component: asComp(CoursesListPage), permission: read('courses') },
|
|
99
|
-
...(modules.membersArea ? [{ path: '/courses/members', component: asComp(MembersAreaPage), permission: read('courses') }] : []),
|
|
100
101
|
...(modules.sales ? [{ path: '/courses/sales', component: asComp(SalesPage), permission: read('courses.sales') }] : []),
|
|
102
|
+
// Order detail — static `/courses/sales/:id` outscores `/courses/:id`.
|
|
103
|
+
...(modules.sales ? [{ path: '/courses/sales/:id', component: asComp(CourseOrderDetailView), permission: read('courses.sales') }] : []),
|
|
101
104
|
...(modules.subscriptions ? [{ path: '/courses/subscriptions', component: asComp(SubscriptionsPage), permission: read('courses.sales') }] : []),
|
|
102
105
|
...(modules.financial ? [{ path: '/courses/financial', component: asComp(FinancialPage), permission: read('courses.finance') }] : []),
|
|
103
106
|
...(modules.reports ? [{ path: '/courses/reports', component: asComp(ReportsPage), permission: read('courses.sales') }] : []),
|
package/src/locales/en.ts
CHANGED
|
@@ -19,14 +19,75 @@ export const en: Record<string, string> = {
|
|
|
19
19
|
'courses.editor.fieldStatus': 'Status',
|
|
20
20
|
'courses.editor.save': 'Save',
|
|
21
21
|
'courses.editor.saved': 'Course saved',
|
|
22
|
+
'courses.editor.saveFailed': 'Could not save the course',
|
|
23
|
+
'courses.editor.unsaved': 'Unsaved changes',
|
|
24
|
+
// General tab
|
|
25
|
+
'courses.editor.sectionBasics': 'Basics',
|
|
26
|
+
'courses.editor.sectionThumbnail': 'Cover image',
|
|
27
|
+
'courses.editor.sectionPricing': 'Pricing & status',
|
|
28
|
+
'courses.editor.descriptionPlaceholder': 'What will students learn?',
|
|
29
|
+
'courses.editor.fieldDelivery': 'Format',
|
|
30
|
+
'courses.editor.deliveryHint': 'How this course is delivered.',
|
|
31
|
+
'courses.editor.delivery.online': 'Online',
|
|
32
|
+
'courses.editor.delivery.in_person': 'In person',
|
|
33
|
+
'courses.editor.delivery.hybrid': 'Hybrid',
|
|
34
|
+
'courses.editor.thumbnailHint': 'Shown on the storefront and in listings. 1280×720 recommended.',
|
|
35
|
+
'courses.editor.thumbnailUpload': 'Upload image',
|
|
36
|
+
'courses.editor.thumbnailUrl': 'Or paste an image URL',
|
|
37
|
+
'courses.editor.thumbnailEmpty': 'No image',
|
|
38
|
+
'courses.editor.thumbnailUploaded': 'Image uploaded — remember to save.',
|
|
39
|
+
'courses.editor.uploading': 'Uploading…',
|
|
40
|
+
'courses.editor.uploadFailed': 'Could not upload the image',
|
|
41
|
+
'courses.editor.uploadUnavailable': 'Upload unavailable — paste an image URL instead.',
|
|
42
|
+
// Curriculum tab
|
|
22
43
|
'courses.editor.addModule': 'Add module',
|
|
23
44
|
'courses.editor.moduleTitle': 'Module title',
|
|
45
|
+
'courses.editor.moduleAdded': 'Module added',
|
|
24
46
|
'courses.editor.addLesson': 'Add lesson',
|
|
25
47
|
'courses.editor.lessonTitle': 'Lesson title',
|
|
26
48
|
'courses.editor.lessonVideo': 'Video URL (embed)',
|
|
27
49
|
'courses.editor.lessonDuration': 'Duration (min)',
|
|
28
50
|
'courses.editor.deleteModule': 'Delete module',
|
|
29
|
-
'courses.editor.deleteLesson': 'Remove',
|
|
51
|
+
'courses.editor.deleteLesson': 'Remove lesson',
|
|
52
|
+
'courses.editor.deleted': 'Removed',
|
|
53
|
+
'courses.editor.autosaved': 'Changes are saved automatically.',
|
|
54
|
+
'courses.editor.modulesEmpty': 'No modules yet',
|
|
55
|
+
'courses.editor.modulesEmptyHint': 'Create a module to start organizing your lessons.',
|
|
56
|
+
'courses.editor.lessonsEmpty': 'No lessons in this module yet.',
|
|
57
|
+
'courses.editor.deleteModuleConfirm': 'Delete this module?',
|
|
58
|
+
'courses.editor.deleteModuleConfirmBody': 'Its lessons will be removed too. This cannot be undone.',
|
|
59
|
+
'courses.editor.deleteLessonConfirm': 'Remove this lesson?',
|
|
60
|
+
'courses.editor.deleteLessonConfirmBody': 'This cannot be undone.',
|
|
61
|
+
'courses.editor.confirmDelete': 'Delete',
|
|
62
|
+
'courses.editor.cancel': 'Cancel',
|
|
63
|
+
// Curriculum tree builder
|
|
64
|
+
'courses.editor.newModule': 'New module',
|
|
65
|
+
'courses.editor.firstModule': 'Create first module',
|
|
66
|
+
'courses.editor.newLesson': 'New lesson',
|
|
67
|
+
'courses.editor.firstLesson': 'Add the first lesson',
|
|
68
|
+
'courses.editor.moduleLabel': 'module',
|
|
69
|
+
'courses.editor.moduleLabelPlural': 'modules',
|
|
70
|
+
'courses.editor.lessonUnitOne': 'lesson',
|
|
71
|
+
'courses.editor.lessonUnitOther': 'lessons',
|
|
72
|
+
'courses.editor.noLessons': 'No lessons',
|
|
73
|
+
'courses.editor.minShort': 'min',
|
|
74
|
+
'courses.editor.hourShort': 'h',
|
|
75
|
+
'courses.editor.dragToReorder': 'Drag to reorder',
|
|
76
|
+
'courses.editor.expand': 'Expand',
|
|
77
|
+
'courses.editor.collapse': 'Collapse',
|
|
78
|
+
'courses.editor.editLesson': 'Edit lesson',
|
|
79
|
+
'courses.editor.addVideo': 'Add video',
|
|
80
|
+
'courses.editor.videoSet': 'Video',
|
|
81
|
+
'courses.editor.videoUrlLabel': 'Video URL',
|
|
82
|
+
'courses.editor.videoUrlPlaceholder': 'Paste the video link (YouTube, Vimeo…)',
|
|
83
|
+
'courses.editor.reorderFailed': 'Could not reorder',
|
|
84
|
+
// Members area
|
|
85
|
+
'courses.editor.membersHelp': 'Buyers are enrolled automatically through orders and get access to all of this course’s content.',
|
|
86
|
+
'courses.editor.enrolledStudents': 'Enrolled students',
|
|
87
|
+
'courses.editor.membersEmpty': 'No students yet',
|
|
88
|
+
'courses.editor.membersEmptyHint': 'Buyers are enrolled here automatically after a paid order.',
|
|
89
|
+
'courses.editor.accessUnlockedBy': 'Access is unlocked by',
|
|
90
|
+
'courses.editor.membersNote': 'Buying any offer grants full access to this course’s content.',
|
|
30
91
|
'courses.editor.untitledCourse': 'Untitled course',
|
|
31
92
|
'courses.editor.untitledModule': 'New module',
|
|
32
93
|
'courses.editor.untitledLesson': 'New lesson',
|
|
@@ -35,29 +96,29 @@ export const en: Record<string, string> = {
|
|
|
35
96
|
'courses.editor.tab.curriculum': 'Curriculum',
|
|
36
97
|
'courses.editor.tab.offers': 'Offers',
|
|
37
98
|
'courses.editor.tab.members': 'Members area',
|
|
38
|
-
|
|
39
|
-
'courses.editor.tab.checkout': 'Checkout',
|
|
40
|
-
'courses.editor.tab.links': 'Links',
|
|
41
|
-
'courses.editor.membersHelp': 'Buyers of this course are added to its members area automatically. Access groups per offer:',
|
|
42
|
-
'courses.editor.defaultGroup': 'Default group',
|
|
43
|
-
'courses.editor.settingsHelp': 'Payment methods offered at checkout (Stripe).',
|
|
99
|
+
// Payment methods (used by the Courses plugin settings surface)
|
|
44
100
|
'courses.editor.payCard': 'Credit card (up to 12x)',
|
|
45
101
|
'courses.editor.payPix': 'Pix',
|
|
46
102
|
'courses.editor.payBoleto': 'Boleto',
|
|
47
|
-
'courses.editor.settingsNote': 'Method availability depends on Stripe in your region; wired in Stage 3.',
|
|
48
|
-
'courses.editor.checkoutSoon': 'Custom checkout builder comes in a later milestone. A default checkout is generated per offer.',
|
|
49
|
-
'courses.editor.linksHelp': 'Shareable checkout links, one per offer.',
|
|
50
103
|
// Offers
|
|
51
|
-
'courses.offers.help': 'Price points
|
|
104
|
+
'courses.offers.help': 'Price points for this course. Sales run through orders and the Financeiro module.',
|
|
52
105
|
'courses.offers.name': 'Offer name',
|
|
53
106
|
'courses.offers.price': 'Price',
|
|
107
|
+
'courses.offers.kind': 'Type',
|
|
54
108
|
'courses.offers.oneTime': 'One-time',
|
|
55
109
|
'courses.offers.subscription': 'Subscription',
|
|
110
|
+
'courses.offers.interval': 'Billing interval',
|
|
56
111
|
'courses.offers.default': 'Default',
|
|
57
|
-
'courses.offers.bump': '
|
|
112
|
+
'courses.offers.bump': 'Order bump',
|
|
58
113
|
'courses.offers.remove': 'Remove',
|
|
59
114
|
'courses.offers.add': 'Add offer',
|
|
115
|
+
'courses.offers.added': 'Offer added',
|
|
116
|
+
'courses.offers.removed': 'Offer removed',
|
|
60
117
|
'courses.offers.newName': 'New offer',
|
|
118
|
+
'courses.offers.empty': 'No offers yet',
|
|
119
|
+
'courses.offers.emptyHint': 'Create an offer to sell this course.',
|
|
120
|
+
'courses.offers.firstOffer': 'Create first offer',
|
|
121
|
+
'courses.offers.checkoutFlagsNote': 'Apply once the public checkout ships (coming soon).',
|
|
61
122
|
// Sales
|
|
62
123
|
'courses.sales.title': 'Sales',
|
|
63
124
|
'courses.sales.subtitle': 'Every transaction across your courses.',
|
package/src/locales/pt-BR.ts
CHANGED
|
@@ -19,14 +19,75 @@ export const ptBR: Record<string, string> = {
|
|
|
19
19
|
'courses.editor.fieldStatus': 'Status',
|
|
20
20
|
'courses.editor.save': 'Salvar',
|
|
21
21
|
'courses.editor.saved': 'Curso salvo',
|
|
22
|
+
'courses.editor.saveFailed': 'Não foi possível salvar o curso',
|
|
23
|
+
'courses.editor.unsaved': 'Alterações não salvas',
|
|
24
|
+
// Geral tab
|
|
25
|
+
'courses.editor.sectionBasics': 'Informações básicas',
|
|
26
|
+
'courses.editor.sectionThumbnail': 'Imagem de capa',
|
|
27
|
+
'courses.editor.sectionPricing': 'Preço e publicação',
|
|
28
|
+
'courses.editor.descriptionPlaceholder': 'O que o aluno vai aprender?',
|
|
29
|
+
'courses.editor.fieldDelivery': 'Formato',
|
|
30
|
+
'courses.editor.deliveryHint': 'Como o curso é entregue.',
|
|
31
|
+
'courses.editor.delivery.online': 'Online',
|
|
32
|
+
'courses.editor.delivery.in_person': 'Presencial',
|
|
33
|
+
'courses.editor.delivery.hybrid': 'Híbrido',
|
|
34
|
+
'courses.editor.thumbnailHint': 'Aparece na loja e nas listagens. Recomendado 1280×720.',
|
|
35
|
+
'courses.editor.thumbnailUpload': 'Enviar imagem',
|
|
36
|
+
'courses.editor.thumbnailUrl': 'Ou cole uma URL de imagem',
|
|
37
|
+
'courses.editor.thumbnailEmpty': 'Sem imagem',
|
|
38
|
+
'courses.editor.thumbnailUploaded': 'Imagem enviada — lembre de salvar.',
|
|
39
|
+
'courses.editor.uploading': 'Enviando…',
|
|
40
|
+
'courses.editor.uploadFailed': 'Não foi possível enviar a imagem',
|
|
41
|
+
'courses.editor.uploadUnavailable': 'Upload indisponível — cole uma URL de imagem.',
|
|
42
|
+
// Conteúdo tab
|
|
22
43
|
'courses.editor.addModule': 'Adicionar módulo',
|
|
23
44
|
'courses.editor.moduleTitle': 'Título do módulo',
|
|
45
|
+
'courses.editor.moduleAdded': 'Módulo adicionado',
|
|
24
46
|
'courses.editor.addLesson': 'Adicionar aula',
|
|
25
47
|
'courses.editor.lessonTitle': 'Título da aula',
|
|
26
48
|
'courses.editor.lessonVideo': 'URL do vídeo (embed)',
|
|
27
49
|
'courses.editor.lessonDuration': 'Duração (min)',
|
|
28
50
|
'courses.editor.deleteModule': 'Excluir módulo',
|
|
29
|
-
'courses.editor.deleteLesson': 'Remover',
|
|
51
|
+
'courses.editor.deleteLesson': 'Remover aula',
|
|
52
|
+
'courses.editor.deleted': 'Removido',
|
|
53
|
+
'courses.editor.autosaved': 'As alterações são salvas automaticamente.',
|
|
54
|
+
'courses.editor.modulesEmpty': 'Nenhum módulo ainda',
|
|
55
|
+
'courses.editor.modulesEmptyHint': 'Crie um módulo para começar a organizar as aulas.',
|
|
56
|
+
'courses.editor.lessonsEmpty': 'Nenhuma aula neste módulo ainda.',
|
|
57
|
+
'courses.editor.deleteModuleConfirm': 'Excluir este módulo?',
|
|
58
|
+
'courses.editor.deleteModuleConfirmBody': 'As aulas deste módulo também serão removidas. Isso não pode ser desfeito.',
|
|
59
|
+
'courses.editor.deleteLessonConfirm': 'Remover esta aula?',
|
|
60
|
+
'courses.editor.deleteLessonConfirmBody': 'Isso não pode ser desfeito.',
|
|
61
|
+
'courses.editor.confirmDelete': 'Excluir',
|
|
62
|
+
'courses.editor.cancel': 'Cancelar',
|
|
63
|
+
// Curriculum tree builder
|
|
64
|
+
'courses.editor.newModule': 'Novo módulo',
|
|
65
|
+
'courses.editor.firstModule': 'Criar primeiro módulo',
|
|
66
|
+
'courses.editor.newLesson': 'Nova aula',
|
|
67
|
+
'courses.editor.firstLesson': 'Adicione a primeira aula',
|
|
68
|
+
'courses.editor.moduleLabel': 'módulo',
|
|
69
|
+
'courses.editor.moduleLabelPlural': 'módulos',
|
|
70
|
+
'courses.editor.lessonUnitOne': 'aula',
|
|
71
|
+
'courses.editor.lessonUnitOther': 'aulas',
|
|
72
|
+
'courses.editor.noLessons': 'Sem aulas',
|
|
73
|
+
'courses.editor.minShort': 'min',
|
|
74
|
+
'courses.editor.hourShort': 'h',
|
|
75
|
+
'courses.editor.dragToReorder': 'Arraste para reordenar',
|
|
76
|
+
'courses.editor.expand': 'Expandir',
|
|
77
|
+
'courses.editor.collapse': 'Recolher',
|
|
78
|
+
'courses.editor.editLesson': 'Editar aula',
|
|
79
|
+
'courses.editor.addVideo': 'Adicionar vídeo',
|
|
80
|
+
'courses.editor.videoSet': 'Vídeo',
|
|
81
|
+
'courses.editor.videoUrlLabel': 'URL do vídeo',
|
|
82
|
+
'courses.editor.videoUrlPlaceholder': 'Cole o link do vídeo (YouTube, Vimeo…)',
|
|
83
|
+
'courses.editor.reorderFailed': 'Não foi possível reordenar',
|
|
84
|
+
// Área de membros
|
|
85
|
+
'courses.editor.membersHelp': 'Quem compra este curso é matriculado automaticamente pelos pedidos e ganha acesso a todo o conteúdo.',
|
|
86
|
+
'courses.editor.enrolledStudents': 'Alunos matriculados',
|
|
87
|
+
'courses.editor.membersEmpty': 'Nenhum aluno ainda',
|
|
88
|
+
'courses.editor.membersEmptyHint': 'Os compradores são matriculados aqui automaticamente após um pedido pago.',
|
|
89
|
+
'courses.editor.accessUnlockedBy': 'Acesso liberado por',
|
|
90
|
+
'courses.editor.membersNote': 'Comprar qualquer oferta libera o acesso completo ao conteúdo deste curso.',
|
|
30
91
|
'courses.editor.untitledCourse': 'Curso sem título',
|
|
31
92
|
'courses.editor.untitledModule': 'Novo módulo',
|
|
32
93
|
'courses.editor.untitledLesson': 'Nova aula',
|
|
@@ -35,29 +96,29 @@ export const ptBR: Record<string, string> = {
|
|
|
35
96
|
'courses.editor.tab.curriculum': 'Conteúdo',
|
|
36
97
|
'courses.editor.tab.offers': 'Ofertas',
|
|
37
98
|
'courses.editor.tab.members': 'Área de membros',
|
|
38
|
-
|
|
39
|
-
'courses.editor.tab.checkout': 'Checkout',
|
|
40
|
-
'courses.editor.tab.links': 'Links',
|
|
41
|
-
'courses.editor.membersHelp': 'Quem compra este curso entra automaticamente na área de membros. Grupos de acesso por oferta:',
|
|
42
|
-
'courses.editor.defaultGroup': 'Grupo padrão',
|
|
43
|
-
'courses.editor.settingsHelp': 'Métodos de pagamento no checkout (Stripe).',
|
|
99
|
+
// Métodos de pagamento (usados na tela de configurações do plugin de Cursos)
|
|
44
100
|
'courses.editor.payCard': 'Cartão de crédito (até 12x)',
|
|
45
101
|
'courses.editor.payPix': 'Pix',
|
|
46
102
|
'courses.editor.payBoleto': 'Boleto',
|
|
47
|
-
'courses.editor.settingsNote': 'A disponibilidade de cada método depende do Stripe na sua região; ligado na Etapa 3.',
|
|
48
|
-
'courses.editor.checkoutSoon': 'O construtor de checkout chega numa etapa futura. Um checkout padrão é gerado por oferta.',
|
|
49
|
-
'courses.editor.linksHelp': 'Links de checkout compartilháveis, um por oferta.',
|
|
50
103
|
// Offers
|
|
51
|
-
'courses.offers.help': 'Preços
|
|
104
|
+
'courses.offers.help': 'Preços deste curso. As vendas acontecem pelos pedidos e pelo Financeiro.',
|
|
52
105
|
'courses.offers.name': 'Nome da oferta',
|
|
53
106
|
'courses.offers.price': 'Preço',
|
|
107
|
+
'courses.offers.kind': 'Tipo',
|
|
54
108
|
'courses.offers.oneTime': 'Único',
|
|
55
109
|
'courses.offers.subscription': 'Assinatura',
|
|
110
|
+
'courses.offers.interval': 'Intervalo de cobrança',
|
|
56
111
|
'courses.offers.default': 'Padrão',
|
|
57
|
-
'courses.offers.bump': '
|
|
112
|
+
'courses.offers.bump': 'Order bump',
|
|
58
113
|
'courses.offers.remove': 'Remover',
|
|
59
114
|
'courses.offers.add': 'Adicionar oferta',
|
|
115
|
+
'courses.offers.added': 'Oferta adicionada',
|
|
116
|
+
'courses.offers.removed': 'Oferta removida',
|
|
60
117
|
'courses.offers.newName': 'Nova oferta',
|
|
118
|
+
'courses.offers.empty': 'Nenhuma oferta ainda',
|
|
119
|
+
'courses.offers.emptyHint': 'Crie uma oferta para vender este curso.',
|
|
120
|
+
'courses.offers.firstOffer': 'Criar primeira oferta',
|
|
121
|
+
'courses.offers.checkoutFlagsNote': 'Aplicam-se quando o checkout público entrar no ar (em breve).',
|
|
61
122
|
// Sales
|
|
62
123
|
'courses.sales.title': 'Vendas',
|
|
63
124
|
'courses.sales.subtitle': 'Todas as transações dos seus cursos.',
|