feat: Save WhatsApp instance on creation

- Save the WhatsApp instance name to the 'instancia_zap' column in the 'usuarios' table when a new instance is created.
- Update the instance status based on connection status.
- Prevent the creation of multiple instances per user.
This commit is contained in:
gpt-engineer-app[bot] 2025-05-24 14:15:57 +00:00
parent ead6400404
commit c004980163
3 changed files with 214 additions and 38 deletions

View File

@ -4,11 +4,12 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/com
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import { Label } from '@/components/ui/label';
import { MessageCircle } from 'lucide-react';
import { MessageCircle, AlertCircle } from 'lucide-react';
import { useToast } from '@/hooks/use-toast';
import { createWhatsAppInstance } from '@/services/whatsAppService';
import { updateUserWhatsAppInstance } from '@/services/whatsAppInstanceService';
import { updateUserWhatsAppInstance, getUserWhatsAppInstance } from '@/services/whatsAppInstanceService';
import { WhatsAppInstance } from '@/types/whatsAppTypes';
import { Alert, AlertDescription } from '@/components/ui/alert';
interface CreateInstanceFormProps {
onInstanceCreated: (instance: WhatsAppInstance) => void;
@ -22,12 +23,44 @@ const CreateInstanceForm = ({
const { toast } = useToast();
const [loading, setLoading] = useState(false);
const [phoneNumber, setPhoneNumber] = useState('');
const [hasExistingInstance, setHasExistingInstance] = useState(false);
const [checkingExistingInstance, setCheckingExistingInstance] = useState(true);
const currentUserId = localStorage.getItem('userId') || '';
const userEmail = localStorage.getItem('userEmail') || '';
// Nome da instância será sempre o email do usuário
const instanceName = userEmail;
// Verificar se o usuário já tem uma instância
useEffect(() => {
const checkExistingInstance = async () => {
if (!userEmail) {
setCheckingExistingInstance(false);
return;
}
try {
console.log('Verificando se usuário já tem instância:', userEmail);
const existingInstance = await getUserWhatsAppInstance(userEmail);
if (existingInstance && existingInstance.instancia_zap) {
console.log('Usuário já possui instância:', existingInstance.instancia_zap);
setHasExistingInstance(true);
} else {
console.log('Usuário não possui instância');
setHasExistingInstance(false);
}
} catch (error) {
console.error('Erro ao verificar instância existente:', error);
setHasExistingInstance(false);
} finally {
setCheckingExistingInstance(false);
}
};
checkExistingInstance();
}, [userEmail]);
const handleCreateInstance = async () => {
// Validate form fields
if (!userEmail) {
@ -58,37 +91,46 @@ const CreateInstanceForm = ({
return;
}
// Verificar novamente se já existe instância antes de criar
if (hasExistingInstance) {
toast({
title: "Erro",
description: "Você já possui uma instância WhatsApp. Apenas uma instância por usuário é permitida.",
variant: "destructive",
});
return;
}
setLoading(true);
try {
console.log(`Creating instance with name ${instanceName} and number ${phoneNumber}`);
const data = await createWhatsAppInstance(instanceName, phoneNumber);
console.log(`Criando instância com nome ${instanceName} e número ${phoneNumber}`);
console.log('API response for create instance:', data);
// 1. Primeiro atualizar o banco de dados com a instância
await updateUserWhatsAppInstance(userEmail, instanceName, 'desconectado');
console.log('Instância registrada no banco de dados');
// Create new instance object with user ID
// 2. Criar instância na API
const data = await createWhatsAppInstance(instanceName, phoneNumber);
console.log('Resposta da API de criação de instância:', data);
// 3. Criar objeto da instância
const newInstance: WhatsAppInstance = {
instanceName,
instanceId: instanceName, // Use instanceName as the ID for consistency
instanceId: instanceName,
phoneNumber,
userId: currentUserId, // Associate with current user
userId: currentUserId,
status: data.instance?.status || 'created',
qrcode: data.qrcode?.base64 || null,
connectionState: 'closed' // Default to closed/disconnected
connectionState: 'closed'
};
console.log('New instance created:', newInstance);
console.log('Nova instância criada:', newInstance);
// Atualizar o banco de dados com a instância
try {
await updateUserWhatsAppInstance(userEmail, instanceName, 'desconectado');
console.log('Instância salva no banco de dados');
} catch (dbError) {
console.error('Erro ao salvar instância no banco:', dbError);
// Não bloquear o processo se falhar ao salvar no banco
}
// 4. Atualizar estado para evitar nova criação
setHasExistingInstance(true);
// Notify parent component about the new instance
// 5. Notificar componente pai
onInstanceCreated(newInstance);
toast({
@ -100,7 +142,16 @@ const CreateInstanceForm = ({
setPhoneNumber('');
} catch (error) {
console.error("Error creating WhatsApp instance:", error);
console.error("Erro ao criar instância WhatsApp:", error);
// Se houve erro na API, remover do banco de dados
try {
await updateUserWhatsAppInstance(userEmail, '', 'desconectado');
console.log('Instância removida do banco devido ao erro na API');
} catch (dbError) {
console.error('Erro ao remover instância do banco:', dbError);
}
toast({
title: "Erro na criação da instância",
description: "Ocorreu um erro ao conectar com a API. Tente novamente mais tarde.",
@ -111,6 +162,53 @@ const CreateInstanceForm = ({
}
};
if (checkingExistingInstance) {
return (
<Card>
<CardContent className="flex items-center justify-center py-8">
<div className="text-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-green-600 mx-auto mb-4"></div>
<p>Verificando instâncias existentes...</p>
</div>
</CardContent>
</Card>
);
}
if (hasExistingInstance) {
return (
<Card>
<CardHeader>
<div className="flex items-center mb-2">
<MessageCircle className="h-6 w-6 mr-2 text-green-600" />
<CardTitle>WhatsApp Conectado</CardTitle>
</div>
<CardDescription>
Você possui uma instância WhatsApp vinculada ao seu email
</CardDescription>
</CardHeader>
<CardContent>
<Alert>
<AlertCircle className="h-4 w-4" />
<AlertDescription>
Apenas uma instância WhatsApp por usuário é permitida.
Sua instância atual: <strong>{instanceName}</strong>
</AlertDescription>
</Alert>
<div className="mt-4 p-4 bg-green-50 border border-green-200 rounded">
<p className="text-green-800 text-sm">
<strong> Instância ativa:</strong> {instanceName}
</p>
<p className="text-green-700 text-sm mt-1">
Para gerenciar sua instância, utilize os botões na lista de instâncias acima.
</p>
</div>
</CardContent>
</Card>
);
}
return (
<Card>
<CardHeader>
@ -155,6 +253,15 @@ const CreateInstanceForm = ({
>
{loading ? "Criando..." : "Criar Instância"}
</Button>
<div className="mt-4 p-4 bg-blue-50 border border-blue-200 rounded">
<h4 className="font-medium text-blue-800 mb-2">Importante:</h4>
<ul className="text-sm text-blue-700 space-y-1">
<li> Apenas uma instância por usuário é permitida</li>
<li> O nome da instância será seu email de login</li>
<li> Após criar, você precisará escanear o QR Code para conectar</li>
</ul>
</div>
</CardContent>
</Card>
);

View File

@ -3,6 +3,7 @@ import { useState, useEffect } from 'react';
import { useToast } from '@/hooks/use-toast';
import { WhatsAppInstance } from '@/types/whatsAppTypes';
import { fetchSpecificInstance } from '@/services/whatsApp/instanceManagement';
import { getUserWhatsAppInstance } from '@/services/whatsAppInstanceService';
// Key used for storing instance name in localStorage
export const WHATSAPP_INSTANCE_KEY = 'whatsapp_instance_name';
@ -12,19 +13,47 @@ export const useWhatsAppInstance = (
addInstance: (instance: WhatsAppInstance) => void
) => {
const { toast } = useToast();
// Get instance name from localStorage
const [instanceName, setInstanceName] = useState(() => {
if (currentUserId) {
return localStorage.getItem(`${WHATSAPP_INSTANCE_KEY}_${currentUserId}`) || '';
}
return '';
});
const userEmail = localStorage.getItem('userEmail') || '';
// Get instance name from database/localStorage
const [instanceName, setInstanceName] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [instanceFound, setInstanceFound] = useState(false);
const [shouldShowToast, setShouldShowToast] = useState(false);
const [toastMessage, setToastMessage] = useState({ title: '', description: '', variant: '' as any });
// Load instance name from database on component mount
useEffect(() => {
const loadInstanceFromDatabase = async () => {
if (!userEmail || !currentUserId) return;
try {
console.log('Carregando instância do banco de dados para:', userEmail);
const instanceData = await getUserWhatsAppInstance(userEmail);
if (instanceData && instanceData.instancia_zap) {
console.log('Instância encontrada no banco:', instanceData.instancia_zap);
setInstanceName(instanceData.instancia_zap);
setInstanceFound(true);
// Also save to localStorage for consistency
localStorage.setItem(`${WHATSAPP_INSTANCE_KEY}_${currentUserId}`, instanceData.instancia_zap);
} else {
console.log('Nenhuma instância encontrada no banco');
setInstanceFound(false);
// Clear localStorage if no instance in database
localStorage.removeItem(`${WHATSAPP_INSTANCE_KEY}_${currentUserId}`);
}
} catch (error) {
console.error('Erro ao carregar instância do banco:', error);
setInstanceFound(false);
}
};
loadInstanceFromDatabase();
}, [userEmail, currentUserId]);
// Function to fetch specific instance by name
const fetchInstanceByName = async () => {
// Skip if no instance name
@ -41,9 +70,9 @@ export const useWhatsAppInstance = (
setIsLoading(true);
try {
console.log(`Fetching specific instance: ${instanceName}`);
console.log(`Buscando instância específica: ${instanceName}`);
const data = await fetchSpecificInstance(instanceName);
console.log('Fetch specific instance response:', data);
console.log('Resposta da busca de instância específica:', data);
if (data && data.instance) {
// Instance found, create or update instance object
@ -57,7 +86,7 @@ export const useWhatsAppInstance = (
qrcode: data.qrcode?.base64 || null
};
console.log('Found instance:', foundInstance);
console.log('Instância encontrada:', foundInstance);
addInstance(foundInstance);
setInstanceFound(true);
@ -70,10 +99,10 @@ export const useWhatsAppInstance = (
setShouldShowToast(true);
} else {
setInstanceFound(false);
console.log(`Instance ${instanceName} not found`);
console.log(`Instância ${instanceName} não encontrada`);
}
} catch (error) {
console.error('Error fetching specific instance:', error);
console.error('Erro ao buscar instância específica:', error);
setInstanceFound(false);
// Set toast data instead of calling toast directly
@ -100,7 +129,7 @@ export const useWhatsAppInstance = (
}
}, [shouldShowToast, toastMessage, toast]);
// Save instance name to localStorage
// Save instance name to localStorage and database
const saveInstanceName = (name: string) => {
if (currentUserId) {
localStorage.setItem(`${WHATSAPP_INSTANCE_KEY}_${currentUserId}`, name);
@ -108,7 +137,7 @@ export const useWhatsAppInstance = (
}
};
// Clear instance name from localStorage
// Clear instance name from localStorage and database
const clearInstanceName = () => {
if (currentUserId) {
localStorage.removeItem(`${WHATSAPP_INSTANCE_KEY}_${currentUserId}`);
@ -117,8 +146,6 @@ export const useWhatsAppInstance = (
}
};
// Don't automatically load the instance - removed the useEffect that called fetchInstanceByName
return {
instanceName,
isLoading,

View File

@ -10,6 +10,8 @@ export async function updateUserWhatsAppInstance(
status: 'conectado' | 'desconectado'
): Promise<void> {
try {
console.log(`Atualizando instância no banco: ${userEmail} -> ${instanceName} (${status})`);
const { error } = await supabase
.from('usuarios')
.update({
@ -23,7 +25,7 @@ export async function updateUserWhatsAppInstance(
throw error;
}
console.log(`Instância WhatsApp atualizada: ${instanceName} - ${status}`);
console.log(`Instância WhatsApp atualizada com sucesso: ${instanceName} - ${status}`);
} catch (error) {
console.error('Erro ao atualizar instância WhatsApp no banco:', error);
throw error;
@ -45,7 +47,7 @@ export async function getUserWhatsAppInstance(userEmail: string): Promise<{
.from('usuarios')
.select('instancia_zap, status_instancia, whatsapp')
.eq('email', userEmail.trim().toLowerCase())
.maybeSingle(); // Use maybeSingle em vez de single para evitar erro quando não encontrar
.maybeSingle();
if (error) {
console.error('Erro ao buscar instância WhatsApp:', error);
@ -59,3 +61,43 @@ export async function getUserWhatsAppInstance(userEmail: string): Promise<{
return null;
}
}
/**
* Verifica se o usuário possui uma instância ativa
*/
export async function checkUserHasInstance(userEmail: string): Promise<boolean> {
try {
const instanceData = await getUserWhatsAppInstance(userEmail);
return !!(instanceData && instanceData.instancia_zap && instanceData.instancia_zap.trim() !== '');
} catch (error) {
console.error('Erro ao verificar se usuário tem instância:', error);
return false;
}
}
/**
* Remove a instância WhatsApp do usuário
*/
export async function removeUserWhatsAppInstance(userEmail: string): Promise<void> {
try {
console.log(`Removendo instância do usuário: ${userEmail}`);
const { error } = await supabase
.from('usuarios')
.update({
instancia_zap: null,
status_instancia: 'desconectado'
})
.eq('email', userEmail.trim().toLowerCase());
if (error) {
console.error('Erro ao remover instância WhatsApp:', error);
throw error;
}
console.log('Instância WhatsApp removida com sucesso');
} catch (error) {
console.error('Erro ao remover instância WhatsApp do banco:', error);
throw error;
}
}