Run SQL to add WhatsApp columns

Adds `instancia_zap` and `status_instancia` columns to the `usuarios` table.
This commit is contained in:
gpt-engineer-app[bot] 2025-05-24 14:02:02 +00:00
parent 80cd8a1517
commit 417c24089b
6 changed files with 299 additions and 72 deletions

View File

@ -1,3 +1,4 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"

View File

@ -7,6 +7,7 @@ import { Label } from '@/components/ui/label';
import { MessageCircle } from 'lucide-react';
import { useToast } from '@/hooks/use-toast';
import { createWhatsAppInstance } from '@/services/whatsAppService';
import { updateUserWhatsAppInstance } from '@/services/whatsAppInstanceService';
import { WhatsAppInstance } from '@/types/whatsAppTypes';
interface CreateInstanceFormProps {
@ -20,23 +21,19 @@ const CreateInstanceForm = ({
}: CreateInstanceFormProps) => {
const { toast } = useToast();
const [loading, setLoading] = useState(false);
const [instanceName, setInstanceName] = useState(initialInstanceName || '');
const [phoneNumber, setPhoneNumber] = useState('');
const currentUserId = localStorage.getItem('userId') || '';
const userEmail = localStorage.getItem('userEmail') || '';
// Update instance name if initial value changes
useEffect(() => {
if (initialInstanceName) {
setInstanceName(initialInstanceName);
}
}, [initialInstanceName]);
// Nome da instância será sempre o email do usuário
const instanceName = userEmail;
const handleCreateInstance = async () => {
// Validate form fields
if (!instanceName.trim()) {
if (!userEmail) {
toast({
title: "Erro",
description: "O nome da instância é obrigatório",
description: "Email do usuário não encontrado. Faça login novamente.",
variant: "destructive",
});
return;
@ -82,6 +79,15 @@ const CreateInstanceForm = ({
console.log('New instance created:', 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
}
// Notify parent component about the new instance
onInstanceCreated(newInstance);
@ -122,11 +128,12 @@ const CreateInstanceForm = ({
<Input
id="instanceName"
value={instanceName}
onChange={(e) => setInstanceName(e.target.value)}
placeholder="Digite um nome para a instância"
required
disabled
className="bg-gray-100"
/>
<p className="text-xs text-muted-foreground">O nome será usado para identificar esta conexão</p>
<p className="text-xs text-muted-foreground">
O nome da instância será automaticamente seu email de login
</p>
</div>
<div className="space-y-2">
@ -144,7 +151,7 @@ const CreateInstanceForm = ({
<Button
className="w-full mt-4"
onClick={handleCreateInstance}
disabled={loading}
disabled={loading || !userEmail || !phoneNumber.trim()}
>
{loading ? "Criando..." : "Criar Instância"}
</Button>

View File

@ -1,11 +1,14 @@
import { useState } from 'react';
import { useState, useEffect } from 'react';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Loader2, Plus } from 'lucide-react';
import { Loader2, Plus, AlertCircle } from 'lucide-react';
import { Alert, AlertDescription } from '@/components/ui/alert';
import { cadastrarGrupoWhatsApp } from '@/services/gruposWhatsAppService';
import { getUserWhatsAppInstance } from '@/services/whatsAppInstanceService';
import { createWhatsAppGroup, updateGroupRemoteJid } from '@/services/whatsAppGroupCreationService';
import { useToast } from '@/hooks/use-toast';
interface CreateGroupFormProps {
@ -17,14 +20,45 @@ const CreateGroupForm = ({ userEmail, onSuccess }: CreateGroupFormProps) => {
const { toast } = useToast();
const [cadastrando, setCadastrando] = useState<boolean>(false);
const [nomeGrupo, setNomeGrupo] = useState<string>('');
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const [debugInfo, setDebugInfo] = useState<string | null>(null);
const [hasWhatsAppInstance, setHasWhatsAppInstance] = useState<boolean>(false);
const [checkingInstance, setCheckingInstance] = useState<boolean>(true);
const [userInstance, setUserInstance] = useState<{
instancia_zap: string | null;
status_instancia: string | null;
whatsapp: string | null;
} | null>(null);
// Cadastrar novo grupo ou atualizar workflow se necessário
// Verificar se o usuário tem instância WhatsApp
useEffect(() => {
const checkUserInstance = async () => {
if (!userEmail) return;
setCheckingInstance(true);
try {
const instanceData = await getUserWhatsAppInstance(userEmail);
console.log('Dados da instância do usuário:', instanceData);
if (instanceData && instanceData.instancia_zap && instanceData.instancia_zap.trim() !== '') {
setHasWhatsAppInstance(true);
setUserInstance(instanceData);
} else {
setHasWhatsAppInstance(false);
setUserInstance(null);
}
} catch (error) {
console.error('Erro ao verificar instância do usuário:', error);
setHasWhatsAppInstance(false);
setUserInstance(null);
} finally {
setCheckingInstance(false);
}
};
checkUserInstance();
}, [userEmail]);
// Cadastrar novo grupo
const handleCadastrarGrupo = async () => {
setErrorMessage(null);
setDebugInfo(null);
if (!userEmail) {
toast({
title: 'Erro',
@ -43,61 +77,78 @@ const CreateGroupForm = ({ userEmail, onSuccess }: CreateGroupFormProps) => {
return;
}
if (!userInstance || !userInstance.instancia_zap || !userInstance.whatsapp) {
toast({
title: 'Erro',
description: 'Dados da instância WhatsApp não encontrados',
variant: 'destructive',
});
return;
}
setCadastrando(true);
try {
console.log("Iniciando processo de cadastro de grupo...");
// 1. Cadastrar grupo no banco de dados local
const grupo = await cadastrarGrupoWhatsApp(nomeGrupo.trim());
if (grupo) {
let successMessage = 'Grupo registrado com sucesso';
let variant: 'default' | 'destructive' = 'default';
if (!grupo) {
throw new Error('Não foi possível cadastrar o grupo no banco de dados');
}
// 2. Criar grupo no WhatsApp via API
try {
const groupResponse = await createWhatsAppGroup(
userInstance.instancia_zap,
userEmail,
userInstance.whatsapp
);
if (grupo.workflow_id) {
successMessage = 'Grupo cadastrado e workflow criado com sucesso!';
console.log('Resposta da criação do grupo:', groupResponse);
// 3. Atualizar remote_jid no banco de dados
if (groupResponse.id) {
await updateGroupRemoteJid(grupo.id, groupResponse.id);
toast({
title: 'Sucesso!',
description: `Grupo "${groupResponse.subject}" criado com sucesso no seu WhatsApp!`,
variant: 'default',
});
} else {
successMessage = 'Grupo cadastrado, mas falha ao criar workflow de automação.';
setDebugInfo('O grupo foi criado no Supabase, mas houve um problema ao criar o workflow no n8n. Verifique os logs do console para mais detalhes.');
variant = 'destructive';
toast({
title: 'Atenção',
description: 'Grupo cadastrado no sistema, mas não foi possível criar no WhatsApp',
variant: 'destructive',
});
}
} catch (apiError) {
console.error('Erro ao criar grupo via API:', apiError);
toast({
title: grupo.workflow_id ? 'Sucesso' : 'Atenção',
description: successMessage,
variant: variant,
});
// Resetar o campo de nome
setNomeGrupo('');
// Atualizar a lista de grupos
onSuccess();
} else {
setErrorMessage('Não foi possível registrar o grupo. Verifique a conexão com o Supabase.');
toast({
title: 'Erro',
description: 'Não foi possível registrar o grupo',
title: 'Atenção',
description: 'Grupo cadastrado no sistema, mas houve erro ao criar no WhatsApp. Verifique sua conexão.',
variant: 'destructive',
});
}
// Resetar o campo de nome
setNomeGrupo('');
// Atualizar a lista de grupos
onSuccess();
} catch (error) {
console.error('Erro ao cadastrar grupo:', error);
let errorMsg = 'Erro desconhecido';
if (error instanceof Error) {
errorMsg = error.message;
// Tentar extrair detalhes específicos do erro se existirem
if (errorMsg.includes('Status')) {
const statusMatch = errorMsg.match(/Status (\d+)/);
if (statusMatch && statusMatch[1]) {
setDebugInfo(`Código de status HTTP da API: ${statusMatch[1]}`);
}
}
}
setErrorMessage('Erro ao cadastrar grupo: ' + errorMsg);
toast({
title: 'Erro',
description: 'Não foi possível registrar o grupo',
description: `Não foi possível registrar o grupo: ${errorMsg}`,
variant: 'destructive',
});
} finally {
@ -105,6 +156,39 @@ const CreateGroupForm = ({ userEmail, onSuccess }: CreateGroupFormProps) => {
}
};
if (checkingInstance) {
return (
<Card>
<CardContent className="flex items-center justify-center py-8">
<Loader2 className="h-6 w-6 animate-spin mr-2" />
<span>Verificando instância WhatsApp...</span>
</CardContent>
</Card>
);
}
if (!hasWhatsAppInstance) {
return (
<Card>
<CardHeader>
<CardTitle>Cadastrar novo grupo</CardTitle>
<CardDescription>
Para criar um grupo é necessário ter uma instância do WhatsApp conectada
</CardDescription>
</CardHeader>
<CardContent>
<Alert>
<AlertCircle className="h-4 w-4" />
<AlertDescription>
Para criar um grupo é necessário cadastrar e conectar sua instância do WhatsApp.
Acesse o menu de conexão e realize este processo primeiro.
</AlertDescription>
</Alert>
</CardContent>
</Card>
);
}
return (
<Card>
<CardHeader>
@ -129,7 +213,7 @@ const CreateGroupForm = ({ userEmail, onSuccess }: CreateGroupFormProps) => {
<Button
onClick={handleCadastrarGrupo}
disabled={cadastrando || !userEmail || !nomeGrupo.trim()}
disabled={cadastrando || !userEmail || !nomeGrupo.trim() || !hasWhatsAppInstance}
className="w-full"
>
{cadastrando ? (
@ -146,23 +230,13 @@ const CreateGroupForm = ({ userEmail, onSuccess }: CreateGroupFormProps) => {
</Button>
<div className="mt-4 border-t pt-4">
<h3 className="font-medium mb-2">Após cadastrar:</h3>
<ol className="list-decimal list-inside space-y-2">
<li>Adicione o número (61)99244-4275 ao grupo do WhatsApp que deseja automatizar</li>
<li>Envie uma mensagem neste grupo com o seguinte texto:</li>
</ol>
<div className="bg-muted p-3 rounded-md font-mono mt-2">
{userEmail}
</div>
<p className="text-sm text-muted-foreground mt-2">
Copie e cole o email exatamente como aparece acima
</p>
<h3 className="font-medium mb-2">Informações importantes:</h3>
<ul className="list-disc list-inside space-y-1 text-sm text-muted-foreground">
<li>O grupo será criado automaticamente no seu WhatsApp</li>
<li>Você será adicionado como participante do grupo</li>
<li>O grupo terá o nome: FinDash - {userEmail.split('@')[0]}</li>
</ul>
</div>
{errorMessage && <p className="text-destructive text-sm mt-4">{errorMessage}</p>}
{debugInfo && <p className="text-amber-800 text-sm mt-2 bg-amber-50 p-3 rounded-md">{debugInfo}</p>}
</CardContent>
</Card>
);

View File

@ -293,9 +293,11 @@ export type Database = {
email: string
empresa: string | null
id: string
instancia_zap: string | null
nome: string
remote_jid: string | null
senha: string
status_instancia: string | null
whatsapp: string
}
Insert: {
@ -303,9 +305,11 @@ export type Database = {
email: string
empresa?: string | null
id?: string
instancia_zap?: string | null
nome: string
remote_jid?: string | null
senha: string
status_instancia?: string | null
whatsapp: string
}
Update: {
@ -313,9 +317,11 @@ export type Database = {
email?: string
empresa?: string | null
id?: string
instancia_zap?: string | null
nome?: string
remote_jid?: string | null
senha?: string
status_instancia?: string | null
whatsapp?: string
}
Relationships: []

View File

@ -0,0 +1,81 @@
import { supabase } from "@/integrations/supabase/client";
interface CreateGroupResponse {
id: string;
subject: string;
description?: string;
participants?: string[];
}
/**
* Cria um grupo WhatsApp via API da Evolution
*/
export async function createWhatsAppGroup(
instanceName: string,
userEmail: string,
userPhone: string
): Promise<CreateGroupResponse> {
try {
// Extrair a parte antes do @ do email
const emailPrefix = userEmail.split('@')[0];
const groupSubject = `FinDash - ${emailPrefix}`;
const url = `https://evolutionapi2.innova1001.com.br/group/create/${instanceName}`;
const requestBody = {
subject: groupSubject,
description: "Seu sistema financeiro Inteligente e Pratico",
participants: [userPhone]
};
console.log('Criando grupo WhatsApp:', { url, requestBody });
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody)
});
if (!response.ok) {
const errorText = await response.text();
console.error('Erro na resposta da API:', response.status, errorText);
throw new Error(`Falha ao criar grupo: ${response.status} - ${errorText}`);
}
const data = await response.json();
console.log('Grupo criado com sucesso:', data);
return data;
} catch (error) {
console.error('Erro ao criar grupo WhatsApp:', error);
throw error;
}
}
/**
* Atualiza o remote_jid do grupo no banco de dados
*/
export async function updateGroupRemoteJid(
groupId: number,
remoteJid: string
): Promise<void> {
try {
const { error } = await supabase
.from('grupos_whatsapp')
.update({ remote_jid: remoteJid })
.eq('id', groupId);
if (error) {
console.error('Erro ao atualizar remote_jid:', error);
throw error;
}
console.log(`Remote JID atualizado para o grupo ${groupId}: ${remoteJid}`);
} catch (error) {
console.error('Erro ao atualizar remote_jid no banco:', error);
throw error;
}
}

View File

@ -0,0 +1,58 @@
import { supabase } from "@/integrations/supabase/client";
/**
* Atualiza a instância WhatsApp do usuário no banco de dados
*/
export async function updateUserWhatsAppInstance(
userEmail: string,
instanceName: string,
status: 'conectado' | 'desconectado'
): Promise<void> {
try {
const { error } = await supabase
.from('usuarios')
.update({
instancia_zap: instanceName,
status_instancia: status
})
.eq('email', userEmail.trim().toLowerCase());
if (error) {
console.error('Erro ao atualizar instância WhatsApp:', error);
throw error;
}
console.log(`Instância WhatsApp atualizada: ${instanceName} - ${status}`);
} catch (error) {
console.error('Erro ao atualizar instância WhatsApp no banco:', error);
throw error;
}
}
/**
* Busca a instância WhatsApp do usuário
*/
export async function getUserWhatsAppInstance(userEmail: string): Promise<{
instancia_zap: string | null;
status_instancia: string | null;
whatsapp: string | null;
} | null> {
try {
const { data, error } = await supabase
.from('usuarios')
.select('instancia_zap, status_instancia, whatsapp')
.eq('email', userEmail.trim().toLowerCase())
.single();
if (error) {
console.error('Erro ao buscar instância WhatsApp:', error);
return null;
}
return data;
} catch (error) {
console.error('Erro ao buscar instância WhatsApp:', error);
return null;
}
}