@fayz-ai/plugin-courses 0.9.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.md +3 -3
  2. package/dist/components/CommerceUI.d.ts +19 -2
  3. package/dist/components/CommerceUI.d.ts.map +1 -1
  4. package/dist/components/dashboardWidgets.d.ts.map +1 -0
  5. package/dist/index.d.ts +26 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +342 -253
  8. package/dist/index.js.map +1 -1
  9. package/dist/lib/nav.d.ts.map +1 -0
  10. package/dist/locales/en.d.ts.map +1 -1
  11. package/dist/locales/pt-BR.d.ts.map +1 -1
  12. package/dist/public/createPublicCheckoutPlugin.d.ts +1 -1
  13. package/dist/public/createPublicCheckoutPlugin.d.ts.map +1 -1
  14. package/dist/public/{data.d.ts → data/index.d.ts} +1 -1
  15. package/dist/public/data/index.d.ts.map +1 -0
  16. package/dist/public/{data.supabase.d.ts → data/supabase.d.ts} +2 -2
  17. package/dist/public/data/supabase.d.ts.map +1 -0
  18. package/dist/public/index.d.ts +4 -4
  19. package/dist/public/index.d.ts.map +1 -1
  20. package/dist/public/index.js +353 -0
  21. package/dist/public/index.js.map +1 -0
  22. package/dist/public/{CheckoutScreen.d.ts → views/CheckoutScreen.d.ts} +1 -1
  23. package/dist/public/views/CheckoutScreen.d.ts.map +1 -0
  24. package/dist/views/CourseEditorPage.d.ts.map +1 -0
  25. package/dist/views/CourseOrderDetailView.d.ts +4 -3
  26. package/dist/views/CourseOrderDetailView.d.ts.map +1 -1
  27. package/dist/{pages → views}/CoursesListPage.d.ts.map +1 -1
  28. package/dist/{pages → views}/FinancialPage.d.ts.map +1 -1
  29. package/dist/{pages → views}/ReportsPage.d.ts.map +1 -1
  30. package/dist/views/SalesPage.d.ts +19 -0
  31. package/dist/views/SalesPage.d.ts.map +1 -0
  32. package/dist/views/SubscriptionsPage.d.ts +7 -0
  33. package/dist/views/SubscriptionsPage.d.ts.map +1 -0
  34. package/package.json +6 -9
  35. package/dist/dashboardWidgets.d.ts.map +0 -1
  36. package/dist/nav.d.ts.map +0 -1
  37. package/dist/pages/CourseEditorPage.d.ts.map +0 -1
  38. package/dist/pages/SalesPage.d.ts +0 -6
  39. package/dist/pages/SalesPage.d.ts.map +0 -1
  40. package/dist/pages/SubscriptionsPage.d.ts +0 -6
  41. package/dist/pages/SubscriptionsPage.d.ts.map +0 -1
  42. package/dist/public/CheckoutScreen.d.ts.map +0 -1
  43. package/dist/public/data.d.ts.map +0 -1
  44. package/dist/public/data.supabase.d.ts.map +0 -1
  45. package/src/components/CommerceUI.tsx +0 -153
  46. package/src/connectors/stripe.ts +0 -34
  47. package/src/dashboardWidgets.tsx +0 -64
  48. package/src/index.ts +0 -150
  49. package/src/lib/format.ts +0 -22
  50. package/src/locales/en.ts +0 -203
  51. package/src/locales/index.ts +0 -7
  52. package/src/locales/pt-BR.ts +0 -203
  53. package/src/nav.ts +0 -9
  54. package/src/pages/CourseEditorPage.tsx +0 -1122
  55. package/src/pages/CoursesListPage.tsx +0 -137
  56. package/src/pages/FinancialPage.tsx +0 -81
  57. package/src/pages/ReportsPage.tsx +0 -59
  58. package/src/pages/SalesPage.tsx +0 -86
  59. package/src/pages/SubscriptionsPage.tsx +0 -56
  60. package/src/public/CheckoutScreen.tsx +0 -279
  61. package/src/public/createPublicCheckoutPlugin.tsx +0 -88
  62. package/src/public/data.supabase.ts +0 -63
  63. package/src/public/data.ts +0 -89
  64. package/src/public/index.ts +0 -26
  65. package/src/settings/CoursesSettings.tsx +0 -55
  66. package/src/views/CourseOrderDetailView.tsx +0 -132
  67. /package/dist/{dashboardWidgets.d.ts → components/dashboardWidgets.d.ts} +0 -0
  68. /package/dist/{nav.d.ts → lib/nav.d.ts} +0 -0
  69. /package/dist/{pages → views}/CourseEditorPage.d.ts +0 -0
  70. /package/dist/{pages → views}/CoursesListPage.d.ts +0 -0
  71. /package/dist/{pages → views}/FinancialPage.d.ts +0 -0
  72. /package/dist/{pages → views}/ReportsPage.d.ts +0 -0
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
+ import * as React3 from 'react';
1
2
  import { hashRouterAdapter, registerPluginFactory, registerTranslations, useTranslation, getSupabaseClientOptional } from '@fayz-ai/core';
2
3
  import { setCoursesTenantResolver, getCoursesProviderOptional, setCoursesProvider, createMockCoursesProvider, getCoursesProvider } from '@fayz-ai/courses';
3
- import { useOrganizationStore, useLimitGuard, usePermissionOptional, PermissionGate, OrderListView, OrderDetailSkeleton, PersonLink, OrderDetailView, invalidateLimit, PluginSettingsPanel, SettingsGroup, SelectRow, ToggleRow } from '@fayz-ai/saas';
4
- import * as React3 from 'react';
5
- import { defineKpiWidget, useModuleLayout, PageHeaderActions, Button, Badge, useSaveBar, toast, Tabs, TabsList, TabsTrigger, TabsContent, Input, SegmentedControl, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Checkbox, ConfirmDialog, KpiCard, Skeleton, Card, DataTable } from '@fayz-ai/ui';
4
+ import { useOrganizationStore, useLimitGuard, usePermissionOptional, PermissionGate, OrderListView, OrderDetailSkeleton, PersonLink, OrderDetailView, invalidateLimit, PluginSettingsPanel, SettingsGroup, SelectRow, ToggleRow } from '@fayz-ai/admin';
5
+ import { defineKpiWidget, useModuleLayout, PageHeaderActions, Button, Badge, useSaveBar, PageHeader, toast, Separator, Tabs, TabsList, TabsTrigger, TabsContent, Input, SegmentedControl, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Checkbox, ConfirmDialog, KpiCard, Skeleton, HeaderTabs, Card, DataTable } from '@fayz-ai/ui';
6
6
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
7
- import { ArrowLeft, Users, ShoppingCart, Layers, Tag, ImagePlus, Loader2, UploadCloud, Info, Plus, Trash2, Clock, Lock, Check, GripVertical, ChevronDown, Video, FileText, PlayCircle } from 'lucide-react';
7
+ import { ImagePlus, Users, ShoppingCart, Layers, Tag, Loader2, UploadCloud, Info, Plus, Trash2, Clock, Lock, Check, GripVertical, ChevronDown, Video, FileText, PlayCircle } from 'lucide-react';
8
8
  import { useSensors, useSensor, PointerSensor, KeyboardSensor, DndContext, closestCenter } from '@dnd-kit/core';
9
9
  import { sortableKeyboardCoordinates, SortableContext, verticalListSortingStrategy, arrayMove, useSortable } from '@dnd-kit/sortable';
10
10
  import { CSS } from '@dnd-kit/utilities';
