@hed-hog/lms 0.0.306 → 0.0.309

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 (117) hide show
  1. package/dist/course/course-structure.controller.d.ts +60 -0
  2. package/dist/course/course-structure.controller.d.ts.map +1 -1
  3. package/dist/course/course-structure.controller.js +79 -0
  4. package/dist/course/course-structure.controller.js.map +1 -1
  5. package/dist/course/course-structure.service.d.ts +61 -1
  6. package/dist/course/course-structure.service.d.ts.map +1 -1
  7. package/dist/course/course-structure.service.js +326 -1
  8. package/dist/course/course-structure.service.js.map +1 -1
  9. package/dist/course/course.controller.d.ts +52 -4
  10. package/dist/course/course.controller.d.ts.map +1 -1
  11. package/dist/course/course.service.d.ts +52 -5
  12. package/dist/course/course.service.d.ts.map +1 -1
  13. package/dist/course/course.service.js +78 -57
  14. package/dist/course/course.service.js.map +1 -1
  15. package/dist/course/dto/create-course-structure-lesson.dto.d.ts.map +1 -1
  16. package/dist/course/dto/create-course-structure-lesson.dto.js +5 -1
  17. package/dist/course/dto/create-course-structure-lesson.dto.js.map +1 -1
  18. package/dist/course/dto/create-course.dto.d.ts +1 -1
  19. package/dist/course/dto/create-course.dto.d.ts.map +1 -1
  20. package/dist/course/dto/create-course.dto.js +4 -1
  21. package/dist/course/dto/create-course.dto.js.map +1 -1
  22. package/dist/course/dto/move-lesson.dto.d.ts +10 -0
  23. package/dist/course/dto/move-lesson.dto.d.ts.map +1 -0
  24. package/dist/course/dto/move-lesson.dto.js +28 -0
  25. package/dist/course/dto/move-lesson.dto.js.map +1 -0
  26. package/dist/course/dto/paste-lessons.dto.d.ts +4 -0
  27. package/dist/course/dto/paste-lessons.dto.d.ts.map +1 -0
  28. package/dist/course/dto/paste-lessons.dto.js +24 -0
  29. package/dist/course/dto/paste-lessons.dto.js.map +1 -0
  30. package/dist/course/dto/reorder-lessons.dto.d.ts +5 -0
  31. package/dist/course/dto/reorder-lessons.dto.d.ts.map +1 -0
  32. package/dist/course/dto/reorder-lessons.dto.js +24 -0
  33. package/dist/course/dto/reorder-lessons.dto.js.map +1 -0
  34. package/dist/course/dto/reorder-sessions.dto.d.ts +5 -0
  35. package/dist/course/dto/reorder-sessions.dto.d.ts.map +1 -0
  36. package/dist/course/dto/reorder-sessions.dto.js +24 -0
  37. package/dist/course/dto/reorder-sessions.dto.js.map +1 -0
  38. package/dist/training/training.controller.js +1 -1
  39. package/dist/training/training.controller.js.map +1 -1
  40. package/hedhog/data/image_type.yaml +20 -0
  41. package/hedhog/data/menu.yaml +2 -2
  42. package/hedhog/data/route.yaml +60 -6
  43. package/hedhog/frontend/app/_components/class-form-sheet.tsx.ejs +146 -165
  44. package/hedhog/frontend/app/_components/course-avatar.tsx.ejs +70 -0
  45. package/hedhog/frontend/app/_components/course-form-sheet.tsx.ejs +372 -22
  46. package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +437 -77
  47. package/hedhog/frontend/app/classes/page.tsx.ejs +311 -289
  48. package/hedhog/frontend/app/courses/[id]/_components/CourseCertificateCard.tsx.ejs +10 -7
  49. package/hedhog/frontend/app/courses/[id]/_components/CourseClassificationCard.tsx.ejs +23 -32
  50. package/hedhog/frontend/app/courses/[id]/_components/CourseContentCard.tsx.ejs +3 -9
  51. package/hedhog/frontend/app/courses/[id]/_components/CourseDangerZoneCard.tsx.ejs +26 -16
  52. package/hedhog/frontend/app/courses/[id]/_components/CourseFlagsCard.tsx.ejs +19 -5
  53. package/hedhog/frontend/app/courses/[id]/_components/CourseMainInfoCard.tsx.ejs +10 -14
  54. package/hedhog/frontend/app/courses/[id]/_components/CourseMediaCard.tsx.ejs +131 -107
  55. package/hedhog/frontend/app/courses/[id]/_components/CourseRelationsCard.tsx.ejs +10 -7
  56. package/hedhog/frontend/app/courses/[id]/_components/CourseSectionCard.tsx.ejs +38 -19
  57. package/hedhog/frontend/app/courses/[id]/_components/CourseSummaryCard.tsx.ejs +1 -1
  58. package/hedhog/frontend/app/courses/[id]/_components/course-edit-types.ts.ejs +1 -1
  59. package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +336 -1057
  60. package/hedhog/frontend/app/courses/[id]/structure/_components/confirm-dialog.tsx.ejs +45 -0
  61. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-dnd.tsx.ejs +362 -0
  62. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-panel.tsx.ejs +111 -0
  63. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-skeleton.tsx.ejs +64 -0
  64. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree.tsx.ejs +134 -0
  65. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-course.tsx.ejs +113 -0
  66. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson.tsx.ejs +314 -0
  67. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-panel.tsx.ejs +62 -0
  68. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-session.tsx.ejs +174 -0
  69. package/hedhog/frontend/app/courses/[id]/structure/_components/drag-handle.tsx.ejs +58 -0
  70. package/hedhog/frontend/app/courses/[id]/structure/_components/drag-overlay.tsx.ejs +52 -0
  71. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-bulk.tsx.ejs +276 -0
  72. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +1216 -0
  73. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +1827 -0
  74. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-session.tsx.ejs +443 -0
  75. package/hedhog/frontend/app/courses/[id]/structure/_components/highlighted-text.tsx.ejs +41 -0
  76. package/hedhog/frontend/app/courses/[id]/structure/_components/mock-data.ts.ejs +184 -0
  77. package/hedhog/frontend/app/courses/[id]/structure/_components/multi-select-bar.tsx.ejs +264 -0
  78. package/hedhog/frontend/app/courses/[id]/structure/_components/search-filter.tsx.ejs +96 -0
  79. package/hedhog/frontend/app/courses/[id]/structure/_components/session-picker-dialog.tsx.ejs +74 -0
  80. package/hedhog/frontend/app/courses/[id]/structure/_components/shortcuts-help.tsx.ejs +136 -0
  81. package/hedhog/frontend/app/courses/[id]/structure/_components/sortable-tree-row.tsx.ejs +80 -0
  82. package/hedhog/frontend/app/courses/[id]/structure/_components/store.ts.ejs +948 -0
  83. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-context-menu.tsx.ejs +525 -0
  84. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-display-settings-popover.tsx.ejs +150 -0
  85. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-helpers.ts.ejs +182 -0
  86. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-course.tsx.ejs +52 -0
  87. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-lesson.tsx.ejs +271 -0
  88. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-session.tsx.ejs +167 -0
  89. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row.tsx.ejs +108 -0
  90. package/hedhog/frontend/app/courses/[id]/structure/_components/types.ts.ejs +122 -0
  91. package/hedhog/frontend/app/courses/[id]/structure/_components/use-course-structure-shortcuts.ts.ejs +318 -0
  92. package/hedhog/frontend/app/courses/[id]/structure/_components/use-tree-display-settings.ts.ejs +97 -0
  93. package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs +347 -0
  94. package/hedhog/frontend/app/courses/[id]/structure/_data/course-structure-contract.ts.ejs +195 -0
  95. package/hedhog/frontend/app/courses/[id]/structure/_data/services/course-structure.service.ts.ejs +420 -0
  96. package/hedhog/frontend/app/courses/[id]/structure/_data/types/api-course.types.ts.ejs +254 -0
  97. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +987 -0
  98. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-query.ts.ejs +86 -0
  99. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure.ts.ejs +160 -0
  100. package/hedhog/frontend/app/courses/[id]/structure/page.tsx.ejs +10 -3212
  101. package/hedhog/frontend/app/courses/page.tsx.ejs +45 -26
  102. package/hedhog/frontend/app/{training → paths}/page.tsx.ejs +29 -7
  103. package/hedhog/frontend/messages/en.json +88 -10
  104. package/hedhog/frontend/messages/pt.json +88 -10
  105. package/hedhog/table/course.yaml +1 -1
  106. package/hedhog/table/image_type.yaml +14 -0
  107. package/package.json +7 -7
  108. package/src/course/course-structure.controller.ts +63 -0
  109. package/src/course/course-structure.service.ts +390 -3
  110. package/src/course/course.service.ts +59 -27
  111. package/src/course/dto/create-course-structure-lesson.dto.ts +3 -2
  112. package/src/course/dto/create-course.dto.ts +4 -1
  113. package/src/course/dto/move-lesson.dto.ts +17 -0
  114. package/src/course/dto/paste-lessons.dto.ts +9 -0
  115. package/src/course/dto/reorder-lessons.dto.ts +10 -0
  116. package/src/course/dto/reorder-sessions.dto.ts +10 -0
  117. package/src/training/training.controller.ts +1 -1
