feat: Enhance UI with improved dark mode styling and color palette adjustments across course pages.

This commit is contained in:
2026-03-03 11:11:06 -03:00
parent 1987acd734
commit 9123337200
15 changed files with 374 additions and 361 deletions
@@ -56,20 +56,20 @@ export default function DropoutRiskDashboard({ courseId }: DropoutRiskDashboardP
<div className="space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-700">
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div>
<h2 className="text-2xl font-black flex items-center gap-3">
<AlertCircle className="text-red-500" />
<h2 className="text-2xl font-black flex items-center gap-3 text-slate-900 dark:text-white">
<AlertCircle className="text-red-600 dark:text-red-500" />
Dropout Risk Analysis
</h2>
<p className="text-sm text-gray-400 mt-1">AI-powered detection based on grades, activity, and engagement.</p>
<p className="text-sm text-slate-500 dark:text-gray-400 mt-1 font-medium">AI-powered detection based on grades, activity, and engagement.</p>
</div>
<div className="relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-500 w-4 h-4" />
<Search className="absolute left-3 top-1/2 -translate-y-1/2 text-slate-400 dark:text-gray-500 w-4 h-4" />
<input
type="text"
placeholder="Search student..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="bg-white/5 border border-white/10 rounded-xl pl-10 pr-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500/50 w-full md:w-64"
className="bg-slate-100 dark:bg-white/5 border border-slate-200 dark:border-white/10 rounded-xl pl-10 pr-4 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500/50 w-full md:w-64 text-slate-900 dark:text-white font-bold"
/>
</div>
</div>
@@ -77,17 +77,17 @@ export default function DropoutRiskDashboard({ courseId }: DropoutRiskDashboardP
{filteredRisks.length > 0 ? (
<div className="grid grid-cols-1 gap-4">
{filteredRisks.map((risk) => (
<div key={risk.id} className="bg-white/5 border border-white/10 rounded-2xl p-6 hover:bg-white/[0.07] transition-all group">
<div key={risk.id} className="bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 rounded-2xl p-6 hover:bg-slate-50 dark:hover:bg-white/[0.07] transition-all group shadow-sm">
<div className="flex flex-col md:flex-row md:items-center justify-between gap-6">
<div className="flex items-center gap-4">
<div className="w-12 h-12 rounded-full bg-blue-500/10 flex items-center justify-center text-blue-400 text-xl font-bold">
{risk.user_full_name?.[0] || <User />}
</div>
<div>
<h3 className="font-bold text-lg">{risk.user_full_name || "Unknown Student"}</h3>
<div className="flex items-center gap-3 text-xs text-gray-500 mt-1">
<span className="flex items-center gap-1"><Mail size={12} /> {risk.user_email || "N/A"}</span>
<span className="flex items-center gap-1"><Calendar size={12} /> Last active: {new Date(risk.last_calculated_at).toLocaleDateString()}</span>
<h3 className="font-bold text-lg text-slate-900 dark:text-white">{risk.user_full_name || "Unknown Student"}</h3>
<div className="flex items-center gap-3 text-xs text-slate-500 dark:text-gray-500 mt-1 font-medium">
<span className="flex items-center gap-1"><Mail size={12} className="text-slate-400" /> {risk.user_email || "N/A"}</span>
<span className="flex items-center gap-1"><Calendar size={12} className="text-slate-400" /> Last active: {new Date(risk.last_calculated_at).toLocaleDateString()}</span>
</div>
</div>
</div>
@@ -98,8 +98,8 @@ export default function DropoutRiskDashboard({ courseId }: DropoutRiskDashboardP
</div>
<div className="flex flex-col items-end min-w-[100px]">
<div className="text-sm font-bold text-gray-400">Score: {Math.round(risk.score * 100)}%</div>
<div className="w-24 h-1 bg-white/5 rounded-full mt-1 overflow-hidden">
<div className="text-[10px] font-black uppercase tracking-widest text-slate-400 dark:text-gray-400">Score: {Math.round(risk.score * 100)}%</div>
<div className="w-24 h-1.5 bg-slate-100 dark:bg-white/5 rounded-full mt-1.5 overflow-hidden">
<div
className={`h-full transition-all duration-1000 ${risk.score > 0.8 ? 'bg-red-500' : risk.score > 0.5 ? 'bg-orange-500' : 'bg-green-500'}`}
style={{ width: `${risk.score * 100}%` }}
@@ -114,10 +114,10 @@ export default function DropoutRiskDashboard({ courseId }: DropoutRiskDashboardP
</div>
{risk.reasons && risk.reasons.length > 0 && (
<div className="mt-6 pt-6 border-t border-white/5 flex flex-wrap gap-3">
<div className="mt-6 pt-6 border-t border-slate-100 dark:border-white/5 flex flex-wrap gap-2">
{risk.reasons.map((reason, _idx) => (
<div key={_idx} className="bg-white/5 rounded-lg px-3 py-1.5 text-[10px] font-bold text-gray-400 flex items-center gap-2">
<Activity size={10} className="text-blue-500" />
<div key={_idx} className="bg-slate-50 dark:bg-white/5 border border-slate-100 dark:border-white/5 rounded-lg px-3 py-1.5 text-[10px] font-black text-slate-500 dark:text-gray-400 flex items-center gap-2 uppercase tracking-wide">
<Activity size={10} className="text-blue-600 dark:text-blue-500" />
{reason.description}
</div>
))}
@@ -127,10 +127,10 @@ export default function DropoutRiskDashboard({ courseId }: DropoutRiskDashboardP
))}
</div>
) : (
<div className="bg-white/5 border border-white/10 rounded-3xl p-20 text-center">
<User size={48} className="text-gray-600 mx-auto mb-4" />
<h3 className="text-xl font-bold text-gray-400">No students at risk</h3>
<p className="text-sm text-gray-500 mt-2">Everyone seems to be doing great in this course!</p>
<div className="bg-slate-50 dark:bg-white/5 border border-slate-200 dark:border-white/10 rounded-3xl p-20 text-center shadow-sm">
<User size={48} className="text-slate-300 dark:text-gray-600 mx-auto mb-4" />
<h3 className="text-xl font-black text-slate-800 dark:text-gray-300 uppercase tracking-widest">No students at risk</h3>
<p className="text-sm text-slate-500 dark:text-gray-500 mt-2 font-medium">Everyone seems to be doing great in this course!</p>
</div>
)}
</div>
@@ -135,7 +135,7 @@ export default function CourseEditorLayout({
<div className="flex items-center gap-3">
<button
onClick={() => router.push("/")}
className="p-1.5 hover:bg-black/5 dark:hover:bg-white/10 rounded-full transition-colors text-slate-500 hover:text-slate-800 dark:hover:text-gray-200"
className="p-1.5 hover:bg-slate-100 dark:hover:bg-white/10 rounded-full transition-colors text-slate-500 hover:text-slate-800 dark:hover:text-gray-200"
aria-label="Volver al inicio"
>
<ChevronLeft className="w-5 h-5" />
@@ -166,10 +166,10 @@ export default function CourseEditorLayout({
{/* ── TAB NAVIGATION ── */}
<nav
className="bg-white dark:bg-black/20 border border-black/8 dark:border-white/8 rounded-xl overflow-hidden mb-6"
className="bg-white dark:bg-black/20 border border-slate-200 dark:border-white/10 rounded-xl overflow-hidden mb-6 shadow-sm"
aria-label="Grupos del editor de cursos"
>
<ul className="flex border-b border-black/8 dark:border-white/8">
<ul className="flex border-b border-slate-200 dark:border-white/10">
{groups.map((group) => {
const Icon = group.icon;
const isGroupActive = group.key === activeGroup?.key;
@@ -207,7 +207,7 @@ export default function CourseEditorLayout({
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"
: "text-slate-500 dark:text-gray-400 hover:text-slate-800 dark:hover:text-gray-100 hover:bg-slate-200/50 dark:hover:bg-white/5"
}`}
>
<Icon className="w-3.5 h-3.5 flex-shrink-0" aria-hidden="true" />
@@ -72,11 +72,11 @@ export default function LiveSessions({ courseId }: LiveSessionsProps) {
<div className="space-y-6">
<div className="flex justify-between items-center">
<div>
<h2 className="text-xl font-bold flex items-center gap-2">
<Video className="text-blue-500" />
<h2 className="text-xl font-black flex items-center gap-2 text-slate-900 dark:text-white">
<Video className="text-blue-600 dark:text-blue-500" />
Virtual Classrooms
</h2>
<p className="text-sm text-gray-500">Schedule and manage your live Jitsi sessions.</p>
<p className="text-sm text-slate-500 dark:text-gray-400 font-medium">Schedule and manage your live Jitsi sessions.</p>
</div>
<button
onClick={() => setShowForm(true)}
@@ -87,34 +87,34 @@ export default function LiveSessions({ courseId }: LiveSessionsProps) {
</div>
{showForm && (
<div className="bg-white/5 border border-white/10 rounded-2xl p-6 animate-in slide-in-from-top-4">
<div className="bg-slate-50 dark:bg-white/5 border border-slate-200 dark:border-white/10 rounded-2xl p-6 animate-in slide-in-from-top-4 shadow-sm">
<form onSubmit={handleCreate} className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-1">
<label className="text-xs font-bold text-gray-400">SESSION TITLE</label>
<label className="text-[10px] font-black text-slate-400 dark:text-gray-500 uppercase tracking-widest">SESSION TITLE</label>
<input
required
className="w-full bg-black/20 border border-white/10 rounded-lg px-4 py-2 focus:border-blue-500 outline-none"
className="w-full bg-white dark:bg-black/20 border border-slate-200 dark:border-white/10 rounded-lg px-4 py-2.5 focus:border-blue-500 outline-none text-slate-900 dark:text-white font-bold"
placeholder="Weekly Sync Up"
value={formData.title}
onChange={e => setFormData({ ...formData, title: e.target.value })}
/>
</div>
<div className="space-y-1">
<label className="text-xs font-bold text-gray-400">START DATE & TIME</label>
<label className="text-[10px] font-black text-slate-400 dark:text-gray-500 uppercase tracking-widest">START DATE & TIME</label>
<input
required
type="datetime-local"
className="w-full bg-black/20 border border-white/10 rounded-lg px-4 py-2 focus:border-blue-500 outline-none"
className="w-full bg-white dark:bg-black/20 border border-slate-200 dark:border-white/10 rounded-lg px-4 py-2.5 focus:border-blue-500 outline-none text-slate-900 dark:text-white font-bold"
value={formData.start_at}
onChange={e => setFormData({ ...formData, start_at: e.target.value })}
/>
</div>
</div>
<div className="space-y-1">
<label className="text-xs font-bold text-gray-400">DESCRIPTION (OPTIONAL)</label>
<label className="text-[10px] font-black text-slate-400 dark:text-gray-500 uppercase tracking-widest">DESCRIPTION (OPTIONAL)</label>
<textarea
className="w-full bg-black/20 border border-white/10 rounded-lg px-4 py-2 focus:border-blue-500 outline-none h-20"
className="w-full bg-white dark:bg-black/20 border border-slate-200 dark:border-white/10 rounded-lg px-4 py-2 focus:border-blue-500 outline-none h-24 text-slate-900 dark:text-white font-medium resize-none shadow-inner"
value={formData.description}
onChange={e => setFormData({ ...formData, description: e.target.value })}
/>
@@ -129,18 +129,18 @@ export default function LiveSessions({ courseId }: LiveSessionsProps) {
<div className="grid grid-cols-1 gap-4">
{meetings.map(m => (
<div key={m.id} className="bg-white/5 border border-white/10 rounded-2xl p-5 hover:bg-white/[0.08] transition-all group">
<div key={m.id} className="bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 rounded-2xl p-5 hover:bg-slate-50 dark:hover:bg-white/[0.08] transition-all group shadow-sm">
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div className="flex items-start gap-4">
<div className="w-12 h-12 rounded-xl bg-blue-500/10 flex items-center justify-center text-blue-400">
<div className="w-12 h-12 rounded-xl bg-blue-600/10 flex items-center justify-center text-blue-600 dark:text-blue-400">
<Video size={24} />
</div>
<div>
<h3 className="font-bold text-lg">{m.title}</h3>
<div className="flex flex-wrap items-center gap-4 text-xs text-gray-400 mt-1">
<span className="flex items-center gap-1"><Calendar size={14} /> {new Date(m.start_at).toLocaleString()}</span>
<span className="flex items-center gap-1"><Clock size={14} /> {m.duration_minutes} min</span>
<span className="capitalize px-2 py-0.5 bg-white/5 rounded-full border border-white/10">{m.provider}</span>
<h3 className="font-bold text-lg text-slate-900 dark:text-white">{m.title}</h3>
<div className="flex flex-wrap items-center gap-4 text-xs text-slate-500 dark:text-gray-400 mt-1 font-medium">
<span className="flex items-center gap-1.5"><Calendar size={14} className="text-slate-400" /> {new Date(m.start_at).toLocaleString()}</span>
<span className="flex items-center gap-1.5"><Clock size={14} className="text-slate-400" /> {m.duration_minutes} min</span>
<span className="capitalize px-2 py-0.5 bg-slate-100 dark:bg-white/5 rounded-full border border-slate-200 dark:border-white/10 text-[10px] font-black uppercase tracking-widest">{m.provider}</span>
</div>
</div>
</div>
@@ -149,7 +149,7 @@ export default function LiveSessions({ courseId }: LiveSessionsProps) {
href={m.join_url}
target="_blank"
rel="noreferrer"
className="flex items-center gap-2 bg-white/10 hover:bg-white/20 px-4 py-2 rounded-xl text-sm font-bold transition-all"
className="flex items-center gap-2 bg-slate-100 dark:bg-white/10 hover:bg-blue-600 dark:hover:bg-white/20 hover:text-white px-4 py-2 rounded-xl text-sm font-bold transition-all shadow-sm text-slate-700 dark:text-white"
>
Join Room <ExternalLink size={14} />
</a>
@@ -165,9 +165,9 @@ export default function LiveSessions({ courseId }: LiveSessionsProps) {
))}
{meetings.length === 0 && !loading && (
<div className="p-12 text-center border-2 border-dashed border-white/5 rounded-3xl">
<AlertCircle className="mx-auto text-gray-600 mb-2" />
<p className="text-gray-500">No sessions scheduled for this course.</p>
<div className="p-12 text-center border-2 border-dashed border-slate-200 dark:border-white/5 rounded-3xl bg-slate-50/50 dark:bg-transparent">
<AlertCircle className="mx-auto text-slate-300 dark:text-gray-600 mb-2" size={32} />
<p className="text-slate-500 dark:text-gray-500 font-medium">No sessions scheduled for this course.</p>
</div>
)}
</div>