@@ -23,7 +23,6 @@ var en = {
23
23
  "courses.status.draft": "Draft",
24
24
  "courses.status.published": "Published",
25
25
  "courses.status.archived": "Archived",
26
- "courses.editor.back": "Back to courses",
27
26
  "courses.editor.details": "Details",
28
27
  "courses.editor.curriculum": "Curriculum",
29
28
  "courses.editor.fieldTitle": "Title",
@@ -135,6 +134,9 @@ var en = {
135
134
  "courses.offers.checkoutFlagsNote": "Apply once the public checkout ships (coming soon).",
136
135
  // Sales
137
136
  "courses.sales.title": "Sales",
137
+ "courses.order.title": "Order",
138
+ "courses.order.notFound": "Order not found",
139
+ "courses.order.noInstallments": "No installments.",
138
140
  "courses.sales.subtitle": "Every transaction across your courses.",
139
141
  "courses.sales.date": "Date",
140
142
  "courses.sales.customer": "Customer",
@@ -228,7 +230,6 @@ var ptBR = {
228
230
  "courses.status.draft": "Rascunho",
229
231
  "courses.status.published": "Publicado",
230
232
  "courses.status.archived": "Arquivado",
231
- "courses.editor.back": "Voltar aos cursos",
232
233
  "courses.editor.details": "Detalhes",
233
234
  "courses.editor.curriculum": "Conte\xFAdo",
234
235
  "courses.editor.fieldTitle": "T\xEDtulo",
@@ -340,6 +341,9 @@ var ptBR = {
340
341
  "courses.offers.checkoutFlagsNote": "Aplicam-se quando o checkout p\xFAblico entrar no ar (em breve).",
341
342
  // Sales
342
343
  "courses.sales.title": "Vendas",
344
+ "courses.order.title": "Pedido",
345
+ "courses.order.notFound": "Pedido n\xE3o encontrado",
346
+ "courses.order.noInstallments": "Sem parcelas.",
343
347
  "courses.sales.subtitle": "Todas as transa\xE7\xF5es dos seus cursos.",
344
348
  "courses.sales.date": "Data",
345
349
  "courses.sales.customer": "Cliente",
@@ -530,9 +534,24 @@ function formatDate(iso) {
530
534
  function formatPercent(fraction, digits = 0) {
531
535
  return `${(fraction * 100).toFixed(digits)}%`;
532
536
  }
533
- function PageContainer({ title, subtitle, actions, children }) {
534
- return /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-6xl p-6 md:p-8", children: [
535
- /* @__PURE__ */ jsxs("div", { className: "mb-6 flex items-start justify-between gap-4", children: [
537
+ function PageContainer({ title, subtitle, actions, navTabs, activeNavTab, onNavTabChange, children }) {
538
+ return /* @__PURE__ */ jsxs("div", { children: [
539
+ navTabs && navTabs.length > 0 ? (
540
+ // Mesma linha de CrudListView/OrderListView: abas à esquerda, ações à
541
+ // direita. Com abas o <h1> sai: a aba ativa já nomeia a tela, e a barra
542
+ // de topo nomeia o grupo.
543
+ /* @__PURE__ */ jsxs("div", { className: "mb-6 flex flex-wrap items-center justify-between gap-3", children: [
544
+ /* @__PURE__ */ jsx(
545
+ HeaderTabs,
546
+ {
547
+ tabs: navTabs,
548
+ value: activeNavTab ?? navTabs[0].value,
549
+ onChange: (v) => onNavTabChange?.(v)
550
+ }
551
+ ),
552
+ actions
553
+ ] })
554
+ ) : /* @__PURE__ */ jsxs("div", { className: "mb-6 flex items-start justify-between gap-4", children: [
536
555
  /* @__PURE__ */ jsxs("div", { children: [
537
556
  /* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold text-foreground", children: title }),
538
557
  subtitle && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: subtitle })
@@ -684,7 +703,18 @@ function CourseEditorPage({ id }) {
684
703
  },
685
704
  saveLabel: t("courses.editor.save") || "Salvar"
686
705
  });
687
- if (loading || !course) return /* @__PURE__ */ jsx(EditorSkeleton, {});
706
+ const header = /* @__PURE__ */ jsx(
707
+ PageHeader,
708
+ {
709
+ title: course?.title ?? "",
710
+ parentLabel: t("courses.title") || "Courses",
711
+ onBack: () => navigateTo("/courses")
712
+ }
713
+ );
714
+ if (loading || !course) return /* @__PURE__ */ jsxs(Fragment, { children: [
715
+ header,
716
+ /* @__PURE__ */ jsx(EditorSkeleton, {})
717
+ ] });
688
718
  function patchCourse(patch) {
689
719
  setCourse((c) => c ? { ...c, ...patch } : c);
690
720
  }
@@ -844,245 +874,246 @@ function CourseEditorPage({ id }) {
844
874
  value: d,
845
875
  label: t(`courses.editor.delivery.${d}`) || d
846
876
  }));
847
- return /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-4xl p-6 md:p-8", children: [
848
- /* @__PURE__ */ jsxs(
849
- "button",
850
- {
851
- onClick: () => navigateTo("/courses"),
852
- className: "mb-4 inline-flex items-center gap-1.5 text-sm text-muted-foreground transition-colors hover:text-foreground",
853
- children: [
854
- /* @__PURE__ */ jsx(ArrowLeft, { className: "h-4 w-4" }),
855
- " ",
856
- t("courses.editor.back") || "Back to courses"
857
- ]
858
- }
859
- ),
860
- /* @__PURE__ */ jsxs("div", { className: "mb-6 flex flex-wrap items-center gap-x-3 gap-y-2", children: [
861
- /* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold text-foreground", children: course.title || (t("courses.editor.untitledCourse") || "Untitled course") }),
862
- /* @__PURE__ */ jsx(Badge, { variant: course.status === "published" ? "success" : course.status === "archived" ? "secondary" : "outline", children: t(`courses.status.${course.status}`) || course.status }),
863
- /* @__PURE__ */ jsx(Badge, { variant: "outline", children: t(`courses.editor.delivery.${course.deliveryType}`) || course.deliveryType })
864
- ] }),
865
- /* @__PURE__ */ jsxs(Tabs, { value: tab, onValueChange: (v) => setTab(v), children: [
866
- /* @__PURE__ */ jsx("div", { className: "-mx-1 mb-6 overflow-x-auto px-1 pb-1", children: /* @__PURE__ */ jsx(TabsList, { className: "h-auto flex-nowrap", children: TABS.map((tk) => {
867
- const Icon = TAB_ICON[tk];
868
- return /* @__PURE__ */ jsxs(TabsTrigger, { value: tk, className: "gap-1.5 whitespace-nowrap py-1.5", children: [
869
- /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5" }),
870
- t(`courses.editor.tab.${tk}`) || tk
871
- ] }, tk);
872
- }) }) }),
873
- /* @__PURE__ */ jsxs(TabsContent, { value: "details", className: "space-y-5", children: [
874
- /* @__PURE__ */ jsx(SectionCard, { title: t("courses.editor.sectionBasics") || "Basics", children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
875
- /* @__PURE__ */ jsx(Field, { label: t("courses.editor.fieldTitle") || "Title", children: /* @__PURE__ */ jsx(Input, { value: course.title, onChange: (e) => patchCourse({ title: e.target.value }) }) }),
876
- /* @__PURE__ */ jsx(Field, { label: t("courses.editor.fieldSubtitle") || "Subtitle", children: /* @__PURE__ */ jsx(Input, { value: course.subtitle ?? "", onChange: (e) => patchCourse({ subtitle: e.target.value }) }) }),
877
- /* @__PURE__ */ jsx(Field, { label: t("courses.editor.fieldDescription") || "Description", children: /* @__PURE__ */ jsx(
878
- Textarea,
879
- {
880
- value: course.description ?? "",
881
- onChange: (e) => patchCourse({ description: e.target.value }),
882
- placeholder: t("courses.editor.descriptionPlaceholder") || "What will students learn?"
883
- }
884
- ) }),
885
- /* @__PURE__ */ jsx(Field, { label: t("courses.editor.fieldDelivery") || "Format", hint: t("courses.editor.deliveryHint") || "How this course is delivered.", children: /* @__PURE__ */ jsx(
886
- SegmentedControl,
887
- {
888
- size: "md",
889
- options: deliveryOptions,
890
- value: course.deliveryType,
891
- onChange: (v) => patchCourse({ deliveryType: v }),
892
- "aria-label": t("courses.editor.fieldDelivery") || "Format"
893
- }
894
- ) })
895
- ] }) }),
896
- /* @__PURE__ */ jsx(SectionCard, { title: t("courses.editor.sectionThumbnail") || "Cover image", description: t("courses.editor.thumbnailHint") || "Shown on the storefront and in listings. 1280\xD7720 recommended.", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-start", children: [
897
- /* @__PURE__ */ jsx("div", { className: "aspect-video w-full shrink-0 overflow-hidden rounded-lg border border-border bg-muted sm:w-56", children: course.thumbnailUrl ? (
898
- // eslint-disable-next-line @next/next/no-img-element
899
- /* @__PURE__ */ jsx("img", { src: course.thumbnailUrl, alt: "", className: "h-full w-full object-cover" })
900
- ) : /* @__PURE__ */ jsxs("div", { className: "flex h-full w-full flex-col items-center justify-center gap-1 text-muted-foreground", children: [
901
- /* @__PURE__ */ jsx(ImagePlus, { className: "h-6 w-6" }),
902
- /* @__PURE__ */ jsx("span", { className: "text-xs", children: t("courses.editor.thumbnailEmpty") || "No image" })
877
+ return (
878
+ // Sem padding e sem max-width próprios: a shell já envolve toda página em
879
+ // `p-3 md:p-6` (AdminShell). Reaplicar `p-6 md:p-8` aqui empurrava o editor
880
+ // 32px à direita do título na barra de topo e de todas as outras telas, e o
881
+ // `mx-auto max-w-4xl` ainda o centralizava enquanto a barra ficava à esquerda.
882
+ /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
883
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-5", children: [
884
+ /* @__PURE__ */ jsx("div", { className: "aspect-video w-36 shrink-0 overflow-hidden rounded-2xl border border-border bg-muted shadow-sm", children: course.thumbnailUrl ? (
885
+ // eslint-disable-next-line @next/next/no-img-element
886
+ /* @__PURE__ */ jsx("img", { src: course.thumbnailUrl, alt: "", className: "h-full w-full object-cover" })
887
+ ) : /* @__PURE__ */ jsx("div", { className: "flex h-full w-full items-center justify-center text-muted-foreground", children: /* @__PURE__ */ jsx(ImagePlus, { className: "h-6 w-6" }) }) }),
888
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-2", children: [
889
+ /* @__PURE__ */ jsx("h1", { className: "truncate text-2xl font-bold text-foreground", children: course.title || (t("courses.editor.untitledCourse") || "Untitled course") }),
890
+ /* @__PURE__ */ jsx(Badge, { variant: course.status === "published" ? "success" : course.status === "archived" ? "secondary" : "outline", children: t(`courses.status.${course.status}`) || course.status }),
891
+ /* @__PURE__ */ jsx(Badge, { variant: "outline", children: t(`courses.editor.delivery.${course.deliveryType}`) || course.deliveryType })
892
+ ] }) })
893
+ ] }),
894
+ /* @__PURE__ */ jsx(Separator, {}),
895
+ /* @__PURE__ */ jsxs(Tabs, { value: tab, onValueChange: (v) => setTab(v), children: [
896
+ /* @__PURE__ */ jsx(TabsList, { className: "max-w-full justify-start overflow-x-auto scrollbar-none", children: TABS.map((tk) => {
897
+ const Icon = TAB_ICON[tk];
898
+ return /* @__PURE__ */ jsxs(TabsTrigger, { value: tk, className: "gap-1.5", children: [
899
+ /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5" }),
900
+ t(`courses.editor.tab.${tk}`) || tk
901
+ ] }, tk);
902
+ }) }),
903
+ /* @__PURE__ */ jsxs(TabsContent, { value: "details", className: "mt-4 space-y-5", children: [
904
+ /* @__PURE__ */ jsx(SectionCard, { title: t("courses.editor.sectionBasics") || "Basics", children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
905
+ /* @__PURE__ */ jsx(Field, { label: t("courses.editor.fieldTitle") || "Title", children: /* @__PURE__ */ jsx(Input, { value: course.title, onChange: (e) => patchCourse({ title: e.target.value }) }) }),
906
+ /* @__PURE__ */ jsx(Field, { label: t("courses.editor.fieldSubtitle") || "Subtitle", children: /* @__PURE__ */ jsx(Input, { value: course.subtitle ?? "", onChange: (e) => patchCourse({ subtitle: e.target.value }) }) }),
907
+ /* @__PURE__ */ jsx(Field, { label: t("courses.editor.fieldDescription") || "Description", children: /* @__PURE__ */ jsx(
908
+ Textarea,
909
+ {
910
+ value: course.description ?? "",
911
+ onChange: (e) => patchCourse({ description: e.target.value }),
912
+ placeholder: t("courses.editor.descriptionPlaceholder") || "What will students learn?"
913
+ }
914
+ ) }),
915
+ /* @__PURE__ */ jsx(Field, { label: t("courses.editor.fieldDelivery") || "Format", hint: t("courses.editor.deliveryHint") || "How this course is delivered.", children: /* @__PURE__ */ jsx(
916
+ SegmentedControl,
917
+ {
918
+ size: "md",
919
+ options: deliveryOptions,
920
+ value: course.deliveryType,
921
+ onChange: (v) => patchCourse({ deliveryType: v }),
922
+ "aria-label": t("courses.editor.fieldDelivery") || "Format"
923
+ }
924
+ ) })
903
925
  ] }) }),
904
- /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-3", children: [
905
- /* @__PURE__ */ jsx("input", { ref: fileRef, type: "file", accept: "image/*", className: "hidden", onChange: onPickThumbnail }),
906
- /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", disabled: uploading, onClick: () => fileRef.current?.click(), children: [
907
- uploading ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(UploadCloud, { className: "h-4 w-4" }),
908
- uploading ? t("courses.editor.uploading") || "Uploading\u2026" : t("courses.editor.thumbnailUpload") || "Upload image"
909
- ] }),
910
- /* @__PURE__ */ jsx(Field, { label: t("courses.editor.thumbnailUrl") || "Or paste an image URL", children: /* @__PURE__ */ jsx(
926
+ /* @__PURE__ */ jsx(SectionCard, { title: t("courses.editor.sectionThumbnail") || "Cover image", description: t("courses.editor.thumbnailHint") || "Shown on the storefront and in listings. 1280\xD7720 recommended.", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-start", children: [
927
+ /* @__PURE__ */ jsx("div", { className: "aspect-video w-full shrink-0 overflow-hidden rounded-lg border border-border bg-muted sm:w-56", children: course.thumbnailUrl ? (
928
+ // eslint-disable-next-line @next/next/no-img-element
929
+ /* @__PURE__ */ jsx("img", { src: course.thumbnailUrl, alt: "", className: "h-full w-full object-cover" })
930
+ ) : /* @__PURE__ */ jsxs("div", { className: "flex h-full w-full flex-col items-center justify-center gap-1 text-muted-foreground", children: [
931
+ /* @__PURE__ */ jsx(ImagePlus, { className: "h-6 w-6" }),
932
+ /* @__PURE__ */ jsx("span", { className: "text-xs", children: t("courses.editor.thumbnailEmpty") || "No image" })
933
+ ] }) }),
934
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-3", children: [
935
+ /* @__PURE__ */ jsx("input", { ref: fileRef, type: "file", accept: "image/*", className: "hidden", onChange: onPickThumbnail }),
936
+ /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", disabled: uploading, onClick: () => fileRef.current?.click(), children: [
937
+ uploading ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(UploadCloud, { className: "h-4 w-4" }),
938
+ uploading ? t("courses.editor.uploading") || "Uploading\u2026" : t("courses.editor.thumbnailUpload") || "Upload image"
939
+ ] }),
940
+ /* @__PURE__ */ jsx(Field, { label: t("courses.editor.thumbnailUrl") || "Or paste an image URL", children: /* @__PURE__ */ jsx(
941
+ Input,
942
+ {
943
+ value: course.thumbnailUrl ?? "",
944
+ placeholder: "https://\u2026",
945
+ onChange: (e) => patchCourse({ thumbnailUrl: e.target.value || null })
946
+ }
947
+ ) })
948
+ ] })
949
+ ] }) }),
950
+ /* @__PURE__ */ jsx(SectionCard, { title: t("courses.editor.sectionPricing") || "Pricing & status", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", children: [
951
+ /* @__PURE__ */ jsx(Field, { label: t("courses.editor.fieldPrice") || "Price", hint: course.currency, children: /* @__PURE__ */ jsx(
911
952
  Input,
912
953
  {
913
- value: course.thumbnailUrl ?? "",
914
- placeholder: "https://\u2026",
915
- onChange: (e) => patchCourse({ thumbnailUrl: e.target.value || null })
954
+ type: "number",
955
+ min: 0,
956
+ step: "0.01",
957
+ inputMode: "decimal",
958
+ value: String(course.price),
959
+ onChange: (e) => patchCourse({ price: Number(e.target.value) || 0 })
916
960
  }
917
- ) })
918
- ] })
919
- ] }) }),
920
- /* @__PURE__ */ jsx(SectionCard, { title: t("courses.editor.sectionPricing") || "Pricing & status", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", children: [
921
- /* @__PURE__ */ jsx(Field, { label: t("courses.editor.fieldPrice") || "Price", hint: course.currency, children: /* @__PURE__ */ jsx(
922
- Input,
923
- {
924
- type: "number",
925
- min: 0,
926
- step: "0.01",
927
- inputMode: "decimal",
928
- value: String(course.price),
929
- onChange: (e) => patchCourse({ price: Number(e.target.value) || 0 })
930
- }
931
- ) }),
932
- /* @__PURE__ */ jsx(Field, { label: t("courses.editor.fieldStatus") || "Status", children: /* @__PURE__ */ jsxs(Select, { value: course.status, onValueChange: (v) => patchCourse({ status: v }), children: [
933
- /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
934
- /* @__PURE__ */ jsx(SelectContent, { children: STATUSES.map((s) => /* @__PURE__ */ jsx(SelectItem, { value: s, children: t(`courses.status.${s}`) || s }, s)) })
961
+ ) }),
962
+ /* @__PURE__ */ jsx(Field, { label: t("courses.editor.fieldStatus") || "Status", children: /* @__PURE__ */ jsxs(Select, { value: course.status, onValueChange: (v) => patchCourse({ status: v }), children: [
963
+ /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
964
+ /* @__PURE__ */ jsx(SelectContent, { children: STATUSES.map((s) => /* @__PURE__ */ jsx(SelectItem, { value: s, children: t(`courses.status.${s}`) || s }, s)) })
965
+ ] }) })
935
966
  ] }) })
936
- ] }) })
937
- ] }),
938
- /* @__PURE__ */ jsx(TabsContent, { value: "curriculum", className: "space-y-4", children: /* @__PURE__ */ jsx(
939
- CurriculumBuilder,
940
- {
941
- t,
942
- modules,
943
- lessons,
944
- autoFocusId,
945
- onAutoFocused: () => setAutoFocusId(null),
946
- onAddModule: addModule,
947
- onRenameModule: renameModule,
948
- onAddLesson: addLesson,
949
- onPatchLesson: patchLesson,
950
- onRequestDelete: (kind, delId) => setPendingDelete({ kind, id: delId }),
951
- onReorderModules: reorderModules,
952
- onReorderLessons: reorderLessons
953
- }
954
- ) }),
955
- /* @__PURE__ */ jsxs(TabsContent, { value: "offers", className: "space-y-4", children: [
956
- /* @__PURE__ */ jsx(InfoNote, { icon: /* @__PURE__ */ jsx(Info, { className: "h-4 w-4" }), children: t("courses.offers.help") || "Price points for this course. Sales run through orders and the Financeiro module." }),
957
- offers.length === 0 ? /* @__PURE__ */ jsx(
958
- EmptyState,
967
+ ] }),
968
+ /* @__PURE__ */ jsx(TabsContent, { value: "curriculum", className: "mt-4 space-y-4", children: /* @__PURE__ */ jsx(
969
+ CurriculumBuilder,
959
970
  {
960
- icon: /* @__PURE__ */ jsx(Tag, { className: "h-5 w-5" }),
961
- title: t("courses.offers.empty") || "No offers yet",
962
- description: t("courses.offers.emptyHint") || "Create an offer to sell this course.",
963
- action: /* @__PURE__ */ jsxs(Button, { onClick: addOffer, children: [
964
- /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }),
965
- t("courses.offers.firstOffer") || "Create first offer"
966
- ] })
971
+ t,
972
+ modules,
973
+ lessons,
974
+ autoFocusId,
975
+ onAutoFocused: () => setAutoFocusId(null),
976
+ onAddModule: addModule,
977
+ onRenameModule: renameModule,
978
+ onAddLesson: addLesson,
979
+ onPatchLesson: patchLesson,
980
+ onRequestDelete: (kind, delId) => setPendingDelete({ kind, id: delId }),
981
+ onReorderModules: reorderModules,
982
+ onReorderLessons: reorderLessons
967
983
  }
968
- ) : /* @__PURE__ */ jsxs(Fragment, { children: [
969
- offers.map((offer) => /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-border bg-card p-4 shadow-sm md:p-5", children: [
970
- /* @__PURE__ */ jsxs("div", { className: "mb-4 flex items-start justify-between gap-3", children: [
971
- /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "gap-1", children: offer.kind === "subscription" ? t("courses.offers.subscription") || "Subscription" : t("courses.offers.oneTime") || "One-time" }),
972
- /* @__PURE__ */ jsx(
973
- "button",
974
- {
975
- onClick: () => deleteOffer(offer.id),
976
- 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",
977
- "aria-label": t("courses.offers.remove") || "Remove",
978
- children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" })
979
- }
980
- )
981
- ] }),
982
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-[1fr_140px_160px]", children: [
983
- /* @__PURE__ */ jsx(Field, { label: t("courses.offers.name") || "Offer name", children: /* @__PURE__ */ jsx(Input, { defaultValue: offer.name, onBlur: (e) => patchOffer(offer.id, { name: e.target.value }) }) }),
984
- /* @__PURE__ */ jsx(Field, { label: t("courses.offers.price") || "Price", hint: offer.currency, children: /* @__PURE__ */ jsx(Input, { type: "number", min: 0, step: "0.01", inputMode: "decimal", defaultValue: String(offer.price), onBlur: (e) => patchOffer(offer.id, { price: Number(e.target.value) || 0 }) }) }),
985
- /* @__PURE__ */ jsx(Field, { label: t("courses.offers.kind") || "Type", children: /* @__PURE__ */ jsxs(
984
+ ) }),
985
+ /* @__PURE__ */ jsxs(TabsContent, { value: "offers", className: "mt-4 space-y-4", children: [
986
+ /* @__PURE__ */ jsx(InfoNote, { icon: /* @__PURE__ */ jsx(Info, { className: "h-4 w-4" }), children: t("courses.offers.help") || "Price points for this course. Sales run through orders and the Financeiro module." }),
987
+ offers.length === 0 ? /* @__PURE__ */ jsx(
988
+ EmptyState,
989
+ {
990
+ icon: /* @__PURE__ */ jsx(Tag, { className: "h-5 w-5" }),
991
+ title: t("courses.offers.empty") || "No offers yet",
992
+ description: t("courses.offers.emptyHint") || "Create an offer to sell this course.",
993
+ action: /* @__PURE__ */ jsxs(Button, { onClick: addOffer, children: [
994
+ /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }),
995
+ t("courses.offers.firstOffer") || "Create first offer"
996
+ ] })
997
+ }
998
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
999
+ offers.map((offer) => /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-border bg-card p-4 shadow-sm md:p-5", children: [
1000
+ /* @__PURE__ */ jsxs("div", { className: "mb-4 flex items-start justify-between gap-3", children: [
1001
+ /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "gap-1", children: offer.kind === "subscription" ? t("courses.offers.subscription") || "Subscription" : t("courses.offers.oneTime") || "One-time" }),
1002
+ /* @__PURE__ */ jsx(
1003
+ "button",
1004
+ {
1005
+ onClick: () => deleteOffer(offer.id),
1006
+ 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",
1007
+ "aria-label": t("courses.offers.remove") || "Remove",
1008
+ children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" })
1009
+ }
1010
+ )
1011
+ ] }),
1012
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-[1fr_140px_160px]", children: [
1013
+ /* @__PURE__ */ jsx(Field, { label: t("courses.offers.name") || "Offer name", children: /* @__PURE__ */ jsx(Input, { defaultValue: offer.name, onBlur: (e) => patchOffer(offer.id, { name: e.target.value }) }) }),
1014
+ /* @__PURE__ */ jsx(Field, { label: t("courses.offers.price") || "Price", hint: offer.currency, children: /* @__PURE__ */ jsx(Input, { type: "number", min: 0, step: "0.01", inputMode: "decimal", defaultValue: String(offer.price), onBlur: (e) => patchOffer(offer.id, { price: Number(e.target.value) || 0 }) }) }),
1015
+ /* @__PURE__ */ jsx(Field, { label: t("courses.offers.kind") || "Type", children: /* @__PURE__ */ jsxs(
1016
+ Select,
1017
+ {
1018
+ value: offer.kind,
1019
+ onValueChange: (v) => patchOffer(offer.id, { kind: v, recurringInterval: v === "subscription" ? offer.recurringInterval ?? "month" : null }),
1020
+ children: [
1021
+ /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
1022
+ /* @__PURE__ */ jsxs(SelectContent, { children: [
1023
+ /* @__PURE__ */ jsx(SelectItem, { value: "one_time", children: t("courses.offers.oneTime") || "One-time" }),
1024
+ /* @__PURE__ */ jsx(SelectItem, { value: "subscription", children: t("courses.offers.subscription") || "Subscription" })
1025
+ ] })
1026
+ ]
1027
+ }
1028
+ ) })
1029
+ ] }),
1030
+ offer.kind === "subscription" && /* @__PURE__ */ jsx("div", { className: "mt-3 max-w-[200px]", children: /* @__PURE__ */ jsx(Field, { label: t("courses.offers.interval") || "Billing interval", children: /* @__PURE__ */ jsxs(
986
1031
  Select,
987
1032
  {
988
- value: offer.kind,
989
- onValueChange: (v) => patchOffer(offer.id, { kind: v, recurringInterval: v === "subscription" ? offer.recurringInterval ?? "month" : null }),
1033
+ value: offer.recurringInterval ?? "month",
1034
+ onValueChange: (v) => patchOffer(offer.id, { recurringInterval: v }),
990
1035
  children: [
991
1036
  /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
992
1037
  /* @__PURE__ */ jsxs(SelectContent, { children: [
993
- /* @__PURE__ */ jsx(SelectItem, { value: "one_time", children: t("courses.offers.oneTime") || "One-time" }),
994
- /* @__PURE__ */ jsx(SelectItem, { value: "subscription", children: t("courses.offers.subscription") || "Subscription" })
1038
+ /* @__PURE__ */ jsx(SelectItem, { value: "month", children: t("courses.interval.month") || "month" }),
1039
+ /* @__PURE__ */ jsx(SelectItem, { value: "year", children: t("courses.interval.year") || "year" })
995
1040
  ] })
996
1041
  ]
997
1042
  }
998
- ) })
999
- ] }),
1000
- offer.kind === "subscription" && /* @__PURE__ */ jsx("div", { className: "mt-3 max-w-[200px]", children: /* @__PURE__ */ jsx(Field, { label: t("courses.offers.interval") || "Billing interval", children: /* @__PURE__ */ jsxs(
1001
- Select,
1002
- {
1003
- value: offer.recurringInterval ?? "month",
1004
- onValueChange: (v) => patchOffer(offer.id, { recurringInterval: v }),
1005
- children: [
1006
- /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
1007
- /* @__PURE__ */ jsxs(SelectContent, { children: [
1008
- /* @__PURE__ */ jsx(SelectItem, { value: "month", children: t("courses.interval.month") || "month" }),
1009
- /* @__PURE__ */ jsx(SelectItem, { value: "year", children: t("courses.interval.year") || "year" })
1010
- ] })
1011
- ]
1012
- }
1013
- ) }) }),
1014
- /* @__PURE__ */ jsxs("div", { className: "mt-4 rounded-lg border border-dashed border-border bg-muted/30 p-3", children: [
1015
- /* @__PURE__ */ jsxs("p", { className: "mb-2.5 flex items-center gap-1.5 text-xs text-muted-foreground", children: [
1016
- /* @__PURE__ */ jsx(Clock, { className: "h-3.5 w-3.5" }),
1017
- t("courses.offers.checkoutFlagsNote") || "Apply once the public checkout ships (coming soon)."
1018
- ] }),
1019
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-x-5 gap-y-2", children: [
1020
- /* @__PURE__ */ jsxs("label", { className: "flex cursor-pointer items-center gap-2 text-sm text-muted-foreground", children: [
1021
- /* @__PURE__ */ jsx(Checkbox, { checked: offer.isDefault, onChange: (v) => patchOffer(offer.id, { isDefault: v }) }),
1022
- t("courses.offers.default") || "Default"
1043
+ ) }) }),
1044
+ /* @__PURE__ */ jsxs("div", { className: "mt-4 rounded-lg border border-dashed border-border bg-muted/30 p-3", children: [
1045
+ /* @__PURE__ */ jsxs("p", { className: "mb-2.5 flex items-center gap-1.5 text-xs text-muted-foreground", children: [
1046
+ /* @__PURE__ */ jsx(Clock, { className: "h-3.5 w-3.5" }),
1047
+ t("courses.offers.checkoutFlagsNote") || "Apply once the public checkout ships (coming soon)."
1023
1048
  ] }),
1024
- /* @__PURE__ */ jsxs("label", { className: "flex cursor-pointer items-center gap-2 text-sm text-muted-foreground", children: [
1025
- /* @__PURE__ */ jsx(Checkbox, { checked: offer.isOrderBump, onChange: (v) => patchOffer(offer.id, { isOrderBump: v }) }),
1026
- t("courses.offers.bump") || "Order bump"
1049
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-x-5 gap-y-2", children: [
1050
+ /* @__PURE__ */ jsxs("label", { className: "flex cursor-pointer items-center gap-2 text-sm text-muted-foreground", children: [
1051
+ /* @__PURE__ */ jsx(Checkbox, { checked: offer.isDefault, onChange: (v) => patchOffer(offer.id, { isDefault: v }) }),
1052
+ t("courses.offers.default") || "Default"
1053
+ ] }),
1054
+ /* @__PURE__ */ jsxs("label", { className: "flex cursor-pointer items-center gap-2 text-sm text-muted-foreground", children: [
1055
+ /* @__PURE__ */ jsx(Checkbox, { checked: offer.isOrderBump, onChange: (v) => patchOffer(offer.id, { isOrderBump: v }) }),
1056
+ t("courses.offers.bump") || "Order bump"
1057
+ ] })
1027
1058
  ] })
1028
1059
  ] })
1060
+ ] }, offer.id)),
1061
+ /* @__PURE__ */ jsxs(Button, { variant: "outline", onClick: addOffer, className: "w-full border-dashed", children: [
1062
+ /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }),
1063
+ t("courses.offers.add") || "Add offer"
1029
1064
  ] })
