@fayz-ai/plugin-courses 0.2.2 → 0.2.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/README.md +2 -0
- package/dist/components/CommerceUI.d.ts +39 -0
- package/dist/components/CommerceUI.d.ts.map +1 -0
- package/dist/connectors/stripe.d.ts +3 -0
- package/dist/connectors/stripe.d.ts.map +1 -0
- package/dist/dashboardWidgets.d.ts +3 -0
- package/dist/dashboardWidgets.d.ts.map +1 -0
- package/dist/index.cjs +735 -54
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +738 -57
- package/dist/index.js.map +1 -1
- package/dist/lib/format.d.ts +4 -0
- package/dist/lib/format.d.ts.map +1 -0
- 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/FinancialPage.d.ts +6 -0
- package/dist/pages/FinancialPage.d.ts.map +1 -0
- package/dist/pages/MembersAreaPage.d.ts +6 -0
- package/dist/pages/MembersAreaPage.d.ts.map +1 -0
- package/dist/pages/ReportsPage.d.ts +6 -0
- package/dist/pages/ReportsPage.d.ts.map +1 -0
- package/dist/pages/SalesPage.d.ts +6 -0
- package/dist/pages/SalesPage.d.ts.map +1 -0
- package/dist/pages/SubscriptionsPage.d.ts +6 -0
- package/dist/pages/SubscriptionsPage.d.ts.map +1 -0
- package/dist/settings/CoursesSettings.d.ts +6 -0
- package/dist/settings/CoursesSettings.d.ts.map +1 -0
- package/package.json +6 -4
- package/src/components/CommerceUI.tsx +77 -0
- package/src/connectors/stripe.ts +34 -0
- package/src/dashboardWidgets.tsx +51 -0
- package/src/index.ts +51 -12
- package/src/lib/format.ts +22 -0
- package/src/locales/en.ts +109 -0
- package/src/locales/pt-BR.ts +109 -0
- package/src/pages/CourseEditorPage.tsx +133 -35
- package/src/pages/FinancialPage.tsx +76 -0
- package/src/pages/MembersAreaPage.tsx +60 -0
- package/src/pages/ReportsPage.tsx +59 -0
- package/src/pages/SalesPage.tsx +57 -0
- package/src/pages/SubscriptionsPage.tsx +56 -0
- package/src/settings/CoursesSettings.tsx +55 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ConnectorDefinition } from '@fayz-ai/core'
|
|
2
|
+
import { getCoursesProvider } from '@fayz-ai/courses'
|
|
3
|
+
|
|
4
|
+
// Stripe Connect modeled as a connector so it surfaces in the plugin's unified
|
|
5
|
+
// "Integrations" settings tab (same pattern as Financial Settings → Integrações),
|
|
6
|
+
// instead of a bespoke block in the General tab. The host plugin is 'courses'.
|
|
7
|
+
export function createStripeConnector(): ConnectorDefinition {
|
|
8
|
+
return {
|
|
9
|
+
id: 'stripe-connect',
|
|
10
|
+
hostPluginId: 'courses',
|
|
11
|
+
name: 'Stripe',
|
|
12
|
+
description: 'Receba os pagamentos dos alunos; a taxa da plataforma é aplicada automaticamente.',
|
|
13
|
+
icon: 'CreditCard',
|
|
14
|
+
authKind: 'oauth',
|
|
15
|
+
async getStatus() {
|
|
16
|
+
try {
|
|
17
|
+
const account = await getCoursesProvider().getCreatorAccount()
|
|
18
|
+
const fee = (account.platformFeeBps / 100).toFixed(2)
|
|
19
|
+
return {
|
|
20
|
+
connected: !!account.stripeAccountId && account.chargesEnabled,
|
|
21
|
+
detail: `Taxa da plataforma: ${fee}%`,
|
|
22
|
+
}
|
|
23
|
+
} catch {
|
|
24
|
+
return { connected: false }
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
async startOAuth(_redirectTo?: string): Promise<string> {
|
|
28
|
+
// Stage 3 wires this to the `courses-connect-onboard` edge function and
|
|
29
|
+
// returns the Stripe account-link URL to redirect to. Until then, surface
|
|
30
|
+
// a clear message (ConnectorsHub toasts a thrown error's message).
|
|
31
|
+
throw new Error('O onboarding do Stripe Connect é ligado na Etapa 3.')
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import type { DashboardWidgetDef } from '@fayz-ai/core'
|
|
3
|
+
import { useTranslation } from '@fayz-ai/core'
|
|
4
|
+
import { KpiCard, defineKpiWidget } from '@fayz-ai/ui'
|
|
5
|
+
import { getCoursesProvider, type FinancialSummary } from '@fayz-ai/courses'
|
|
6
|
+
import { formatMoney, formatPercent } from './lib/format'
|
|
7
|
+
|
|
8
|
+
// Course KPIs contributed to the central admin Dashboard (rendered by
|
|
9
|
+
// @fayz-ai/plugin-dashboard's DashboardCanvas, which auto-collects every
|
|
10
|
+
// plugin's dashboardWidgets for surface 'home'). Mirrors Kiwify's dashboard.
|
|
11
|
+
|
|
12
|
+
function useSummary(): FinancialSummary | null {
|
|
13
|
+
const [summary, setSummary] = React.useState<FinancialSummary | null>(null)
|
|
14
|
+
React.useEffect(() => {
|
|
15
|
+
void getCoursesProvider().getFinancialSummary().then(setSummary)
|
|
16
|
+
}, [])
|
|
17
|
+
return summary
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function RevenueKpi() {
|
|
21
|
+
const t = useTranslation()
|
|
22
|
+
const s = useSummary()
|
|
23
|
+
return <KpiCard label={t('courses.kpi.revenue') || 'Total revenue'} icon="DollarSign" loading={!s} value={s ? formatMoney(s.totalRevenue, s.currency) : undefined} />
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function SalesKpi() {
|
|
27
|
+
const t = useTranslation()
|
|
28
|
+
const s = useSummary()
|
|
29
|
+
return <KpiCard label={t('courses.kpi.sales') || 'Sales'} icon="TrendingUp" loading={!s} value={s ? String(s.salesCount) : undefined} sub={s ? formatPercent(s.refundRate, 1) + ' ' + (t('courses.kpi.refunds') || 'refunds') : undefined} />
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function MrrKpi() {
|
|
33
|
+
const t = useTranslation()
|
|
34
|
+
const s = useSummary()
|
|
35
|
+
return <KpiCard label={t('courses.kpi.mrr') || 'MRR'} icon="Repeat" loading={!s} value={s ? formatMoney(s.mrr, s.currency) : undefined} sub={s ? `${s.activeSubscriptions} ${t('courses.kpi.activeSubs') || 'active'}` : undefined} />
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function FeesKpi() {
|
|
39
|
+
const t = useTranslation()
|
|
40
|
+
const s = useSummary()
|
|
41
|
+
return <KpiCard label={t('courses.kpi.fees') || 'Platform fees'} icon="Percent" loading={!s} value={s ? formatMoney(s.platformFeeTotal, s.currency) : undefined} />
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function createCoursesDashboardWidgets(): DashboardWidgetDef[] {
|
|
45
|
+
return [
|
|
46
|
+
defineKpiWidget({ id: 'courses.kpi.revenue', title: 'courses.kpi.revenue', domain: 'courses', defaultOrder: 0, component: RevenueKpi }),
|
|
47
|
+
defineKpiWidget({ id: 'courses.kpi.sales', title: 'courses.kpi.sales', domain: 'courses', defaultOrder: 1, component: SalesKpi }),
|
|
48
|
+
defineKpiWidget({ id: 'courses.kpi.mrr', title: 'courses.kpi.mrr', domain: 'courses', defaultOrder: 2, component: MrrKpi }),
|
|
49
|
+
defineKpiWidget({ id: 'courses.kpi.fees', title: 'courses.kpi.fees', domain: 'courses', defaultOrder: 3, component: FeesKpi }),
|
|
50
|
+
]
|
|
51
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -5,15 +5,28 @@ import {
|
|
|
5
5
|
getCoursesProviderOptional,
|
|
6
6
|
setCoursesProvider,
|
|
7
7
|
createMockCoursesProvider,
|
|
8
|
+
setCoursesTenantResolver,
|
|
8
9
|
} from '@fayz-ai/courses'
|
|
10
|
+
import { useOrganizationStore } from '@fayz-ai/saas'
|
|
9
11
|
import { coursesLocales } from './locales'
|
|
10
12
|
import { CoursesListPage } from './pages/CoursesListPage'
|
|
11
13
|
import { CourseEditorPage } from './pages/CourseEditorPage'
|
|
14
|
+
import { SalesPage } from './pages/SalesPage'
|
|
15
|
+
import { SubscriptionsPage } from './pages/SubscriptionsPage'
|
|
16
|
+
import { FinancialPage } from './pages/FinancialPage'
|
|
17
|
+
import { ReportsPage } from './pages/ReportsPage'
|
|
18
|
+
import { MembersAreaPage } from './pages/MembersAreaPage'
|
|
19
|
+
import { CoursesSettingsTab } from './settings/CoursesSettings'
|
|
20
|
+
import { createCoursesDashboardWidgets } from './dashboardWidgets'
|
|
21
|
+
import { createStripeConnector } from './connectors/stripe'
|
|
12
22
|
|
|
13
23
|
// ---------------------------------------------------------------------------
|
|
14
|
-
// @fayz-ai/plugin-courses — admin management for the courses domain.
|
|
15
|
-
//
|
|
16
|
-
//
|
|
24
|
+
// @fayz-ai/plugin-courses — admin management for the courses domain. This is a
|
|
25
|
+
// PLUGIN, not an app: it only contributes nav + routes + settings + dashboard
|
|
26
|
+
// widgets to the @fayz-ai/saas admin scaffold. Login, auth, permissions, the
|
|
27
|
+
// sidebar/AppShell chrome and layout all come from @fayz-ai/saas — the plugin
|
|
28
|
+
// never rebuilds them. Its screens render inside the saas AdminShell and use
|
|
29
|
+
// @fayz-ai/ui primitives.
|
|
17
30
|
// ---------------------------------------------------------------------------
|
|
18
31
|
|
|
19
32
|
export interface CoursesPluginOptions {
|
|
@@ -24,16 +37,26 @@ export interface CoursesPluginOptions {
|
|
|
24
37
|
verticalId?: VerticalId
|
|
25
38
|
}
|
|
26
39
|
|
|
40
|
+
const asComp = (c: unknown) => c as React.ComponentType<unknown>
|
|
41
|
+
const read = (feature: string) => ({ feature, action: 'read' as const })
|
|
42
|
+
|
|
27
43
|
export function createCoursesPlugin(options?: CoursesPluginOptions): PluginManifest {
|
|
28
44
|
// Register locales globally so keys resolve even before the I18nProvider mounts.
|
|
29
45
|
registerTranslations(coursesLocales)
|
|
30
46
|
|
|
47
|
+
// Scope Supabase queries to the active organization (the creator/tenant). The
|
|
48
|
+
// mock provider ignores this; the Supabase provider appends .eq('tenant_id').
|
|
49
|
+
setCoursesTenantResolver(() => useOrganizationStore.getState().currentOrg?.id)
|
|
50
|
+
|
|
31
51
|
// Defensive default: if the host app didn't set a provider, fall back to a
|
|
32
52
|
// mock so the admin still renders. Apps that share admin↔member set their own.
|
|
33
53
|
if (!getCoursesProviderOptional()) {
|
|
34
54
|
setCoursesProvider(createMockCoursesProvider())
|
|
35
55
|
}
|
|
36
56
|
|
|
57
|
+
const base = options?.navPosition ?? 1
|
|
58
|
+
const section = options?.navSection ?? 'main'
|
|
59
|
+
|
|
37
60
|
return {
|
|
38
61
|
id: 'courses',
|
|
39
62
|
name: options?.navLabel ?? 'Courses',
|
|
@@ -43,20 +66,36 @@ export function createCoursesPlugin(options?: CoursesPluginOptions): PluginManif
|
|
|
43
66
|
verticalId: options?.verticalId,
|
|
44
67
|
defaultEnabled: true,
|
|
45
68
|
dependencies: [],
|
|
69
|
+
declaredFeatures: [
|
|
70
|
+
{ id: 'courses', label: 'Courses', group: 'Courses' },
|
|
71
|
+
{ id: 'courses.sales', label: 'Sales', group: 'Courses' },
|
|
72
|
+
{ id: 'courses.finance', label: 'Financial', group: 'Courses' },
|
|
73
|
+
],
|
|
46
74
|
navigation: [
|
|
47
|
-
{
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
},
|
|
75
|
+
{ section, position: base, label: options?.navLabel ?? 'Courses', route: '/courses', icon: 'BookOpen', permission: read('courses') },
|
|
76
|
+
{ section, position: base + 1, label: 'Members area', route: '/courses/members', icon: 'GraduationCap', permission: read('courses') },
|
|
77
|
+
{ section, position: base + 2, label: 'Sales', route: '/courses/sales', icon: 'TrendingUp', permission: read('courses.sales') },
|
|
78
|
+
{ section, position: base + 3, label: 'Subscriptions', route: '/courses/subscriptions', icon: 'Repeat', permission: read('courses.sales') },
|
|
79
|
+
{ section, position: base + 4, label: 'Financial', route: '/courses/financial', icon: 'CreditCard', permission: read('courses.finance') },
|
|
80
|
+
{ section, position: base + 5, label: 'Reports', route: '/courses/reports', icon: 'BarChart3', permission: read('courses.sales') },
|
|
54
81
|
],
|
|
55
82
|
routes: [
|
|
56
|
-
|
|
57
|
-
|
|
83
|
+
// Static segments outscore the `:id` param in the saas router, so these
|
|
84
|
+
// resolve before the editor route (AdminShell sorts by routeScore).
|
|
85
|
+
{ path: '/courses', component: asComp(CoursesListPage), permission: read('courses') },
|
|
86
|
+
{ path: '/courses/members', component: asComp(MembersAreaPage), permission: read('courses') },
|
|
87
|
+
{ path: '/courses/sales', component: asComp(SalesPage), permission: read('courses.sales') },
|
|
88
|
+
{ path: '/courses/subscriptions', component: asComp(SubscriptionsPage), permission: read('courses.sales') },
|
|
89
|
+
{ path: '/courses/financial', component: asComp(FinancialPage), permission: read('courses.finance') },
|
|
90
|
+
{ path: '/courses/reports', component: asComp(ReportsPage), permission: read('courses.sales') },
|
|
91
|
+
{ path: '/courses/:id', component: asComp(CourseEditorPage) },
|
|
58
92
|
],
|
|
59
93
|
widgets: [],
|
|
94
|
+
dashboardWidgets: createCoursesDashboardWidgets(),
|
|
95
|
+
connectors: [createStripeConnector()],
|
|
96
|
+
settings: [
|
|
97
|
+
{ id: 'courses', label: 'Courses', icon: 'BookOpen', component: asComp(CoursesSettingsTab), order: 20, permission: read('courses') },
|
|
98
|
+
],
|
|
60
99
|
locales: coursesLocales,
|
|
61
100
|
}
|
|
62
101
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Small display helpers shared across the courses admin pages. Kept local so the
|
|
2
|
+
// plugin depends only on @fayz-ai/core + @fayz-ai/ui + @fayz-ai/courses.
|
|
3
|
+
|
|
4
|
+
export function formatMoney(value: number, currency = 'BRL'): string {
|
|
5
|
+
try {
|
|
6
|
+
return new Intl.NumberFormat('pt-BR', { style: 'currency', currency }).format(value)
|
|
7
|
+
} catch {
|
|
8
|
+
return `${currency} ${value.toFixed(2)}`
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function formatDate(iso: string): string {
|
|
13
|
+
try {
|
|
14
|
+
return new Intl.DateTimeFormat('pt-BR', { day: '2-digit', month: '2-digit', year: 'numeric' }).format(new Date(iso))
|
|
15
|
+
} catch {
|
|
16
|
+
return iso.slice(0, 10)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function formatPercent(fraction: number, digits = 0): string {
|
|
21
|
+
return `${(fraction * 100).toFixed(digits)}%`
|
|
22
|
+
}
|
package/src/locales/en.ts
CHANGED
|
@@ -30,4 +30,113 @@ export const en: Record<string, string> = {
|
|
|
30
30
|
'courses.editor.untitledCourse': 'Untitled course',
|
|
31
31
|
'courses.editor.untitledModule': 'New module',
|
|
32
32
|
'courses.editor.untitledLesson': 'New lesson',
|
|
33
|
+
// Editor tabs
|
|
34
|
+
'courses.editor.tab.details': 'General',
|
|
35
|
+
'courses.editor.tab.curriculum': 'Curriculum',
|
|
36
|
+
'courses.editor.tab.offers': 'Offers',
|
|
37
|
+
'courses.editor.tab.members': 'Members area',
|
|
38
|
+
'courses.editor.tab.settings': 'Payment',
|
|
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).',
|
|
44
|
+
'courses.editor.payCard': 'Credit card (up to 12x)',
|
|
45
|
+
'courses.editor.payPix': 'Pix',
|
|
46
|
+
'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
|
+
// Offers
|
|
51
|
+
'courses.offers.help': 'Price points a customer can buy. Mark one default; order-bumps show on other checkouts.',
|
|
52
|
+
'courses.offers.name': 'Offer name',
|
|
53
|
+
'courses.offers.price': 'Price',
|
|
54
|
+
'courses.offers.oneTime': 'One-time',
|
|
55
|
+
'courses.offers.subscription': 'Subscription',
|
|
56
|
+
'courses.offers.default': 'Default',
|
|
57
|
+
'courses.offers.bump': 'Bump',
|
|
58
|
+
'courses.offers.remove': 'Remove',
|
|
59
|
+
'courses.offers.add': 'Add offer',
|
|
60
|
+
'courses.offers.newName': 'New offer',
|
|
61
|
+
// Sales
|
|
62
|
+
'courses.sales.title': 'Sales',
|
|
63
|
+
'courses.sales.subtitle': 'Every transaction across your courses.',
|
|
64
|
+
'courses.sales.date': 'Date',
|
|
65
|
+
'courses.sales.customer': 'Customer',
|
|
66
|
+
'courses.sales.method': 'Method',
|
|
67
|
+
'courses.sales.status': 'Status',
|
|
68
|
+
'courses.sales.fee': 'Platform fee',
|
|
69
|
+
'courses.sales.amount': 'Amount',
|
|
70
|
+
'courses.sales.count': 'Sales',
|
|
71
|
+
'courses.sales.revenue': 'Revenue',
|
|
72
|
+
'courses.sales.empty': 'No sales yet.',
|
|
73
|
+
'courses.financialStatus.paid': 'Paid',
|
|
74
|
+
'courses.financialStatus.pending': 'Pending',
|
|
75
|
+
'courses.financialStatus.refunded': 'Refunded',
|
|
76
|
+
'courses.financialStatus.chargeback': 'Chargeback',
|
|
77
|
+
// Subscriptions
|
|
78
|
+
'courses.subs.title': 'Subscriptions',
|
|
79
|
+
'courses.subs.subtitle': 'Recurring plans and MRR.',
|
|
80
|
+
'courses.subs.start': 'Start date',
|
|
81
|
+
'courses.subs.customer': 'Customer',
|
|
82
|
+
'courses.subs.status': 'Status',
|
|
83
|
+
'courses.subs.value': 'Net value',
|
|
84
|
+
'courses.subs.active': 'Active subscriptions',
|
|
85
|
+
'courses.subs.mrr': 'Monthly recurring revenue',
|
|
86
|
+
'courses.subs.empty': 'No subscriptions yet.',
|
|
87
|
+
'courses.subStatus.active': 'Active',
|
|
88
|
+
'courses.subStatus.canceled': 'Canceled',
|
|
89
|
+
'courses.subStatus.past_due': 'Past due',
|
|
90
|
+
'courses.interval.month': 'month',
|
|
91
|
+
'courses.interval.year': 'year',
|
|
92
|
+
// Financial
|
|
93
|
+
'courses.financial.title': 'Financial settings',
|
|
94
|
+
'courses.financial.subtitle': 'Balance, payouts and your Stripe connection.',
|
|
95
|
+
'courses.financial.available': 'Available balance',
|
|
96
|
+
'courses.financial.pending': 'Pending balance',
|
|
97
|
+
'courses.financial.revenue': 'Total revenue',
|
|
98
|
+
'courses.financial.feeCollected': 'Platform fees',
|
|
99
|
+
'courses.financial.refundRate': 'refund rate',
|
|
100
|
+
'courses.financial.payouts': 'Payouts',
|
|
101
|
+
'courses.financial.noPayouts': 'No payouts yet.',
|
|
102
|
+
'courses.financial.date': 'Date',
|
|
103
|
+
'courses.financial.amount': 'Amount',
|
|
104
|
+
'courses.financial.status': 'Status',
|
|
105
|
+
'courses.financial.connect': 'Connect Stripe',
|
|
106
|
+
'courses.financial.connected': 'Stripe connected',
|
|
107
|
+
'courses.financial.notConnected': 'Connect your Stripe account to receive payouts. The platform fee is applied automatically on each sale.',
|
|
108
|
+
'courses.financial.platformFee': 'Platform fee',
|
|
109
|
+
'courses.financial.connectSoon': 'Stripe Connect onboarding wires up in Stage 3.',
|
|
110
|
+
'courses.payoutStatus.paid': 'Paid',
|
|
111
|
+
'courses.payoutStatus.pending': 'Pending',
|
|
112
|
+
'courses.payoutStatus.failed': 'Failed',
|
|
113
|
+
// Reports
|
|
114
|
+
'courses.reports.title': 'Reports',
|
|
115
|
+
'courses.reports.subtitle': 'Revenue by course.',
|
|
116
|
+
'courses.reports.byCourse': 'Revenue by course',
|
|
117
|
+
'courses.reports.course': 'Course',
|
|
118
|
+
'courses.reports.sales': 'Sales',
|
|
119
|
+
'courses.reports.revenue': 'Revenue',
|
|
120
|
+
'courses.reports.empty': 'No revenue yet.',
|
|
121
|
+
// Members area
|
|
122
|
+
'courses.membersArea.title': 'Members area',
|
|
123
|
+
'courses.membersArea.subtitle': 'One area per course; students land here after purchase.',
|
|
124
|
+
'courses.membersArea.name': 'Members area',
|
|
125
|
+
'courses.membersArea.status': 'Status',
|
|
126
|
+
'courses.membersArea.students': 'Students',
|
|
127
|
+
'courses.membersArea.empty': 'No members areas yet.',
|
|
128
|
+
// Settings
|
|
129
|
+
'courses.settings.title': 'Course settings',
|
|
130
|
+
'courses.settings.subtitle': 'Payments, currency and checkout methods',
|
|
131
|
+
'courses.settings.payments': 'Payments',
|
|
132
|
+
'courses.settings.paymentsHelp': 'Connect Stripe in the Integrations tab; the platform fee applies automatically.',
|
|
133
|
+
'courses.settings.currency': 'Default currency',
|
|
134
|
+
'courses.settings.methods': 'Payment methods',
|
|
135
|
+
// Dashboard KPIs
|
|
136
|
+
'courses.kpi.revenue': 'Total revenue',
|
|
137
|
+
'courses.kpi.sales': 'Sales',
|
|
138
|
+
'courses.kpi.refunds': 'refunds',
|
|
139
|
+
'courses.kpi.mrr': 'Recurring revenue (MRR)',
|
|
140
|
+
'courses.kpi.activeSubs': 'active',
|
|
141
|
+
'courses.kpi.fees': 'Platform fees',
|
|
33
142
|
}
|
package/src/locales/pt-BR.ts
CHANGED
|
@@ -30,4 +30,113 @@ export const ptBR: Record<string, string> = {
|
|
|
30
30
|
'courses.editor.untitledCourse': 'Curso sem título',
|
|
31
31
|
'courses.editor.untitledModule': 'Novo módulo',
|
|
32
32
|
'courses.editor.untitledLesson': 'Nova aula',
|
|
33
|
+
// Editor tabs
|
|
34
|
+
'courses.editor.tab.details': 'Geral',
|
|
35
|
+
'courses.editor.tab.curriculum': 'Conteúdo',
|
|
36
|
+
'courses.editor.tab.offers': 'Ofertas',
|
|
37
|
+
'courses.editor.tab.members': 'Área de membros',
|
|
38
|
+
'courses.editor.tab.settings': 'Pagamento',
|
|
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).',
|
|
44
|
+
'courses.editor.payCard': 'Cartão de crédito (até 12x)',
|
|
45
|
+
'courses.editor.payPix': 'Pix',
|
|
46
|
+
'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
|
+
// Offers
|
|
51
|
+
'courses.offers.help': 'Preços que o cliente pode comprar. Marque um padrão; order-bumps aparecem nos checkouts das outras.',
|
|
52
|
+
'courses.offers.name': 'Nome da oferta',
|
|
53
|
+
'courses.offers.price': 'Preço',
|
|
54
|
+
'courses.offers.oneTime': 'Único',
|
|
55
|
+
'courses.offers.subscription': 'Assinatura',
|
|
56
|
+
'courses.offers.default': 'Padrão',
|
|
57
|
+
'courses.offers.bump': 'Bump',
|
|
58
|
+
'courses.offers.remove': 'Remover',
|
|
59
|
+
'courses.offers.add': 'Adicionar oferta',
|
|
60
|
+
'courses.offers.newName': 'Nova oferta',
|
|
61
|
+
// Sales
|
|
62
|
+
'courses.sales.title': 'Vendas',
|
|
63
|
+
'courses.sales.subtitle': 'Todas as transações dos seus cursos.',
|
|
64
|
+
'courses.sales.date': 'Data',
|
|
65
|
+
'courses.sales.customer': 'Cliente',
|
|
66
|
+
'courses.sales.method': 'Método',
|
|
67
|
+
'courses.sales.status': 'Status',
|
|
68
|
+
'courses.sales.fee': 'Taxa da plataforma',
|
|
69
|
+
'courses.sales.amount': 'Valor',
|
|
70
|
+
'courses.sales.count': 'Vendas',
|
|
71
|
+
'courses.sales.revenue': 'Receita',
|
|
72
|
+
'courses.sales.empty': 'Nenhuma venda ainda.',
|
|
73
|
+
'courses.financialStatus.paid': 'Pago',
|
|
74
|
+
'courses.financialStatus.pending': 'Pendente',
|
|
75
|
+
'courses.financialStatus.refunded': 'Reembolsado',
|
|
76
|
+
'courses.financialStatus.chargeback': 'Chargeback',
|
|
77
|
+
// Subscriptions
|
|
78
|
+
'courses.subs.title': 'Assinaturas',
|
|
79
|
+
'courses.subs.subtitle': 'Planos recorrentes e MRR.',
|
|
80
|
+
'courses.subs.start': 'Início',
|
|
81
|
+
'courses.subs.customer': 'Cliente',
|
|
82
|
+
'courses.subs.status': 'Status',
|
|
83
|
+
'courses.subs.value': 'Valor líquido',
|
|
84
|
+
'courses.subs.active': 'Assinaturas ativas',
|
|
85
|
+
'courses.subs.mrr': 'Receita recorrente mensal',
|
|
86
|
+
'courses.subs.empty': 'Nenhuma assinatura ainda.',
|
|
87
|
+
'courses.subStatus.active': 'Ativa',
|
|
88
|
+
'courses.subStatus.canceled': 'Cancelada',
|
|
89
|
+
'courses.subStatus.past_due': 'Em atraso',
|
|
90
|
+
'courses.interval.month': 'mês',
|
|
91
|
+
'courses.interval.year': 'ano',
|
|
92
|
+
// Financial
|
|
93
|
+
'courses.financial.title': 'Financeiro',
|
|
94
|
+
'courses.financial.subtitle': 'Saldo, saques e sua conexão com o Stripe.',
|
|
95
|
+
'courses.financial.available': 'Saldo disponível',
|
|
96
|
+
'courses.financial.pending': 'Saldo pendente',
|
|
97
|
+
'courses.financial.revenue': 'Receita total',
|
|
98
|
+
'courses.financial.feeCollected': 'Taxas da plataforma',
|
|
99
|
+
'courses.financial.refundRate': 'taxa de reembolso',
|
|
100
|
+
'courses.financial.payouts': 'Saques',
|
|
101
|
+
'courses.financial.noPayouts': 'Nenhum saque ainda.',
|
|
102
|
+
'courses.financial.date': 'Data',
|
|
103
|
+
'courses.financial.amount': 'Valor',
|
|
104
|
+
'courses.financial.status': 'Status',
|
|
105
|
+
'courses.financial.connect': 'Conectar Stripe',
|
|
106
|
+
'courses.financial.connected': 'Stripe conectado',
|
|
107
|
+
'courses.financial.notConnected': 'Conecte sua conta Stripe para receber os saques. A taxa da plataforma é aplicada automaticamente em cada venda.',
|
|
108
|
+
'courses.financial.platformFee': 'Taxa da plataforma',
|
|
109
|
+
'courses.financial.connectSoon': 'O onboarding do Stripe Connect é ligado na Etapa 3.',
|
|
110
|
+
'courses.payoutStatus.paid': 'Concluído',
|
|
111
|
+
'courses.payoutStatus.pending': 'Pendente',
|
|
112
|
+
'courses.payoutStatus.failed': 'Falhou',
|
|
113
|
+
// Reports
|
|
114
|
+
'courses.reports.title': 'Relatórios',
|
|
115
|
+
'courses.reports.subtitle': 'Receita por curso.',
|
|
116
|
+
'courses.reports.byCourse': 'Receita por curso',
|
|
117
|
+
'courses.reports.course': 'Curso',
|
|
118
|
+
'courses.reports.sales': 'Vendas',
|
|
119
|
+
'courses.reports.revenue': 'Receita',
|
|
120
|
+
'courses.reports.empty': 'Nenhuma receita ainda.',
|
|
121
|
+
// Members area
|
|
122
|
+
'courses.membersArea.title': 'Área de membros',
|
|
123
|
+
'courses.membersArea.subtitle': 'Uma área por curso; o aluno entra após a compra.',
|
|
124
|
+
'courses.membersArea.name': 'Área de membros',
|
|
125
|
+
'courses.membersArea.status': 'Status',
|
|
126
|
+
'courses.membersArea.students': 'Alunos',
|
|
127
|
+
'courses.membersArea.empty': 'Nenhuma área de membros ainda.',
|
|
128
|
+
// Settings
|
|
129
|
+
'courses.settings.title': 'Configurações do curso',
|
|
130
|
+
'courses.settings.subtitle': 'Pagamentos, moeda e métodos de checkout',
|
|
131
|
+
'courses.settings.payments': 'Pagamentos',
|
|
132
|
+
'courses.settings.paymentsHelp': 'Conecte o Stripe na aba Integrações; a taxa da plataforma é aplicada automaticamente.',
|
|
133
|
+
'courses.settings.currency': 'Moeda padrão',
|
|
134
|
+
'courses.settings.methods': 'Métodos de pagamento',
|
|
135
|
+
// Dashboard KPIs
|
|
136
|
+
'courses.kpi.revenue': 'Receita total',
|
|
137
|
+
'courses.kpi.sales': 'Vendas',
|
|
138
|
+
'courses.kpi.refunds': 'reembolsos',
|
|
139
|
+
'courses.kpi.mrr': 'Receita recorrente (MRR)',
|
|
140
|
+
'courses.kpi.activeSubs': 'ativas',
|
|
141
|
+
'courses.kpi.fees': 'Taxas da plataforma',
|
|
33
142
|
}
|