From 1c6aa56b325c3e0b255d9799bd66938cd003e09f Mon Sep 17 00:00:00 2001 From: Lucas Santana Date: Thu, 6 Feb 2025 21:54:01 -0300 Subject: [PATCH] =?UTF-8?q?style:=20padroniza=20visual=20da=20listagem=20d?= =?UTF-8?q?e=20reda=C3=A7=C3=B5es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Alinha estilo com StudentStoriesPage - Adiciona busca e filtros avançados - Melhora feedback visual e estados interativos - Implementa loading states animados --- src/components/ui/editor.tsx | 2 + src/pages/student-dashboard/essays/index.tsx | 279 +++++++++++++++---- tailwind.config.js | 55 +++- 3 files changed, 263 insertions(+), 73 deletions(-) diff --git a/src/components/ui/editor.tsx b/src/components/ui/editor.tsx index 659b744..407c22b 100644 --- a/src/components/ui/editor.tsx +++ b/src/components/ui/editor.tsx @@ -160,6 +160,7 @@ export function Editor({ 'rounded-md border border-input bg-transparent', 'focus-within:outline-none focus-within:ring-2', 'focus-within:ring-ring focus-within:ring-offset-2', + 'transition-all duration-200', className )} > @@ -172,6 +173,7 @@ export function Editor({ '[&_.is-editor-empty]:before:float-left', '[&_.is-editor-empty]:before:h-0', '[&_.is-editor-empty]:before:pointer-events-none', + 'transition-all duration-200', readOnly && 'prose-sm' )} style={{ minHeight }} diff --git a/src/pages/student-dashboard/essays/index.tsx b/src/pages/student-dashboard/essays/index.tsx index 598fa8f..09322df 100644 --- a/src/pages/student-dashboard/essays/index.tsx +++ b/src/pages/student-dashboard/essays/index.tsx @@ -1,10 +1,11 @@ import { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { supabase } from '@/lib/supabase'; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; -import { PlusCircle } from 'lucide-react'; -import { Badge } from '@/components/ui/badge'; +import { Plus, Search, Filter, PenTool } from 'lucide-react'; +import { useSession } from '@/hooks/useSession'; +import { useUppercasePreference } from '@/hooks/useUppercasePreference'; +import { AdaptiveText } from '@/components/ui/adaptive-text'; interface Essay { id: string; @@ -21,107 +22,265 @@ interface Essay { }; } +type EssayStatus = 'all' | 'draft' | 'submitted' | 'analyzed'; +type SortOption = 'recent' | 'oldest' | 'title'; + export function EssaysPage() { const navigate = useNavigate(); const [essays, setEssays] = useState([]); const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [searchTerm, setSearchTerm] = useState(''); + const [statusFilter, setStatusFilter] = useState('all'); + const [sortBy, setSortBy] = useState('recent'); + const [showFilters, setShowFilters] = useState(false); + const { session } = useSession(); + const { isUpperCase } = useUppercasePreference(session?.user?.id); useEffect(() => { loadEssays(); - }, []); + }, [statusFilter, sortBy]); async function loadEssays() { try { - const { data, error } = await supabase + const { data: { session } } = await supabase.auth.getSession(); + if (!session?.user?.id) return; + + const query = supabase .from('student_essays') .select(` *, essay_type:essay_types(title), essay_genre:essay_genres(title) `) - .order('created_at', { ascending: false }); + .eq('student_id', session.user.id); - if (error) throw error; - setEssays(data || []); + if (statusFilter !== 'all') { + query.eq('status', statusFilter); + } + + const { data, error } = await query; + if (error) { + setError('Não foi possível carregar suas redações'); + return; + } + + // 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); + default: // recent + return new Date(b.created_at).getTime() - new Date(a.created_at).getTime(); + } + }); + + setEssays(sortedData); } catch (error) { console.error('Erro ao carregar redações:', error); + setError('Não foi possível carregar suas redações'); } finally { setLoading(false); } } + const filteredEssays = essays.filter(essay => + essay.title.toLowerCase().includes(searchTerm.toLowerCase()) + ); + function getStatusBadge(status: Essay['status']) { const statusMap = { - draft: { label: 'Rascunho', variant: 'secondary' as const }, - submitted: { label: 'Enviada', variant: 'default' as const }, - analyzed: { label: 'Analisada', variant: 'success' as const } + draft: { label: 'Rascunho', variant: 'secondary' as const, classes: 'bg-yellow-100 text-yellow-800' }, + submitted: { label: 'Enviada', variant: 'default' as const, classes: 'bg-blue-100 text-blue-800' }, + analyzed: { label: 'Analisada', variant: 'success' as const, classes: 'bg-green-100 text-green-800' } }; - const { label, variant } = statusMap[status]; - return {label}; + const { label, classes } = statusMap[status]; + return ( + + + + ); + } + + if (loading) { + return ( +
+
+
+ {[...Array(6)].map((_, i) => ( +
+ ))} +
+
+ ); } return ( -
+
-

Minhas Redações

+

+ +

- {loading ? ( -
Carregando...
- ) : essays.length === 0 ? ( - - -

Você ainda não tem nenhuma redação

- -
-
- ) : ( -
- {essays.map((essay) => ( - navigate(`/aluno/redacoes/${essay.id}`)}> - -
-
- {essay.title || 'Sem título'} - - {essay.essay_type?.title} - {essay.essay_genre?.title} - -
- {getStatusBadge(essay.status)} -
-
- -

- Criada em {new Date(essay.created_at).toLocaleDateString('pt-BR')} -

-
-
- ))} +
+
+
+ {/* Busca */} +
+ + setSearchTerm(e.target.value)} + className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-purple-500 focus:border-purple-500" + /> +
+ + {/* Filtros e Ordenação */} +
+ + +
+
+ + {/* Painel de Filtros */} + {showFilters && ( +
+
+ + + + +
+
+ )}
- )} + + {/* Lista de Redações */} + {filteredEssays.length === 0 ? ( +
+ +

+ +

+

+ +

+ {!searchTerm && ( + + )} +
+ ) : ( +
+ {filteredEssays.map((essay) => ( +
navigate(`/aluno/redacoes/${essay.id}`)} + > +
+

+ +

+
+ + + +
+
+ {new Date(essay.created_at).toLocaleDateString('pt-BR')} + {getStatusBadge(essay.status)} +
+
+
+ ))} +
+ )} +
); } \ No newline at end of file diff --git a/tailwind.config.js b/tailwind.config.js index 647c83f..058fb88 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -50,25 +50,55 @@ module.exports = { DEFAULT: "hsl(var(--card))", foreground: "hsl(var(--card-foreground))", }, + purple: { + 50: '#f5f3ff', + 100: '#ede9fe', + 200: '#ddd6fe', + 300: '#c4b5fd', + 400: '#a78bfa', + 500: '#8b5cf6', + 600: '#7c3aed', + 700: '#6d28d9', + 800: '#5b21b6', + 900: '#4c1d95', + }, + blue: { + 50: '#eff6ff', + 100: '#dbeafe', + 200: '#bfdbfe', + 300: '#93c5fd', + 400: '#60a5fa', + 500: '#3b82f6', + 600: '#2563eb', + 700: '#1d4ed8', + 800: '#1e40af', + 900: '#1e3a8a', + }, }, borderRadius: { - lg: "var(--radius)", - md: "calc(var(--radius) - 2px)", - sm: "calc(var(--radius) - 4px)", + none: '0', + sm: '0.125rem', + DEFAULT: '0.25rem', + md: '0.375rem', + lg: '0.5rem', + xl: '0.75rem', + '2xl': '1rem', + '3xl': '1.5rem', + full: '9999px', }, keyframes: { - "accordion-down": { - from: { height: 0 }, - to: { height: "var(--radix-accordion-content-height)" }, - }, - "accordion-up": { - from: { height: "var(--radix-accordion-content-height)" }, - to: { height: 0 }, + "fade-in": { + "0%": { opacity: 0 }, + "100%": { opacity: 1 } }, + "fade-out": { + "0%": { opacity: 1 }, + "100%": { opacity: 0 } + } }, animation: { - "accordion-down": "accordion-down 0.2s ease-out", - "accordion-up": "accordion-up 0.2s ease-out", + "fade-in": "fade-in 200ms ease-in", + "fade-out": "fade-out 200ms ease-out" }, typography: { DEFAULT: { @@ -282,7 +312,6 @@ module.exports = { }, }, plugins: [ - require("tailwindcss-animate"), require('@tailwindcss/typography'), ], }