1030
- ] }, offer.id)),
1031
- /* @__PURE__ */ jsxs(Button, { variant: "outline", onClick: addOffer, className: "w-full border-dashed", children: [
1032
- /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }),
1033
- t("courses.offers.add") || "Add offer"
1034
1065
  ] })
1035
- ] })
1036
- ] }),
1037
- /* @__PURE__ */ jsxs(TabsContent, { value: "members", className: "space-y-4", children: [
1038
- /* @__PURE__ */ jsx(InfoNote, { icon: /* @__PURE__ */ jsx(Users, { className: "h-4 w-4" }), children: t("courses.editor.membersHelp") || "Buyers are enrolled automatically through orders and get access to all of this course\u2019s content." }),
1039
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1040
- /* @__PURE__ */ jsxs("p", { className: "text-xs font-medium uppercase tracking-wide text-muted-foreground", children: [
1041
- t("courses.editor.enrolledStudents") || "Enrolled students",
1042
- " (",
1043
- students.length,
1044
- ")"
1045
- ] }),
1046
- students.length === 0 ? /* @__PURE__ */ jsx(
1047
- EmptyState,
1048
- {
1049
- icon: /* @__PURE__ */ jsx(Users, { className: "h-5 w-5" }),
1050
- title: t("courses.editor.membersEmpty") || "No students yet",
1051
- description: t("courses.editor.membersEmptyHint") || "Buyers are enrolled here automatically after a paid order."
1052
- }
1053
- ) : /* @__PURE__ */ jsx("div", { className: "divide-y divide-border overflow-hidden rounded-xl border border-border bg-card", children: students.map((s) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-4 py-3", children: [
1054
- /* @__PURE__ */ jsx("span", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-muted text-xs font-semibold uppercase text-muted-foreground", "aria-hidden": true, children: (s.name || s.email || "?").charAt(0) }),
1055
- /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-1 flex-col", children: [
1056
- /* @__PURE__ */ jsx("span", { className: "truncate font-medium text-foreground", children: s.name }),
1057
- s.email && /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-muted-foreground", children: s.email })
1058
- ] }),
1059
- s.since && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", children: new Date(s.since).toLocaleDateString() })
1060
- ] }, s.key)) })
1061
1066
  ] }),
