Run SQL to add WhatsApp group features.
This commit is contained in:
parent
5a16246b23
commit
8435c03464
@ -8,6 +8,7 @@ import NotFound from '@/pages/NotFound';
|
||||
import Auth from '@/pages/Auth';
|
||||
import Metas from '@/pages/Metas';
|
||||
import WhatsApp from '@/pages/WhatsApp';
|
||||
import GruposWhatsApp from '@/pages/GruposWhatsApp';
|
||||
import { Toaster } from '@/components/ui/toaster';
|
||||
import './App.css';
|
||||
import ProtectedRoute from '@/components/auth/ProtectedRoute';
|
||||
@ -47,6 +48,11 @@ function App() {
|
||||
<WhatsApp />
|
||||
</ProtectedRoute>
|
||||
} />
|
||||
<Route path="/grupos-whatsapp" element={
|
||||
<ProtectedRoute>
|
||||
<GruposWhatsApp />
|
||||
</ProtectedRoute>
|
||||
} />
|
||||
<Route path="/login" element={<Auth />} />
|
||||
<Route path="/auth" element={<Auth />} />
|
||||
<Route path="*" element={<NotFound />} />
|
||||
|
||||
@ -3,9 +3,9 @@ import React from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight, Home, CalendarDays, DollarSign, PieChart, Flag, MessageCircle } from 'lucide-react';
|
||||
import { ChevronLeft, ChevronRight, Home, CalendarDays, DollarSign, PieChart, Flag, MessageCircle, Users } from 'lucide-react';
|
||||
|
||||
// Updated navItems with the new WhatsApp connection page
|
||||
// Atualizado para incluir a página de grupos do WhatsApp
|
||||
const navItems = [
|
||||
{
|
||||
name: 'Dashboard',
|
||||
@ -37,6 +37,11 @@ const navItems = [
|
||||
path: '/whatsapp',
|
||||
icon: <MessageCircle className="mr-2 h-5 w-5" />
|
||||
},
|
||||
{
|
||||
name: 'Grupos WhatsApp',
|
||||
path: '/grupos-whatsapp',
|
||||
icon: <Users className="mr-2 h-5 w-5" />
|
||||
},
|
||||
];
|
||||
|
||||
interface SidebarProps {
|
||||
|
||||
@ -5,19 +5,19 @@ import { cva, type VariantProps } from "class-variance-authority"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const badgeVariants = cva(
|
||||
"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
||||
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
|
||||
"border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
|
||||
secondary:
|
||||
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
destructive:
|
||||
"border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
|
||||
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
||||
outline: "text-foreground",
|
||||
success:
|
||||
"border-transparent bg-finance-green text-white shadow hover:bg-finance-green/80",
|
||||
"border-transparent bg-green-500 text-white hover:bg-green-600",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
@ -124,22 +124,28 @@ export type Database = {
|
||||
Row: {
|
||||
created_at: string | null
|
||||
id: number
|
||||
login: string | null
|
||||
nome_grupo: string | null
|
||||
remote_jid: string
|
||||
status: string | null
|
||||
user_id: string
|
||||
}
|
||||
Insert: {
|
||||
created_at?: string | null
|
||||
id?: number
|
||||
login?: string | null
|
||||
nome_grupo?: string | null
|
||||
remote_jid: string
|
||||
status?: string | null
|
||||
user_id: string
|
||||
}
|
||||
Update: {
|
||||
created_at?: string | null
|
||||
id?: number
|
||||
login?: string | null
|
||||
nome_grupo?: string | null
|
||||
remote_jid?: string
|
||||
status?: string | null
|
||||
user_id?: string
|
||||
}
|
||||
Relationships: []
|
||||
@ -249,138 +255,6 @@ export type Database = {
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
"transacoes_5f8e8181-e850-4551-88e4-bbbdc64a57ca": {
|
||||
Row: {
|
||||
categoria: string | null
|
||||
created_at: string
|
||||
detalhes: string | null
|
||||
estabelecimento: string | null
|
||||
id: number
|
||||
quando: string | null
|
||||
tipo: string | null
|
||||
usuario_id: string | null
|
||||
valor: number | null
|
||||
}
|
||||
Insert: {
|
||||
categoria?: string | null
|
||||
created_at?: string
|
||||
detalhes?: string | null
|
||||
estabelecimento?: string | null
|
||||
id?: number
|
||||
quando?: string | null
|
||||
tipo?: string | null
|
||||
usuario_id?: string | null
|
||||
valor?: number | null
|
||||
}
|
||||
Update: {
|
||||
categoria?: string | null
|
||||
created_at?: string
|
||||
detalhes?: string | null
|
||||
estabelecimento?: string | null
|
||||
id?: number
|
||||
quando?: string | null
|
||||
tipo?: string | null
|
||||
usuario_id?: string | null
|
||||
valor?: number | null
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "transacoes_5f8e8181-e850-4551-88e4-bbbdc64a57ca_usuario_id_fkey"
|
||||
columns: ["usuario_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "usuarios"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
]
|
||||
}
|
||||
"transacoes_9f267008-9128-4a2f-b730-de0a0b5602a9": {
|
||||
Row: {
|
||||
categoria: string | null
|
||||
created_at: string
|
||||
detalhes: string | null
|
||||
estabelecimento: string | null
|
||||
id: number
|
||||
quando: string | null
|
||||
tipo: string | null
|
||||
usuario_id: string | null
|
||||
valor: number | null
|
||||
}
|
||||
Insert: {
|
||||
categoria?: string | null
|
||||
created_at?: string
|
||||
detalhes?: string | null
|
||||
estabelecimento?: string | null
|
||||
id?: number
|
||||
quando?: string | null
|
||||
tipo?: string | null
|
||||
usuario_id?: string | null
|
||||
valor?: number | null
|
||||
}
|
||||
Update: {
|
||||
categoria?: string | null
|
||||
created_at?: string
|
||||
detalhes?: string | null
|
||||
estabelecimento?: string | null
|
||||
id?: number
|
||||
quando?: string | null
|
||||
tipo?: string | null
|
||||
usuario_id?: string | null
|
||||
valor?: number | null
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "transacoes_9f267008-9128-4a2f-b730-de0a0b5602a9_usuario_id_fkey"
|
||||
columns: ["usuario_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "usuarios"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
]
|
||||
}
|
||||
"transacoes_d2ca2174-fb8f-41d0-9897-a71a4af474a8": {
|
||||
Row: {
|
||||
categoria: string | null
|
||||
created_at: string
|
||||
detalhes: string | null
|
||||
estabelecimento: string | null
|
||||
id: number
|
||||
quando: string | null
|
||||
tipo: string | null
|
||||
usuario_id: string | null
|
||||
valor: number | null
|
||||
}
|
||||
Insert: {
|
||||
categoria?: string | null
|
||||
created_at?: string
|
||||
detalhes?: string | null
|
||||
estabelecimento?: string | null
|
||||
id?: number
|
||||
quando?: string | null
|
||||
tipo?: string | null
|
||||
usuario_id?: string | null
|
||||
valor?: number | null
|
||||
}
|
||||
Update: {
|
||||
categoria?: string | null
|
||||
created_at?: string
|
||||
detalhes?: string | null
|
||||
estabelecimento?: string | null
|
||||
id?: number
|
||||
quando?: string | null
|
||||
tipo?: string | null
|
||||
usuario_id?: string | null
|
||||
valor?: number | null
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "transacoes_d2ca2174-fb8f-41d0-9897-a71a4af474a8_usuario_id_fkey"
|
||||
columns: ["usuario_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "usuarios"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
]
|
||||
}
|
||||
usuarios: {
|
||||
Row: {
|
||||
created_at: string
|
||||
|
||||
205
src/pages/GruposWhatsApp.tsx
Normal file
205
src/pages/GruposWhatsApp.tsx
Normal file
@ -0,0 +1,205 @@
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import Layout from '@/components/layout/Layout';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Loader2, Plus, RefreshCw, MessageSquare } from 'lucide-react';
|
||||
import { cadastrarGrupoWhatsApp, listarGruposWhatsApp } from '@/services/gruposWhatsAppService';
|
||||
import { WhatsAppGroup } from '@/types/financialTypes';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
|
||||
const GruposWhatsApp = () => {
|
||||
const { toast } = useToast();
|
||||
const [grupos, setGrupos] = useState<WhatsAppGroup[]>([]);
|
||||
const [carregando, setCarregando] = useState<boolean>(true);
|
||||
const [cadastrando, setCadastrando] = useState<boolean>(false);
|
||||
const [userEmail, setUserEmail] = useState<string>('');
|
||||
|
||||
// Buscar os grupos do usuário ao carregar a página
|
||||
const buscarGrupos = async () => {
|
||||
setCarregando(true);
|
||||
try {
|
||||
const gruposData = await listarGruposWhatsApp();
|
||||
setGrupos(gruposData);
|
||||
} catch (error) {
|
||||
console.error('Erro ao buscar grupos:', error);
|
||||
toast({
|
||||
title: 'Erro',
|
||||
description: 'Não foi possível carregar seus grupos do WhatsApp',
|
||||
variant: 'destructive',
|
||||
});
|
||||
} finally {
|
||||
setCarregando(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// Obter o email do usuário do localStorage
|
||||
const email = localStorage.getItem('userEmail');
|
||||
if (email) {
|
||||
setUserEmail(email.trim().toLowerCase());
|
||||
}
|
||||
|
||||
buscarGrupos();
|
||||
}, []);
|
||||
|
||||
// Cadastrar novo grupo
|
||||
const handleCadastrarGrupo = async () => {
|
||||
setCadastrando(true);
|
||||
try {
|
||||
const novoGrupo = await cadastrarGrupoWhatsApp();
|
||||
|
||||
if (novoGrupo) {
|
||||
toast({
|
||||
title: 'Grupo registrado com sucesso',
|
||||
description: 'Siga as instruções para completar o vínculo',
|
||||
variant: 'default',
|
||||
});
|
||||
|
||||
// Atualizar a lista de grupos
|
||||
buscarGrupos();
|
||||
} else {
|
||||
toast({
|
||||
title: 'Erro',
|
||||
description: 'Não foi possível registrar o grupo',
|
||||
variant: 'destructive',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Erro ao cadastrar grupo:', error);
|
||||
toast({
|
||||
title: 'Erro',
|
||||
description: 'Não foi possível registrar o grupo',
|
||||
variant: 'destructive',
|
||||
});
|
||||
} finally {
|
||||
setCadastrando(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<div className="space-y-6">
|
||||
<div className="flex justify-between items-center">
|
||||
<h1 className="text-2xl font-bold tracking-tight">Grupos do WhatsApp</h1>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={buscarGrupos}
|
||||
disabled={carregando}
|
||||
>
|
||||
<RefreshCw className={`h-4 w-4 mr-2 ${carregando ? 'animate-spin' : ''}`} />
|
||||
{carregando ? 'Atualizando...' : 'Atualizar'}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleCadastrarGrupo}
|
||||
disabled={cadastrando}
|
||||
>
|
||||
{cadastrando ? (
|
||||
<>
|
||||
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
|
||||
Cadastrando...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
Cadastrar Grupo
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Como cadastrar um grupo</CardTitle>
|
||||
<CardDescription>
|
||||
Siga os passos abaixo para vincular um grupo do WhatsApp à sua conta
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<ol className="list-decimal list-inside space-y-2">
|
||||
<li>Clique no botão "Cadastrar Grupo" acima</li>
|
||||
<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">
|
||||
{userEmail}
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Copie e cole o email exatamente como aparece acima
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Seus grupos cadastrados</CardTitle>
|
||||
<CardDescription>
|
||||
Lista de grupos do WhatsApp que você cadastrou
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{carregando ? (
|
||||
<div className="flex justify-center py-8">
|
||||
<Loader2 className="h-8 w-8 animate-spin text-primary" />
|
||||
</div>
|
||||
) : grupos.length > 0 ? (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>ID</TableHead>
|
||||
<TableHead>Nome do grupo</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
<TableHead>Cadastro</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{grupos.map((grupo) => (
|
||||
<TableRow key={grupo.id}>
|
||||
<TableCell className="font-medium">{grupo.id}</TableCell>
|
||||
<TableCell>
|
||||
{grupo.nome_grupo || (
|
||||
<span className="text-muted-foreground italic">
|
||||
{grupo.remote_jid ? 'Sem nome definido' : 'Aguardando vínculo...'}
|
||||
</span>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant={grupo.remote_jid ? "success" : "outline"}>
|
||||
{grupo.remote_jid ? 'Ativo' : 'Pendente'}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell>{new Date(grupo.created_at).toLocaleDateString()}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
) : (
|
||||
<div className="text-center py-8">
|
||||
<MessageSquare className="h-12 w-12 text-muted-foreground opacity-20 mx-auto mb-4" />
|
||||
<p className="text-muted-foreground">Você ainda não cadastrou nenhum grupo</p>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="mt-4"
|
||||
onClick={handleCadastrarGrupo}
|
||||
disabled={cadastrando}
|
||||
>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
Cadastrar seu primeiro grupo
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default GruposWhatsApp;
|
||||
74
src/services/gruposWhatsAppService.ts
Normal file
74
src/services/gruposWhatsAppService.ts
Normal file
@ -0,0 +1,74 @@
|
||||
|
||||
import { supabase } from "@/integrations/supabase/client";
|
||||
import { WhatsAppGroup } from "@/types/financialTypes";
|
||||
|
||||
export async function cadastrarGrupoWhatsApp(): Promise<WhatsAppGroup | null> {
|
||||
try {
|
||||
// Obter o email do usuário do localStorage
|
||||
const userEmail = localStorage.getItem('userEmail');
|
||||
|
||||
if (!userEmail) {
|
||||
console.error('Email do usuário não encontrado no localStorage');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Normalizar o email (minúsculo e sem espaços)
|
||||
const normalizedEmail = userEmail.trim().toLowerCase();
|
||||
|
||||
// Criar registro na tabela grupos_whatsapp
|
||||
const { data, error } = await supabase
|
||||
.from('grupos_whatsapp')
|
||||
.insert([
|
||||
{
|
||||
user_id: localStorage.getItem('userId') || '',
|
||||
login: normalizedEmail,
|
||||
status: 'pendente'
|
||||
}
|
||||
])
|
||||
.select();
|
||||
|
||||
if (error) {
|
||||
console.error('Erro ao cadastrar grupo do WhatsApp:', error);
|
||||
throw new Error('Não foi possível cadastrar o grupo de WhatsApp');
|
||||
}
|
||||
|
||||
console.log('Grupo WhatsApp cadastrado com sucesso:', data);
|
||||
return data[0];
|
||||
} catch (error) {
|
||||
console.error('Erro ao cadastrar grupo do WhatsApp:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function listarGruposWhatsApp(): Promise<WhatsAppGroup[]> {
|
||||
try {
|
||||
// Obter o email do usuário do localStorage
|
||||
const userEmail = localStorage.getItem('userEmail');
|
||||
|
||||
if (!userEmail) {
|
||||
console.error('Email do usuário não encontrado no localStorage');
|
||||
return [];
|
||||
}
|
||||
|
||||
// Normalizar o email (minúsculo e sem espaços)
|
||||
const normalizedEmail = userEmail.trim().toLowerCase();
|
||||
|
||||
// Buscar grupos associados ao email do usuário
|
||||
const { data, error } = await supabase
|
||||
.from('grupos_whatsapp')
|
||||
.select('*')
|
||||
.eq('login', normalizedEmail)
|
||||
.order('created_at', { ascending: false });
|
||||
|
||||
if (error) {
|
||||
console.error('Erro ao listar grupos do WhatsApp:', error);
|
||||
throw new Error('Não foi possível listar os grupos de WhatsApp');
|
||||
}
|
||||
|
||||
console.log('Grupos WhatsApp encontrados:', data);
|
||||
return data || [];
|
||||
} catch (error) {
|
||||
console.error('Erro ao listar grupos do WhatsApp:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
|
||||
|
||||
export interface Transaction {
|
||||
id: string;
|
||||
user: string;
|
||||
@ -56,3 +57,14 @@ export interface ResultadoMeta {
|
||||
percentual_atingido: number;
|
||||
meta_batida: boolean;
|
||||
}
|
||||
|
||||
export interface WhatsAppGroup {
|
||||
id: number;
|
||||
user_id: string;
|
||||
remote_jid: string | null;
|
||||
nome_grupo: string | null;
|
||||
created_at: string;
|
||||
status: string;
|
||||
login: string;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user