feat: Add i18n support, new content block types, course export, and lesson interaction tracking.

This commit is contained in:
2026-01-17 02:19:39 -03:00
parent b166387a48
commit 05faa20993
50 changed files with 3368 additions and 388 deletions
+20 -17
View File
@@ -1,7 +1,8 @@
"use client";
import { useState, useEffect, useRef } from "react";
import { useState, useEffect, useRef, useCallback } from "react";
import { useAuth } from "@/context/AuthContext";
import { useTranslation } from "@/context/I18nContext";
import { lmsApi, CMS_API_URL } from "@/lib/api";
import {
Save,
@@ -19,6 +20,7 @@ import {
} from "lucide-react";
export default function ProfilePage() {
const { t, setLanguage: setContextLanguage } = useTranslation();
const { user, logout } = useAuth();
const [fullName, setFullName] = useState(user?.full_name || "");
const [email, setEmail] = useState(user?.email || "");
@@ -31,6 +33,16 @@ export default function ProfilePage() {
const [gamification, setGamification] = useState<any>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
const fetchGamification = useCallback(async () => {
if (!user) return;
try {
const data = await lmsApi.getGamification(user.id);
setGamification(data);
} catch (err) {
console.error("Failed to fetch gamification:", err);
}
}, [user]);
useEffect(() => {
if (user) {
setFullName(user.full_name);
@@ -40,17 +52,7 @@ export default function ProfilePage() {
setAvatarUrl(user.avatar_url || "");
fetchGamification();
}
}, [user]);
const fetchGamification = async () => {
if (!user) return;
try {
const data = await lmsApi.getGamification(user.id);
setGamification(data);
} catch (err) {
console.error("Failed to fetch gamification:", err);
}
};
}, [user, fetchGamification]);
const getImageUrl = (path?: string) => {
if (!path) return '';
@@ -95,7 +97,8 @@ export default function ProfilePage() {
avatar_url: avatarUrl
});
setMessage({ type: 'success', text: '¡Perfil actualizado con éxito!' });
setContextLanguage(language);
setMessage({ type: 'success', text: t('common.save') + '!' });
} catch (err) {
console.error(err);
setMessage({ type: 'error', text: 'Error al actualizar el perfil.' });
@@ -271,8 +274,8 @@ export default function ProfilePage() {
type="button"
onClick={() => setLanguage(lang.code)}
className={`flex items-center justify-center gap-3 p-4 rounded-2xl border transition-all ${language === lang.code
? 'bg-blue-600 border-blue-500 text-white shadow-lg shadow-blue-500/20'
: 'bg-black/20 border-white/5 text-gray-400 hover:border-white/20 hover:text-white'
? 'bg-blue-600 border-blue-500 text-white shadow-lg shadow-blue-500/20'
: 'bg-black/20 border-white/5 text-gray-400 hover:border-white/20 hover:text-white'
}`}
>
<span className="text-xl">{lang.flag}</span>
@@ -284,8 +287,8 @@ export default function ProfilePage() {
{message && (
<div className={`p-5 rounded-2xl text-sm font-bold animate-in fade-in slide-in-from-top-4 ${message.type === 'success'
? 'bg-green-500/10 text-green-400 border border-green-500/20'
: 'bg-red-500/10 text-red-400 border border-red-500/20'
? 'bg-green-500/10 text-green-400 border border-green-500/20'
: 'bg-red-500/10 text-red-400 border border-red-500/20'
}`}>
{message.text}
</div>