1062
- offers.length > 0 && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1063
- /* @__PURE__ */ jsx("p", { className: "text-xs font-medium uppercase tracking-wide text-muted-foreground", children: t("courses.editor.accessUnlockedBy") || "Access is unlocked by" }),
1064
- /* @__PURE__ */ jsx("div", { className: "divide-y divide-border overflow-hidden rounded-xl border border-border bg-card", children: offers.map((offer) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-4 py-3", children: [
1065
- /* @__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" }) }),
1066
- /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate font-medium text-foreground", children: offer.name }),
1067
- /* @__PURE__ */ jsx("span", { className: "shrink-0 text-sm tabular-nums text-muted-foreground", children: formatMoney(offer.price, offer.currency) })
1068
- ] }, offer.id)) })
1067
+ /* @__PURE__ */ jsxs(TabsContent, { value: "members", className: "mt-4 space-y-4", children: [
1068
+ /* @__PURE__ */ jsx(InfoNote, { icon: /* @__PURE__ */ jsx(Users, { className: "h-4 w-4" }), children: t("courses.editor.membersHelp") || "Buyers are enrolled automatically through orders and get access to all of this course\u2019s content." }),
1069
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1070
+ /* @__PURE__ */ jsxs("p", { className: "text-xs font-medium uppercase tracking-wide text-muted-foreground", children: [
1071
+ t("courses.editor.enrolledStudents") || "Enrolled students",
1072
+ " (",
1073
+ students.length,
1074
+ ")"
1075
+ ] }),
1076
+ students.length === 0 ? /* @__PURE__ */ jsx(
1077
+ EmptyState,
1078
+ {
1079
+ icon: /* @__PURE__ */ jsx(Users, { className: "h-5 w-5" }),
1080
+ title: t("courses.editor.membersEmpty") || "No students yet",
1081
+ description: t("courses.editor.membersEmptyHint") || "Buyers are enrolled here automatically after a paid order."
1082
+ }
1083
+ ) : /* @__PURE__ */ jsx("div", { className: "divide-y divide-border overflow-hidden rounded-xl border border-border bg-card", children: students.map((s) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-4 py-3", children: [
1084
+ /* @__PURE__ */ jsx("span", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-muted text-xs font-semibold uppercase text-muted-foreground", "aria-hidden": true, children: (s.name || s.email || "?").charAt(0) }),
1085
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-1 flex-col", children: [
1086
+ /* @__PURE__ */ jsx("span", { className: "truncate font-medium text-foreground", children: s.name }),
1087
+ s.email && /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-muted-foreground", children: s.email })
1088
+ ] }),
1089
+ s.since && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", children: new Date(s.since).toLocaleDateString() })
1090
+ ] }, s.key)) })
1091
+ ] }),
1092
+ offers.length > 0 && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1093
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium uppercase tracking-wide text-muted-foreground", children: t("courses.editor.accessUnlockedBy") || "Access is unlocked by" }),
1094
+ /* @__PURE__ */ jsx("div", { className: "divide-y divide-border overflow-hidden rounded-xl border border-border bg-card", children: offers.map((offer) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-4 py-3", children: [
1095
+ /* @__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" }) }),
1096
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate font-medium text-foreground", children: offer.name }),
1097
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-sm tabular-nums text-muted-foreground", children: formatMoney(offer.price, offer.currency) })
1098
+ ] }, offer.id)) })
1099
+ ] })
1069
1100
  ] })
1070
- ] })
1071
- ] }),
1072
- /* @__PURE__ */ jsx(
1073
- ConfirmDialog,
1074
- {
1075
- open: pendingDelete !== null,
1076
- variant: "destructive",
1077
- title: pendingDelete?.kind === "module" ? t("courses.editor.deleteModuleConfirm") || "Delete this module?" : t("courses.editor.deleteLessonConfirm") || "Remove this lesson?",
1078
- description: pendingDelete?.kind === "module" ? t("courses.editor.deleteModuleConfirmBody") || "Its lessons will be removed too. This cannot be undone." : t("courses.editor.deleteLessonConfirmBody") || "This cannot be undone.",
1079
- confirmLabel: t("courses.editor.confirmDelete") || "Delete",
1080
- cancelLabel: t("courses.editor.cancel") || "Cancel",
1081
- onConfirm: confirmDelete,
1082
- onCancel: () => setPendingDelete(null)
1083
- }
1084
- )
1085
- ] });
1101
+ ] }),
1102
+ /* @__PURE__ */ jsx(
1103
+ ConfirmDialog,
1104
+ {
1105
+ open: pendingDelete !== null,
1106
+ variant: "destructive",
1107
+ title: pendingDelete?.kind === "module" ? t("courses.editor.deleteModuleConfirm") || "Delete this module?" : t("courses.editor.deleteLessonConfirm") || "Remove this lesson?",
1108
+ description: pendingDelete?.kind === "module" ? t("courses.editor.deleteModuleConfirmBody") || "Its lessons will be removed too. This cannot be undone." : t("courses.editor.deleteLessonConfirmBody") || "This cannot be undone.",
1109
+ confirmLabel: t("courses.editor.confirmDelete") || "Delete",
1110
+ cancelLabel: t("courses.editor.cancel") || "Cancel",
1111
+ onConfirm: confirmDelete,
1112
+ onCancel: () => setPendingDelete(null)
1113
+ }
1114
+ )
1115
+ ] })
1116
+ );
1086
1117
  }