@@ -62,6 +62,7 @@ import { useRouter } from 'next/navigation';
62
62
  import { useEffect, useMemo, useRef, useState } from 'react';
63
63
  import { useForm, useWatch } from 'react-hook-form';
64
64
  import { toast } from 'sonner';
65
+ import { CourseAvatar } from '../_components/course-avatar';
65
66
  import {
66
67
  CourseCategoryOption,
67
68
  CourseFormSheet,
@@ -76,7 +77,7 @@ const API_COURSES_CACHE_KEY = 'lms:courses:api-cache';
76
77
 
77
78
  interface Curso {
78
79
  id: number;
79
- codigo: string;
80
+ slug: string;
80
81
  nomeInterno: string;
81
82
  tituloComercial: string;
82
83
  descricao: string;
@@ -90,11 +91,12 @@ interface Curso {
90
91
  listado: boolean;
91
92
  alunosInscritos: number;
92
93
  criadoEm: string;
94
+ logoFileId?: number | null;
93
95
  }
94
96
 
95
97
  type ApiCourse = {
96
98
  id: number;
97
- code: string;
99
+ name: string;
98
100
  slug: string;
99
101
  title: string;
100
102
  description: string;
@@ -110,6 +112,7 @@ type ApiCourse = {
110
112
  isListed: boolean;
111
113
  enrollmentCount: number;
112
114
  createdAt: string;
115
+ logoFileId?: number | null;
113
116
  };
114
117
 
115
118
  type ApiCourseList = {
@@ -199,8 +202,8 @@ function toPtStatus(status?: string | null): CourseSheetFormValues['status'] {
199
202
  function mapApiCourse(course: ApiCourse): Curso {
200
203
  return {
201
204
  id: course.id,
202
- codigo: course.code,
203
- nomeInterno: course.slug,
205
+ slug: course.slug,
206
+ nomeInterno: course.name ?? '',
204
207
  tituloComercial: course.title,
205
208
  descricao: course.description ?? '',
206
209
  primaryColor: course.primaryColor ?? null,
@@ -213,6 +216,7 @@ function mapApiCourse(course: ApiCourse): Curso {
213
216
  listado: course.isListed,
214
217
  alunosInscritos: course.enrollmentCount ?? 0,
215
218
  criadoEm: course.createdAt ?? '',
219
+ logoFileId: course.logoFileId ?? null,
216
220
  };
217
221
  }
218
222
 
@@ -592,6 +596,7 @@ export default function CursosPage() {
592
596
  setEditingCurso(curso);
593
597
  form.reset({
594
598
  nomeInterno: curso.nomeInterno,
599
+ slug: curso.slug,
595
600
  tituloComercial: curso.tituloComercial,
596
601
  descricao: curso.descricao,
597
602
  primaryColor: curso.primaryColor || '#1D4ED8',
@@ -599,6 +604,7 @@ export default function CursosPage() {
599
604
  nivel: curso.nivel,
600
605
  status: curso.status,
601
606
  categorias: curso.categorias,
607
+ logoFileId: curso.logoFileId ?? null,
602
608
  });
603
609
  setSheetOpen(true);
604
610
  }
@@ -607,7 +613,8 @@ export default function CursosPage() {
607
613
  setSaving(true);
608
614
  try {
609
615
  const payload = {
610
- slug: data.nomeInterno.trim(),
616
+ name: data.nomeInterno.trim(),
617
+ slug: data.slug.trim().toLowerCase(),
611
618
  title: data.tituloComercial,
612
619
  description: data.descricao,
613
620
  level: toApiLevel(data.nivel),
@@ -617,6 +624,7 @@ export default function CursosPage() {
617
624
  primaryContrastColor: getContrastColor(data.primaryColor),
618
625
  secondaryColor: data.secondaryColor,
619
626
  secondaryContrastColor: getContrastColor(data.secondaryColor),
627
+ logoFileId: data.logoFileId ?? null,
620
628
  };
621
629
 
622
630
  if (editingCurso) {
@@ -1071,13 +1079,16 @@ export default function CursosPage() {
1071
1079
 
1072
1080
  <CardContent className="flex h-full flex-col p-4">
1073
1081
  <div className="mb-3 flex items-start gap-3">
1074
- <div className="flex size-12 shrink-0 items-center justify-center rounded-xl border bg-muted/60">
1075
- <BookOpen className="size-6 text-foreground" />
1076
- </div>
1082
+ <CourseAvatar
1083
+ fileId={curso.logoFileId}
1084
+ title={curso.nomeInterno}
1085
+ className="size-12 rounded-xl"
1086
+ iconSize="size-6"
1087
+ />
1077
1088
  <div className="min-w-0 flex-1">
1078
1089
  <div className="mb-1.5 flex items-start justify-between gap-2">
1079
1090
  <h3 className="line-clamp-2 font-semibold leading-snug text-foreground">
1080
- {curso.tituloComercial}
1091
+ {curso.nomeInterno}
1081
1092
  </h3>
1082
1093
  <DropdownMenu>
1083
1094
  <DropdownMenuTrigger asChild>
@@ -1123,12 +1134,12 @@ export default function CursosPage() {
1123
1134
  </div>
1124
1135
  <p className="text-xs text-muted-foreground">
1125
1136
  <code className="rounded bg-muted px-1.5 py-0.5 font-mono text-[10px]">
1126
- {curso.codigo}
1137
+ {curso.slug}
1127
1138
  </code>
1128
1139
  <span className="mx-1.5 text-muted-foreground/50">
1129
1140
  |
1130
1141
  </span>
1131
- <span>{curso.nomeInterno}</span>
1142
+ <span>{curso.tituloComercial}</span>
1132
1143
  </p>
1133
1144
  </div>
1134
1145
  </div>
@@ -1236,25 +1247,33 @@ export default function CursosPage() {
1236
1247
  onCheckedChange={() => {}}
1237
1248
  onClick={(e) => toggleSelect(curso.id, e)}
1238
1249
  aria-label={t('table.selectCourse', {
1239
- title: curso.tituloComercial,
1250
+ title: curso.nomeInterno,
1240
1251
  })}
1241
1252
  className="border-2 bg-background shadow-sm"
1242
1253
  />
1243
1254
  </TableCell>
1244
1255
  <TableCell>
1245
- <div className="min-w-0">
1246
- <p className="truncate font-semibold text-foreground">
1247
- {curso.tituloComercial}
1248
- </p>
1249
- <p className="mt-1 truncate text-xs text-muted-foreground">
1250
- <code className="rounded bg-muted px-1.5 py-0.5 font-mono text-[10px]">
1251
- {curso.codigo}
1252
- </code>
1253
- <span className="mx-1.5 text-muted-foreground/50">
1254
- |
1255
- </span>
1256
- <span>{curso.nomeInterno}</span>
1257
- </p>
1256
+ <div className="flex min-w-0 items-center gap-3">
1257
+ <CourseAvatar
1258
+ fileId={curso.logoFileId}
1259
+ title={curso.nomeInterno}
1260
+ className="size-8 shrink-0 rounded-lg"
1261
+ iconSize="size-4"
1262
+ />
1263
+ <div className="min-w-0">
1264
+ <p className="truncate font-semibold text-foreground">
1265
+ {curso.nomeInterno}
1266
+ </p>
1267
+ <p className="mt-1 truncate text-xs text-muted-foreground">
1268
+ <code className="rounded bg-muted px-1.5 py-0.5 font-mono text-[10px]">
1269
+ {curso.slug}
1270
+ </code>
1271
+ <span className="mx-1.5 text-muted-foreground/50">
1272
+ |
1273
+ </span>
1274
+ <span>{curso.tituloComercial}</span>
1275
+ </p>
1276
+ </div>
1258
1277
  </div>
1259
1278
  </TableCell>
1260
1279
  <TableCell>
@@ -1383,8 +1402,8 @@ export default function CursosPage() {
1383
1402
  form={form}
1384
1403
  onSubmit={onSubmit}
1385
1404
  categories={categoryOptions}
1386
- courseCode={editingCurso?.codigo}
1387
1405
  onCreateCategory={() => router.push('/category?new=1')}
1406
+ initialLogoFileId={editingCurso?.logoFileId ?? null}
1388
1407
  t={t}
1389
1408
  />
1390
1409
 
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
 
3
+ import { CourseAvatar } from '@/app/(app)/(libraries)/lms/_components/course-avatar';
3
4
  import {
4
5
  CourseCategoryOption,
5
6
  CourseFormSheet,
@@ -141,6 +142,8 @@ interface CursoOption {
141
142
  cargaHoraria: number;
142
143
  categories: string[];
143
144
  area: string;
145
+ name?: string;
146
+ logoFileId?: number | null;
144
147
  }
145
148
 
146
149
  interface ExameOption {
@@ -188,6 +191,8 @@ type ApiCourseListResponse = {
188
191
  title: string;
189
192
  durationHours: number;
190
193
  categories?: string[];
194
+ name?: string;
195
+ logoFileId?: number | null;
191
196
  }>;
192
197
  };
193
198
 
@@ -590,7 +595,7 @@ export default function TrainingPage() {
590
595
  ],
591
596
  queryFn: async () => {
592
597
  const response = await request<ApiTrainingListResponse>({
593
- url: '/lms/training',
598
+ url: '/lms/paths',
594
599
  method: 'GET',
595
600
  params: {
596
601
  page: currentPage,
@@ -613,7 +618,7 @@ export default function TrainingPage() {
613
618
  queryKey: ['lms-training-stats'],
614
619
  queryFn: async () => {
615
620
  const response = await request<ApiTrainingStatsResponse>({
616
- url: '/lms/training/stats',
621
+ url: '/lms/paths/stats',
617
622
  method: 'GET',
618
623
  });
619
624
 
@@ -708,6 +713,8 @@ export default function TrainingPage() {
708
713
  cargaHoraria: course.durationHours ?? 0,
709
714
  categories: course.categories ?? [],
710
715
  area: categorySlugToArea(course.categories ?? []),
716
+ name: course.name,
717
+ logoFileId: course.logoFileId ?? null,
711
718
  })
712
719
  );
713
720
 
@@ -975,7 +982,7 @@ export default function TrainingPage() {
975
982
  try {
976
983
  const [response] = await Promise.all([
977
984
  request<Formacao>({
978
- url: `/lms/training/${formacao.id}`,
985
+ url: `/lms/paths/${formacao.id}`,
979
986
  method: 'GET',
980
987
  }),
981
988
  refetchCourses(),
@@ -1220,7 +1227,7 @@ export default function TrainingPage() {
1220
1227
  }
1221
1228
 
1222
1229
  await request({
1223
- url: `/lms/training/${editingFormacao.id}`,
1230
+ url: `/lms/paths/${editingFormacao.id}`,
1224
1231
  method: 'PATCH',
1225
1232
  data: payload,
1226
1233
  });
@@ -1243,7 +1250,7 @@ export default function TrainingPage() {
1243
1250
  };
1244
1251
 
1245
1252
  await request({
1246
- url: '/lms/training',
1253
+ url: '/lms/paths',
1247
1254
  method: 'POST',
1248
1255
  data: payload,
1249
1256
  });
@@ -1334,7 +1341,7 @@ export default function TrainingPage() {
1334
1341
  if (!formacaoToDelete) return;
1335
1342
 
1336
1343
  await request({
1337
- url: `/lms/training/${formacaoToDelete.id}`,
1344
+ url: `/lms/paths/${formacaoToDelete.id}`,
1338
1345
  method: 'DELETE',
1339
1346
  });
1340
1347
 
@@ -2054,7 +2061,22 @@ export default function TrainingPage() {
2054
2061
  key={course.id}
2055
2062
  value={String(course.id)}
2056
2063
  >
2057
- {course.nome} ({course.cargaHoraria}h)
2064
+ <div className="flex items-center gap-2">
2065
+ <CourseAvatar
2066
+ fileId={course.logoFileId}
2067
+ title={course.nome}
2068
+ className="size-5 shrink-0 rounded"
2069
+ iconSize="size-3"
2070
+ />
2071
+ <div className="min-w-0">
2072
+ <span className="truncate">
2073
+ {course.nome}
2074
+ </span>
2075
+ <span className="ml-1.5 text-xs text-muted-foreground">
2076
+ {course.name ?? '—'} · #{course.id}
2077
+ </span>
2078
+ </div>
2079
+ </div>
2058
2080
  </SelectItem>
2059
2081
  ))}
2060
2082
  </SelectContent>
@@ -526,15 +526,15 @@
526
526
  "edit": "Update the course information below."
527
527
  },
528
528
  "fields": {
529
- "code": {
530
- "label": "Course Code",
531
- "placeholder": "Ex: REACT-ADV",
532
- "description": "Unique course identifier (e.g.: REACT-ADV)"
533
- },
534
529
  "internalName": {
535
530
  "label": "Internal Name",
536
- "placeholder": "Ex: react-advanced",
537
- "description": "Internal slug for system use"
531
+ "placeholder": "Ex: Advanced React",
532
+ "description": "Internal course name for administrative use"
533
+ },
534
+ "slug": {
535
+ "label": "Slug",
536
+ "placeholder": "Ex: advanced-react",
537
+ "description": "Unique URL identifier (lowercase letters, numbers and hyphens)"
538
538
  },
539
539
  "commercialTitle": {
540
540
  "label": "Commercial Title",
@@ -596,6 +596,7 @@
596
596
  "codeMaxLength": "Code must have at most 16 characters",
597
597
  "codePattern": "Only letters, numbers, and hyphens",
598
598
  "internalNameMinLength": "Internal name must have at least 3 characters",
599
+ "slugPattern": "Slug must contain only lowercase letters, numbers and hyphens",
599
600
  "commercialTitleMinLength": "Commercial title must have at least 3 characters",
600
601
  "descriptionMinLength": "Description must have at least 10 characters",
601
602
  "levelRequired": "Select a level",
@@ -666,7 +667,8 @@
666
667
  "autoSaveSaved": "Session saved automatically",
667
668
  "autoSaveError": "Automatic session save failed",
668
669
  "create": "Create Session",
669
- "update": "Update"
670
+ "update": "Update",
671
+ "save": "Save session"
670
672
  },
671
673
  "lessonForm": {
672
674
  "titleCreate": "New Lesson",
@@ -736,7 +738,25 @@
736
738
  "autoSaveError": "Automatic save failed",
737
739
  "cancel": "Cancel",
738
740
  "create": "Create Lesson",
739
- "update": "Update"
741
+ "update": "Update",
742
+ "save": "Save lesson",
743
+ "tabData": "Data",
744
+ "tabTranscription": "Transcription",
745
+ "tabResources": "Resources",
746
+ "videoProvider": "Video provider",
747
+ "videoUrl": "Video URL",
748
+ "autoDuration": "Auto duration",
749
+ "postContent": "Content",
750
+ "linkedExam": "Linked exam",
751
+ "linkedExamPlaceholder": "Exam ID...",
752
+ "transcriptionHint": "Full video transcription.",
753
+ "transcriptionVideoOnly": "Transcription is only available for video lessons.",
754
+ "transcriptionPlaceholder": "Write or paste the transcription here...",
755
+ "resourcesCount": "{count} resource(s)",
756
+ "addResource": "Add resource",
757
+ "noResources": "No resources added",
758
+ "public": "Public",
759
+ "private": "Private"
740
760
  },
741
761
  "deleteSession": {
742
762
  "title": "Delete Session",
@@ -799,9 +819,13 @@
799
819
  "sessionUpdated": "Session updated!",
800
820
  "sessionCreated": "Session created!",
801
821
  "sessionDeleted": "Session removed!",
822
+ "sessionDuplicated": "Session duplicated!",
802
823
  "lessonUpdated": "Lesson updated!",
803
824
  "lessonCreated": "Lesson created!",
804
825
  "lessonDeleted": "Lesson removed!",
826
+ "lessonDuplicated": "Lesson duplicated!",
827
+ "lessonMoved": "Lesson moved!",
828
+ "courseUpdated": "Course updated!",
805
829
  "resourceAdded": "Resource added!",
806
830
  "resourceRemoved": "Resource removed!",
807
831
  "lessonsDeleted": "{count} lesson(s) removed!",
@@ -828,6 +852,56 @@
828
852
  "titleMin": "Title must be at least 3 characters",
829
853
  "durationMin": "Minimum duration of 1 minute",
830
854
  "typeRequired": "Select a type"
855
+ },
856
+ "pageDescription": "{sessions} sessions · {lessons} lessons · {hours}h {minutes}min",
857
+ "mobileTabs": {
858
+ "tree": "Tree",
859
+ "detail": "Details"
860
+ },
861
+ "breadcrumbs": {
862
+ "lms": "LMS",
863
+ "courses": "Courses",
864
+ "structure": "Structure"
865
+ },
866
+ "search": {
867
+ "placeholder": "Search..."
868
+ },
869
+ "tree": {
870
+ "collapseAll": "Collapse all",
871
+ "expandAll": "Expand all",
872
+ "addItem": "Add item",
873
+ "addSession": "New session"
874
+ },
875
+ "contextMenu": {
876
+ "edit": "Edit",
877
+ "addLesson": "Add lesson",
878
+ "addSession": "New session",
879
+ "duplicate": "Duplicate",
880
+ "mergeWith": "Merge with...",
881
+ "moveTo": "Move to...",
882
+ "delete": "Delete"
883
+ },
884
+ "deleteDialog": {
885
+ "title": "Confirm deletion",
886
+ "sessionMessage": "Are you sure? All lessons in this session will be removed.",
887
+ "lessonMessage": "Are you sure? This lesson will be permanently removed.",
888
+ "cancel": "Cancel",
889
+ "confirm": "Delete"
890
+ },
891
+ "detailPanel": {
892
+ "emptyHint": "Select an item in the tree to view details"
893
+ },
894
+ "detailCourse": {
895
+ "subtitle": "General course data",
896
+ "title": "Title",
897
+ "code": "Code",
898
+ "slug": "Slug",
899
+ "description": "Description",
900
+ "published": "Published",
901
+ "save": "Save course"
902
+ },
903
+ "detailSession": {
904
+ "subtitle": "{lessons} lessons · {minutes}min"
831
905
  }
832
906
  }
833
907
  },
@@ -2434,7 +2508,11 @@
2434
2508
  "helper": "Click a lesson to edit it or create new lessons.",
2435
2509
  "viewSingle": "Month",
2436
2510
  "viewQuarter": "Quarter",
2437
- "viewYear": "Year"
2511
+ "viewYear": "Year",
2512
+ "empty": {
2513
+ "title": "No lessons registered",
2514
+ "description": "Add the first lesson to get started."
2515
+ }
2438
2516
  },
2439
2517
  "info": {
2440
2518
  "period": "Period",
@@ -253,7 +253,7 @@
253
253
  "internalName": {
254
254
  "label": "Nome Interno *",
255
255
  "placeholder": "Ex: react-avancado",
256
- "description": "Slug interno para uso do sistema"
256
+ "description": "Nome Interno para uso do sistema"
257
257
  },
258
258
  "title": {
259
259
  "label": "Título Comercial *",
@@ -535,15 +535,15 @@
535
535
  "edit": "Atualize as informações do curso abaixo."
536
536
  },
537
537
  "fields": {
538
- "code": {
539
- "label": "Código do Curso",
540
- "placeholder": "Ex: REACT-ADV",
541
- "description": "Identificador único do curso (ex: REACT-ADV)"
542
- },
543
538
  "internalName": {
544
539
  "label": "Nome Interno",
540
+ "placeholder": "Ex: React Avançado",
541
+ "description": "Nome interno do curso para uso administrativo"
542
+ },
543
+ "slug": {
544
+ "label": "Slug",
545
545
  "placeholder": "Ex: react-avancado",
546
- "description": "Slug interno para uso do sistema"
546
+ "description": "Identificador único na URL (letras minúsculas, números e hífens)"
547
547
  },
548
548
  "commercialTitle": {
549
549
  "label": "Título Comercial",
@@ -605,6 +605,7 @@
605
605
  "codeMaxLength": "Código deve ter no máximo 16 caracteres",
606
606
  "codePattern": "Apenas letras, números e hífens",
607
607
  "internalNameMinLength": "Nome interno deve ter pelo menos 3 caracteres",
608
+ "slugPattern": "Slug deve conter apenas letras minúsculas, números e hífens",
608
609
  "commercialTitleMinLength": "Título comercial deve ter pelo menos 3 caracteres",
609
610
  "descriptionMinLength": "Descrição deve ter pelo menos 10 caracteres",
610
611
  "levelRequired": "Selecione um nível",
@@ -675,7 +676,8 @@
675
676
  "autoSaveSaved": "Sessão salva automaticamente",
676
677
  "autoSaveError": "Falha no salvamento automático da sessão",
677
678
  "create": "Criar Sessão",
678
- "update": "Atualizar"
679
+ "update": "Atualizar",
680
+ "save": "Salvar sessão"
679
681
  },
680
682
  "lessonForm": {
681
683
  "titleCreate": "Nova Aula",
@@ -745,7 +747,25 @@
745
747
  "autoSaveError": "Falha no salvamento automático",
746
748
  "cancel": "Cancelar",
747
749
  "create": "Criar Aula",
748
- "update": "Atualizar"
750
+ "update": "Atualizar",
751
+ "save": "Salvar aula",
752
+ "tabData": "Dados",
753
+ "tabTranscription": "Transcrição",
754
+ "tabResources": "Recursos",
755
+ "videoProvider": "Provedor de vídeo",
756
+ "videoUrl": "URL do vídeo",
757
+ "autoDuration": "Duração automática",
758
+ "postContent": "Conteúdo",
759
+ "linkedExam": "Exame vinculado",
760
+ "linkedExamPlaceholder": "ID do exame...",
761
+ "transcriptionHint": "Transcrição completa do vídeo.",
762
+ "transcriptionVideoOnly": "A transcrição está disponível somente para aulas do tipo vídeo.",
763
+ "transcriptionPlaceholder": "Escreva ou cole a transcrição aqui...",
764
+ "resourcesCount": "{count} recurso(s)",
765
+ "addResource": "Adicionar recurso",
766
+ "noResources": "Nenhum recurso adicionado",
767
+ "public": "Público",
768
+ "private": "Privado"
749
769
  },
750
770
  "deleteSession": {
751
771
  "title": "Excluir Sessão",
@@ -808,9 +828,13 @@
808
828
  "sessionUpdated": "Sessão atualizada!",
809
829
  "sessionCreated": "Sessão criada!",
810
830
  "sessionDeleted": "Sessão removida!",
831
+ "sessionDuplicated": "Sessão duplicada!",
811
832
  "lessonUpdated": "Aula atualizada!",
812
833
  "lessonCreated": "Aula criada!",
813
834
  "lessonDeleted": "Aula removida!",
835
+ "lessonDuplicated": "Aula duplicada!",
836
+ "lessonMoved": "Aula movida!",
837
+ "courseUpdated": "Curso atualizado!",
814
838
  "resourceAdded": "Recurso adicionado!",
815
839
  "resourceRemoved": "Recurso removido!",
816
840
  "lessonsDeleted": "{count} aula(s) removida(s)!",
@@ -837,6 +861,56 @@
837
861
  "titleMin": "Título deve ter pelo menos 3 caracteres",
838
862
  "durationMin": "Duração mínima de 1 minuto",
839
863
  "typeRequired": "Selecione um tipo"
864
+ },
865
+ "pageDescription": "{sessions} sessões · {lessons} aulas · {hours}h {minutes}min",
866
+ "mobileTabs": {
867
+ "tree": "Árvore",
868
+ "detail": "Detalhes"
869
+ },
870
+ "breadcrumbs": {
871
+ "lms": "LMS",
872
+ "courses": "Cursos",
873
+ "structure": "Estrutura"
874
+ },
875
+ "search": {
876
+ "placeholder": "Buscar..."
877
+ },
878
+ "tree": {
879
+ "collapseAll": "Recolher tudo",
880
+ "expandAll": "Expandir tudo",
881
+ "addItem": "Adicionar item",
882
+ "addSession": "Nova sessão"
883
+ },
884
+ "contextMenu": {
885
+ "edit": "Editar",
886
+ "addLesson": "Adicionar aula",
887
+ "addSession": "Nova sessão",
888
+ "duplicate": "Duplicar",
889
+ "mergeWith": "Mesclar com...",
890
+ "moveTo": "Mover para...",
891
+ "delete": "Excluir"
892
+ },
893
+ "deleteDialog": {
894
+ "title": "Confirmar exclusão",
895
+ "sessionMessage": "Tem certeza? Todas as aulas desta sessão serão removidas.",
896
+ "lessonMessage": "Tem certeza? Esta aula será removida permanentemente.",
897
+ "cancel": "Cancelar",
898
+ "confirm": "Excluir"
899
+ },
900
+ "detailPanel": {
901
+ "emptyHint": "Selecione um item na árvore para ver os detalhes"
902
+ },
903
+ "detailCourse": {
904
+ "subtitle": "Dados gerais do curso",
905
+ "title": "Título",
906
+ "code": "Código",
907
+ "slug": "Slug",
908
+ "description": "Descrição",
909
+ "published": "Publicado",
910
+ "save": "Salvar curso"
911
+ },
912
+ "detailSession": {
913
+ "subtitle": "{lessons} aulas · {minutes}min"
840
914
  }
841
915
  }
842
916
  },
@@ -2441,7 +2515,11 @@
2441
2515
  "helper": "Clique em uma aula para editar ou criar novas aulas.",
2442
2516
  "viewSingle": "Mês",
2443
2517
  "viewQuarter": "Trimestre",
2444
- "viewYear": "Ano"
2518
+ "viewYear": "Ano",
2519
+ "empty": {
2520
+ "title": "Nenhuma aula cadastrada",
2521
+ "description": "Cadastre a primeira aula para começar."
2522
+ }
2445
2523
  },
2446
2524
  "info": {
2447
2525
  "period": "Período",
@@ -1,6 +1,6 @@
1
1
  columns:
2
2
  - type: pk
3
- - name: code
3
+ - name: name
4
4
  type: varchar
5
5
  length: 32
6
6
  isNullable: true
@@ -15,6 +15,20 @@ columns:
15
15
  - name: applies_to_learning_path
16
16
  type: boolean
17
17
  default: false
18
+ - name: suggested_width
19
+ type: int
20
+ isNullable: true
21
+ - name: suggested_height
22
+ type: int
23
+ isNullable: true
24
+ - name: allowed_extensions
25
+ type: varchar
26
+ length: 100
27
+ isNullable: true
28
+ - name: aspect_ratio
29
+ type: varchar
30
+ length: 20
31
+ isNullable: true
18
32
  - name: status
19
33
  type: enum
20
34
  values: [active, inactive]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hed-hog/lms",
3
- "version": "0.0.306",
3
+ "version": "0.0.309",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "dependencies": {
@@ -10,14 +10,14 @@
10
10
  "@nestjs/jwt": "^11",
11
11
  "@nestjs/mapped-types": "*",
12
12
  "@hed-hog/api-prisma": "0.0.6",
13
- "@hed-hog/contact": "0.0.306",
13
+ "@hed-hog/api-types": "0.0.1",
14
+ "@hed-hog/contact": "0.0.309",
14
15
  "@hed-hog/api-pagination": "0.0.7",
15
- "@hed-hog/core": "0.0.306",
16
+ "@hed-hog/core": "0.0.309",
16
17
  "@hed-hog/api-locale": "0.0.14",
17
- "@hed-hog/api-types": "0.0.1",
18
- "@hed-hog/finance": "0.0.306",
19
- "@hed-hog/category": "0.0.306",
20
- "@hed-hog/api": "0.0.6"
18
+ "@hed-hog/api": "0.0.6",
19
+ "@hed-hog/finance": "0.0.309",
20
+ "@hed-hog/category": "0.0.309"
21
21
  },
22
22
  "exports": {
23
23
  ".": {