@fayz-ai/plugin-courses 0.10.35 → 0.10.37
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/index.js +85 -78
- package/dist/index.js.map +1 -1
- package/dist/integrations/kiwify/connectorDef.d.ts.map +1 -1
- package/dist/lib/format.d.ts +0 -1
- package/dist/lib/format.d.ts.map +1 -1
- package/dist/public/index.js +3 -10
- package/dist/public/index.js.map +1 -1
- package/dist/public/views/CheckoutScreen.d.ts.map +1 -1
- package/dist/views/CourseEditorPage.d.ts.map +1 -1
- package/dist/views/CourseOrderDetailView.d.ts.map +1 -1
- package/dist/views/ReportsPage.d.ts.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as React3 from 'react';
|
|
2
2
|
import { useState, useEffect, useMemo } from 'react';
|
|
3
|
-
import { hashRouterAdapter, createConnectionStore, connectionSettings, connectionStatus, settingsStatus, registerPluginFactory, registerTranslations, useTranslation, getSupabaseClientOptional, connectorRuntimeToken, CONNECTOR_RUNTIME_TOKEN_HEADER } from '@fayz-ai/core';
|
|
3
|
+
import { hashRouterAdapter, createConnectionStore, connectionSettings, connectionStatus, settingsStatus, registerPluginFactory, registerTranslations, useTranslation, formatCurrency, getSupabaseClientOptional, connectorRuntimeToken, CONNECTOR_RUNTIME_TOKEN_HEADER } from '@fayz-ai/core';
|
|
4
4
|
import { setCoursesTenantResolver, getCoursesProviderOptional, setCoursesProvider, createMockCoursesProvider, getCoursesTenantId, COURSES_TABLES, getCoursesProvider } from '@fayz-ai/courses';
|
|
5
5
|
import { useOrganizationStore, useLimitGuard, usePermissionOptional, PermissionGate, OrderListView, OrderDetailSkeleton, PersonLink, OrderDetailView, invalidateLimit, PluginSettingsPanel, SettingsGroup, SelectRow, ToggleRow } from '@fayz-ai/admin';
|
|
6
|
-
import { Button, DatePicker, defineKpiWidget, toast, useModuleLayout, PageHeaderActions, Badge, useSaveBar, PageHeader, Separator, Tabs, TabsList, TabsTrigger, TabsContent, Input,
|
|
6
|
+
import { Button, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, DatePicker, defineKpiWidget, toast, useModuleLayout, PageHeaderActions, Badge, useSaveBar, PageHeader, Separator, Tabs, TabsList, TabsTrigger, TabsContent, Input, Textarea, SegmentedControl, Checkbox, ConfirmDialog, KpiCard, Skeleton, HeaderTabs, Card, DataTable } from '@fayz-ai/ui';
|
|
7
7
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
8
8
|
import { Loader2, Plug, Link2, Download, History, CheckCircle2, AlertCircle, ImagePlus, Users, ShoppingCart, Layers, Tag, UploadCloud, Info, Plus, Trash2, Clock, Lock, Check, GripVertical, ChevronDown, Video, FileText, PlayCircle } from 'lucide-react';
|
|
9
9
|
import { useSensors, useSensor, PointerSensor, KeyboardSensor, DndContext, closestCenter } from '@dnd-kit/core';
|
|
@@ -470,10 +470,12 @@ function CoursesListPage() {
|
|
|
470
470
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3", children: [
|
|
471
471
|
courses === null && Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ jsx(CourseCardSkeleton, {}, i)),
|
|
472
472
|
courses?.map((course) => /* @__PURE__ */ jsxs(
|
|
473
|
-
|
|
473
|
+
Button,
|
|
474
474
|
{
|
|
475
|
+
variant: "bare",
|
|
476
|
+
size: "bare",
|
|
475
477
|
onClick: () => navigateTo(`/courses/${course.id}`),
|
|
476
|
-
className: "group overflow-hidden rounded-card border border-border bg-card text-left transition hover:border-primary hover:shadow-md",
|
|
478
|
+
className: "group overflow-hidden rounded-card border border-border bg-card text-left transition hover:border-primary hover:shadow-md block whitespace-normal font-normal",
|
|
477
479
|
children: [
|
|
478
480
|
/* @__PURE__ */ jsx("div", { className: "aspect-video w-full overflow-hidden bg-muted", children: course.thumbnailUrl && // eslint-disable-next-line @next/next/no-img-element
|
|
479
481
|
/* @__PURE__ */ jsx("img", { src: course.thumbnailUrl, alt: "", className: "h-full w-full object-cover" }) }),
|
|
@@ -506,25 +508,6 @@ function CourseCardSkeleton() {
|
|
|
506
508
|
] })
|
|
507
509
|
] });
|
|
508
510
|
}
|
|
509
|
-
|
|
510
|
-
// src/lib/format.ts
|
|
511
|
-
function formatMoney(value, currency = "BRL") {
|
|
512
|
-
try {
|
|
513
|
-
return new Intl.NumberFormat("pt-BR", { style: "currency", currency }).format(value);
|
|
514
|
-
} catch {
|
|
515
|
-
return `${currency} ${value.toFixed(2)}`;
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
function formatDate(iso) {
|
|
519
|
-
try {
|
|
520
|
-
return new Intl.DateTimeFormat("pt-BR", { day: "2-digit", month: "2-digit", year: "numeric" }).format(new Date(iso));
|
|
521
|
-
} catch {
|
|
522
|
-
return iso.slice(0, 10);
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
function formatPercent(fraction, digits = 0) {
|
|
526
|
-
return `${(fraction * 100).toFixed(digits)}%`;
|
|
527
|
-
}
|
|
528
511
|
function PageContainer({ title, subtitle, actions, navTabs, activeNavTab, onNavTabChange, children }) {
|
|
529
512
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
530
513
|
navTabs && navTabs.length > 0 ? (
|
|
@@ -898,6 +881,7 @@ function CourseEditorPage({ id }) {
|
|
|
898
881
|
/* @__PURE__ */ jsx(Field, { label: t("courses.editor.fieldDescription") || "Description", children: /* @__PURE__ */ jsx(
|
|
899
882
|
Textarea,
|
|
900
883
|
{
|
|
884
|
+
className: "min-h-[96px] px-2 py-1.5",
|
|
901
885
|
value: course.description ?? "",
|
|
902
886
|
onChange: (e) => patchCourse({ description: e.target.value }),
|
|
903
887
|
placeholder: t("courses.editor.descriptionPlaceholder") || "What will students learn?"
|
|
@@ -991,8 +975,10 @@ function CourseEditorPage({ id }) {
|
|
|
991
975
|
/* @__PURE__ */ jsxs("div", { className: "mb-4 flex items-start justify-between gap-3", children: [
|
|
992
976
|
/* @__PURE__ */ jsx(Badge, { variant: "outline", className: "gap-1", children: offer.kind === "subscription" ? t("courses.offers.subscription") || "Subscription" : t("courses.offers.oneTime") || "One-time" }),
|
|
993
977
|
/* @__PURE__ */ jsx(
|
|
994
|
-
|
|
978
|
+
Button,
|
|
995
979
|
{
|
|
980
|
+
variant: "bare",
|
|
981
|
+
size: "bare",
|
|
996
982
|
onClick: () => deleteOffer(offer.id),
|
|
997
983
|
className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-muted-foreground/60 transition-colors hover:bg-destructive/10 hover:text-destructive",
|
|
998
984
|
"aria-label": t("courses.offers.remove") || "Remove",
|
|
@@ -1085,7 +1071,7 @@ function CourseEditorPage({ id }) {
|
|
|
1085
1071
|
/* @__PURE__ */ jsx("div", { className: "divide-y divide-border overflow-hidden rounded-card border border-border bg-card", children: offers.map((offer) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-4 py-3", children: [
|
|
1086
1072
|
/* @__PURE__ */ jsx("span", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-muted text-muted-foreground", "aria-hidden": true, children: /* @__PURE__ */ jsx(Lock, { className: "h-4 w-4" }) }),
|
|
1087
1073
|
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate font-medium text-foreground", children: offer.name }),
|
|
1088
|
-
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-sm tabular-nums text-muted-foreground", children:
|
|
1074
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-sm tabular-nums text-muted-foreground", children: formatCurrency(offer.price, offer.currency) })
|
|
1089
1075
|
] }, offer.id)) })
|
|
1090
1076
|
] })
|
|
1091
1077
|
] })
|
|
@@ -1254,8 +1240,10 @@ function SortableModuleCard({
|
|
|
1254
1240
|
children: [
|
|
1255
1241
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 px-2 py-2 sm:gap-2 sm:px-3", children: [
|
|
1256
1242
|
/* @__PURE__ */ jsx(
|
|
1257
|
-
|
|
1243
|
+
Button,
|
|
1258
1244
|
{
|
|
1245
|
+
variant: "bare",
|
|
1246
|
+
size: "bare",
|
|
1259
1247
|
...attributes,
|
|
1260
1248
|
...listeners,
|
|
1261
1249
|
className: "hidden h-8 w-6 shrink-0 cursor-grab touch-none items-center justify-center rounded-md text-muted-foreground/40 transition-colors hover:text-muted-foreground active:cursor-grabbing sm:flex",
|
|
@@ -1264,8 +1252,10 @@ function SortableModuleCard({
|
|
|
1264
1252
|
}
|
|
1265
1253
|
),
|
|
1266
1254
|
/* @__PURE__ */ jsx(
|
|
1267
|
-
|
|
1255
|
+
Button,
|
|
1268
1256
|
{
|
|
1257
|
+
variant: "bare",
|
|
1258
|
+
size: "bare",
|
|
1269
1259
|
onClick: onToggle,
|
|
1270
1260
|
className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
1271
1261
|
"aria-label": expanded ? t("courses.editor.collapse") || "Collapse" : t("courses.editor.expand") || "Expand",
|
|
@@ -1288,8 +1278,10 @@ function SortableModuleCard({
|
|
|
1288
1278
|
),
|
|
1289
1279
|
/* @__PURE__ */ jsx("span", { className: "hidden shrink-0 text-xs text-muted-foreground tabular-nums md:inline", children: count > 0 ? `${count} ${count === 1 ? t("courses.editor.lessonUnitOne") || "lesson" : t("courses.editor.lessonUnitOther") || "lessons"} \xB7 ${formatDuration(totalSec, t)}` : t("courses.editor.noLessons") || "No lessons" }),
|
|
1290
1280
|
/* @__PURE__ */ jsx(
|
|
1291
|
-
|
|
1281
|
+
Button,
|
|
1292
1282
|
{
|
|
1283
|
+
variant: "bare",
|
|
1284
|
+
size: "bare",
|
|
1293
1285
|
onClick: () => onRequestDelete("module", mod.id),
|
|
1294
1286
|
className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-muted-foreground/60 transition-colors hover:bg-destructive/10 hover:text-destructive",
|
|
1295
1287
|
"aria-label": t("courses.editor.deleteModule") || "Delete module",
|
|
@@ -1299,10 +1291,12 @@ function SortableModuleCard({
|
|
|
1299
1291
|
] }),
|
|
1300
1292
|
expanded && /* @__PURE__ */ jsxs("div", { className: "border-t border-border/60 bg-muted/20 p-2 sm:p-3", children: [
|
|
1301
1293
|
count === 0 ? /* @__PURE__ */ jsxs(
|
|
1302
|
-
|
|
1294
|
+
Button,
|
|
1303
1295
|
{
|
|
1296
|
+
variant: "bare",
|
|
1297
|
+
size: "bare",
|
|
1304
1298
|
onClick: () => onAddLesson(mod.id),
|
|
1305
|
-
className: "flex w-full items-center justify-center gap-2 rounded-lg border border-dashed border-border px-3 py-4 text-sm text-muted-foreground transition-colors hover:border-primary/40 hover:bg-card hover:text-foreground",
|
|
1299
|
+
className: "flex w-full items-center justify-center gap-2 rounded-lg border border-dashed border-border px-3 py-4 text-sm text-muted-foreground transition-colors hover:border-primary/40 hover:bg-card hover:text-foreground font-normal",
|
|
1306
1300
|
children: [
|
|
1307
1301
|
/* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }),
|
|
1308
1302
|
" ",
|
|
@@ -1322,8 +1316,10 @@ function SortableModuleCard({
|
|
|
1322
1316
|
lesson.id
|
|
1323
1317
|
)) }) }) }),
|
|
1324
1318
|
count > 0 && /* @__PURE__ */ jsxs(
|
|
1325
|
-
|
|
1319
|
+
Button,
|
|
1326
1320
|
{
|
|
1321
|
+
variant: "bare",
|
|
1322
|
+
size: "bare",
|
|
1327
1323
|
onClick: () => onAddLesson(mod.id),
|
|
1328
1324
|
className: "mt-2 flex items-center gap-1.5 rounded-md px-2 py-1.5 text-sm font-medium text-primary transition-colors hover:bg-primary/10",
|
|
1329
1325
|
children: [
|
|
@@ -1365,8 +1361,10 @@ function SortableLessonRow({ t, lesson, autoFocusId, onAutoFocused, onPatchLesso
|
|
|
1365
1361
|
children: [
|
|
1366
1362
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 px-1.5 py-1.5 sm:gap-2 sm:px-2", children: [
|
|
1367
1363
|
/* @__PURE__ */ jsx(
|
|
1368
|
-
|
|
1364
|
+
Button,
|
|
1369
1365
|
{
|
|
1366
|
+
variant: "bare",
|
|
1367
|
+
size: "bare",
|
|
1370
1368
|
...attributes,
|
|
1371
1369
|
...listeners,
|
|
1372
1370
|
className: "hidden h-8 w-5 shrink-0 cursor-grab touch-none items-center justify-center text-muted-foreground/40 transition-colors hover:text-muted-foreground active:cursor-grabbing sm:flex",
|
|
@@ -1401,8 +1399,10 @@ function SortableLessonRow({ t, lesson, autoFocusId, onAutoFocused, onPatchLesso
|
|
|
1401
1399
|
t("courses.editor.minShort") || "min"
|
|
1402
1400
|
] }),
|
|
1403
1401
|
/* @__PURE__ */ jsxs(
|
|
1404
|
-
|
|
1402
|
+
Button,
|
|
1405
1403
|
{
|
|
1404
|
+
variant: "bare",
|
|
1405
|
+
size: "bare",
|
|
1406
1406
|
onClick: () => setOpen((v) => !v),
|
|
1407
1407
|
className: "inline-flex shrink-0 items-center gap-1 rounded-full border px-2 py-1 text-xs font-medium transition-colors " + (hasVideo ? "border-border text-muted-foreground hover:text-foreground" : "border-dashed border-border text-muted-foreground hover:border-primary/40 hover:text-foreground") + " opacity-100 sm:opacity-0 sm:group-hover:opacity-100 " + (open ? "sm:opacity-100" : ""),
|
|
1408
1408
|
"aria-label": t("courses.editor.editLesson") || "Edit lesson",
|
|
@@ -1414,8 +1414,10 @@ function SortableLessonRow({ t, lesson, autoFocusId, onAutoFocused, onPatchLesso
|
|
|
1414
1414
|
}
|
|
1415
1415
|
),
|
|
1416
1416
|
/* @__PURE__ */ jsx(
|
|
1417
|
-
|
|
1417
|
+
Button,
|
|
1418
1418
|
{
|
|
1419
|
+
variant: "bare",
|
|
1420
|
+
size: "bare",
|
|
1419
1421
|
onClick: () => onRequestDelete("lesson", lesson.id),
|
|
1420
1422
|
className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-muted-foreground/60 transition-colors hover:bg-destructive/10 hover:text-destructive sm:opacity-0 sm:group-hover:opacity-100",
|
|
1421
1423
|
"aria-label": t("courses.editor.deleteLesson") || "Remove lesson",
|
|
@@ -1471,8 +1473,8 @@ function TitleField({ value, autoFocus, onAutoFocused, onCommit, placeholder, ar
|
|
|
1471
1473
|
onAutoFocused?.();
|
|
1472
1474
|
}
|
|
1473
1475
|
}, [autoFocus]);
|
|
1474
|
-
return /* @__PURE__ */ jsx(
|
|
1475
|
-
|
|
1476
|
+
return /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx(
|
|
1477
|
+
Input,
|
|
1476
1478
|
{
|
|
1477
1479
|
ref,
|
|
1478
1480
|
defaultValue: value,
|
|
@@ -1482,9 +1484,9 @@ function TitleField({ value, autoFocus, onAutoFocused, onCommit, placeholder, ar
|
|
|
1482
1484
|
onKeyDown: (e) => {
|
|
1483
1485
|
if (e.key === "Enter") e.currentTarget.blur();
|
|
1484
1486
|
},
|
|
1485
|
-
className: "
|
|
1487
|
+
className: "h-auto rounded-md border-0 bg-transparent px-2 py-1.5 shadow-none transition-colors placeholder:text-muted-foreground/60 hover:bg-muted/60 focus:bg-background focus-visible:ring-offset-0 " + (className ?? "")
|
|
1486
1488
|
}
|
|
1487
|
-
);
|
|
1489
|
+
) });
|
|
1488
1490
|
}
|
|
1489
1491
|
function Field({ label, hint, children }) {
|
|
1490
1492
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
@@ -1495,16 +1497,6 @@ function Field({ label, hint, children }) {
|
|
|
1495
1497
|
children
|
|
1496
1498
|
] });
|
|
1497
1499
|
}
|
|
1498
|
-
function Textarea(props) {
|
|
1499
|
-
const { className, ...rest } = props;
|
|
1500
|
-
return /* @__PURE__ */ jsx(
|
|
1501
|
-
"textarea",
|
|
1502
|
-
{
|
|
1503
|
-
className: "flex min-h-[96px] w-full rounded-input border border-input bg-card px-2 py-1.5 text-sm text-foreground shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 " + (className ?? ""),
|
|
1504
|
-
...rest
|
|
1505
|
-
}
|
|
1506
|
-
);
|
|
1507
|
-
}
|
|
1508
1500
|
function AutosaveHint({ t }) {
|
|
1509
1501
|
return /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
|
|
1510
1502
|
/* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5 text-success" }),
|
|
@@ -1522,6 +1514,18 @@ function EditorSkeleton() {
|
|
|
1522
1514
|
] })
|
|
1523
1515
|
] });
|
|
1524
1516
|
}
|
|
1517
|
+
|
|
1518
|
+
// src/lib/format.ts
|
|
1519
|
+
function formatDate(iso) {
|
|
1520
|
+
try {
|
|
1521
|
+
return new Intl.DateTimeFormat("pt-BR", { day: "2-digit", month: "2-digit", year: "numeric" }).format(new Date(iso));
|
|
1522
|
+
} catch {
|
|
1523
|
+
return iso.slice(0, 10);
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
function formatPercent(fraction, digits = 0) {
|
|
1527
|
+
return `${(fraction * 100).toFixed(digits)}%`;
|
|
1528
|
+
}
|
|
1525
1529
|
function statusVariant2(s) {
|
|
1526
1530
|
if (s === "paid") return "success";
|
|
1527
1531
|
if (s === "pending") return "secondary";
|
|
@@ -1567,8 +1571,8 @@ function SalesPage({ navTabs, activeNavTab, ordersRoute = "/courses/sales" } = {
|
|
|
1567
1571
|
o.installmentCount && o.installmentCount > 1 ? ` \xB7 ${o.installmentCount}x` : ""
|
|
1568
1572
|
] }) },
|
|
1569
1573
|
{ key: "status", header: t("courses.sales.status") || "Status", render: (o) => /* @__PURE__ */ jsx(Badge, { variant: statusVariant2(o.financialStatus), children: t(`courses.financialStatus.${o.financialStatus}`) || o.financialStatus }) },
|
|
1570
|
-
{ key: "fee", header: t("courses.sales.fee") || "Taxa", align: "right", render: (o) => /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children:
|
|
1571
|
-
{ key: "total", header: t("courses.sales.amount") || "Valor", align: "right", render: (o) => /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children:
|
|
1574
|
+
{ key: "fee", header: t("courses.sales.fee") || "Taxa", align: "right", render: (o) => /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: formatCurrency(o.platformFee, o.currency) }) },
|
|
1575
|
+
{ key: "total", header: t("courses.sales.amount") || "Valor", align: "right", render: (o) => /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: formatCurrency(o.total, o.currency) }) }
|
|
1572
1576
|
];
|
|
1573
1577
|
return /* @__PURE__ */ jsx(
|
|
1574
1578
|
OrderListView,
|
|
@@ -1583,7 +1587,7 @@ function SalesPage({ navTabs, activeNavTab, ordersRoute = "/courses/sales" } = {
|
|
|
1583
1587
|
},
|
|
1584
1588
|
stats: [
|
|
1585
1589
|
{ label: t("courses.sales.count") || "Vendas", value: String(paid.length) },
|
|
1586
|
-
{ label: t("courses.sales.revenue") || "Receita", value:
|
|
1590
|
+
{ label: t("courses.sales.revenue") || "Receita", value: formatCurrency(revenue, currency) }
|
|
1587
1591
|
],
|
|
1588
1592
|
columns,
|
|
1589
1593
|
rows: orders ?? [],
|
|
@@ -1642,14 +1646,14 @@ function CourseOrderDetailView({ id, ordersRoute = "/courses/sales" }) {
|
|
|
1642
1646
|
{
|
|
1643
1647
|
title: t("courses.order.notFound") || "Pedido n\xE3o encontrado",
|
|
1644
1648
|
description: "",
|
|
1645
|
-
action: /* @__PURE__ */ jsx("
|
|
1649
|
+
action: /* @__PURE__ */ jsx(Button, { variant: "bare", size: "bare", className: "text-sm text-primary underline font-normal", onClick: back, children: t("common.back") || "Voltar" })
|
|
1646
1650
|
}
|
|
1647
1651
|
) });
|
|
1648
1652
|
}
|
|
1649
1653
|
const cur = order.currency;
|
|
1650
1654
|
const installments = order.installments.map((m) => ({
|
|
1651
1655
|
number: m.installmentNumber,
|
|
1652
|
-
amount:
|
|
1656
|
+
amount: formatCurrency(m.amount, cur),
|
|
1653
1657
|
status: m.status,
|
|
1654
1658
|
statusTone: instTone(m.status),
|
|
1655
1659
|
dueDate: m.dueDate ? formatDate(m.dueDate) : null,
|
|
@@ -1679,19 +1683,19 @@ function CourseOrderDetailView({ id, ordersRoute = "/courses/sales" }) {
|
|
|
1679
1683
|
name: order.courseTitle ?? "\u2014",
|
|
1680
1684
|
description: order.offerName,
|
|
1681
1685
|
quantity: 1,
|
|
1682
|
-
unitPrice:
|
|
1683
|
-
total:
|
|
1686
|
+
unitPrice: formatCurrency(order.total, cur),
|
|
1687
|
+
total: formatCurrency(order.total, cur)
|
|
1684
1688
|
}],
|
|
1685
1689
|
totals: [
|
|
1686
|
-
{ label: "Total", value:
|
|
1687
|
-
...order.platformFee > 0 ? [{ label: "Taxa", value: `\u2212 ${
|
|
1688
|
-
{ label: "L\xEDquido", value:
|
|
1690
|
+
{ label: "Total", value: formatCurrency(order.total, cur), strong: true },
|
|
1691
|
+
...order.platformFee > 0 ? [{ label: "Taxa", value: `\u2212 ${formatCurrency(order.platformFee, cur)}`, muted: true }] : [],
|
|
1692
|
+
{ label: "L\xEDquido", value: formatCurrency(order.netValue, cur) }
|
|
1689
1693
|
],
|
|
1690
1694
|
paymentMethod: order.paymentMethod ?? void 0,
|
|
1691
1695
|
receivable: {
|
|
1692
1696
|
rows: [
|
|
1693
|
-
{ label: "Recebido", value:
|
|
1694
|
-
{ label: "Saldo", value:
|
|
1697
|
+
{ label: "Recebido", value: formatCurrency(order.paidAmount, cur) },
|
|
1698
|
+
{ label: "Saldo", value: formatCurrency(order.balance, cur), strong: order.balance > 0 }
|
|
1695
1699
|
],
|
|
1696
1700
|
installments,
|
|
1697
1701
|
emptyLabel: t("courses.order.noInstallments") || "Sem parcelas."
|
|
@@ -1725,7 +1729,7 @@ function SubscriptionsPage({ navTabs, activeNavTab } = {}) {
|
|
|
1725
1729
|
] }) },
|
|
1726
1730
|
{ key: "status", header: t("courses.subs.status") || "Status", render: (s) => /* @__PURE__ */ jsx(Badge, { variant: statusVariant3(s.status), children: t(`courses.subStatus.${s.status}`) || s.status }) },
|
|
1727
1731
|
{ key: "value", header: t("courses.subs.value") || "Net value", align: "right", render: (s) => /* @__PURE__ */ jsxs("span", { className: "font-medium text-foreground", children: [
|
|
1728
|
-
|
|
1732
|
+
formatCurrency(s.netValue, s.currency),
|
|
1729
1733
|
/* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
1730
1734
|
" / ",
|
|
1731
1735
|
t(`courses.interval.${s.interval}`) || s.interval
|
|
@@ -1745,7 +1749,7 @@ function SubscriptionsPage({ navTabs, activeNavTab } = {}) {
|
|
|
1745
1749
|
},
|
|
1746
1750
|
stats: [
|
|
1747
1751
|
{ label: t("courses.subs.active") || "Active subscriptions", value: String(active.length) },
|
|
1748
|
-
{ label: t("courses.subs.mrr") || "Monthly recurring revenue", value:
|
|
1752
|
+
{ label: t("courses.subs.mrr") || "Monthly recurring revenue", value: formatCurrency(mrr, currency) }
|
|
1749
1753
|
],
|
|
1750
1754
|
columns,
|
|
1751
1755
|
rows: subs ?? [],
|
|
@@ -1767,7 +1771,7 @@ function FinancialPage() {
|
|
|
1767
1771
|
const currency = summary?.currency ?? "BRL";
|
|
1768
1772
|
const columns = [
|
|
1769
1773
|
{ key: "date", header: t("courses.financial.date") || "Date", render: (p) => /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: formatDate(p.createdAt) }) },
|
|
1770
|
-
{ key: "amount", header: t("courses.financial.amount") || "Amount", align: "right", render: (p) => /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children:
|
|
1774
|
+
{ key: "amount", header: t("courses.financial.amount") || "Amount", align: "right", render: (p) => /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: formatCurrency(p.amount, p.currency) }) },
|
|
1771
1775
|
{ key: "status", header: t("courses.financial.status") || "Status", align: "right", render: (p) => /* @__PURE__ */ jsx(Badge, { variant: p.status === "paid" ? "success" : p.status === "pending" ? "secondary" : "destructive", children: t(`courses.payoutStatus.${p.status}`) || p.status }) }
|
|
1772
1776
|
];
|
|
1773
1777
|
return /* @__PURE__ */ jsxs(
|
|
@@ -1777,10 +1781,10 @@ function FinancialPage() {
|
|
|
1777
1781
|
subtitle: t("courses.financial.subtitle") || "Balance and payouts.",
|
|
1778
1782
|
children: [
|
|
1779
1783
|
/* @__PURE__ */ jsxs(StatGrid, { children: [
|
|
1780
|
-
/* @__PURE__ */ jsx(StatCard, { label: t("courses.financial.available") || "Available balance", value:
|
|
1781
|
-
/* @__PURE__ */ jsx(StatCard, { label: t("courses.financial.pending") || "Pending balance", value:
|
|
1782
|
-
/* @__PURE__ */ jsx(StatCard, { label: t("courses.financial.revenue") || "Total revenue", value:
|
|
1783
|
-
/* @__PURE__ */ jsx(StatCard, { label: t("courses.financial.feeCollected") || "Platform fees", value:
|
|
1784
|
+
/* @__PURE__ */ jsx(StatCard, { label: t("courses.financial.available") || "Available balance", value: formatCurrency(summary?.availableBalance ?? 0, currency) }),
|
|
1785
|
+
/* @__PURE__ */ jsx(StatCard, { label: t("courses.financial.pending") || "Pending balance", value: formatCurrency(summary?.pendingBalance ?? 0, currency) }),
|
|
1786
|
+
/* @__PURE__ */ jsx(StatCard, { label: t("courses.financial.revenue") || "Total revenue", value: formatCurrency(summary?.totalRevenue ?? 0, currency) }),
|
|
1787
|
+
/* @__PURE__ */ jsx(StatCard, { label: t("courses.financial.feeCollected") || "Platform fees", value: formatCurrency(summary?.platformFeeTotal ?? 0, currency), hint: summary ? formatPercent(summary.refundRate, 1) + " " + (t("courses.financial.refundRate") || "refund rate") : void 0 })
|
|
1784
1788
|
] }),
|
|
1785
1789
|
/* @__PURE__ */ jsx("h2", { className: "mb-3 text-sm font-semibold text-foreground", children: t("courses.financial.payouts") || "Payouts" }),
|
|
1786
1790
|
/* @__PURE__ */ jsx(
|
|
@@ -1808,7 +1812,7 @@ function ReportsPage() {
|
|
|
1808
1812
|
const columns = [
|
|
1809
1813
|
{ key: "title", header: t("courses.reports.course") || "Course", render: (r) => /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: r.title }) },
|
|
1810
1814
|
{ key: "sales", header: t("courses.reports.sales") || "Sales", align: "right", render: (r) => /* @__PURE__ */ jsx("span", { children: r.sales }) },
|
|
1811
|
-
{ key: "revenue", header: t("courses.reports.revenue") || "Revenue", align: "right", render: (r) => /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children:
|
|
1815
|
+
{ key: "revenue", header: t("courses.reports.revenue") || "Revenue", align: "right", render: (r) => /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: formatCurrency(r.revenue, r.currency) }) }
|
|
1812
1816
|
];
|
|
1813
1817
|
return /* @__PURE__ */ jsxs(PageContainer, { title: t("courses.reports.title") || "Reports", subtitle: t("courses.reports.subtitle") || "Revenue by course.", children: [
|
|
1814
1818
|
/* @__PURE__ */ jsx("h2", { className: "mb-3 text-sm font-semibold text-foreground", children: t("courses.reports.byCourse") || "Revenue by course" }),
|
|
@@ -1892,7 +1896,7 @@ function useSummary() {
|
|
|
1892
1896
|
function RevenueKpi() {
|
|
1893
1897
|
const t = useTranslation();
|
|
1894
1898
|
const s = useSummary();
|
|
1895
|
-
return /* @__PURE__ */ jsx(KpiCard, { label: t("courses.kpi.revenue") || "Total revenue", icon: "DollarSign", loading: !s, value: s ?
|
|
1899
|
+
return /* @__PURE__ */ jsx(KpiCard, { label: t("courses.kpi.revenue") || "Total revenue", icon: "DollarSign", loading: !s, value: s ? formatCurrency(s.totalRevenue, s.currency) : void 0 });
|
|
1896
1900
|
}
|
|
1897
1901
|
function SalesKpi() {
|
|
1898
1902
|
const t = useTranslation();
|
|
@@ -1902,12 +1906,12 @@ function SalesKpi() {
|
|
|
1902
1906
|
function MrrKpi() {
|
|
1903
1907
|
const t = useTranslation();
|
|
1904
1908
|
const s = useSummary();
|
|
1905
|
-
return /* @__PURE__ */ jsx(KpiCard, { label: t("courses.kpi.mrr") || "MRR", icon: "Repeat", loading: !s, value: s ?
|
|
1909
|
+
return /* @__PURE__ */ jsx(KpiCard, { label: t("courses.kpi.mrr") || "MRR", icon: "Repeat", loading: !s, value: s ? formatCurrency(s.mrr, s.currency) : void 0, sub: s ? `${s.activeSubscriptions} ${t("courses.kpi.activeSubs") || "active"}` : void 0 });
|
|
1906
1910
|
}
|
|
1907
1911
|
function FeesKpi() {
|
|
1908
1912
|
const t = useTranslation();
|
|
1909
1913
|
const s = useSummary();
|
|
1910
|
-
return /* @__PURE__ */ jsx(KpiCard, { label: t("courses.kpi.fees") || "Platform fees", icon: "Percent", loading: !s, value: s ?
|
|
1914
|
+
return /* @__PURE__ */ jsx(KpiCard, { label: t("courses.kpi.fees") || "Platform fees", icon: "Percent", loading: !s, value: s ? formatCurrency(s.platformFeeTotal, s.currency) : void 0 });
|
|
1911
1915
|
}
|
|
1912
1916
|
function createCoursesDashboardWidgets(modules = {}) {
|
|
1913
1917
|
return [
|
|
@@ -1971,6 +1975,7 @@ async function saveMapping(courseId, externalId) {
|
|
|
1971
1975
|
if (error) throw new Error(error.message);
|
|
1972
1976
|
}
|
|
1973
1977
|
var KIWIFY_CONNECTOR_ID = "kiwify";
|
|
1978
|
+
var UNLINKED = "__unlinked__";
|
|
1974
1979
|
var KIWIFY_FIELDS = [
|
|
1975
1980
|
{ key: "accountId", label: "Account ID", type: "text", placeholder: "Kiwify \u2192 Apps \u2192 API" },
|
|
1976
1981
|
{ key: "clientId", label: "Client ID", type: "text", secret: true, placeholder: "Kiwify \u2192 Apps \u2192 API" },
|
|
@@ -2132,15 +2137,17 @@ function KiwifyExtraPanel() {
|
|
|
2132
2137
|
mappings.map((m) => /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-3 px-3 py-2 text-sm", children: [
|
|
2133
2138
|
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: m.title }),
|
|
2134
2139
|
/* @__PURE__ */ jsxs(
|
|
2135
|
-
|
|
2140
|
+
Select,
|
|
2136
2141
|
{
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
onChange: (e) => void handleMap(m.courseId, e.target.value),
|
|
2142
|
+
value: m.externalId ?? UNLINKED,
|
|
2143
|
+
onValueChange: (v) => void handleMap(m.courseId, v === UNLINKED ? "" : v),
|
|
2140
2144
|
children: [
|
|
2141
|
-
/* @__PURE__ */ jsx(
|
|
2142
|
-
|
|
2143
|
-
|
|
2145
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "h-8 w-56 shrink-0 rounded-md border-border px-2 py-0 text-xs shadow-none", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
2146
|
+
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
2147
|
+
/* @__PURE__ */ jsx(SelectItem, { value: UNLINKED, children: "\u2014 sem v\xEDnculo \u2014" }),
|
|
2148
|
+
m.externalId && !(products ?? []).some((p) => p.id === m.externalId) && /* @__PURE__ */ jsx(SelectItem, { value: m.externalId, children: m.externalId }),
|
|
2149
|
+
(products ?? []).map((p) => /* @__PURE__ */ jsx(SelectItem, { value: p.id, children: p.name }, p.id))
|
|
2150
|
+
] })
|
|
2144
2151
|
]
|
|
2145
2152
|
}
|
|
2146
2153
|
)
|