@hed-hog/lms 0.0.306 → 0.0.310

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 (120) 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/certificates/models/CanvasStage.tsx.ejs +10 -1
  47. package/hedhog/frontend/app/certificates/models/TopBar.tsx.ejs +44 -3
  48. package/hedhog/frontend/app/certificates/models/page.tsx.ejs +32 -0
  49. package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +437 -77
  50. package/hedhog/frontend/app/classes/page.tsx.ejs +311 -289
  51. package/hedhog/frontend/app/courses/[id]/_components/CourseCertificateCard.tsx.ejs +10 -7
  52. package/hedhog/frontend/app/courses/[id]/_components/CourseClassificationCard.tsx.ejs +23 -32
  53. package/hedhog/frontend/app/courses/[id]/_components/CourseContentCard.tsx.ejs +3 -9
  54. package/hedhog/frontend/app/courses/[id]/_components/CourseDangerZoneCard.tsx.ejs +26 -16
  55. package/hedhog/frontend/app/courses/[id]/_components/CourseFlagsCard.tsx.ejs +19 -5
  56. package/hedhog/frontend/app/courses/[id]/_components/CourseMainInfoCard.tsx.ejs +10 -14
  57. package/hedhog/frontend/app/courses/[id]/_components/CourseMediaCard.tsx.ejs +131 -107
  58. package/hedhog/frontend/app/courses/[id]/_components/CourseRelationsCard.tsx.ejs +10 -7
  59. package/hedhog/frontend/app/courses/[id]/_components/CourseSectionCard.tsx.ejs +38 -19
  60. package/hedhog/frontend/app/courses/[id]/_components/CourseSummaryCard.tsx.ejs +1 -1
  61. package/hedhog/frontend/app/courses/[id]/_components/course-edit-types.ts.ejs +1 -1
  62. package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +336 -1057
  63. package/hedhog/frontend/app/courses/[id]/structure/_components/confirm-dialog.tsx.ejs +45 -0
  64. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-dnd.tsx.ejs +362 -0
  65. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-panel.tsx.ejs +111 -0
  66. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-skeleton.tsx.ejs +64 -0
  67. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree.tsx.ejs +134 -0
  68. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-course.tsx.ejs +113 -0
  69. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson.tsx.ejs +314 -0
  70. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-panel.tsx.ejs +62 -0
  71. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-session.tsx.ejs +174 -0
  72. package/hedhog/frontend/app/courses/[id]/structure/_components/drag-handle.tsx.ejs +58 -0
  73. package/hedhog/frontend/app/courses/[id]/structure/_components/drag-overlay.tsx.ejs +52 -0
  74. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-bulk.tsx.ejs +276 -0
  75. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +1216 -0
  76. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +1827 -0
  77. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-session.tsx.ejs +443 -0
  78. package/hedhog/frontend/app/courses/[id]/structure/_components/highlighted-text.tsx.ejs +41 -0
  79. package/hedhog/frontend/app/courses/[id]/structure/_components/mock-data.ts.ejs +184 -0
  80. package/hedhog/frontend/app/courses/[id]/structure/_components/multi-select-bar.tsx.ejs +264 -0
  81. package/hedhog/frontend/app/courses/[id]/structure/_components/search-filter.tsx.ejs +96 -0
  82. package/hedhog/frontend/app/courses/[id]/structure/_components/session-picker-dialog.tsx.ejs +74 -0
  83. package/hedhog/frontend/app/courses/[id]/structure/_components/shortcuts-help.tsx.ejs +136 -0
  84. package/hedhog/frontend/app/courses/[id]/structure/_components/sortable-tree-row.tsx.ejs +80 -0
  85. package/hedhog/frontend/app/courses/[id]/structure/_components/store.ts.ejs +948 -0
  86. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-context-menu.tsx.ejs +525 -0
  87. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-display-settings-popover.tsx.ejs +150 -0
  88. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-helpers.ts.ejs +182 -0
  89. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-course.tsx.ejs +52 -0
  90. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-lesson.tsx.ejs +271 -0
  91. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-session.tsx.ejs +167 -0
  92. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row.tsx.ejs +108 -0
  93. package/hedhog/frontend/app/courses/[id]/structure/_components/types.ts.ejs +122 -0
  94. package/hedhog/frontend/app/courses/[id]/structure/_components/use-course-structure-shortcuts.ts.ejs +318 -0
  95. package/hedhog/frontend/app/courses/[id]/structure/_components/use-tree-display-settings.ts.ejs +97 -0
  96. package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs +347 -0
  97. package/hedhog/frontend/app/courses/[id]/structure/_data/course-structure-contract.ts.ejs +195 -0
  98. package/hedhog/frontend/app/courses/[id]/structure/_data/services/course-structure.service.ts.ejs +420 -0
  99. package/hedhog/frontend/app/courses/[id]/structure/_data/types/api-course.types.ts.ejs +254 -0
  100. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +987 -0
  101. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-query.ts.ejs +86 -0
  102. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure.ts.ejs +160 -0
  103. package/hedhog/frontend/app/courses/[id]/structure/page.tsx.ejs +10 -3212
  104. package/hedhog/frontend/app/courses/page.tsx.ejs +45 -26
  105. package/hedhog/frontend/app/{training → paths}/page.tsx.ejs +29 -7
  106. package/hedhog/frontend/messages/en.json +91 -11
  107. package/hedhog/frontend/messages/pt.json +91 -11
  108. package/hedhog/table/course.yaml +1 -1
  109. package/hedhog/table/image_type.yaml +14 -0
  110. package/package.json +7 -7
  111. package/src/course/course-structure.controller.ts +63 -0
  112. package/src/course/course-structure.service.ts +390 -3
  113. package/src/course/course.service.ts +59 -27
  114. package/src/course/dto/create-course-structure-lesson.dto.ts +3 -2
  115. package/src/course/dto/create-course.dto.ts +4 -1
  116. package/src/course/dto/move-lesson.dto.ts +17 -0
  117. package/src/course/dto/paste-lessons.dto.ts +9 -0
  118. package/src/course/dto/reorder-lessons.dto.ts +10 -0
  119. package/src/course/dto/reorder-sessions.dto.ts +10 -0
  120. package/src/training/training.controller.ts +1 -1
