@hed-hog/lms 0.0.309 → 0.0.311

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.
@@ -944,7 +944,7 @@ export function ClassFormSheet({
944
944
  setCustomRecurrenceDialogOpen(false);
945
945
  }
946
946
 
947
- const handleProfessorCreated = async (instructor: {
947
+ const handleProfessorCreated = (instructor: {
948
948
  id: number;
949
949
  personId: number;
950
950
  name: string;
@@ -960,7 +960,6 @@ export function ClassFormSheet({
960
960
  shouldTouch: true,
961
961
  shouldValidate: true,
962
962
  });
963
- await refetchProfessorOptions();
964
963
  };
965
964
 
966
965
  async function onSubmitCourse(data: CourseSheetFormValues) {
@@ -658,7 +658,16 @@ async function applyBg(canvas: any, fabricMod: any, src: string) {
658
658
  });
659
659
  const scaleX = CANVAS_W / (img.width || 1);
660
660
  const scaleY = CANVAS_H / (img.height || 1);
661
- img.set({ scaleX, scaleY, selectable: false, evented: false });
661
+ img.set({
662
+ left: 0,
663
+ top: 0,
664
+ originX: 'left',
665
+ originY: 'top',
666
+ scaleX,
667
+ scaleY,
668
+ selectable: false,
669
+ evented: false,
670
+ });
662
671
  canvas.backgroundImage = img;
663
672
  canvas.requestRenderAll();
664
673
  } catch {
@@ -21,7 +21,7 @@ import {
21
21
  ZoomIn,
22
22
  ZoomOut,
23
23
  } from 'lucide-react';
24
- import { useCallback, useRef } from 'react';
24
+ import { useCallback, useEffect, useRef } from 'react';
25
25
  import { toast } from 'sonner';
26
26
  import { getCanvasAPI } from '../../_lib/editor/canvasInstance';
27
27
  import {
@@ -63,6 +63,49 @@ export default function Topbar({ templateContext }: TopbarProps) {
63
63
 
64
64
  const importRef = useRef<HTMLInputElement>(null);
65
65
 
66
+ const isFirstRender = useRef(true);
67
+ const autoSaveTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
68
+ const templateContextRef = useRef(templateContext);
69
+ templateContextRef.current = templateContext;
70
+ const requestRef = useRef(request);
71
+ requestRef.current = request;
72
+
73
+ useEffect(() => {
74
+ if (isFirstRender.current) {
75
+ isFirstRender.current = false;
76
+ return;
77
+ }
78
+
79
+ localStorage.setItem(LS_KEY, JSON.stringify(templateState));
80
+
81
+ const ctx = templateContextRef.current;
82
+ if (!ctx) return;
83
+
84
+ if (autoSaveTimerRef.current) clearTimeout(autoSaveTimerRef.current);
85
+
86
+ autoSaveTimerRef.current = setTimeout(() => {
87
+ requestRef
88
+ .current({
89
+ url: `/lms/certificates/templates/${ctx.id}`,
90
+ method: 'PATCH',
91
+ data: {
92
+ name: templateState.name,
93
+ slug: ctx.slug,
94
+ status: ctx.status,
95
+ templateContent: JSON.stringify(templateState),
96
+ },
97
+ })
98
+ .catch(() => {
99
+ toast.error('Falha ao salvar automaticamente');
100
+ });
101
+ }, 1500);
102
+
103
+ return () => {
104
+ if (autoSaveTimerRef.current) clearTimeout(autoSaveTimerRef.current);
105
+ };
106
+ // eslint-disable-next-line react-hooks/exhaustive-deps
107
+ }, [templateState]);
108
+
66
109
  const handleNew = useCallback(() => {
67
110
  resetTemplate();
68
111
  getCanvasAPI()?.loadTemplate(useTemplateStore.getState().template);
@@ -72,7 +115,6 @@ export default function Topbar({ templateContext }: TopbarProps) {
72
115
  const handleSave = useCallback(() => {
73
116
  const run = async () => {
74
117
  if (!templateContext) {
75
- localStorage.setItem(LS_KEY, JSON.stringify(templateState));
76
118
  toast.success('Template salvo no localStorage');
77
119
  return;
78
120
  }
@@ -88,7 +130,6 @@ export default function Topbar({ templateContext }: TopbarProps) {
88
130
  },
89
131
  });
90
132
 
91
- localStorage.setItem(LS_KEY, JSON.stringify(templateState));
92
133
  toast.success('Template salvo');
93
134
  };
94
135
 
@@ -99,6 +99,7 @@ type CreateCertificateTemplatePayload = {
99
99
  type UpdateCertificateTemplatePayload = {
100
100
  name: string;
101
101
  description?: string;
102
+ status: TemplateStatus;
102
103
  };
103
104
 
104
105
  const PAGE_SIZES = [6, 12, 24];
@@ -114,6 +115,7 @@ const updateTemplateSchema = (t: (key: string) => string) =>
114
115
  z.object({
115
116
  name: z.string().trim().min(1, t('editSheet.validation.required')),
116
117
  description: z.string().trim().optional(),
118
+ status: z.enum(['draft', 'active', 'inactive']),
117
119
  });
118
120
 
119
121
  type CreateTemplateFormValues = z.infer<
@@ -173,6 +175,7 @@ export default function ModelsPage() {
173
175
  defaultValues: {
174
176
  name: '',
175
177
  description: '',
178
+ status: 'draft',
176
179
  },
177
180
  });
178
181
 
@@ -304,6 +307,7 @@ export default function ModelsPage() {
304
307
  editForm.reset({
305
308
  name: '',
306
309
  description: '',
310
+ status: 'draft',
307
311
  });
308
312
  setEditingTemplateId(null);
309
313
  }
@@ -325,6 +329,7 @@ export default function ModelsPage() {
325
329
  editForm.reset({
326
330
  name: template.name,
327
331
  description: template.description ?? '',
332
+ status: template.status,
328
333
  });
329
334
  setIsEditSheetOpen(true);
330
335
  }
@@ -410,6 +415,7 @@ export default function ModelsPage() {
410
415
  const payload: UpdateCertificateTemplatePayload = {
411
416
  name,
412
417
  description: values.description?.trim() || undefined,
418
+ status: values.status,
413
419
  };
414
420
 
415
421
  try {
@@ -825,6 +831,32 @@ export default function ModelsPage() {
825
831
  />
826
832
  </div>
827
833
 
834
+ <div className="space-y-2">
835
+ <p className="text-sm font-medium">
836
+ {t('editSheet.fields.status')}
837
+ </p>
838
+ <Controller
839
+ name="status"
840
+ control={editForm.control}
841
+ render={({ field }) => (
842
+ <Select value={field.value} onValueChange={field.onChange}>
843
+ <SelectTrigger className="w-full">
844
+ <SelectValue placeholder={t('editSheet.fields.status')} />
845
+ </SelectTrigger>
846
+ <SelectContent>
847
+ <SelectItem value="draft">{t('status.draft')}</SelectItem>
848
+ <SelectItem value="active">
849
+ {t('status.active')}
850
+ </SelectItem>
851
+ <SelectItem value="inactive">
852
+ {t('status.inactive')}
853
+ </SelectItem>
854
+ </SelectContent>
855
+ </Select>
856
+ )}
857
+ />
858
+ </div>
859
+
828
860
  {editForm.formState.errors.name?.message ? (
829
861
  <p className="text-sm text-destructive">
830
862
  {editForm.formState.errors.name.message}
@@ -475,17 +475,14 @@ export function EditorLesson({ lessonId }: EditorLessonProps) {
475
475
  try {
476
476
  const results = await Promise.allSettled(
477
477
  files.map((f) =>
478
- uploadFile(request, f).then(
479
- (res) =>
480
- ({
481
- id: String(res.id),
482
- name: f.name,
483
- size: formatFileSize(f.size),
484
- type: f.type || f.name.split('.').pop() || 'file',
485
- public: false,
486
- url: undefined,
487
- }) satisfies Resource
488
- )
478
+ uploadFile(request, f).then<Resource>((res) => ({
479
+ id: String(res.id),
480
+ name: f.name,
481
+ size: formatFileSize(f.size),
482
+ type: f.type || f.name.split('.').pop() || 'file',
483
+ public: false,
484
+ url: undefined,
485
+ }))
489
486
  )
490
487
  );
491
488
  const succeeded = results
@@ -25,6 +25,7 @@ import type {
25
25
  export const MOCK_COURSE: Course = {
26
26
  id: 'course-1',
27
27
  code: 'REACT-ADV',
28
+ name: 'React Avancado',
28
29
  title: 'React Avancado',
29
30
  description:
30
31
  'Domine os conceitos avancados do React: hooks, patterns, performance e gerenciamento de estado moderno.',
@@ -258,6 +258,7 @@ export const useStructureStore = create<StructureState>((set, get) => ({
258
258
  courseId: '',
259
259
  course: {
260
260
  id: '',
261
+ code: '',
261
262
  name: '',
262
263
  title: 'Carregando...',
263
264
  description: '',
@@ -19,6 +19,7 @@ export type Visibility = 'publico' | 'privado' | 'restrito';
19
19
 
20
20
  export interface Course {
21
21
  id: string;
22
+ code: string;
22
23
  name: string;
23
24
  title: string;
24
25
  description: string;
@@ -91,6 +91,7 @@ function normalizeResource(raw: ApiLessonResource): Resource {
91
91
  export function normalizeCourse(raw: ApiCourse): Course {
92
92
  return {
93
93
  id: raw.id,
94
+ code: raw.slug || raw.id,
94
95
  name: raw.name ?? raw.slug,
95
96
  title: raw.titulo,
96
97
  description: raw.descricao,
@@ -2171,6 +2171,7 @@
2171
2171
  "cards": {
2172
2172
  "status": "Status",
2173
2173
  "updatedAt": "Updated",
2174
+ "noDescription": "No description",
2174
2175
  "actions": {
2175
2176
  "menuLabel": "More actions",
2176
2177
  "editTemplate": "Edit template",
@@ -2205,7 +2206,8 @@
2205
2206
  "description": "Update the template basic information.",
2206
2207
  "fields": {
2207
2208
  "name": "Name",
2208
- "description": "Description"
2209
+ "description": "Description",
2210
+ "status": "Status"
2209
2211
  },
2210
2212
  "placeholders": {
2211
2213
  "name": "e.g. Corporate Certificate",
@@ -2178,6 +2178,7 @@
2178
2178
  "cards": {
2179
2179
  "status": "Status",
2180
2180
  "updatedAt": "Atualizado",
2181
+ "noDescription": "Sem descrição",
2181
2182
  "actions": {
2182
2183
  "menuLabel": "Mais ações",
2183
2184
  "editTemplate": "Editar template",
@@ -2212,7 +2213,8 @@
2212
2213
  "description": "Atualize os dados básicos do template.",
2213
2214
  "fields": {
2214
2215
  "name": "Nome",
2215
- "description": "Descrição"
2216
+ "description": "Descrição",
2217
+ "status": "Status"
2216
2218
  },
2217
2219
  "placeholders": {
2218
2220
  "name": "Ex.: Certificado Corporativo",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hed-hog/lms",
3
- "version": "0.0.309",
3
+ "version": "0.0.311",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "dependencies": {
@@ -9,15 +9,15 @@
9
9
  "@nestjs/core": "^11",
10
10
  "@nestjs/jwt": "^11",
11
11
  "@nestjs/mapped-types": "*",
12
- "@hed-hog/api-prisma": "0.0.6",
13
- "@hed-hog/api-types": "0.0.1",
14
- "@hed-hog/contact": "0.0.309",
12
+ "@hed-hog/contact": "0.0.311",
15
13
  "@hed-hog/api-pagination": "0.0.7",
16
- "@hed-hog/core": "0.0.309",
17
- "@hed-hog/api-locale": "0.0.14",
14
+ "@hed-hog/api-types": "0.0.1",
18
15
  "@hed-hog/api": "0.0.6",
19
- "@hed-hog/finance": "0.0.309",
20
- "@hed-hog/category": "0.0.309"
16
+ "@hed-hog/api-locale": "0.0.14",
17
+ "@hed-hog/finance": "0.0.311",
18
+ "@hed-hog/category": "0.0.311",
19
+ "@hed-hog/core": "0.0.311",
20
+ "@hed-hog/api-prisma": "0.0.6"
21
21
  },
22
22
  "exports": {
23
23
  ".": {