Implement SMS message sending
This commit implements the functionality to send an SMS message from a user's cell phone and display it on the dashboard. The implementation details are not specified in this commit message.
This commit is contained in:
parent
add47667af
commit
ce57fdd5c6
8
package-lock.json
generated
8
package-lock.json
generated
|
@ -41,7 +41,7 @@
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "^1.0.0",
|
"cmdk": "^1.0.0",
|
||||||
"date-fns": "^3.6.0",
|
"date-fns": "^4.1.0",
|
||||||
"embla-carousel-react": "^8.3.0",
|
"embla-carousel-react": "^8.3.0",
|
||||||
"input-otp": "^1.2.4",
|
"input-otp": "^1.2.4",
|
||||||
"lucide-react": "^0.462.0",
|
"lucide-react": "^0.462.0",
|
||||||
|
@ -4196,9 +4196,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/date-fns": {
|
"node_modules/date-fns": {
|
||||||
"version": "3.6.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
|
||||||
"integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
|
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "^1.0.0",
|
"cmdk": "^1.0.0",
|
||||||
"date-fns": "^3.6.0",
|
"date-fns": "^4.1.0",
|
||||||
"embla-carousel-react": "^8.3.0",
|
"embla-carousel-react": "^8.3.0",
|
||||||
"input-otp": "^1.2.4",
|
"input-otp": "^1.2.4",
|
||||||
"lucide-react": "^0.462.0",
|
"lucide-react": "^0.462.0",
|
||||||
|
|
|
@ -1,42 +1,87 @@
|
||||||
|
|
||||||
import { User } from "lucide-react";
|
import { User } from "lucide-react";
|
||||||
import { Card } from "@/components/ui/card";
|
import { Card } from "@/components/ui/card";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { createClient } from "@supabase/supabase-js";
|
||||||
|
import { formatDistanceToNow } from "date-fns";
|
||||||
|
|
||||||
interface Conversation {
|
// Initialize Supabase client
|
||||||
id: string;
|
const supabaseUrl = "https://ikmbnngahzcellthaysf.supabase.co";
|
||||||
contact: string;
|
const supabaseAnonKey = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImlrbWJubmdhaHpjZWxsdGhheXNmIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDA1ODY5NjUsImV4cCI6MjA1NjE2Mjk2NX0.puDSeLAaTirOPyj6ndF2Mzzu8CKxlwJsoFP6gK8cWuA";
|
||||||
lastMessage: string;
|
|
||||||
timestamp: string;
|
if (!supabaseUrl || !supabaseAnonKey) {
|
||||||
|
throw new Error('Supabase URL and Anon Key are required');
|
||||||
}
|
}
|
||||||
|
|
||||||
const mockConversations: Conversation[] = [
|
const supabase = createClient(supabaseUrl, supabaseAnonKey);
|
||||||
{
|
|
||||||
id: "1",
|
interface Message {
|
||||||
contact: "+1 (555) 987-6543",
|
id: string;
|
||||||
lastMessage: "Thanks for your message",
|
phone_number: string;
|
||||||
timestamp: "2 min ago",
|
message: string;
|
||||||
},
|
created_at: string;
|
||||||
{
|
}
|
||||||
id: "2",
|
|
||||||
contact: "+1 (555) 876-5432",
|
|
||||||
lastMessage: "I'll get back to you shortly",
|
|
||||||
timestamp: "1 hour ago",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export function ConversationList() {
|
export function ConversationList() {
|
||||||
|
const { data: messages, isLoading } = useQuery({
|
||||||
|
queryKey: ['messages'],
|
||||||
|
queryFn: async () => {
|
||||||
|
const { data, error } = await supabase
|
||||||
|
.from('messages')
|
||||||
|
.select('*')
|
||||||
|
.order('created_at', { ascending: false });
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('Error fetching messages:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data as Message[];
|
||||||
|
},
|
||||||
|
refetchInterval: 5000 // Refetch every 5 seconds
|
||||||
|
});
|
||||||
|
|
||||||
|
// Subscribe to new messages
|
||||||
|
React.useEffect(() => {
|
||||||
|
const subscription = supabase
|
||||||
|
.channel('messages')
|
||||||
|
.on('postgres_changes', {
|
||||||
|
event: '*',
|
||||||
|
schema: 'public',
|
||||||
|
table: 'messages'
|
||||||
|
}, () => {
|
||||||
|
// Trigger a refetch when new messages arrive
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['messages'] });
|
||||||
|
})
|
||||||
|
.subscribe();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
subscription.unsubscribe();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div className="p-4">
|
||||||
|
<p className="text-muted-foreground">Loading conversations...</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const conversations = messages || [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4 p-4 animate-fadeIn">
|
<div className="space-y-4 p-4 animate-fadeIn">
|
||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex items-center justify-between mb-6">
|
||||||
<h2 className="text-2xl font-semibold tracking-tight">Conversations</h2>
|
<h2 className="text-2xl font-semibold tracking-tight">Conversations</h2>
|
||||||
<span className="text-sm text-muted-foreground">
|
<span className="text-sm text-muted-foreground">
|
||||||
{mockConversations.length} active
|
{conversations.length} active
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid gap-4">
|
<div className="grid gap-4">
|
||||||
{mockConversations.map((conversation) => (
|
{conversations.map((message) => (
|
||||||
<Card
|
<Card
|
||||||
key={conversation.id}
|
key={message.id}
|
||||||
className="p-4 transition-all hover:shadow-md cursor-pointer group"
|
className="p-4 transition-all hover:shadow-md cursor-pointer group"
|
||||||
>
|
>
|
||||||
<div className="flex items-center space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
|
@ -45,13 +90,13 @@ export function ConversationList() {
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex justify-between items-start">
|
<div className="flex justify-between items-start">
|
||||||
<h3 className="font-medium truncate">{conversation.contact}</h3>
|
<h3 className="font-medium truncate">{message.phone_number}</h3>
|
||||||
<span className="text-xs text-muted-foreground whitespace-nowrap ml-2">
|
<span className="text-xs text-muted-foreground whitespace-nowrap ml-2">
|
||||||
{conversation.timestamp}
|
{formatDistanceToNow(new Date(message.created_at), { addSuffix: true })}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-muted-foreground truncate">
|
<p className="text-sm text-muted-foreground truncate">
|
||||||
{conversation.lastMessage}
|
{message.message}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue