Fix: Restore help icon and move subscription page

Restore the help icon with its original functionality.
Move the subscription page to the sidebar.
Improve category chart percentage display.
This commit is contained in:
gpt-engineer-app[bot] 2025-06-22 00:45:38 +00:00
parent 36b64f7e5c
commit 641e4c4d11
5 changed files with 183 additions and 167 deletions

View File

@ -12,26 +12,6 @@ interface CategoryChartProps {
const CategoryChart: React.FC<CategoryChartProps> = ({ categories, isLoading = false }) => {
const validCategories = categories.filter(cat => cat.valor > 0);
const renderCustomizedLabel = ({ cx, cy, midAngle, innerRadius, outerRadius, percent, index }: any) => {
if (percent < 0.05) return null;
const radius = innerRadius + (outerRadius - innerRadius) * 0.7;
const x = cx + radius * Math.cos(-midAngle * Math.PI / 180);
const y = cy + radius * Math.sin(-midAngle * Math.PI / 180);
return (
<text
x={x}
y={y}
fill="#fff"
textAnchor={x > cx ? 'start' : 'end'}
dominantBaseline="central"
className="text-xs font-bold drop-shadow-lg"
>
{`${(percent * 100).toFixed(0)}%`}
</text>
);
};
const formatCurrency = (value: number) => {
return new Intl.NumberFormat('pt-BR', {
style: 'currency',
@ -49,7 +29,9 @@ const CategoryChart: React.FC<CategoryChartProps> = ({ categories, isLoading = f
className="w-3 h-3 mr-2 rounded-full shadow"
style={{ backgroundColor: entry.color }}
/>
<span className="truncate font-semibold text-zinc-700 dark:text-white">{entry.value}</span>
<span className="truncate font-semibold text-zinc-700 dark:text-white">
{entry.value} ({(entry.payload.percentage * 100).toFixed(0)}%)
</span>
</div>
))}
</div>
@ -94,8 +76,6 @@ const CategoryChart: React.FC<CategoryChartProps> = ({ categories, isLoading = f
data={validCategories}
cx="50%"
cy="45%"
labelLine={false}
label={renderCustomizedLabel}
outerRadius={90}
innerRadius={40}
fill="#8884d8"

View File

@ -4,6 +4,7 @@ import { useIsMobile } from '@/hooks/use-mobile';
import Header from './Header';
import Sidebar from './Sidebar';
import OnboardingTour from '@/components/onboarding/OnboardingTour';
import HelpIcon from '@/components/help/HelpIcon';
import { useOnboardingTour } from '@/hooks/useOnboardingTour';
interface LayoutProps {
@ -62,6 +63,9 @@ export default function Layout({ children }: LayoutProps) {
</main>
</div>
{/* Help Icon */}
<HelpIcon />
{/* Tour de Onboarding */}
<OnboardingTour
isOpen={tourOpen}

View File

@ -12,7 +12,8 @@ import {
Users,
Menu,
X,
Settings
Settings,
Crown
} from 'lucide-react';
import { cn } from '@/lib/utils';
import { Button } from '@/components/ui/button';
@ -60,6 +61,14 @@ export default function Sidebar({ isOpen, onClose }: SidebarProps) {
<Home className="mr-2 h-4 w-4 transition-transform group-hover:scale-110" />
<span>Dashboard</span>
</NavLink>
<NavLink
to="/assinatura"
className={({ isActive }) => getNavLinkClass(isActive)}
onClick={isMobile ? onClose : undefined}
>
<Crown className="mr-2 h-4 w-4 transition-transform group-hover:scale-110" />
<span>Assinatura</span>
</NavLink>
<NavLink
to="/transacoes"
className={({ isActive }) => getNavLinkClass(isActive)}

163
src/pages/Assinatura.tsx Normal file
View File

@ -0,0 +1,163 @@
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { ArrowLeft, Check } from "lucide-react";
import { Link } from "react-router-dom";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { format } from "date-fns";
import { ptBR } from "date-fns/locale";
import { toast } from "sonner";
import { supabase } from "@/integrations/supabase/client";
const Assinatura = () => {
const [isSubscribing, setIsSubscribing] = useState(false);
const [subscription] = useState({
active: true,
planName: "Plano Mensal Finance Home",
expires_at: new Date(new Date().getFullYear(), new Date().getMonth() + 1, 15),
});
const handleSubscribe = async () => {
setIsSubscribing(true);
try {
// Puxa os dados mais atuais do usuário logado NO MOMENTO do click!
const { data: { user } } = await supabase.auth.getUser();
if (!user || !user.id || !user.email) {
toast.error("Sessão inválida. Por favor, faça login novamente.");
setIsSubscribing(false);
return;
}
// log para debug - remova depois de testar!
console.log("Assinatura MercadoPago - email enviado:", user.email, "id:", user.id);
const { data, error } = await supabase.functions.invoke('mercado-pago-subscribe', {
body: { email: user.email, userId: user.id },
});
if (error) {
throw new Error(`Erro de comunicação: ${error.message}`);
}
if (data.error) {
throw new Error(data.error);
}
if (data.init_point) {
window.location.href = data.init_point;
} else {
throw new Error("Não foi possível obter a URL de checkout. Tente novamente.");
}
} catch (error: any) {
console.error("Erro ao criar a assinatura:", error);
toast.error(error.message || "Ocorreu um erro inesperado. Por favor, tente mais tarde.");
} finally {
setIsSubscribing(false);
}
};
return (
<div className="p-6">
<div className="flex items-center gap-4 mb-4">
<Button asChild variant="outline" size="icon">
<Link to="/">
<ArrowLeft className="h-4 w-4" />
<span className="sr-only">Voltar</span>
</Link>
</Button>
<h1 className="text-2xl font-bold">Assinatura</h1>
</div>
<div className="w-full max-w-md mx-auto space-y-6 mt-8">
<Card className="shadow-lg animate-fade-in">
<CardHeader className="items-center text-center p-6">
<CardTitle className="text-2xl">
Plano Mensal Finance Home
</CardTitle>
<CardDescription className="text-5xl font-extrabold pt-4">
R$14,99
<span className="text-lg font-medium text-muted-foreground">
/mês
</span>
</CardDescription>
</CardHeader>
<CardContent className="px-6 pb-6">
<ul className="space-y-4">
<li className="flex items-center gap-3">
<Check className="h-5 w-5 text-primary" />
<span className="text-muted-foreground">
Acesso total ao Finance Home
</span>
</li>
<li className="flex items-center gap-3">
<Check className="h-5 w-5 text-primary" />
<span className="text-muted-foreground">
Relatórios ilimitados
</span>
</li>
<li className="flex items-center gap-3">
<Check className="h-5 w-5 text-primary" />
<span className="text-muted-foreground">
Integração com WhatsApp
</span>
</li>
</ul>
</CardContent>
<CardFooter className="flex flex-col-reverse sm:flex-row sm:justify-end gap-2 p-6 bg-muted/50 rounded-b-lg">
<Button variant="outline" className="w-full sm:w-auto">
Ver Detalhes
</Button>
<Button
className="w-full sm:w-auto"
onClick={handleSubscribe}
disabled={isSubscribing}
>
{isSubscribing ? 'Processando...' : 'Assinar Agora'}
</Button>
</CardFooter>
</Card>
{subscription.active && (
<Card>
<CardHeader>
<CardTitle>Sua Assinatura Atual</CardTitle>
<CardDescription>
Obrigado por ser um assinante!
</CardDescription>
</CardHeader>
<CardContent className="space-y-2">
<p className="font-medium">
Plano:{" "}
<span className="font-normal text-muted-foreground">
{subscription.planName}
</span>
</p>
<p className="font-medium">
Vence em:{" "}
<span className="font-normal text-muted-foreground">
{format(subscription.expires_at, "dd 'de' MMMM 'de' yyyy", {
locale: ptBR,
})}
</span>
</p>
</CardContent>
<CardFooter>
<Button variant="outline">Gerenciar Assinatura</Button>
</CardFooter>
</Card>
)}
</div>
</div>
);
};
export default Assinatura;

View File

@ -1,71 +1,13 @@
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
import MeuCadastroForm from "@/components/settings/MeuCadastroForm";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { ArrowLeft, Check } from "lucide-react";
import { ArrowLeft } from "lucide-react";
import { Link } from "react-router-dom";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { format } from "date-fns";
import { ptBR } from "date-fns/locale";
import { toast } from "sonner";
import { supabase } from "@/integrations/supabase/client";
const Configuracoes = () => {
const [tab, setTab] = useState("visao-geral");
const [isSubscribing, setIsSubscribing] = useState(false);
const [subscription] = useState({
active: true,
planName: "Plano Mensal Finance Home",
expires_at: new Date(new Date().getFullYear(), new Date().getMonth() + 1, 15),
});
const handleSubscribe = async () => {
setIsSubscribing(true);
try {
// Puxa os dados mais atuais do usuário logado NO MOMENTO do click!
const { data: { user } } = await supabase.auth.getUser();
if (!user || !user.id || !user.email) {
toast.error("Sessão inválida. Por favor, faça login novamente.");
setIsSubscribing(false);
return;
}
// log para debug - remova depois de testar!
console.log("Assinatura MercadoPago - email enviado:", user.email, "id:", user.id);
const { data, error } = await supabase.functions.invoke('mercado-pago-subscribe', {
body: { email: user.email, userId: user.id },
});
if (error) {
throw new Error(`Erro de comunicação: ${error.message}`);
}
if (data.error) {
throw new Error(data.error);
}
if (data.init_point) {
window.location.href = data.init_point;
} else {
throw new Error("Não foi possível obter a URL de checkout. Tente novamente.");
}
} catch (error: any) {
console.error("Erro ao criar a assinatura:", error);
toast.error(error.message || "Ocorreu um erro inesperado. Por favor, tente mais tarde.");
} finally {
setIsSubscribing(false);
}
};
return (
<div className="p-6">
@ -83,98 +25,16 @@ const Configuracoes = () => {
<TabsList className="mb-6">
<TabsTrigger value="visao-geral">Visão Geral</TabsTrigger>
<TabsTrigger value="meu-cadastro">Meu cadastro</TabsTrigger>
<TabsTrigger value="assinatura">Assinatura</TabsTrigger>
</TabsList>
<TabsContent value="visao-geral">
<div className="p-6 bg-muted rounded-lg border text-muted-foreground text-center">
<div className="text-lg font-semibold mb-2">Bem-vindo à página de Configurações!</div>
<div>Aqui você pode gerenciar suas informações, assinatura e muito mais.</div>
<div>Aqui você pode gerenciar suas informações pessoais e configurações da conta.</div>
</div>
</TabsContent>
<TabsContent value="meu-cadastro">
<MeuCadastroForm />
</TabsContent>
<TabsContent value="assinatura">
<div className="w-full max-w-md mx-auto space-y-6">
<Card className="shadow-lg animate-fade-in">
<CardHeader className="items-center text-center p-6">
<CardTitle className="text-2xl">
Plano Mensal Finance Home
</CardTitle>
<CardDescription className="text-5xl font-extrabold pt-4">
R$14,99
<span className="text-lg font-medium text-muted-foreground">
/mês
</span>
</CardDescription>
</CardHeader>
<CardContent className="px-6 pb-6">
<ul className="space-y-4">
<li className="flex items-center gap-3">
<Check className="h-5 w-5 text-primary" />
<span className="text-muted-foreground">
Acesso total ao Finance Home
</span>
</li>
<li className="flex items-center gap-3">
<Check className="h-5 w-5 text-primary" />
<span className="text-muted-foreground">
Relatórios ilimitados
</span>
</li>
<li className="flex items-center gap-3">
<Check className="h-5 w-5 text-primary" />
<span className="text-muted-foreground">
Integração com WhatsApp
</span>
</li>
</ul>
</CardContent>
<CardFooter className="flex flex-col-reverse sm:flex-row sm:justify-end gap-2 p-6 bg-muted/50 rounded-b-lg">
<Button variant="outline" className="w-full sm:w-auto">
Ver Detalhes
</Button>
<Button
className="w-full sm:w-auto"
onClick={handleSubscribe}
disabled={isSubscribing}
>
{isSubscribing ? 'Processando...' : 'Assinar Agora'}
</Button>
</CardFooter>
</Card>
{subscription.active && (
<Card>
<CardHeader>
<CardTitle>Sua Assinatura Atual</CardTitle>
<CardDescription>
Obrigado por ser um assinante!
</CardDescription>
</CardHeader>
<CardContent className="space-y-2">
<p className="font-medium">
Plano:{" "}
<span className="font-normal text-muted-foreground">
{subscription.planName}
</span>
</p>
<p className="font-medium">
Vence em:{" "}
<span className="font-normal text-muted-foreground">
{format(subscription.expires_at, "dd 'de' MMMM 'de' yyyy", {
locale: ptBR,
})}
</span>
</p>
</CardContent>
<CardFooter>
<Button variant="outline">Gerenciar Assinatura</Button>
</CardFooter>
</Card>
)}
</div>
</TabsContent>
</Tabs>
</div>
);