- {/* Controls & Stats */}
-
-
-
-
setSelectedCohortId(e.target.value)}
- className="appearance-none bg-black/20 text-gray-900 dark:text-white border border-white/10 rounded-xl pl-4 pr-10 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500/50 min-w-[200px]"
- >
- All Cohorts
- {cohorts.map(c => {c.name} )}
-
-
- ▼
-
-
-
-
-
setSearchQuery(e.target.value)}
- className="w-full bg-black/20 border border-white/10 rounded-xl pl-10 pr-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500/50 text-gray-900 dark:text-white placeholder-gray-600"
- />
+
+
+
+ Registro de Calificaciones
+
+ {/* Controls & Stats */}
+
+
+
+
setSelectedCohortId(e.target.value)}
+ className="appearance-none bg-black/20 text-gray-900 dark:text-white border border-white/10 rounded-xl pl-4 pr-10 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500/50 min-w-[200px]"
+ >
+ All Cohorts
+ {cohorts.map(c => {c.name} )}
+
+
+ ▼
-
-
-
-
Students
-
{filteredStudents.length}
-
-
-
Avg Score
-
- {(averageScore * 100).toFixed(0)}%
-
-
+
+
+ setSearchQuery(e.target.value)}
+ className="w-full bg-black/20 border border-white/10 rounded-xl pl-10 pr-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500/50 text-gray-900 dark:text-white placeholder-gray-600"
+ />
- {/* Student List */}
-
-
-
-
- Student
- Progress
- Avg. Score
- Last Active
- Status
-
-
-
- {filteredStudents.length === 0 ? (
-
- No students found.
-
- ) : filteredStudents.map((s) => (
-
-
-
-
- {s.full_name.charAt(0)}
-
-
-
{s.full_name}
-
- {s.email}
-
-
-
-
-
-
-
- {(s.progress * 100).toFixed(0)}%
-
-
-
-
-
- {s.average_score !== null ? (
- = 0.8 ? 'text-green-400' : (s.average_score || 0) >= 0.6 ? 'text-yellow-400' : 'text-red-400'}`}>
- {((s.average_score || 0) * 100).toFixed(0)}%
-
- ) : (
- No grades
- )}
-
-
-
-
- {s.last_active_at ? new Date(s.last_active_at).toLocaleDateString() : 'Never'}
-
-
-
- {s.progress >= 1 ? (
-
- Completed
-
- ) : s.last_active_at && (Date.now() - new Date(s.last_active_at).getTime() > 7 * 24 * 60 * 60 * 1000) ? (
-
- Inactive
-
- ) : (
-
- Active
-
- )}
-
-
- ))}
-
-
+
+
+
Students
+
{filteredStudents.length}
+
+
+
Avg Score
+
+ {(averageScore * 100).toFixed(0)}%
+
+
-
-
-
+
+ {/* Student List */}
+
+
+
+
+ Student
+ Progress
+ Avg. Score
+ Last Active
+ Status
+
+
+
+ {filteredStudents.length === 0 ? (
+
+ No students found.
+
+ ) : filteredStudents.map((s) => (
+
+
+
+
+ {s.full_name.charAt(0)}
+
+
+
{s.full_name}
+
+ {s.email}
+
+
+
+
+
+
+
+ {(s.progress * 100).toFixed(0)}%
+
+
+
+
+
+ {s.average_score !== null ? (
+ = 0.8 ? 'text-green-400' : (s.average_score || 0) >= 0.6 ? 'text-yellow-400' : 'text-red-400'}`}>
+ {((s.average_score || 0) * 100).toFixed(0)}%
+
+ ) : (
+ No grades
+ )}
+
+
+
+
+ {s.last_active_at ? new Date(s.last_active_at).toLocaleDateString() : 'Never'}
+
+
+
+ {s.progress >= 1 ? (
+
+ Completed
+
+ ) : s.last_active_at && (Date.now() - new Date(s.last_active_at).getTime() > 7 * 24 * 60 * 60 * 1000) ? (
+
+ Inactive
+
+ ) : (
+
+ Active
+
+ )}
+
+
+ ))}
+
+
+
+
+ >
+
);
}
diff --git a/web/studio/src/app/courses/[id]/settings/TeamManagementSection.tsx b/web/studio/src/app/courses/[id]/settings/TeamManagementSection.tsx
index c5ff71b..b2abea6 100644
--- a/web/studio/src/app/courses/[id]/settings/TeamManagementSection.tsx
+++ b/web/studio/src/app/courses/[id]/settings/TeamManagementSection.tsx
@@ -79,8 +79,8 @@ export default function TeamManagementSection({ courseId }: TeamManagementSectio
-
Course Team
-
Manage instructors and assistants for this course
+
Equipo del Curso
+
Gestiona los instructores y asistentes de este curso
diff --git a/web/studio/src/app/courses/[id]/settings/page.tsx b/web/studio/src/app/courses/[id]/settings/page.tsx
index a7a45ee..ee265dd 100644
--- a/web/studio/src/app/courses/[id]/settings/page.tsx
+++ b/web/studio/src/app/courses/[id]/settings/page.tsx
@@ -3,7 +3,7 @@
import React, { useState, useEffect } from "react";
import { useParams, useRouter } from "next/navigation";
import { cmsApi, Course } from "@/lib/api";
-import { ArrowLeft, Save, Settings as SettingsIcon, BookOpen, Calendar, Clock, Download, Upload } from "lucide-react";
+import { Save, Settings as SettingsIcon, BookOpen, Calendar, Clock, Download, Upload } from "lucide-react";
const DEFAULT_CERTIFICATE_TEMPLATE = `
@@ -129,327 +129,305 @@ export default function CourseSettingsPage() {
);
- if (!course) return (
-
- Course not found.
-
- );
-
return (
-
-
- {/* Header */}
-
-
-
router.back()}
- className="p-2 hover:bg-white/10 rounded-full transition-colors"
- >
-
-
+
+
+ {saving ? "Guardando..." : "Guardar Cambios"}
+
+ }
+ >
+
+
+
+ {/* Passing Percentage Section */}
+
+
+
+
+
+
Configuración de Calificaciones
+
+
+
-
- Course Settings
-
-
Configure general course properties and certificates for {course?.title}
+
+ Passing Percentage
+
+
+
setPassingPercentage(parseInt(e.target.value))}
+ className="flex-1 h-2 bg-white/10 rounded-lg appearance-none cursor-pointer accent-blue-500"
+ />
+
+ {passingPercentage}%
+
+
+
+ Students must achieve at least this percentage to pass the course.
+
+
+
+ {/* Performance Tiers Preview */}
+
+
Performance Tiers Preview
+
+
+
+
Reprobado:
+
0% - {Math.max(0, passingPercentage - 1)}%
+
+
+
+
Rendimiento Bajo:
+
{passingPercentage}% - {passingPercentage + 9}%
+
+
+
+
Rendimiento Medio:
+
{passingPercentage + 10}% - {passingPercentage + 15}%
+
+
+
+
Buen Rendimiento:
+
{passingPercentage + 16}% - 90%
+
+
+
+
Excelente:
+
91% - 100%
+
+
-
-
- {saving ? "Saving..." : "Save Changes"}
-
-
+
-
-
-
-
- {/* Passing Percentage Section */}
-
-
-
-
-
-
Grading Configuration
-
-
-
-
-
- Passing Percentage
-
-
-
setPassingPercentage(parseInt(e.target.value))}
- className="flex-1 h-2 bg-white/10 rounded-lg appearance-none cursor-pointer accent-blue-500"
- />
-
- {passingPercentage}%
-
-
-
- Students must achieve at least this percentage to pass the course.
-
-
-
- {/* Performance Tiers Preview */}
-
-
Performance Tiers Preview
-
-
-
-
Reprobado:
-
0% - {Math.max(0, passingPercentage - 1)}%
-
-
-
-
Rendimiento Bajo:
-
{passingPercentage}% - {passingPercentage + 9}%
-
-
-
-
Rendimiento Medio:
-
{passingPercentage + 10}% - {passingPercentage + 15}%
-
-
-
-
Buen Rendimiento:
-
{passingPercentage + 16}% - 90%
-
-
-
-
Excelente:
-
91% - 100%
-
-
-
-
-
-
- {/* Course Pacing Section */}
-
-
-
-
-
-
Course Pacing & Schedule
-
-
-
-
-
Pacing Mode
-
-
setPacingMode('self_paced')}
- className={`flex-1 p-4 rounded-2xl border-2 transition-all text-left ${pacingMode === 'self_paced' ? 'border-blue-500 bg-blue-500/10' : 'border-white/5 bg-white/5 hover:border-white/10'}`}
- >
- Self-Paced
- Learners go at their own speed.
-
-
setPacingMode('instructor_led')}
- className={`flex-1 p-4 rounded-2xl border-2 transition-all text-left ${pacingMode === 'instructor_led' ? 'border-purple-500 bg-purple-500/10' : 'border-white/5 bg-white/5 hover:border-white/10'}`}
- >
- Instructor-Led
- Cohort-based with specific dates.
-
-
-
-
- {pacingMode === 'instructor_led' && (
-
-
Course Schedule
-
-
-
Start Date
-
-
- setStartDate(e.target.value)}
- className="w-full bg-black/30 border border-white/10 rounded-xl py-2 pl-10 pr-4 text-sm focus:outline-none focus:border-blue-500"
- />
-
-
-
-
End Date
-
-
- setEndDate(e.target.value)}
- className="w-full bg-black/30 border border-white/10 rounded-xl py-2 pl-10 pr-4 text-sm focus:outline-none focus:border-blue-500"
- />
-
-
-
-
- )}
-
-
-
- {/* Course Pricing Section */}
-
-
-
- $
-
-
Course Pricing
-
-
-
-
-
Price
-
-
setPrice(parseFloat(e.target.value))}
- className="w-full bg-black/30 border border-white/10 rounded-xl py-3 px-4 text-gray-900 dark:text-white focus:outline-none focus:border-blue-500 transition-colors"
- placeholder="0.00"
- />
-
- {currency}
-
-
-
Set to 0 for a free course.
-
-
-
- Currency
- setCurrency(e.target.value)}
- className="w-full bg-black/30 border border-white/10 rounded-xl py-3 px-4 text-gray-900 dark:text-white focus:outline-none focus:border-blue-500 transition-colors appearance-none"
- >
- USD - US Dollar
- CLP - Chilean Peso
- ARS - Argentine Peso
- BRL - Brazilian Real
- MXN - Mexican Peso
- COP - Colombian Peso
-
-
-
-
-
- {/* Certificate Template Section */}
-
-
-
-
-
-
Certificate Template
-
-
-
-
- Design the HTML certificate that students will receive upon passing the course.
- Available variables: {"{{student_name}}"}, {"{{course_title}}"}, {"{{date}}"}, {"{{score}}"}.
-
-
-
-
- HTML Template
-
-
-
-
-
-
- {/* Course Portability Section */}
-
-
-
-
-
-
Course Portability
-
-
-
-
-
Export Course
-
- Download the entire course structure, modules, and lessons as a JSON file.
- You can use this to backup or move content between organizations.
-
-
-
- {exporting ? "Exporting..." : "Download JSON"}
-
-
-
-
-
Import Course
-
- Upload a previously exported course JSON file. This will create a NEW course
- within the current organization based on that data.
-
-
-
-
-
- {importing ? "Importing..." : "Upload JSON"}
-
-
-
-
-
+ {/* Course Pacing Section */}
+
+
+
+
+
+
Ritmo y Calendario del Curso
-
+
+
+
+
Pacing Mode
+
+
setPacingMode('self_paced')}
+ className={`flex-1 p-4 rounded-2xl border-2 transition-all text-left ${pacingMode === 'self_paced' ? 'border-blue-500 bg-blue-500/10' : 'border-white/5 bg-white/5 hover:border-white/10'}`}
+ >
+ Self-Paced
+ Learners go at their own speed.
+
+
setPacingMode('instructor_led')}
+ className={`flex-1 p-4 rounded-2xl border-2 transition-all text-left ${pacingMode === 'instructor_led' ? 'border-purple-500 bg-purple-500/10' : 'border-white/5 bg-white/5 hover:border-white/10'}`}
+ >
+ Instructor-Led
+ Cohort-based with specific dates.
+
+
+
+
+ {pacingMode === 'instructor_led' && (
+
+
Course Schedule
+
+
+
Start Date
+
+
+ setStartDate(e.target.value)}
+ className="w-full bg-black/30 border border-white/10 rounded-xl py-2 pl-10 pr-4 text-sm focus:outline-none focus:border-blue-500"
+ />
+
+
+
+
End Date
+
+
+ setEndDate(e.target.value)}
+ className="w-full bg-black/30 border border-white/10 rounded-xl py-2 pl-10 pr-4 text-sm focus:outline-none focus:border-blue-500"
+ />
+
+
+
+
+ )}
+
+
+
+ {/* Course Pricing Section */}
+
+
+
+ $
+
+
Precio del Curso
+
+
+
+
+
Price
+
+
setPrice(parseFloat(e.target.value))}
+ className="w-full bg-black/30 border border-white/10 rounded-xl py-3 px-4 text-gray-900 dark:text-white focus:outline-none focus:border-blue-500 transition-colors"
+ placeholder="0.00"
+ />
+
+ {currency}
+
+
+
Set to 0 for a free course.
+
+
+
+ Currency
+ setCurrency(e.target.value)}
+ className="w-full bg-black/30 border border-white/10 rounded-xl py-3 px-4 text-gray-900 dark:text-white focus:outline-none focus:border-blue-500 transition-colors appearance-none"
+ >
+ USD - US Dollar
+ CLP - Chilean Peso
+ ARS - Argentine Peso
+ BRL - Brazilian Real
+ MXN - Mexican Peso
+ COP - Colombian Peso
+
+
+
+
+
+ {/* Certificate Template Section */}
+
+
+
+
+
+
Plantilla de Certificado
+
+
+
+
+ Design the HTML certificate that students will receive upon passing the course.
+ Available variables: {"{{student_name}}"}, {"{{course_title}}"}, {"{{date}}"}, {"{{score}}"}.
+
+
+
+
+ HTML Template
+
+
+
+
+
+
+ {/* Course Portability Section */}
+
+
+
+
+
+
Portabilidad del Curso
+
+
+
+
+
Export Course
+
+ Download the entire course structure, modules, and lessons as a JSON file.
+ You can use this to backup or move content between organizations.
+
+
+
+ {exporting ? "Exporting..." : "Download JSON"}
+
+
+
+
+
Import Course
+
+ Upload a previously exported course JSON file. This will create a NEW course
+ within the current organization based on that data.
+
+
+
+
+
+ {importing ? "Importing..." : "Upload JSON"}
+
+
+
+
+
-
+
);
}
diff --git a/web/studio/src/app/courses/[id]/students/page.tsx b/web/studio/src/app/courses/[id]/students/page.tsx
index 4425379..56fe643 100644
--- a/web/studio/src/app/courses/[id]/students/page.tsx
+++ b/web/studio/src/app/courses/[id]/students/page.tsx
@@ -116,185 +116,180 @@ export default function CourseStudentsPage() {
}
return (
-
-
-
-
-
router.back()} className="p-2 hover:bg-white/10 rounded-full transition-colors">
-
-
-
-
- Students & Groups
-
-
Manage enrollments and segment your audience
-
-
+ <>
+
setIsEnrollModalOpen(true)}
- className="btn-premium px-6 py-3 flex items-center gap-2"
+ className="flex items-center gap-2 px-6 py-2.5 bg-blue-600 hover:bg-blue-500 text-white rounded-xl font-bold text-sm shadow-md shadow-blue-600/20 transition-all active:scale-95"
>
- Enroll Students
+ Inscribir Estudiantes
-
-
-
-
- {/* Search and Filters */}
-
-
-
- setSearchTerm(e.target.value)}
- className="w-full bg-black/20 border border-white/10 rounded-xl py-2 pl-10 pr-4 text-sm focus:outline-none focus:border-blue-500/50 transition-all font-medium"
- />
-
-
-
- setSelectedCohortId(e.target.value)}
- >
- All Cohorts
- {cohorts.map(c => {c.name} )}
-
-
+ }
+ >
+
+
+
+ Listado de Estudiantes
+
+ {/* Search and Filters */}
+
+
+
+ setSearchTerm(e.target.value)}
+ className="w-full bg-black/20 border border-white/10 rounded-xl py-2 pl-10 pr-4 text-sm focus:outline-none focus:border-blue-500/50 transition-all font-medium"
+ />
-
- {/* Student List */}
-
-
-
-
- Student
- Enrollment Date
- Actions
-
-
-
- {filteredStudents.length === 0 ? (
-
- No students found.
-
- ) : filteredStudents.map(student => (
-
-
-
-
- {student.full_name.charAt(0)}
-
-
-
{student.full_name}
-
{student.email}
-
-
-
-
- {/* In a real app we'd have the enrollment date here */}
- Available upon request
-
-
-
-
-
-
-
-
-
Move to Cohort
- {cohorts.map(c => (
-
handleCohortAssignment(student.user_id, c.id)}
- className="w-full text-left px-3 py-2 text-xs hover:bg-white/5 rounded-lg transition-colors"
- >
- {c.name}
-
- ))}
-
{ if (confirm("Unenroll student?")) handleCohortAssignment(student.user_id, "", true) }}
- >
- Unenroll
-
-
-
-
-
-
- ))}
-
-
+
+
+ setSelectedCohortId(e.target.value)}
+ >
+ All Cohorts
+ {cohorts.map(c => {c.name} )}
+
-
-
- {/* Enroll Modal */}
- {isEnrollModalOpen && (
-
-
-
-
-
- Enroll Organization Students
-
- setIsEnrollModalOpen(false)} className="p-2 hover:bg-white/10 rounded-full transition-colors">
-
-
-
-
-
-
- setEnrollSearch(e.target.value)}
- className="w-full bg-black/20 border border-white/10 rounded-xl py-3 pl-10 pr-4 text-sm focus:outline-none"
- />
-
-
-
- {orgUsersLoading ? (
-
- ) : allOrgUsers.filter(u => u.full_name.toLowerCase().includes(enrollSearch.toLowerCase())).length === 0 ? (
-
No remaining students to enroll.
- ) : (
- allOrgUsers.filter(u => u.full_name.toLowerCase().includes(enrollSearch.toLowerCase())).map(user => (
-
-
-
+ {/* Student List */}
+
+
+
+
+ Student
+ Enrollment Date
+ Actions
+
+
+
+ {filteredStudents.length === 0 ? (
+
+ No students found.
+
+ ) : filteredStudents.map(student => (
+
+
+
+
+ {student.full_name.charAt(0)}
+
-
{user.full_name}
-
{user.email}
+
{student.full_name}
+
{student.email}
- handleEnroll([user.email])}
- className="px-4 py-1.5 bg-blue-600 hover:bg-blue-500 text-gray-900 dark:text-white text-xs font-bold rounded-lg transition-all"
- >
- Enroll Now
-
-
- ))
- )}
-
+
+
+ {/* In a real app we'd have the enrollment date here */}
+ Available upon request
+
+
+
+
+
+
+
+
+
Move to Cohort
+ {cohorts.map(c => (
+
handleCohortAssignment(student.user_id, c.id)}
+ className="w-full text-left px-3 py-2 text-xs hover:bg-white/5 rounded-lg transition-colors"
+ >
+ {c.name}
+
+ ))}
+
{ if (confirm("Unenroll student?")) handleCohortAssignment(student.user_id, "", true) }}
+ >
+ Unenroll
+
+
+
+
+
+
+ ))}
+
+
+
+
+
-
-
-
- You can also enroll external students by going to the
Gradebook and using the Bulk Enroll feature.
+ {/* Enroll Modal */}
+ {
+ isEnrollModalOpen && (
+
+
+
+
+
+ Enroll Organization Students
+
+ setIsEnrollModalOpen(false)} className="p-2 hover:bg-white/10 rounded-full transition-colors">
+
+
+
+
+
+
+ setEnrollSearch(e.target.value)}
+ className="w-full bg-black/20 border border-white/10 rounded-xl py-3 pl-10 pr-4 text-sm focus:outline-none"
+ />
+
+
+
+ {orgUsersLoading ? (
+
+ ) : allOrgUsers.filter(u => u.full_name.toLowerCase().includes(enrollSearch.toLowerCase())).length === 0 ? (
+
No remaining students to enroll.
+ ) : (
+ allOrgUsers.filter(u => u.full_name.toLowerCase().includes(enrollSearch.toLowerCase())).map(user => (
+
+
+
+
+
{user.full_name}
+
{user.email}
+
+
+
handleEnroll([user.email])}
+ className="px-4 py-1.5 bg-blue-600 hover:bg-blue-500 text-gray-900 dark:text-white text-xs font-bold rounded-lg transition-all"
+ >
+ Enroll Now
+
+
+ ))
+ )}
+
+
+
+
+
+ You can also enroll external students by going to the Gradebook and using the Bulk Enroll feature.
+
-
- )}
-
+ )}
+ >
);
}
diff --git a/web/studio/src/app/courses/[id]/team/page.tsx b/web/studio/src/app/courses/[id]/team/page.tsx
index e97490b..21cca40 100644
--- a/web/studio/src/app/courses/[id]/team/page.tsx
+++ b/web/studio/src/app/courses/[id]/team/page.tsx
@@ -96,162 +96,168 @@ export default function CourseTeamPage() {
};
return (
-
-
-
-
Course Team
-
Manage multiple instructors and assistants for this course
-
-
setIsAddModalOpen(true)}
- className="btn-premium px-6 py-2.5 flex items-center gap-2 group"
- >
- Add Member
-
-
-
-
-
- {loading ? (
-
Loading team members...
- ) : instructors.length === 0 ? (
-
-
-
No instructors assigned yet.
-
- ) : (
- instructors.map((inst) => (
-
-
-
- {inst.full_name?.charAt(0) || inst.email?.charAt(0)}
-
-
-
- {inst.full_name}
- {inst.role === 'primary' && (
-
- Owner
+ <>
+ setIsAddModalOpen(true)}
+ className="flex items-center gap-2 px-6 py-2.5 bg-blue-600 hover:bg-blue-500 text-white rounded-xl font-bold text-sm shadow-md shadow-blue-600/20 transition-all active:scale-95 group"
+ >
+ Agregar Miembro
+
+ }
+ >
+
+
+
+ Miembros del Equipo
+
+
+ {loading ? (
+
Loading team members...
+ ) : instructors.length === 0 ? (
+
+
+
No instructors assigned yet.
+
+ ) : (
+ instructors.map((inst) => (
+
+
+
+ {inst.full_name?.charAt(0) || inst.email?.charAt(0)}
+
+
+
+ {inst.full_name}
+ {inst.role === 'primary' && (
+
+ Owner
+
+ )}
+
+
+
+ {inst.email}
- )}
-
-
-
- {inst.email}
-
-
-
- {getRoleIcon(inst.role)} {getRoleLabel(inst.role)}
-
+
+
+ {getRoleIcon(inst.role)} {getRoleLabel(inst.role)}
+
+
+
+ {inst.role !== 'primary' && (
+ handleRemoveMember(inst.user_id)}
+ className="p-3 rounded-xl hover:bg-red-500/10 text-gray-500 hover:text-red-400 transition-all active:scale-95"
+ title="Remove member"
+ >
+
+
+ )}
+
-
- {inst.role !== 'primary' && (
- handleRemoveMember(inst.user_id)}
- className="p-3 rounded-xl hover:bg-red-500/10 text-gray-500 hover:text-red-400 transition-all active:scale-95"
- title="Remove member"
- >
-
-
- )}
-
-
- ))
- )}
+ ))
+ )}
+
{/* Add Member Modal */}
- {isAddModalOpen && (
-
-
-
-
-
Add Team Member
-
Search for a user by name or email
-
-
setIsAddModalOpen(false)} className="p-2 hover:bg-white/5 rounded-full transition-colors text-gray-500">
-
-
-
-
-
-
-
-
-
- Assign Role
-
-
setSelectedRole('instructor')}
- className={`flex items-center gap-3 p-4 rounded-2xl border transition-all text-left ${selectedRole === 'instructor' ? 'bg-blue-600/10 border-blue-500/50 text-blue-400' : 'bg-white/5 border-white/10 text-gray-500 hover:border-white/20'}`}
- >
-
-
-
Instructor
-
Full access
-
-
-
setSelectedRole('assistant')}
- className={`flex items-center gap-3 p-4 rounded-2xl border transition-all text-left ${selectedRole === 'assistant' ? 'bg-blue-600/10 border-blue-500/50 text-blue-400' : 'bg-white/5 border-white/10 text-gray-500 hover:border-white/20'}`}
- >
-
-
-
Assistant
-
Limited access
-
-
-
-
+ {
+ isAddModalOpen && (
+
+
+
+
+
Add Team Member
+
Search for a user by name or email
+
+
setIsAddModalOpen(false)} className="p-2 hover:bg-white/5 rounded-full transition-colors text-gray-500">
+
+
-
- {users.length > 0 ? (
- users.map(u => (
-
handleAddMember(u)}
- className="w-full flex items-center justify-between p-4 rounded-2xl bg-white/5 border border-white/10 hover:border-blue-500/30 hover:bg-white/10 transition-all group"
- >
-
-
- {u.full_name.charAt(0)}
-
+
+
+
+
+
+ Assign Role
+
+
setSelectedRole('instructor')}
+ className={`flex items-center gap-3 p-4 rounded-2xl border transition-all text-left ${selectedRole === 'instructor' ? 'bg-blue-600/10 border-blue-500/50 text-blue-400' : 'bg-white/5 border-white/10 text-gray-500 hover:border-white/20'}`}
+ >
+
-
{u.full_name}
-
{u.email}
+
Instructor
+
Full access
-
-
-
- ))
- ) : searchQuery && !searching ? (
- No users found matching your search.
- ) : null}
+
+ setSelectedRole('assistant')}
+ className={`flex items-center gap-3 p-4 rounded-2xl border transition-all text-left ${selectedRole === 'assistant' ? 'bg-blue-600/10 border-blue-500/50 text-blue-400' : 'bg-white/5 border-white/10 text-gray-500 hover:border-white/20'}`}
+ >
+
+
+
Assistant
+
Limited access
+
+
+
+
+
+
+
+ {users.length > 0 ? (
+ users.map(u => (
+
handleAddMember(u)}
+ className="w-full flex items-center justify-between p-4 rounded-2xl bg-white/5 border border-white/10 hover:border-blue-500/30 hover:bg-white/10 transition-all group"
+ >
+
+
+ {u.full_name.charAt(0)}
+
+
+
{u.full_name}
+
{u.email}
+
+
+
+
+ ))
+ ) : searchQuery && !searching ? (
+
No users found matching your search.
+ ) : null}
+
-
- )}
-
+ )}
+ >
);
}
diff --git a/web/studio/src/app/globals.css b/web/studio/src/app/globals.css
index 6d11bf6..7a3404f 100644
--- a/web/studio/src/app/globals.css
+++ b/web/studio/src/app/globals.css
@@ -65,6 +65,30 @@ body {
@apply scale-95;
}
+/* Premium Typography System */
+.heading-premium {
+ @apply text-3xl md:text-4xl font-black tracking-tight leading-tight;
+ background: linear-gradient(to bottom right, #111827, #374151);
+ background-clip: text;
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+}
+
+.dark .heading-premium {
+ background: linear-gradient(to bottom right, #ffffff, #9ca3af);
+ background-clip: text;
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+}
+
+.section-title {
+ @apply text-xl md:text-2xl font-black tracking-tight flex items-center gap-3 text-gray-900 dark:text-white;
+}
+
+.text-description-premium {
+ @apply text-sm md:text-base text-slate-500 dark:text-gray-400 font-medium;
+}
+
/* Custom Scrollbar */
::-webkit-scrollbar {
width: 6px;
diff --git a/web/studio/src/components/CourseEditorLayout.tsx b/web/studio/src/components/CourseEditorLayout.tsx
index c149ea4..05a8dc8 100644
--- a/web/studio/src/components/CourseEditorLayout.tsx
+++ b/web/studio/src/components/CourseEditorLayout.tsx
@@ -141,15 +141,15 @@ export default function CourseEditorLayout({
-
+
{displayTitle}
-
+
{displayDescription}
{course?.pacing_mode && (
-
{course.pacing_mode.replace("_", " ").toUpperCase()}
@@ -179,8 +179,8 @@ export default function CourseEditorLayout({
@@ -206,8 +206,8 @@ export default function CourseEditorLayout({
href={tab.href}
aria-current={isActive ? "page" : undefined}
className={`flex items-center gap-1.5 px-4 py-2 my-1 text-xs font-bold uppercase tracking-wider rounded-lg transition-all whitespace-nowrap ${isActive
- ? "bg-blue-600 text-white shadow-sm shadow-blue-500/30"
- : "text-slate-500 dark:text-gray-400 hover:text-slate-800 dark:hover:text-gray-100 hover:bg-black/5 dark:hover:bg-white/5"
+ ? "bg-blue-600 text-white shadow-sm shadow-blue-500/30"
+ : "text-slate-500 dark:text-gray-400 hover:text-slate-800 dark:hover:text-gray-100 hover:bg-black/5 dark:hover:bg-white/5"
}`}
>