style: padroniza visual da criação de redações

- Alinha estilo dos cards com CreateStoryPage
- Adiciona suporte a texto adaptativo
- Melhora feedback visual e estados interativos
- Implementa loading states animados
This commit is contained in:
Lucas Santana 2025-02-06 21:38:01 -03:00
parent 63498e92c6
commit b767d60c50

View File

@ -3,7 +3,12 @@ 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 { ArrowLeft } from 'lucide-react';
import { ArrowLeft, Sparkles } from 'lucide-react';
import { useSession } from '@/hooks/useSession';
import { useUppercasePreference } from '@/hooks/useUppercasePreference';
import { AdaptiveText, AdaptiveTitle, AdaptiveParagraph } from '@/components/ui/adaptive-text';
import { TextCaseToggle } from '@/components/ui/text-case-toggle';
import { cn } from '@/lib/utils';
interface EssayType {
id: string;
@ -31,6 +36,8 @@ export function NewEssay() {
const [types, setTypes] = useState<EssayType[]>([]);
const [genres, setGenres] = useState<EssayGenre[]>([]);
const [loading, setLoading] = useState(true);
const { session } = useSession();
const { isUpperCase, toggleUppercase, isLoading } = useUppercasePreference(session?.user?.id);
useEffect(() => {
loadTypes();
@ -102,9 +109,11 @@ export function NewEssay() {
{types.map((type) => (
<Card
key={type.id}
className={`cursor-pointer hover:shadow-lg transition-shadow ${
selectedType?.id === type.id ? 'ring-2 ring-primary' : ''
}`}
className={cn(
"cursor-pointer hover:shadow-lg transition-all duration-200",
"bg-white border border-gray-200",
selectedType?.id === type.id && "ring-2 ring-purple-600 bg-purple-50"
)}
onClick={() => {
setSelectedType(type);
loadGenres(type.id);
@ -113,10 +122,22 @@ export function NewEssay() {
>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<span>{type.icon}</span>
{type.title}
<div className="p-2 bg-purple-100 rounded-lg">
<span className="text-2xl">{type.icon}</span>
</div>
<AdaptiveText
text={type.title}
isUpperCase={isUpperCase}
className="font-bold"
/>
</CardTitle>
<CardDescription>{type.description}</CardDescription>
<CardDescription>
<AdaptiveText
text={type.description}
isUpperCase={isUpperCase}
className="text-gray-600"
/>
</CardDescription>
</CardHeader>
</Card>
))}
@ -131,26 +152,48 @@ export function NewEssay() {
{genres.map((genre) => (
<Card
key={genre.id}
className="cursor-pointer hover:shadow-lg transition-shadow"
className="cursor-pointer hover:shadow-lg transition-all duration-200 bg-white border border-gray-200"
onClick={() => createEssay(genre.id)}
>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<span>{genre.icon}</span>
{genre.title}
<div className="p-2 bg-purple-100 rounded-lg">
<span className="text-2xl">{genre.icon}</span>
</div>
<AdaptiveText
text={genre.title}
isUpperCase={isUpperCase}
className="font-bold"
/>
</CardTitle>
<CardDescription>{genre.description}</CardDescription>
<CardDescription>
<AdaptiveText
text={genre.description}
isUpperCase={isUpperCase}
className="text-gray-600"
/>
</CardDescription>
</CardHeader>
<CardContent>
<div className="text-sm text-muted-foreground">
<p>Mínimo: {genre.requirements.min_words} palavras</p>
<p>Máximo: {genre.requirements.max_words} palavras</p>
<p className="mt-2">Elementos necessários:</p>
<ul className="list-disc list-inside">
{genre.requirements.required_elements.map((element, index) => (
<li key={index}>{element}</li>
))}
</ul>
<div className="space-y-4">
<div className="bg-purple-50 p-4 rounded-lg border border-purple-100">
<h4 className="text-sm font-medium text-purple-900 mb-2">Requisitos</h4>
<div className="space-y-2 text-sm text-purple-800">
<p>Mínimo: {genre.requirements.min_words} palavras</p>
<p>Máximo: {genre.requirements.max_words} palavras</p>
<p className="mt-2 font-medium">Elementos necessários:</p>
<ul className="list-disc list-inside space-y-1">
{genre.requirements.required_elements.map((element, index) => (
<li key={index}>
<AdaptiveText
text={element}
isUpperCase={isUpperCase}
/>
</li>
))}
</ul>
</div>
</div>
</div>
</CardContent>
</Card>
@ -161,11 +204,38 @@ export function NewEssay() {
return (
<div className="container mx-auto p-6">
<div className="flex items-center gap-4 mb-6">
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
<div className="flex items-center gap-3 mb-8">
<div className="p-2 bg-purple-100 rounded-lg">
<Sparkles className="h-6 w-6 text-purple-600" />
</div>
<div>
<AdaptiveTitle
text="Nova Redação"
isUpperCase={isUpperCase}
className="text-2xl font-bold text-gray-900"
/>
<AdaptiveParagraph
text={step === 'type'
? "Selecione o tipo textual para começar"
: "Escolha o gênero textual da sua redação"}
isUpperCase={isUpperCase}
className="text-gray-600"
/>
</div>
<TextCaseToggle
isUpperCase={isUpperCase}
onToggle={toggleUppercase}
isLoading={isLoading}
className="ml-auto"
/>
</div>
{step === 'genre' && (
<Button
variant="ghost"
onClick={() => setStep('type')}
className="mb-6"
trackingId="essay-new-back-button"
trackingProperties={{
action: 'back_to_type_selection',
@ -174,26 +244,22 @@ export function NewEssay() {
}}
>
<ArrowLeft className="mr-2 h-4 w-4" />
Voltar
Voltar para tipos textuais
</Button>
)}
<div>
<h1 className="text-3xl font-bold">Nova Redação</h1>
<p className="text-muted-foreground">
{step === 'type'
? 'Selecione o tipo textual'
: 'Selecione o gênero textual'}
</p>
</div>
</div>
{loading ? (
<div>Carregando...</div>
) : step === 'type' ? (
renderTypeSelection()
) : (
renderGenreSelection()
)}
{loading ? (
<div className="animate-pulse space-y-4">
<div className="h-48 bg-gray-100 rounded-lg" />
<div className="h-48 bg-gray-100 rounded-lg" />
<div className="h-48 bg-gray-100 rounded-lg" />
</div>
) : step === 'type' ? (
renderTypeSelection()
) : (
renderGenreSelection()
)}
</div>
</div>
);
}