1087
1118
  CourseEditorPage.displayName = "CourseEditorPage";
1088
1119
  function formatDuration(sec, t) {
@@ -1507,9 +1538,10 @@ function statusVariant2(s) {
1507
1538
  return "outline";
1508
1539
  }
1509
1540
  var salesCache = null;
1510
- function SalesPage() {
1541
+ function SalesPage({ navTabs, activeNavTab, ordersRoute = "/courses/sales" } = {}) {
1511
1542
  const t = useTranslation();
1512
1543
  const [orders, setOrders] = React3.useState(salesCache);
1544
+ const tabs = navTabs?.();
1513
1545
  React3.useEffect(() => {
1514
1546
  if (salesCache) return;
1515
1547
  let alive = true;
@@ -1552,6 +1584,12 @@ function SalesPage() {
1552
1584
  {
1553
1585
  title: t("courses.sales.title") || "Vendas",
1554
1586
  subtitle: t("courses.sales.subtitle") || "Todas as transa\xE7\xF5es dos seus cursos.",
1587
+ navTabs: tabs?.map((tb) => ({ value: tb.value, label: tb.label })),
1588
+ activeNavTab,
1589
+ onNavTabChange: (v) => {
1590
+ const target = tabs?.find((tb) => tb.value === v);
1591
+ if (target) navigateTo(target.route);
1592
+ },
1555
1593
  stats: [
1556
1594
  { label: t("courses.sales.count") || "Vendas", value: String(paid.length) },
1557
1595
  { label: t("courses.sales.revenue") || "Receita", value: formatMoney(revenue, currency) }
@@ -1560,7 +1598,7 @@ function SalesPage() {
1560
1598
  rows: orders ?? [],
1561
1599
  loading: orders === null,
1562
1600
  empty: t("courses.sales.empty") || "Nenhuma venda ainda.",
1563
- onRowClick: (o) => navigateTo(`/courses/sales/${o.id}`)
1601
+ onRowClick: (o) => navigateTo(`${ordersRoute}/${o.id}`)
1564
1602
  }
1565
1603
  );
1566
1604
  }
@@ -1578,7 +1616,7 @@ function instTone(s) {
1578
1616
  if (s === "overdue") return "destructive";
1579
1617
  return "warning";
1580
1618
  }
1581
- function CourseOrderDetailView({ id }) {
1619
+ function CourseOrderDetailView({ id, ordersRoute = "/courses/sales" }) {
1582
1620
  const t = useTranslation();
1583
1621
  const [order, setOrder] = React3.useState(() => id ? orderCache.get(id) ?? null : null);
1584
1622
  const [loading, setLoading] = React3.useState(() => !(id && orderCache.has(id)));
@@ -1605,7 +1643,7 @@ function CourseOrderDetailView({ id }) {
1605
1643
  alive = false;
1606
1644
  };
1607
1645
  }, [id]);
1608
- const back = () => navigateTo("/courses/sales");
1646
+ const back = () => navigateTo(ordersRoute);
1609
1647
  if (loading) return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(OrderDetailSkeleton, {}) });
1610
1648
  if (!order) {
1611
1649
  return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(
@@ -1678,9 +1716,10 @@ function statusVariant3(s) {
1678
1716
  if (s === "past_due") return "secondary";
1679
1717
  return "destructive";
1680
1718
  }
1681
- function SubscriptionsPage() {
1719
+ function SubscriptionsPage({ navTabs, activeNavTab } = {}) {
1682
1720
  const t = useTranslation();
1683
1721
  const [subs, setSubs] = React3.useState(null);
1722
+ const tabs = navTabs?.();
1684
1723
  React3.useEffect(() => {
1685
1724
  void getCoursesProvider().listSubscriptions().then(setSubs);
1686
1725
  }, []);
@@ -1702,20 +1741,33 @@ function SubscriptionsPage() {
1702
1741
  ] })
1703
1742
  ] }) }
1704
1743
  ];
1705
- return /* @__PURE__ */ jsxs(PageContainer, { title: t("courses.subs.title") || "Subscriptions", subtitle: t("courses.subs.subtitle") || "Recurring plans and MRR.", children: [
1706
- /* @__PURE__ */ jsxs(StatGrid, { children: [
1707
- /* @__PURE__ */ jsx(StatCard, { label: t("courses.subs.active") || "Active subscriptions", value: String(active.length) }),
1708
- /* @__PURE__ */ jsx(StatCard, { label: t("courses.subs.mrr") || "Monthly recurring revenue", value: formatMoney(mrr, currency) })
1709
- ] }),
1710
- /* @__PURE__ */ jsx(
1711
- SimpleTable,
1712
- {
1713
- columns,
1714
- rows: subs ?? [],
1715
- empty: t("courses.subs.empty") || "No subscriptions yet."
1716
- }
1717
- )
1718
- ] });
1744
+ return /* @__PURE__ */ jsxs(
1745
+ PageContainer,
1746
+ {
1747
+ title: t("courses.subs.title") || "Subscriptions",
1748
+ subtitle: t("courses.subs.subtitle") || "Recurring plans and MRR.",
1749
+ navTabs: tabs?.map((tb) => ({ value: tb.value, label: tb.label })),
1750
+ activeNavTab,
1751
+ onNavTabChange: (v) => {
1752
+ const target = tabs?.find((tb) => tb.value === v);
1753
+ if (target) navigateTo(target.route);
1754
+ },
1755
+ children: [
1756
+ /* @__PURE__ */ jsxs(StatGrid, { children: [
1757
+ /* @__PURE__ */ jsx(StatCard, { label: t("courses.subs.active") || "Active subscriptions", value: String(active.length) }),
1758
+ /* @__PURE__ */ jsx(StatCard, { label: t("courses.subs.mrr") || "Monthly recurring revenue", value: formatMoney(mrr, currency) })
1759
+ ] }),
1760
+ /* @__PURE__ */ jsx(
1761
+ SimpleTable,
1762
+ {
1763
+ columns,
1764
+ rows: subs ?? [],
1765
+ empty: t("courses.subs.empty") || "No subscriptions yet."
1766
+ }
1767
+ )
1768
+ ]
1769
+ }
1770
+ );
1719
1771
  }