@@ -6,7 +6,7 @@ import { UpdateCourseDto } from './dto/update-course.dto';
6
6
  type CourseImageTypeSlug = 'course-logo' | 'course-banner';
7
7
 
8
8
  type CourseExtraFields = {
9
- code?: string | null;
9
+ name?: string | null;
10
10
  is_featured?: boolean | null;
11
11
  has_certificate?: boolean | null;
12
12
  is_listed?: boolean | null;
@@ -14,7 +14,7 @@ type CourseExtraFields = {
14
14
  };
15
15
 
16
16
  type PersistCourseExtrasInput = {
17
- code?: string;
17
+ name?: string;
18
18
  isFeatured?: boolean;
19
19
  hasCertificate?: boolean;
20
20
  isListed?: boolean;
@@ -209,7 +209,13 @@ export class CourseService {
209
209
  },
210
210
  include: {
211
211
  image_type: {
212
- select: { slug: true },
212
+ select: {
213
+ slug: true,
214
+ suggested_width: true,
215
+ suggested_height: true,
216
+ allowed_extensions: true,
217
+ aspect_ratio: true,
218
+ },
213
219
  },
214
220
  file: {
215
221
  select: { id: true, filename: true },
@@ -309,7 +315,6 @@ export class CourseService {
309
315
  async create(dto: CreateCourseDto) {
310
316
  const { categorySlugs, logoFileId, bannerFileId, instructorIds, ...data } = dto;
311
317
  const normalizedSlug = data.slug.trim();
312
- const resolvedCode = this.normalizeCourseCode(data.code ?? normalizedSlug);
313
318
  const resolvedTitle = this.resolveCourseTitle(data.title, normalizedSlug);
314
319
 
315
320
  const categories = categorySlugs?.length
@@ -332,6 +337,7 @@ export class CourseService {
332
337
 
333
338
  const c = await this.prisma.course.create({
334
339
  data: {
340
+ name: data.name,
335
341
  slug: normalizedSlug,
336
342
  title: resolvedTitle,
337
343
  ...(data.description !== undefined && {
@@ -427,7 +433,6 @@ export class CourseService {
427
433
 
428
434
  await this.persistCourseExtras(c.id, {
429
435
  ...data,
430
- ...(resolvedCode && { code: resolvedCode }),
431
436
  offeringType: this.normalizeOfferingType(data.offeringType) ?? 'on_demand',
432
437
  });
433
438
 
@@ -490,6 +495,7 @@ export class CourseService {
490
495
  const c = await this.prisma.course.update({
491
496
  where: { id },
492
497
  data: {
498
+ ...(data.name !== undefined && { name: data.name }),
493
499
  ...(resolvedSlug !== undefined && { slug: resolvedSlug }),
494
500
  ...(resolvedTitle !== undefined && { title: resolvedTitle }),
495
501
  ...(data.description !== undefined && { description: data.description }),
@@ -621,7 +627,7 @@ export class CourseService {
621
627
  },
622
628
  extras?: CourseExtraFields,
623
629
  ) {
624
- const rawCode = extras?.code ?? c.code ?? c.slug;
630
+ const resolvedName = extras?.name ?? c.name ?? null;
625
631
  const resolvedTitle = this.normalizeOptionalText(c.title) ?? c.slug;
626
632
  const courseImages = Array.isArray(c.course_image) ? c.course_image : [];
627
633
  const classCount = c._count?.course_class_group ?? 0;
@@ -638,9 +644,7 @@ export class CourseService {
638
644
 
639
645
  return {
640
646
  id: c.id,
641
- code:
642
- rawCode?.toUpperCase().replace(/[^A-Z0-9-]+/g, '-').slice(0, 32) ??
643
- '',
647
+ name: resolvedName ?? '',
644
648
  slug: c.slug,
645
649
  title: resolvedTitle,
646
650
  description: c.description ?? '',
@@ -657,8 +661,24 @@ export class CourseService {
657
661
  secondaryContrastColor: c.secondary_contrast_color ?? null,
658
662
  logoFileId: logoImage?.file?.id ?? logoImage?.file_id ?? null,
659
663
  logoFilename: logoImage?.file?.filename ?? null,
664
+ logoImageType: logoImage?.image_type
665
+ ? {
666
+ suggestedWidth: logoImage.image_type.suggested_width ?? null,
667
+ suggestedHeight: logoImage.image_type.suggested_height ?? null,
668
+ allowedExtensions: logoImage.image_type.allowed_extensions ?? null,
669
+ aspectRatio: logoImage.image_type.aspect_ratio ?? null,
670
+ }
671
+ : null,
660
672
  bannerFileId: bannerImage?.file?.id ?? bannerImage?.file_id ?? null,
661
673
  bannerFilename: bannerImage?.file?.filename ?? null,
674
+ bannerImageType: bannerImage?.image_type
675
+ ? {
676
+ suggestedWidth: bannerImage.image_type.suggested_width ?? null,
677
+ suggestedHeight: bannerImage.image_type.suggested_height ?? null,
678
+ allowedExtensions: bannerImage.image_type.allowed_extensions ?? null,
679
+ aspectRatio: bannerImage.image_type.aspect_ratio ?? null,
680
+ }
681
+ : null,
662
682
  categories: (c.course_category ?? []).map(
663
683
  (cc: any) => cc.category?.slug ?? '',
664
684
  ),
@@ -721,13 +741,13 @@ export class CourseService {
721
741
  const idsCsv = normalizedIds.join(',');
722
742
  const rows = (await this.prisma.$queryRawUnsafe(
723
743
  `
724
- SELECT id, code, is_featured, has_certificate, is_listed, offering_type
744
+ SELECT id, name, is_featured, has_certificate, is_listed, offering_type
725
745
  FROM course
726
746
  WHERE id IN (${idsCsv})
727
747
  `,
728
748
  )) as Array<{
729
749
  id: number;
730
- code: string | null;
750
+ name: string | null;
731
751
  is_featured: boolean | null;
732
752
  has_certificate: boolean | null;
733
753
  is_listed: boolean | null;
@@ -736,7 +756,7 @@ export class CourseService {
736
756
 
737
757
  for (const row of rows) {
738
758
  extrasById.set(row.id, {
739
- code: row.code,
759
+ name: row.name,
740
760
  is_featured: row.is_featured,
741
761
  has_certificate: row.has_certificate,
742
762
  is_listed: row.is_listed,
@@ -757,9 +777,9 @@ export class CourseService {
757
777
  const sets: string[] = [];
758
778
  const params: Array<string | boolean | number> = [];
759
779
 
760
- if (data.code !== undefined) {
761
- params.push(data.code);
762
- sets.push(`code = $${params.length}`);
780
+ if (data.name !== undefined) {
781
+ params.push(data.name);
782
+ sets.push(`name = $${params.length}`);
763
783
  }
764
784
 
765
785
  if (data.isFeatured !== undefined) {
@@ -802,18 +822,6 @@ export class CourseService {
802
822
  }
803
823
  }
804
824
 
805
- private normalizeCourseCode(value?: string | null) {
806
- const normalized = String(value ?? '')
807
- .trim()
808
- .toUpperCase()
809
- .replace(/[^A-Z0-9-]+/g, '-')
810
- .replace(/-+/g, '-')
811
- .replace(/^-+|-+$/g, '')
812
- .slice(0, 32);
813
-
814
- return normalized || undefined;
815
- }
816
-
817
825
  private async syncCourseImages(
818
826
  courseId: number,
819
827
  data: {
@@ -908,11 +916,35 @@ export class CourseService {
908
916
  return existing;
909
917
  }
910
918
 
919
+ const specsBySlug: Record<
920
+ CourseImageTypeSlug,
921
+ {
922
+ suggested_width: number;
923
+ suggested_height: number;
924
+ allowed_extensions: string;
925
+ aspect_ratio: string;
926
+ }
927
+ > = {
928
+ 'course-logo': {
929
+ suggested_width: 500,
930
+ suggested_height: 500,
931
+ allowed_extensions: 'png,webp',
932
+ aspect_ratio: '1:1',
933
+ },
934
+ 'course-banner': {
935
+ suggested_width: 1920,
936
+ suggested_height: 540,
937
+ allowed_extensions: 'jpg,png,webp',
938
+ aspect_ratio: '16:9',
939
+ },
940
+ };
941
+
911
942
  return this.prisma.image_type.create({
912
943
  data: {
913
944
  name: imageTypeSlug === 'course-logo' ? 'Course Logo' : 'Course Banner',
914
945
  slug: imageTypeSlug,
915
946
  applies_to_course: true,
947
+ ...specsBySlug[imageTypeSlug],
916
948
  },
917
949
  select: { id: true },
918
950
  });
@@ -56,8 +56,9 @@ export class CreateCourseStructureLessonDto {
56
56
  tipo: 'video' | 'questao' | 'post' | 'exercicio';
57
57
 
58
58
  @IsInt()
59
- @Min(1)
60
- duracao: number;
59
+ @Min(0)
60
+ @IsOptional()
61
+ duracao: number = 0;
61
62
 
62
63
  @IsEnum(['youtube', 'vimeo', 'bunny', 'custom'])
63
64
  @IsOptional()
@@ -14,11 +14,14 @@ export class CreateCourseDto {
14
14
  @IsString()
15
15
  @MaxLength(32)
16
16
  @IsOptional()
17
- code?: string;
17
+ name?: string;
18
18
 
19
19
  @IsString()
20
20
  @IsNotEmpty()
21
21
  @MaxLength(255)
22
+ @Matches(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, {
23
+ message: 'slug deve conter apenas letras minúsculas, números e hífens, sem espaços',
24
+ })
22
25
  slug: string;
23
26
 
24
27
  @IsString()
@@ -0,0 +1,17 @@
1
+ import { IsInt, IsOptional, IsPositive, Min } from 'class-validator';
2
+
3
+ export class MoveLessonDto {
4
+ /** ID of the destination session (course_module). */
5
+ @IsInt()
6
+ @IsPositive()
7
+ toSessionId: number;
8
+
9
+ /**
10
+ * Zero-based index within the destination session.
11
+ * If omitted, the lesson is appended at the end.
12
+ */
13
+ @IsOptional()
14
+ @IsInt()
15
+ @Min(0)
16
+ toIndex?: number;
17
+ }
@@ -0,0 +1,9 @@
1
+ import { ArrayMinSize, IsArray, IsInt, IsPositive } from 'class-validator';
2
+
3
+ export class PasteLessonsDto {
4
+ @IsArray()
5
+ @ArrayMinSize(1)
6
+ @IsInt({ each: true })
7
+ @IsPositive({ each: true })
8
+ lessonIds: number[];
9
+ }
@@ -0,0 +1,10 @@
1
+ import { ArrayMinSize, IsArray, IsInt, IsPositive } from 'class-validator';
2
+
3
+ export class ReorderLessonsDto {
4
+ /** Ordered array of lesson IDs in their new positions within the session. */
5
+ @IsArray()
6
+ @ArrayMinSize(1)
7
+ @IsInt({ each: true })
8
+ @IsPositive({ each: true })
9
+ lessonIds: number[];
10
+ }
@@ -0,0 +1,10 @@
1
+ import { ArrayMinSize, IsArray, IsInt, IsPositive } from 'class-validator';
2
+
3
+ export class ReorderSessionsDto {
4
+ /** Ordered array of session IDs reflecting the new visual order. */
5
+ @IsArray()
6
+ @ArrayMinSize(1)
7
+ @IsInt({ each: true })
8
+ @IsPositive({ each: true })
9
+ sessionIds: number[];
10
+ }
@@ -15,7 +15,7 @@ import { UpdateTrainingDto } from './dto/update-training.dto';
15
15
  import { TrainingService } from './training.service';
16
16
 
17
17
  @Role()
18
- @Controller('lms/training')
18
+ @Controller('lms/paths')
19
19
  export class TrainingController {
20
20
  constructor(private readonly trainingService: TrainingService) {}
21
21