diff --git a/src/components/panel/MessageComposer.tsx b/src/components/panel/MessageComposer.tsx index 386e40b..4cd3298 100644 --- a/src/components/panel/MessageComposer.tsx +++ b/src/components/panel/MessageComposer.tsx @@ -5,21 +5,41 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Card } from "@/components/ui/card"; import { toast } from "sonner"; +import { createClient } from "@supabase/supabase-js"; + +const supabase = createClient( + import.meta.env.VITE_SUPABASE_URL, + import.meta.env.VITE_SUPABASE_ANON_KEY +); export function MessageComposer() { const [message, setMessage] = useState(""); const [phoneNumber, setPhoneNumber] = useState(""); + const [isSending, setIsSending] = useState(false); - const handleSend = () => { + const handleSend = async () => { if (!message.trim() || !phoneNumber.trim()) { toast.error("Please enter both phone number and message"); return; } - - // Mock send functionality - toast.success("Message sent successfully!"); - setMessage(""); - setPhoneNumber(""); + + setIsSending(true); + try { + const { data, error } = await supabase.functions.invoke('send-sms', { + body: { phoneNumber, message } + }); + + if (error) throw error; + + toast.success("Message sent successfully!"); + setMessage(""); + setPhoneNumber(""); + } catch (error) { + console.error('Error sending message:', error); + toast.error("Failed to send message. Please try again."); + } finally { + setIsSending(false); + } }; return ( @@ -40,6 +60,7 @@ export function MessageComposer() { value={phoneNumber} onChange={(e) => setPhoneNumber(e.target.value)} className="w-full" + disabled={isSending} />
@@ -55,14 +76,16 @@ export function MessageComposer() { value={message} onChange={(e) => setMessage(e.target.value)} className="w-full" + disabled={isSending} />
diff --git a/supabase/functions/_shared/cors.ts b/supabase/functions/_shared/cors.ts new file mode 100644 index 0000000..765bfe2 --- /dev/null +++ b/supabase/functions/_shared/cors.ts @@ -0,0 +1,5 @@ + +export const corsHeaders = { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', +} diff --git a/supabase/functions/send-sms/index.ts b/supabase/functions/send-sms/index.ts new file mode 100644 index 0000000..f42a253 --- /dev/null +++ b/supabase/functions/send-sms/index.ts @@ -0,0 +1,47 @@ + +import { serve } from 'https://deno.land/std@0.168.0/http/server.ts' +import { corsHeaders } from '../_shared/cors.ts' +import { createClient } from 'https://esm.sh/@supabase/supabase-js@2' + +serve(async (req) => { + // Handle CORS + if (req.method === 'OPTIONS') { + return new Response('ok', { headers: corsHeaders }) + } + + try { + const { phoneNumber, message } = await req.json() + const twilioAccountSid = Deno.env.get('TWILIO_ACCOUNT_SID') + const twilioAuthToken = Deno.env.get('TWILIO_AUTH_TOKEN') + const twilioPhoneNumber = Deno.env.get('TWILIO_PHONE_NUMBER') + + if (!twilioAccountSid || !twilioAuthToken || !twilioPhoneNumber) { + throw new Error('Missing Twilio credentials') + } + + const twilioUrl = `https://api.twilio.com/2010-04/Accounts/${twilioAccountSid}/Messages.json` + const response = await fetch(twilioUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + Authorization: `Basic ${btoa(`${twilioAccountSid}:${twilioAuthToken}`)}`, + }, + body: new URLSearchParams({ + To: phoneNumber, + From: twilioPhoneNumber, + Body: message, + }), + }) + + const result = await response.json() + return new Response(JSON.stringify(result), { + headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + status: 200, + }) + } catch (error) { + return new Response(JSON.stringify({ error: error.message }), { + headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + status: 400, + }) + } +})