feat: Move user button and integrate webhook
Move the logged-in user button above the month filter on the dashboard. Integrate a webhook call to send contact form data to a specified URL.
This commit is contained in:
parent
6f6292f857
commit
1f3197caf5
97
src/components/dashboard/UserProfileButton.tsx
Normal file
97
src/components/dashboard/UserProfileButton.tsx
Normal file
@ -0,0 +1,97 @@
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { LogOut, User } from 'lucide-react';
|
||||
import { supabase } from '@/integrations/supabase/client';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
const UserProfileButton = () => {
|
||||
const [userEmail, setUserEmail] = useState<string>('');
|
||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||
const { toast } = useToast();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
// Buscar dados do usuário logado
|
||||
const getUserData = async () => {
|
||||
const { data: { user } } = await supabase.auth.getUser();
|
||||
if (user?.email) {
|
||||
setUserEmail(user.email);
|
||||
}
|
||||
};
|
||||
|
||||
getUserData();
|
||||
}, []);
|
||||
|
||||
const handleLogout = async () => {
|
||||
try {
|
||||
const { error } = await supabase.auth.signOut();
|
||||
if (error) throw error;
|
||||
|
||||
// Limpar localStorage
|
||||
localStorage.removeItem('userEmail');
|
||||
|
||||
toast({
|
||||
title: "Logout realizado",
|
||||
description: "Você foi desconectado com sucesso"
|
||||
});
|
||||
|
||||
navigate('/auth');
|
||||
} catch (error) {
|
||||
console.error('Erro no logout:', error);
|
||||
toast({
|
||||
title: "Erro no logout",
|
||||
description: "Ocorreu um erro ao sair da conta",
|
||||
variant: "destructive"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<DropdownMenu open={dropdownOpen} onOpenChange={setDropdownOpen}>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
className="rounded-full py-0 ps-0 h-10 justify-start"
|
||||
variant="outline"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setDropdownOpen(!dropdownOpen);
|
||||
}}
|
||||
>
|
||||
<div className="me-2 flex aspect-square h-full p-1.5">
|
||||
<div className="h-full w-full rounded-full bg-blue-600 flex items-center justify-center">
|
||||
<User className="h-4 w-4 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
<span className="text-sm truncate">
|
||||
{userEmail || 'Usuário'}
|
||||
</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-56 z-50">
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setDropdownOpen(false);
|
||||
handleLogout();
|
||||
}}
|
||||
className="flex items-center gap-2 text-red-600 hover:text-red-700 hover:bg-red-50 cursor-pointer"
|
||||
>
|
||||
<LogOut className="h-4 w-4" />
|
||||
Sair
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserProfileButton;
|
||||
@ -4,6 +4,7 @@ import { ArrowLeft } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
import { supabase } from '@/integrations/supabase/client';
|
||||
import { enviarFaleConoscoParaWebhook } from '@/utils/webhookService';
|
||||
import ContactFormFields from './ContactFormFields';
|
||||
import ContactFormHeader from './ContactFormHeader';
|
||||
|
||||
@ -37,6 +38,15 @@ const ContactForm = ({ onBack }: ContactFormProps) => {
|
||||
|
||||
try {
|
||||
let anexoUrl = null;
|
||||
let userId = null;
|
||||
let userEmail = null;
|
||||
|
||||
// Obter dados do usuário
|
||||
const { data: { user } } = await supabase.auth.getUser();
|
||||
if (user) {
|
||||
userId = user.id;
|
||||
userEmail = user.email;
|
||||
}
|
||||
|
||||
// Upload do anexo se existe
|
||||
if (formData.anexo) {
|
||||
@ -64,12 +74,31 @@ const ContactForm = ({ onBack }: ContactFormProps) => {
|
||||
motivo: formData.motivo,
|
||||
mensagem: formData.mensagem,
|
||||
anexo_url: anexoUrl,
|
||||
user_id: (await supabase.auth.getUser()).data.user?.id,
|
||||
user_id: userId,
|
||||
status: 'pendente'
|
||||
});
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
// Enviar dados para o webhook do N8N
|
||||
const webhookData = {
|
||||
assunto: formData.assunto,
|
||||
motivo: formData.motivo,
|
||||
mensagem: formData.mensagem,
|
||||
anexo_url: anexoUrl,
|
||||
user_id: userId,
|
||||
user_email: userEmail,
|
||||
data_envio: new Date().toISOString()
|
||||
};
|
||||
|
||||
try {
|
||||
await enviarFaleConoscoParaWebhook(webhookData);
|
||||
console.log("Dados enviados para webhook N8N com sucesso");
|
||||
} catch (webhookError) {
|
||||
console.error("Erro ao enviar para webhook N8N:", webhookError);
|
||||
// Não interrompe o fluxo se o webhook falhar
|
||||
}
|
||||
|
||||
toast({
|
||||
title: "Mensagem enviada!",
|
||||
description: "Você vai receber a resposta no e-mail que cadastrou aqui. Obrigado!",
|
||||
|
||||
@ -78,10 +78,6 @@ export default function ModernLayout({ children }: ModernLayoutProps) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const location = useLocation();
|
||||
const isMobile = useIsMobile();
|
||||
const { toast } = useToast();
|
||||
const navigate = useNavigate();
|
||||
const [userEmail, setUserEmail] = useState<string>('');
|
||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||
|
||||
const {
|
||||
isOpen: tourOpen,
|
||||
@ -91,42 +87,6 @@ export default function ModernLayout({ children }: ModernLayoutProps) {
|
||||
closeTour
|
||||
} = useOnboardingTour();
|
||||
|
||||
useEffect(() => {
|
||||
// Buscar dados do usuário logado
|
||||
const getUserData = async () => {
|
||||
const { data: { user } } = await supabase.auth.getUser();
|
||||
if (user?.email) {
|
||||
setUserEmail(user.email);
|
||||
}
|
||||
};
|
||||
|
||||
getUserData();
|
||||
}, []);
|
||||
|
||||
const handleLogout = async () => {
|
||||
try {
|
||||
const { error } = await supabase.auth.signOut();
|
||||
if (error) throw error;
|
||||
|
||||
// Limpar localStorage
|
||||
localStorage.removeItem('userEmail');
|
||||
|
||||
toast({
|
||||
title: "Logout realizado",
|
||||
description: "Você foi desconectado com sucesso"
|
||||
});
|
||||
|
||||
navigate('/auth');
|
||||
} catch (error) {
|
||||
console.error('Erro no logout:', error);
|
||||
toast({
|
||||
title: "Erro no logout",
|
||||
description: "Ocorreu um erro ao sair da conta",
|
||||
variant: "destructive"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const mainLinks = [
|
||||
{
|
||||
id: "dashboard",
|
||||
@ -260,52 +220,6 @@ export default function ModernLayout({ children }: ModernLayoutProps) {
|
||||
className="space-y-1"
|
||||
iconContainerClassName={!open ? "justify-center" : ""}
|
||||
/>
|
||||
|
||||
{/* User Profile */}
|
||||
<div className="mt-4 px-3">
|
||||
<DropdownMenu open={dropdownOpen} onOpenChange={setDropdownOpen}>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
className={`w-full rounded-full py-0 ps-0 h-10 ${open ? 'justify-start' : 'justify-center px-0'}`}
|
||||
variant="outline"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setDropdownOpen(!dropdownOpen);
|
||||
}}
|
||||
>
|
||||
<div className={`${open ? 'me-2' : ''} flex aspect-square h-full p-1.5`}>
|
||||
<div className="h-full w-full rounded-full bg-blue-600 flex items-center justify-center">
|
||||
<User className="h-4 w-4 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
{open && (
|
||||
<span className="text-sm truncate">
|
||||
{userEmail || 'Usuário'}
|
||||
</span>
|
||||
)}
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
align={open ? "end" : "center"}
|
||||
side={open ? "top" : "right"}
|
||||
className="w-56 z-50"
|
||||
>
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setDropdownOpen(false);
|
||||
handleLogout();
|
||||
}}
|
||||
className="flex items-center gap-2 text-red-600 hover:text-red-700 hover:bg-red-50 cursor-pointer"
|
||||
>
|
||||
<LogOut className="h-4 w-4" />
|
||||
Sair
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import Layout from '@/components/layout/Layout';
|
||||
import SummaryCard from '@/components/dashboard/SummaryCard';
|
||||
import TransactionsTable from '@/components/dashboard/TransactionsTable';
|
||||
import CategoryChart from '@/components/dashboard/CategoryChart';
|
||||
import MonthlyChart from '@/components/dashboard/MonthlyChart';
|
||||
import UserProfileButton from '@/components/dashboard/UserProfileButton';
|
||||
import { MonthFilter } from '@/components/filters/MonthFilter';
|
||||
import { OnboardingTour, useOnboardingTour } from '@/components/onboarding';
|
||||
import { Wallet, ArrowUp, ArrowDown, PiggyBank } from 'lucide-react';
|
||||
@ -154,10 +154,13 @@ const Dashboard = () => {
|
||||
Dados de: {formatMonthDisplay(selectedMonth)}
|
||||
</p>
|
||||
</div>
|
||||
<MonthFilter
|
||||
selectedMonth={selectedMonth}
|
||||
onMonthChange={setSelectedMonth}
|
||||
/>
|
||||
<div className="flex items-center gap-4">
|
||||
<UserProfileButton />
|
||||
<MonthFilter
|
||||
selectedMonth={selectedMonth}
|
||||
onMonthChange={setSelectedMonth}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Resumo em cards */}
|
||||
|
||||
70
src/services/webhookService.ts
Normal file
70
src/services/webhookService.ts
Normal file
@ -0,0 +1,70 @@
|
||||
|
||||
// URL do webhook para notificação de novo cadastro
|
||||
const WEBHOOK_URL = "https://hook.us1.make.com/j6y1odto82tyo38qv3jh4579kkluhsx7";
|
||||
|
||||
// URL do webhook para fale conosco
|
||||
const FALE_CONOSCO_WEBHOOK_URL = "https://webhookn8n.innova1001.com.br/webhook/faleconosco";
|
||||
|
||||
interface UserData {
|
||||
nome: string;
|
||||
empresa?: string;
|
||||
email: string;
|
||||
whatsapp?: string;
|
||||
data_cadastro: string;
|
||||
origem: string;
|
||||
}
|
||||
|
||||
interface FaleConoscoData {
|
||||
assunto: string;
|
||||
motivo: string;
|
||||
mensagem: string;
|
||||
anexo_url?: string;
|
||||
user_id?: string;
|
||||
user_email?: string;
|
||||
data_envio: string;
|
||||
}
|
||||
|
||||
// Função para enviar dados do usuário para o webhook
|
||||
export const enviarDadosParaWebhook = async (dadosUsuario: UserData) => {
|
||||
try {
|
||||
console.log("Enviando dados do usuário para webhook:", dadosUsuario);
|
||||
|
||||
await fetch(WEBHOOK_URL, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
mode: 'no-cors', // Necessário para webhooks externos
|
||||
body: JSON.stringify(dadosUsuario),
|
||||
});
|
||||
|
||||
console.log("Dados enviados com sucesso para o webhook");
|
||||
} catch (error) {
|
||||
console.error("Erro ao enviar dados para o webhook:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// Função para enviar dados do Fale Conosco para o webhook do N8N
|
||||
export const enviarFaleConoscoParaWebhook = async (dadosFaleConosco: FaleConoscoData) => {
|
||||
try {
|
||||
console.log("Enviando dados do Fale Conosco para webhook N8N:", dadosFaleConosco);
|
||||
|
||||
const response = await fetch(FALE_CONOSCO_WEBHOOK_URL, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(dadosFaleConosco),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
console.log("Dados do Fale Conosco enviados com sucesso para o webhook N8N");
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Erro ao enviar dados do Fale Conosco para o webhook N8N:", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user