@fayz-ai/plugin-courses 0.2.0

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 ADDED
@@ -0,0 +1,365 @@
1
+ import { hashRouterAdapter, registerPluginFactory, registerTranslations, useTranslation } from '@fayz-ai/core';
2
+ import { getCoursesProviderOptional, setCoursesProvider, createMockCoursesProvider, getCoursesProvider } from '@fayz-ai/courses';
3
+ import * as React2 from 'react';
4
+ import { useModuleLayout, PageHeaderActions, Button, Badge, Input, toast } from '@fayz-ai/ui';
5
+ import { jsxs, jsx } from 'react/jsx-runtime';
6
+
7
+ // src/index.ts
8
+
9
+ // src/locales/en.ts
10
+ var en = {
11
+ "courses.nav": "Courses",
12
+ "courses.title": "Courses",
13
+ "courses.new": "New course",
14
+ "courses.empty": "No courses yet. Create your first one.",
15
+ "courses.count": "{count} courses",
16
+ "courses.students": "students",
17
+ "courses.lessons": "lessons",
18
+ "courses.status.draft": "Draft",
19
+ "courses.status.published": "Published",
20
+ "courses.status.archived": "Archived",
21
+ "courses.editor.back": "Back to courses",
22
+ "courses.editor.details": "Details",
23
+ "courses.editor.curriculum": "Curriculum",
24
+ "courses.editor.fieldTitle": "Title",
25
+ "courses.editor.fieldSubtitle": "Subtitle",
26
+ "courses.editor.fieldDescription": "Description",
27
+ "courses.editor.fieldPrice": "Price",
28
+ "courses.editor.fieldStatus": "Status",
29
+ "courses.editor.save": "Save",
30
+ "courses.editor.saved": "Course saved",
31
+ "courses.editor.addModule": "Add module",
32
+ "courses.editor.moduleTitle": "Module title",
33
+ "courses.editor.addLesson": "Add lesson",
34
+ "courses.editor.lessonTitle": "Lesson title",
35
+ "courses.editor.lessonVideo": "Video URL (embed)",
36
+ "courses.editor.lessonDuration": "Duration (min)",
37
+ "courses.editor.deleteModule": "Delete module",
38
+ "courses.editor.deleteLesson": "Remove",
39
+ "courses.editor.untitledCourse": "Untitled course",
40
+ "courses.editor.untitledModule": "New module",
41
+ "courses.editor.untitledLesson": "New lesson"
42
+ };
43
+
44
+ // src/locales/pt-BR.ts
45
+ var ptBR = {
46
+ "courses.nav": "Cursos",
47
+ "courses.title": "Cursos",
48
+ "courses.new": "Novo curso",
49
+ "courses.empty": "Nenhum curso ainda. Crie o primeiro.",
50
+ "courses.count": "{count} cursos",
51
+ "courses.students": "alunos",
52
+ "courses.lessons": "aulas",
53
+ "courses.status.draft": "Rascunho",
54
+ "courses.status.published": "Publicado",
55
+ "courses.status.archived": "Arquivado",
56
+ "courses.editor.back": "Voltar aos cursos",
57
+ "courses.editor.details": "Detalhes",
58
+ "courses.editor.curriculum": "Conte\xFAdo",
59
+ "courses.editor.fieldTitle": "T\xEDtulo",
60
+ "courses.editor.fieldSubtitle": "Subt\xEDtulo",
61
+ "courses.editor.fieldDescription": "Descri\xE7\xE3o",
62
+ "courses.editor.fieldPrice": "Pre\xE7o",
63
+ "courses.editor.fieldStatus": "Status",
64
+ "courses.editor.save": "Salvar",
65
+ "courses.editor.saved": "Curso salvo",
66
+ "courses.editor.addModule": "Adicionar m\xF3dulo",
67
+ "courses.editor.moduleTitle": "T\xEDtulo do m\xF3dulo",
68
+ "courses.editor.addLesson": "Adicionar aula",
69
+ "courses.editor.lessonTitle": "T\xEDtulo da aula",
70
+ "courses.editor.lessonVideo": "URL do v\xEDdeo (embed)",
71
+ "courses.editor.lessonDuration": "Dura\xE7\xE3o (min)",
72
+ "courses.editor.deleteModule": "Excluir m\xF3dulo",
73
+ "courses.editor.deleteLesson": "Remover",
74
+ "courses.editor.untitledCourse": "Curso sem t\xEDtulo",
75
+ "courses.editor.untitledModule": "Novo m\xF3dulo",
76
+ "courses.editor.untitledLesson": "Nova aula"
77
+ };
78
+
79
+ // src/locales/index.ts
80
+ var coursesLocales = {
81
+ en,
82
+ "pt-BR": ptBR
83
+ };
84
+ var adapter = hashRouterAdapter();
85
+ function navigateTo(to) {
86
+ adapter.navigate(to);
87
+ }
88
+ function statusVariant(status) {
89
+ if (status === "published") return "success";
90
+ if (status === "archived") return "outline";
91
+ return "secondary";
92
+ }
93
+ function CoursesListPage() {
94
+ const t = useTranslation();
95
+ const shellOwnsHeader = useModuleLayout() === "tabs";
96
+ const [courses, setCourses] = React2.useState(null);
97
+ const [creating, setCreating] = React2.useState(false);
98
+ const load = React2.useCallback(async () => {
99
+ setCourses(await getCoursesProvider().listCourses());
100
+ }, []);
101
+ React2.useEffect(() => {
102
+ void load();
103
+ }, [load]);
104
+ async function onCreate() {
105
+ setCreating(true);
106
+ try {
107
+ const course = await getCoursesProvider().createCourse({
108
+ title: t("courses.editor.untitledCourse") || "Untitled course",
109
+ status: "draft"
110
+ });
111
+ navigateTo(`/courses/${course.id}`);
112
+ } finally {
113
+ setCreating(false);
114
+ }
115
+ }
116
+ return /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-6xl p-6 md:p-8", children: [
117
+ shellOwnsHeader ? /* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsx(Button, { onClick: onCreate, disabled: creating, children: t("courses.new") || "New course" }) }) : /* @__PURE__ */ jsxs("div", { className: "mb-6 flex items-center justify-between", children: [
118
+ /* @__PURE__ */ jsxs("div", { children: [
119
+ /* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold text-foreground", children: t("courses.title") || "Courses" }),
120
+ courses && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: (t("courses.count") || "{count} courses").replace("{count}", String(courses.length)) })
121
+ ] }),
122
+ /* @__PURE__ */ jsx(Button, { onClick: onCreate, disabled: creating, children: t("courses.new") || "New course" })
123
+ ] }),
124
+ courses && courses.length === 0 && /* @__PURE__ */ jsx("div", { className: "rounded-xl border border-dashed border-border p-12 text-center text-muted-foreground", children: t("courses.empty") || "No courses yet. Create your first one." }),
125
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3", children: courses?.map((course) => /* @__PURE__ */ jsxs(
126
+ "button",
127
+ {
128
+ onClick: () => navigateTo(`/courses/${course.id}`),
129
+ className: "group overflow-hidden rounded-xl border border-border bg-card text-left transition hover:border-primary hover:shadow-md",
130
+ children: [
131
+ /* @__PURE__ */ jsx("div", { className: "aspect-video w-full overflow-hidden bg-muted", children: course.thumbnailUrl && // eslint-disable-next-line @next/next/no-img-element
132
+ /* @__PURE__ */ jsx("img", { src: course.thumbnailUrl, alt: "", className: "h-full w-full object-cover" }) }),
133
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2 p-4", children: [
134
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
135
+ /* @__PURE__ */ jsx("h3", { className: "font-semibold text-foreground line-clamp-2", children: course.title }),
136
+ /* @__PURE__ */ jsx(Badge, { variant: statusVariant(course.status), children: t(`courses.status.${course.status}`) || course.status })
137
+ ] }),
138
+ course.subtitle && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground line-clamp-2", children: course.subtitle }),
139
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-foreground", children: course.price > 0 ? `${course.currency} ${course.price.toFixed(2)}` : "\u2014" })
140
+ ] })
141
+ ]
142
+ },
143
+ course.id
144
+ )) })
145
+ ] });
146
+ }
147
+ CoursesListPage.displayName = "CoursesListPage";
148
+ var STATUSES = ["draft", "published", "archived"];
149
+ function CourseEditorPage({ id }) {
150
+ const t = useTranslation();
151
+ const [tab, setTab] = React2.useState("details");
152
+ const [course, setCourse] = React2.useState(null);
153
+ const [modules, setModules] = React2.useState([]);
154
+ const [lessons, setLessons] = React2.useState([]);
155
+ const reload = React2.useCallback(async () => {
156
+ if (!id) return;
157
+ const provider = getCoursesProvider();
158
+ const c = await provider.getCourse(id);
159
+ setCourse(c);
160
+ if (c) {
161
+ const [m, l] = await Promise.all([provider.listModules(c.id), provider.listLessons(c.id)]);
162
+ setModules(m);
163
+ setLessons(l);
164
+ }
165
+ }, [id]);
166
+ React2.useEffect(() => {
167
+ void reload();
168
+ }, [reload]);
169
+ if (!course) {
170
+ return /* @__PURE__ */ jsx("div", { className: "p-8 text-muted-foreground", children: "\u2026" });
171
+ }
172
+ async function saveDetails() {
173
+ const provider = getCoursesProvider();
174
+ await provider.updateCourse(course.id, {
175
+ title: course.title,
176
+ subtitle: course.subtitle ?? void 0,
177
+ description: course.description ?? void 0,
178
+ price: course.price,
179
+ status: course.status
180
+ });
181
+ toast.success(t("courses.editor.saved") || "Course saved");
182
+ }
183
+ function patchCourse(patch) {
184
+ setCourse((c) => c ? { ...c, ...patch } : c);
185
+ }
186
+ async function addModule() {
187
+ await getCoursesProvider().createModule({
188
+ courseId: course.id,
189
+ title: t("courses.editor.untitledModule") || "New module"
190
+ });
191
+ await reload();
192
+ }
193
+ async function renameModule(moduleId, title) {
194
+ await getCoursesProvider().updateModule(moduleId, { title });
195
+ }
196
+ async function deleteModule(moduleId) {
197
+ await getCoursesProvider().deleteModule(moduleId);
198
+ await reload();
199
+ }
200
+ async function addLesson(moduleId) {
201
+ await getCoursesProvider().createLesson({
202
+ courseId: course.id,
203
+ moduleId,
204
+ title: t("courses.editor.untitledLesson") || "New lesson",
205
+ videoUrl: "https://www.youtube.com/embed/dQw4w9WgXcQ"
206
+ });
207
+ await reload();
208
+ }
209
+ async function patchLesson(lessonId, patch) {
210
+ await getCoursesProvider().updateLesson(lessonId, {
211
+ title: patch.title,
212
+ videoUrl: patch.videoUrl,
213
+ durationSec: patch.durationSec,
214
+ description: patch.description ?? void 0
215
+ });
216
+ }
217
+ async function deleteLesson(lessonId) {
218
+ await getCoursesProvider().deleteLesson(lessonId);
219
+ await reload();
220
+ }
221
+ return /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-4xl p-6 md:p-8", children: [
222
+ /* @__PURE__ */ jsxs("button", { onClick: () => navigateTo("/courses"), className: "mb-4 text-sm text-muted-foreground hover:text-foreground", children: [
223
+ "\u2190 ",
224
+ t("courses.editor.back") || "Back to courses"
225
+ ] }),
226
+ /* @__PURE__ */ jsx("h1", { className: "mb-1 text-2xl font-bold text-foreground", children: course.title }),
227
+ /* @__PURE__ */ jsx("div", { className: "mb-6 mt-4 flex gap-2 border-b border-border", children: ["details", "curriculum"].map((tk) => /* @__PURE__ */ jsx(
228
+ "button",
229
+ {
230
+ onClick: () => setTab(tk),
231
+ className: `-mb-px border-b-2 px-3 py-2 text-sm font-medium ${tab === tk ? "border-primary text-foreground" : "border-transparent text-muted-foreground"}`,
232
+ children: tk === "details" ? t("courses.editor.details") || "Details" : t("courses.editor.curriculum") || "Curriculum"
233
+ },
234
+ tk
235
+ )) }),
236
+ tab === "details" && /* @__PURE__ */ jsxs("div", { className: "max-w-xl space-y-4", children: [
237
+ /* @__PURE__ */ jsx(Field, { label: t("courses.editor.fieldTitle") || "Title", children: /* @__PURE__ */ jsx(Input, { value: course.title, onChange: (e) => patchCourse({ title: e.target.value }) }) }),
238
+ /* @__PURE__ */ jsx(Field, { label: t("courses.editor.fieldSubtitle") || "Subtitle", children: /* @__PURE__ */ jsx(Input, { value: course.subtitle ?? "", onChange: (e) => patchCourse({ subtitle: e.target.value }) }) }),
239
+ /* @__PURE__ */ jsx(Field, { label: t("courses.editor.fieldDescription") || "Description", children: /* @__PURE__ */ jsx(
240
+ "textarea",
241
+ {
242
+ className: "min-h-24 w-full rounded-md border border-input bg-background px-3 py-2 text-sm",
243
+ value: course.description ?? "",
244
+ onChange: (e) => patchCourse({ description: e.target.value })
245
+ }
246
+ ) }),
247
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
248
+ /* @__PURE__ */ jsx(Field, { label: t("courses.editor.fieldPrice") || "Price", children: /* @__PURE__ */ jsx(
249
+ Input,
250
+ {
251
+ type: "number",
252
+ value: String(course.price),
253
+ onChange: (e) => patchCourse({ price: Number(e.target.value) || 0 })
254
+ }
255
+ ) }),
256
+ /* @__PURE__ */ jsx(Field, { label: t("courses.editor.fieldStatus") || "Status", children: /* @__PURE__ */ jsx(
257
+ "select",
258
+ {
259
+ className: "h-10 rounded-md border border-input bg-background px-3 text-sm",
260
+ value: course.status,
261
+ onChange: (e) => patchCourse({ status: e.target.value }),
262
+ children: STATUSES.map((s) => /* @__PURE__ */ jsx("option", { value: s, children: t(`courses.status.${s}`) || s }, s))
263
+ }
264
+ ) })
265
+ ] }),
266
+ /* @__PURE__ */ jsx(Button, { onClick: saveDetails, children: t("courses.editor.save") || "Save" })
267
+ ] }),
268
+ tab === "curriculum" && /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
269
+ modules.map((mod) => /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-border bg-card p-4", children: [
270
+ /* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-center gap-2", children: [
271
+ /* @__PURE__ */ jsx(
272
+ Input,
273
+ {
274
+ className: "font-medium",
275
+ defaultValue: mod.title,
276
+ onBlur: (e) => renameModule(mod.id, e.target.value),
277
+ "aria-label": t("courses.editor.moduleTitle") || "Module title"
278
+ }
279
+ ),
280
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: () => deleteModule(mod.id), children: t("courses.editor.deleteModule") || "Delete module" })
281
+ ] }),
282
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
283
+ lessons.filter((l) => l.moduleId === mod.id).map((lesson) => /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-2 rounded-md border border-border p-3 md:grid-cols-[1fr_1fr_90px_auto]", children: [
284
+ /* @__PURE__ */ jsx(
285
+ Input,
286
+ {
287
+ defaultValue: lesson.title,
288
+ placeholder: t("courses.editor.lessonTitle") || "Lesson title",
289
+ onBlur: (e) => patchLesson(lesson.id, { title: e.target.value })
290
+ }
291
+ ),
292
+ /* @__PURE__ */ jsx(
293
+ Input,
294
+ {
295
+ defaultValue: lesson.videoUrl,
296
+ placeholder: t("courses.editor.lessonVideo") || "Video URL (embed)",
297
+ onBlur: (e) => patchLesson(lesson.id, { videoUrl: e.target.value })
298
+ }
299
+ ),
300
+ /* @__PURE__ */ jsx(
301
+ Input,
302
+ {
303
+ type: "number",
304
+ defaultValue: String(Math.round(lesson.durationSec / 60)),
305
+ onBlur: (e) => patchLesson(lesson.id, { durationSec: (Number(e.target.value) || 0) * 60 }),
306
+ "aria-label": t("courses.editor.lessonDuration") || "Duration (min)"
307
+ }
308
+ ),
309
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: () => deleteLesson(lesson.id), children: t("courses.editor.deleteLesson") || "Remove" })
310
+ ] }, lesson.id)),
311
+ /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => addLesson(mod.id), children: [
312
+ "+ ",
313
+ t("courses.editor.addLesson") || "Add lesson"
314
+ ] })
315
+ ] })
316
+ ] }, mod.id)),
317
+ /* @__PURE__ */ jsx(Button, { onClick: addModule, children: t("courses.editor.addModule") || "Add module" })
318
+ ] })
319
+ ] });
320
+ }
321
+ CourseEditorPage.displayName = "CourseEditorPage";
322
+ function Field({ label, children }) {
323
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
324
+ /* @__PURE__ */ jsx("label", { className: "text-sm font-medium text-foreground", children: label }),
325
+ children
326
+ ] });
327
+ }
328
+
329
+ // src/index.ts
330
+ function createCoursesPlugin(options) {
331
+ registerTranslations(coursesLocales);
332
+ if (!getCoursesProviderOptional()) {
333
+ setCoursesProvider(createMockCoursesProvider());
334
+ }
335
+ return {
336
+ id: "courses",
337
+ name: options?.navLabel ?? "Courses",
338
+ icon: "BookOpen",
339
+ version: "1.0.0",
340
+ scope: options?.scope ?? "vertical",
341
+ verticalId: options?.verticalId,
342
+ defaultEnabled: true,
343
+ dependencies: [],
344
+ navigation: [
345
+ {
346
+ section: options?.navSection ?? "main",
347
+ position: options?.navPosition ?? 1,
348
+ label: options?.navLabel ?? "Courses",
349
+ route: "/courses",
350
+ icon: "BookOpen"
351
+ }
352
+ ],
353
+ routes: [
354
+ { path: "/courses", component: CoursesListPage },
355
+ { path: "/courses/:id", component: CourseEditorPage }
356
+ ],
357
+ widgets: [],
358
+ locales: coursesLocales
359
+ };
360
+ }
361
+ registerPluginFactory("courses", createCoursesPlugin);
362
+
363
+ export { coursesLocales, createCoursesPlugin };
364
+ //# sourceMappingURL=index.js.map
365
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/locales/en.ts","../src/locales/pt-BR.ts","../src/locales/index.ts","../src/nav.ts","../src/pages/CoursesListPage.tsx","../src/pages/CourseEditorPage.tsx","../src/index.ts"],"names":["React","useTranslation","getCoursesProvider","jsx","jsxs","Button"],"mappings":"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","file":"index.js","sourcesContent":["export const en: Record<string, string> = {\n 'courses.nav': 'Courses',\n 'courses.title': 'Courses',\n 'courses.new': 'New course',\n 'courses.empty': 'No courses yet. Create your first one.',\n 'courses.count': '{count} courses',\n 'courses.students': 'students',\n 'courses.lessons': 'lessons',\n 'courses.status.draft': 'Draft',\n 'courses.status.published': 'Published',\n 'courses.status.archived': 'Archived',\n 'courses.editor.back': 'Back to courses',\n 'courses.editor.details': 'Details',\n 'courses.editor.curriculum': 'Curriculum',\n 'courses.editor.fieldTitle': 'Title',\n 'courses.editor.fieldSubtitle': 'Subtitle',\n 'courses.editor.fieldDescription': 'Description',\n 'courses.editor.fieldPrice': 'Price',\n 'courses.editor.fieldStatus': 'Status',\n 'courses.editor.save': 'Save',\n 'courses.editor.saved': 'Course saved',\n 'courses.editor.addModule': 'Add module',\n 'courses.editor.moduleTitle': 'Module title',\n 'courses.editor.addLesson': 'Add lesson',\n 'courses.editor.lessonTitle': 'Lesson title',\n 'courses.editor.lessonVideo': 'Video URL (embed)',\n 'courses.editor.lessonDuration': 'Duration (min)',\n 'courses.editor.deleteModule': 'Delete module',\n 'courses.editor.deleteLesson': 'Remove',\n 'courses.editor.untitledCourse': 'Untitled course',\n 'courses.editor.untitledModule': 'New module',\n 'courses.editor.untitledLesson': 'New lesson',\n}\n","export const ptBR: Record<string, string> = {\n 'courses.nav': 'Cursos',\n 'courses.title': 'Cursos',\n 'courses.new': 'Novo curso',\n 'courses.empty': 'Nenhum curso ainda. Crie o primeiro.',\n 'courses.count': '{count} cursos',\n 'courses.students': 'alunos',\n 'courses.lessons': 'aulas',\n 'courses.status.draft': 'Rascunho',\n 'courses.status.published': 'Publicado',\n 'courses.status.archived': 'Arquivado',\n 'courses.editor.back': 'Voltar aos cursos',\n 'courses.editor.details': 'Detalhes',\n 'courses.editor.curriculum': 'Conteúdo',\n 'courses.editor.fieldTitle': 'Título',\n 'courses.editor.fieldSubtitle': 'Subtítulo',\n 'courses.editor.fieldDescription': 'Descrição',\n 'courses.editor.fieldPrice': 'Preço',\n 'courses.editor.fieldStatus': 'Status',\n 'courses.editor.save': 'Salvar',\n 'courses.editor.saved': 'Curso salvo',\n 'courses.editor.addModule': 'Adicionar módulo',\n 'courses.editor.moduleTitle': 'Título do módulo',\n 'courses.editor.addLesson': 'Adicionar aula',\n 'courses.editor.lessonTitle': 'Título da aula',\n 'courses.editor.lessonVideo': 'URL do vídeo (embed)',\n 'courses.editor.lessonDuration': 'Duração (min)',\n 'courses.editor.deleteModule': 'Excluir módulo',\n 'courses.editor.deleteLesson': 'Remover',\n 'courses.editor.untitledCourse': 'Curso sem título',\n 'courses.editor.untitledModule': 'Novo módulo',\n 'courses.editor.untitledLesson': 'Nova aula',\n}\n","import { en } from './en'\nimport { ptBR } from './pt-BR'\n\nexport const coursesLocales: Record<string, Record<string, string>> = {\n en,\n 'pt-BR': ptBR,\n}\n","import { hashRouterAdapter } from '@fayz-ai/core'\n\n// Shared hash-router adapter — the same singleton the admin shell drives, so\n// navigateTo here updates the shell's active route.\nconst adapter = hashRouterAdapter()\n\nexport function navigateTo(to: string): void {\n adapter.navigate(to)\n}\n","import * as React from 'react'\nimport { Button, Badge, PageHeaderActions, useModuleLayout } from '@fayz-ai/ui'\nimport { useTranslation } from '@fayz-ai/core'\nimport { getCoursesProvider, type Course } from '@fayz-ai/courses'\nimport { navigateTo } from '../nav'\n\nfunction statusVariant(status: Course['status']): 'success' | 'secondary' | 'outline' {\n if (status === 'published') return 'success'\n if (status === 'archived') return 'outline'\n return 'secondary'\n}\n\nexport function CoursesListPage() {\n const t = useTranslation()\n // When the app shell owns the page header (sidebar/GHL-style layouts), don't\n // render our own title — and project the action button into the top bar.\n const shellOwnsHeader = useModuleLayout() === 'tabs'\n const [courses, setCourses] = React.useState<Course[] | null>(null)\n const [creating, setCreating] = React.useState(false)\n\n const load = React.useCallback(async () => {\n setCourses(await getCoursesProvider().listCourses())\n }, [])\n\n React.useEffect(() => {\n void load()\n }, [load])\n\n async function onCreate() {\n setCreating(true)\n try {\n const course = await getCoursesProvider().createCourse({\n title: t('courses.editor.untitledCourse') || 'Untitled course',\n status: 'draft',\n })\n navigateTo(`/courses/${course.id}`)\n } finally {\n setCreating(false)\n }\n }\n\n return (\n <div className=\"mx-auto max-w-6xl p-6 md:p-8\">\n {shellOwnsHeader ? (\n <PageHeaderActions>\n <Button onClick={onCreate} disabled={creating}>\n {t('courses.new') || 'New course'}\n </Button>\n </PageHeaderActions>\n ) : (\n <div className=\"mb-6 flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-foreground\">{t('courses.title') || 'Courses'}</h1>\n {courses && (\n <p className=\"mt-1 text-sm text-muted-foreground\">\n {(t('courses.count') || '{count} courses').replace('{count}', String(courses.length))}\n </p>\n )}\n </div>\n <Button onClick={onCreate} disabled={creating}>\n {t('courses.new') || 'New course'}\n </Button>\n </div>\n )}\n\n {courses && courses.length === 0 && (\n <div className=\"rounded-xl border border-dashed border-border p-12 text-center text-muted-foreground\">\n {t('courses.empty') || 'No courses yet. Create your first one.'}\n </div>\n )}\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {courses?.map((course) => (\n <button\n key={course.id}\n onClick={() => navigateTo(`/courses/${course.id}`)}\n className=\"group overflow-hidden rounded-xl border border-border bg-card text-left transition hover:border-primary hover:shadow-md\"\n >\n <div className=\"aspect-video w-full overflow-hidden bg-muted\">\n {course.thumbnailUrl && (\n // eslint-disable-next-line @next/next/no-img-element\n <img src={course.thumbnailUrl} alt=\"\" className=\"h-full w-full object-cover\" />\n )}\n </div>\n <div className=\"space-y-2 p-4\">\n <div className=\"flex items-start justify-between gap-2\">\n <h3 className=\"font-semibold text-foreground line-clamp-2\">{course.title}</h3>\n <Badge variant={statusVariant(course.status)}>\n {t(`courses.status.${course.status}`) || course.status}\n </Badge>\n </div>\n {course.subtitle && (\n <p className=\"text-sm text-muted-foreground line-clamp-2\">{course.subtitle}</p>\n )}\n <p className=\"text-sm font-medium text-foreground\">\n {course.price > 0 ? `${course.currency} ${course.price.toFixed(2)}` : '—'}\n </p>\n </div>\n </button>\n ))}\n </div>\n </div>\n )\n}\nCoursesListPage.displayName = 'CoursesListPage'\n","import * as React from 'react'\nimport { Button, Input, toast } from '@fayz-ai/ui'\nimport { useTranslation } from '@fayz-ai/core'\nimport {\n getCoursesProvider,\n type Course, type Module, type Lesson, type CourseStatus,\n} from '@fayz-ai/courses'\nimport { navigateTo } from '../nav'\n\nconst STATUSES: CourseStatus[] = ['draft', 'published', 'archived']\n\nexport function CourseEditorPage({ id }: { id?: string }) {\n const t = useTranslation()\n const [tab, setTab] = React.useState<'details' | 'curriculum'>('details')\n const [course, setCourse] = React.useState<Course | null>(null)\n const [modules, setModules] = React.useState<Module[]>([])\n const [lessons, setLessons] = React.useState<Lesson[]>([])\n\n const reload = React.useCallback(async () => {\n if (!id) return\n const provider = getCoursesProvider()\n const c = await provider.getCourse(id)\n setCourse(c)\n if (c) {\n const [m, l] = await Promise.all([provider.listModules(c.id), provider.listLessons(c.id)])\n setModules(m)\n setLessons(l)\n }\n }, [id])\n\n React.useEffect(() => { void reload() }, [reload])\n\n if (!course) {\n return <div className=\"p-8 text-muted-foreground\">…</div>\n }\n\n async function saveDetails() {\n const provider = getCoursesProvider()\n await provider.updateCourse(course!.id, {\n title: course!.title,\n subtitle: course!.subtitle ?? undefined,\n description: course!.description ?? undefined,\n price: course!.price,\n status: course!.status,\n })\n toast.success(t('courses.editor.saved') || 'Course saved')\n }\n\n function patchCourse(patch: Partial<Course>) {\n setCourse((c) => (c ? { ...c, ...patch } : c))\n }\n\n // --- module / lesson mutations (persist immediately, then reload) ---------\n async function addModule() {\n await getCoursesProvider().createModule({\n courseId: course!.id,\n title: t('courses.editor.untitledModule') || 'New module',\n })\n await reload()\n }\n async function renameModule(moduleId: string, title: string) {\n await getCoursesProvider().updateModule(moduleId, { title })\n }\n async function deleteModule(moduleId: string) {\n await getCoursesProvider().deleteModule(moduleId)\n await reload()\n }\n async function addLesson(moduleId: string) {\n await getCoursesProvider().createLesson({\n courseId: course!.id,\n moduleId,\n title: t('courses.editor.untitledLesson') || 'New lesson',\n videoUrl: 'https://www.youtube.com/embed/dQw4w9WgXcQ',\n })\n await reload()\n }\n async function patchLesson(lessonId: string, patch: Partial<Lesson>) {\n await getCoursesProvider().updateLesson(lessonId, {\n title: patch.title,\n videoUrl: patch.videoUrl,\n durationSec: patch.durationSec,\n description: patch.description ?? undefined,\n })\n }\n async function deleteLesson(lessonId: string) {\n await getCoursesProvider().deleteLesson(lessonId)\n await reload()\n }\n\n return (\n <div className=\"mx-auto max-w-4xl p-6 md:p-8\">\n <button onClick={() => navigateTo('/courses')} className=\"mb-4 text-sm text-muted-foreground hover:text-foreground\">\n ← {t('courses.editor.back') || 'Back to courses'}\n </button>\n\n <h1 className=\"mb-1 text-2xl font-bold text-foreground\">{course.title}</h1>\n\n <div className=\"mb-6 mt-4 flex gap-2 border-b border-border\">\n {(['details', 'curriculum'] as const).map((tk) => (\n <button\n key={tk}\n onClick={() => setTab(tk)}\n className={`-mb-px border-b-2 px-3 py-2 text-sm font-medium ${\n tab === tk ? 'border-primary text-foreground' : 'border-transparent text-muted-foreground'\n }`}\n >\n {tk === 'details' ? (t('courses.editor.details') || 'Details') : (t('courses.editor.curriculum') || 'Curriculum')}\n </button>\n ))}\n </div>\n\n {tab === 'details' && (\n <div className=\"max-w-xl space-y-4\">\n <Field label={t('courses.editor.fieldTitle') || 'Title'}>\n <Input value={course.title} onChange={(e) => patchCourse({ title: e.target.value })} />\n </Field>\n <Field label={t('courses.editor.fieldSubtitle') || 'Subtitle'}>\n <Input value={course.subtitle ?? ''} onChange={(e) => patchCourse({ subtitle: e.target.value })} />\n </Field>\n <Field label={t('courses.editor.fieldDescription') || 'Description'}>\n <textarea\n className=\"min-h-24 w-full rounded-md border border-input bg-background px-3 py-2 text-sm\"\n value={course.description ?? ''}\n onChange={(e) => patchCourse({ description: e.target.value })}\n />\n </Field>\n <div className=\"flex gap-4\">\n <Field label={t('courses.editor.fieldPrice') || 'Price'}>\n <Input\n type=\"number\"\n value={String(course.price)}\n onChange={(e) => patchCourse({ price: Number(e.target.value) || 0 })}\n />\n </Field>\n <Field label={t('courses.editor.fieldStatus') || 'Status'}>\n <select\n className=\"h-10 rounded-md border border-input bg-background px-3 text-sm\"\n value={course.status}\n onChange={(e) => patchCourse({ status: e.target.value as CourseStatus })}\n >\n {STATUSES.map((s) => (\n <option key={s} value={s}>{t(`courses.status.${s}`) || s}</option>\n ))}\n </select>\n </Field>\n </div>\n <Button onClick={saveDetails}>{t('courses.editor.save') || 'Save'}</Button>\n </div>\n )}\n\n {tab === 'curriculum' && (\n <div className=\"space-y-6\">\n {modules.map((mod) => (\n <div key={mod.id} className=\"rounded-xl border border-border bg-card p-4\">\n <div className=\"mb-3 flex items-center gap-2\">\n <Input\n className=\"font-medium\"\n defaultValue={mod.title}\n onBlur={(e) => renameModule(mod.id, e.target.value)}\n aria-label={t('courses.editor.moduleTitle') || 'Module title'}\n />\n <Button variant=\"ghost\" size=\"sm\" onClick={() => deleteModule(mod.id)}>\n {t('courses.editor.deleteModule') || 'Delete module'}\n </Button>\n </div>\n <div className=\"space-y-2\">\n {lessons.filter((l) => l.moduleId === mod.id).map((lesson) => (\n <div key={lesson.id} className=\"grid grid-cols-1 gap-2 rounded-md border border-border p-3 md:grid-cols-[1fr_1fr_90px_auto]\">\n <Input\n defaultValue={lesson.title}\n placeholder={t('courses.editor.lessonTitle') || 'Lesson title'}\n onBlur={(e) => patchLesson(lesson.id, { title: e.target.value })}\n />\n <Input\n defaultValue={lesson.videoUrl}\n placeholder={t('courses.editor.lessonVideo') || 'Video URL (embed)'}\n onBlur={(e) => patchLesson(lesson.id, { videoUrl: e.target.value })}\n />\n <Input\n type=\"number\"\n defaultValue={String(Math.round(lesson.durationSec / 60))}\n onBlur={(e) => patchLesson(lesson.id, { durationSec: (Number(e.target.value) || 0) * 60 })}\n aria-label={t('courses.editor.lessonDuration') || 'Duration (min)'}\n />\n <Button variant=\"ghost\" size=\"sm\" onClick={() => deleteLesson(lesson.id)}>\n {t('courses.editor.deleteLesson') || 'Remove'}\n </Button>\n </div>\n ))}\n <Button variant=\"outline\" size=\"sm\" onClick={() => addLesson(mod.id)}>\n + {t('courses.editor.addLesson') || 'Add lesson'}\n </Button>\n </div>\n </div>\n ))}\n <Button onClick={addModule}>{t('courses.editor.addModule') || 'Add module'}</Button>\n </div>\n )}\n </div>\n )\n}\nCourseEditorPage.displayName = 'CourseEditorPage'\n\nfunction Field({ label, children }: { label: string; children: React.ReactNode }) {\n return (\n <div className=\"space-y-1.5\">\n <label className=\"text-sm font-medium text-foreground\">{label}</label>\n {children}\n </div>\n )\n}\n","import * as React from 'react'\nimport type { PluginManifest, PluginScope, VerticalId } from '@fayz-ai/core'\nimport { registerTranslations, registerPluginFactory } from '@fayz-ai/core'\nimport {\n getCoursesProviderOptional,\n setCoursesProvider,\n createMockCoursesProvider,\n} from '@fayz-ai/courses'\nimport { coursesLocales } from './locales'\nimport { CoursesListPage } from './pages/CoursesListPage'\nimport { CourseEditorPage } from './pages/CourseEditorPage'\n\n// ---------------------------------------------------------------------------\n// @fayz-ai/plugin-courses — admin management for the courses domain. Mirrors\n// @fayz-ai/plugin-shop: a navigation entry + routes pointing at custom admin pages\n// built directly on @fayz-ai/courses. Native from day one (no saas-core imports).\n// ---------------------------------------------------------------------------\n\nexport interface CoursesPluginOptions {\n navPosition?: number\n navSection?: 'main' | 'secondary' | 'settings'\n navLabel?: string\n scope?: PluginScope\n verticalId?: VerticalId\n}\n\nexport function createCoursesPlugin(options?: CoursesPluginOptions): PluginManifest {\n // Register locales globally so keys resolve even before the I18nProvider mounts.\n registerTranslations(coursesLocales)\n\n // Defensive default: if the host app didn't set a provider, fall back to a\n // mock so the admin still renders. Apps that share admin↔member set their own.\n if (!getCoursesProviderOptional()) {\n setCoursesProvider(createMockCoursesProvider())\n }\n\n return {\n id: 'courses',\n name: options?.navLabel ?? 'Courses',\n icon: 'BookOpen',\n version: '1.0.0',\n scope: options?.scope ?? 'vertical',\n verticalId: options?.verticalId,\n defaultEnabled: true,\n dependencies: [],\n navigation: [\n {\n section: options?.navSection ?? 'main',\n position: options?.navPosition ?? 1,\n label: options?.navLabel ?? 'Courses',\n route: '/courses',\n icon: 'BookOpen',\n },\n ],\n routes: [\n { path: '/courses', component: CoursesListPage as React.ComponentType<unknown> },\n { path: '/courses/:id', component: CourseEditorPage as React.ComponentType<unknown> },\n ],\n widgets: [],\n locales: coursesLocales,\n }\n}\n\n// Self-register the factory so renderApp(manifest) with a PluginRef { id:\n// 'courses' } resolves without a separate plugins.generated.ts entry (apps may\n// still register explicitly; registration is idempotent on id).\nregisterPluginFactory('courses', createCoursesPlugin as (config?: Record<string, unknown>) => PluginManifest)\n\nexport { coursesLocales } from './locales'\n"]}
@@ -0,0 +1,2 @@
1
+ export declare const en: Record<string, string>;
2
+ //# sourceMappingURL=en.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../src/locales/en.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAgCrC,CAAA"}
@@ -0,0 +1,2 @@
1
+ export declare const coursesLocales: Record<string, Record<string, string>>;
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/locales/index.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAGjE,CAAA"}
@@ -0,0 +1,2 @@
1
+ export declare const ptBR: Record<string, string>;
2
+ //# sourceMappingURL=pt-BR.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pt-BR.d.ts","sourceRoot":"","sources":["../../src/locales/pt-BR.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAgCvC,CAAA"}
package/dist/nav.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare function navigateTo(to: string): void;
2
+ //# sourceMappingURL=nav.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nav.d.ts","sourceRoot":"","sources":["../src/nav.ts"],"names":[],"mappings":"AAMA,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE3C"}
@@ -0,0 +1,8 @@
1
+ import * as React from 'react';
2
+ export declare function CourseEditorPage({ id }: {
3
+ id?: string;
4
+ }): React.JSX.Element;
5
+ export declare namespace CourseEditorPage {
6
+ var displayName: string;
7
+ }
8
+ //# sourceMappingURL=CourseEditorPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CourseEditorPage.d.ts","sourceRoot":"","sources":["../../src/pages/CourseEditorPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAW9B,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,EAAE,EAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,qBA6LvD;yBA7Le,gBAAgB"}
@@ -0,0 +1,6 @@
1
+ import * as React from 'react';
2
+ export declare function CoursesListPage(): React.JSX.Element;
3
+ export declare namespace CoursesListPage {
4
+ var displayName: string;
5
+ }
6
+ //# sourceMappingURL=CoursesListPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CoursesListPage.d.ts","sourceRoot":"","sources":["../../src/pages/CoursesListPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAY9B,wBAAgB,eAAe,sBA2F9B;yBA3Fe,eAAe"}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@fayz-ai/plugin-courses",
3
+ "version": "0.2.0",
4
+ "description": "Fayz SDK — courses admin management plugin",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "source": "./src/index.ts",
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "src"
20
+ ],
21
+ "peerDependencies": {
22
+ "react": "^18.0.0 || ^19.0.0",
23
+ "react-dom": "^18.0.0 || ^19.0.0"
24
+ },
25
+ "dependencies": {
26
+ "lucide-react": "^0.400.0",
27
+ "@fayz-ai/ui": "^0.2.0",
28
+ "@fayz-ai/core": "^0.2.0",
29
+ "@fayz-ai/courses": "^0.2.0"
30
+ },
31
+ "devDependencies": {
32
+ "@types/react": "^18.3.0",
33
+ "react": "^18.3.0",
34
+ "tsup": "^8.2.0",
35
+ "typescript": "^5.5.0"
36
+ },
37
+ "license": "MIT",
38
+ "keywords": [
39
+ "fayz",
40
+ "fayz-plugin",
41
+ "fayz-sdk",
42
+ "courses"
43
+ ],
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "scripts": {
48
+ "build": "tsup && tsc --emitDeclarationOnly --declaration --declarationMap --noEmit false",
49
+ "dev": "tsup --watch",
50
+ "typecheck": "tsc --noEmit",
51
+ "clean": "rm -rf dist"
52
+ }
53
+ }
package/src/index.ts ADDED
@@ -0,0 +1,69 @@
1
+ import * as React from 'react'
2
+ import type { PluginManifest, PluginScope, VerticalId } from '@fayz-ai/core'
3
+ import { registerTranslations, registerPluginFactory } from '@fayz-ai/core'
4
+ import {
5
+ getCoursesProviderOptional,
6
+ setCoursesProvider,
7
+ createMockCoursesProvider,
8
+ } from '@fayz-ai/courses'
9
+ import { coursesLocales } from './locales'
10
+ import { CoursesListPage } from './pages/CoursesListPage'
11
+ import { CourseEditorPage } from './pages/CourseEditorPage'
12
+
13
+ // ---------------------------------------------------------------------------
14
+ // @fayz-ai/plugin-courses — admin management for the courses domain. Mirrors
15
+ // @fayz-ai/plugin-shop: a navigation entry + routes pointing at custom admin pages
16
+ // built directly on @fayz-ai/courses. Native from day one (no saas-core imports).
17
+ // ---------------------------------------------------------------------------
18
+
19
+ export interface CoursesPluginOptions {
20
+ navPosition?: number
21
+ navSection?: 'main' | 'secondary' | 'settings'
22
+ navLabel?: string
23
+ scope?: PluginScope
24
+ verticalId?: VerticalId
25
+ }
26
+
27
+ export function createCoursesPlugin(options?: CoursesPluginOptions): PluginManifest {
28
+ // Register locales globally so keys resolve even before the I18nProvider mounts.
29
+ registerTranslations(coursesLocales)
30
+
31
+ // Defensive default: if the host app didn't set a provider, fall back to a
32
+ // mock so the admin still renders. Apps that share admin↔member set their own.
33
+ if (!getCoursesProviderOptional()) {
34
+ setCoursesProvider(createMockCoursesProvider())
35
+ }
36
+
37
+ return {
38
+ id: 'courses',
39
+ name: options?.navLabel ?? 'Courses',
40
+ icon: 'BookOpen',
41
+ version: '1.0.0',
42
+ scope: options?.scope ?? 'vertical',
43
+ verticalId: options?.verticalId,
44
+ defaultEnabled: true,
45
+ dependencies: [],
46
+ navigation: [
47
+ {
48
+ section: options?.navSection ?? 'main',
49
+ position: options?.navPosition ?? 1,
50
+ label: options?.navLabel ?? 'Courses',
51
+ route: '/courses',
52
+ icon: 'BookOpen',
53
+ },
54
+ ],
55
+ routes: [
56
+ { path: '/courses', component: CoursesListPage as React.ComponentType<unknown> },
57
+ { path: '/courses/:id', component: CourseEditorPage as React.ComponentType<unknown> },
58
+ ],
59
+ widgets: [],
60
+ locales: coursesLocales,
61
+ }
62
+ }
63
+
64
+ // Self-register the factory so renderApp(manifest) with a PluginRef { id:
65
+ // 'courses' } resolves without a separate plugins.generated.ts entry (apps may
66
+ // still register explicitly; registration is idempotent on id).
67
+ registerPluginFactory('courses', createCoursesPlugin as (config?: Record<string, unknown>) => PluginManifest)
68
+
69
+ export { coursesLocales } from './locales'