feat: improve learning and platform operations
All checks were successful
CI / Validate frontend and API (push) Successful in 45s
CI / Build and publish Docker images (push) Successful in 24s

This commit is contained in:
Cauê Faleiros
2026-09-01 15:38:14 -03:00
parent 4736fb5208
commit 3d4c72e85c
20 changed files with 292 additions and 45 deletions

View File

@@ -42,6 +42,7 @@ const CourseModal: React.FC<{
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const [category, setCategory] = useState('');
const [status, setStatus] = useState<Course['status']>('draft');
const [thumbnailPreview, setThumbnailPreview] = useState<string>('');
const [instructor, setInstructor] = useState('Mário Morgado');
const [instructorRole, setInstructorRole] = useState('Especialista em Performance');
@@ -76,6 +77,7 @@ const CourseModal: React.FC<{
setTitle(initialData.title);
setDescription(initialData.description);
setCategory(initialData.category);
setStatus(initialData.status || 'published');
setThumbnailPreview(initialData.thumbnail);
setInstructor(initialData.instructor || 'Mário Morgado');
setInstructorRole(initialData.instructorRole || 'Especialista em Performance');
@@ -86,6 +88,7 @@ const CourseModal: React.FC<{
setTitle('');
setDescription('');
setCategory(CATEGORIES[0]);
setStatus('draft');
setThumbnailPreview('');
setInstructor('Mário Morgado');
setInstructorRole('Especialista em Performance');
@@ -187,6 +190,7 @@ const CourseModal: React.FC<{
title,
description,
category: category || CATEGORIES[0],
status,
thumbnail: thumbnailPreview || 'https://images.unsplash.com/photo-1460925895917-afdab827c52f?auto=format&fit=crop&w=800&q=80',
duration: durationStr,
instructor,
@@ -198,8 +202,8 @@ const CourseModal: React.FC<{
attachments: attachments
});
onClose();
} catch {
alert('We could not save this course. Confirm that you are still signed in and that all asset URLs are valid.');
} catch (error) {
alert(error instanceof Error ? error.message : 'Não foi possível salvar este curso.');
} finally {
setIsSubmitting(false);
}
@@ -275,6 +279,14 @@ const CourseModal: React.FC<{
</select>
</div>
<div>
<label className="text-xs text-gray-400 uppercase font-bold tracking-wider mb-1 block">Publicação</label>
<select value={status} onChange={(e) => setStatus(e.target.value as Course['status'])} className="w-full bg-black/50 text-white border border-white/10 rounded-xl px-3 py-2.5 text-xs focus:outline-none focus:border-orange-500/50">
<option value="draft" className="bg-zinc-900">Salvar como rascunho</option>
<option value="published" className="bg-zinc-900">Publicar agora</option>
</select>
</div>
<div>
<label className="text-xs text-gray-400 uppercase font-bold tracking-wider mb-1 block">Instrutor</label>
<input
@@ -507,7 +519,7 @@ const CourseModal: React.FC<{
disabled={isSubmitting}
className="w-full bg-orange-500 hover:bg-orange-600 text-white font-extrabold text-base py-4 rounded-2xl active:scale-95 transition-all shadow-lg shadow-orange-500/25"
>
{isSubmitting ? 'Salvando...' : (initialData ? 'Salvar Alterações no Curso' : 'Publicar Curso & Materiais')}
{isSubmitting ? 'Salvando...' : status === 'draft' ? 'Salvar como Rascunho' : (initialData ? 'Salvar e Publicar Curso' : 'Publicar Curso & Materiais')}
</button>
</div>
</div>
@@ -532,21 +544,18 @@ export const ManageCourses: React.FC = () => {
const loadData = async () => {
setLoading(true);
const data = await getManagedCourses();
setCourses(data);
const analyticsResponse = await instructorApi.analytics();
setAnalytics(analyticsResponse.data);
// Load all comments
const commentsAccumulator: {comment: Comment, courseTitle: string}[] = [];
for (const c of data) {
const courseComments = await getComments(c.id);
courseComments.forEach(comm => {
commentsAccumulator.push({ comment: comm, courseTitle: c.title });
});
try {
const data = await getManagedCourses();
setCourses(data);
const analyticsResponse = await instructorApi.analytics();
setAnalytics(analyticsResponse.data);
const commentLists = await Promise.all(data.map(async (course) => ({ course, comments: await getComments(course.id) })));
setAllComments(commentLists.flatMap(({ course, comments }) => comments.map((comment) => ({ comment, courseTitle: course.title }))));
} catch {
showToast('Não foi possível carregar o painel do instrutor.', 'error');
} finally {
setLoading(false);
}
setAllComments(commentsAccumulator);
setLoading(false);
};
useEffect(() => {
@@ -713,6 +722,7 @@ export const ManageCourses: React.FC = () => {
<div className="absolute top-3 left-3 bg-black/70 backdrop-blur-md px-2.5 py-1 rounded-[980px] text-caption font-semibold uppercase tracking-caption text-white border border-white/10">
{course.category}
</div>
{course.status === 'draft' && <div className="absolute top-3 right-3 bg-amber-500/20 backdrop-blur-md px-2.5 py-1 rounded-[980px] text-caption font-semibold uppercase tracking-caption text-amber-300 border border-amber-500/30">Rascunho</div>}
</div>
{/* Body */}