mirror of
https://github.com/lucasrcsantana/story-generator.git
synced 2025-12-17 05:47:52 +00:00
Compare commits
No commits in common. "69dbb5fa48c4008933e89fd0b0afb7dad11c1d67" and "abe4ce86d48193192d543fee881dabccebf863fa" have entirely different histories.
69dbb5fa48
...
abe4ce86d4
20
CHANGELOG.md
20
CHANGELOG.md
@ -181,23 +181,3 @@ e este projeto adere ao [Semantic Versioning](https://semver.org/lang/pt-BR/).
|
|||||||
- Implementada validação de idioma antes da geração
|
- Implementada validação de idioma antes da geração
|
||||||
- Atualizado payload da Edge Function para incluir `language_type`
|
- Atualizado payload da Edge Function para incluir `language_type`
|
||||||
- Melhorada tipagem para suporte a múltiplos idiomas
|
- Melhorada tipagem para suporte a múltiplos idiomas
|
||||||
|
|
||||||
## [1.4.0] - 2024-01-31
|
|
||||||
|
|
||||||
### Adicionado
|
|
||||||
- Integração completa com a tabela `languages` do banco de dados
|
|
||||||
- Suporte para ícones de bandeira nos seletores de idioma
|
|
||||||
- Instruções específicas por idioma na interface
|
|
||||||
- Hook `useLanguages` para gerenciamento centralizado de idiomas
|
|
||||||
|
|
||||||
### Modificado
|
|
||||||
- Removido `LANGUAGE_OPTIONS` hard coded do `StoryGenerator`
|
|
||||||
- Atualizado `CreateStoryPage` para usar `DEFAULT_LANGUAGE` do type
|
|
||||||
- Melhorada a validação de idiomas usando dados do banco
|
|
||||||
- Aprimorada a UX do seletor de idiomas com ícones e instruções
|
|
||||||
|
|
||||||
### Técnico
|
|
||||||
- Refatorado o sistema de idiomas para usar dados dinâmicos do banco
|
|
||||||
- Adicionada tipagem forte para dados de idioma
|
|
||||||
- Implementada validação robusta de códigos de idioma
|
|
||||||
- Melhorada a estrutura de componentes para suportar dados dinâmicos
|
|
||||||
|
|||||||
@ -40,21 +40,6 @@ export function StudentDashboardLayout() {
|
|||||||
<nav className="p-4 space-y-1">
|
<nav className="p-4 space-y-1">
|
||||||
<NavLink
|
<NavLink
|
||||||
to="/aluno"
|
to="/aluno"
|
||||||
onClick={handleNavigation}
|
|
||||||
className={({ isActive }) =>
|
|
||||||
`flex items-center gap-2 px-4 py-2 rounded-lg text-sm ${
|
|
||||||
isActive
|
|
||||||
? 'bg-purple-50 text-purple-700'
|
|
||||||
: 'text-gray-600 hover:bg-gray-50'
|
|
||||||
}`
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<BookOpen className="h-5 w-5" />
|
|
||||||
{!isCollapsed && <span>Minhas Histórias</span>}
|
|
||||||
</NavLink>
|
|
||||||
|
|
||||||
<NavLink
|
|
||||||
to="/aluno/painel"
|
|
||||||
end
|
end
|
||||||
onClick={handleNavigation}
|
onClick={handleNavigation}
|
||||||
className={({ isActive }) =>
|
className={({ isActive }) =>
|
||||||
@ -66,7 +51,22 @@ export function StudentDashboardLayout() {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<LayoutDashboard className="h-5 w-5" />
|
<LayoutDashboard className="h-5 w-5" />
|
||||||
{!isCollapsed && <span>Painel</span>}
|
{!isCollapsed && <span>Início</span>}
|
||||||
|
</NavLink>
|
||||||
|
|
||||||
|
<NavLink
|
||||||
|
to="/aluno/historias"
|
||||||
|
onClick={handleNavigation}
|
||||||
|
className={({ isActive }) =>
|
||||||
|
`flex items-center gap-2 px-4 py-2 rounded-lg text-sm ${
|
||||||
|
isActive
|
||||||
|
? 'bg-purple-50 text-purple-700'
|
||||||
|
: 'text-gray-600 hover:bg-gray-50'
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<BookOpen className="h-5 w-5" />
|
||||||
|
{!isCollapsed && <span>Minhas Histórias</span>}
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
|
||||||
{/* <NavLink
|
{/* <NavLink
|
||||||
|
|||||||
@ -14,6 +14,7 @@ interface DashboardMetrics {
|
|||||||
export function StudentDashboardPage() {
|
export function StudentDashboardPage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [student, setStudent] = React.useState<Student | null>(null);
|
const [student, setStudent] = React.useState<Student | null>(null);
|
||||||
|
const [stories, setStories] = React.useState<Story[]>([]);
|
||||||
const [metrics, setMetrics] = React.useState<DashboardMetrics>({
|
const [metrics, setMetrics] = React.useState<DashboardMetrics>({
|
||||||
totalStories: 0,
|
totalStories: 0,
|
||||||
averageReadingFluency: 0,
|
averageReadingFluency: 0,
|
||||||
@ -53,16 +54,18 @@ export function StudentDashboardPage() {
|
|||||||
.limit(6);
|
.limit(6);
|
||||||
|
|
||||||
if (storiesError) throw storiesError;
|
if (storiesError) throw storiesError;
|
||||||
|
setStories(storiesData || []);
|
||||||
|
|
||||||
// Calcular métricas (exemplo simplificado)
|
// Calcular métricas
|
||||||
|
// Em produção: Implementar cálculos reais baseados nos dados
|
||||||
setMetrics({
|
setMetrics({
|
||||||
totalStories: storiesData?.length || 0,
|
totalStories: storiesData?.length || 0,
|
||||||
averageReadingFluency: 85,
|
averageReadingFluency: 85, // Exemplo
|
||||||
totalReadingTime: 120,
|
totalReadingTime: 120, // Exemplo: 120 minutos
|
||||||
currentLevel: 3
|
currentLevel: 3 // Exemplo
|
||||||
});
|
});
|
||||||
|
|
||||||
// Buscar histórias recentes com a capa definida
|
// Buscar histórias recentes com a primeira página como capa
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('stories')
|
.from('stories')
|
||||||
.select(`
|
.select(`
|
||||||
@ -77,26 +80,7 @@ export function StudentDashboardPage() {
|
|||||||
.limit(6);
|
.limit(6);
|
||||||
|
|
||||||
if (error) throw error;
|
if (error) throw error;
|
||||||
|
setRecentStories(data || []);
|
||||||
// Transformar os dados para definir a propriedade 'cover' de forma robusta
|
|
||||||
const mappedData = (data || []).map(story => {
|
|
||||||
let coverObj = null;
|
|
||||||
if (story.cover) {
|
|
||||||
coverObj = Array.isArray(story.cover) ? story.cover[0] : story.cover;
|
|
||||||
} else if (story.pages && story.pages.length > 0) {
|
|
||||||
coverObj = story.pages[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (coverObj && typeof coverObj === 'object' && coverObj.image_url) {
|
|
||||||
return { ...story, cover: coverObj };
|
|
||||||
} else if (coverObj && typeof coverObj === 'string') {
|
|
||||||
return { ...story, cover: { image_url: coverObj } };
|
|
||||||
}
|
|
||||||
|
|
||||||
return { ...story, cover: null };
|
|
||||||
});
|
|
||||||
|
|
||||||
setRecentStories(mappedData);
|
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Erro ao carregar dashboard:', err);
|
console.error('Erro ao carregar dashboard:', err);
|
||||||
@ -144,10 +128,10 @@ export function StudentDashboardPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{/* Cabeçalho */}
|
{/* Cabeçalho */}
|
||||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-4 sm:p-6 mb-8">
|
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6 mb-8">
|
||||||
<div className="flex flex-col sm:flex-row justify-between items-center sm:items-start gap-4 sm:gap-0">
|
<div className="flex justify-between items-start">
|
||||||
<div className="flex flex-col sm:flex-row items-center sm:items-center gap-4 text-center sm:text-left">
|
<div className="flex items-center gap-4">
|
||||||
<div className="w-16 h-16 bg-purple-100 rounded-full flex items-center justify-center flex-shrink-0">
|
<div className="w-16 h-16 bg-purple-100 rounded-full flex items-center justify-center">
|
||||||
{student?.avatar_url ? (
|
{student?.avatar_url ? (
|
||||||
<img
|
<img
|
||||||
src={student.avatar_url}
|
src={student.avatar_url}
|
||||||
@ -161,18 +145,18 @@ export function StudentDashboardPage() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-xl sm:text-2xl font-bold text-gray-900">{student?.name}</h1>
|
<h1 className="text-2xl font-bold text-gray-900">{student?.name}</h1>
|
||||||
<p className="text-xs text-gray-500 truncate max-w-[250px] sm:max-w-none">
|
<p className="text-xs text-gray-500 truncate">
|
||||||
{student?.class?.name} - {student?.class?.grade} • {student?.school?.name}
|
{student?.class?.name} - {student?.class?.grade} • {student?.school?.name}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate('/aluno/historias/nova')}
|
onClick={() => navigate('/aluno/historias/nova')}
|
||||||
className="w-full sm:w-auto flex items-center justify-center gap-2 px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition"
|
className="flex items-center gap-2 px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition"
|
||||||
>
|
>
|
||||||
<Plus className="h-5 w-5" />
|
<Plus className="h-5 w-5" />
|
||||||
<span>Nova História</span>
|
Nova História
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -42,7 +42,7 @@ export function StudentStoriesPage() {
|
|||||||
if (error) throw error;
|
if (error) throw error;
|
||||||
|
|
||||||
// Aplicar ordenação
|
// Aplicar ordenação
|
||||||
let sortedData = (data || []).sort((a, b) => {
|
const sortedData = (data || []).sort((a, b) => {
|
||||||
switch (sortBy) {
|
switch (sortBy) {
|
||||||
case 'oldest':
|
case 'oldest':
|
||||||
return new Date(a.created_at).getTime() - new Date(b.created_at).getTime();
|
return new Date(a.created_at).getTime() - new Date(b.created_at).getTime();
|
||||||
@ -55,12 +55,6 @@ export function StudentStoriesPage() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Adicionar a propriedade 'cover', usando a imagem da primeira página
|
|
||||||
sortedData = sortedData.map(story => ({
|
|
||||||
...story,
|
|
||||||
cover: story.pages && story.pages.length > 0 ? story.pages[0] : null
|
|
||||||
}));
|
|
||||||
|
|
||||||
setStories(sortedData);
|
setStories(sortedData);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Erro ao buscar histórias:', err);
|
console.error('Erro ao buscar histórias:', err);
|
||||||
|
|||||||
@ -177,7 +177,7 @@ export const router = createBrowserRouter([
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
index: true,
|
index: true,
|
||||||
element: <StudentStoriesPage />,
|
element: <StudentDashboardPage />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'historias',
|
path: 'historias',
|
||||||
@ -200,10 +200,6 @@ export const router = createBrowserRouter([
|
|||||||
path: 'configuracoes',
|
path: 'configuracoes',
|
||||||
element: <StudentSettingsPage />,
|
element: <StudentSettingsPage />,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'painel',
|
|
||||||
element: <StudentDashboardPage />,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'conquistas',
|
path: 'conquistas',
|
||||||
element: <AchievementsPage />,
|
element: <AchievementsPage />,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user