1720
1772
  SubscriptionsPage.displayName = "SubscriptionsPage";
1721
1773
  function FinancialPage() {
@@ -1929,6 +1981,22 @@ function createStripeConnector() {
1929
1981
  // src/index.ts
1930
1982
  var asComp = (c) => c;
1931
1983
  var read = (feature) => ({ feature, action: "read" });
1984
+ function withProps(Page2, props) {
1985
+ const Bound = (routeProps) => React3.createElement(Page2, { ...props, ...routeProps });
1986
+ Bound.displayName = `${Page2.displayName ?? Page2.name}(bound)`;
1987
+ return Bound;
1988
+ }
1989
+ function redirectTo(target) {
1990
+ const Redirect = (params) => {
1991
+ const to = typeof target === "function" ? target(params) : target;
1992
+ React3.useEffect(() => {
1993
+ navigateTo(to);
1994
+ }, [to]);
1995
+ return null;
1996
+ };
1997
+ Redirect.displayName = `Redirect(${typeof target === "string" ? target : "fn"})`;
1998
+ return Redirect;
1999
+ }
1932
2000
  function createCoursesPlugin(options) {
1933
2001
  registerTranslations(coursesLocales);
1934
2002
  setCoursesTenantResolver(() => useOrganizationStore.getState().currentOrg?.id);
@@ -1939,27 +2007,48 @@ function createCoursesPlugin(options) {
1939
2007
  const section = options?.navSection ?? "main";
1940
2008
  const modules = options?.modules ?? {};
1941
2009
  const labels = options?.labels ?? {};
2010
+ const ordersRoute = options?.routes?.orders ?? "/courses/sales";
2011
+ const subscriptionsRoute = options?.routes?.subscriptions ?? "/courses/subscriptions";
2012
+ const salesTabs = options?.salesTabs ?? false;
2013
+ const salesTabStrip = salesTabs ? () => [
2014
+ { value: "orders", label: labels.sales ?? "Sales", route: ordersRoute },
2015
+ { value: "subscriptions", label: labels.subscriptions ?? "Subscriptions", route: subscriptionsRoute }
2016
+ ] : void 0;
1942
2017
  const navigation = [
1943
2018
  { section, position: base, label: options?.navLabel ?? "Courses", route: "/courses", icon: "BookOpen", permission: read("courses") },
1944
2019
  // The members area is no longer a standalone nav entry — it lives inside each
1945
2020
  // course's record (CourseEditorPage → "Alunos" tab). modules.membersArea is
1946
2021
  // kept accepted-but-inert for backward compat with existing manifests.
1947
- ...modules.sales ? [{ section, position: base + 2, label: labels.sales ?? "Sales", route: "/courses/sales", icon: "TrendingUp", permission: read("courses.sales") }] : [],
1948
- ...modules.subscriptions ? [{ section, position: base + 3, label: labels.subscriptions ?? "Subscriptions", route: "/courses/subscriptions", icon: "Repeat", permission: read("courses.sales") }] : [],
2022
+ ...modules.sales ? [{ section, position: options?.ordersNavPosition ?? base + 2, label: labels.sales ?? "Sales", route: ordersRoute, icon: "TrendingUp", permission: read("courses.sales") }] : [],
2023
+ // Com `salesTabs`, Assinaturas é uma ABA de Pedidos — listá-la também no menu
2024
+ // seria a mesma navegação duas vezes, que é o que a faixa existe para evitar.
2025
+ ...modules.subscriptions && !salesTabs ? [{ section, position: base + 3, label: labels.subscriptions ?? "Subscriptions", route: subscriptionsRoute, icon: "Repeat", permission: read("courses.sales") }] : [],
1949
2026
  ...modules.financial ? [{ section, position: base + 4, label: labels.financial ?? "Financial", route: "/courses/financial", icon: "CreditCard", permission: read("courses.finance") }] : [],
1950
2027
  ...modules.reports ? [{ section, position: base + 5, label: labels.reports ?? "Reports", route: "/courses/reports", icon: "BarChart3", permission: read("courses.sales") }] : []
1951
2028
  ];
2029
+ const salesProps = { navTabs: salesTabStrip, activeNavTab: "orders" };
2030
+ const subsProps = { navTabs: salesTabStrip, activeNavTab: "subscriptions" };
1952
2031
  const routes = [
1953
2032
  // Static segments outscore the `:id` param in the saas router, so these
1954
2033
  // resolve before the editor route (AdminShell sorts by routeScore).
1955
2034
  { path: "/courses", component: asComp(CoursesListPage), permission: read("courses") },
1956
- ...modules.sales ? [{ path: "/courses/sales", component: asComp(SalesPage), permission: read("courses.sales") }] : [],
1957
- // Order detail — static `/courses/sales/:id` outscores `/courses/:id`.
1958
- ...modules.sales ? [{ path: "/courses/sales/:id", component: asComp(CourseOrderDetailView), permission: read("courses.sales") }] : [],
1959
- ...modules.subscriptions ? [{ path: "/courses/subscriptions", component: asComp(SubscriptionsPage), permission: read("courses.sales") }] : [],
2035
+ ...modules.sales ? [{ path: ordersRoute, component: asComp(withProps(SalesPage, salesProps)), permission: read("courses.sales") }] : [],
2036
+ // Order detail — the static `${ordersRoute}/:id` outscores `/courses/:id`.
2037
+ ...modules.sales ? [{ path: `${ordersRoute}/:id`, component: asComp(withProps(CourseOrderDetailView, { ordersRoute })), permission: read("courses.sales") }] : [],
2038
+ ...modules.subscriptions ? [{ path: subscriptionsRoute, component: asComp(withProps(SubscriptionsPage, subsProps)), permission: read("courses.sales") }] : [],
1960
2039
  ...modules.financial ? [{ path: "/courses/financial", component: asComp(FinancialPage), permission: read("courses.finance") }] : [],
1961
2040
  ...modules.reports ? [{ path: "/courses/reports", component: asComp(ReportsPage), permission: read("courses.sales") }] : [],
1962
- { path: "/courses/:id", component: asComp(CourseEditorPage) }
2041
+ { path: "/courses/:id", component: asComp(CourseEditorPage) },
2042
+ // Onde as telas moravam quando o host as reposiciona. Redirecionam para não
2043
+ // deixar um link antigo renderizando tela em branco.
2044
+ ...modules.sales && ordersRoute !== "/courses/sales" ? [
2045
+ // O detalhe vem PRIMEIRO e com o param: `/courses/sales/:id` (score 40)
2046
+ // ganha do curinga `/courses/sales/*` (29) que o shell registra junto
2047
+ // com o pai — sem ele, todo link antigo de pedido caía na lista.
2048
+ { path: "/courses/sales/:id", component: asComp(redirectTo((p) => `${ordersRoute}/${p.id}`)) },
2049
+ { path: "/courses/sales", component: asComp(redirectTo(ordersRoute)) }
2050
+ ] : [],
2051
+ ...modules.subscriptions && subscriptionsRoute !== "/courses/subscriptions" ? [{ path: "/courses/subscriptions", component: asComp(redirectTo(subscriptionsRoute)) }] : []
1963
2052
  ];
1964
2053
  return {
1965
2054
  id: "courses",