Skip to content

Commit e0036de

Browse files
committed
melhorias nos componentes de biblioteca
1 parent d6720e9 commit e0036de

14 files changed

Lines changed: 842 additions & 549 deletions

frontend/src/App.jsx

Lines changed: 79 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
/**
22
* 🚀 SYNTAX APP - Performance Optimized + PWA
3-
*
3+
*
44
* Otimizações aplicadas:
55
* - Route-based Code Splitting (React.lazy)
66
* - Suspense com LoadingScreen elegante
77
* - Transições suaves entre páginas
88
* - PWA com suporte offline
9-
*
9+
*
1010
* Resultado: Bundle inicial menor, navegação 60fps, instalável
1111
*/
1212

@@ -27,35 +27,33 @@ import { initPWA } from './utils/pwaUtils';
2727
import { useFontSize } from './utils/useFontSize';
2828
import { initializeStreakTracking } from './services/streakService';
2929

30-
3130
// 🔥 LAZY LOADING - Páginas carregadas sob demanda
32-
// Cada página vira um chunk separado no build
33-
const LoginMinimal = lazy(() => import('./pages/LoginMinimal'));
34-
const Home = lazy(() => import('./pages/Home'));
35-
const Materias = lazy(() => import('./pages/Materias'));
36-
const Resumos = lazy(() => import('./pages/Resumos'));
37-
const Flashcards = lazy(() => import('./pages/Flashcards'));
38-
const Simulado = lazy(() => import('./pages/Simulado'));
39-
const ConsultaRapida = lazy(() => import('./pages/ConsultaRapida'));
40-
const SystemArchitect = lazy(() => import('./pages/SystemArchitect3D'));
41-
const Notificacoes = lazy(() => import('./pages/Notificacoes'));
42-
const Configuracoes = lazy(() => import('./pages/Configuracoes'));
43-
const MeuPerfil = lazy(() => import('./pages/MeuPerfil'));
31+
const LoginMinimal = lazy(() => import('./pages/LoginMinimal'));
32+
const Home = lazy(() => import('./pages/Home'));
33+
const Materias = lazy(() => import('./pages/Materias'));
34+
const Resumos = lazy(() => import('./pages/Resumos'));
35+
const Flashcards = lazy(() => import('./pages/Flashcards'));
36+
const Simulado = lazy(() => import('./pages/Simulado'));
37+
const ConsultaRapida = lazy(() => import('./pages/ConsultaRapida'));
38+
const SystemArchitect = lazy(() => import('./pages/SystemArchitect3D'));
39+
const Notificacoes = lazy(() => import('./pages/Notificacoes'));
40+
const Configuracoes = lazy(() => import('./pages/Configuracoes'));
41+
const MeuPerfil = lazy(() => import('./pages/MeuPerfil'));
4442
const HistoricoSimulados = lazy(() => import('./pages/HistoricoSimulados'));
45-
const Conquistas = lazy(() => import('./pages/Conquistas'));
46-
const Analytics = lazy(() => import('./pages/Analytics'));
47-
const Amigos = lazy(() => import('./pages/Amigos'));
48-
const IDE = lazy(() => import('./pages/IDE'));
49-
const FeedTech = lazy(() => import('./pages/FeedTech'));
50-
const MockInterview = lazy(() => import('./pages/MockInterview'));
51-
const KnowledgeMap = lazy(() => import('./pages/KnowledgeMap'));
52-
const Roadmaps = lazy(() => import('./pages/Roadmaps'));
53-
const StudyRooms = lazy(() => import('./pages/StudyRooms'));
54-
const CommunityLibrary = lazy(() => import('./pages/CommunityLibrary'));
55-
const PeerCodeReview = lazy(() => import('./pages/PeerCodeReview'));
43+
const Conquistas = lazy(() => import('./pages/Conquistas'));
44+
const Analytics = lazy(() => import('./pages/Analytics'));
45+
const Amigos = lazy(() => import('./pages/Amigos'));
46+
const IDE = lazy(() => import('./pages/IDE'));
47+
const FeedTech = lazy(() => import('./pages/FeedTech'));
48+
const MockInterview = lazy(() => import('./pages/MockInterview'));
49+
const KnowledgeMap = lazy(() => import('./pages/KnowledgeMap'));
50+
const Roadmaps = lazy(() => import('./pages/Roadmaps'));
51+
const StudyRooms = lazy(() => import('./pages/StudyRooms'));
52+
const CommunityLibrary = lazy(() => import('./pages/CommunityLibrary'));
53+
const PeerCodeReview = lazy(() => import('./pages/PeerCodeReview'));
5654
const GitHubIntegration = lazy(() => import('./pages/GitHubIntegration'));
5755

58-
// ⚡ PREFETCH - Pré-carrega todos os chunks no idle para navegação instantânea
56+
// ⚡ PREFETCH - Pré-carrega todos os chunks no idle
5957
const PAGE_IMPORTS = [
6058
() => import('./pages/Home'),
6159
() => import('./pages/Materias'),
@@ -93,32 +91,30 @@ function usePrefetchRoutes() {
9391
}, []);
9492
}
9593

