diff --git a/src/App.tsx b/src/App.tsx
index b910543..53de152 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,4 +1,3 @@
-
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
import Index from '@/pages/Index';
import Transacoes from '@/pages/Transacoes';
@@ -11,6 +10,7 @@ import WhatsApp from '@/pages/WhatsApp';
import { Toaster } from '@/components/ui/toaster';
import './App.css';
import ProtectedRoute from '@/components/auth/ProtectedRoute';
+import RodrigoAudio from './pages/RodrigoAudio';
function App() {
return (
@@ -49,6 +49,7 @@ function App() {
} />
} />
} />
+ } />
} />
diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx
index 66f2318..d6f1169 100644
--- a/src/components/layout/Sidebar.tsx
+++ b/src/components/layout/Sidebar.tsx
@@ -1,99 +1,72 @@
+import React from "react";
+import {
+ LayoutDashboard,
+ Users,
+ Settings,
+ MessageSquare,
+ Headphones
+} from "lucide-react";
-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';
-
-// Updated navItems with the new WhatsApp connection page
-const navItems = [
- {
- name: 'Dashboard',
- path: '/dashboard',
- icon:
- },
- {
- name: 'Transações',
- path: '/transacoes',
- icon:
- },
- {
- name: 'Categorias',
- path: '/categorias',
- icon:
- },
- {
- name: 'Metas',
- path: '/metas',
- icon:
- },
- {
- name: 'Calendário',
- path: '/calendario',
- icon:
- },
- {
- name: 'Conectar WhatsApp',
- path: '/whatsapp',
- icon:
- },
-];
+import { MainNavItem } from "@/types";
+import { siteConfig } from "@/config/site";
interface SidebarProps {
- className?: string;
+ items?: MainNavItem[]
}
-const Sidebar = ({ className }: SidebarProps) => {
- const location = useLocation();
- const [collapsed, setCollapsed] = React.useState(false);
-
- // Improved function to verify if the item is active based on exact path
- const isItemActive = (path: string) => {
- return location.pathname === path;
- };
-
+export function Sidebar({ items }: SidebarProps) {
return (
-
-
- {!collapsed && (
-
FinDash
- )}
-
-
-
-
diff --git a/src/services/audioMessageService.ts b/src/services/audioMessageService.ts
new file mode 100644
index 0000000..5317198
--- /dev/null
+++ b/src/services/audioMessageService.ts
@@ -0,0 +1,104 @@
+
+import { supabase } from '@/integrations/supabase/client';
+import { useToast } from '@/hooks/use-toast';
+
+// Define types for audio message data
+export interface AudioMessage {
+ id?: string;
+ created_at?: string;
+ sender_id: string;
+ sender_name?: string;
+ message_id?: string;
+ audio_url?: string;
+ duration?: number;
+ instance_id: string;
+ metadata?: Record;
+}
+
+// Constants for specific users
+const RODRIGO_GROUP_ID = '120363420212322973@g.us';
+const RODRIGO_EMAIL = 'rodrigobm10@gmail.com';
+
+/**
+ * Stores an audio message from a specific sender to Rodrigo's database
+ */
+export const storeAudioMessageForRodrigo = async (audioMessage: AudioMessage): Promise => {
+ try {
+ console.log(`Storing audio message for Rodrigo from sender ${audioMessage.sender_id}`);
+
+ // Ensure this is a message for Rodrigo's group
+ if (audioMessage.sender_id !== RODRIGO_GROUP_ID) {
+ console.log('Message not for Rodrigo group, skipping storage');
+ return false;
+ }
+
+ // Insert the audio message into Rodrigo's table
+ const { data, error } = await supabase
+ .from('rodrigo_audio_messages')
+ .insert([audioMessage]);
+
+ if (error) {
+ console.error('Error storing audio message for Rodrigo:', error);
+ return false;
+ }
+
+ console.log('Successfully stored audio message for Rodrigo', data);
+ return true;
+ } catch (error) {
+ console.error('Exception storing audio message for Rodrigo:', error);
+ return false;
+ }
+};
+
+/**
+ * Retrieves all audio messages stored for Rodrigo
+ */
+export const getRodrigoAudioMessages = async (): Promise => {
+ try {
+ const { data, error } = await supabase
+ .from('rodrigo_audio_messages')
+ .select('*')
+ .order('created_at', { ascending: false });
+
+ if (error) {
+ console.error('Error fetching Rodrigo audio messages:', error);
+ return [];
+ }
+
+ return data as AudioMessage[];
+ } catch (error) {
+ console.error('Exception fetching Rodrigo audio messages:', error);
+ return [];
+ }
+};
+
+/**
+ * Hook for managing audio messages for Rodrigo
+ */
+export const useRodrigoAudioMessages = () => {
+ const { toast } = useToast();
+
+ const handleNewAudioMessage = async (audioMessage: AudioMessage): Promise => {
+ const success = await storeAudioMessageForRodrigo(audioMessage);
+
+ if (success) {
+ toast({
+ title: "Áudio gravado",
+ description: `Áudio de ${audioMessage.sender_name || 'usuário'} foi gravado para o Rodrigo.`,
+ });
+ return true;
+ } else {
+ toast({
+ title: "Erro",
+ description: "Não foi possível gravar o áudio para o Rodrigo.",
+ variant: "destructive",
+ });
+ return false;
+ }
+ };
+
+ return {
+ handleNewAudioMessage,
+ getRodrigoAudioMessages
+ };
+};
diff --git a/src/services/webhookService.ts b/src/services/webhookService.ts
new file mode 100644
index 0000000..acb0e87
--- /dev/null
+++ b/src/services/webhookService.ts
@@ -0,0 +1,125 @@
+
+import { AudioMessage, storeAudioMessageForRodrigo } from './audioMessageService';
+
+// Constants for specific users
+const RODRIGO_GROUP_ID = '120363420212322973@g.us';
+const RODRIGO_EMAIL = 'rodrigobm10@gmail.com';
+
+interface WebhookMessageEvent {
+ instance: {
+ id: string;
+ name: string;
+ };
+ message: {
+ id: string;
+ from: string;
+ fromMe: boolean;
+ to: string;
+ body: string;
+ type: string;
+ caption?: string;
+ timestamp: number;
+ hasMedia: boolean;
+ mediaUrl?: string;
+ mediaMimeType?: string;
+ mediaDuration?: number;
+ sender?: {
+ id: string;
+ name?: string;
+ pushname?: string;
+ };
+ };
+}
+
+/**
+ * Process an incoming webhook event from the Evolution API
+ */
+export const processWebhookEvent = async (event: any): Promise => {
+ try {
+ console.log('Processing webhook event:', JSON.stringify(event));
+
+ // Ensure this is a message event
+ if (!event.message) {
+ console.log('Not a message event, skipping');
+ return false;
+ }
+
+ const messageEvent = event as WebhookMessageEvent;
+
+ // Check if this is an audio message
+ if (messageEvent.message.type === 'audio' || messageEvent.message.type === 'voice' || messageEvent.message.type === 'ptt') {
+ return await processAudioMessage(messageEvent);
+ }
+
+ return false;
+ } catch (error) {
+ console.error('Error processing webhook event:', error);
+ return false;
+ }
+};
+
+/**
+ * Process an audio message from the webhook event
+ */
+const processAudioMessage = async (event: WebhookMessageEvent): Promise => {
+ // Check if the message is for Rodrigo's group
+ if (event.message.to === RODRIGO_GROUP_ID || event.message.from === RODRIGO_GROUP_ID) {
+ console.log(`Audio message for Rodrigo detected from ${event.message.from}`);
+
+ const audioMessage: AudioMessage = {
+ sender_id: event.message.from,
+ sender_name: event.message.sender?.name || event.message.sender?.pushname,
+ message_id: event.message.id,
+ audio_url: event.message.mediaUrl,
+ duration: event.message.mediaDuration,
+ instance_id: event.instance.id,
+ metadata: {
+ timestamp: event.message.timestamp,
+ mediaType: event.message.mediaMimeType,
+ caption: event.message.caption
+ }
+ };
+
+ return await storeAudioMessageForRodrigo(audioMessage);
+ }
+
+ return false;
+};
+
+/**
+ * Configure a webhook for an instance in the Evolution API
+ */
+export const configureWebhook = async (
+ instanceName: string,
+ webhookUrl: string,
+ apiKey: string
+): Promise => {
+ try {
+ const serverUrl = "evolutionapi2.innova1001.com.br";
+
+ const response = await fetch(`https://${serverUrl}/instance/webhook/${encodeURIComponent(instanceName)}`, {
+ method: 'PUT',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'apikey': apiKey
+ },
+ body: JSON.stringify({
+ url: webhookUrl,
+ events: ['messages', 'messages.ack', 'messages.upsert']
+ })
+ });
+
+ if (!response.ok) {
+ const errorData = await response.json();
+ console.error('Error configuring webhook:', errorData);
+ return false;
+ }
+
+ const data = await response.json();
+ console.log('Webhook configuration response:', data);
+ return true;
+ } catch (error) {
+ console.error('Error configuring webhook:', error);
+ return false;
+ }
+};
diff --git a/supabase/config.toml b/supabase/config.toml
index 46af35f..f283032 100644
--- a/supabase/config.toml
+++ b/supabase/config.toml
@@ -1 +1,6 @@
-project_id = "tnurlgbvfsxwqgwxamni"
\ No newline at end of file
+
+project_id = "tnurlgbvfsxwqgwxamni"
+
+# Enable webhook edge function without JWT verification
+[functions.whatsapp-webhook]
+verify_jwt = false
diff --git a/supabase/functions/whatsapp-webhook/index.ts b/supabase/functions/whatsapp-webhook/index.ts
new file mode 100644
index 0000000..66beb8e
--- /dev/null
+++ b/supabase/functions/whatsapp-webhook/index.ts
@@ -0,0 +1,132 @@
+
+import { serve } from "https://deno.land/std@0.190.0/http/server.ts";
+import { createClient } from "https://esm.sh/@supabase/supabase-js@2.49.4";
+
+// Constants for specific users
+const RODRIGO_GROUP_ID = '120363420212322973@g.us';
+const RODRIGO_EMAIL = 'rodrigobm10@gmail.com';
+
+const corsHeaders = {
+ "Access-Control-Allow-Origin": "*",
+ "Access-Control-Allow-Headers":
+ "authorization, x-client-info, apikey, content-type",
+};
+
+interface AudioMessage {
+ sender_id: string;
+ sender_name?: string;
+ message_id?: string;
+ audio_url?: string;
+ duration?: number;
+ instance_id: string;
+ metadata?: Record;
+}
+
+const supabaseUrl = Deno.env.get('SUPABASE_URL') || '';
+const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') || '';
+
+const supabase = createClient(supabaseUrl, supabaseServiceKey);
+
+async function storeAudioMessageForRodrigo(audioMessage: AudioMessage) {
+ try {
+ // Insert the audio message into Rodrigo's table
+ const { data, error } = await supabase
+ .from('rodrigo_audio_messages')
+ .insert([audioMessage]);
+
+ if (error) {
+ console.error('Error storing audio message for Rodrigo:', error);
+ return false;
+ }
+
+ console.log('Successfully stored audio message for Rodrigo', data);
+ return true;
+ } catch (error) {
+ console.error('Exception storing audio message for Rodrigo:', error);
+ return false;
+ }
+}
+
+// Process the webhook event and store audio messages for Rodrigo
+async function processWebhookEvent(event: any): Promise {
+ try {
+ console.log('Processing webhook event:', JSON.stringify(event));
+
+ // Ensure this is a message event
+ if (!event.message) {
+ console.log('Not a message event, skipping');
+ return false;
+ }
+
+ // Check if this is an audio message
+ const messageType = event.message.type;
+ if (messageType === 'audio' || messageType === 'voice' || messageType === 'ptt') {
+ // Check if the message is for Rodrigo's group
+ if (event.message.to === RODRIGO_GROUP_ID || event.message.from === RODRIGO_GROUP_ID) {
+ console.log(`Audio message for Rodrigo detected from ${event.message.from}`);
+
+ const audioMessage: AudioMessage = {
+ sender_id: event.message.from,
+ sender_name: event.message.sender?.name || event.message.sender?.pushname,
+ message_id: event.message.id,
+ audio_url: event.message.mediaUrl,
+ duration: event.message.mediaDuration,
+ instance_id: event.instance.id,
+ metadata: {
+ timestamp: event.message.timestamp,
+ mediaType: event.message.mediaMimeType,
+ caption: event.message.caption
+ }
+ };
+
+ return await storeAudioMessageForRodrigo(audioMessage);
+ }
+ }
+
+ return false;
+ } catch (error) {
+ console.error('Error processing webhook event:', error);
+ return false;
+ }
+}
+
+serve(async (req) => {
+ // Handle CORS preflight requests
+ if (req.method === "OPTIONS") {
+ return new Response(null, { headers: corsHeaders });
+ }
+
+ try {
+ if (req.method === "POST") {
+ const event = await req.json();
+
+ // Process the webhook event
+ const success = await processWebhookEvent(event);
+
+ return new Response(
+ JSON.stringify({ success }),
+ {
+ headers: { ...corsHeaders, "Content-Type": "application/json" },
+ status: 200,
+ }
+ );
+ }
+
+ return new Response(
+ JSON.stringify({ error: "Method not allowed" }),
+ {
+ headers: { ...corsHeaders, "Content-Type": "application/json" },
+ status: 405,
+ }
+ );
+ } catch (error) {
+ console.error("Error handling webhook:", error);
+ return new Response(
+ JSON.stringify({ error: "Internal server error" }),
+ {
+ headers: { ...corsHeaders, "Content-Type": "application/json" },
+ status: 500,
+ }
+ );
+ }
+});