Run SQL to add WhatsApp columns
Adds `instancia_zap` and `status_instancia` columns to the `usuarios` table.
This commit is contained in:
parent
80cd8a1517
commit
417c24089b
@ -1,3 +1,4 @@
|
||||
|
||||
import * as React from "react"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
);
|
||||
|
||||
@ -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: []
|
||||
|
||||
81
src/services/whatsAppGroupCreationService.ts
Normal file
81
src/services/whatsAppGroupCreationService.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
58
src/services/whatsAppInstanceService.ts
Normal file
58
src/services/whatsAppInstanceService.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user