96-
// FIX-003: Clear dashboard cache on logout to prevent cross-user data leaks
94+
// FIX-003: Limpa cache ao fazer logout para evitar vazamento de dados
9795
function CacheCleaner() {
9896
const { user } = useAuth();
9997
const { clearCache } = useDashboardData();
10098
const prevUserRef = useRef(null);
99+
101100
useEffect(() => {
102101
if (prevUserRef.current !== null && user === null) {
103102
clearCache();
104103
}
105104
prevUserRef.current = user;
106105
}, [user, clearCache]);
106+
107107
return null;
108108
}
109109

110-
// Componente de rota protegida
110+
// Rota protegida
111111
const ProtectedRoute = ({ children }) => {
112112
const { isAuthenticated, loading } = useAuth();
113-
114-
if (loading) {
115-
return <LoadingScreen />;
116-
}
117-
113+
if (loading) return <LoadingScreen />;
118114
return isAuthenticated ? children : <Navigate to="/login" replace />;
119115
};
120116

121-
// Spinner leve para transições entre páginas (não bloqueia o layout)
117+
// Spinner leve para transições entre páginas
122118
function PageSpinner() {
123119
return (
124120
<div className="flex items-center justify-center w-full h-64">
@@ -130,11 +126,7 @@ function PageSpinner() {
130126
function AppContent() {
131127
const { isAuthenticated, user } = useAuth();
132128

133-
// ⚡ Pré-carrega todos os chunks de rota no idle após o app montar
134129
usePrefetchRoutes();
135-
136-
// 🔍 Hook de Acessibilidade - Controle de tamanho de fonte
137-
// Inicializa o hook para aplicar font-size no <html> element
138130
useFontSize();
139131

140132
useEffect(() => {
@@ -146,99 +138,92 @@ function AppContent() {
146138
return (
147139
<Suspense fallback={<LoadingScreen />}>
148140
<Routes>
149-
<Route
150-
path="/login"
151-
element={isAuthenticated ? <Navigate to="/" replace /> : <LoginMinimal />}
141+
{/* Rota pública */}
142+
<Route
143+
path="/login"
144+
element={isAuthenticated ? <Navigate to="/" replace /> : <LoginMinimal />}
152145
/>
153-
154-
<Route
155-
path="/*"
146+
147+
{/* Rotas protegidas */}
148+
<Route
149+
path="/*"
156150
element={
157151
<ProtectedRoute>
158152
<DashboardDataProvider>
159-
<FocusModeProvider>
160-
<SocialProvider>
161-
<CacheCleaner />
162-
<OnboardingFlow />
163-
<Layout>
164-
{/* Suspense interno para transições entre páginas protegidas */}
165-
<Suspense fallback={<PageSpinner />}>
166-
<Routes>
167-
<Route path="/" element={<Home />} />
168-
<Route path="/materias" element={<Materias />} />
169-
<Route path="/resumos" element={<Resumos />} />
170-
<Route path="/flashcards" element={<Flashcards />} />
171-
<Route path="/simulado" element={<Simulado />} />
172-
<Route path="/consulta-rapida" element={<ConsultaRapida />} />
173-
<Route path="/atlas-3d" element={<SystemArchitect />} />
174-
<Route path="/notificacoes" element={<Notificacoes />} />
175-
<Route path="/configuracoes" element={<Configuracoes />} />
176-
<Route path="/meu-perfil" element={<MeuPerfil />} />
177-
<Route path="/historico-simulados" element={<HistoricoSimulados />} />
178-
<Route path="/conquistas" element={<Conquistas />} />
179-
<Route path="/analytics" element={<Analytics />} />
180-
<Route path="/amigos" element={<Amigos />} />
181-
<Route path="/ide" element={<IDE />} />
182-
<Route path="/feed" element={<FeedTech />} />
183-
<Route path="/mock-interview" element={<MockInterview />} />
184-
<Route path="/knowledge-map" element={<KnowledgeMap />} />
185-
<Route path="/roadmaps" element={<Roadmaps />} />
186-
<Route path="/study-rooms" element={<StudyRooms />} />
187-
<Route path="/community" element={<CommunityLibrary />} />
188-
<Route path="/peer-review" element={<PeerCodeReview />} />
189-
<Route path="/github" element={<GitHubIntegration />} />
190-
<Route path="*" element={<Navigate to="/" replace />} />
191-
</Routes>
192-
</Suspense>
193-
</Layout>
194-
</SocialProvider>
195-
</FocusModeProvider>
153+
<FocusModeProvider>
154+
<SocialProvider>
155+
<CacheCleaner />
156+
<OnboardingFlow />
157+
<Layout>
158+
<Suspense fallback={<PageSpinner />}>
159+
<Routes>
160+
<Route path="/" element={<Home />} />
161+
<Route path="/materias" element={<Materias />} />
162+
<Route path="/resumos" element={<Resumos />} />
163+
<Route path="/flashcards" element={<Flashcards />} />
164+
<Route path="/simulado" element={<Simulado />} />
165+
<Route path="/consulta-rapida" element={<ConsultaRapida />} />
166+
<Route path="/atlas-3d" element={<SystemArchitect />} />
167+
<Route path="/notificacoes" element={<Notificacoes />} />
168+
<Route path="/configuracoes" element={<Configuracoes />} />
169+
<Route path="/meu-perfil" element={<MeuPerfil />} />
170+
<Route path="/historico-simulados" element={<HistoricoSimulados />} />
171+
<Route path="/conquistas" element={<Conquistas />} />
172+
<Route path="/analytics" element={<Analytics />} />
173+
<Route path="/amigos" element={<Amigos />} />
174+
<Route path="/ide" element={<IDE />} />
175+
<Route path="/feed" element={<FeedTech />} />
176+
<Route path="/mock-interview" element={<MockInterview />} />
177+
<Route path="/knowledge-map" element={<KnowledgeMap />} />
178+
<Route path="/roadmaps" element={<Roadmaps />} />
179+
<Route path="/study-rooms" element={<StudyRooms />} />
180+
<Route path="/community" element={<CommunityLibrary />} />
181+
<Route path="/peer-review" element={<PeerCodeReview />} />
182+
<Route path="/github" element={<GitHubIntegration />} />
183+
<Route path="*" element={<Navigate to="/" replace />} />
184+
</Routes>
185+
</Suspense>
186+
</Layout>
187+
</SocialProvider>
188+
</FocusModeProvider>
196189
</DashboardDataProvider>
197190
</ProtectedRoute>
198-
}
191+
}
199192
/>
200193
</Routes>
201194
</Suspense>
202195
);
203196
}
204197

205-
// Toaster dinâmico que reage ao tema do app
198+
// Toaster que reage ao tema
206199
function DynamicToaster() {
207200
const { isDarkMode } = useTheme();
208201
return (
209202
<Toaster
210203
theme={isDarkMode ? 'dark' : 'light'}
211204
position="top-right"
212205
richColors
213-
toastOptions={{
214-
style: {
215-
fontFamily: 'inherit',
216-
},
217-
}}
206+
toastOptions={{ style: { fontFamily: 'inherit' } }}
218207
/>
219208
);
220209
}
221210

222211
function App() {
223-
// Inicializar PWA (listeners de instalação e status)
224212
useEffect(() => {
225213
initPWA();
226214
}, []);
227215

228-
229216
return (
230217
<Router future={{ v7_startTransition: true, v7_relativeSplatPath: true }}>
231218
<ThemeProvider>
232219
<AuthProvider>
233220
<AppContent />
234-
{/* Banner de instalação PWA */}
235221
<PWAInstallBanner />
236-
{/* Toast notifications */}
237222
<DynamicToaster />
238223
</AuthProvider>
239224
</ThemeProvider>
240225
</Router>
241226
);
242227
}
243228

244-
export default App;
229+
export default App;

frontend/src/components/FlashcardItem.jsx

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,25 @@ const FlashcardItem = memo(({
4848

4949
const materiaColor = flashcard.materiaCor || '#0EA5E9';
5050

51+
// Validações para lidar com Decks importados (Arrays) vs Flashcards simples (Strings)
52+
const renderPergunta = () => {
53+
if (typeof flashcard.pergunta === 'string') return flashcard.pergunta;
54+
if (flashcard.title) return flashcard.title;
55+
return 'Deck de Flashcards';
56+
};
57+
58+
const renderResposta = () => {
59+
// Se a resposta ou o conteúdo for um Array, significa que é um Deck importado
60+
if (Array.isArray(flashcard.resposta) || Array.isArray(flashcard.content)) {
61+
const qtd = Array.isArray(flashcard.resposta) ? flashcard.resposta.length : flashcard.content.length;
62+
return `${qtd} cartões neste deck. Abra o "Modo Estudo" para revisá-los!`;
63+
}
64+
// Se for texto normal
65+
if (typeof flashcard.resposta === 'string') return flashcard.resposta;
66+
67+
return 'Conteúdo indisponível';
68+
};
69+
5170
return (
5271
<div
5372
className="group perspective-1000 h-[280px] w-full cursor-pointer"
@@ -117,7 +136,7 @@ const FlashcardItem = memo(({
117136
</div>
118137

119138
<h3 className="text-[17px] sm:text-[18px] font-extrabold text-slate-900 dark:text-white leading-snug line-clamp-4 relative z-10 tracking-tight">
120-
{flashcard.pergunta}
139+
{renderPergunta()}
121140
</h3>
122141

123142
{flashcard.tags && flashcard.tags.length > 0 && (
@@ -160,7 +179,7 @@ const FlashcardItem = memo(({
160179
</h3>
161180

162181
<p className="text-[15px] sm:text-[16px] text-slate-800 dark:text-slate-200 leading-relaxed font-medium">
163-
{flashcard.resposta}
182+
{renderResposta()}
164183
</p>
165184

166185
{flashcard.imagemUrl && (

0 commit comments

Comments
 (0)