@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 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, SegmentedControl, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Checkbox, ConfirmDialog, KpiCard, Skeleton, HeaderTabs, Card, DataTable } from '@fayz-ai/ui';
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
- "button",
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
- "button",
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: formatMoney(offer.price, offer.currency) })
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
- "button",
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
- "button",
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
- "button",
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
- "button",
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
- "button",
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
- "button",
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
- "button",
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
- "button",
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
- "input",
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: "min-w-0 flex-1 rounded-md bg-transparent px-2 py-1.5 text-foreground outline-none transition-colors placeholder:text-muted-foreground/60 hover:bg-muted/60 focus:bg-background focus:ring-2 focus:ring-ring " + (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: formatMoney(o.platformFee, o.currency) }) },
1571
- { key: "total", header: t("courses.sales.amount") || "Valor", align: "right", render: (o) => /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: formatMoney(o.total, o.currency) }) }
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: formatMoney(revenue, currency) }
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("button", { className: "text-sm text-primary underline", onClick: back, children: t("common.back") || "Voltar" })
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: formatMoney(m.amount, cur),
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: formatMoney(order.total, cur),
1683
- total: formatMoney(order.total, cur)
1686
+ unitPrice: formatCurrency(order.total, cur),
1687
+ total: formatCurrency(order.total, cur)
1684
1688
  }],
1685
1689
  totals: [
1686
- { label: "Total", value: formatMoney(order.total, cur), strong: true },
1687
- ...order.platformFee > 0 ? [{ label: "Taxa", value: `\u2212 ${formatMoney(order.platformFee, cur)}`, muted: true }] : [],
1688
- { label: "L\xEDquido", value: formatMoney(order.netValue, cur) }
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: formatMoney(order.paidAmount, cur) },
1694
- { label: "Saldo", value: formatMoney(order.balance, cur), strong: order.balance > 0 }
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
- formatMoney(s.netValue, s.currency),
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: formatMoney(mrr, currency) }
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: formatMoney(p.amount, p.currency) }) },
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: formatMoney(summary?.availableBalance ?? 0, currency) }),
1781
- /* @__PURE__ */ jsx(StatCard, { label: t("courses.financial.pending") || "Pending balance", value: formatMoney(summary?.pendingBalance ?? 0, currency) }),
1782
- /* @__PURE__ */ jsx(StatCard, { label: t("courses.financial.revenue") || "Total revenue", value: formatMoney(summary?.totalRevenue ?? 0, currency) }),
1783
- /* @__PURE__ */ jsx(StatCard, { label: t("courses.financial.feeCollected") || "Platform fees", value: formatMoney(summary?.platformFeeTotal ?? 0, currency), hint: summary ? formatPercent(summary.refundRate, 1) + " " + (t("courses.financial.refundRate") || "refund rate") : void 0 })
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: formatMoney(r.revenue, r.currency) }) }
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 ? formatMoney(s.totalRevenue, s.currency) : void 0 });
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 ? formatMoney(s.mrr, s.currency) : void 0, sub: s ? `${s.activeSubscriptions} ${t("courses.kpi.activeSubs") || "active"}` : void 0 });
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 ? formatMoney(s.platformFeeTotal, s.currency) : void 0 });
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
- "select",
2140
+ Select,
2136
2141
  {
2137
- className: "h-8 w-56 shrink-0 rounded-md border border-border bg-background px-2 text-xs",
2138
- value: m.externalId ?? "",
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("option", { value: "", children: "\u2014 sem v\xEDnculo \u2014" }),
2142
- m.externalId && !(products ?? []).some((p) => p.id === m.externalId) && /* @__PURE__ */ jsx("option", { value: m.externalId, children: m.externalId }),
2143
- (products ?? []).map((p) => /* @__PURE__ */ jsx("option", { value: p.id, children: p.name }, p.id))
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
  )