@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.
- package/dist/course/course-structure.controller.d.ts +60 -0
- package/dist/course/course-structure.controller.d.ts.map +1 -1
- package/dist/course/course-structure.controller.js +79 -0
- package/dist/course/course-structure.controller.js.map +1 -1
- package/dist/course/course-structure.service.d.ts +61 -1
- package/dist/course/course-structure.service.d.ts.map +1 -1
- package/dist/course/course-structure.service.js +326 -1
- package/dist/course/course-structure.service.js.map +1 -1
- package/dist/course/course.controller.d.ts +52 -4
- package/dist/course/course.controller.d.ts.map +1 -1
- package/dist/course/course.service.d.ts +52 -5
- package/dist/course/course.service.d.ts.map +1 -1
- package/dist/course/course.service.js +78 -57
- package/dist/course/course.service.js.map +1 -1
- package/dist/course/dto/create-course-structure-lesson.dto.d.ts.map +1 -1
- package/dist/course/dto/create-course-structure-lesson.dto.js +5 -1
- package/dist/course/dto/create-course-structure-lesson.dto.js.map +1 -1
- package/dist/course/dto/create-course.dto.d.ts +1 -1
- package/dist/course/dto/create-course.dto.d.ts.map +1 -1
- package/dist/course/dto/create-course.dto.js +4 -1
- package/dist/course/dto/create-course.dto.js.map +1 -1
- package/dist/course/dto/move-lesson.dto.d.ts +10 -0
- package/dist/course/dto/move-lesson.dto.d.ts.map +1 -0
- package/dist/course/dto/move-lesson.dto.js +28 -0
- package/dist/course/dto/move-lesson.dto.js.map +1 -0
- package/dist/course/dto/paste-lessons.dto.d.ts +4 -0
- package/dist/course/dto/paste-lessons.dto.d.ts.map +1 -0
- package/dist/course/dto/paste-lessons.dto.js +24 -0
- package/dist/course/dto/paste-lessons.dto.js.map +1 -0
- package/dist/course/dto/reorder-lessons.dto.d.ts +5 -0
- package/dist/course/dto/reorder-lessons.dto.d.ts.map +1 -0
- package/dist/course/dto/reorder-lessons.dto.js +24 -0
- package/dist/course/dto/reorder-lessons.dto.js.map +1 -0
- package/dist/course/dto/reorder-sessions.dto.d.ts +5 -0
- package/dist/course/dto/reorder-sessions.dto.d.ts.map +1 -0
- package/dist/course/dto/reorder-sessions.dto.js +24 -0
- package/dist/course/dto/reorder-sessions.dto.js.map +1 -0
- package/dist/training/training.controller.js +1 -1
- package/dist/training/training.controller.js.map +1 -1
- package/hedhog/data/image_type.yaml +20 -0
- package/hedhog/data/menu.yaml +2 -2
- package/hedhog/data/route.yaml +60 -6
- package/hedhog/frontend/app/_components/class-form-sheet.tsx.ejs +146 -165
- package/hedhog/frontend/app/_components/course-avatar.tsx.ejs +70 -0
- package/hedhog/frontend/app/_components/course-form-sheet.tsx.ejs +372 -22
- package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +437 -77
- package/hedhog/frontend/app/classes/page.tsx.ejs +311 -289
- package/hedhog/frontend/app/courses/[id]/_components/CourseCertificateCard.tsx.ejs +10 -7
- package/hedhog/frontend/app/courses/[id]/_components/CourseClassificationCard.tsx.ejs +23 -32
- package/hedhog/frontend/app/courses/[id]/_components/CourseContentCard.tsx.ejs +3 -9
- package/hedhog/frontend/app/courses/[id]/_components/CourseDangerZoneCard.tsx.ejs +26 -16
- package/hedhog/frontend/app/courses/[id]/_components/CourseFlagsCard.tsx.ejs +19 -5
- package/hedhog/frontend/app/courses/[id]/_components/CourseMainInfoCard.tsx.ejs +10 -14
- package/hedhog/frontend/app/courses/[id]/_components/CourseMediaCard.tsx.ejs +131 -107
- package/hedhog/frontend/app/courses/[id]/_components/CourseRelationsCard.tsx.ejs +10 -7
- package/hedhog/frontend/app/courses/[id]/_components/CourseSectionCard.tsx.ejs +38 -19
- package/hedhog/frontend/app/courses/[id]/_components/CourseSummaryCard.tsx.ejs +1 -1
- package/hedhog/frontend/app/courses/[id]/_components/course-edit-types.ts.ejs +1 -1
- package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +336 -1057
- package/hedhog/frontend/app/courses/[id]/structure/_components/confirm-dialog.tsx.ejs +45 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-dnd.tsx.ejs +362 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-panel.tsx.ejs +111 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-skeleton.tsx.ejs +64 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree.tsx.ejs +134 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/detail-course.tsx.ejs +113 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson.tsx.ejs +314 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/detail-panel.tsx.ejs +62 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/detail-session.tsx.ejs +174 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/drag-handle.tsx.ejs +58 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/drag-overlay.tsx.ejs +52 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-bulk.tsx.ejs +276 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +1216 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +1827 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-session.tsx.ejs +443 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/highlighted-text.tsx.ejs +41 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/mock-data.ts.ejs +184 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/multi-select-bar.tsx.ejs +264 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/search-filter.tsx.ejs +96 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/session-picker-dialog.tsx.ejs +74 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/shortcuts-help.tsx.ejs +136 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/sortable-tree-row.tsx.ejs +80 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/store.ts.ejs +948 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-context-menu.tsx.ejs +525 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-display-settings-popover.tsx.ejs +150 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-helpers.ts.ejs +182 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-course.tsx.ejs +52 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-lesson.tsx.ejs +271 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-session.tsx.ejs +167 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row.tsx.ejs +108 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/types.ts.ejs +122 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/use-course-structure-shortcuts.ts.ejs +318 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/use-tree-display-settings.ts.ejs +97 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs +347 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/course-structure-contract.ts.ejs +195 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/services/course-structure.service.ts.ejs +420 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/types/api-course.types.ts.ejs +254 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +987 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-query.ts.ejs +86 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure.ts.ejs +160 -0
- package/hedhog/frontend/app/courses/[id]/structure/page.tsx.ejs +10 -3212
- package/hedhog/frontend/app/courses/page.tsx.ejs +45 -26
- package/hedhog/frontend/app/{training → paths}/page.tsx.ejs +29 -7
- package/hedhog/frontend/messages/en.json +88 -10
- package/hedhog/frontend/messages/pt.json +88 -10
- package/hedhog/table/course.yaml +1 -1
- package/hedhog/table/image_type.yaml +14 -0
- package/package.json +7 -7
- package/src/course/course-structure.controller.ts +63 -0
- package/src/course/course-structure.service.ts +390 -3
- package/src/course/course.service.ts +59 -27
- package/src/course/dto/create-course-structure-lesson.dto.ts +3 -2
- package/src/course/dto/create-course.dto.ts +4 -1
- package/src/course/dto/move-lesson.dto.ts +17 -0
- package/src/course/dto/paste-lessons.dto.ts +9 -0
- package/src/course/dto/reorder-lessons.dto.ts +10 -0
- package/src/course/dto/reorder-sessions.dto.ts +10 -0
- package/src/training/training.controller.ts +1 -1
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { Button } from '@/components/ui/button';
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
Tooltip,
|
|
4
|
+
TooltipContent,
|
|
5
|
+
TooltipProvider,
|
|
6
|
+
TooltipTrigger,
|
|
7
|
+
} from '@/components/ui/tooltip';
|
|
4
8
|
import {
|
|
5
9
|
Eye,
|
|
6
10
|
FileImage,
|
|
7
11
|
ImagePlus,
|
|
8
12
|
Loader2,
|
|
13
|
+
Trash2,
|
|
9
14
|
UploadCloud,
|
|
10
15
|
} from 'lucide-react';
|
|
11
16
|
import { type ChangeEvent, useRef } from 'react';
|
|
@@ -18,35 +23,58 @@ type MediaFile = {
|
|
|
18
23
|
name: string;
|
|
19
24
|
};
|
|
20
25
|
|
|
21
|
-
type
|
|
26
|
+
type ImageTypeSpec = {
|
|
27
|
+
suggestedWidth: number | null;
|
|
28
|
+
suggestedHeight: number | null;
|
|
29
|
+
allowedExtensions: string | null;
|
|
30
|
+
aspectRatio: string | null;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
type MediaRowProps = {
|
|
22
34
|
label: string;
|
|
23
|
-
description: string;
|
|
24
|
-
ratioClassName: string;
|
|
25
35
|
preview: string | null;
|
|
26
36
|
file?: MediaFile;
|
|
27
37
|
loading: boolean;
|
|
38
|
+
imageType?: ImageTypeSpec | null;
|
|
28
39
|
onSelect: (event: ChangeEvent<HTMLInputElement>) => void;
|
|
29
40
|
onOpenFile?: () => void;
|
|
41
|
+
onRemoveFile?: () => void;
|
|
30
42
|
};
|
|
31
43
|
|
|
32
|
-
function
|
|
44
|
+
function MediaRow({
|
|
33
45
|
label,
|
|
34
|
-
description,
|
|
35
|
-
ratioClassName,
|
|
36
46
|
preview,
|
|
37
47
|
file,
|
|
38
48
|
loading,
|
|
49
|
+
imageType,
|
|
39
50
|
onSelect,
|
|
40
51
|
onOpenFile,
|
|
41
|
-
|
|
52
|
+
onRemoveFile,
|
|
53
|
+
}: MediaRowProps) {
|
|
42
54
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
43
55
|
|
|
56
|
+
const specParts: string[] = [];
|
|
57
|
+
if (imageType?.suggestedWidth && imageType?.suggestedHeight) {
|
|
58
|
+
specParts.push(
|
|
59
|
+
`${imageType.suggestedWidth}×${imageType.suggestedHeight}px`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
if (imageType?.aspectRatio) {
|
|
63
|
+
specParts.push(imageType.aspectRatio);
|
|
64
|
+
}
|
|
65
|
+
if (imageType?.allowedExtensions) {
|
|
66
|
+
specParts.push(
|
|
67
|
+
imageType.allowedExtensions
|
|
68
|
+
.split(',')
|
|
69
|
+
.map((ext) => ext.trim().toUpperCase())
|
|
70
|
+
.join(' / ')
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
const specText = specParts.join(' · ');
|
|
74
|
+
|
|
44
75
|
return (
|
|
45
|
-
<div className="space-y-
|
|
46
|
-
<
|
|
47
|
-
<p className="text-sm font-medium text-foreground">{label}</p>
|
|
48
|
-
<p className="text-xs leading-5 text-muted-foreground">{description}</p>
|
|
49
|
-
</div>
|
|
76
|
+
<div className="space-y-1.5">
|
|
77
|
+
<p className="text-xs font-medium text-muted-foreground">{label}</p>
|
|
50
78
|
|
|
51
79
|
<input
|
|
52
80
|
ref={inputRef}
|
|
@@ -56,99 +84,87 @@ function MediaPane({
|
|
|
56
84
|
onChange={onSelect}
|
|
57
85
|
/>
|
|
58
86
|
|
|
59
|
-
<
|
|
60
|
-
type="button"
|
|
61
|
-
className={cn(
|
|
62
|
-
'group relative block w-full overflow-hidden rounded-2xl border border-border/70 bg-gradient-to-br from-muted/40 to-background text-left transition-all hover:border-border hover:shadow-sm',
|
|
63
|
-
ratioClassName
|
|
64
|
-
)}
|
|
65
|
-
onClick={() => inputRef.current?.click()}
|
|
66
|
-
>
|
|
87
|
+
<div className="relative h-40 w-full overflow-hidden rounded-xl border border-border/70 bg-muted/30">
|
|
67
88
|
{preview ? (
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
/>
|
|
74
|
-
<div className="absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/70 via-black/10 to-transparent p-4">
|
|
75
|
-
<div className="flex flex-wrap gap-2">
|
|
76
|
-
<span className="rounded-full bg-white/15 px-2.5 py-1 text-xs font-medium text-white backdrop-blur">
|
|
77
|
-
Preview pronta
|
|
78
|
-
</span>
|
|
79
|
-
<span className="rounded-full bg-white/15 px-2.5 py-1 text-xs font-medium text-white backdrop-blur">
|
|
80
|
-
Clique para substituir
|
|
81
|
-
</span>
|
|
82
|
-
</div>
|
|
83
|
-
</div>
|
|
84
|
-
</>
|
|
89
|
+
<img
|
|
90
|
+
src={preview}
|
|
91
|
+
alt={label}
|
|
92
|
+
className="h-full w-full object-contain"
|
|
93
|
+
/>
|
|
85
94
|
) : (
|
|
86
|
-
<div className="flex h-full flex-col items-center justify-center gap-
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
</div>
|
|
90
|
-
<div className="space-y-1">
|
|
91
|
-
<p className="text-sm font-medium text-foreground">
|
|
92
|
-
Enviar {label.toLowerCase()}
|
|
93
|
-
</p>
|
|
94
|
-
<p className="text-xs leading-5 text-muted-foreground">
|
|
95
|
-
Clique para selecionar uma imagem.
|
|
96
|
-
</p>
|
|
97
|
-
</div>
|
|
95
|
+
<div className="flex h-full flex-col items-center justify-center gap-2 text-center">
|
|
96
|
+
<ImagePlus className="h-5 w-5 text-muted-foreground" />
|
|
97
|
+
<p className="text-xs text-muted-foreground">Sem imagem</p>
|
|
98
98
|
</div>
|
|
99
99
|
)}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
<Loader2 className="h-5 w-5 animate-spin text-foreground" />
|
|
104
|
-
<div className="w-full max-w-[180px] space-y-2">
|
|
105
|
-
<Progress value={72} className="h-2" />
|
|
106
|
-
<p className="text-xs text-muted-foreground">Enviando arquivo...</p>
|
|
107
|
-
</div>
|
|
100
|
+
{loading && (
|
|
101
|
+
<div className="absolute inset-0 flex items-center justify-center bg-background/70">
|
|
102
|
+
<Loader2 className="h-5 w-5 animate-spin" />
|
|
108
103
|
</div>
|
|
104
|
+
)}
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
{specText ? (
|
|
108
|
+
<p className="text-[11px] text-muted-foreground/70">{specText}</p>
|
|
109
|
+
) : null}
|
|
110
|
+
|
|
111
|
+
<div className="flex items-center justify-end gap-1">
|
|
112
|
+
<TooltipProvider>
|
|
113
|
+
<Tooltip>
|
|
114
|
+
<TooltipTrigger asChild>
|
|
115
|
+
<Button
|
|
116
|
+
type="button"
|
|
117
|
+
variant="outline"
|
|
118
|
+
size="icon"
|
|
119
|
+
className="h-7 w-7"
|
|
120
|
+
onClick={() => inputRef.current?.click()}
|
|
121
|
+
>
|
|
122
|
+
<UploadCloud className="h-3.5 w-3.5" />
|
|
123
|
+
</Button>
|
|
124
|
+
</TooltipTrigger>
|
|
125
|
+
<TooltipContent>
|
|
126
|
+
{preview ? 'Substituir imagem' : 'Enviar imagem'}
|
|
127
|
+
</TooltipContent>
|
|
128
|
+
</Tooltip>
|
|
129
|
+
</TooltipProvider>
|
|
130
|
+
|
|
131
|
+
{file && onOpenFile ? (
|
|
132
|
+
<TooltipProvider>
|
|
133
|
+
<Tooltip>
|
|
134
|
+
<TooltipTrigger asChild>
|
|
135
|
+
<Button
|
|
136
|
+
type="button"
|
|
137
|
+
variant="outline"
|
|
138
|
+
size="icon"
|
|
139
|
+
className="h-7 w-7"
|
|
140
|
+
onClick={onOpenFile}
|
|
141
|
+
>
|
|
142
|
+
<Eye className="h-3.5 w-3.5" />
|
|
143
|
+
</Button>
|
|
144
|
+
</TooltipTrigger>
|
|
145
|
+
<TooltipContent>Visualizar imagem original</TooltipContent>
|
|
146
|
+
</Tooltip>
|
|
147
|
+
</TooltipProvider>
|
|
109
148
|
) : null}
|
|
110
|
-
</button>
|
|
111
149
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
variant="outline"
|
|
131
|
-
size="sm"
|
|
132
|
-
className="gap-2"
|
|
133
|
-
onClick={() => inputRef.current?.click()}
|
|
134
|
-
>
|
|
135
|
-
<UploadCloud className="h-4 w-4" />
|
|
136
|
-
{preview ? 'Substituir' : 'Enviar'}
|
|
137
|
-
</Button>
|
|
138
|
-
|
|
139
|
-
{file && onOpenFile ? (
|
|
140
|
-
<Button
|
|
141
|
-
type="button"
|
|
142
|
-
variant="ghost"
|
|
143
|
-
size="sm"
|
|
144
|
-
className="gap-2"
|
|
145
|
-
onClick={onOpenFile}
|
|
146
|
-
>
|
|
147
|
-
<Eye className="h-4 w-4" />
|
|
148
|
-
Visualizar
|
|
149
|
-
</Button>
|
|
150
|
-
) : null}
|
|
151
|
-
</div>
|
|
150
|
+
{file && onRemoveFile ? (
|
|
151
|
+
<TooltipProvider>
|
|
152
|
+
<Tooltip>
|
|
153
|
+
<TooltipTrigger asChild>
|
|
154
|
+
<Button
|
|
155
|
+
type="button"
|
|
156
|
+
variant="outline"
|
|
157
|
+
size="icon"
|
|
158
|
+
className="h-7 w-7 text-destructive hover:text-destructive"
|
|
159
|
+
onClick={onRemoveFile}
|
|
160
|
+
>
|
|
161
|
+
<Trash2 className="h-3.5 w-3.5" />
|
|
162
|
+
</Button>
|
|
163
|
+
</TooltipTrigger>
|
|
164
|
+
<TooltipContent>Remover imagem</TooltipContent>
|
|
165
|
+
</Tooltip>
|
|
166
|
+
</TooltipProvider>
|
|
167
|
+
) : null}
|
|
152
168
|
</div>
|
|
153
169
|
</div>
|
|
154
170
|
);
|
|
@@ -165,6 +181,10 @@ type CourseMediaCardProps = {
|
|
|
165
181
|
bannerFile?: MediaFile;
|
|
166
182
|
onOpenLogoFile?: () => void;
|
|
167
183
|
onOpenBannerFile?: () => void;
|
|
184
|
+
onRemoveLogoFile?: () => void;
|
|
185
|
+
onRemoveBannerFile?: () => void;
|
|
186
|
+
logoImageType?: ImageTypeSpec | null;
|
|
187
|
+
bannerImageType?: ImageTypeSpec | null;
|
|
168
188
|
t: TranslationFn;
|
|
169
189
|
};
|
|
170
190
|
|
|
@@ -179,6 +199,10 @@ export function CourseMediaCard({
|
|
|
179
199
|
bannerFile,
|
|
180
200
|
onOpenLogoFile,
|
|
181
201
|
onOpenBannerFile,
|
|
202
|
+
onRemoveLogoFile,
|
|
203
|
+
onRemoveBannerFile,
|
|
204
|
+
logoImageType,
|
|
205
|
+
bannerImageType,
|
|
182
206
|
t,
|
|
183
207
|
}: CourseMediaCardProps) {
|
|
184
208
|
return (
|
|
@@ -187,27 +211,27 @@ export function CourseMediaCard({
|
|
|
187
211
|
description="Gerencie os ativos visuais do curso com preview e ações rápidas."
|
|
188
212
|
icon={FileImage}
|
|
189
213
|
>
|
|
190
|
-
<div className="space-y-
|
|
191
|
-
<
|
|
214
|
+
<div className="space-y-4">
|
|
215
|
+
<MediaRow
|
|
192
216
|
label={t('form.fields.banner.label')}
|
|
193
|
-
description={t('form.fields.banner.description')}
|
|
194
|
-
ratioClassName="aspect-[16/9]"
|
|
195
217
|
preview={bannerPreview}
|
|
196
218
|
file={bannerFile}
|
|
197
219
|
loading={uploadingBanner}
|
|
220
|
+
imageType={bannerImageType}
|
|
198
221
|
onSelect={onBannerSelect}
|
|
199
222
|
onOpenFile={onOpenBannerFile}
|
|
223
|
+
onRemoveFile={onRemoveBannerFile}
|
|
200
224
|
/>
|
|
201
225
|
|
|
202
|
-
<
|
|
226
|
+
<MediaRow
|
|
203
227
|
label={t('form.fields.logo.label')}
|
|
204
|
-
description={t('form.fields.logo.description')}
|
|
205
|
-
ratioClassName="aspect-square max-w-[220px]"
|
|
206
228
|
preview={logoPreview}
|
|
207
229
|
file={logoFile}
|
|
208
230
|
loading={uploadingLogo}
|
|
231
|
+
imageType={logoImageType}
|
|
209
232
|
onSelect={onLogoSelect}
|
|
210
233
|
onOpenFile={onOpenLogoFile}
|
|
234
|
+
onRemoveFile={onRemoveLogoFile}
|
|
211
235
|
/>
|
|
212
236
|
</div>
|
|
213
237
|
</CourseSectionCard>
|
|
@@ -22,7 +22,9 @@ type CourseRelationsCardProps = {
|
|
|
22
22
|
t: TranslationFn;
|
|
23
23
|
categoryOptions: PickerOption[];
|
|
24
24
|
instructorOptions: PickerOption[];
|
|
25
|
-
onCreateCategory: (
|
|
25
|
+
onCreateCategory: (
|
|
26
|
+
values: Record<string, string>
|
|
27
|
+
) => Promise<PickerOption | null>;
|
|
26
28
|
onCreateInstructor: () => void;
|
|
27
29
|
};
|
|
28
30
|
|
|
@@ -40,7 +42,7 @@ export function CourseRelationsCard({
|
|
|
40
42
|
description="Agrupe os vínculos que ajudam a encontrar, operar e ensinar este curso."
|
|
41
43
|
icon={Network}
|
|
42
44
|
>
|
|
43
|
-
<div className="space-y-
|
|
45
|
+
<div className="space-y-3">
|
|
44
46
|
<FormField
|
|
45
47
|
control={form.control}
|
|
46
48
|
name="categorias"
|
|
@@ -52,7 +54,9 @@ export function CourseRelationsCard({
|
|
|
52
54
|
onChange={field.onChange}
|
|
53
55
|
options={categoryOptions}
|
|
54
56
|
placeholder={t('form.fields.categories.selectPlaceholder')}
|
|
55
|
-
searchPlaceholder={t(
|
|
57
|
+
searchPlaceholder={t(
|
|
58
|
+
'form.fields.categories.searchPlaceholder'
|
|
59
|
+
)}
|
|
56
60
|
entityLabel="categoria"
|
|
57
61
|
emptyStateDescription={t('form.fields.categories.noResults')}
|
|
58
62
|
emptyHint="Nenhuma categoria vinculada ainda."
|
|
@@ -101,7 +105,9 @@ export function CourseRelationsCard({
|
|
|
101
105
|
onChange={field.onChange}
|
|
102
106
|
options={instructorOptions}
|
|
103
107
|
placeholder={t('form.fields.instructors.selectPlaceholder')}
|
|
104
|
-
searchPlaceholder={t(
|
|
108
|
+
searchPlaceholder={t(
|
|
109
|
+
'form.fields.instructors.searchPlaceholder'
|
|
110
|
+
)}
|
|
105
111
|
entityLabel="instrutor"
|
|
106
112
|
emptyStateDescription={t('form.fields.instructors.noResults')}
|
|
107
113
|
emptyHint="Nenhum instrutor vinculado ainda."
|
|
@@ -128,9 +134,6 @@ export function CourseRelationsCard({
|
|
|
128
134
|
placeholder={t('form.fields.prerequisites.placeholder')}
|
|
129
135
|
/>
|
|
130
136
|
</FormControl>
|
|
131
|
-
<p className="text-sm text-muted-foreground">
|
|
132
|
-
{t('form.fields.prerequisites.description')}
|
|
133
|
-
</p>
|
|
134
137
|
<FormMessage />
|
|
135
138
|
</FormItem>
|
|
136
139
|
)}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
2
|
+
import {
|
|
3
|
+
Tooltip,
|
|
4
|
+
TooltipContent,
|
|
5
|
+
TooltipProvider,
|
|
6
|
+
TooltipTrigger,
|
|
7
|
+
} from '@/components/ui/tooltip';
|
|
2
8
|
import { cn } from '@/lib/utils';
|
|
3
9
|
import type { LucideIcon } from 'lucide-react';
|
|
4
10
|
import type { ReactNode } from 'react';
|
|
@@ -22,27 +28,40 @@ export function CourseSectionCard({
|
|
|
22
28
|
contentClassName,
|
|
23
29
|
action,
|
|
24
30
|
}: CourseSectionCardProps) {
|
|
31
|
+
const iconEl = Icon ? (
|
|
32
|
+
<div className="flex h-7 w-7 shrink-0 items-center justify-center rounded-lg border border-border/60 bg-muted/30 text-foreground">
|
|
33
|
+
<Icon className="h-3.5 w-3.5" />
|
|
34
|
+
</div>
|
|
35
|
+
) : null;
|
|
36
|
+
|
|
25
37
|
return (
|
|
26
38
|
<Card className={cn('border-border/70 shadow-sm', className)}>
|
|
27
|
-
<CardHeader className="space-y-
|
|
28
|
-
<div className="flex items-
|
|
29
|
-
<div className="flex min-w-0 items-
|
|
30
|
-
{Icon ? (
|
|
31
|
-
<
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
<CardHeader className="space-y-1.5 pb-3">
|
|
40
|
+
<div className="flex items-center justify-between gap-3">
|
|
41
|
+
<div className="flex min-w-0 items-center gap-2">
|
|
42
|
+
{Icon && description ? (
|
|
43
|
+
<TooltipProvider>
|
|
44
|
+
<Tooltip>
|
|
45
|
+
<TooltipTrigger asChild>
|
|
46
|
+
<button
|
|
47
|
+
type="button"
|
|
48
|
+
tabIndex={-1}
|
|
49
|
+
className="cursor-default focus:outline-none"
|
|
50
|
+
>
|
|
51
|
+
{iconEl}
|
|
52
|
+
</button>
|
|
53
|
+
</TooltipTrigger>
|
|
54
|
+
<TooltipContent side="right" className="max-w-65 text-xs">
|
|
55
|
+
{description}
|
|
56
|
+
</TooltipContent>
|
|
57
|
+
</Tooltip>
|
|
58
|
+
</TooltipProvider>
|
|
59
|
+
) : (
|
|
60
|
+
iconEl
|
|
61
|
+
)}
|
|
62
|
+
<CardTitle className="text-sm font-semibold tracking-tight">
|
|
63
|
+
{title}
|
|
64
|
+
</CardTitle>
|
|
46
65
|
</div>
|
|
47
66
|
|
|
48
67
|
{action ? <div className="shrink-0">{action}</div> : null}
|