mirror of
https://github.com/lucasrcsantana/story-generator.git
synced 2025-12-16 21:37:51 +00:00
refactor: atualiza interface de capa das histórias
- Adiciona tipagem para cover na interface Story - Atualiza queries para usar story_pages como capa - Usa página 1 como capa padrão das histórias - Otimiza carregamento de imagens com parâmetros
This commit is contained in:
parent
fbeeace8bb
commit
7087a87ece
@ -43,6 +43,11 @@ e este projeto adere ao [Semantic Versioning](https://semver.org/lang/pt-BR/).
|
||||
- Uso da primeira página como capa
|
||||
- Tamanho reduzido para thumbnails
|
||||
- Carregamento lazy para melhor performance
|
||||
- Refatoração da interface de capa
|
||||
- Tipagem específica para cover na interface Story
|
||||
- Padronização do uso da primeira página como capa
|
||||
- Otimização de queries para busca de capas
|
||||
- Parâmetros de transformação para thumbnails
|
||||
|
||||
- Padronização da interface de histórias
|
||||
- Consistência visual entre dashboard e lista
|
||||
|
||||
@ -194,11 +194,11 @@ export function StoryPage() {
|
||||
if (storyError) throw storyError;
|
||||
|
||||
// Ordenar páginas por número
|
||||
const orderedPages = storyData.story_pages.sort((a, b) => a.page_number - b.page_number);
|
||||
const orderedPages = storyData.story_pages.sort((a: { page_number: number }, b: { page_number: number }) => a.page_number - b.page_number);
|
||||
setStory({
|
||||
...storyData,
|
||||
content: {
|
||||
pages: orderedPages.map(page => ({
|
||||
pages: orderedPages.map((page: { text: string; image_url: string }) => ({
|
||||
text: page.text,
|
||||
image: page.image_url
|
||||
}))
|
||||
@ -301,9 +301,10 @@ export function StoryPage() {
|
||||
|
||||
// Pré-carregar próxima imagem
|
||||
useEffect(() => {
|
||||
if (story?.content?.pages?.[currentPage + 1]?.image) {
|
||||
const nextImageUrl = story?.content?.pages?.[currentPage + 1]?.image;
|
||||
if (nextImageUrl) {
|
||||
const nextImage = new Image();
|
||||
nextImage.src = story.content.pages[currentPage + 1].image;
|
||||
nextImage.src = nextImageUrl;
|
||||
}
|
||||
}, [currentPage, story]);
|
||||
|
||||
|
||||
@ -65,7 +65,7 @@ export function StudentDashboardPage() {
|
||||
currentLevel: 3 // Exemplo
|
||||
});
|
||||
|
||||
// Buscar histórias recentes
|
||||
// Buscar histórias recentes com a primeira página como capa
|
||||
const { data, error } = await supabase
|
||||
.from('stories')
|
||||
.select(`
|
||||
@ -75,7 +75,7 @@ export function StudentDashboardPage() {
|
||||
)
|
||||
`)
|
||||
.eq('student_id', session.user.id)
|
||||
.eq('story_pages.page_number', 1)
|
||||
.eq('story_pages.page_number', 1) // Garante que pegamos a primeira página
|
||||
.order('created_at', { ascending: false })
|
||||
.limit(3);
|
||||
|
||||
|
||||
@ -23,22 +23,35 @@ export function StudentStoriesPage() {
|
||||
const { data: { session } } = await supabase.auth.getSession();
|
||||
if (!session?.user?.id) return;
|
||||
|
||||
const { data, error } = await supabase
|
||||
const query = supabase
|
||||
.from('stories')
|
||||
.select(`
|
||||
*,
|
||||
cover:story_pages!inner(
|
||||
image_url
|
||||
)
|
||||
`)
|
||||
.eq('student_id', session.user.id)
|
||||
.eq('story_pages.page_number', 1)
|
||||
.order('created_at', { ascending: false });
|
||||
.select('*')
|
||||
.eq('student_id', session.user.id);
|
||||
|
||||
if (statusFilter !== 'all') {
|
||||
query.eq('status', statusFilter);
|
||||
}
|
||||
|
||||
let { data, error } = await query;
|
||||
if (error) throw error;
|
||||
setStories(data || []);
|
||||
|
||||
// Aplicar ordenação
|
||||
const sortedData = (data || []).sort((a, b) => {
|
||||
switch (sortBy) {
|
||||
case 'oldest':
|
||||
return new Date(a.created_at).getTime() - new Date(b.created_at).getTime();
|
||||
case 'title':
|
||||
return a.title.localeCompare(b.title);
|
||||
case 'performance':
|
||||
return (b.performance_score || 0) - (a.performance_score || 0);
|
||||
default: // recent
|
||||
return new Date(b.created_at).getTime() - new Date(a.created_at).getTime();
|
||||
}
|
||||
});
|
||||
|
||||
setStories(sortedData);
|
||||
} catch (err) {
|
||||
console.error('Erro ao carregar histórias:', err);
|
||||
console.error('Erro ao buscar histórias:', err);
|
||||
setError('Não foi possível carregar suas histórias');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@ -46,7 +59,7 @@ export function StudentStoriesPage() {
|
||||
};
|
||||
|
||||
fetchStories();
|
||||
}, []);
|
||||
}, [statusFilter, sortBy]);
|
||||
|
||||
const filteredStories = stories.filter(story =>
|
||||
story.title.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
@ -184,10 +197,10 @@ export function StudentStoriesPage() {
|
||||
className="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden cursor-pointer hover:shadow-md transition"
|
||||
onClick={() => navigate(`/aluno/historias/${story.id}`)}
|
||||
>
|
||||
{story.cover && (
|
||||
<div className="relative aspect-video bg-gray-100">
|
||||
{story.content?.pages?.[0]?.image && (
|
||||
<div className="relative aspect-video">
|
||||
<img
|
||||
src={`${story.cover.image_url}?width=400&quality=80&format=webp`}
|
||||
src={story.content.pages[0].image}
|
||||
alt={story.title}
|
||||
className="w-full h-48 object-cover"
|
||||
loading="lazy"
|
||||
|
||||
@ -1,158 +0,0 @@
|
||||
import { createBrowserRouter } from 'react-router-dom';
|
||||
import { RootLayout } from '../components/layout/RootLayout';
|
||||
import { HomePage } from '../components/home/HomePage';
|
||||
import { LoginForm } from '../components/auth/LoginForm';
|
||||
import { SchoolRegistrationForm } from '../components/auth/SchoolRegistrationForm';
|
||||
import { RegistrationForm } from '../components/RegistrationForm';
|
||||
import { StoryViewer } from '../components/StoryViewer';
|
||||
import { AuthCallback } from '../pages/AuthCallback';
|
||||
import { DashboardLayout } from '../pages/dashboard/DashboardLayout';
|
||||
import { DashboardHome } from '../pages/dashboard/DashboardHome';
|
||||
import { ClassesPage } from '../pages/dashboard/classes/ClassesPage';
|
||||
import { CreateClassPage } from '../pages/dashboard/classes/CreateClassPage';
|
||||
import { TeachersPage } from '../pages/dashboard/teachers/TeachersPage';
|
||||
import { InviteTeacherPage } from '../pages/dashboard/teachers/InviteTeacherPage';
|
||||
import { StudentsPage } from '../pages/dashboard/students/StudentsPage';
|
||||
import { AddStudentPage } from '../pages/dashboard/students/AddStudentPage';
|
||||
import { DemoPage } from '../pages/demo/DemoPage';
|
||||
import { StoryPage } from '../pages/story/StoryPage';
|
||||
import { StudentSettingsPage } from '../pages/student-dashboard/StudentSettingsPage';
|
||||
import { StudentDashboardLayout } from '../pages/student-dashboard/StudentDashboardLayout';
|
||||
import { StudentDashboard, StudentClassPage } from '../pages/student-dashboard';
|
||||
import { AchievementsPage } from '../pages/student-dashboard/AchievementsPage';
|
||||
import React from 'react';
|
||||
|
||||
export const router = createBrowserRouter([
|
||||
{
|
||||
path: '/',
|
||||
element: <RootLayout />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <HomePage />,
|
||||
},
|
||||
{
|
||||
path: 'demo',
|
||||
element: <StoryPage demo={true} />
|
||||
},
|
||||
{
|
||||
path: 'login',
|
||||
children: [
|
||||
{
|
||||
path: 'school',
|
||||
element: <LoginForm userType="school" />,
|
||||
},
|
||||
{
|
||||
path: 'teacher',
|
||||
element: <LoginForm userType="teacher" />,
|
||||
},
|
||||
{
|
||||
path: 'student',
|
||||
element: <LoginForm userType="student" />,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'register',
|
||||
children: [
|
||||
{
|
||||
path: 'school',
|
||||
element: <SchoolRegistrationForm />,
|
||||
},
|
||||
{
|
||||
path: 'teacher',
|
||||
element: <RegistrationForm
|
||||
userType="teacher"
|
||||
onComplete={(userData) => {
|
||||
console.log(userData);
|
||||
}}
|
||||
/>,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'dashboard',
|
||||
element: <DashboardLayout />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <DashboardHome />,
|
||||
},
|
||||
{
|
||||
path: 'turmas',
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <ClassesPage />,
|
||||
},
|
||||
{
|
||||
path: 'nova',
|
||||
element: <CreateClassPage />,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'professores',
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <TeachersPage />,
|
||||
},
|
||||
{
|
||||
path: 'convidar',
|
||||
element: <InviteTeacherPage />,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'alunos',
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <StudentsPage />,
|
||||
},
|
||||
{
|
||||
path: 'novo',
|
||||
element: <AddStudentPage />,
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'auth/callback',
|
||||
element: <AuthCallback />
|
||||
},
|
||||
{
|
||||
path: 'story/:storyId',
|
||||
element: <StoryPage />
|
||||
},
|
||||
{
|
||||
path: 'aluno',
|
||||
element: <StudentDashboardLayout />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <StudentDashboard />,
|
||||
},
|
||||
{
|
||||
path: 'configuracoes',
|
||||
element: <StudentSettingsPage />,
|
||||
},
|
||||
{
|
||||
path: 'conquistas',
|
||||
element: <AchievementsPage />,
|
||||
},
|
||||
{
|
||||
path: 'historias/:id',
|
||||
element: <StoryPage />,
|
||||
},
|
||||
{
|
||||
path: 'turmas/:classId',
|
||||
element: <StudentClassPage />,
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
}
|
||||
]);
|
||||
@ -105,6 +105,7 @@ export interface StoryPage {
|
||||
}
|
||||
|
||||
export interface Story {
|
||||
cover: any;
|
||||
id: string;
|
||||
student_id: string;
|
||||
class_id: string;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user