@hed-hog/lms 0.0.354 → 0.0.357
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/course/course-audio-transcription.service.d.ts.map +1 -1
- package/dist/course/course-audio-transcription.service.js +15 -7
- package/dist/course/course-audio-transcription.service.js.map +1 -1
- package/dist/course/course-structure.controller.d.ts +12 -0
- package/dist/course/course-structure.controller.d.ts.map +1 -1
- package/dist/course/course-structure.service.d.ts +22 -0
- package/dist/course/course-structure.service.d.ts.map +1 -1
- package/dist/course/course-structure.service.js +147 -18
- package/dist/course/course-structure.service.js.map +1 -1
- package/dist/course/course.service.d.ts +4 -2
- package/dist/course/course.service.d.ts.map +1 -1
- package/dist/course/course.service.js +61 -2
- package/dist/course/course.service.js.map +1 -1
- package/dist/course/dto/create-course-structure-lesson.dto.d.ts +3 -0
- package/dist/course/dto/create-course-structure-lesson.dto.d.ts.map +1 -1
- package/dist/course/dto/create-course-structure-lesson.dto.js +15 -0
- package/dist/course/dto/create-course-structure-lesson.dto.js.map +1 -1
- package/dist/course/dto/create-course-structure-session.dto.d.ts +1 -0
- package/dist/course/dto/create-course-structure-session.dto.d.ts.map +1 -1
- package/dist/course/dto/create-course-structure-session.dto.js +5 -0
- package/dist/course/dto/create-course-structure-session.dto.js.map +1 -1
- package/dist/enterprise/training/training-student.controller.d.ts +0 -95
- package/dist/enterprise/training/training-student.controller.d.ts.map +1 -1
- package/dist/enterprise/training/training-student.controller.js +1 -34
- package/dist/enterprise/training/training-student.controller.js.map +1 -1
- package/dist/enterprise/training/training-student.service.d.ts +63 -0
- package/dist/enterprise/training/training-student.service.d.ts.map +1 -1
- package/dist/enterprise/training/training-student.service.js +320 -4
- package/dist/enterprise/training/training-student.service.js.map +1 -1
- package/dist/lms.module.d.ts.map +1 -1
- package/dist/lms.module.js +2 -0
- package/dist/lms.module.js.map +1 -1
- package/dist/platforma/platforma.controller.d.ts +287 -0
- package/dist/platforma/platforma.controller.d.ts.map +1 -0
- package/dist/platforma/platforma.controller.js +147 -0
- package/dist/platforma/platforma.controller.js.map +1 -0
- package/hedhog/data/route.yaml +75 -9
- package/hedhog/frontend/app/_components/class-form-sheet.tsx.ejs +2 -2
- package/hedhog/frontend/app/_components/course-form-sheet.tsx.ejs +10 -13
- package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +2 -2
- package/hedhog/frontend/app/courses/[id]/_components/course-edit-types.ts.ejs +1 -1
- package/hedhog/frontend/app/courses/[id]/structure/_components/confirm-dialog.tsx.ejs +17 -6
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +14 -7
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +92 -53
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-session.tsx.ejs +25 -60
- package/hedhog/frontend/app/courses/[id]/structure/_components/mock-data.ts.ejs +1 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/store.ts.ejs +11 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-context-menu.tsx.ejs +22 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-lesson.tsx.ejs +79 -64
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-session.tsx.ejs +31 -14
- package/hedhog/frontend/app/courses/[id]/structure/_components/types.ts.ejs +3 -4
- package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs +28 -24
- package/hedhog/frontend/app/courses/[id]/structure/_data/types/api-course.types.ts.ejs +20 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +41 -6
- package/hedhog/frontend/app/courses/page.tsx.ejs +59 -15
- package/hedhog/frontend/app/enterprise/_components/enterprise-course-create-sheet.tsx.ejs +1 -1
- package/hedhog/frontend/app/enterprise/_components/enterprise-course-edit-sheet.tsx.ejs +2 -2
- package/hedhog/frontend/app/paths/page.tsx.ejs +1 -1
- package/hedhog/frontend/app/training/page.tsx.ejs +1 -1
- package/hedhog/frontend/messages/en.json +7 -2
- package/hedhog/frontend/messages/pt.json +7 -2
- package/hedhog/table/course_lesson.yaml +2 -2
- package/hedhog/table/course_module.yaml +3 -0
- package/package.json +9 -9
- package/src/course/course-audio-transcription.service.ts +21 -8
- package/src/course/course-structure.service.ts +204 -3
- package/src/course/course.service.ts +67 -1
- package/src/course/dto/create-course-structure-lesson.dto.ts +17 -0
- package/src/course/dto/create-course-structure-session.dto.ts +13 -1
- package/src/enterprise/training/training-student.controller.ts +3 -27
- package/src/enterprise/training/training-student.service.ts +350 -2
- package/src/lms.module.ts +2 -0
- package/src/platforma/platforma.controller.ts +92 -0
package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs
CHANGED
|
@@ -16,9 +16,7 @@
|
|
|
16
16
|
* 1. exameVinculado (number) ↔ linkedExam (string) — converted with String()/Number()
|
|
17
17
|
* 2. 'exercicio' tipo — exists in backend and is now part of frontend LessonType.
|
|
18
18
|
* 3. Session.order — not returned by API; inferred from sorted array position.
|
|
19
|
-
* 4.
|
|
20
|
-
* 5. Lesson.status / visibility — not in DB schema; left undefined.
|
|
21
|
-
* 6. Resource.size / url — not returned by API; left as empty string / undefined.
|
|
19
|
+
* 4. Resource.size / url — not returned by API; left as empty string / undefined.
|
|
22
20
|
*/
|
|
23
21
|
|
|
24
22
|
import type {
|
|
@@ -125,7 +123,7 @@ function normalizeFrame(raw: ApiLessonFrame): VideoFrame {
|
|
|
125
123
|
export function normalizeCourse(raw: ApiCourse): Course {
|
|
126
124
|
return {
|
|
127
125
|
id: raw.id,
|
|
128
|
-
code: raw.slug
|
|
126
|
+
code: raw.codigo ?? raw.code ?? raw.slug ?? raw.id,
|
|
129
127
|
name: raw.name ?? raw.slug,
|
|
130
128
|
title: raw.titulo,
|
|
131
129
|
description: raw.descricao,
|
|
@@ -154,12 +152,7 @@ export function normalizeSession(raw: ApiSession, index: number): Session {
|
|
|
154
152
|
* TODO[BACKEND]: Include `order` in the ApiSession response.
|
|
155
153
|
*/
|
|
156
154
|
order: index + 1,
|
|
157
|
-
|
|
158
|
-
* ⚠️ BACKEND NOTE: visibility and published are not in the backend schema.
|
|
159
|
-
* Defaulted here. If these fields are added to the DB, read them from `raw`.
|
|
160
|
-
*/
|
|
161
|
-
visibility: 'publico',
|
|
162
|
-
published: false,
|
|
155
|
+
published: raw.published ?? false,
|
|
163
156
|
};
|
|
164
157
|
}
|
|
165
158
|
|
|
@@ -200,12 +193,9 @@ export function normalizeLesson(raw: ApiLesson, order = 0): Lesson {
|
|
|
200
193
|
resources: (raw.recursos ?? []).map(normalizeResource),
|
|
201
194
|
frames: (raw.frames ?? []).map(normalizeFrame),
|
|
202
195
|
instructors,
|
|
203
|
-
|
|
204
|
-
* ⚠️ BACKEND NOTE: status and visibility are not in the DB schema.
|
|
205
|
-
* Left undefined. If added to the schema, read them from `raw`.
|
|
206
|
-
*/
|
|
207
|
-
status: undefined,
|
|
196
|
+
status: raw.statusProducao,
|
|
208
197
|
visibility: undefined,
|
|
198
|
+
published: raw.published ?? false,
|
|
209
199
|
};
|
|
210
200
|
}
|
|
211
201
|
|
|
@@ -249,17 +239,17 @@ export function normalizeStructureResponse(raw: ApiGetStructureResponse): {
|
|
|
249
239
|
* Convert SessionFormValues → ApiCreateSessionPayload.
|
|
250
240
|
* Used for POST /lms/courses/:id/structure/sessions.
|
|
251
241
|
*
|
|
252
|
-
* ⚠️ ADAPTER POINT:
|
|
253
|
-
* `visibility` and `published` from SessionFormValues are intentionally
|
|
254
|
-
* omitted — the backend schema does not have these fields yet.
|
|
255
|
-
* TODO[BACKEND]: Add visibility/published to course_module if required.
|
|
256
242
|
*/
|
|
257
243
|
export function toCreateSessionPayload(
|
|
258
|
-
data: Pick<
|
|
244
|
+
data: Pick<
|
|
245
|
+
SessionFormValues,
|
|
246
|
+
'title' | 'duration' | 'description' | 'code' | 'published'
|
|
247
|
+
>
|
|
259
248
|
): ApiCreateSessionPayload {
|
|
260
249
|
return {
|
|
261
250
|
titulo: data.title,
|
|
262
251
|
duracao: data.duration,
|
|
252
|
+
published: data.published,
|
|
263
253
|
...(data.description?.trim() && { descricao: data.description }),
|
|
264
254
|
...(data.code?.trim() && { codigo: data.code }),
|
|
265
255
|
};
|
|
@@ -271,7 +261,10 @@ export function toCreateSessionPayload(
|
|
|
271
261
|
*/
|
|
272
262
|
export function toUpdateSessionPayload(
|
|
273
263
|
data: Partial<
|
|
274
|
-
Pick<
|
|
264
|
+
Pick<
|
|
265
|
+
SessionFormValues,
|
|
266
|
+
'title' | 'duration' | 'description' | 'code' | 'published'
|
|
267
|
+
>
|
|
275
268
|
>
|
|
276
269
|
): ApiUpdateSessionPayload {
|
|
277
270
|
const payload: ApiUpdateSessionPayload = {};
|
|
@@ -279,6 +272,7 @@ export function toUpdateSessionPayload(
|
|
|
279
272
|
if (data.duration !== undefined) payload.duracao = data.duration;
|
|
280
273
|
if (data.description !== undefined) payload.descricao = data.description;
|
|
281
274
|
if (data.code !== undefined) payload.codigo = data.code;
|
|
275
|
+
if (data.published !== undefined) payload.published = data.published;
|
|
282
276
|
return payload;
|
|
283
277
|
}
|
|
284
278
|
|
|
@@ -286,9 +280,7 @@ export function toUpdateSessionPayload(
|
|
|
286
280
|
* Convert LessonFormValues → ApiCreateLessonPayload.
|
|
287
281
|
* Used for POST /lms/courses/:id/structure/sessions/:sessionId/lessons.
|
|
288
282
|
*
|
|
289
|
-
*
|
|
290
|
-
* `status` and `visibility` from LessonFormValues are intentionally omitted
|
|
291
|
-
* (not in the DB schema). `linkedExam` (string) is converted to number.
|
|
283
|
+
* `linkedExam` (string) is converted to number.
|
|
292
284
|
*/
|
|
293
285
|
export function toCreateLessonPayload(
|
|
294
286
|
data: LessonFormValues & {
|
|
@@ -296,6 +288,7 @@ export function toCreateLessonPayload(
|
|
|
296
288
|
videoConversionJobId?: number;
|
|
297
289
|
instructorIds?: number[];
|
|
298
290
|
resources?: LessonResourceInput[];
|
|
291
|
+
confirmarPublicacaoComStatus?: boolean;
|
|
299
292
|
}
|
|
300
293
|
): ApiCreateLessonPayload {
|
|
301
294
|
return {
|
|
@@ -304,6 +297,11 @@ export function toCreateLessonPayload(
|
|
|
304
297
|
// backend accepts it. Cast is safe as long as LessonType ⊆ ApiLesson['tipo'].
|
|
305
298
|
tipo: data.type as ApiCreateLessonPayload['tipo'],
|
|
306
299
|
duracao: data.duration,
|
|
300
|
+
published: data.published,
|
|
301
|
+
statusProducao: data.status,
|
|
302
|
+
...(data.confirmarPublicacaoComStatus !== undefined && {
|
|
303
|
+
confirmarPublicacaoComStatus: data.confirmarPublicacaoComStatus,
|
|
304
|
+
}),
|
|
307
305
|
...(data.publicDescription && { descricaoPublica: data.publicDescription }),
|
|
308
306
|
...(data.privateDescription && {
|
|
309
307
|
descricaoPrivada: data.privateDescription,
|
|
@@ -349,6 +347,7 @@ export function toUpdateLessonPayload(
|
|
|
349
347
|
videoConversionJobId?: number;
|
|
350
348
|
instructorIds?: number[];
|
|
351
349
|
resources?: LessonResourceInput[];
|
|
350
|
+
confirmarPublicacaoComStatus?: boolean;
|
|
352
351
|
}
|
|
353
352
|
>
|
|
354
353
|
): ApiUpdateLessonPayload {
|
|
@@ -357,6 +356,11 @@ export function toUpdateLessonPayload(
|
|
|
357
356
|
if (data.type !== undefined)
|
|
358
357
|
payload.tipo = data.type as ApiCreateLessonPayload['tipo'];
|
|
359
358
|
if (data.duration !== undefined) payload.duracao = data.duration;
|
|
359
|
+
if (data.published !== undefined) payload.published = data.published;
|
|
360
|
+
if (data.status !== undefined) payload.statusProducao = data.status;
|
|
361
|
+
if (data.confirmarPublicacaoComStatus !== undefined) {
|
|
362
|
+
payload.confirmarPublicacaoComStatus = data.confirmarPublicacaoComStatus;
|
|
363
|
+
}
|
|
360
364
|
if (data.publicDescription !== undefined)
|
|
361
365
|
payload.descricaoPublica = data.publicDescription;
|
|
362
366
|
if (data.privateDescription !== undefined)
|
|
@@ -81,6 +81,13 @@ export interface ApiLesson {
|
|
|
81
81
|
tipo: 'video' | 'questao' | 'post' | 'exercicio';
|
|
82
82
|
/** Duration in minutes. */
|
|
83
83
|
duracao: number;
|
|
84
|
+
published?: boolean;
|
|
85
|
+
statusProducao?:
|
|
86
|
+
| 'preparada'
|
|
87
|
+
| 'gravada'
|
|
88
|
+
| 'editada'
|
|
89
|
+
| 'finalizada'
|
|
90
|
+
| 'publicada';
|
|
84
91
|
/** ID of the parent session (course_module). */
|
|
85
92
|
sessaoId: string;
|
|
86
93
|
videoProvedor?: 'youtube' | 'vimeo' | 'file_storage' | 'bunny' | 'custom';
|
|
@@ -115,6 +122,7 @@ export interface ApiSession {
|
|
|
115
122
|
titulo: string;
|
|
116
123
|
/** Duration in minutes. */
|
|
117
124
|
duracao: number;
|
|
125
|
+
published?: boolean;
|
|
118
126
|
collapsed: boolean;
|
|
119
127
|
}
|
|
120
128
|
|
|
@@ -124,6 +132,8 @@ export interface ApiSession {
|
|
|
124
132
|
export interface ApiCourse {
|
|
125
133
|
id: string;
|
|
126
134
|
name: string;
|
|
135
|
+
codigo?: string | null;
|
|
136
|
+
code?: string | null;
|
|
127
137
|
slug: string;
|
|
128
138
|
titulo: string;
|
|
129
139
|
descricao: string;
|
|
@@ -159,6 +169,7 @@ export interface ApiCreateSessionPayload {
|
|
|
159
169
|
duracao: number;
|
|
160
170
|
descricao?: string;
|
|
161
171
|
codigo?: string;
|
|
172
|
+
published?: boolean;
|
|
162
173
|
}
|
|
163
174
|
|
|
164
175
|
/**
|
|
@@ -185,6 +196,7 @@ export interface ApiUpdateSessionPayload {
|
|
|
185
196
|
duracao?: number;
|
|
186
197
|
descricao?: string;
|
|
187
198
|
codigo?: string;
|
|
199
|
+
published?: boolean;
|
|
188
200
|
}
|
|
189
201
|
|
|
190
202
|
/**
|
|
@@ -196,6 +208,14 @@ export interface ApiCreateLessonPayload {
|
|
|
196
208
|
tipo: 'video' | 'questao' | 'post' | 'exercicio';
|
|
197
209
|
/** Duration in minutes. */
|
|
198
210
|
duracao: number;
|
|
211
|
+
published?: boolean;
|
|
212
|
+
statusProducao?:
|
|
213
|
+
| 'preparada'
|
|
214
|
+
| 'gravada'
|
|
215
|
+
| 'editada'
|
|
216
|
+
| 'finalizada'
|
|
217
|
+
| 'publicada';
|
|
218
|
+
confirmarPublicacaoComStatus?: boolean;
|
|
199
219
|
descricaoPublica?: string;
|
|
200
220
|
descricaoPrivada?: string;
|
|
201
221
|
videoProvedor?: 'youtube' | 'vimeo' | 'file_storage' | 'bunny' | 'custom';
|
package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs
CHANGED
|
@@ -153,6 +153,7 @@ export function useCreateSessionMutation() {
|
|
|
153
153
|
duration: 0,
|
|
154
154
|
code: `S${String(nextIndex).padStart(2, '0')}`,
|
|
155
155
|
description: '',
|
|
156
|
+
published: false,
|
|
156
157
|
});
|
|
157
158
|
return apiCreateSession(request, courseId, payload);
|
|
158
159
|
},
|
|
@@ -220,6 +221,8 @@ export function useUpdateSessionMutation() {
|
|
|
220
221
|
title: formValues.title ?? s.title,
|
|
221
222
|
code: formValues.code ?? s.code,
|
|
222
223
|
duration: formValues.duration ?? s.duration,
|
|
224
|
+
description: formValues.description ?? s.description,
|
|
225
|
+
published: formValues.published ?? s.published,
|
|
223
226
|
}
|
|
224
227
|
: s
|
|
225
228
|
),
|
|
@@ -277,7 +280,7 @@ export function useCreateLessonMutation() {
|
|
|
277
280
|
publicDescription: '',
|
|
278
281
|
privateDescription: '',
|
|
279
282
|
status: 'preparada',
|
|
280
|
-
|
|
283
|
+
published: false,
|
|
281
284
|
});
|
|
282
285
|
return apiCreateLesson(request, courseId, sessionId, payload);
|
|
283
286
|
},
|
|
@@ -311,6 +314,7 @@ interface UpdateLessonVars {
|
|
|
311
314
|
videoConversionJobId?: number;
|
|
312
315
|
resources?: Resource[];
|
|
313
316
|
instructorIds?: number[];
|
|
317
|
+
confirmarPublicacaoComStatus?: boolean;
|
|
314
318
|
}
|
|
315
319
|
>;
|
|
316
320
|
}
|
|
@@ -336,8 +340,27 @@ export function useUpdateLessonMutation() {
|
|
|
336
340
|
lessonId,
|
|
337
341
|
toUpdateLessonPayload(formValues)
|
|
338
342
|
),
|
|
339
|
-
onSuccess: (
|
|
340
|
-
|
|
343
|
+
onSuccess: (apiLesson, { lessonId, formValues }) => {
|
|
344
|
+
const normalizedLesson = normalizeLesson(apiLesson);
|
|
345
|
+
|
|
346
|
+
updateLessonInStore(String(lessonId), {
|
|
347
|
+
...formValues,
|
|
348
|
+
code: normalizedLesson.code,
|
|
349
|
+
title: normalizedLesson.title,
|
|
350
|
+
publicDescription: normalizedLesson.publicDescription,
|
|
351
|
+
privateDescription: normalizedLesson.privateDescription,
|
|
352
|
+
duration: normalizedLesson.duration,
|
|
353
|
+
type: normalizedLesson.type,
|
|
354
|
+
status: normalizedLesson.status,
|
|
355
|
+
published: normalizedLesson.published,
|
|
356
|
+
videoProvider: normalizedLesson.videoProvider,
|
|
357
|
+
videoUrl: normalizedLesson.videoUrl,
|
|
358
|
+
linkedExam: normalizedLesson.linkedExam,
|
|
359
|
+
postContent: normalizedLesson.postContent,
|
|
360
|
+
transcription: normalizedLesson.transcription,
|
|
361
|
+
videoConversionJobId: normalizedLesson.videoConversionJobId,
|
|
362
|
+
resources: normalizedLesson.resources,
|
|
363
|
+
});
|
|
341
364
|
// Manual cache update for in-place field changes.
|
|
342
365
|
queryClient.setQueryData<CourseStructureCacheData>(
|
|
343
366
|
courseStructureQueryKey(courseId),
|
|
@@ -349,9 +372,21 @@ export function useUpdateLessonMutation() {
|
|
|
349
372
|
l.id === String(lessonId)
|
|
350
373
|
? {
|
|
351
374
|
...l,
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
375
|
+
code: normalizedLesson.code,
|
|
376
|
+
title: normalizedLesson.title,
|
|
377
|
+
publicDescription: normalizedLesson.publicDescription,
|
|
378
|
+
privateDescription: normalizedLesson.privateDescription,
|
|
379
|
+
duration: normalizedLesson.duration,
|
|
380
|
+
type: normalizedLesson.type,
|
|
381
|
+
status: normalizedLesson.status,
|
|
382
|
+
published: normalizedLesson.published,
|
|
383
|
+
videoProvider: normalizedLesson.videoProvider,
|
|
384
|
+
videoUrl: normalizedLesson.videoUrl,
|
|
385
|
+
linkedExam: normalizedLesson.linkedExam,
|
|
386
|
+
postContent: normalizedLesson.postContent,
|
|
387
|
+
transcription: normalizedLesson.transcription,
|
|
388
|
+
videoConversionJobId: normalizedLesson.videoConversionJobId,
|
|
389
|
+
resources: normalizedLesson.resources,
|
|
355
390
|
}
|
|
356
391
|
: l
|
|
357
392
|
),
|
|
@@ -98,7 +98,7 @@ interface Curso {
|
|
|
98
98
|
primaryColor?: string | null;
|
|
99
99
|
secondaryColor?: string | null;
|
|
100
100
|
nivel: 'iniciante' | 'intermediario' | 'avancado';
|
|
101
|
-
status: '
|
|
101
|
+
status: 'publicado' | 'rascunho' | 'arquivado';
|
|
102
102
|
tipoCurso: 'on_demand' | 'agendado' | 'hibrido';
|
|
103
103
|
categorias: string[];
|
|
104
104
|
destaque: boolean;
|
|
@@ -214,11 +214,12 @@ function toPtStatus(status?: string | null): CourseSheetFormValues['status'] {
|
|
|
214
214
|
const normalizedStatus = normalizeEnumValue(status);
|
|
215
215
|
|
|
216
216
|
if (
|
|
217
|
+
normalizedStatus === 'publicado' ||
|
|
217
218
|
normalizedStatus === 'ativo' ||
|
|
218
219
|
normalizedStatus === 'active' ||
|
|
219
220
|
normalizedStatus === 'published'
|
|
220
221
|
) {
|
|
221
|
-
return '
|
|
222
|
+
return 'publicado';
|
|
222
223
|
}
|
|
223
224
|
if (normalizedStatus === 'rascunho' || normalizedStatus === 'draft') {
|
|
224
225
|
return 'rascunho';
|
|
@@ -264,7 +265,7 @@ function toApiLevel(level: CourseSheetFormValues['nivel']) {
|
|
|
264
265
|
}
|
|
265
266
|
|
|
266
267
|
function toApiStatus(status: CourseSheetFormValues['status']) {
|
|
267
|
-
if (status === '
|
|
268
|
+
if (status === 'publicado') return 'published';
|
|
268
269
|
if (status === 'rascunho') return 'draft';
|
|
269
270
|
return 'archived';
|
|
270
271
|
}
|
|
@@ -296,7 +297,7 @@ const NIVEL_COLOR: Record<string, string> = {
|
|
|
296
297
|
};
|
|
297
298
|
|
|
298
299
|
const STATUS_VARIANT: Record<string, 'default' | 'secondary' | 'outline'> = {
|
|
299
|
-
|
|
300
|
+
publicado: 'default',
|
|
300
301
|
rascunho: 'secondary',
|
|
301
302
|
arquivado: 'outline',
|
|
302
303
|
};
|
|
@@ -407,7 +408,7 @@ export default function CursosPage() {
|
|
|
407
408
|
...(filtroStatusInput !== 'todos'
|
|
408
409
|
? {
|
|
409
410
|
status:
|
|
410
|
-
filtroStatusInput === '
|
|
411
|
+
filtroStatusInput === 'publicado'
|
|
411
412
|
? 'published'
|
|
412
413
|
: filtroStatusInput === 'rascunho'
|
|
413
414
|
? 'draft'
|
|
@@ -579,6 +580,19 @@ export default function CursosPage() {
|
|
|
579
580
|
curso.id === previewCurso.id ? previewCurso : curso
|
|
580
581
|
);
|
|
581
582
|
}, [cursos, previewCurso]);
|
|
583
|
+
const selectedCourses = useMemo(
|
|
584
|
+
() => cursos.filter((curso) => selectedIds.has(curso.id)),
|
|
585
|
+
[cursos, selectedIds]
|
|
586
|
+
);
|
|
587
|
+
const selectedArchivedIds = useMemo(
|
|
588
|
+
() =>
|
|
589
|
+
selectedCourses
|
|
590
|
+
.filter((curso) => curso.status === 'arquivado')
|
|
591
|
+
.map((curso) => curso.id),
|
|
592
|
+
[selectedCourses]
|
|
593
|
+
);
|
|
594
|
+
const hasSelectedNonArchived =
|
|
595
|
+
selectedCourses.length > selectedArchivedIds.length;
|
|
582
596
|
const totalPages = Math.max(1, effectiveCourseList?.lastPage ?? 1);
|
|
583
597
|
const safePage = Math.min(currentPage, totalPages);
|
|
584
598
|
function clearFilters() {
|
|
@@ -604,12 +618,18 @@ export default function CursosPage() {
|
|
|
604
618
|
|
|
605
619
|
function getStatusLabel(curso: Curso) {
|
|
606
620
|
return t(
|
|
607
|
-
`status.${curso.status === '
|
|
621
|
+
`status.${curso.status === 'publicado' ? 'published' : curso.status === 'rascunho' ? 'draft' : 'archived'}`
|
|
608
622
|
);
|
|
609
623
|
}
|
|
610
624
|
|
|
611
625
|
function openDeleteDialog(curso: Curso, e: React.MouseEvent) {
|
|
612
626
|
e.stopPropagation();
|
|
627
|
+
|
|
628
|
+
if (curso.status !== 'arquivado') {
|
|
629
|
+
toast.error(t('toasts.deleteOnlyArchived'));
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
|
|
613
633
|
setCursoToDelete(curso);
|
|
614
634
|
setDeleteDialogOpen(true);
|
|
615
635
|
}
|
|
@@ -715,6 +735,11 @@ export default function CursosPage() {
|
|
|
715
735
|
|
|
716
736
|
async function confirmDelete() {
|
|
717
737
|
if (!cursoToDelete) return;
|
|
738
|
+
if (cursoToDelete.status !== 'arquivado') {
|
|
739
|
+
toast.error(t('toasts.deleteOnlyArchived'));
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
|
|
718
743
|
await request({
|
|
719
744
|
url: `/lms/courses/${cursoToDelete.id}`,
|
|
720
745
|
method: 'DELETE',
|
|
@@ -935,7 +960,7 @@ export default function CursosPage() {
|
|
|
935
960
|
placeholder: t('table.headers.status'),
|
|
936
961
|
options: [
|
|
937
962
|
{ value: 'todos', label: t('filters.allStatuses') },
|
|
938
|
-
{ value: '
|
|
963
|
+
{ value: 'publicado', label: t('status.published') },
|
|
939
964
|
{ value: 'rascunho', label: t('status.draft') },
|
|
940
965
|
{ value: 'arquivado', label: t('status.archived') },
|
|
941
966
|
],
|
|
@@ -1043,13 +1068,24 @@ export default function CursosPage() {
|
|
|
1043
1068
|
variant="secondary"
|
|
1044
1069
|
className="gap-1.5 text-destructive hover:text-destructive"
|
|
1045
1070
|
onClick={() => {
|
|
1071
|
+
if (selectedArchivedIds.length === 0) {
|
|
1072
|
+
toast.error(t('toasts.deleteOnlyArchived'));
|
|
1073
|
+
return;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
if (hasSelectedNonArchived) {
|
|
1077
|
+
toast.warning(t('toasts.bulkDeleteArchivedOnly'));
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1046
1080
|
Promise.all(
|
|
1047
|
-
|
|
1081
|
+
selectedArchivedIds.map((id) =>
|
|
1048
1082
|
request({ url: `/lms/courses/${id}`, method: 'DELETE' })
|
|
1049
1083
|
)
|
|
1050
1084
|
).then(async () => {
|
|
1051
1085
|
toast.success(
|
|
1052
|
-
t('toasts.coursesRemoved', {
|
|
1086
|
+
t('toasts.coursesRemoved', {
|
|
1087
|
+
count: selectedArchivedIds.length,
|
|
1088
|
+
})
|
|
1053
1089
|
);
|
|
1054
1090
|
setSelectedIds(new Set());
|
|
1055
1091
|
await refetchCourses();
|
|
@@ -1254,6 +1290,7 @@ export default function CursosPage() {
|
|
|
1254
1290
|
</DropdownMenuItem>
|
|
1255
1291
|
<DropdownMenuSeparator />
|
|
1256
1292
|
<DropdownMenuItem
|
|
1293
|
+
disabled={curso.status !== 'arquivado'}
|
|
1257
1294
|
className="text-destructive focus:text-destructive"
|
|
1258
1295
|
onClick={(e) => openDeleteDialog(curso, e)}
|
|
1259
1296
|
>
|
|
@@ -1283,18 +1320,24 @@ export default function CursosPage() {
|
|
|
1283
1320
|
<div className="mt-3 flex items-center gap-3 border-t border-border/50 pt-2.5 text-xs text-muted-foreground">
|
|
1284
1321
|
<span className="flex items-center gap-1">
|
|
1285
1322
|
<Layers className="size-3.5 shrink-0" />
|
|
1286
|
-
<span className="font-medium text-foreground">
|
|
1287
|
-
|
|
1323
|
+
<span className="font-medium text-foreground">
|
|
1324
|
+
{curso.quantidadeSessoes}
|
|
1325
|
+
</span>{' '}
|
|
1326
|
+
sess.
|
|
1288
1327
|
</span>
|
|
1289
1328
|
<span className="flex items-center gap-1">
|
|
1290
1329
|
<BookOpen className="size-3.5 shrink-0" />
|
|
1291
|
-
<span className="font-medium text-foreground">
|
|
1292
|
-
|
|
1330
|
+
<span className="font-medium text-foreground">
|
|
1331
|
+
{curso.quantidadeAulas}
|
|
1332
|
+
</span>{' '}
|
|
1333
|
+
aulas
|
|
1293
1334
|
</span>
|
|
1294
1335
|
<span className="flex items-center gap-1">
|
|
1295
1336
|
<Paperclip className="size-3.5 shrink-0" />
|
|
1296
|
-
<span className="font-medium text-foreground">
|
|
1297
|
-
|
|
1337
|
+
<span className="font-medium text-foreground">
|
|
1338
|
+
{curso.quantidadeRecursos}
|
|
1339
|
+
</span>{' '}
|
|
1340
|
+
rec.
|
|
1298
1341
|
</span>
|
|
1299
1342
|
</div>
|
|
1300
1343
|
</CardContent>
|
|
@@ -1448,6 +1491,7 @@ export default function CursosPage() {
|
|
|
1448
1491
|
</DropdownMenuItem>
|
|
1449
1492
|
<DropdownMenuSeparator />
|
|
1450
1493
|
<DropdownMenuItem
|
|
1494
|
+
disabled={curso.status !== 'arquivado'}
|
|
1451
1495
|
className="text-destructive focus:text-destructive"
|
|
1452
1496
|
onClick={(e) => openDeleteDialog(curso, e)}
|
|
1453
1497
|
>
|
|
@@ -23,7 +23,7 @@ function toApiLevel(level: CourseSheetFormValues['nivel']) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
function toApiStatus(status: CourseSheetFormValues['status']) {
|
|
26
|
-
if (status === '
|
|
26
|
+
if (status === 'publicado') return 'published';
|
|
27
27
|
if (status === 'rascunho') return 'draft';
|
|
28
28
|
return 'archived';
|
|
29
29
|
}
|
|
@@ -54,7 +54,7 @@ function toPtLevel(level?: string | null): CourseSheetFormValues['nivel'] {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
function toPtStatus(status?: string | null): CourseSheetFormValues['status'] {
|
|
57
|
-
if (status === 'published' || status === 'active') return '
|
|
57
|
+
if (status === 'published' || status === 'active') return 'publicado';
|
|
58
58
|
if (status === 'draft') return 'rascunho';
|
|
59
59
|
if (status === 'archived') return 'arquivado';
|
|
60
60
|
return 'rascunho';
|
|
@@ -67,7 +67,7 @@ function toApiLevel(level: CourseSheetFormValues['nivel']) {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
function toApiStatus(status: CourseSheetFormValues['status']) {
|
|
70
|
-
if (status === '
|
|
70
|
+
if (status === 'publicado') return 'published';
|
|
71
71
|
if (status === 'rascunho') return 'draft';
|
|
72
72
|
return 'archived';
|
|
73
73
|
}
|
|
@@ -1146,7 +1146,7 @@ export default function TrainingPage() {
|
|
|
1146
1146
|
}
|
|
1147
1147
|
|
|
1148
1148
|
function courseStatusToApi(value: CourseSheetFormValues['status']) {
|
|
1149
|
-
if (value === '
|
|
1149
|
+
if (value === 'publicado') return 'published';
|
|
1150
1150
|
if (value === 'arquivado') return 'archived';
|
|
1151
1151
|
return 'draft';
|
|
1152
1152
|
}
|
|
@@ -1132,7 +1132,7 @@ export default function TrainingPage() {
|
|
|
1132
1132
|
}
|
|
1133
1133
|
|
|
1134
1134
|
function courseStatusToApi(value: CourseSheetFormValues['status']) {
|
|
1135
|
-
if (value === '
|
|
1135
|
+
if (value === 'publicado') return 'published';
|
|
1136
1136
|
if (value === 'arquivado') return 'archived';
|
|
1137
1137
|
return 'draft';
|
|
1138
1138
|
}
|
|
@@ -400,7 +400,7 @@
|
|
|
400
400
|
"advanced": "Advanced"
|
|
401
401
|
},
|
|
402
402
|
"status": {
|
|
403
|
-
"
|
|
403
|
+
"published": "Published",
|
|
404
404
|
"draft": "Draft",
|
|
405
405
|
"archived": "Archived"
|
|
406
406
|
},
|
|
@@ -574,7 +574,7 @@
|
|
|
574
574
|
"sub": "registered"
|
|
575
575
|
},
|
|
576
576
|
"activeCourses": {
|
|
577
|
-
"label": "
|
|
577
|
+
"label": "Published Courses",
|
|
578
578
|
"sub": "published"
|
|
579
579
|
},
|
|
580
580
|
"totalStudents": {
|
|
@@ -610,6 +610,7 @@
|
|
|
610
610
|
"advanced": "Advanced"
|
|
611
611
|
},
|
|
612
612
|
"status": {
|
|
613
|
+
"published": "Published",
|
|
613
614
|
"active": "Active",
|
|
614
615
|
"draft": "Draft",
|
|
615
616
|
"archived": "Archived"
|
|
@@ -771,6 +772,8 @@
|
|
|
771
772
|
"courseRemoved": "Course \"{title}\" removed.",
|
|
772
773
|
"coursesRemoved": "{count} course(s) removed.",
|
|
773
774
|
"coursesArchived": "{count} course(s) archived.",
|
|
775
|
+
"deleteOnlyArchived": "Only archived courses can be deleted.",
|
|
776
|
+
"bulkDeleteArchivedOnly": "Only selected archived courses will be deleted.",
|
|
774
777
|
"codeCopied": "Code \"{code}\" copied."
|
|
775
778
|
},
|
|
776
779
|
"StructurePage": {
|
|
@@ -942,6 +945,8 @@
|
|
|
942
945
|
},
|
|
943
946
|
"lessonRow": {
|
|
944
947
|
"rename": "Rename lesson",
|
|
948
|
+
"draft": "Draft",
|
|
949
|
+
"published": "Published",
|
|
945
950
|
"visibility": "Visibility: {value}",
|
|
946
951
|
"status": "Status: {value}",
|
|
947
952
|
"videoLinked": "Linked video",
|
|
@@ -409,7 +409,7 @@
|
|
|
409
409
|
"advanced": "Avançado"
|
|
410
410
|
},
|
|
411
411
|
"status": {
|
|
412
|
-
"
|
|
412
|
+
"published": "Publicado",
|
|
413
413
|
"draft": "Rascunho",
|
|
414
414
|
"archived": "Arquivado"
|
|
415
415
|
},
|
|
@@ -583,7 +583,7 @@
|
|
|
583
583
|
"sub": "cadastrados"
|
|
584
584
|
},
|
|
585
585
|
"activeCourses": {
|
|
586
|
-
"label": "Cursos
|
|
586
|
+
"label": "Cursos Publicados",
|
|
587
587
|
"sub": "publicados"
|
|
588
588
|
},
|
|
589
589
|
"totalStudents": {
|
|
@@ -619,6 +619,7 @@
|
|
|
619
619
|
"advanced": "Avançado"
|
|
620
620
|
},
|
|
621
621
|
"status": {
|
|
622
|
+
"published": "Publicado",
|
|
622
623
|
"active": "Ativo",
|
|
623
624
|
"draft": "Rascunho",
|
|
624
625
|
"archived": "Arquivado"
|
|
@@ -780,6 +781,8 @@
|
|
|
780
781
|
"courseRemoved": "Curso \"{title}\" removido.",
|
|
781
782
|
"coursesRemoved": "{count} curso(s) removido(s).",
|
|
782
783
|
"coursesArchived": "{count} curso(s) arquivado(s).",
|
|
784
|
+
"deleteOnlyArchived": "Apenas cursos arquivados podem ser excluídos.",
|
|
785
|
+
"bulkDeleteArchivedOnly": "Somente cursos arquivados selecionados serão excluídos.",
|
|
783
786
|
"codeCopied": "Código \"{code}\" copiado."
|
|
784
787
|
},
|
|
785
788
|
"StructurePage": {
|
|
@@ -951,6 +954,8 @@
|
|
|
951
954
|
},
|
|
952
955
|
"lessonRow": {
|
|
953
956
|
"rename": "Renomear aula",
|
|
957
|
+
"draft": "Rascunho",
|
|
958
|
+
"published": "Publicada",
|
|
954
959
|
"visibility": "Visibilidade: {value}",
|
|
955
960
|
"status": "Status: {value}",
|
|
956
961
|
"videoLinked": "Vídeo vinculado",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hed-hog/lms",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.357",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"dependencies": {
|
|
@@ -9,16 +9,16 @@
|
|
|
9
9
|
"@nestjs/core": "^11",
|
|
10
10
|
"@nestjs/jwt": "^11",
|
|
11
11
|
"@nestjs/mapped-types": "*",
|
|
12
|
-
"@hed-hog/api": "0.0.8",
|
|
13
|
-
"@hed-hog/api-types": "0.0.1",
|
|
14
12
|
"@hed-hog/api-prisma": "0.0.6",
|
|
15
|
-
"@hed-hog/
|
|
16
|
-
"@hed-hog/api
|
|
13
|
+
"@hed-hog/api-types": "0.0.1",
|
|
14
|
+
"@hed-hog/api": "0.0.8",
|
|
15
|
+
"@hed-hog/category": "0.0.356",
|
|
17
16
|
"@hed-hog/api-locale": "0.0.14",
|
|
18
|
-
"@hed-hog/
|
|
19
|
-
"@hed-hog/
|
|
20
|
-
"@hed-hog/finance": "0.0.
|
|
21
|
-
"@hed-hog/
|
|
17
|
+
"@hed-hog/api-pagination": "0.0.7",
|
|
18
|
+
"@hed-hog/crm": "0.0.356",
|
|
19
|
+
"@hed-hog/finance": "0.0.356",
|
|
20
|
+
"@hed-hog/core": "0.0.356",
|
|
21
|
+
"@hed-hog/queue": "0.0.356"
|
|
22
22
|
},
|
|
23
23
|
"exports": {
|
|
24
24
|
".": {
|